手写Spring AOP,快来瞧一瞧看一看撒!


目录

  • AOP分析
  • Advice实现
    • 定义Advice接口
    • 定义前置、后置、环绕和异常增强接口
  • Pointcut实现
    • 定义PointCut接口
    • 定义正则表达式的实现类:RegExpressionPointcut
    • 定义AspectJ切点表达式的实现类:AspectJExpressionPointcut
  • Aspact实现
    • 定义Advisor接口
    • 定义PointcutAdvisor接口
    • 定义AspectJPointcutAdvisor实现类
  • Weaving实现
    • 定义观察者接口BeanPostProcessor
    • 定义通知接口Aware
    • 定义通知者注册接口AdvisorRegistry
    • 定义增强处理的观察者实现类AdvisorAutoProxyCreator
    • 注册BeanPostProcessor
    • 定义AopProxy代理接口
    • JDK动态代理实现
    • CGLIB动态代理实现
    • 责任链AopAdviceChainInvocation类
    • 定义AopProxyFactory接口
  • 测试一下


手写Spring AOP,快来瞧一瞧看一看撒!_Spring AOP


上面两篇内容说到了手写IOC和DI,本篇内容就是手写AOP,由于内容中使用到了上面两篇内容写的代码,所以此处贴下链接:

手写Spring IOC容器:点击进入

手写Spring DI依赖注入:点击进入

AOP分析

AOP是什么

Aspect Oriented Programming 面向切面编程,在不改变类的代码的情况下,对类的方法进行功能增强

那么如果要实现一个AOP,需要做的事情就是要向使用的用户提供AOP功能,能够通过AOP技术实现对类的方法进行功能增强

AOP中的元素

  • Advice 通知,即增强的功能
  • Join points 连接点,可以选择的方法点,即有哪些可以增强的方法供选择
  • Pointcut 切入点,选择切入的方法点,即对哪些方法进行增强
  • Aspact 切面,选择的多个方法点 + 增强的功能,即Advice和Pointcut的组合
  • Introduction 引入,添加新的方法、属性到已经存在的类中,叫做引入
  • Weaving 织入,即不改变原来的代码,加入增强的功能

手写AOP的话,上面几个元素有的是需要用户提供的:Advice、Pointcut、Aspact,用户需要提供增强的功能和切入点、切面信息;AOP框架需要提供Join points、Weaving

AOP提供的功能

  • 需要功能的增强,即Advice通知
  • 对类的方法进行增强,那需要可以选择的要增强的方法点,即Pointcut切入点
  • 需要在不改变原类代码的情况下,进行功能的增强,即Weaving织入

图解AOP
手写Spring AOP,快来瞧一瞧看一看撒!_Spring AOP_02
假设我们现在玩一局王者荣耀,选择了一个英雄鲁班,这个鲁班需要参与整个游戏直到结束,不管是否挂机;刚刚开始的时候,没有任何装备,假设鲁班身上的各个部位是Join points,即这些部位需要装备去增强它,那每一个装备就是Advice增强的功能,比如增加攻速、生命等,现在买了一个鞋子,那么就是选择在速度上进行增强,即穿在脚上,那么就是Pointcut,那么这个鞋子加上脚就是Aspact切面,上面说的例子可能不够好,见谅啊!

特点分析

  1. Advice

实现一个Advice,就需要知道Advice有哪些特点,从上面可以知道Advice是用户来提供的,所以有很多不可控的因素

  • 用户性:由用户来提供增强的功能,即增强功能的代码是用户来进行编写的
  • 多变性:既然用户来提供,那么对于不同的需求,增强的逻辑都是不一样的
  • 可选时机:用户可以选择在方法的前面、后面、异常时进行功能增强
  • 多重性:同一个切入点,即同一个方法,可以有多重的增强,不止增强一次
  1. Pointcut

切入点,通过上面知道它也是用户提供的,所以它的特点和Advice基本上差不多

  • 用户性:由用户来指定,对哪些方法进行功能增强
  • 多变性:用户可以灵活的来指定
  • 多点性:用户可以选择在多个方法点上进行功能增强
  1. Weaving

织入,这部分代码,是需要框架提供的,即是需要我们自己去实现的逻辑,通过这个可以把增强的功能加入到用户指定的方法上面

  • 无侵入性:不改变原来类的代码,去实现功能的增强
  • 需要我们在AOP框架中自己实现逻辑

通过上面的内容,可以知道,需要做的事情就是Advice、Pointcut、Weaving三个部分,Join points为什么不需要去实现呢,这部分内容在编写代码的过程中就可以知道了

Advice实现

Advice是由用户来实现的,这部分逻辑需要用户写好然后我们实现AOP的时候来进行使用;我们需要认识用户的东西,用户需要使用我们写的框架,而且还需要隔绝用户的多变性

那么需要做的事情是啥呢,可以定义一个标准的接口,用户通过实现接口来提供不同的增强逻辑,这就是应对变化的方式,面向接口编程

定义Advice接口

定义的Advice是一个顶级接口,不需要写任何的方法,然后根据前置、后置、环绕、异常增强,来去实现Advice接口,那么这些增强需要的参数是一样的吗,请往下看

Advice接口:

/**
 * @className: Advice
 * @description: 通知的标准接口
 * @author TR
 */
public interface Advice {

}

首先,我们知道,增强是对方法进行增强,那么使用Advice的时候,需要给的就是方法的一些信息

  1. 前置增强

在方法执行前进行增强,那么可以知道前置增强是不需要返回值的,需要的参数如下:

  • 方法自身 Method
  • 方法的参数 Object[]
  • 方法所在的类(对象) Object
  1. 后置增强

