likes
comments
collection
share

【重写SpringFramework】第二章aop模块:自动代理上(chapter 2-6)

作者站长头像
站长
· 阅读数 23

1. 前言

在前边几节内容中,我们介绍了 Spring AOP 的三大核心,增强、切面和代理,此时已经可以实现一个完整的 AOP 的功能。但仔细想一下,代理对象需要一个个去创建,显得异常繁琐,因此我们需要把创建代理对象的流程自动化。具体思路是,依托 Spring 容器强大的管理能力,在 Bean 创建流程中检查对象是否需要被代理,从而实现自动创建代理的功能。更为重要的是,通过自动代理组件将 AOP 机制与 Spring 容器联系起来,这意味着 Spring 框架的两大基石就此得以确立。

2. 自动代理组件

2.1 继承结构

ProxyConfig 类有两个分支,AdvisedSupport 及其子类实现了创建代理的功能,ProxyProcessorSupport 及其子类则将创建代理的功能整合到 BeanFactory 体系之中。尤其是 AbstractAutoProxyCreator 依赖 ProxyFactory,这说明自动代理的底层仍是通过手动地方式创建代理,手动代理是自动代理的基础

  • AopInfrastructureBean:标记接口,表示一个 AOP 组件类,自身不应该被代理。

  • ProxyProcessorSupport:提供辅助功能,供子类使用。

  • AbstractAutoProxyCreator:自动创建代理的核心类,最大的特点是通过 ProxyFactory 来创建代理对象。

  • AbstractAdvisorAutoProxyCreator:使用 Spring 容器中已有的 Advisor 实例构建拦截器链。

  • InfrastructureAdvisorAutoProxyCreator:排除自定义的 Advisor,使用 Spring 内置的组件。

【重写SpringFramework】第二章aop模块:自动代理上(chapter 2-6)

2.2 AbstractAutoProxyCreator

AbstractAutoProxyCreator 作为自动代理的核心类,实现了 InstantiationAwareBeanPostProcessor 接口,拥有了对 Bean 进行处理的能力,同时将 AOP 功能与 IOC 容器联系在一起。

  • advisedBeans:缓存所有已处理的 Bean,确保每个对象只被处理一次
  • earlyProxyReferences:缓存提前创建的代理对象,解决了代理对象的循环依赖问题
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport implements InstantiationAwareBeanPostProcessor, BeanFactoryAware {
    private BeanFactory beanFactory;
    private AdvisorAdapterRegistry advisorAdapterRegistry = new DefaultAdvisorAdapterRegistry();
    private final Map<Object, Boolean> advisedBeans = new ConcurrentHashMap<>(256);
    private final Map<Object, Object> earlyProxyReferences = new ConcurrentHashMap<>(16);
}

AbstractAutoProxyCreator 实现了 InstantiationAwareBeanPostProcessor 接口及其父接口 BeanPostProcessor 的三个方法,它们都能用来创建代理对象,区别在于调用时机和用途。

  • postProcessAfterInitialization 方法在初始化之后执行,是创建代理对象的主要方式。

  • getEarlyBeanReference 方法在填充对象之前执行,专门负责处理代理对象的循环依赖。

  • postProcessBeforeInstantiation 方法在实例化之前执行,是一种很特殊的代理方式。

三个方法的调用时机如下图所示,本节我们只讨论 postProcessBeforeInstantiation 方法的实现,也就是初始化后创建代理的流程,另外两种代理方式在下一节介绍。

【重写SpringFramework】第二章aop模块:自动代理上(chapter 2-6)

3. 初始化后创建代理

3.1 主流程

AbstractAutoProxyCreator 实现了 BeanPostProcessor 接口的 postProcessAfterInitialization 方法,该方法在初始化之后执行。此时实例已创建,依赖关系已解析,完成度相当高,正是创建代理对象的最佳时机。postProcessAfterInitialization 方法起到了辅助作用,首先检查 earlyProxyReferences 字段中是否缓存了当前对象,如果没有提前暴露代理对象,则调用 wrapIfNecessary 方法创建代理对象。

