手写简易版Spring框架(十一):把AOP动态代理融入到Bean的生命周期(上)

目标

在上一章节我们通过基于 Proxy.newProxyInstance 代理操作中处理方法匹配和方法拦截,对匹配的对象进行自定义的处理操作。并把这样的技术核心内容拆解到 Spring 中,用于实现 AOP 部分,通过拆分后基本可以明确各个类的职责,包括你的代理目标对象属性、拦截器属性、方法匹配属性,以及两种不同的代理操作 JDK 和 CGlib 的方式。

再有了一个 AOP 核心功能的实现后,我们可以通过单元测试的方式进行验证切面功能对方法进行拦截,但如果这是一个面向用户使用的功能,就不太可能让用户这么复杂且没有与 Spring 结合的方式单独使用 AOP,虽然可以满足需求,但使用上还是过去分散。

因此我们需要在本章节完成 AOP 核心功能与 Spring 框架的整合,最终能通过在 Spring 配置的方式完成切面的操作。

设计

其实在有了AOP的核心功能实现后,把这部分功能服务融入到 Spring 其实也不难,只不过要解决几个问题,包括:怎么借着 BeanPostProcessor 把动态代理融入到 Bean 的生命周期中,以及如何组装各项切点、拦截、前置的功能和适配对应的代理器。

为了可以让对象创建过程中,能把xml中配置的代理对象也就是切面的一些类对象实例化,就需要用到 BeanPostProcessor 提供的方法,因为这个类的中的方法可以分别作用与 Bean 对象执行初始化前后修改 Bean 的对象的扩展信息。但这里需要集合于 BeanPostProcessor 实现新的接口和实现类,这样才能定向获取对应的类信息。

但因为创建的是代理对象不是之前流程里的普通对象,所以我们需要前置于其他对象的创建,所以在实际开发的过程中,需要在 AbstractAutowireCapableBeanFactory#createBean 优先完成 Bean 对象的判断,是否需要代理,有则直接返回代理对象。

这里还包括要解决方法拦截器的具体功能,提供一些 BeforeAdvice、AfterAdvice 的实现,让用户可以更简化的使用切面功能。除此之外还包括需要包装切面表达式以及拦截方法的整合,以及提供不同类型的代理方式的代理工厂,来包装我们的切面服务。

类图如下:
在这里插入图片描述
整个类关系图中可以看到,在以 BeanPostProcessor 接口实现继承的 InstantiationAwareBeanPostProcessor 接口后,做了一个自动代理创建的类 DefaultAdvisorAutoProxyCreator,这个类的就是用于处理整个 AOP 代理融入到 Bean 生命周期中的核心类。

DefaultAdvisorAutoProxyCreator 会依赖于拦截器、代理工厂和Pointcut与Advisor的包装服务 AspectJExpressionPointcutAdvisor,由它提供切面、拦截方法和表达式。

Spring 的 AOP 把 Advice 细化了 BeforeAdvice、AfterAdvice、AfterReturningAdvice、ThrowsAdvice,目前我们做的测试案例中只用到了 BeforeAdvice,这部分可以对照 Spring 的源码进行补充测试。

实现

定义Advice拦截器链

public interface BeforeAdvice extends Advice {
    
    

}
public interface MethodBeforeAdvice extends BeforeAdvice {
    
    
    /**
     * 一个方法被调用前的回调方法
     */
    void before(Method method, Object[] args, Object target) throws Throwable;
}

在 Spring 框架中,Advice 都是通过方法拦截器 MethodInterceptor 实现的。环绕 Advice 类似一个拦截器的链路,Before Advice、After advice等,不过暂时我们需要那么多就只定义了一个 MethodBeforeAdvice 的接口定义。

定义 Advisor 访问者

package com.qingyun.springframework.aop;

import org.aopalliance.aop.Advice;

/**
 * @description: 访问者
 * @author: 張青云
 * @create: 2021-08-24 00:48
 **/
public interface Advisor {
    
    

    Advice getAdvice();
}

package com.qingyun.springframework.aop;

/**
 * @description: 承担了Pointcut和Advice的组合,Pointcut用于获取JoinPoint,而Advice决定于JoinPoint执行什么操作。
 * @author: 張青云
 * @create: 2021-08-24 00:49
 **/
public interface PointcutAdvisor extends Advisor{
    
    

    Pointcut getPointcut();
}

Advisor 承担了 Pointcut 和 Advice 的组合,Pointcut 用于获取 JoinPoint,而 Advice 决定于 JoinPoint 执行什么操作。

package com.qingyun.springframework.aop.aspectj;

