二刷AOP源码

2,312 阅读15分钟

他做每一件小事都像救命稻草一样紧紧抓住,等到有一天我一看,嚯,好家伙,他抱住的已经是可以让我仰望的参天大树了 <士兵突击>

1.静态代理与JDK动态代理与CGLB动态代理2.AOP体系2.1 基础概念的理解2.2 Spring提供操作组件2.2.1.提建议2.2.2.寻找建议者2.2.3.代理的创建时机2.2.4.代理的创建2.2.5.代理的执行3.总结

有一天:领导建议我们给删除操作方法添加操作日志。

为了完成这个意愿,AOP为此做了多少工作呢??

这一切的一切,都要从代理说起

1.静态代理与JDK动态代理与CGLB动态代理

1.静态代理:

  • 硬编码静态代理: 说白了就是通过编码上的设计达到代理的效果。
  • AspectJ编译织入 :编译期把内容编译到字节码中,

重点:编译期把内容织入代理对象

2.动态代理:通过动态生成新的类字节码,来达到动态代理的目标

  • JDK动态代理:Proxy+InvocationHandler 模式
  • CGLB动态代理: Enhancer + MethodInterceptor(CallBack)

重点:生成了新的字节码,动态代理

具体可阅读我的静态代理与JDK动态代理与CGLIB动态代理

2.AOP体系

2.1 基础概念的理解

再来重温这句话领导建议给删除操作方法添加操作日志

这里面包含的点:

  • 谁提的:领导
  • 给谁提的:删除操作
  • 提的什么建议:添加操作日志

至此引出了三个接口出来:

  • Advisor: 提建议者
  • Pointcut:切点,给哪一类人提建议
  • Advise:建议,通知(建议的内容)

当然这里面还有几个隐藏的角色,AOP体系也有对应的定义:

  • TargetSource: 目标对象
  • Joinpoint :连接点,当领导提到建议具体到某个方法上时,Joinpoint就是那个方法的信息封装体。在Joinpoint中我们可以获取到目标方法的相关信息
  • Advised:当一个人被建议成功后,他就可以看做一个Advised

他们之间的关系:

  • Advisor= Pointcut + Advise
  • Advised = N * Advisor :一个人可能被多个 建议者建议。

这几个概念就组成了AOP体系的整体概念框架。

此时:我们达成一个共识,

  • 当我在做AOP编程的时候,其实就是一个提建议的过程。
  • 当给目标对象提了有效建议会为其创建代理

2.2 Spring提供操作组件

有了规范的指导,剩下的就是实现规范,Spring提供了哪些东西呢?

以我们最熟悉的切面编程为例,事务,来看看AOP体系的运作过程.

2.2.1.提建议

切面,事务本质都是提建议。例如下面,我给cn.wqd.aop包下的方法提了日志记录的建议,事务是给方法提了事务控制的建议

(1.切面

@Aspect
@Component
public class WebLogAcpect {
    private Logger logger = LoggerFactory.getLogger(WebLogAcpect.class);

    //定义切入点,切入点为com.example.aop下的所有函数
    @Pointcut("execution(public * cn.wqd.aop..*.*(..))")
    public void webLog(){}

    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        System.out.println("这里是AOP前置方法");
    }
}

