徒手撸框架--实现Aop

原文地址:犀利豆的博客java

上一讲咱们讲解了Spring 的 IoC 实现。你们能够去个人博客查看点击连接,这一讲咱们继续说说 Spring 的另一个重要特性 AOP。以前在看过的大部分教程,对于Spring Aop的实现讲解的都不太透彻,大部分文章介绍了Spring Aop的底层技术使用了动态代理,至于Spring Aop的具体实现都语焉不详。这类文章看之后之后,我脑子里浮现的就是这样一个画面:git

个人想法就是,带领你们,首先梳理 Spring Aop的实现,而后屏蔽细节,本身实现一个Aop框架。加深对Spring Aop的理解。在了解上图1-4步骤的同时,补充 4 到 5 步骤之间的其余细节。github

读完这篇文章你将会了解:算法

  • Aop是什么?
  • 为何要使用Aop?
  • Spirng 实现Aop的思路是什么
  • 本身根据Spring 思想实现一个 Aop框架

Aop 是什么?

面向切面的程序设计(aspect-oriented programming,AOP)。经过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。spring

为何须要使用Aop?

面向切面编程,实际上就是经过预编译或者动态代理技术在不修改源代码的状况下给原来的程序统一添加功能的一种技术。咱们看几个关键词,第一个是“动态代理技术”,这个就是Spring Aop实现底层技术。第二个“不修改源代码”,这个就是Aop最关键的地方,也就是咱们平时所说的非入侵性。。第三个“添加功能”,不改变原有的源代码,为程序添加功能。shell

举个例子:若是某天你须要统计若干方法的执行时间,若是不是用Aop技术,你要作的就是为每个方法开始的时候获取一个开始时间,在方法结束的时候获取结束时间。两者之差就是方法的执行时间。若是对每个须要统计的方法都作如上的操做,那代码简直就是灾难。若是咱们使用Aop技术,在不修改代码的状况下,添加一个统计方法执行时间的切面。代码就变得十分优雅。具体这个切面怎么实现?看完下面的文章你必定就会知道。编程

Spring Aop 是怎么实现的?

所谓:json

计算机程序 = 数据结构 + 算法设计模式

在阅读过Spring源码以后,你就会对这个说法理解更深刻了。数据结构

Spring Aop实现的代码很是很是的绕。也就是说 Spring 为了灵活作了很是深层次的抽象。同时 Spring为了兼容 @AspectJ 的Aop协议,使用了不少 Adapter (适配器)模式又进一步的增长了代码的复杂程度。 Spring 的 Aop 实现主要如下几个步骤:

  1. 初始化 Aop 容器。
  2. 读取配置文件。
  3. 将配置文件装换为 Aop 可以识别的数据结构 -- Advisor。这里展开讲一讲这个advisor。Advisor对象中包又含了两个重要的数据结构,一个是 Advice,一个是 PointcutAdvice的做用就是描述一个切面的行为,pointcut描述的是切面的位置。两个数据结的组合就是”在哪里,干什么“。这样 Advisor 就包含了”在哪里干什么“的信息,就可以全面的描述切面了。
  4. Spring 将这个 Advisor 转换成本身可以识别的数据结构 -- AdvicedSupport。Spirng 动态的将这些方法拦截器织入到对应的方法。
  5. 生成动态代理代理。
  6. 提供调用,在使用的时候,调用方调用的就是代理方法。也就是已经织入了加强方法的方法。

本身实现一个 Aop 框架。

一样,我也是参考了Aop的设计。只实现了基于方法的拦截器。去除了不少的实现细节。

使用上一讲的 IoC 框架管理对象。使用 Cglib 做为动态代理的基础类。使用 maven 管理 jar 包和 module。因此上一讲的 IoC 框架会做为一个 modules 引入项目。

下面咱们就来实现咱们的Aop 框架吧。

首先来看看代码的基本结构。

代码结构比上一讲的 IoC 复杂很多。咱们首先对包每一个包都干了什么作一个简单介绍。

  • invocation 描述的就是一个方法的调用。注意这里指的是“方法的调用”,而不是调用这个动做。
  • interceptor 你们最熟悉的拦截器,拦截器拦截的目标就是 invcation 包里面的调用。
  • advisor 这个包里的对象,都是用来描述切面的数据结构。
  • adapter 这个包里面是一些适配器方法。对于"适配器"不了解的同窗能够去看看"设计模式"里面的"适配模式"。他的做用就是将 advice 包里的对象适配为 interceptor
  • bean 描述咱们 json 配置文件的对象。
  • core 咱们框架的核心逻辑。

这个时候宏观的看咱们大概梳理出了一条路线, adaperadvisor 适配为 interceptor 去拦截 invoction

下面咱们从这个链条的最末端讲起:

invcation

