49 JavaConfig方式如何启用AOP?如何强制使用cglib?
在JavaConfig类,加上@EnableAspectJAutoProxy
如果要强制使用CGLIB动态代理 ,加上(proxyTargetClass = true)
加上(exposeProxy = true) 就是将对象暴露到线程池中。
50 介绍AOP在Spring中的实现方式。
-
接口实现
-
XML配置
-
注解实现
51 AOP什么情况下会失效,怎么解决?
内部调用不会触发AOP
-
方法是private也会失效。
-
目标类没有配置Bean,没有交给Spring去管理。
-
切点表达式没有配置正确。
解决方法
在方法里面拿到动态代理对象,再去调用目标方法。
-
可以注入一个类,IOC会帮你注入增强的代理对象,通过这个对象进行对应的方法的调用,就不会有这些问题。
-
注意这里的接收对象,要使用接口来接收,因为这里要注入的代理对象,前面说过SpringAOP中,如果实现了接口,默认是使用JDK的动态代理,而JDK实现动态代理的一个很关键的步骤就是:让这个代理类实现目标类所实现的接口。
-
-
或者在@EnableAspectJAutoProxy(exposeProxy = true) ,在线程中暴露对象。
52 Spring的AOP是在哪里创建的动态代理?
-
在Bean初始化后,通过Bean的后置处理器进行动态代理。
-
如果在属性注入的时候,有循环依赖注入的情况,也会使用Bean的后置处理器为Bean生成代理对象。
53 Spring的AOP的完整实现流程
当@EnableAspectJAutoProxy 会通过@lmport注册一个BeanPostProcessor处理AOP.
-
解析切面:在Bean创建之前的第一个Bean后置处理器会去解析切面(解析切面中通知、切点,一个通知就会解析成一个advisor(通知、切点)
-
创建动态代理 正常的Bean初始化后调用BeanPostProcessor拿到之前缓存的advisor,再通过advisor中oointcut 判断当前Bean是否被切点表达式匹配,如果匹配,就会为Bean创建动态代理(创建方式1.jdk动态代理2.cglib)。
-
调用:拿到动态代理对象, 调用方法 就会判断当前方法是否增强的方法, 就会通过调用链的方式依次去执行通知.
54 事务的四大特性
-
A:原子性
-
一个事务要么成功,要么失败。
-
-
C:一致性
-
从一个状态转换成另一个状态,数据保持一致。
-
-
I:隔离性
-
多用户并发操作一张表的时候,数据库为每一个用户开启事务,事务之间都是隔离的,不干扰。
-
-
D:持久性
-
事务一旦提交了,对数据库的改变是永久的。
-
55 Spring支持的事务管理类型,spring事务实现方式有哪些?
-
编程式事务:
-
比如JDBC中,使用对应的方法开启一个事务,进行提交、回滚等操作。
-
-
声明式事务:
-
@Transactional注解
-
56 Spring的事务传播行为
@Transactional public void function1(){ function2(); } @Transactional public void function2(){ //TODO:业务代码 }
都是针对function2来说的
57 Spring的隔离级别
并发下产生的问题:
-
脏读
事务2修改了a的值,但是没有提交事务;这是事务1读到了事务2修改的值,且事务2又回滚了,产生了数据不一致问题,导致了脏读。
@Transactional(isolation = Isolation.READ_COMMITED) //设置成读已提交。
-
不可重复读
一个事务中,多次读取的内容不同:比如事务1在事务2操作的时候读取a,由于设置成读已提交,这是事务2没有提交,所以读到的值是事务2没操作之前a的值,但是事务2提交后,事务1再次进行读取,两次读的值不同。
@Transactional(isolation = Isolation.REPEATABLE_READ) //设置成可重复读。
解决方案就是加了行锁,在事务1操作的时候,不允许别的事务进行操作。
-
幻读
如果事务1在事务2操作的期间使用统计类的函数,如:sum() ,average()等方法,事务2进行的是insert,就会导致事务1两次结果不同,因为可重复读是加的行锁,解决这个问题需要加表锁。
@Transactional(isolation = Isolation.SERIALIZABLE) //设置成最高级别:表锁。
58 Spring事务实现原理
开启事务注解:@EnableTrasactionalManagement
原理:
-
解析切面 ——> bean的创建第一个后置处理器就行解析。advisor(ponitcut[通过@Transactional解析的切点],advise[这个advice是通过@EnableTrasactionalManagement注册了一个配置类,该配置类就配置了advisor])
-
创建动态代理 ——> bean初始化后,调用Bean的后置处理器,调用后置方法进行动态代理(规则就是原来的内容)。
-
调用
增强的代码,会把autoCommit设置成false,然后执行方法中的sql语句。
59 Spring事务传播的原理
主要是在调用这块:
包括两个方面:
-
融入:当传播行为是融入外部事务,则拿到ThreadLocal的Connection,共享一个数据库连接共同提交、回滚。
-
创建新的事物:当传播行为是创新事务,会将嵌套新事务存入ThreadLocal、再将外部事务暂存起来;当嵌套事务提交、回滚后,会将暂存的事务信息恢复到ThreadLocal中。
比如两个事务:一个外部,一个内嵌在外部方法中。
-
外部:创建一个Connection放到ThreadLocal中,并且修改autoCommit为false,返回事务状态信息(TransactionInfo.newTransaction())
-
外部,执行对应的内嵌方法,
-
内嵌:判断ThreadLocal中是不是已经有了Connection,这时显然是有的,下一步继续判断事务传播行为:
-
融入:
不会创建新的connection,返回事务状态信息:TransactionInfo.newTransaction = false
-
创建:
把外层的事务相关的事务信息(Connection、隔离级别、是否只读...暂存,同时把外层事务的ThreadLocal存储到事务信息都置空)创建一个Connection,放到ThreadLocal中,发回事务状态信息
(TransactionInfo.newTransaction = true ,TransactionInfo也包含暂存的事务信息);判断newTransaction是不是true,就提交事务,判断是否暂存事务,把暂存的事务信息回归。
-
-
外部:
-
判断外部 Transaction.newTransaction = true 提交事务。(有异常就回滚)
-
60 Spring多线程事务,能否保持一致性
比如:
public viod test(){ int result = testDao.getCount(); new Thread(()->{ //进行数据库的操作 }).start(); }
1.Spring的事务信息是存在ThreadLǎcal中的Connection,所以一个线程永远只能有一个事务
2.所以Spring 的事务是无法实现事务一致性的
3.可以通过编程式事务,或者通过分布式事务的思路:二阶段提交方式
61 Spring事务失效的原因
Spring是基于AOP来的。
-
方法是private
-
目标类没有交给spring IOC管理
-
自己捕获了异常,没有把异常抛出。
-
使用cglib动态代理,但是@Transactional声明在接口上面。
内部调用不会触发。
内部方法没有走的代理,比如:
public void way1(){ this.way2(); } @Trasactional(propagation = Propagation.REQUIRES_NEW) public void way2(){ }
这时,可以在重新注入自己一遍。
62 Spring事件监听的核心机制
原理
观察者模式
Spring事件监听有三个部分组成
-
事件:负责对应相应监听器事件源发生事件是特定的事件监听器被触发的原因。
-
监听器:对应于观察者模式中的观察者。监听器监听特定的事件,并在内部定义了事件发生后的逻辑响应。
-
事件发布器:对应于观察者模式中的被观察者/主题, 负责通知观察者 对外提供发布事件和增删事件监听器的接口,维护事件和事件监听器之间的映射关系,并在事件发生时负责通知相关监听器。
支持异步
异步发布事件的核心机制:
多线程。
63 Spring如如何整合Mybatis,管理Mapper接口的呢?
实现:基于JDK的动态代理。
-
Spring会排除接口,无法注册到IOC容器中
-
MyBatis 实现了BeanDefinitionRegistryPostProcessor 可以动态注册BeanDefinition
-
需要自定义扫描器(继承Spring内部扫描器ClassPathBeanDefinitionScanner)重写排除接口的方法(isCandidateComponent5.但是接口虽然注册成了BeanDefinition但是无法实例化Bean 因为接口无法实例化
-
需要将BeanDefinition的BeanClass 替换成JDK动态代理的实例(偷天换日)
-
Mybatis 通过FactoryBean的工厂方法设计模式可以自由控制Bean的实例化过程,可以在getObject方法中创建JDK动态代理
64 解决SpringMVC中修正POST 和 GET 方法乱码问题
-
web.xml中配置一个CharacterEncodingFilter过滤器,设置成utf-8。
<filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class> org.springframework.web.filter.characterEncodingFilter </filter-class> <init-param> <param-name>encoding</param-name> <param-value>utf-8</param-value> </init-param> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </filter>
-
修改Tomcat配置文件的编码。
-
对参数进行重新编码。
65 SpringMVC的控制器是不是单例模式,如果是,有什么问题,怎么解决?
避免在类中声明成员变量,否则就会存在线程安全问题;如果有必须需要,可以放到ThreadLocal中去,因为ThreadLocal是一个线程对应一个对象。
也可以使用线程锁,但是会影响性能。
66 SpringMVC的工作流程?描述一下DispatcherServlet的工作流程?
DispatcherServlet 核心调度器和前端处理器。
-
用户发送请求至前端控制器DispatcherServlet;
-
DispatcherServlet收到请求后,调用HandlerMapping处理器映射器,请求获取Handle;
-
处理器映射器根据请求ur1找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet
-
DispatcherServlet 调用 HandlerAdapter处理器适配器;
-
HandlerAdapter 经过适配调用 具体处理器(Handler,也叫后端控制器);
-
Handler执行完成返回ModelAndView;
-
HandlerAdapter将Handler执行结果ModelAndView返回给DispatcherServlet,
-
DispatcherServlet将ModelAndView传给ViewResolver视图解析器进行解析;
-
ViewResolver解析后返回具体View;
-
DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中) DispatcherServlet啊应用户。
67 SpringMVC 中如何处理AJAX请求?
-
加入Jackson依赖
-
在配置文件中配置json的消息转换器。(jackson不需要配置HttpMessageConvert 消息转换器)
<mvc:annotaion-driven conversion-service="conversionService" />
-
在接受AJAX请求的方法上就可以加入@RequestBody等注解,就可以自定接受JSON文本,转换成对象。
68 Spring和SpringMVC为什么需要父子容器?
-
主要作用是划分边界:Dao和Service层主要通过Spring的容器进行管理;controller一般通过SpringMVC的容器。
-
规范架构:父容器service无法访问子容器controller,子容器controller可以访问父容器的service。
-
将方便子容器的切换。如果我们想把web层从SpringMVC改成struts,只需要将spring-mvc.xml换成Struts的配置文件,Spring的配置文件是不需要改变的。
-
为了节省重复Bean创建。
69 是否可以把SpringMVC中的所有的Bean都交给Spring容器来管理?
不可以,这样会导致我们请求接口的时候产生404。如果所有的Bean都交给父容器,SpringMVC在初始化HandlerMethods的时候(initHandlerMethods)无法根据Controller的handler方法注册HandlerMethod,并没有去査找父容器的bean;也就无法根据请求URI获取到 HandlerMethod来进行匹配,只有getBean才是从父容器中拿。
70 是否可以把Spring容器中的所有的Bean放到SpringMVC中进行管理。
可以,因为父容器的体现无非是为了获取子容器不包含的bean,如果全部包含在子容器完全用不到父容器了, 所以是可以全部放在springmvc子容器来管理。 虽然可以这么做不过一般应该是不推荐这么去做的,一般人也不会这么干的。如果你的项目里有用到事物、或者aop记得也需要把这部分配置需要放到Spring-mvc子容器的配置文件来,不然一部分内容在子容器和一部分内容在父容器,可能就会导致你的事物或者AOP不生效。所以如果aop或事物如果不牛效也,有可能是通讨父容器(Spring)去增强子容器(SpringMVC),也就无法增强。
71 如和实现零配置的SpringMVC?原来是什么?
JavaConfig的配置方式。
-
省略web.xml
-
servlet3.0之后规范中提供了SPI扩展:META-INF/services/javax.servlet.ServletContainerlnitializer
-
SpringMVC通过实现ServletContainerlnitializer接囗
-
动态注册ContextLoaderListener 和DispatcherServlet并创建子父容器(Application)
-
-
省略spring.xml和spring-mvc.xml(只是sprinmvc方式,springboot在自动配置类完成)
-
实现一个继承AbstractAnnotationConfigDispatcherServletlnitializer的类
-
该类就实现了ServetContainernitializer,它会创建ContextLoaderListener 和DispatcherServlet
-
还会创建父子容器,创建容器时传入父子容器配置类则可以替代spring.xml和spring-mvc.xml
-