在方法执行后进行增强,那需要的参数是不是需要增加一个啊,即需要方法的返回值,因为我如果需要对返回值做一下处理,就需要用到它,而且后置增强也是不需要返回值的,需要的参数如下:

  • 方法自身 Method
  • 方法的参数 Object[]
  • 方法所在的类(对象) Object
  • 方法的返回值 Object
  1. 环绕增强

包裹方法进行增强,它是需要包裹整个方法,即方法由它来执行,那么环绕增强是需要返回值的,这个返回值是需要增强方法的返回值,需要的参数如下:

  • 方法自身 Method
  • 方法的参数 Object[]
  • 方法所在的类(对象) Object
  1. 异常增强

捕获方法执行时的异常信息,然后进行增强,而且它也是需要包裹方法进行增强的,即它可以在环绕增强中来实现

通过上面知道,需要定义三个方法:前置增强的方法、后置增强的方法、环绕和异常增强的方法,那这三个方法是定义在一个接口里面,还是分三个接口呢,根据单一职责原则,还是分三个接口来实现比较好,而且还可以根据不同的接口类型来区分是哪个Advice

定义前置、后置、环绕和异常增强接口

定义前置增强接口MethodBeforeAdvice:

/**
 * @className: MethodBeforeAdvice
 * @description: 前置增强接口
 * @author TR
 */
public interface MethodBeforeAdvice extends Advice {

    /**
     * 前置增强方法
     * @param method: 将要被执行的方法
     * @param target: 执行方法的目标对象
     * @param args: 执行方法的参数
     * @return: void
     **/
    void before(Method method, Object target, Object[] args);
}

定义后置增强接口AfterReturningAdvice:

/**
 * @className: AfterReturningAdvice
 * @description: 后置增强接口
 * @author TR
 */
public interface AfterReturningAdvice extends Advice {
    
    /**
     * 后置增强方法
     * @param method: 将要被执行的方法
     * @param target: 执行方法的目标对象
     * @param args: 执行方法的参数
     * @param returnValue: 执行方法的返回值
     * @return: void
     **/
    void after(Method method, Object target, Object[] args, Object returnValue);
}

定义环绕、异常增强接口MethodInterceptor:

/**
 * @className: MethodInterceptor
 * @description: 环绕、异常增强接口
 * @author TR
 */
public interface MethodInterceptor extends Advice {

    /**
     * 环绕、异常增强方法,在方法实现中需要调用目标方法
     * @param method: 将要被执行的方法
     * @param target: 执行方法的目标对象
     * @param args: 执行方法的参数
     * @return: java.lang.Object 执行方法的返回值
     * @throws Throwable
     **/
    Object invoke(Method method, Object target, Object[] args) throws Throwable;
}

类图如下:
手写Spring AOP,快来瞧一瞧看一看撒!_Spring AOP_03

Pointcut实现

Pointcut的特点:

  • 用户性:由用户来指定,对哪些方法进行功能增强
  • 多变性:用户可以灵活的来指定
  • 多点性:用户可以选择在多个方法点上进行功能增强

我们需要给用户提供一个东西,让用户可以灵活的来指定方法点,而且我们获取到的时候又能够知道,用户对哪些方法点进行了指定

指定对哪些方法进行增强,指定的信息是什么,其实就是一个或者多个方法,而且如果有重载存在呢,所以这个指定的东西,就是一个完整的方法签名

那该怎么做到灵活性、多点性呢,这个指定的信息是可以描述一类方法的,比如:

  • 某个包下某个类的某个方法
  • 某个包下某个类的所有方法
  • 某个包下所有类中以get开头的方法
  • 某个包下所有类中以get开头以sevice结尾的方法
  • 某个包下及其子包下所有类中以get开头以sevice结尾的方法

那么可以定义一个表达式,来去描述上面的信息,这个描述的信息是包名.类名.方法名(参数类型)

每一部分是如何要求的呢?

  • 包名:需要有父子特点,可以模糊匹配
  • 类名:可以模糊匹配
  • 方法名:可以模糊匹配
  • 参数类型:可以有多个

定义的表达式是需要来判断:是否需要对某个类的某个方法进行增强,那么需要去匹配类和匹配方法

匹配的表达式:

  • 正则表达式
  • AspactJ表达式

AspactJ表达式学习

/**
 * @className: AspectJTest
 * @description: AspectJ测试类
 */
public class AspectJTest {

    public static void main(String[] args) throws NoSuchMethodException {
        //获得切点解析器
        PointcutParser pp = PointcutParser
                .getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution();
        //切点解析器根据规则,解析出一个类型匹配器
        TypePatternMatcher tp = pp.parseTypePattern("di.*");
        //根据表达式生成一个切点表达式
        PointcutExpression pe = pp.parsePointcutExpression("execution(* demo.beans.BeanFactory.get*(..))");

        //匹配MagicGril的getName方法
        Class<?> cl =  MagicGril.class;
        Method method = cl.getMethod("getName", null);
        //匹配方法执行
        ShadowMatch sm = pe.matchesMethodExecution(method);
        System.out.println("是否匹配到方法:" + sm.alwaysMatches());
        System.out.println(cl.getName() + ",是否匹配表达式:" + pe.couldMatchJoinPointsInType(cl));
        System.out.println(DefaultBeanFactory.class + ",是否匹配表达式:" + pe.couldMatchJoinPointsInType(DefaultBeanFactory.class));

        System.out.println(cl.getName() +"类下的所有方法:");
        for (Method method1 : cl.getMethods()) {
            System.out.println(method1.getName());
        }
    }
}