//所属类[cn.stimd.spring.aop.framework.AbstractAutoProxyCreator]
//初始化后回调
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
    if (bean != null) {
        Object cacheKey = StringUtils.hasLength(beanName) ? beanName : bean.getClass();

        //检查是否提前创建代理,确保创建代理的操作只会执行一次
        if (this.earlyProxyReferences.remove(cacheKey) != bean) {
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return null;
}

wrapIfNecessary 方法是创建代理的核心流程,如果不需要被代理,则返回当前对象本身。wrapIfNecessary 方法可以分为三步,最重要的是第二步,如下所示:

  1. 前置检查,如果当前对象已经处理过,或者本身是 AOP 相关的组件类,则不处理
  2. 获取适用于当前对象的拦截器集合,如果不为空,说明需要被代理
  3. 通过 ProxyFactory 完成代理对象的创建工作
//所属类[cn.stimd.spring.aop.framework.AbstractAutoProxyCreator]
//如有必要,创建代理对象
private Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    //1. 前置检查
    //1.1 如果目标Bean已经在缓存中,且不需要代理,直接返回
    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
        return bean;
    }

    //1.2 如果是AOP相关的组件类,则加入缓存并标记为不处理
    Class<?> beanClass = bean.getClass();
    if (isInfrastructureClass(beanClass)) {
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

    //2. 获取增强器(模板方法,由子类实现)
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    if (specificInterceptors != null) {
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        // 3. 创建代理对象
        Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }

    //不需要代理,加入缓存,标记为false
    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
}

3.2 获取增强器集合

getAdvicesAndAdvisorsForBean 方法由子类 AbstractAdvisorAutoProxyCreator 实现,类名说明了增强器的类型是 Advisor。具体的逻辑是由 findEligibleAdvisors 方法完成的,可以分为三步:

  1. 从 Spring 容器中找出所有的 Advisor 组件
  2. 过滤适用于当前类的 Advisor 集合
  3. 对符合条件的 Advisor 进行排序并返回
//所属类[cn.stimd.spring.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator]
//获取适用于指定类的所有Advisor集合
@Override
protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource customTargetSource) {
    List<Advisor> eligibleAdvisors = findEligibleAdvisors(beanClass);
    if (eligibleAdvisors.isEmpty()) {
        return null;
    }
    return eligibleAdvisors.toArray();
}

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass) {
    //1. 从BeanFactory寻找候选的Advisor集合
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    //2. 过滤出符合当前类的所有Advisor
    List<Advisor> eligibleAdvisors = AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
    //3. 排序
    if (!eligibleAdvisors.isEmpty()) {
        AnnotationAwareOrderComparator.sort(eligibleAdvisors);
    }
    return eligibleAdvisors;
}

第一步,寻找候选的 Advisor 组件。首先找出 Spring 容器中所有 Advisor 类型的单例,然后遍历这些组件,调用 isEligibleAdvisorBean 方法判断 Advisor 是否符合条件,如果符合条件,则将 Advisor 组件添加到候选列表中。

//cn.stimd.spring.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator
//查找候选的Advisor
protected List<Advisor> findCandidateAdvisors(){
    List<String> advisorNames = getBeanFactory().getBeanNamesForType(Advisor.class);
    List<Advisor> advisors = new LinkedList<>();
    for (String name : advisorNames) {
        if (isEligibleAdvisorBean(name)) {
            //从容器中获取Advisor组件,并添加到候选列表中
            advisors.add(getBeanFactory().getBean(name, Advisor.class));
        }
    }
    return advisors;
}

子类 InfrastructureAdvisorAutoProxyCreator 重写了 isEligibleAdvisorBean 方法,判定逻辑有两个,一是 Spring 容器中存在 Advisor 实例,二是单例的角色是 ROLE_INFRASTRUCTURE,也就是框架内部使用。

public class InfrastructureAdvisorAutoProxyCreator extends AbstractAdvisorAutoProxyCreator {

    @Override
    protected boolean isEligibleAdvisorBean(String beanName) {
        ConfigurableBeanFactory beanFactory = (ConfigurableBeanFactory) getBeanFactory();
        return beanFactory.containsBeanDefinition(beanName)
                && beanFactory.getBeanDefinition(beanName).getRole() == BeanDefinition.ROLE_INFRASTRUCTURE;
    }
}

