Srping源码解析之AOP

看个简单的例子

被代理类

需要对add方法增强

@Component
public class StudentService {
    public String add(String s) {
        System.out.println(s);
        return "ok";
    }
}

切面类

@Aspect
@Component
public class AopDemo {

    @Pointcut("execution(public * cn.com.dq.annotation.aop.StudentService.*(..))")
    public void pointCut() {
    }

    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕通知前");
        Object object = joinPoint.proceed();
        System.out.println("环绕通知后");
        return object;
    }
}

开启aop的类

@Component
@EnableAspectJAutoProxy
public class EnableAspectJAutoProxyBean {

}

扫描注解的类

@ComponentScan("cn.com.dq.annotation")
public class Start {
}

测试方法

@Test
public void test2() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Start.class);
        StudentService studentService = (StudentService) applicationContext.getBean("studentService");
        studentService.add("你好");
    }

结果输出


结果分析

从结果看,确实对我们的add方法进行了增强,通过上面的示例:引发如下思考:
1.没有@EnableAspectJAutoProxy可以吗
2.@EnableAspectJAutoProxy是怎么被spring扫描到的
3.@EnableAspectJAutoProxy做了哪些事情?
4.aop的入口在哪里?什么时候注册,并且实例化的?
5.代理类是如何生成的?被代理类去了哪里?我们如何拿到?
6.@Aspect,@Pointcut,@Around,@Before,@After,@AfterReturning,@AfterThrowing注解,spring是如何认识他们的,他们的作用是什么?
7.如何对目标方法进行增强的?链式调用的原理?

没有@EnableAspectJAutoProxy可以吗?

我们将EnableAspectJAutoProxyBean上的注解先注释掉,看一下运行结果

我们可以看到没有对方法进行增强,因此
@EnableAspectJAutoProxy注解开启了aop功能

@EnableAspectJAutoProxy是怎么被spring扫描到的



@EnableAspectJAutoProxy 注解上面是个@Import注解,实际是扫描@Import注解

@Import是如何被spring容器识别到的

由于我们EnableAspectJAutoProxyBean这个类上面有@Component注解,EnableAspectJAutoProxyBean类才会被spring扫描到并实例化,然后spring才会识别@Import注解。在spring的bean注册与实例化章节,我们知道spring识别的最基础注解是@Component,@Service,@Configuration,@Service,@Configuration的本质就是@Component。
支撑@Import的组件是ConfigurationClassPostProcessor

ConfigurationClassPostProcessor是什么时候注册的,什么时候实例化的

ConfigurationClassPostProcessor的父类是BeanDefinitionRegistryPostProcessor,在spring bean的注册与实例化流程中,refresh方法类,在bean完成注册之后,注册了几个重要的组件,其中有一个就是ConfigurationClassPostProcessor,完成bean的注册以后,执行了一个invokeBeanFactoryPostProcessors(beanFactory)方法,该方法对BeanDefinitionRegistryPostProcessor 类型的类进行了实例化,并且调用了postProcessBeanDefinitionRegistry方法
1.ConfigurationClassPostProcessor类的注册源码


扫描注册完bean以后,就进行了ConfigurationClassPostProcessor类的注册
2.ConfigurationClassPostProcessor类的实例化源码

它是拿到BeanDefinitionRegistryPostProcessor类型的所有beanNames,然后通过beanFactory.getBean操作去进行类的实例化
3.postProcessBeanDefinitionRegistry方法调用的源码

循环所有的BeanDefinitionRegistryPostProcessor类型的类,再调用postProcessBeanDefinitionRegistry方法

ConfigurationClassPostProcessor类的postProcessBeanDefinitionRegistry方法干了哪些事情

在postProcessBeanDefinitionRegistry方法里面追踪我们可以看到一个比较重要的类ConfigurationClassParser,他的parse方法

parse方法里面处理了@Component,@PropertySources,@ComponentScan,@Import, @ImportResource,@Bean

处理import注解的方法体内
1.处理了实现ImportSelector接口的类

2.处理了实现ImportBeanDefinitionRegistrar接口的类

3.又回到了doProcessConfigurationClass方法,这个方法是继续处理引入的目标类

