目录
四、 Spring、SpringMCV、SpringBoot 三者的区别
七、SpringMVC中的控制器是不是单例模式?如果是如何保证。
一、Mybatis的插件原理,如何编写一个插件
答: Mybatis 只支持针对 ParameterHandler、ResultSetHandler、StatementHandler、Executor这4种接口的插件, Mybatis使用JDK的动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这4种接口对象的方法时,就会进入拦截方法,具体就是InvocationHandler的 invoke()方法,拦截那些你指定需要拦截的方法。
ParameterHandler:
写SQL中传的参数(JAVA类型==>数据库支持的类型)的转换工具
ResultSetHandler:
接受数据库中的数据映射成结果集
StatementHandler:
针对JDBC中的Statement进行封装,负责设置参数,将结果集进行转换
Executor:
负责生成SQL语句 ,SQL语句查询缓存维护
编写插件(本质是拦截器): 实现 Mybatis 的 Interceptor接口并复写intercept()方法,然后在给插件编写注解,指定要拦截哪一个接口的哪些方法即可,在配置文件中配置编写的插件。
@Intercepts({ @Signature(type = StatementHandler.class,method = "query", args = {Statement.class,ResultHandler.class}),
@Signature(type = statementHandler .class,method = "update",args={Statement.class}),
@Signature(type = StatementHandler.class,method = "batch",args
= {Statement.c1ass }) })
@Component
注:query、update、batch :具体业务逻辑,哪些方法
@Intercepts :插件
@Signature :要拦截的接口
@Component:放入IOC容器中
args:方法中的参数列表(多个重载方法,参数列表是不一样的)
invocation.proceed()执行具体的业务逻辑
二、Mybatis 的优点和缺点
优点:
- 基于SQL语句编程,相当灵活,不会对应用程序或者数据库的现有设计造成任何影响,SQL单独写,解除sql与程序代码的耦合,便于统一管理。
- 与JDBC相比,减少了50%以上的代码量,消除了JDBC大量冗余的代码,不需要手动开关连接。
- 很好的与各种数据库兼容(因为MyBatis 使用JDBC来连接数据库,所以只要JDBC支持的数据库MyBatis都支持)。
- 能够与Spring 很好的集成。
- 提供映射标签,支持对象与数据库的ORM字段关系映射;提供对象关系映射标签,支持对象关系组件维护。
缺点:
- SQL语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写SQL语句的功底有一定要求。
- SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库
三、Mybatis中#{}和${}的区别是什么?
- #{}是预编译处理、是占位符,${}是字符串替换、是拼接符
- Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement来赋值
- Mybatis 在处理${}时,就是把${}替换成变量的值,调用Statement来赋值
- 使用#{}可以有效的防止SQL注入,提高系统安全性
四、 Spring、SpringMCV、SpringBoot 三者的区别
- spring是一个IOC容器,用来管理Bean,使用依赖注入实现控制反转,可以很方便的整合各种框架,提供AOP机制弥补OOP的代码重复问题、更方便将不同类不同方法中的共同处理抽取成切面、自动注入给方法执行,比如日志、异常等
- springmvc是spring对web框架的一个解决方案,提供了一个总的前端控制器Servlet,用来接收请求,然后定义了一套路由策略(url到handle的映射)及适配执行handle,将handle结果使用视图解析技术生成视图展现给前端
- springboot是spring提供的一个快速开发工具包,让程序员能更方便、更快速的开发spring+springmvc应用,简化了配置(约定>默认配置),整合了一系列的解决方案(starter机制) 、 redis、mongodb、es,可以开箱即用
五、SpringBoot自动配置原理
@lmport + @Configuration + Spring spi
自动配置类由各个starter提供,使用@Configuration + @Bean定义配置类,放到 META-INF/spring.factories下使用Spring spi扫描META-INF/spring.factories下的配置类
使用@lmport导入自动配置类
六、SpringMVC中的九大组件
Handler:也就是处理器。它直接应对着MVC中的C也就是Controller层,它的具体表现形式有很多,可以是类,也可以是方法。在Controller层中@RequestMapping标注的所有方法都可以看成是一个Handler,只要可以实际处理请求就可以是Handler
- HandlerMapping(*) initHandlerMappings(context),处理器映射器,根据用户请求的资源url来查找Handler <url,hanler>。在SpringMVC中会有很多请求,每个请求都需要一个Handler处理,具体接收到一个请求之后使用哪个Handler进行。
- HandlerAdapter(*) initHandlerAdapters(context),适配器。因为SpringMC中的Handler可以是任意的形式,只要能处理请求就ok,但是Servlet需要的处理方法的结构却是固定的,都是以request和response为参数的方法。如何让固定的Servlet处理方法调用灵活的Handler来进行处理呢?这就是HandlerAdapter要做的事情。Handler是用来干活的工具;HandlerMapping用于根据需要干的活找到相应的工具:HandlerAdapter是使用工具干活的人。
- HandlerExceptionResolver (异常处理器) initHandlerExceptionResolvers(context),其它组件都是用来干活的。在干活的过程中难免会出现问题,出问题后怎么办呢?这就需要有一个专门的角色对异常情况进行处理,在SpringMVC中就是HandlerExceptionResolver。具体来说,此组件的作用是根据异常设置ModelAndView,之后再交给render方法进行渲染。
- ViewResolver (视图解析器) initViewResolvers(context),ViewResolver用来将String类型的视图名和Locale解析为View类型的视图。View是用来渲染页面的,也就是将程序返回的参数填入模板里,生成html(也可能是其它类型)文件。这里就有两个关键问题:使用哪个模板?用什么技术(规则)填入参数?这其实是ViewResolver主要要做的工作,ViewResolver需要找到渲染所用的模板和所用的技术(也就是视图的类型)进行渲染,具体的渲染过程则交由不同的视图自己完成。
- RequestToviewNameTranslator initRequestToViewNameTranslator(context),viewResolver是根据ViewName查找view,但有的Handler处理完后并没有设置View也没有设置ViewNarme,这时就需要从request获取ViewName了,如何从request中获取ViewName就是RequestToViewNameTranslator要做的事情了。RequestToViewNameTranislator在Spring MC容器里只可以配置一个,所以所有request到ViewNarme的转换规则都要在一个Translator里面全部实现。
- LocaleResolver initLocaleResolver(context),解析视图需要两个参数:一是视图名,另一个是Locale。视图名是处理器返回的,Locale是从哪里来的?这就是LocaleResolver要做的事情。LocaleResolver用于从request解析出Locale,Locale就是zh-cn之类,表示一个区域,有了这个就可以对不同区域的用户显示不同的结果。SpringMVC主要有两个地方用到了Locale:一是ViewResolver视图解析的时候;二是用到国际化资源或者主题的时候。
- ThemeResolver
initThemeResolver(context),用于解析主题。SpringMVC中一个主题对应一个properties文件,里面存放着跟当前主题相关的所有资源、如图片、css样式等。SpringMVC的主题也支持国际化,同一个主题不同区域也可以显示不同的风格。SpringMVC中跟主题相关的类有ThemeResolver、ThemeSource和Theme。主题是通过一系列资源来具体体现的,要得到一个主题的资源,首先要得到资源的名称,这是ThemeResolver的工作。然后通过主题名称找到对应的主题(可以理解为一个配置)文件,这是ThemeSource的工作。最后从主题中获取资源就可以了。 - MultipartResolver
initMultipartResolver(context),用于处理(文件)上传请求。处理方法是将普通的request包装成。MultipartHttpServletRequest,后者可以直接调用getFile方法获取File,如果上传多个文件,还可以调用getFileMap得到FileName->File结构的Map。此组件中一共有三个方法,作用分别是判断是不是上传请求,将request包装成MultipartHttpServletRequest、处理完后清理上传过程中产生的临时资源。 - FlashMapManager
initFlashMapManager(context),用来管理FlashMap的,FlashMap主要用在redirect中传递参数。
七、SpringMVC中的控制器是不是单例模式?如果是如何保证。
是
Spring保证线程安全方法
- 将scope设置成非singleton。prototype,request。(并发量大的时候,控制器实例非常多)
- 最好的方式将控制器设置成无状态模式(不要属性,如果属性是service,它也是无状态的,不会影响本来的控制器变成有状态)。在控制器中不要携带数据,可以引用无状态的service和dao
八、Spring框架中的单例Bean是线程安全的么?
Spring中的Bean默认是单例模式的,框架并没有对bean进行多线程的封装处理。
如果Bean是有状态的.那就需要开发人员自己来进行线程安全的保证,最简单的办法就是改变bean的作用域把"singleton"改为"protopyte'这样每次请求Bean就相当于是new Bean()这样就可以保证线程的安全了。
- 有状态就是有数据存储功能
- 无状态就是不会保存数据 controller、service和dao层本身并不是线程安全的,只是如果只是调用里面的方法,而且多线程调用一个实例的方法,会在内存中复制变量,这是自己的线程的工作内存,是安全的。
Dao会操作数据库Connection,Connection是带有状态的,比如说数据库事务,Spring的事务管理器使用
Threadlocal为不同线程维护了一套独立的connection副本,保证线程之间不会互相影响(Spring是如何保证事务获取同一个connection的)
不要在bean中声明任何有状态的实例变量或类变量,如果必须如此,那么就使用ThreadLocal把变量变为线程私有的,如果bean的实例变量或类变量需要在多个线程之间共享,那么就只能使用synchronized、lock、CAS等这些实现线程同步的方法了。
九、如何实现一个IOC容器
简略:
- 配置文件配置包扫描路径
- 递归包扫描获取.class文件
- 反射、确定需要交给IOC管理的类
- 对需要注入的类进行依赖注入
详细:
- 配置文件中指定需要扫描的包路径
- 定义一些注解,分别表示访问控制层、业务服务层、数据持久层、依赖注入注解、获取配置文件注解
- 从配置文件中获取需要扫描的包路径,获取到当前路径下的文件信息及文件夹信息,我们将当前路径下所有以.class结尾的文件添加到一个Set集合中进行存储
- 遍历这个set集合,获取在类上有指定注解的类,并将其交给IOC容器,定义一个安全的Map用来存储这些对象
- 遍历这个lOC容器,获取到每一个类的实例,判断里面是有有依赖其他的类的实例,然后进行递归注入
十、Spring容器的启动流程是怎么样的
使用AnnotationConfigApplicationContext来跟踪一下启动流程:
- this();初始化reader 和 scanner
- scan(basePackages);使用scanner组件扫描basePackage下的所有对象,将配置类的BeanDefinition注册到容器中。
- refresh();刷新容器。
- prepareRefresh刷新前的预处理
- obtainFreshBeanFactory:获取在容器初始化时创建的BeanFactory
- prepareBeanFactory: BeanFactory的预处理工作,会向容器中添加一些组件。
- postProcessBeanFactory:子类重写该方法,可以实现在BeanFactory创建并预处理完成后做进一步的设置invokeBeanFactoryPostProcessors: 在BeanFactory初始化之后执行BeanFactory的后处理器。
- registerBeanPostProcessors:向容器中注册Bean的后处理器,他的主要作用就是干预Spring初始化Bean的流程,
- initMessageSource:初始化messagesource组件,主要用于国际化。
- initApplicationEventMulticaster:初始化事件分发器
- onRefresh:留给子容器,子类重写的方法,在容器刷新的时候可以自定义一些逻辑。
- registerListeners:注册监听器。
- finishBeanFactoryInitialization:完成BeanFactory的初始化,主要作用是初始化所有剩下的单例Bean。
- finshRefresh:完成整个容器的初始化,发布BeanFactory容器刷新完成的事件。
十一、Spring中 Bean 的作用域之间有什么区别?
默认情况下,Spring只为每个在IOC"容器里声明的bean创建唯一一个实例,整个IOC容器范围内都能共享该实例:所有后续的getBean()调用和 bean引用都将返回这个唯一的bean实例。该作用域被称为singleton,它是所有bean的默认作用域。
类别 | 说明 |
singleton | 在SpringIOC容器中仅存在 唯一 bean 实例, bean 默认都是单例的。 |
prototype | 每次调用 getBean() 时都会创建一个新的 实例。 |
request | 每一次 HTTP 请求都会产生一个新的 bean,该作用域仅适用于WebApplicationContext环境 |
session | 同一个HTTP Session 共享一个 Bean ,不同的HTTP Session 使用不同的Bean,该作用域仅适用于WebApplicationContext环境 |
在 Spring 的配置文件中,给 bean 加上 scope 属性来指定 bean 的作用域如下:
singleton:默认值,当IOC容器一创建就创建bean的实例,而且是单例的。
prototype:原型的。当IOC容器一创建不再实例化bean,每次调用getBean方法再实例化该 bean,而且每调用一次就创建一次对象。
request: 每每次请求实例化一个 bean
session:在一次会话中共享一个 bean
global-session:全局session作用域,仅仅在基于portlet的web应用中才有意义,Spring5已经没有了。Portlet是能够生成语义代码(例如:HTML)片段的小型Java Web插件。它们基于portlet容器,可以像servlet一样处理HTTP请求。但是,与 servlet 不同,每个 portlet 都有不同的会话。