微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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类的调用

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