第二步,过滤出符合指定类的 Advisor 集合。AopUtils 工具类的 findAdvisorsThatCanApply 方法对所有候选的 Advisor 进行检查,判断逻辑由 canApply 方法实现。

//所属类[cn.stimd.spring.aop.support.AopUtils]
//检索适用于指定类的Advisor列表
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
    List<Advisor> eligibleAdvisors = new LinkedList<>();

    for (Advisor candidate : candidateAdvisors) {
        if (canApply(candidate, clazz)) {
            eligibleAdvisors.add(candidate);
        }
    }
    return eligibleAdvisors;
}

AopUtils 类有两个重载的 canApply 方法,先来看第一个。canApply 方法将 Advisor 分为切点和引入两种类型分别处理。需要注意的是,如果引入或切点都匹配失败,则默认该 Advisor 是符合条件的。为什么匹配失败还会认为是符合条件的?这是说,当 Advisor 失去了切面的特性,就退化成了 Advice 组件,可以应用于所有的对象。

//所属类[cn.stimd.spring.aop.support.AopUtils]
//检查Advisor是否可以应用于指定类
private static boolean canApply(Advisor advisor, Class<?> targetClass) {
    //IntroductionAdvisor略

    //PointcutAdvisor
    if (advisor instanceof PointcutAdvisor) {
        PointcutAdvisor pa = (PointcutAdvisor) advisor;
        return canApply(pa.getPointcut(), targetClass);
    }
    //Advisor失去切面特性,默认适用于所有类
    return true;
}

我们不关心引入,来看第二个 canApply 方法的实现,可以分为三步:

  1. 检查 ClassFliter 是否匹配当前类,以及 MethodMatcher 是否默认适用所有方法。
  2. 获取当前类实现的所有接口类型,再加上当前类自身,得到一个 Class 集合。在创建代理对象的时候,需要实现目标对象的接口。
  3. 遍历这个集合中的所有方法,如果 MethodMatcher 匹配了至少一个方法,则认为 Advisor 是符合条件的。(JDK 动态代理必须要定义接口,因此可以匹配接口或者实现类上的方法。对于 CGLIB 代理来说,只检查当前类自身的所有方法即可)
//所属类[cn.stimd.spring.aop.support.AopUtils]
//检查切点是否匹配指定的类
private static boolean canApply(Pointcut pointcut, Class<?> targetClass){
    //1. 检查ClassFilter和MethodMatcher是否匹配
    if (!pointcut.getClassFilter().matches(targetClass)) {
        return false;
    }

    MethodMatcher methodMatcher = pointcut.getMethodMatcher();
    if(methodMatcher == MethodMatcher.TRUE){
        return true;
    }

    //2. 获取当前类的所有接口类型
    Set<Class<?>> classes = new LinkedHashSet<>(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
    classes.add(targetClass);

    //3. 遍历当前类及接口的所有方法,只要有一个符合条件,则认为PointcutAdvisor适用于当前类
    for (Class<?> clazz : classes) {
        Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
        for (Method method : methods) {
            if(methodMatcher.matches(method, targetClass)){
                return true;
            }
        }
    }
    return false;
}

第三步,由于多个切面可以对同一个方法进行增强,因此有必要确定多个切面的执行顺序。AbstractPointcutAdvisor 类实现了 Ordered 接口,调用 AnnotationAwareOrderComparator 的静态方法 sort 进行排序。

3.3 创建代理对象

在执行完 getAdvicesAndAdvisorsForBean 方法后,返回一个拦截器的集合,如果拦截器集合不为空,说明当前实例应当被代理。这一点非常重要,手动代理只解决了创建代理的问题,哪些对象有资格被代理才是自动的含义所在。

接下来是创建代理对象的工作,由 createProxy 方法完成,实际上重复了手动代理的流程,前边已经详细介绍过了。需要注意的是,对于每个需要代理的对象,都创建了一个代理工厂的的实例。这一点也很好理解,ProxyFactory 的父类 AdvisedSupport 保存了目标对象和 Advisor 集合等重要信息,这些内容对于每个代理来说都是唯一的。

//所属类[cn.stimd.spring.aop.framework.AbstractAutoProxyCreator]
//创建代理对象
protected Object createProxy(Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.copyFrom(this);    //将AopConfig复制一份给代理对象

    //判断目标对象使用JDK代理还是Cglib代理
    if (!proxyFactory.isProxyTargetClass()) {
        evaluateProxyInterfaces(beanClass, proxyFactory);
    }

    //将各种Advice转换为统一的Advisor
    Advisor[] advisors = buildAdvisors(specificInterceptors);
    for (Advisor advisor : advisors) {
        proxyFactory.addAdvisor(advisor);
    }
    proxyFactory.setTargetSource(targetSource);
    //使用代理工厂创建代理
    return proxyFactory.getProxy();
}

4. 配置自动代理组件

InfrastructureAdvisorAutoProxyCreator 是 Spring 内置的自动代理组件,对于 AOP 功能来说该组件是标配,因此可以将注册和配置的逻辑固定下来方便使用。AopConfigUtils 工具类提供了两个静态方法,如下所示:

  • registerAutoProxyCreatorIfNecessary 方法:注册 InfrastructureAdvisorAutoProxyCreator 组件
  • forceAutoProxyCreatorToUseClassProxying 方法:将 proxyTargetClass 属性设置为 true,也就是通过 CGLIB 框架来创建代理
public class AopConfigUtils {
    public static final String AUTO_PROXY_CREATOR_BEAN_NAME = "org.springframework.aop.config.internalAutoProxyCreator";

    //注册AutoProxyCreator组件类
    public static void registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
        if (!registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
            RootBeanDefinition rbd = new RootBeanDefinition(InfrastructureAdvisorAutoProxyCreator.class);
            rbd.getPropertyValues().addPropertyValue("order", Ordered.HIGHEST_PRECEDENCE);
            rbd.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
            registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, rbd);
        }
    }

    //强行将proxyTargetClass属性设置为true
    public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) {
        if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
            BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
            definition.getPropertyValues().addPropertyValue("proxyTargetClass", Boolean.TRUE);
        }
    }
}

