深入理解Spring 之 源码剖析 SpringBoot Aop 切面编织过程和代理执行过程

源码:



# 前言

前两篇文章我们分析了AOP的原理,知道了AOP的核心就是代理,通知器,切点。我们也知道了XML配置方式和注解方式的底层实现都是相同的,都是通过 ProxyCreatorSupport 创建代理,但是XML是通过 ProxyFactoryBean 来实现的,而 注解是通过 BeanPostProcessor 来实现创建代理的逻辑。

我们上篇讲解注解方式的文章,剖析了 AnnotationAwareAspectJAutoProxyCreator 这个类,这个类就是根据注解创建代理的默认类。那么到底该类在Spring 中是如何运行的?基于注解的 AOP 在现在流行的 SpringBoot 中是如何设计实现的?今天楼主就要以一个简单的demo来 debug 一次源码,彻底了解 SpringBoot 下的AOP 是如何运作的。

我们将分为几个方面来讲:
1. AnnotationAwareAspectJAutoProxyCreator 后置处理器注册过程。
2. 标注 @Aspect 注解的 bean 的后置处理器的处理过程
3. 创建代理的过程
4. 目标对象方法调用的过程

1. AnnotationAwareAspectJAutoProxyCreator 后置处理器注册过程

1.1 后置处理器继承图谱

我们先回顾一下该类的继承图谱 ,看图中红框部分,可以看到该类间接实现了 BeanPostProcessor 接口,而且也实现了 InstantiationAwareBeanPostProcessor 接口,InstantiationAwareBeanPostProcessor 接口继承了BeanPostProcessor 接口。

我们重点看看这两个接口的关系,InstantiationAwareBeanPostProcessor 接口通过继承 BeanPostProcessor 拥有了父接口的两个方法,父类的两个方法是在bean初始化前后做一些控制,而 InstantiationAwareBeanPostProcessor 自己增加的方法则是在bean的实例化做一些控制,顺序:先执行 InstantiationAwareBeanPostProcessor 自身的两个实例化方法,再执行父接口的初始化方法。

我们看到这些方法肯定会问,BeanPostProcessor 这个接口不是在初始化前和初始化后都能做一些操作吗,为什么只叫后置处理器,应该叫前后置处理器啊?那么我们就去看看源码,该方法被多处重写,我们查看重写该方法的所有的实现,可以看到所有处理该方法的逻辑都是直接返回bean,没有任何逻辑操作,可见该方法就是一个空方法。而 postProcessAfterInitialization 后置处理初始化方法各个实现类都有不同的操作。还有一个需要注意的的地方就是,InstantiationAwareBeanPostProcessor 和 BeanPostProcessor 这两个方法名称极其相似,注意区分,BeanPostProcessor 是初始化,InstantiationAwareBeanPostProcessor 是实例化,实例化先执行,初始化后执行。

为什么大张旗鼓的说后置处理器这个事情呢?因为 AnnotationAwareAspectJAutoProxyCreator 就是后置处理器啊,他继承了 AbstractAutoProxyCreator 抽象类,该类实现了后置处理器接口的方法。

既然是个后置处理器,那么就需要注册,我们就看看注册后置处理器的过程。

1.2 后置处理器注册过程

我们会议一下Spring 容器初始化的方法 refresh ,当时我们说,该方法是整个spring 的核心代码,所有的逻辑都是从这里作为入口,那么该方法中有注册后置处理器的逻辑吗?

    @Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // 为刷新准备应用上下文
            prepareRefresh();
            // 告诉子类刷新内部bean工厂,即在子类中启动refreshBeanFactory()的地方----创建bean工厂,根据配置文件生成bean定义
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
            // 在这个上下文中使用bean工厂
            prepareBeanFactory(beanFactory);
            try {
                // 设置BeanFactory的后置处理器// 默认什么都不做
                postProcessBeanFactory(beanFactory);
                // 调用BeanFactory的后处理器,这些后处理器是在Bean定义中向容器注册的
                invokeBeanFactoryPostProcessors(beanFactory);
                // 注册Bean的后处理器,在Bean创建过程中调用
                registerBeanPostProcessors(beanFactory);
                //对上下文的消息源进行初始化
                initMessageSource();
                // 初始化上下文中的事件机制
                initApplicationEventMulticaster();
                // 初始化其他的特殊Bean
                onRefresh();
                // 检查监听Bean并且将这些Bean向容器注册
                registerListeners();
                // 实例化所有的(non-lazy-init)单件
                finishBeanFactoryInitialization(beanFactory);
                //  发布容器事件,结束refresh过程
                finishRefresh();
            } catch (BeansException ex) {
                // 为防止bean资源占用,在异常处理中,销毁已经在前面过程中生成的单件bean
                destroyBeans();
                // 重置“active”标志
                cancelRefresh(ex);
                // Propagate exception to caller.
                throw ex;
            } finally {
                resetCommonCaches();
            }
        }
    }

