Spring之aop源码解析

aop就是面向切面编程,是为了解耦而生。aop是不仅工作常用而且面试也经常问,所以看aop源码是非常有必要的。

在看源码之前,我们先要带着结论去看源码,而不是一上来看的一脸懵逼,一定要先总后分。

aop的流程就是在spring的生命周期过程中,会缓存所有的切面,然后在bean初始化的时候,它会找当前bean的所有方法是否跟缓存中的切面有匹配的,如果匹配,说明需要代理并创建代理对象,当调用被增强方法时,执行代理逻辑。

本文基于springboot2.3.5.RELEASE,如果有错,欢迎指出。

依赖

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.5.RELEASE</version>
        <relativePath/>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.4</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.34</version>
        </dependency>
    </dependencies>

被代理类

@Component
public class User {
    int id;
    String password;
    String username;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getPassword() {
        return password;
    }

    public String getUsername() {
        return username;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public User(){

    }

    public User(int id, String password, String username) {
        this.id = id;
        this.password = password;
        this.username = username;
    }

    public String sleep() {
        return "睡觉";
    }
}

切面类

@Component
@Aspect
public class AspectUser {

    @Pointcut("execution(* *.sleep(..))")
    public void test(){}

    @Before("test()")
    public void before(){
         System.out.println("刷牙");
    }

    @After("test()")
    public void after() {
        System.out.println("关灯");
    }

    @Around("test()")
    public Object around(ProceedingJoinPoint jp) throws Throwable {
        System.out.println("洗脸");
        Object o = jp.proceed();
        System.out.println("上床");
        return o;
    }

    @AfterReturning(pointcut = "test()",returning = "result")
    public void afterReturning(JoinPoint jp,Object result) {
        System.out.println("闭眼");
        System.out.println(result);
    }

    @AfterThrowing(pointcut = "test()",throwing = "e")
    public void afterThrowing(JoinPoint jp,Throwable e) {
        System.out.println("没有睡意");
    }

}

增强结果

在这里插入图片描述

源码解析

在这里插入图片描述

当开启aop的时候,在启动类上加上@EnableAspectJAutoProxy即可,所以看aop源码的第一步,就是看这个注解。

在这里插入图片描述

在这里插入图片描述
spring会解析启动类上的@Import注解,而@Import注解中的类实现ImportBeanDefinitionRegistrar接口 ,所以会调用这个接口的registerBeanDefinitions方法。关于如何解析,这里就不说了,有兴趣的可以搜下spring如何解析各种注解。(后续我也会出篇文章)

我们直接看registerBeanDefinitions方法。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

可以看到往spring容易内注入一个
名字为org.springframework.aop.config.internalAutoProxyCreator
class为AnnotationAwareAspectJAutoProxyCreator的beanDefinition

那么,AnnotationAwareAspectJAutoProxyCreator这个类就是aop的关键

AnnotationAwareAspectJAutoProxyCreator的类图如下

在这里插入图片描述
从类图上得出结论AnnotationAwareAspectJAutoProxyCreator是个BeanPostProcessor(BeanPostProcessor是spring的扩展机制)

我们来到AbstractApplicationContext的refresh()方法中的finishBeanFactoryInitialization方法,在这个方法中会完成bean的实例化初始化。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

protected boolean isInfrastructureClass(Class<?> beanClass) {
        boolean retVal = Advice.class.isAssignableFrom(beanClass) || Pointcut.class.isAssignableFrom(beanClass) || Advisor.class.isAssignableFrom(beanClass) || AopInfrastructureBean.class.isAssignableFrom(beanClass);
        if (retVal && this.logger.isTraceEnabled()) {
            this.logger.trace("Did not attempt to auto-proxy infrastructure class [" + beanClass.getName() + "]");
        }

        return retVal;
    }

判断当前bean是否是基础类或者是否有@Aspect注解
在这里插入图片描述

在isInfrastructureClass方法中会判断你是否是基础类如Advice,Pointcut,Advisor,
AopInfrastructureBean,是则加入缓存中
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

从spring容器内部拿到所有advisor的名字(我这里拿不到,因为我是用的切面类@Aspect)
在这里插入图片描述

拿到所有advisor的名字去spring内部拿(经过Bean的生命周期),然后加入advisors这个list中
在这里插入图片描述
在这里插入图片描述

首先在bean工厂拿到所有的Bean名字
在这里插入图片描述

遍历所有的name,根据name获得class,然后通过反射判断是否有注解@Aspect(只有我们的AspectUser能进到这里)

在这里插入图片描述

解析我们的切面类,将切面类里面的增强方法封装成Advisor
在这里插入图片描述

获取增强方法
在这里插入图片描述

获取所有没有@Pointcut注解的方法(注意没有被增强的方法也被获取)
在这里插入图片描述

将需要增强的方法封装成Advisor
在这里插入图片描述

获取表达式Pointcut,如果没有,说明不需要增强,有的话封装成Advisor并返回(这里不同advisor的advice类型不一样,感兴趣的自己看一下)
在这里插入图片描述
总结一下,在bean的周期过程中,实例化之前,会调用AnnotationAwareAspectJAutoProxyCreatorresolveBeforeInstantiation方法,
在这个方法中,会找到容器内中的所有Advisor,并找到容器内的切面类然后解析成Advisor,最终这些Advisor被加到缓存中。

下面就是被增强类的初始化了。

把断点打到User的bean创建过程

先经过实例化,此时User对象还不是代理类,属性都是空

在这里插入图片描述
经过属性注入后,初始化Bean
在这里插入图片描述
在这里插入图片描述

遍历到AnnotationAwareAspectJAutoProxyCreator
在这里插入图片描述

判断User这个Bean是否需要代理
在这里插入图片描述

找到特定的Advisor,也就是开始匹配切面,如果不为空,说明需要代理
在这里插入图片描述

在这里插入图片描述

从缓存里面找到所有Advisor(这里不进去了,前面看过了),然后进行匹配
在这里插入图片描述

开始匹配

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

返回匹配成功的advisor,接下来就是创建代理了

在这里插入图片描述
创建一个代理工厂,设置advisor跟被代理类,然后生成代理
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

判断选择用jdk代理还是cglib代理(我这里用的cglib)
在这里插入图片描述

创建代理的过程就不看了。

在这里插入图片描述

在这里插入图片描述
经过初始化后置增强后,bean对象已经被换成代理类了。

当调用被增强方法时,就会被拦截下来

![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/539a23f4baaa4c54a17d6680ac5b9567.png

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

执行@Around
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

又回到这里
在这里插入图片描述

执行@Before
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

执行完@Before
在这里插入图片描述
在这里插入图片描述

来到@After
在这里插入图片描述

先放行,在finally最终执行after增强逻辑
在这里插入图片描述

来到@AfterReturning

在这里插入图片描述

先放行,在执行增强逻辑

在这里插入图片描述
在这里插入图片描述

执行@AfterThrowing
在这里插入图片描述

放行,抛异常在catch代码块执行增强逻辑
在这里插入图片描述

在这里插入图片描述

执行原方法后返回
在这里插入图片描述

开始执行AfterReturing增强逻辑
在这里插入图片描述

执行After增强逻辑
在这里插入图片描述

执行@Around环绕后的增强逻辑

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

总结,spring aop在bean实例化前置增强时加载好切面并放入缓存,被代理类经过初始化后置增强时,将被代理类所有方法跟缓存中的切面匹配,如果匹配,则生成代理类。当调用被增强方法时,使用责任链加递归的方式执行切面逻辑。

  • 31
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值