注:在实际使用中,@EnableAspectJAutoProxy 注解的作用是开启 AOP 功能,@EnableTransactionManagement 注解的作用是开启事务,而事务建立在 AOP 功能的基础之上。这两个注解都是通过 AopConfigUtils 来注册和配置 AOP 组件的。

5. 测试

5.1 准备工作

本次测试需要的类比较多,首先是 LoggerInterceptorLoggerPointcutLoggerAdvisor 三个类提供 AOP 功能的支持,其次是 Logger 注解类起到了标识的作用,最后是 LoggerTarget 作为创建代理的目标类。

Logger 是简单的注解类,声明在方法上,表示当前类需要被代理。

//测试类:日志注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Logger {}

LoggerInterceptor 实现了 MethodInterceptor 接口,增强的逻辑是在执行目标方法前打印日志。

//测试类:日志拦截器
public class LoggerInterceptor implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("日志拦截: " + invocation.getThis().getClass().getSimpleName() + " 方法:" + invocation.getMethod().getName());
        return invocation.proceed();
    }
}

LoggerPointcut 继承了 StaticMethodMatcherPointcut,说明对方法进行静态匹配。matches 方法实现了匹配逻辑,检查目标方法是否声明了 @Logger 注解。

//测试类:日志切点
public class LoggerPointcut extends StaticMethodMatcherPointcut {

    @Override
    public boolean matches(Method method, Class<?> targetClass) {
        AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(method,
                Logger.class, false, false);
        return attributes != null;
    }
}

LoggerAdvisor 主要起到了整合的作用,使用 LoggerPointcut 作为切点,使用 LoggerInterceptor 作为增强器。

//测试类:日志Advisor
public class LoggerAdvisor extends AbstractGenericPointcutAdvisor {
    private Pointcut pointcut = new LoggerPointcut();

    public LoggerAdvisor() {
        //设置增强器为LoggerInterceptor
        setAdvice(new LoggerInterceptor());
    }

    @Override
    public Pointcut getPointcut() {
        return this.pointcut;
    }
}

LoggerTarget 是普通对象,foo 方法声明了 @Logger 注解,bar 方法没有声明注解,作为对照组。

//测试类
public class LoggerTarget {

    @Logger
    public void foo(){
        System.out.println("执行foo方法...");
    }


    public void bar() {
        System.out.println("执行bar方法...");
    }
}

