参考书籍《Spring技术内幕》Spring AOP的实现章节
书有点老,但是里面一些概念还是总结比较到位
源码基于Spring-aop 5.3.22 可能和旧版本有所差异但是大体逻辑一致
一丶AOP概述
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。 是一种新的模块化机制,用来描述分散在对象,类,或函数中的横切关注点,分离关注点使解决特定领域问题的代码从业务逻辑中独立出来,业务逻辑代码中不在含义针对特定领域的代码调用,业务逻辑同特定领域问题的关系通过切面封装,维护,这样原本分散在整个应用程序中的变动可以很好地管理起来。
用人话说就是,通过切面完成特定逻辑(事务,入参出参日志等)
可以和业务逻辑(crud)抽离开,便于维护
1. Advice 通知
定义在连接点做什么,为切面增强提供植入接口。描述Spring AOP围绕方法调而注入的切面行为
2.Pointcut切入点
切点决定Advice通知应该作用在哪个连接点,也就是通过Poincut来定义需要增强的方法集合,这些集合可以按照一定规则来完成,这种情况下,Pointcut意味着标识方法(比如事务切面定义了事务注解方法上生效)切入点是一些列织入逻辑代码的连接点集合
3.Advisor通知器
整合Advice 和 Pointcut,定义应该在哪个关注点使用什么通知进行增强。
二丶aop 重要接口和编程体验
我们先抛弃Spring框架,利用Spring aop中存在的工具实现aop增强。
1.基于Advice
Advice
接口的实现有 AfterAdvice后置通知
, Beforeadvice前置通知
, MethodInterceptor方法拦截器
可以看做是环绕通知。
/**
* 服务类
*/
public static class Service{
public void doSomething(){
System.out.println("service doSomething");
}
}
/***
* 自定义的advice 环绕通知
*/
public static class MyAdvice implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("my advice before");
//service 方法执行
Object res = invocation.proceed();
System.out.println("my advice after");
return res;
}
}
public static void main(String[] args) {
//代理工程 用于生成代理对象
ProxyFactory proxyFactory = new ProxyFactory();
//目标对象
Service service = new Service();
//设置需要代理的对象
proxyFactory.setTarget(service);
proxyFactory.addAdvice(new MyAdvice());
//生成代理对象
Service proxyService = (Service) proxyFactory.getProxy();
//代理对象执行
proxyService.doSomething();
}
上述代码执行结果
基于 Advice
, ProxyFactory
成功实现了Aop代理增强
2.基于Advisor
public static void advisorBased(){
//代理工程 用于生成代理对象
ProxyFactory proxyFactory = new ProxyFactory();
//目标对象
Service service = new Service();
//advisor
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
//根据名称匹配方法的pointcut
NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
//指定只有doSomething 才增强
pointcut.setMappedName("doSomething");
advisor.setPointcut(pointcut);
advisor.setAdvice(new MyAdvice());
//设置需要代理的对象
proxyFactory.setTarget(service);
proxyFactory.addAdvisor(advisor);
//生成代理对象
Service proxyService = (Service) proxyFactory.getProxy();
//代理对象执行
proxyService.doSomething();
System.out.println();
proxyService.sayHello();
}
最后我们发现只有名称匹配的方法才生效。
Advisor
接口具备方法 Advice getAdvice()
来获取通知。 PointcutAdvisor
实现了 Advisor
并且新增方法 Pointcut getPointcut()
来获取切入点的定义。 Pointcut
接口定义了两个方法 ClassFilter getClassFilter()
, MethodMatcher getMethodMatcher()
分别是对类和方法的筛选,来决定Advise是不是应该作用于当前类。
三丶ProxyFactory 和 ProxyFactoryBean
1.ProxyFactory
1.1类图
TargetSource 用于获取 AOP 调用的当前“目标”
getTargetClass
可以获取被代理对象的类型, getTarget
可以获取被代理对象, HotSwappableTargetSource
中的swap方法可以替换掉代理对象,Spring aop常用的是 SingletonTargetSource
它持有了原始的被代理对象。
1.2 proxyFactory是如何创建代理对象的。
public Object getProxy() {
return createAopProxy().getProxy();
}
1.2.1 DefaultAopProxyFactory # createAopProxy
这里生成的AopProxy 才是负责生成代理对象的,其中spring内置了两种策略——JDK动态代理和CGLIB动态代理。
只有设置了需要代理目标类,或者说没有指定代理的接口,且代理目标类不是接口,不是lambda,不是已经被JDK动态代理后的类,那么才会使用CGLIB进行动态代理。
1.2.2 AopPorxy
其中 JdkDynamicAopProxy
,还实现了 InvocationHandler
。
- Jdk动态代理
- 生成代理对象
Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this)
,这里的this便是自己。 - invoke
首先是对equals
,hashCode
的处理,目标对象声明了让目标对象执行,反之调用JdkDynamicAopProxy
对应的方法,其次是如果配置中设置了暴露代理对象,那么将其放入到AopContext
中的ThreadLocal
中
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
然后获取当前对象的拦截器链,如果拦截器链为空 那么直接反射调用目标对象的方法。如果存在拦截器链那么new 一个 ReflectiveMethodInvocation
利用反射依次执行。Spring只支持方法拦截器 MethodInterceptor
进行代理增强,对于Advise都会适配成 MethodInterceptor
,Spring采用适配器模式,具体的适配器如下
public interface AdvisorAdapter {
//支持什么Advise被适配
boolean supportsAdvice(Advice advice);
//适配
MethodInterceptor getInterceptor(Advisor advisor);
}
自然是Spring遍历每一个Advise责任链模式依次找到 AdvisorAdapter
然后调用适配方法得到一个 MethodInterceptor
,下面是适配成的 MethodInterceptor
。
- CGLIB动态代理
2.ProxyFactoryBean
ProxyFactoryBean
创建代理对象的逻辑和ProxyFactory类似,但是 ProxyFactoryBean
是一个FactoryBean,我们可以利用这一点在bean初始化的时候生成一个代理对象
创建原型对象类似,但是不会判断之前是否创建过,直接无脑创建即可
四丶Spring AOP 和IOC是如何结合起来的
通常我们在使用Spring Aop的时候会在启动类上加一个 @EnableAspectJAutoProxy
注解,这个注解 @Import(AspectJAutoProxyRegistrar.class)
导入了 AspectJAutoProxyRegistrar
,这个类实现了 ImportBeanDefinitionRegistrar
,Spring容器会调用其 registerBeanDefinitions
方法为我们注入 BeanDefinition
,后续会实例化一个 AnnotationAwareAspectJAutoProxyCreator
类型的bean,它是Spring IOC和AOP结合的关键
1.AnnotationAwareAspectJAutoProxyCreator类结构图
这其中最为关键的必然是 AnnotationAwareAspectJAutoProxyCreator
是一个 BeanPostProcessor
,从而在Spring 回调 postProcessAfterInitialization
对bean进行代理的增强,并且它实现了 SmartInstantiationAwareBeanPostProcessor
Spring容器创建bean的时候如果出现了循环依赖那么会调用到 getEarlyBeanReference
,在这个方法里面同样也会进行aop的增强
AbstractAutoProxyCreator
实现了SmartInstantiationAwareBeanPostProcessor
是一个bean后置处理器,使用 AOP 代理包装每个符合条件的 bean,在调用 bean 本身之前委托给指定的拦截器,AOP代理发生的地方。AbstractAdvisorAutoProxyCreator
为了每一个Bean找到合适的Advisor
并且进行,如果Advisor
标注了@Order
或者说实现了Ordered
接口那么会进行排序。AspectJAwareAdvisorAutoProxyCreator
AbstractAdvisorAutoProxyCreator
子类,对一个切面中的多个Advisor进行优先级排序AnnotationAwareAspectJAutoProxyCreator
AspectJAwareAdvisorAutoProxyCreator
的子类,会将容器中标注了@AspectJ
注解的类解析成Advisor
(整合Advice 和 Pointcut,定义应该使用哪个通知器并在哪个关注点使用它)
2. AbstractAutoProxyCreator
是如何进行Aop增强的
进行AOP增强的地方其实还有 postProcessBeforeInstantiation
如果我们自己配置了 TargetSourceCreator
并且可以为当前bean生成,那么才可能发生aop,这里一般不会进行任何操作。
2.1 wrapIfNecessary
其中 shouldSkip
被 AspectJAwareAdvisorAutoProxyCreator
重写,如果 Advisor
是 AspectJPointcutAdvisor
并且切面名称和bean名称相同那么会跳过,这应该是我们标注 @Aspect
的时候需要保证这个类会被Spring加入到容器,所有需要加 @Componet
所以会过滤掉
2.1.1 如何找到所有合适的advice 和advisor
findCandidateAdvisors
方法会找到容器中所以的Advisor
类型的bean,AnnotationAwareAspectJAutoProxyCreator
进行了重写,它还会把所以标注了@Aspect
注解的bean中的增强逻辑封装成Advisor
findAdvisorsThatCanApply
这个方法内部逻辑基本上就是调用PointcutAdvisor
获取类过滤器,方法匹配器进行匹配。- sortAdvisors 这里默认是通过@Order注解,或者Ordered接口进行排序,但是
AspectJAwareAdvisorAutoProxyCreator
进行了重写,因为它需要对同一个标注@Aspect切面里面的前置后置等进行排序
2.1.2 创建代理对象
protected Object createProxy(Class beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
}
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
//这里的ProxyTargetClass 来自上面的copyFrom 取决于EnableAspectJAutoProxy注解的proxyTargetClass
//proxyTargetClass 表示是否使用基于CGLIB子类的代理
if (!proxyFactory.isProxyTargetClass()) {
//shouldProxyTargetClass 方法就是去BeanFactory中看当前bean的BeanDefinition中是否存在AutoProxy.PRESERVE_TARGET_CLASS_ATTRIBUTE=trued的attribute,当我们手动注入bean的时候可以使用这个强制让当前bean使用CGLIB增强
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
//获取当前类中非Spring回调(InitializingBean,DisposableBean,Aware)类型的接口,且如果接口的方法大于0,那么会把接口类型加入到proxyFactory中,否则设置ProxyTargetClass(没有接口那么没办法使用JDK动态代理)
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
//主要是把上面找到的advise 适配成Advisor。调用的是advisorAdapterRegistry的wrap方法
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors);
//这里的targetSource是SingletonTargetSource
proxyFactory.setTargetSource(targetSource);
//留给子类扩展的方法
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
//生成代理对象
return proxyFactory.getProxy(getProxyClassLoader());
}
这里其实和我们上面的Aop编程体验中基于 Advisor
类似,最后都是AopProxy创建代理对象
2.2 AnnotationAwareAspectJAutoProxyCreator
上面我们讲了其父类 AbstractAutoProxyCreator
的大体逻辑, AnnotationAwareAspectJAutoProxyCreator
会将@Aspect注解类解析成 Advisor
,下面我们重点看下 AnnotationAwareAspectJAutoProxyCreator
是怎么将@Aspect注解类解析成 Advisor
的
这里依赖了 BeanFactoryAspectJAdvisorsBuilder
,它会遍历所有bean,并调用 isAspect
方法
然后调用 ReflectiveAspectJAdvisorFactory
的 getAdvisors
方法将其适配成多个 Advisor
,会遍历每一个没有标注 @Pointcut
的方法,然后获取 @Around, @Before, @After, @AfterReturning, @AfterThrowing
(如果没有那么直接返回)然后获取 value
中的内容包装成 AspectJExpressionPointcut
(AspectJ表达式pointcut),然后包装成 InstantiationModelAwarePointcutAdvisorImpl
在这个类中会把对应注解的方法封装成对应的 AbstractAspectJAdvice
的子类
调用对应方法依旧采用反射,其子类在合适的实际进行调用。
Original: https://www.cnblogs.com/cuzzz/p/16621320.html
Author: Cuzzz
Title: Spring 源码学习笔记10——Spring AOP
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/714263/
转载文章受原作者版权保护。转载请注明原作者出处!