输出结果:
手写Spring AOP,快来瞧一瞧看一看撒!_Spring AOP_04
通过上面的描述,可以知道,切点该怎么去设计了

切点需要有的属性就是切点表达式,需要提供的功能就是匹配类和匹配方法

而且这里也可以像Advice一样定义一个顶级接口,因为如果以后有其他的表达式更加好用的话,需要扩展,那么只需要继承定义的这个顶级接口就行了,不管它内部如何实现,都要去实现我们定义的匹配类和匹配方法的行为

定义PointCut接口

/**
 * @className: PointCut
 * @description: 切点匹配接口
 * @author: TR
 */
public interface PointCut {

    /**
     * 匹配类
     * @author: jinpeng.sun
     * @date: 2021/4/19 13:46
     * @param targetClass: 匹配的目标类
     * @return: boolean
     **/
     boolean matchClass(Class<?> targetClass);

     /**
      * 匹配方法
      * @author: jinpeng.sun
      * @date: 2021/4/19 13:46
      * @param method: 匹配的目标方法
      * @param targetClass: 匹配的目标类
      * @return: boolean
      **/
     boolean matchMethod(Method method, Class<?> targetClass);
}

定义正则表达式的实现类:RegExpressionPointcut

此处不实现,主要使用AspactJ的方式

/**
 * @className: RegExpressionPointcut
 * @description: 正则表达式实现类
 * @author: TR
 */
public class RegExpressionPointcut implements PointCut {

    @Override
    public boolean matchClass(Class<?> targetClass) {
        return false;
    }

    @Override
    public boolean matchMethod(Method method, Class<?> targetClass) {
        return false;
    }
}

定义AspectJ切点表达式的实现类:AspectJExpressionPointcut

/**
 * @className: AspectJExpressionPointcut
 * @description: AspectJ切点表达式实现类
 * @author: TR
 */
public class AspectJExpressionPointcut implements PointCut {

    /** 获得切点解析器 */
    PointcutParser pp = PointcutParser
            .getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution();

    /** 切点表达式的字符串形式 */
    private String expression;

    /** AspectJ中的切点表达式 */
    private PointcutExpression pe;

    public AspectJExpressionPointcut(String expression) {
        super();
        this.expression = expression;
        pe = pp.parsePointcutExpression(expression);
    }


    @Override
    public boolean matchClass(Class<?> targetClass) {
        return pe.couldMatchJoinPointsInType(targetClass);
    }

    @Override
    public boolean matchMethod(Method method, Class<?> targetClass) {
        ShadowMatch sm = pe.matchesMethodExecution(method);
        return sm.alwaysMatches();
    }

    public String getExpression() {
        return expression;
    }
}

实现步骤:

  1. maven引入Aspectj的jar
 <dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.6</version>
 </dependency>
  1. 获得切点解析器
 PointcutParser pp = PointcutParser
        .getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution();
  1. 解析切点表达式,获得PointcutExpression
PointcutExpression pe = pp.parsePointcutExpression(
				"execution(* edu.dongnao.courseware.beans.BeanFactory.get*(..))");
  1. 使用PointcutExpression匹配类,不可靠
pe.couldMatchJoinPointsInType(targetClass)
  1. 使用PointcutExpression匹配方法,可靠
ShadowMatch sm = pe.matchesMethodExecution(method);
return sm.alwaysMatches();

类图如下:
手写Spring AOP,快来瞧一瞧看一看撒!_Spring AOP_05

Aspact实现

上面实现了Advice和Pointcut,那么用户该如何使用呢?

Advice是用户提供的功能增强实现,是需要继承接口的,那么可以把Advice配置成一个bean,Pointcut是切点表达式,是用来匹配类和方法的,是不需要配置成bean的,只需要提供一个字符串形式的表达式就行了,那么adviceBeanName+expression就组成了切面

下面通过外观模式来实现切面,把Advice和Pointcut组合起来

定义Advisor接口

/**
 * @className: Advisor
 * @description: 构建切面的接口,组合advice和pointcut
 * @author: TR
 */
public interface Advisor {

    /**
     * 获取通知bean的名称
     * @return: java.lang.String
     **/
    String getAdviceBeanName();

    /**
     * 获取切点表达式
     * @return: java.lang.String
     **/
    String getExpression();
}

定义PointcutAdvisor接口

/**
 * @className: PointcutAdvisor
 * @description: 切点通知者,继承自Advisor,扩展了pointcut
 * @author: TR
 */
public interface PointcutAdvisor extends Advisor {

    /**
     * 获取切点
     * @return: PointCut
     **/
    PointCut getPointCut();
}

定义AspectJPointcutAdvisor实现类

/**
 * @className: AspectJPointcutAdvisor
 * @description: AspectJ切点表达式的通知者实现类
 * @author: TR
 */
public class AspectJPointcutAdvisor implements PointcutAdvisor {

    /** 通知bean的名称 */
    private String adviceBeanName;

    /** 表达式 */
    private String expression;

    /** 切点 */
    private PointCut pointCut;

    public AspectJPointcutAdvisor(String adviceBeanName, String expression) {
        this.adviceBeanName = adviceBeanName;
        this.expression = expression;
        this.pointCut = new AspectJExpressionPointcut(expression);
    }

    @Override
    public PointCut getPointCut() {
        return pointCut;
    }

    @Override
    public String getAdviceBeanName() {
        return adviceBeanName;
    }

    @Override
    public String getExpression() {
        return expression;
    }
}

下面的类不予实现,主要使用AspactJ的形式

/**
 * @className: RegPointcutAdvisor
 * @description: 正则切点表达式的通知者实现类
 * @author: TR
 */
public class RegPointcutAdvisor implements PointcutAdvisor {