@Import导入的目标类,会被spring容器实例化并管理吗?答案是否,因为处理import注解只有上述三种类型,前2种不满足,第三种继续处理目标类,但是目标类上面没有任何的注解,所有它是不会被spring容器实例化并管理的,但是如果目标类的方法上面加上@Bean注解,该bean是会被spring容器实例化并管理的。
@EnableAspectJAutoProxy注解的@Import导入的类是AspectJAutoProxyRegistrar,这个类有什么特别?我们发现该类是实现了ImportBeanDefinitionRegistrar接口,正好满足@Import处理流程2,该流程主要是通过反射,创建了ImportBeanDefinitionRegistrar类型的实例,并放在容器中。
在执行完parse方法后执行这段代码this.reader.loadBeanDefinitions(configClasses);这个方法中有这么一行代码
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
该方法中完成了对AspectJAutoProxyRegistrar接口的registerBeanDefinitions方法的调用

spring容器扫描@EnableAspectJAutoProxy实际是对@Import的扫描,支撑@Import注解的类是ConfigurationClassPostProcessor

@EnableAspectJAutoProxy做了哪些事情?

从上面我们可以看到 @EnableAspectJAutoProxy 实际是导入了AspectJAutoProxyRegistrar类,并实例化了这个类,然后完成了registerBeanDefinitions方法的调用

registerBeanDefinitions方法干了哪些事情

1.注册aop入口类AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

2.aop入口类是AnnotationAwareAspectJAutoProxyCreator,它是spring容器中的beanName是org.springframework.aop.config.internalAutoProxyCreator

3.然后对注解的2个属性进行了赋值proxyTargetClass,exposeProxy

@EnableAspectJAutoProxy 实际上做的事情是完成aop入口类AnnotationAwareAspectJAutoProxyCreator的注册,同时将@EnableAspectJAutoProxy注解的2个属性赋值给该bean

aop的入口在哪里?什么时候注册,并且实例化的?

aop的入口类是AnnotationAwareAspectJAutoProxyCreator,它是通过@Import导入AspectJAutoProxyRegistrar类,完成registerBeanDefinitions方法的调用进行注册的

AnnotationAwareAspectJAutoProxyCreator何时实例化的

AnnotationAwareAspectJAutoProxyCreator 的父类是SmartInstantiationAwareBeanPostProcessor
SmartInstantiationAwareBeanPostProcessor是在何处实例化的?
在spring bean的注册与实例化流程中,refresh方法类,在bean完成注册之后,执行了registerBeanPostProcessors(beanFactory);这个方法,该方法是通过beanFactory.getBean操作完成BeanPostProcessor类型的类的实例化

aop的入口类是AnnotationAwareAspectJAutoProxyCreator,它是通过@Import导入AspectJAutoProxyRegistrar类,完成registerBeanDefinitions方法的调用进行注册的,在spring bean的注册与实例化流程中执行registerBeanPostProcessors方法完成AnnotationAwareAspectJAutoProxyCreator的实例化

代理类是如何生成的?被代理类去了哪里?我们如何拿到?

spring 在实例化且依赖注入完成以后,执行了initializeBean方法,initializeBean方法体内执行3个比较重要的流程
1.执行BeanPostProcessor的postProcessBeforeInitialization方法
2.执行invokeInitMethods方法
3.执行BeanPostProcessor的postProcessAfterInitialization方法
而AnnotationAwareAspectJAutoProxyCreator的父类是AbstractAutoProxyCreator,而在AbstractAutoProxyCreator的postProcessAfterInitialization方法中,完成了代理类的创建,最后将代理类返回,存在spring容器的一级缓存中

在创建代理的时候,我们可以看到被代理对象呗封装成SingletonTargetSource作为参数传进了创建代理方法内

从源码上我们可以看到最后作为一个属性设置在ProxyFactory的TargetSource属性的,而TargetSource这个属性是其父类AdvisedSupport的一个属性,而在我们的JDK代理JdkDynamicAopProxy中有个属性就是AdvisedSupport,我们拿到AdvisedSupport就能拿到被代理对象


在AopProxyUtils类中有个方法getSingletonTarget就能拿到被代理的类

AnnotationAwareAspectJAutoProxyCreator的父类是AbstractAutoProxyCreator,AbstractAutoProxyCreator实现了
SmartInstantiationAwareBeanPostProcessor,在postProcessAfterInitialization方法中完成了代理类的创建,并且将代理类存在一级缓存中,被代理类封装在代理类的advised属性中,advised是一个AdvisedSupport对象,AdvisedSupport中有个属性就是TargetSource

