AOP
概念
AOP,Aspect Oriented Programming,面向切面编程
AOP的理念:将分散在各个业务逻辑代码中相同的代码通过横向切面的方式,抽取到一个独立的模块中。
SpringAOP底层原理就是动态代理。
代理的意义:增强对象的行为,使用动态代理实质上就是调用时拦截对象方法,对方法进行改造、增强。
Spring AOP使用纯JAVA实现,它不需要专门的编译过程,也不需要特殊的类装载器,它在运行期通过代理方式向目标织入增强代码。
来源《Spring 实战 (第4版)》一句话:
SpringAOP构建在动态代理之上,因此,Spring对AOP的支持局限于方法拦截。
在java中动态代理有两种方式:
1.JDK动态代理(针对接口)
2.CGlib动态代理(针对类)
CGlib代理其生成的动态代理对象是目标类的子类
SpringAOP默认是使用JDK动态代理,如果代理的类没有接口则会使用CGlib代理
那么JDK代理和CGLib代理我们该用哪个呢??在《精通Spring4.x 企业应用开发实战》给出了建议:
如果是单例模式我们最好使用CGlib,如果是多例最好使用JDK动态代理
原因:
1.JDK在创建代理对象时的性能要高于CGlib,而生成代理对象的运行性能却比CGlib低
CGLIB代码实践
单例Bean顺序
TestService–>推断构造方法–>普通对象–>依赖注入–>初始化前(@PostConstruct)–>初始化(InitializingBean)–>初始化后(AOP)–>代理对象–>放入Map单例池–>Bean对象
AOP对象内部逻辑
父子类
1 | //现象:调用TestServiceProxy时TestService为Null,但当使用TestServiceProxy.test()方法时,TestService则有值 |
注意点
Spring事务也是AOP代理实现的,因此当Proxy.a()调用本类中的b()方法时,b()方法的@Transactional 会出现失效的状态,原因即为targer实际为this.test()方法,此时相当于Proxy代理类中new Bean.test()导致无法管制
解决方法
将调用方法变为代理方法
- 移入其他类
- 自己注入自己
- 上下午获取自己
解决原因
调用时相当于再找一个Proxy对象,因此属于代理对象调用代理对象,此刻不属于new Bean调用
业务支撑
- 日志打印
- 性能监控
- 事务处理
组件能力
- XML
- @Annotation
Aspect - Pointcut - Advice
底层实现
- Advisor
- aspect
- pointcut
- advice
多个切点转换为多个Advisor,形成Chain,递归方法层层调用
实践
接口的实现类打AOP注解使用动态代理方式
Cglib动态代理
使用JDK动态代理时机
- 当Bean实现接口时,Spring就会用JDK的动态代理
- 当Bean没有实现接口时,Spring使用CGlib是实现
- 可以强制使用CGlib(@EnableAspectJAutoProxy(proxyTargetClass = true))
注解打在接口方法上能否识别
JDK动态代理不能直接识别
- 接口没有实现方法,
spring
依赖注入管理的是对象,接口spring
可管理不到,既然不是spirng
管理的对象,spirng+aop
的配置肯定是失效的 - 注解是可以继承,但只有类的注解是可以继承的,还需要
@Inherited
,方法的注解继承不了,spring
管理的是实际对象,加不上注解,更不可能拦截到
由注解继承规则,该实现类的方法并不能继承接口方法上的注解,因而spring也就无法为该实现类生成代理,aop也就拦截失败
- 接口没有实现方法,
CGLIB动态代理
有类Parent,类Sub,Sub继承自Parent。根据注解的继承原则,切点注解在父类方法,
若Sub覆写了父类所有带注解方法,实际Sub中并未被AOP标识,所有spring并不会为Sub创建代理类,也就不会被拦截。
若Sub覆写了父类部分带注解方法,spring会为Sub创建代理类,对于覆写了父类的方法,由于注解未被继承,不会被拦截;未覆写的方法,可以被拦截。
启动Bean的流程
Refresh
- BeanFactory、后置处理完成
preInstantiateSingleton
CreateBean
- 查找到被Aspect标记的Bean
在populate后进行InitalizeBean
后置处理
AbstractAutoProxycreator.java
1
2
3
4
5
6
7
8
9
10
public Object postProcessAfterInitialization({ Object bean, String beanName)
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean){
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
JDK/CGLIB动态代理
切面转为advisor
createAopProxy返回一个Proxy用于其他类的使用
执行动态代理流程
聚合所有的Chain 递归
- 递归
- 结束条件:Chain执行结束
- Before/After invoke()
来源:
https://www.bilibili.com/video/BV1tS4y1s7ax
https://blog.51cto.com/u_15360778/4195832
https://blog.csdn.net/qianhuan_/article/details/118034747
https://blog.csdn.net/qq_37130607/article/details/113844786
https://blog.csdn.net/xybz1993/article/details/80627432
https://blog.csdn.net/u012938226/article/details/103505875
https://segmentfault.com/q/1010000010652475