    /** 通知bean的名称 */
    private String adviceBeanName;

    /** 表达式 */
    private String expression;

    /** 切点 */
    private PointCut pointCut;

    public RegPointcutAdvisor(String adviceBeanName, String expression) {
        this.adviceBeanName = adviceBeanName;
        this.expression = expression;
        this.pointCut = new RegExpressionPointcut();
    }

    @Override
    public PointCut getPointCut() {
        return null;
    }

    @Override
    public String getAdviceBeanName() {
        return null;
    }

    @Override
    public String getExpression() {
        return null;
    }
}

类图如下:
手写Spring AOP,快来瞧一瞧看一看撒!_Spring AOP_06

从上面的图中看到了,如果再扩展其他的切点表达式,那么在实现类中,可以看到,有很多的重复代码,那么是不是还可以优化一下呢,可以在实现类的上层再加一个抽象类,来继承PointcutAdvisor,然后主要的切点实现继承这个抽象类就行了

Weaving实现

要完成的事情

将用户提供的增强功能加入到指定的方法上,需要我们来实现

什么时候做织入

在创建Bean实例的时候,在Bean初始化完成后,再进行织入

怎么知道Bean要进行增强

需要遍历Bean类及其所有的方法,然后依次的去匹配用户指定的切面,如果存在匹配的切面,就是需要增强了

怎么织入呢,就是需要用到代理了!
手写Spring AOP,快来瞧一瞧看一看撒!_Spring AOP_07
用户需要去注册切面,我们还需要实现判断匹配、织入的的逻辑,这部分代码改如何写,需要写到哪里呢

现在要做的事情,就是在Bean创建的过程中加一项处理,后面可能会在Bean的创建过程中加入更多的处理,如果这部分代码都写在BeanFactory中,那么这个类是不是就会有特别多的代码,而且后面不方便扩展

看下Bean的创建过程:
手写Spring AOP,快来瞧一瞧看一看撒!_Spring AOP_08
上面图中,每个箭头都是加的扩展点,后面可能存在的是,需要在这些点上加入更多的处理逻辑,那么就需要设计一种方式,在不改变BeanFactory的情况下,能灵活的扩展,那么可以使用观察者模式,有几个扩展点,就是有几个主题,六个观察者
手写Spring AOP,快来瞧一瞧看一看撒!_Spring AOP_09

定义观察者接口BeanPostProcessor

/**
 * @className: BeanPostProcessor
 * @description: 后置处理器,Bean实例化完成后及依赖注入完成后触发
 * @author: TR
 */
public interface BeanPostProcessor {

    /**
     * bean初始化前的处理
     * @author: TR
     * @param bean: bean实例
     * @param beanName: bean名称
     * @return: java.lang.Object
     **/
    default Object postProcessBeforeInitialization(Object bean, String beanName) throws Exception {
       return bean;
    }

    /**
     * bean初始化后的处理
     * @author: TR
     * @param bean: bean实例
     * @param beanName: bean名称
     * @return: java.lang.Object
     **/
    default Object postProcessAfterInitialization(Object bean, String beanName) throws Exception {
        return bean;
    }
}

定义通知接口Aware

/**
 * @className: Aware
 * @description: 构建通知接口
 * @date: 2021/4/19 16:38
 * @author: jinpeng.sun
 */
public interface Aware {
}
/**
 * @className: BeanFactoryAware
 * @description: bean工厂构建通知接口
 * @author: TR
 */
public interface BeanFactoryAware extends Aware {

    /**
     * 接口实现者获得bean工厂方法
     * @author: TR
     * @param bf:
     * @return: void
     **/
    void setBeanFactory(BeanFactory bf);
}

上面的接口主要用来获得Bean工厂信息

定义通知者注册接口AdvisorRegistry

这个接口主要用来把Advisor注册到增强功能的实现类里面

/**
 * @className: AdvisorRegistry
 * @description: 通知者注册接口
 * @author: TR
 */
public interface AdvisorRegistry {

    /**
     * 注册通知者
     * @author: TR
     * @param advisor:
     * @return: void
     **/
    public void registerAdvisor(Advisor advisor);

    /**
     * 获得通知者列表
     * @author: TR
     * @date: 2021/4/19 16:45
     *
     * @return: java.util.List<demo.aop.advisor.Advisor>
     **/
    public List<Advisor> getAdvisors();
}

定义增强处理的观察者实现类AdvisorAutoProxyCreator

/**
 * @className: AdvisorAutoProxyCreator
 * @description: 功能增强的实现类,用户和框架交互的核心类
 * 用户通过Advisor提供切面,向DefaultBeanFactory注入该实现
 * 框架内部:DefaultBeanFactory注入ioc容器
 * DefaultBeanFactory调用BeanPostProcessor接口相关方法,进行功能增强
 * @author: TR
 */
public class AdvisorAutoProxyCreator implements BeanPostProcessor, BeanFactoryAware, AdvisorRegistry {

    /** 通知者列表 */
    private List<Advisor> advisors;

    /** 当前的bean */
    private BeanFactory beanFactory;

    public AdvisorAutoProxyCreator() {
        this.advisors = new ArrayList<>();
    }

    @Override
    public void registerAdvisor(Advisor advisor) {
        this.advisors.add(advisor);
    }

    @Override
    public List<Advisor> getAdvisors() {
        return this.advisors;
    }

    @Override
    public void setBeanFactory(BeanFactory bf) {
        this.beanFactory = bf;
    }

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

注册BeanPostProcessor

BeanFactory接口中增加注册BeanPostProcessor的方法