首先 MethodInvocation 做为全部方法调用的接口。要描述一个方法的调用包含三个方法,获取方法自己getMethod,获取方法的参数getArguments,还有执行方法自己proceed()

public interface MethodInvocation {
    Method getMethod();
    Object[] getArguments();
    Object proceed() throws Throwable;
}
复制代码

ProxyMethodInvocation 看名字就知道,是代理方法的调用,增长了一个获取代理的方法。

public interface ProxyMethodInvocation extends MethodInvocation {
    Object getProxy();
}
复制代码

interceptor

AopMethodInterceptor 是 Aop 容器全部拦截器都要实现的接口:

public interface AopMethodInterceptor {
    Object invoke(MethodInvocation mi) throws Throwable;
}
复制代码

同时咱们实现了两种拦截器BeforeMethodAdviceInterceptorAfterRunningAdviceInterceptor,顾名思义前者就是在方法执行之前拦截,后者就在方法运行结束之后拦截:

public class BeforeMethodAdviceInterceptor implements AopMethodInterceptor {
    private BeforeMethodAdvice advice;
    public BeforeMethodAdviceInterceptor(BeforeMethodAdvice advice) {
        this.advice = advice;
    }
    @Override
    public Object invoke(MethodInvocation mi) throws Throwable {
        advice.before(mi.getMethod(),mi.getArguments(),mi);
        return mi.proceed();
    }
}
复制代码
public class AfterRunningAdviceInterceptor implements AopMethodInterceptor {
    private AfterRunningAdvice advice;

    public AfterRunningAdviceInterceptor(AfterRunningAdvice advice) {
        this.advice = advice;
    }

    @Override
    public Object invoke(MethodInvocation mi) throws Throwable {
        Object returnVal = mi.proceed();
        advice.after(returnVal,mi.getMethod(),mi.getArguments(),mi);
        return returnVal;
    }
}

复制代码

看了上面的代码咱们发现,实际上 mi.proceed()才是执行原有的方法。而advice咱们上文就说过,是描述加强的方法”干什么“的数据结构,因此对于这个before拦截器,咱们就把advice对应的加强方法放在了真正执行的方法前面。而对于after拦截器而言,就放在了真正执行的方法后面。

这个时候咱们过头来看最关键的 ReflectioveMethodeInvocation

public class ReflectioveMethodeInvocation implements ProxyMethodInvocation {
    public ReflectioveMethodeInvocation(Object proxy, Object target, Method method, Object[] arguments, List<AopMethodInterceptor> interceptorList) {
        this.proxy = proxy;
        this.target = target;
        this.method = method;
        this.arguments = arguments;
        this.interceptorList = interceptorList;
    }

    protected final Object proxy;

    protected final Object target;

    protected final Method method;

    protected Object[] arguments = new Object[0];

    //存储全部的拦截器
    protected final List<AopMethodInterceptor> interceptorList;

    private int currentInterceptorIndex = -1;

    @Override
    public Object getProxy() {
        return proxy;
    }

    @Override
    public Method getMethod() {
        return method;
    }

    @Override
    public Object[] getArguments() {
        return arguments;
    }

    @Override
    public Object proceed() throws Throwable {

        //执行完全部的拦截器后,执行目标方法
        if(currentInterceptorIndex == this.interceptorList.size() - 1) {
            return invokeOriginal();
        }

        //迭代的执行拦截器。回顾上面的讲解,咱们实现的拦击都会执行 im.proceed() 实际上又会调用这个方法。实现了一个递归的调用,直到执行完全部的拦截器。
        AopMethodInterceptor interceptor = interceptorList.get(++currentInterceptorIndex);
        return interceptor.invoke(this);

    }

    protected Object invokeOriginal() throws Throwable{
        return ReflectionUtils.invokeMethodUseReflection(target,method,arguments);
    }

}
复制代码

 在实际的运用中,咱们的方法极可能被多个方法的拦截器所加强。因此咱们,使用了一个list来保存全部的拦截器。因此咱们须要递归的去增长拦截器。当处理完了全部的拦截器以后,才会真正调用调用被加强的方法。咱们能够认为,前文所述的动态的织入代码就发生在这里。

public class CglibMethodInvocation extends ReflectioveMethodeInvocation {

    private MethodProxy methodProxy;

    public CglibMethodInvocation(Object proxy, Object target, Method method, Object[] arguments, List<AopMethodInterceptor> interceptorList, MethodProxy methodProxy) {
        super(proxy, target, method, arguments, interceptorList);
        this.methodProxy = methodProxy;
    }

    @Override
    protected Object invokeOriginal() throws Throwable {
        return methodProxy.invoke(target,arguments);
    }
}
复制代码

CglibMethodInvocation 只是重写了 invokeOriginal 方法。使用代理类来调用被加强的方法。