import com.qingyun.springframework.aop.Pointcut;
import com.qingyun.springframework.aop.PointcutAdvisor;
import org.aopalliance.aop.Advice;

/**
 * @description: 把切点 pointcut、拦截方法 advice 和具体的拦截表达式包装在一起。
 * @author: 張青云
 * @create: 2021-08-24 00:51
 **/
public class AspectJExpressionPointcutAdvisor implements PointcutAdvisor {
    
    

    // 切点
    private AspectJExpressionPointcut pointcut;
    // 具体的拦截方法
    private Advice advice;
    // 表达式
    private String expression;

    public void setExpression(String expression){
    
    
        this.expression = expression;
    }

    @Override
    public Pointcut getPointcut() {
    
    
        if (null == pointcut) {
    
    
            pointcut = new AspectJExpressionPointcut(expression);
        }
        return pointcut;
    }

    @Override
    public Advice getAdvice() {
    
    
        return advice;
    }

    public void setAdvice(Advice advice){
    
    
        this.advice = advice;
    }

}

AspectJExpressionPointcutAdvisor 实现了 PointcutAdvisor 接口,把切面 pointcut、拦截方法 advice 和具体的拦截表达式包装在一起。这样就可以在 xml 的配置中定义一个 pointcutAdvisor 切面拦截器了。

方法拦截器

public class MethodBeforeAdviceInterceptor implements MethodInterceptor {
    
    

    private MethodBeforeAdvice advice;

    public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
    
    
        this.advice = advice;
    }

    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
    
    
        this.advice.before(methodInvocation.getMethod(), methodInvocation.getArguments(), methodInvocation.getThis());
        return methodInvocation.proceed();
    }

}

MethodBeforeAdviceInterceptor 实现了 MethodInterceptor 接口,在 invoke 方法中调用 advice 中的 before 方法,传入对应的参数信息。而这个 advice.before 则是用于自己实现 MethodBeforeAdvice 接口后做的相应处理。

代理工厂

package com.qingyun.springframework.aop.framework;

import com.qingyun.springframework.aop.AdvisedSupport;

/**
 * @description: 代理工厂,解决的是关于 JDK 和 Cglib 两种代理的选择问题
 * @author: 張青云
 * @create: 2021-08-24 00:54
 **/
public class ProxyFactory {
    
    
    private AdvisedSupport advisedSupport;

    public ProxyFactory(AdvisedSupport advisedSupport) {
    
    
        this.advisedSupport = advisedSupport;
    }

    public Object getProxy() {
    
    
        return createAopProxy().getProxy();
    }

    private AopProxy createAopProxy() {
    
    
        if (advisedSupport.isProxyTargetClass()) {
    
    
            return new CglibAopProxy(advisedSupport);
        }

        return new JdkDynamicAopProxy(advisedSupport);
    }
}

其实这个代理工厂主要解决的是关于 JDK 和 Cglib 两种代理的选择问题,有了代理工厂就可以按照不同的创建需求进行控制。

融入Bean生命周期的自动代理创建者

package com.qingyun.springframework.aop.framework.autoproxy;

import com.qingyun.springframework.aop.*;
import com.qingyun.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor;
import com.qingyun.springframework.aop.framework.ProxyFactory;
import com.qingyun.springframework.beans.BeansException;
import com.qingyun.springframework.beans.factory.BeanFactory;
import com.qingyun.springframework.beans.factory.BeanFactoryAware;
import com.qingyun.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import com.qingyun.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;

import java.util.Collection;

/**
 * @description: 判断是否是与切点相匹配的类,如果是则通过代理的方式生成对象
 * @author: 張青云
 * @create: 2021-08-24 00:58
 **/
public class DefaultAdvisorAutoProxyCreator implements InstantiationAwareBeanPostProcessor, BeanFactoryAware {
    
    