    /** 注册BeanPostProcessor */
    void registerBeanPostProcessor(BeanPostProcessor beanPostProcessor);

DefaultBeanFactory实现类中增加如下方法:

    private List<BeanPostProcessor> postProcessors = Collections.synchronizedList(new ArrayList<>());

    @Override
    public void registerBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
        postProcessors.add(beanPostProcessor);
        if (beanPostProcessor instanceof BeanFactoryAware) {
            ((BeanFactoryAware) beanPostProcessor).setBeanFactory(this);
        }
    }

getBean方法中增加bean初始化前后的处理:

 @Override
    public Object getBean(String beanName) throws Exception {
        //获取bean定义
        BeanDefinition bd = beanDefinitionMap.get(beanName);
        Object bean = doGetBean(beanName);

        //属性注入
        setPropertyDIValues(bd, bean);

        //bean初始化前的处理
        bean = this.applyPostProcessBeforeInitialization(bean, beanName);

        //bean的生命周期
        if (StringUtils.isNotBlank(bd.getInitMethodName())) {
            doInitMethod(bean,bd);
        }

        //bean初始化后的处理
        bean = this.applyPostProcessAfterInitialization(bean, beanName);

        return bean;
    }
    
    /**
     * bean初始化后的处理
     * @author: TR 
     * @param bean: 
     * @param beanName: 
     * @return: java.lang.Object
     **/
    private Object applyPostProcessAfterInitialization(Object bean, String beanName) throws Exception {
        for (BeanPostProcessor bp : this.postProcessors) {
            bean = bp.postProcessBeforeInitialization(bean, beanName);
        }
        return bean;
    }

    /**
     * bean初始化前的处理
     * @author: TR 
     * @param bean: 
     * @param beanName: 
     * @return: java.lang.Object
     **/
    private Object applyPostProcessBeforeInitialization(Object bean, String beanName) throws Exception {
        for (BeanPostProcessor bp : this.postProcessors) {
            bean = bp.postProcessAfterInitialization(bean, beanName);
        }
        return bean;
    }

下面需要实现的就是判断bean是否需要增强了,那么需要获取到bean的所有方法,然后根据注册进来的advisors去遍历它,然后得到切点去匹配类和方法

修改AdvisorAutoProxyCreator类中的postProcessAfterInitialization方法:

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws Exception {
        //判断是否需要进行切面增强,及获得增强的通知实现
        List<Advisor> advisors = getMatchedAdvisors(bean, beanName);
        //如果需要增强,返回增强后的对象
        if (CollectionUtils.isNotEmpty(advisors)) {
            //使用代理模式进行功能增强
        }
        return bean;
    }

下面是获取匹配的方法代码:

    /**
     * 获取匹配的方法
     * @author: TR
     * @param bean: bean实例
     * @param beanName: bean名称
     * @return: void
     **/
    private List<Advisor> getMatchedAdvisors(Object bean, String beanName) {
        if (CollectionUtils.isEmpty(advisors)) {
            return null;
        }
        //得到类
        Class<?> beanClass = bean.getClass();
        //得到所有的方法
        List<Method> allMethods = getAllMethodForClass(beanClass);

        //存放匹配的Advisor
        List<Advisor> advisors = new ArrayList<>();
        for (Advisor ad : this.advisors) {
            if (ad instanceof PointcutAdvisor) {
                //判断是否匹配
                if (isPointcutMatchBean((PointcutAdvisor) ad, beanClass, allMethods)) {
                    advisors.add(ad);
                }
            }
        }
        return advisors;
    }

获取所有的方法,使用的是Spring framework提供的工具类,来找到类下面所有的方法:

    /**
     * 获取所有的方法
     * @author: TR
     * @param beanClass:
     * @return: void
     **/
    private List<Method> getAllMethodForClass(Class<?> beanClass) {
        List<Method> allMethods = new LinkedList<>();
        Set<Class<?>> classes = new LinkedHashSet<>(ClassUtils.getAllInterfacesForClassAsSet(beanClass));
        classes.add(beanClass);
        for (Class<?> cc : classes) {
            //根据工具类获取所有的方法
            Method[] methods = ReflectionUtils.getAllDeclaredMethods(cc);
            for (Method m : methods) {
              allMethods.add(m);
            }
        }
        return  allMethods;
    }

下面是判断是否有匹配切点规则的方法,使用PointCut中定义的方法来实现:

   /**
     * 判断是否有匹配切点规则的方法
     * @author: TR
     * @param ad: 切面
     * @param beanClass: 指定的类
     * @param allMethods: 指定的类下面所有的方法
     * @return: void
     **/
    private boolean isPointcutMatchBean(PointcutAdvisor ad, Class<?> beanClass, List<Method> allMethods) {
        PointCut p = ad.getPointCut();

        //匹配类
        if (!p.matchClass(beanClass)) {
            return false;
        }
        for (Method m : allMethods) {
            //匹配方法
            if (p.matchMethod(m, beanClass)) {
                return true;
            }
        }
        return false;
    }

判断是否增强之后,就是需要进行代理增强了,那么这里的实现逻辑又是啥样的呢
手写Spring AOP,快来瞧一瞧看一看撒!_Spring AOP_10
通过上图可以看到,需要判断代理的方式,即使用JDK动态代理还是CGLIB动态代码,那么这里也可以抽象出一个接口。然后不同的代理方式分别去实现

定义AopProxy代理接口

/**
 * @className: AopProxy
 * @description: AOP代理接口,用来创建和获取代理对象
 * @author: TR
 */
public interface AopProxy {

    Object getProxy();

    Object getProxy(ClassLoader classLoader);
}

JDK动态代理实现