advisor

这个包里面都是一些描述切面的数据结构,我们讲解两个重要的。

@Data
public class Advisor {
    //干什么
    private Advice advice;
    //在哪里
    private Pointcut pointcut;

}
复制代码

如上文所说,advisor 描述了在哪里,干什么。

@Data
public class AdvisedSupport extends Advisor {
    //目标对象
    private TargetSource targetSource;
    //拦截器列表
    private List<AopMethodInterceptor> list = new LinkedList<>();

    public void addAopMethodInterceptor(AopMethodInterceptor interceptor){
        list.add(interceptor);
    }

    public void addAopMethodInterceptors(List<AopMethodInterceptor> interceptors){
        list.addAll(interceptors);
    }

}
复制代码

这个AdvisedSupport就是 咱们Aop框架可以理解的数据结构,这个时候问题就变成了--对于哪一个目标,增长哪些拦截器。

core

有了上面的准备,咱们就开始讲解核心逻辑了。

@Data
public class CglibAopProxy implements AopProxy{
    private AdvisedSupport advised;
    private Object[] constructorArgs;
    private Class<?>[] constructorArgTypes;
    public CglibAopProxy(AdvisedSupport config){
        this.advised = config;
    }

    @Override
    public Object getProxy() {
        return getProxy(null);
    }
    @Override
    public Object getProxy(ClassLoader classLoader) {
        Class<?> rootClass = advised.getTargetSource().getTagetClass();
        if(classLoader == null){
            classLoader = ClassUtils.getDefultClassLoader();
        }
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(rootClass.getSuperclass());
        //增长拦截器的核心方法
        Callback callbacks = getCallBack(advised);
        enhancer.setCallback(callbacks);
        enhancer.setClassLoader(classLoader);
        if(constructorArgs != null && constructorArgs.length > 0){
            return enhancer.create(constructorArgTypes,constructorArgs);
        }
        return enhancer.create();
    }
    private Callback getCallBack(AdvisedSupport advised) {
        return new DynamicAdvisedIcnterceptor(advised.getList(),advised.getTargetSource());
    }
}
复制代码

CglibAopProxy就是咱们代理对象生成的核心方法。使用 cglib 生成代理类。咱们能够与以前ioc框架的代码。比较发现区别就在于:

Callback callbacks = getCallBack(advised);
    enhancer.setCallback(callbacks);
复制代码

callback与以前不一样了,而是写了一个getCallback()的方法,咱们就来看看 getCallback 里面的 DynamicAdvisedIcnterceptor到底干了啥。

篇幅问题,这里不会介绍 cglib 的使用,对于callback的做用,不理解的同窗须要自行学习。

public class DynamicAdvisedInterceptor implements MethodInterceptor{

    protected final List<AopMethodInterceptor> interceptorList;
    protected final TargetSource targetSource;

    public DynamicAdvisedInterceptor(List<AopMethodInterceptor> interceptorList, TargetSource targetSource) {
        this.interceptorList = interceptorList;
        this.targetSource = targetSource;
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        MethodInvocation invocation = new CglibMethodInvocation(obj,targetSource.getTagetObject(),method, args,interceptorList,proxy);
        return invocation.proceed();
    }
}
复制代码

这里须要注意,DynamicAdvisedInterceptor这个类实现的 MethodInterceptor 是 gclib的接口,并不是咱们以前的 AopMethodInterceptor。

咱们近距离观察 intercept 这个方法咱们看到:

MethodInvocation invocation = new CglibMethodInvocation(obj,targetSource.getTagetObject(),method, args,interceptorList,proxy);
复制代码

经过这行代码,咱们的整个逻辑终于连起来了。也就是这个动态的拦截器,把咱们经过 CglibMethodInvocation 织入了加强代码的方法,委托给了 cglib 来生成代理对象。

至此咱们的 Aop 的核心功能就实现了。

AopBeanFactoryImpl

public class AopBeanFactoryImpl extends BeanFactoryImpl{

    private static final ConcurrentHashMap<String,AopBeanDefinition> aopBeanDefinitionMap = new ConcurrentHashMap<>();

    private static final ConcurrentHashMap<String,Object> aopBeanMap = new ConcurrentHashMap<>();

    @Override
    public Object getBean(String name) throws Exception {
        Object aopBean = aopBeanMap.get(name);

        if(aopBean != null){
            return aopBean;
        }
        if(aopBeanDefinitionMap.containsKey(name)){
            AopBeanDefinition aopBeanDefinition = aopBeanDefinitionMap.get(name);
            AdvisedSupport advisedSupport = getAdvisedSupport(aopBeanDefinition);
            aopBean = new CglibAopProxy(advisedSupport).getProxy();
            aopBeanMap.put(name,aopBean);
            return aopBean;
        }

        return super.getBean(name);
    }
    protected void registerBean(String name, AopBeanDefinition aopBeanDefinition){
        aopBeanDefinitionMap.put(name,aopBeanDefinition);
    }