5.2 自动代理

测试方法可以分为三步,第一步注册 Advisor,需要指定角色为 ROLE_INFRASTRUCTURE,否则无法识别。第二步注册自动代理的组件 InfrastructureAdvisorAutoProxyCreator。第三步注册目标对象,当调用 getBean 方法时,会回调 AbstractAutoProxyCreatorpostProcessAfterInitialization 方法,完成创建代理对象的工作。

//测试方法
@Test
public void testAutoproxy(){
    DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
    //注册Advisor
    RootBeanDefinition definition = new RootBeanDefinition(LoggerAdvisor.class);
    definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    factory.registerBeanDefinition("loggerAdvisor", definition);

    //注册自动代理组件
    InfrastructureAdvisorAutoProxyCreator processor = new InfrastructureAdvisorAutoProxyCreator();
    processor.setBeanFactory(factory);
    factory.addBeanPostProcessor(processor);

    //注册目标对象,完成代理流程
    factory.registerBeanDefinition("target", new RootBeanDefinition(LoggerTarget.class));
    LoggerTarget target = factory.getBean("target", LoggerTarget.class);
    target.foo();
    target.bar();
}

从测试结果可以看到,foo 方法作为增强方法执行了日志拦截的操作,而 bar 方法仅作为普通方法调用。这说明代理对象创建成功,并且切面是起作用的。

日志拦截: LoggerTarget 方法:foo
执行foo方法...
执行bar方法...

6. 总结

Spring AOP 代理是实现面向切面编程的基本单元,对于每个有增强需求的对象,都需要创建一个代理对象。问题在于创建代理对象是一个复杂的过程,即使通过代理工厂屏蔽了一部分细节,仍有相当多的工作需要手动完成。鉴于此,Spring 提供了一种标准化的自动创建代理对象的机制,我们可以从三个方面来进行考量。

首先,为了参与对象的创建流程,必须与 BeanFactory 建立关联,这一点是通过 BeanPostProcessor 接口提供的扩展性实现的。

其次,Advisor 组件封装了增强和切面,并对各种增强实现进行统一地适配,其设计的目的就是作为独立实体被 Spring 容器管理。此时,Spring 容器既有增强组件,又有普通对象,且两者之间是多对多的关系。一个对象可以对应多个 Advisor,一个 Advisor 也可以对应多个对象。也就是说,自动代理的主要工作就是为指定对象寻找符合条件的 Advisor 集合。

第三,Spring 提供了若干自动代理组件,最重要的是 AbstractAutoProxyCreator,该类定义了三个创建代理的方法,这些方法的调用时机和用途均不相同。本节只讨论了 postProcessAfterInitialization 方法,调用时机是在初始化之后,这是创建代理最主要的途径。

7. 项目信息

新增修改一览,新增(12),修改(1)。

aop
└─ src
   ├─ main
   │  └─ java
   │     └─ cn.stimd.springwheel.aop
   │        ├─ config
   │        │  └─ AopConfigUtils.java (+)
   │        ├─ framework
   │        │  ├─ autoproxy
   │        │  │  ├─ AbstractAdvisorAutoProxyCreator.java(+)
   │        │  │  └─ InfrastructureAdvisorAutoProxyCreator.java(+)
   │        │  ├─ AbstractAutoProxyCreator.java (+)
   │        │  ├─ AopInfrastructureBean.java (+)
   │        │  └─ ProxyProcessorSupport.java (+)
   │        └─ support
   │           └─ AopUtils.java (+)
   └─ aop.test
      └─ proxy
         ├─ autoproxy
         │  ├─Logger.java(+)
         │  ├─LoggerAdvisor.java(+)
         │  ├─LoggerInterceptor.java(+)
         │  ├─LoggerPointcut.java(+)
         │  └─LoggerTarget.java(+)
         └─ ProxyTest.java (*)

注:+号表示新增、*表示修改

注:项目的 master 分支会跟随教程的进度不断更新,如果想查看某一节的代码,请选择对应小节的分支代码。


欢迎关注公众号【Java编程探微】,加群一起讨论。

原创不易,觉得内容不错请关注、点赞、收藏。

转载自:https://juejin.cn/post/7391694596692295730
评论
请登录