我们知道JDK动态代理是对接口进行的,那么在实现中需要哪些数据呢?

  • 要实现的接口
  • 目标对象
  • 匹配的Advisors
  • BeanFactory

需要的参数:

  • 需要实现的接口
  • InvocationHandler
/**
 * @className: JdkDynamicAopProxy
 * @description: JDK动态代理实现
 * @author: TR
 */
public class JdkDynamicAopProxy implements AopProxy, InvocationHandler {
    private static final Logger logger = LoggerFactory.getLogger(JdkDynamicAopProxy.class);

    //bean名称
    private String beanName;
    //bean对象,需要被代理的对象
    private Object target;
    //通知列表,需要被增强的功能
    private List<Advisor> advisors;
    //当前的bean
    private BeanFactory beanFactory;

    public JdkDynamicAopProxy(String beanName, Object target, List<Advisor> advisors, BeanFactory beanFactory) {
        this.beanName = beanName;
        this.target = target;
        this.advisors = advisors;
        this.beanFactory = beanFactory;
    }

    @Override
    public Object getProxy() {
        return this.getProxy(target.getClass().getClassLoader());
    }

    @Override
    public Object getProxy(ClassLoader classLoader) {
        if (logger.isDebugEnabled()) {
            logger.debug("为" + target + "创建JDK代理。");
        }
        return Proxy.newProxyInstance(classLoader, target.getClass().getInterfaces(), this);
    }

    /**
     * InvocationHandler接口的实现
     * 用来进行代理增强后返回实际的结果
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return AopProxyUtils.applyAdvices(target, method, args, advisors, proxy, beanFactory);
    }
}

CGLIB动态代理实现

CGLIB动态代理可以对接口和类进行,那么它需要下面的数据:

  • 要继承的类
  • 要实现的接口
  • 目标对象
  • 匹配的Advisors
  • BeanFactory
  • 构造参数类
  • 构造参数

需要的参数:

  • 继承的类
  • 需要实现的接口
  • Callback
  • 构造参数类型
  • 构造参数

构造参数类型和构造参数在创建实例的时候会有的

/**
 * @className: CglibDynamicAopProxy
 * @description: Cglib动态代理
 * @author: TR
 */
public class CglibDynamicAopProxy implements AopProxy, MethodInterceptor {
    private static final Logger logger = LoggerFactory.getLogger(CglibDynamicAopProxy.class);
    private static Enhancer enhancer = new Enhancer();

    //bean名称
    private String beanName;
    //bean对象,需要被代理的对象
    private Object target;
    //通知列表,需要被增强的功能
    private List<Advisor> advisors;
    //当前的bean
    private BeanFactory beanFactory;

    public CglibDynamicAopProxy(String beanName, Object target, List<Advisor> advisors, BeanFactory beanFactory) {
        this.beanName = beanName;
        this.target = target;
        this.advisors = advisors;
        this.beanFactory = beanFactory;
    }

    @Override
    public Object getProxy() {
        return this.getProxy(target.getClass().getClassLoader());
    }

    @Override
    public Object getProxy(ClassLoader classLoader) {
        if (logger.isDebugEnabled()) {
            logger.debug("为" + target + "创建cglib代理。");
        }
        Class<?> superClass = this.target.getClass();
        enhancer.setSuperclass(superClass);
        enhancer.setInterfaces(this.getClass().getInterfaces());
        enhancer.setCallback(this);
        Constructor<?> constructor = null;

        try {
            constructor = superClass.getConstructor(new Class<?>[] {});
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        if (constructor != null) {
            return enhancer.create();
        } else {
            BeanDefinition bd = ((DefaultBeanFactory)beanFactory).getBeanDefinition(beanName);
            return enhancer.create(bd.getConstructor().getParameterTypes(), bd.getConstructorArgumentRealValues());
        }
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        return AopProxyUtils.applyAdvices(target, method, objects, advisors, o, beanFactory);
    }
}

AopProxyUtils它是用来干什么的呢,从上面可以看到,这部分的处理,主要是用来实现增强的,都是使用Advice来增强的,所以增强的逻辑是一样的
手写Spring AOP,快来瞧一瞧看一看撒!_Spring AOP_11

/**
 * @className: AopProxyUtils
 * @description: aop代理工具类
 * @author: TR
 */
public class AopProxyUtils {


    /**
     * 对方法应用advice增强,获得最终返回结果
     * @author: TR
     * @param target: 需要被增强的对象
     * @param method: 需要被增强的方法
     * @param args: 增强方法的参数
     * @param advisors: 匹配到的切面
     * @param proxy: bean对象功能增强后的代理对象
     * @param beanFactory: bean工厂
     * @return: java.lang.Object
     **/
    public static Object applyAdvices(Object target, Method method, Object[] args, List<Advisor> advisors,
                                      Object proxy, BeanFactory beanFactory) throws Throwable {
        //获取对当前方法进行增强的advices
        List<Object> advices = AopProxyUtils.getShouldApplyAdvices(target.getClass(), method, advisors, beanFactory);
        if (CollectionUtils.isEmpty(advices)) {
            return method.invoke(target, args);
        } else {
            //使用责任链进行增强
            AopAdviceChainInvocation ac = new AopAdviceChainInvocation(proxy, target, method, args, advices);
            return ac.invoke();
        }
    }