    private AdvisedSupport getAdvisedSupport(AopBeanDefinition aopBeanDefinition) throws Exception {

        AdvisedSupport advisedSupport = new AdvisedSupport();
        List<String> interceptorNames = aopBeanDefinition.getInterceptorNames();
        if(interceptorNames != null && !interceptorNames.isEmpty()){
            for (String interceptorName : interceptorNames) {

                Advice advice = (Advice) getBean(interceptorName);

                Advisor advisor = new Advisor();
                advisor.setAdvice(advice);

                if(advice instanceof BeforeMethodAdvice){
                    AopMethodInterceptor interceptor = BeforeMethodAdviceAdapter.getInstants().getInterceptor(advisor);
                    advisedSupport.addAopMethodInterceptor(interceptor);
                }

                if(advice instanceof AfterRunningAdvice){
                    AopMethodInterceptor interceptor = AfterRunningAdviceAdapter.getInstants().getInterceptor(advisor);
                    advisedSupport.addAopMethodInterceptor(interceptor);
                }

            }
        }

        TargetSource targetSource = new TargetSource();
        Object object = getBean(aopBeanDefinition.getTarget());
        targetSource.setTagetClass(object.getClass());
        targetSource.setTagetObject(object);
        advisedSupport.setTargetSource(targetSource);
        return advisedSupport;

    }

}

复制代码

AopBeanFactoryImpl是咱们产生代理对象的工厂类,继承了上一讲咱们实现的 IoC 容器的BeanFactoryImpl。重写了 getBean方法,若是是一个切面代理类,咱们使用Aop框架生成代理类,若是是普通的对象,咱们就用原来的IoC容器进行依赖注入。 getAdvisedSupport就是获取 Aop 框架认识的数据结构。

剩下没有讲到的类都比较简单,你们看源码就行。与核心逻辑无关。

写个方法测试一下

咱们须要统计一个方法的执行时间。面对这个需求咱们怎么作?

public class StartTimeBeforeMethod implements BeforeMethodAdvice{
    @Override
    public void before(Method method, Object[] args, Object target) {
        long startTime = System.currentTimeMillis();
        System.out.println("开始计时");
        ThreadLocalUtils.set(startTime);
    }
}
复制代码
public class EndTimeAfterMethod implements AfterRunningAdvice {
    @Override
    public Object after(Object returnVal, Method method, Object[] args, Object target) {
        long endTime = System.currentTimeMillis();
        long startTime = ThreadLocalUtils.get();
        ThreadLocalUtils.remove();
        System.out.println("方法耗时:" + (endTime - startTime) + "ms");
        return returnVal;
    }
}
复制代码

方法开始前,记录时间,保存到 ThredLocal里面,方法结束记录时间,打印时间差。完成统计。

目标类:

public class TestService {
    public void testMethod() throws InterruptedException {
        System.out.println("this is a test method");
        Thread.sleep(1000);
    }
}
复制代码

配置文件:

[
  {
    "name":"beforeMethod",
    "className":"com.xilidou.framework.aop.test.StartTimeBeforeMethod"
  },
  {
    "name":"afterMethod",
    "className":"com.xilidou.framework.aop.test.EndTimeAfterMethod"
  },
  {
    "name":"testService",
    "className":"com.xilidou.framework.aop.test.TestService"
  },
  {
    "name":"testServiceProxy",
    "className":"com.xilidou.framework.aop.core.ProxyFactoryBean",
    "target":"testService",
    "interceptorNames":[
      "beforeMethod",
      "afterMethod"
    ]
  }
]
复制代码

测试类:

public class MainTest {
    public static void main(String[] args) throws Exception {
        AopApplictionContext aopApplictionContext = new AopApplictionContext("application.json");
        aopApplictionContext.init();
        TestService testService = (TestService) aopApplictionContext.getBean("testServiceProxy");
        testService.testMethod();
    }
}
复制代码

最终咱们的执行结果:

开始计时
this is a test method
方法耗时:1015ms

Process finished with exit code 0
复制代码

至此 Aop 框架完成。

后记

Spring 的两大核心特性 IoC 与 Aop 两大特性就讲解完了,但愿你们经过我写的两篇文章可以深刻理解两个特性。

Spring的源码实在是复杂,阅读起来经常给人极大的挫败感,可是只要可以坚持,并采用一些行之有效的方法。仍是可以理解Spring的代码。而且从中汲取养分。

下一篇文章,我会给你们讲讲阅读开源代码的一些方法和我本身的体会,敬请期待。

最后

github:github.com/diaozxin007…

相关文章
相关标签/搜索