@Aspect,@Pointcut,@Around,@Before,@After,@AfterReturning,@AfterThrowing注解,spring是如何认识他们的,他们的作用是什么?

我们知道在创建某个类的代理,必须满足该类存在切面,我们才会创建代理,所以创建代理之前我们要寻找切面,如下图

跟踪代码,首先是获取所有的bean,然后循环这些bean,判断他们是否存在 @Aspect注解,如下图


然后将其封装成AspectMetadata对象,如下图

接着是创建了一个MetadataAwareAspectInstanceFactory对象,然后创建advisor对象,如下图

拿到有@Aspect注解的类Class,然后遍历他的所有方法判断是否存在Pointcut注解,拿到所有的没有Pointcut注解的方法,注意这里是没有Pointcut注解的方法,那就是@Around,@Before,@After,@AfterReturning @AfterThrowing,如下图

有个pointCut方法,接着就是创建pointCut对象,如下图


创建pointCut对象之前首先是将注解信息封装在AspectJAnnotation对象中,然后创建pointCut对象,将表达式设置进去,注意此时的pointCut对象是除了@Pointcut之外注解的注解信息。
一个Advisor需要PointCut和Advice,有了PointCut,我们还需要Advice。如下图



我们可以看到根据不同的注解类型创建了不同的advice,拿到了advice,封装成advisor返回了,最后放在了一个advisors容器中,如下图:

当我们拿到所有的advisor以后,然后就要判断我的这个切面是否是作用于当前bean上面,此时是个匹配过程

总结:@Aspect注解定义了一个切面Advisor,@Pointcut定义了一个切点PointCut,@Around,@Before,@After,@AfterReturning,@AfterThrowing,定义了各种通知Advice

如何对目标方法进行增强的?链式调用的原理?

在java中当我们为一个类创建了动态代理,当我们执行这个类的方法的时候,会调到动态代理的invoke方法
下面我们以jdk动态代理,来看aop的调用过程

如上图,第一步获取调用链
如下图,获取所有的advisor,先进行类匹配,再进行方法匹配

如下图,拿到advisor中的advice对象,如果该advice实现了MethodInterceptor接口,直接加入调用链,如果没有实现MethodInterceptor接口,他会把对应的advice包装成MethodInterceptor,然后加入过滤器链


实现MethodInterceptor接口有AspectJAfterAdvice,AspectJAroundAdvice,AspectJAfterThrowingAdvice
没有实现MethodIntercepto接口的有AfterReturningAdvice,最后将其包装成AfterReturningAdviceInterceptor,然后持有AfterReturningAdvice的引用,MethodBeforeAdvice,最后将其包装成MethodBeforeAdviceInterceptor,然后持有MethodBeforeAdvice的引用
拿到调用链后,并开始调用

调用完调用链后,执行被代理方法

下图是调用过程

问题一:
链条中的顺序是怎么样的?
在循环切面类的所有方法的时候

获取所有没有Pointcut注解的方法,对方法进行了排序,按照如下规则排序


从上面我们可以看到调用连中顺序为
Around, Before, After, AfterReturning, AfterThrowing
问题二:
是如何形成调用链的

调用advice中的invoke方法,根据链条中的顺序,对链条中的advice按顺序执行。执行完链条后,执行被代理方法,这样就完成了目标方法的增强
执行顺序为
1.AspectJAroundAdvice类执行aroud方法,打印环绕通知前,进行火炬传递,传回procee()
2.MethodBeforeAdviceInterceptor类执行before方法,打印before,进行火炬传递,传回procee()
3.AspectJAfterAdvice类,直接进行了火炬传递,传回procee()
4.AfterReturningAdviceInterceptor类,直接进行了火炬传递,传回procee()
5.AspectJAfterThrowingAdvice类,直接进行了火炬传递,传回procee()
6.无链条,执行被代理方法
7.aroud执行完,打印环绕通知后
8.AspectJAfterAdvice类执行完,执行finally,执行after方法,打印after
9.AfterReturningAdviceInterceptor执行完,执行afterReturning方法,打印afterReturning
总结:调用链的核心流程,是拿到所有的MethodInterceptor,不是MethodInterceptor,要包装成MethodInterceptor,通过调用invoke方法完成通知方法的调用,调用完成以后通过invoke的参数MethodInvocation调用proceed方法返回主方法,完成下一个advice类的调用。

posted @ 2022-05-15 19:27  DengQ  阅读(181)  评论(0编辑  收藏  举报