    /**
     * 获得与方法匹配的Advice切面列表
     * @author: TR
     * @param aClass:
     * @param method:
     * @param advisors:
     * @param beanFactory:
     * @return: java.util.List<demo.aop.advice.Advice>
     **/
    private static List<Object> getShouldApplyAdvices(Class<?> aClass, Method method,
                                                      List<Advisor> advisors, BeanFactory beanFactory) throws Exception {
        if (CollectionUtils.isEmpty(advisors)) {
            return null;
        }
        List<Object> advices = new ArrayList<>();
        for (Advisor advisor : advisors) {
            if (advisor instanceof PointcutAdvisor) {
                if (((PointcutAdvisor)advisor).getPointCut().matchMethod(method, aClass)) {
                    advices.add(beanFactory.getBean(advisor.getAdviceBeanName()));
                }
            }
        }
        return advices;
    }
}

如何来传递创建bean实例时获得的数据到初始化后的Aop中,就是要在BeanDefinition中使用ThreadLocal持有参数值

BeanDefinition增加如下方法:

   /** 获取构造参数值 */
    public Object[] getConstructorArgumentRealValues();

    /** 设置构造参数值 */
    public void setConstructorArgumentRealValues(Object[] values);

GeneralBeanDefinition类中相应的实现:

   private ThreadLocal<Object[]> realConstructorArgumentValues = new ThreadLocal<>();

    @Override
    public Object[] getConstructorArgumentRealValues() {
        return realConstructorArgumentValues.get();
    }

    @Override
    public void setConstructorArgumentRealValues(Object[] values) {
       this.realConstructorArgumentValues.set(values);
    }

责任链AopAdviceChainInvocation类

/**
 * @className: AopAdviceChainInvocation
 * @description: AOP责任链调用类
 * @author: TR
 */
public class AopAdviceChainInvocation {

    /** AOP责任链执行的方法 */
    private static Method invokeMethod;

    static {
        try {
            invokeMethod = AopAdviceChainInvocation.class.getMethod("invoke", null);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }

    //代理类对象
    private Object proxy;
    //目标类对象
    private Object target;
    //调用执行的对象方法
    private Method method;
    //执行方法的参数
    private Object[] args;
    //方法被增强的功能:通知列表
    private List<Object> advices;

    public AopAdviceChainInvocation(Object proxy, Object target, Method method, Object[] args, List<Object> advices) {
          this.proxy = proxy;
          this.target = target;
          this.method = method;
          this.args = args;
          this.advices = advices;
    }

    //责任链执行记录的索引号
    private int i = 0;

    public Object invoke() throws Throwable {
        if (i < this.advices.size()) {
            Object advice = this.advices.get(i++);
            if (advice instanceof MethodBeforeAdvice) {
                //执行前增强
                ((MethodBeforeAdvice)advice).before(method, target, args);
            } else if (advice instanceof MethodInterceptor) {
                //执行环绕增强和异常增强,这里给的method和对象是invoke方法和链对象
                ((MethodInterceptor)advice).invoke(invokeMethod,this, null);
            } else if (advice instanceof AfterReturningAdvice) {
                //后置增强,先获得结果,在增强
                Object returnValue = this.invoke();
                ((AfterReturningAdvice)advice).after(method, target, args, returnValue);
                return returnValue;
            }
            //回调
            return this.invoke();
        } else {
            return method.invoke(target, args);
        }
    }
}

定义AopProxyFactory接口

代理的方式实现完了之后,就需要使用它了,这里使用工厂模式实现:

/**
 * @className: AopProxyFactory
 * @description: AOP代理的工厂接口
 * @author: TR
 */
public interface AopProxyFactory {

    /**
     * 根据参数获取AOP代理接口的实现
     * @param bean: 实例
     * @param beanName: bean名称
     * @param advisors: advisors列表
     * @param beanFactory: bean工厂
     * @return: AopProxyFactory
     **/
    AopProxy createAopProxy(Object bean, String beanName, List<Advisor> advisors, BeanFactory beanFactory);

    /**
     * 获取默认的AopProxyFactory
     * @return: AopProxyFactory
     **/
    static AopProxyFactory getDefaultAopProxyFactory() {
        return new DefaultAopProxyFactory();
    }
}

实现类:

/**
 * @className: DefaultAopProxyFactory
 * @description: 代理工厂实现类
 * @author: TR
 */
public class DefaultAopProxyFactory implements AopProxyFactory {

    @Override
    public AopProxy createAopProxy(Object bean, String beanName, List<Advisor> advisors, BeanFactory beanFactory) {
        //是用JDK动态代理还是CGLIB动态代理
        if (shouldUseJDKDynamicProxy(bean, beanName)) {
            return new JdkDynamicAopProxy(beanName, bean, advisors, beanFactory);
        } else {
            return new CglibDynamicAopProxy(beanName, bean, advisors, beanFactory);
        }
    }

    private boolean shouldUseJDKDynamicProxy(Object bean, String beanName) {
        return false;
    }
}

下面需要修改下AdvisorAutoProxyCreator类中的postProcessAfterInitialization方法:

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws Exception {
        //判断是否需要进行切面增强,及获得增强的通知实现
        List<Advisor> advisors = getMatchedAdvisors(bean, beanName);
        //如果需要增强,返回增强后的对象
        if (CollectionUtils.isNotEmpty(advisors)) {
            //使用代理模式进行功能增强
            bean = this.createProxy(bean, beanName, advisors);
        }
        return bean;
    }
    
    private Object createProxy(Object bean, String beanName, List<Advisor> advisors) {
        return AopProxyFactory.getDefaultAopProxyFactory()
                .createAopProxy(bean, beanName, advisors, beanFactory)
                .getProxy();
    }

DefaultBeanFactory类中缓存构造函数的方式需要改变一下

determineConstructor方法中缓存的代码注释掉:

if (ct != null) {
//            //对于原型bean,缓存起来
//            if (bd.isProtoType()) {
//                bd.setConstructor(ct);
//            }
            return ct;
        } else {
            throw new Exception("找不到对应的构造方法:" + bd);
        }

在createBeanByConstructor中增加代码