(2.事务

Service
public class TransactionalService {
    @Autowired
    DataSource dataSource;

    @Autowired
    UserDao userDao;

    @Transactional
    public String save(){
        System.out.println("被事务方法");
        User user = new User("被事务方法",1);
        Map<Object, Object> map = TransactionSynchronizationManager.getResourceMap();
        System.out.println(userDao.getClass().getName());//
        userDao.save(user);
        return "save";
    }

如何使用AOP是我们最熟悉的,但是他们又和上面的角色有啥关系呢?别急往下看

2.2.2.寻找建议者

提了建议,Spring如何知道是否有提建议者。

(1.BeanFactoryAdvisorRetrievalHelper

为了把应用中的那些建议者找出来,Spring首先提供了一个BeanFactoryAdvisorRetrievalHelper,建议者检索工具:目的是识别出那些实现了Advisor接口的的建议者,并把他们注册成一个Bean,并缓存起来。

public class BeanFactoryAdvisorRetrievalHelper {
public List<Advisor> findAdvisorBeans() {
  (1
  advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                    this.beanFactory, Advisor.class, truefalse);
  (2
  this.cachedAdvisorBeanNames = advisorNames;
  (3
  advisors.add(this.beanFactory.getBean(name, Advisor.class));
}
}

检索逻辑:找到所有实现了Advisor的Beandefiniton;缓存他们的beanName;调用Bean创建逻辑,创建一个Bean出来。

直接实现Advisor接口,显示的表明自己就是一个建议者。

但是平时我们开发并不是用这种直接实现Advisor的方式。@Aspect注解,事务才是我们常用的。那他们到底是不是建议者呢?

(2.@Aspect切面建议者

BeanFactoryAspectJAdvisorsBuilder

@Aspect注解的类的建议读取出来,Spring为此提供了一个建议者构建工具BeanFactoryAspectJAdvisorsBuilder
Aspect建议者构建器。

从其名字直译:他是一个专门构建Aspect建议者的Bean的工具

private final AspectJAdvisorFactory advisorFactory;
public BeanFactoryAspectJAdvisorsBuilder(ListableBeanFactory beanFactory) {
        this(beanFactory, new ReflectiveAspectJAdvisorFactory(beanFactory));
}

通过其构造方法,我们发现他默认会创建一个ReflectiveAspectJAdvisorFactory,从其名字可以看出,他是通过反射的机制来工作的。BeanFactoryAspectJAdvisorsBuilder的大部分工作其实就是由ReflectiveAspectJAdvisorFactory完成的。

public List<Advisor> buildAspectJAdvisors() {

synchronized (this) {
    (1)
     String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                            this.beanFactory, Object.class, truefalse);
    (2)
                    for (String beanName : beanNames) {
          (3)
          if (this.advisorFactory.isAspect(beanType)) {
              MetadataAwareAspectInstanceFactory factory =
                                                    new PrototypeAspectInstanceFactory(this.beanFactory, beanName);         
              (4)
              advisors.addAll(this.advisorFactory.getAdvisors(factory));
          }
    (5)
     this.aspectBeanNames = aspectNames;
                    return advisors;
     }
}

}

构建过程分析:

  1. 找到容器所有的BeanDefinition
  2. 遍历这些BeanDefinition
  3. 判断他是不是isAspect类: 此步(AnnotationUtils.findAnnotation(clazz, Aspect.class) != null);查看其是不是被Aspect标注
  4. 从此Aspect类中把所有的建议者提取出来,注册成Bean
  5. 标配缓存(spring 很多工具类都注重缓存的使用

建议者识别提取过程最重要的就在第4步中,我们再深入看看这一步到底干了什么。
这一步是由ReflectiveAspectJAdvisorFactory完成的

Aspect类中识别建议者并注册Bean

public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
        for (Method method : getAdvisorMethods(aspectClass)【1】) {
            Advisor advisor = 【2】getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
            if (advisor != null) {
                advisors.add(advisor);
            }
        }

        // Find introduction fields.
        for (Field field : aspectClass.getDeclaredFields()【3】) {
            Advisor advisor = 【4】getDeclareParentsAdvisor(field);
            if (advisor != null) {
                advisors.add(advisor);
            }
        }

        return advisors;
}

流程:

  1. 先获取被Aspect注解的类中,所有的可能是建议者的方法:getAdvisorMethods获取的是不被Pointcut注解注释的方法,这些都可能是建议者方法,(包含父类,接口方法)
  2. 遍历这些方法:getAdvisor尝试解析这些方法是不是建议者,通过查看其是否被以下注解标注来判断。
private static final Class<?>[] ASPECTJ_ANNOTATION_CLASSES = new Class<?>[] {
            Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class};

是的话,解析注解上的切点信息[列如:上文例子中@Before("webLog()")注解里的weblog()所代表的切点] . 连同本方法信息包装成一个InstantiationModelAwarePointcutAdvisorImpl建议者。

此时

  • 本方法内容是建议内容,方法会包装为AspectJMethodBeforeAdvice建议
  • webLog()切点
  • InstantiationModelAwarePointcutAdvisorImpl 就是建议者
  1. 获取被@DeclareParents注解的属性:这里也可以配置建议者。不同于Before之类的注解标注的方法的是对存在方法的增强。 此种增强叫做引介增强,一个Java类,没有实现A接口,在不修改Java类的情况下,使其具备A接口的功能.这就是引介增强

4.为配置的引介增强创建一个建议者:DeclareParentsAdvisor

此时@Aspect注解的类里配置的建议都被解析出来,并创建了建议者

  • @Aspect注解的类:可以配置多个建议者
  • @Aspect注解的类:可以配置两种增强,普通增强与引介增强

(3.事务建议者

我们再来看看事务建议者是如何被找到的呢?
启动事务注解EnableTransactionManagement 会注册一个ProxyTransactionManagementConfiguration配置类

public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {

    @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {
        BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
        advisor.setTransactionAttributeSource(transactionAttributeSource());
        advisor.setAdvice(transactionInterceptor());
        advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
        return advisor;
    }
  ....
}

此配置类,直接注册一个建议者:BeanFactoryTransactionAttributeSourceAdvisor
此建议者:以TransactionInterceptor为建议,以TransactionAttributeSourcePointcut为切点。

我们的@Transactional注解在目标方法扮演的角色是标记,谁的标记?切点的标记,

我们回顾下切点的意义:表示对哪些进行增强,具体表现在其matches方法上,来匹配范围

public boolean matches(Method method, Class<?> targetClass) {
        if (TransactionalProxy.class.isAssignableFrom(targetClass)) {
            return false;
        }
        TransactionAttributeSource tas = getTransactionAttributeSource();
        return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
}

TransactionAttributeSourcePointcut切点的TransactionAttributeSource的getTransactionAttribute方法,会计算目标方法是否符合被增强的条件,其中决定性的是否被@Transactional标记

到此两种建议者的获取过程,我们已经知晓。有了建议者,接下来就是给目标方法创建创建代理,采纳这些建议。这里涉及到两个问题:

  1. 在什么时机创建代理?
  2. 如何创建代理?
2.2.3.代理的创建时机

这一块涉及到Bean的生命周期。

代理的创建时机肯定是有了目标对象后,才会创建代理啊。目标对象都没有,建议者又是建议的谁呢?

AbstractAdvisorAutoProxyCreator

在Bean创建的生命周期最后一步,即初始化完成后,会走一遍BeanPostProcessor.postProcessAfterInitialization方法,再执行一次扩展。此时Bean已成型。

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
        ......
    ......
        if (mbd == null || !mbd.isSynthetic()) {
            wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        }
        return wrappedBean;
    }

其中BeanPostProcessor的抽象类AbstractAdvisorAutoProxyCreator会根据是否存在给当前目标对象的建议者,来决定是否创建代理对象。

这块意思很清晰,一个Bean创建出现后,判断是是否有给他提建议的人,有将创建个代理。

2020-5-20修正:说到代理的创建时机,其实有另一种情况。Bean在创建过程中,会尽早暴露一个封装了Bean早期引用的ObjectFactory到第三级缓存,调用ObjectFactory.getObject() 会对Bean执行扩展逻辑。

  • 当不存在循环引用时,第三缓存中的逻辑不会被执行,Bean创建完成后将其从第三缓存移除。
  • 但是当出现循环依赖时,这个第三缓存中的逻辑会被执行 在获取此Bean时会提前执行 Bean的代理创建过程,但同时此Bean并没有走过生命周期。

所以准确的说,创建的代理时机是在目标对象引用创建后。这个之后可能是属性赋值之前;也可能是Bean赋值属性,执行完初始化方法之后.

创建代理的时机就在此处

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
            return bean;
        }
        if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
            return bean;
        }
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }

        【1】是否有适合当前Bean的建议者。
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
        if (specificInterceptors != DO_NOT_PROXY) {
            this.advisedBeans.put(cacheKey, Boolean.TRUE);
      【2】创建代理对象
            Object proxy = createProxy(
                    bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
            this.proxyTypes.put(cacheKey, proxy.getClass());
            return proxy;
        }

        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

getAdvicesAndAdvisorsForBean:

  • 先执行上文提到的寻找建议者的逻辑,找到当前上下文存在的建议者
  • 判断建议者是否给当前Bean提了建议(引介增强会使用类匹配,切点增强会使用切点的MethodMatcher进行匹配),提了建议就创建代理。
2.2.4.代理的创建

有了建议者,有了目标对象,创建代理也就顺理成章。但创建所涉及的功能组件却没有那么简单。

(1)JdkDynamicAopProxy与CglibAopProxy

  • JdkDynamicAopProxy:是对JDK动态创建代理的封装,JdkDynamicAopProxy本身就是一个InvocationHandler,并提供getProxy方法,通过反射Proxy创建代理。
  • CglibAopProxy:对CGLB动态代理创建的封装。getProxy通过Enhancer+DynamicAdvisedInterceptor创建代理对象

这两种增强都会有一个AdvisedSupport属性保存建议者。

private final AdvisedSupport advised;

这两个是底层封装,Spring创建代理并没有直接调用他们,而是封装了三个更高层次的组件

(2)AspectJProxyFactory与ProxyFactoryBean与ProxyFactory

这三个组件都是AdvisedSupport的子类。

这三个组件都可以用作创建代理对象,他们上没有本质的区别,都是ProxyCreatorSupport的子类。创建代理对象的逻辑调用也在ProxyCreatorSupport中。

多个建议者告诉ProxyCreatorSupport ,ProxyCreatorSupport会调用策略工厂AopProxyFactory选择JdkDynamicAopProxy或者CglibAopProxy创建代理对象。

protected Object createProxy(
            Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource)
 
{
    【1】ProxyFactory组件
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.copyFrom(this);

      【2】设置建议者
        Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
        proxyFactory.addAdvisors(advisors);
      【3】创建代理对象
        return proxyFactory.getProxy(getProxyClassLoader());
    }

此时,我们定义的建议者正式与JDK或者CGLB挂上了钩

可以看出,简简单单的JDK,CGLB代理。Spring 融入此功能时做了多么复杂的设计。

2.2.5.代理的执行

以JDK动态代理为例,执行invoke方法

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

[1]
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
[2]
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
[3]
// Proceed to the joinpoint through the interceptor chain.
retVal = invocation.proceed();
}
  • (1)从AdvisedSupport获建议者链,此过程通过AdvisorChainFactory组件完成,将适用于当前目标对象的建议者取出来,创建一个执行建议链。
  • (2)根据建议链等信息,创建一个MethodInvocation执行器
  • (3)执行得到结果

代理类其实也是实现了Advied接口,但是内存生成的那个类,并不会展示Advied接口,但是通过instance of 查看可以查看。

3.总结

AOP之所以复杂,就在于Spring在上层建筑上所做的东西比较多。其本质就是JDK动态代理(Proxy+InvocationHandler)与CGLB(Enhancer + MethodInterceptor(CallBack))

搞清楚SpringAOP组件的角色,以及与底层基础的连接点。理解AOP就不难了。

万层高楼平地起,抓住根 与 主干,就能窥探其奥秘

反过来讲,当我们设计架构时,应该列出我们的核心,围绕核心做上层设计。


第一遍刷