我们看一下代码,可以看到,其中有一行代码,registerBeanPostProcessors(beanFactory),注册后置处理器,携带BeanFactory参数,该方法会调用 PostProcessorRegistrationDelegate 的一个静态方法:

该静态方法很长,主要逻辑就是,从BeanFactory 中找到所有实现了 BeanPostProcessor 接口的bean,然后添加进集合,最后调用自身的 registerBeanPostProcessors 静态方法,循环调用 beanFactory 的addBeanPostProcessor 方法,将后置处理器添加进bean工厂。

bean工厂的 addBeanPostProcessor 方法如下:

将后置处理器放在一个集合中,并保证只有一个不会重复。然后判断是否 InstantiationAwareBeanPostProcessor 类型的后置处理器,如果有,就将状态hasInstantiationAwareBeanPostProcessors 改为 true。

这个时候也就是完成了后置处理器的注册过程。

2. 标注 @Aspect 注解的 bean 的后置处理器的处理过程

我们在使用 @Aspec 同时也会使用类似 @Component 的注解,表示这是一个Bean,只要是bean,就会调用getBean方法,只要调用 getBean 方法,都会调用 AbstractAutowireCapableBeanFactory 的 createBean 方法,该方法其中有一行函数调用:

注释说,会返回一个代理,我们看看该方法到底是什么逻辑:

我们注意看该方法的 if 判断,hasInstantiationAwareBeanPostProcessors, 就是判断我们刚刚设置的状态,向下走,获取到Bean的类型,先调用 applyBeanPostProcessorsBeforeInstantiation 方法,如果该方法有返回值,则调用 applyBeanPostProcessorsAfterInitialization 方法,注意,上面调用的是 “实例化前方法”,下面那个调用的是 “初始化后方法”,是不同的接口实现。通常在第一次调用的的时候,是不会有返回值的。我们看看该方法实现:

该方法会获取所有的后置处理器,就是之前注册在BeanFactory 的后置处理器,并循环调用他们的 postProcessBeforeInstantiation 方法,但是还是要判断是不是 InstantiationAwareBeanPostProcessor 类型,我们关注的当然是我们在之前的说的 AbstractAutoProxyCreator 后置处理器,我们看看他的 postProcessBeforeInstantiation 方法:

该方法首先检验参数,如果缓存中存在的话就直接返回。如果不在,则判断该bean的类型是否是基础类型,上面是基础类型? 我们看代码:

该方法首先调用父类的的 isInfrastructureClass 方法,然后调用 isAspcet 方法。我们看看这两个方法的实现:

isInfrastructureClass

判断是否是这几个基础类。

isAspect

判断该类是否含有Aspect 注解并且该类属性中含有 “ajc$” 开头的成员变量。

那么后面的那个判断呢?shouldSkip ,字面意思:是否应该跳过。 该方法默认实现是false,但该类注释说,子类需要重写该方法。我们看看该方法是如何重写的:

首先找到所有候选的通知器,然后循环通知器数组,如果该通知器是 AspectJPointcutAdvisor 类型,并且该通知器的通知的切面名称和bean的名字相同,就返回 true。重点在如果获取通知器,在XML配置中,我们知道,通知器是我们显式的实现了 PointcutAdvisor 接口的类,并在配置文件中配置。而这里我们没有配置。Spring 是如何找到的呢?我们看看 findCandidate

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值