 /** 通过构造函数构建bean */
    private Object createBeanByConstructor(BeanDefinition bd) throws Exception {
        Object object = null;
        if (CollectionUtils.isEmpty(bd.getConstructorArgumentValues())) {
            //获得的构造参数值是空的,就不传参
            object = bd.getBeanClass().newInstance();
        } else {
            //获得的参数值是空的,就不传参
            Object[] args = getConstructorArgumentValues(bd);
            if (args == null) {
                object = bd.getBeanClass().newInstance();
            } else {
                // 根据参数匹配决定构造方法,然后进行实例化调用
                bd.setConstructorArgumentRealValues(args);
                Constructor<?> ct = this.determineConstructor(bd, args);
                // 缓存构造函数由determineConstructor 中移到了这里,无论原型否都缓存,因为后面AOP需要用
                bd.setConstructor(ct);
                return ct.newInstance(args);
            }
        }
        return object;
    }
测试一下

前置增强实现:

public class MyBeforeAdvice implements MethodBeforeAdvice {

    @Override
    public void before(Method method, Object target, Object[] args) {
        System.out.println(this + " 对 " + target + " 进行了前置增强!");
    }
}

后置增强实现:

public class MyAfterReturningAdvice implements AfterReturningAdvice {

    @Override
    public void after(Method method, Object target, Object[] args, Object returnValue) {
        System.out.println(this + " 对 " + target + " 做了后置增强,得到的返回值=" + returnValue);
    }
}

环绕增强实现:

public class MyMethodInterceptor implements MethodInterceptor {

    @Override
    public Object invoke(Method method, Object target, Object[] args) throws Throwable {
        System.out.println(this + "对 " + target + "进行了环绕--前增强");
        Object ret = method.invoke(target, args);
        System.out.println(this + "对 " + target + "进行了环绕 --后增强。方法的返回值为:" + ret);
        return ret;
    }
}

测试类:

public class AopTest {
    static PreBuildBeanFactory bf = new PreBuildBeanFactory();

    @Test
    public void testCirculationDI() throws Throwable {

        GeneralBeanDefinition bd = new GeneralBeanDefinition();
        bd.setBeanClass(Lad.class);
        List<Object> args = new ArrayList<>();
        args.add("孙悟空");
        args.add(new BeanReference("baigujing"));
        bd.setConstructorArgumentValues(args);
        bf.registerBeanDefinition("swk", bd);

        bd = new GeneralBeanDefinition();
        bd.setBeanClass(MagicGirl.class);
        args = new ArrayList<>();
        args.add("白骨精");
        bd.setConstructorArgumentValues(args);
        bf.registerBeanDefinition("baigujing", bd);

        bd = new GeneralBeanDefinition();
        bd.setBeanClass(Renminbi.class);
        bf.registerBeanDefinition("renminbi", bd);

        // 前置增强advice bean注册
        bd = new GeneralBeanDefinition();
        bd.setBeanClass(MyBeforeAdvice.class);
        bf.registerBeanDefinition("myBeforeAdvice", bd);

        // 环绕增强advice bean注册
        bd = new GeneralBeanDefinition();
        bd.setBeanClass(MyMethodInterceptor.class);
        bf.registerBeanDefinition("myMethodInterceptor", bd);

        // 后置增强advice bean注册
        bd = new GeneralBeanDefinition();
        bd.setBeanClass(MyAfterReturningAdvice.class);
        bf.registerBeanDefinition("myAfterReturningAdvice", bd);

        // 往BeanFactory中注册AOP的BeanPostProcessor
        AdvisorAutoProxyCreator aapc = new AdvisorAutoProxyCreator();
        bf.registerBeanPostProcessor(aapc);
        // 向AdvisorAutoProxyCreator注册Advisor
        aapc.registerAdvisor(
                new AspectJPointcutAdvisor("myBeforeAdvice", "execution(* demo.di.MagicGirl.*(..))"));
        // 向AdvisorAutoProxyCreator注册Advisor
        aapc.registerAdvisor(
                new AspectJPointcutAdvisor("myMethodInterceptor", "execution(* demo.di.Lad.say*(..))"));
        // 向AdvisorAutoProxyCreator注册Advisor
        aapc.registerAdvisor(new AspectJPointcutAdvisor("myAfterReturningAdvice",
                "execution(* demo.di.Renminbi.*(..))"));

        bf.preInstantiateSingletons();

        System.out.println("-----------------myBeforeAdvice---------------");
        MagicGirl gril = (MagicGirl) bf.getBean("baigujing");
        gril.getFriend();
        gril.getName();

        System.out.println("----------------myMethodInterceptor----------------");
        Boy boy = (Boy) bf.getBean("swk");
        boy.sayLove();

        System.out.println("-----------------myAfterReturningAdvice---------------");
        Renminbi rmb = (Renminbi) bf.getBean("renminbi");
        rmb.pay();
    }
}

运行结果:
手写Spring AOP,快来瞧一瞧看一看撒!_Spring AOP_12
手写Spring AOP,快来瞧一瞧看一看撒!_Spring AOP_13
手写Spring AOP,快来瞧一瞧看一看撒!_Spring AOP_14
本文其他知识点:

  • 由一名保安引发的Java设计模式:外观模式
  • 什么?女神发了朋友圈,快来围观之Java设计模式:观察者模式
  • 击鼓传花联想到了Java设计模式:责任链模式
  • 女娲造人引发思考之Java设计模式:工厂模式
  • Tony老师带你来看Java设计模式:代理模式