    private DefaultListableBeanFactory beanFactory;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
    
    
        this.beanFactory = (DefaultListableBeanFactory) beanFactory;
    }

    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
    
    

        //  对于Advice、Advice、Advisor类型,不使用代理的方式来创建对象
        if (isInfrastructureClass(beanClass)) {
    
    
            return null;
        }

        Collection<AspectJExpressionPointcutAdvisor> advisors = beanFactory.getBeansOfType(AspectJExpressionPointcutAdvisor.class).values();

        for (AspectJExpressionPointcutAdvisor advisor : advisors) {
    
    
            ClassFilter classFilter = advisor.getPointcut().getClassFilter();
            if (!classFilter.matches(beanClass)) continue;

            AdvisedSupport advisedSupport = new AdvisedSupport();

            TargetSource targetSource = null;
            try {
    
    
                targetSource = new TargetSource(beanClass.getDeclaredConstructor().newInstance());
            } catch (Exception e) {
    
    
                e.printStackTrace();
            }
            advisedSupport.setTargetSource(targetSource);
            advisedSupport.setMethodInterceptor((MethodInterceptor) advisor.getAdvice());
            advisedSupport.setMethodMatcher(advisor.getPointcut().getMethodMatcher());
            advisedSupport.setProxyTargetClass(false);

            return new ProxyFactory(advisedSupport).getProxy();

        }

        return null;
    }

    /**
     * 判断该类型是否为Advice、Advice、Advisor
     */
    private boolean isInfrastructureClass(Class<?> beanClass) {
    
    
        return Advice.class.isAssignableFrom(beanClass) || Pointcut.class.isAssignableFrom(beanClass) || Advisor.class.isAssignableFrom(beanClass);
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    
    
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    
    
        return bean;
    }

}

这个 DefaultAdvisorAutoProxyCreator 类的主要核心实现在于 postProcessBeforeInstantiation 方法中,从通过 beanFactory.getBeansOfType 获取 AspectJExpressionPointcutAdvisor 开始。

获取了 advisors 以后就可以遍历相应的 AspectJExpressionPointcutAdvisor 填充对应的属性信息,包括:目标对象、拦截方法、匹配器,之后返回代理对象即可。

那么现在调用方获取到的这个 Bean 对象就是一个已经被切面注入的对象了,当调用方法的时候,则会被按需拦截,处理用户需要的信息。

测试

准备

public interface IUserService {
    
    
    String queryUserInfo();

    String register(String userName);
}
public class UserService implements IUserService {
    
    

    public String queryUserInfo() {
    
    
        try {
    
    
            Thread.sleep(new Random(1).nextInt(100));
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        return "青云先生,100001,深圳";
    }

    public String register(String userName) {
    
    
        try {
    
    
            Thread.sleep(new Random(1).nextInt(100));
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        return "注册用户:" + userName + " success!";
    }

}

在 UserService 中提供了2个不同方法,另外你还可以增加新的类来加入测试。后面我们的测试过程,会给这个两个方法添加我们的拦截处理,打印方法执行耗时。

自定义拦截方法

public class UserServiceBeforeAdvice implements MethodBeforeAdvice {
    
    

    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
    
    
        System.out.println("拦截方法:" + method.getName());
    }

}

与上一章节的拦截方法相比,我们不在是实现 MethodInterceptor 接口,而是实现 MethodBeforeAdvice 环绕拦截。在这个方法中我们可以获取到方法的一些信息,如果还开发了它的 MethodAfterAdvice 则可以两个接口一起实现。

spring.xml 配置 AOP

<?xml version="1.0" encoding="UTF-8"?>
<beans>

    <!--要被切入的类,最终会为其创建一个代理类,方法的调用都是通过代理类来完成的-->
    <bean id="userService" class="bean.UserService"/>

    <!--用于创建代理类的类-->
    <bean class="com.qingyun.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>

    <!--增强的内容-->
    <bean id="beforeAdvice" class="bean.UserServiceBeforeAdvice"/>

    <!--拦截器,决定如何增强-->
    <bean id="methodInterceptor" class="com.qingyun.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor">
        <property name="advice" ref="beforeAdvice"/>
    </bean>

    <!--切面,有切点和拦截器-->
    <bean id="pointcutAdvisor" class="com.qingyun.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor">
        <property name="expression" value="execution(* bean.IUserService.*(..))"/>
        <property name="advice" ref="methodInterceptor"/>
    </bean>

</beans>

这回再使用 AOP 就可以像 Spring 中一样,通过在 xml 中配置即可。因为我们已经把 AOP 的功能融合到 Bean 的生命周期里去了,你的新增拦截方法都会被自动处理。

单元测试

@Test
public void test_aop() {
    
    
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");
    IUserService userService = applicationContext.getBean("userService", IUserService.class);
    System.out.println("测试结果:" + userService.queryUserInfo());
}

结果

在这里插入图片描述
可以发现对目标类拦截成功并进行了增强,事实上调用方法的过程是通过代理类来完成的(切点方法才增强,否则不增强)!

项目代码Github地址:https://github.com/Zhang-Qing-Yun/mini-spring,本节代码对应的commit标识为074ba27

欢迎标星

猜你喜欢

转载自blog.csdn.net/zhang_qing_yun/article/details/119894735