请问你能自己实现一个AOP吗?

前言

在之前的文章 # 通过手写aop来聊聊切点表达式那些事 我们了解aop的表达式如何完成匹配的,那么这篇文章我们就来聊聊如何实现一个aop

包装一个切面通知信息

什么是切面通知信息,他是做什么的?

说白了就是将我们AOP中所需要的目标对象,方法匹配器,方法拦截器,同一整合到一个类中。这样我们后续实现代理类的时候,就可以通过关联关系(通俗的理解就是作为代理类的一个成员变量)使用这个切面通知信息类。

切面通知信息成员属性介绍

我们首先来看看笔者实现的切面通知信息类,实现也很简单,一个目标对象类TargetSource,一个方法匹配器MethodMatcher,一个方法拦截器methodInterceptor,我们不妨看看这每个成员变量的具体实现

package cn.shark.springframework.aop;

import org.aopalliance.intercept.MethodInterceptor;

/**
 * 将目标、拦截器、表达式全部集成到一个类中,方便代理使用这些工具
 */
public class AdvisedSupport {

    /**
     * 目标对象
     */
    private TargetSource targetSource;

    /**
     * 方法匹配器
     */
    private MethodMatcher methodMatcher;


//    拦截器
    private MethodInterceptor methodInterceptor;

    private boolean proxyTargetClass;


    get()/set()....
}
复制代码

targetsource其实做的也很简单,无法是将目标对象包装一层,考虑到后续jdk代理需要用到类对象的接口信息,所以封装了一个getTargetClass方法

public class TargetSource {

    private final Object target;

    public TargetSource(Object target) {
        this.target = target;
    }


    public Class<?>[] getTargetClass(){
        //它能够获得这个对象所实现的所有接口
       return target.getClass().getInterfaces();
    }

    public Object getTarget() {

        return target;
    }
}
复制代码

MethodMatcher

/**
 * 方法匹配器
 */
public interface MethodMatcher {

    /**
     * 判断当前类的方法是否匹配
     * @param method
     * @param targetClass
     * @return
     */
    boolean matches(Method method,Class<?> targetClass);
}
复制代码

MethodInterceptor

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.aopalliance.intercept;

public interface MethodInterceptor extends Interceptor {
    Object invoke(MethodInvocation var1) throws Throwable;
}
复制代码

代理对象

定义一个代理对象接口

我们后续代理的方式可能多种多样,那么我们就是用一个接口同一他们的动作,并且方便其他类使用无需关联具体而是关联抽象(通俗来说就是用接口作为成员成员变量起到解耦的作用)

/**
 * AopProxy 是代理的抽象对象,它的实现主要是基于 JDK 的代理和 Cglib 代理。
 */
public interface AopProxy {

    Object getProxy();
}
复制代码

jdk代理对象的设计与实现

看看下方的代码,实现逻辑也很简单,如果方法匹配则进行方法拦截,反之直接反射调用。但是我们看到了在方法拦截器的传参是一个ReflectiveMethodInvocation,这到底是什么呢?我们不妨步进看看

public class JdkDynamicAopProxy implements AopProxy, InvocationHandler {

    private final AdvisedSupport advisedSupport;

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

    @Override
    public Object getProxy() {
//        因为继承了InvocationHandler 所以就可以用this作为第三个参数
        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),advisedSupport.getTargetSource().getTargetClass(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (advisedSupport.getMethodMatcher().matches(method,advisedSupport.getTargetSource().getTargetClass().getClass())){
            MethodInterceptor methodInterceptor = advisedSupport.getMethodInterceptor();
            return methodInterceptor.invoke(new ReflectiveMethodInvocation(advisedSupport.getTargetSource().getTarget(),method,args));
        }


        return method.invoke(advisedSupport.getTargetSource().getTarget(),args);
    }
}
复制代码

可以看到,该类无法是一个继承MethodInvocation且包装了目标对象,目标对象方法,目标对象方法参数的一个类而已。这么做的好处只有一个,由于拦截器invoke需要的数据需要用MethodInvocation类型,所以我们就继承他然后自己实现一个这样的类出来并将需要的参数set进去。

/**
 * 本质上就是一个对目标对象、目标对象方法、目标对象参数的一个包装,仅此而已
 */
public class ReflectiveMethodInvocation implements MethodInvocation {

    protected Object target;

    protected Method method;

    protected Object[] args;

    public ReflectiveMethodInvocation(Object target, Method method, Object[] args) {
        this.target = target;
        this.method = method;
        this.args = args;
    }

    get()/set()
}
复制代码

CGLIB代理对象的设计与实现

CGLIB的getProxy实现与jdk代理有所区别,因为cglib使用的Enhancer类可以在运行期间为接口使用底层的ASM字节码增强技术生成对象,所以代理对象不需要继承任何接口。
我们注意到Cglib的getProxy中创建代理类的enhancer使用setSuperclass、setInterfaces完成设置目标对象和目标对象所有的接口。再用setCallback设置一个DynamicAdvisedInterceptor即动态拦截器,我们不妨步进看看这具体是什么东西

public class Cglib2AopProxy implements AopProxy {

    private final AdvisedSupport advisedSupport;


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

    @Override
    public Object getProxy() {
//        基于 Cglib 使用 Enhancer 代理的类可以在运行期间为接口使用底层 ASM 字节码增强技术处理对象的代理对象生成,因此被代理类不需要实现任何接口。
        Enhancer enhancer=new Enhancer();
        enhancer.setSuperclass(advisedSupport.getTargetSource().getTarget().getClass());
        enhancer.setInterfaces(advisedSupport.getTargetSource().getTargetClass());
        enhancer.setCallback(new DynamicAdvisedInterceptor(advisedSupport));
        return enhancer.create();
    }

    private static class DynamicAdvisedInterceptor implements MethodInterceptor {

        private AdvisedSupport advisedSupport;

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

        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            CglibMethodInvocation cglibMethodInvocation = new CglibMethodInvocation(advisedSupport.getTargetSource().getTarget(),method,objects,methodProxy);
            if (advisedSupport.getMethodMatcher().matches(method,advisedSupport.getTargetSource().getTarget().getClass())){
                return advisedSupport.getMethodInterceptor().invoke(cglibMethodInvocation);
            }
            return cglibMethodInvocation.proceed();
        }
    }


    private static class CglibMethodInvocation extends ReflectiveMethodInvocation {

        private final MethodProxy methodProxy;

        public CglibMethodInvocation(Object target, Method method, Object[] args, MethodProxy MethodProxy) {
            super(target, method, args);
            this.methodProxy = MethodProxy;
        }

        /**
         * 因为是cglib的远古继承ReflectiveMethodInvocation重写proceed,使用自己的代理完成方法调用
         * @return
         * @throws Throwable
         */
        @Override
        public Object proceed() throws Throwable {
            return methodProxy.invoke(this.target, this.args);
        }
    }
}
复制代码

可以看到他是cglib代理的一个内部类,继承了MethodInterceptor实现拦截逻辑。我们注意到这里的方法拦截器调用了一个cglib自己的实现的方法调用器cglibMethodInvocation,我们不妨看看他的实现

private static class DynamicAdvisedInterceptor implements MethodInterceptor {

    private AdvisedSupport advisedSupport;

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

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        CglibMethodInvocation cglibMethodInvocation = new CglibMethodInvocation(advisedSupport.getTargetSource().getTarget(),method,objects,methodProxy);
        if (advisedSupport.getMethodMatcher().matches(method,advisedSupport.getTargetSource().getTarget().getClass())){
            return advisedSupport.getMethodInterceptor().invoke(cglibMethodInvocation);
        }
        return cglibMethodInvocation.proceed();
    }
}
复制代码

不过如此,只不过继承了ReflectiveMethodInvocation,将proceed逻辑改为cglib反射调用方法的形式而已

private static class CglibMethodInvocation extends ReflectiveMethodInvocation {

    private final MethodProxy methodProxy;

    public CglibMethodInvocation(Object target, Method method, Object[] args, MethodProxy MethodProxy) {
        super(target, method, args);
        this.methodProxy = MethodProxy;
    }

    /**
     * 因为是cglib的远古继承ReflectiveMethodInvocation重写proceed,使用自己的代理完成方法调用
     * @return
     * @throws Throwable
     */
    @Override
    public Object proceed() throws Throwable {
        return methodProxy.invoke(this.target, this.args);
    }
}
复制代码

用一个类图总结一下上述的设计结构

图片.png

用一个测试用例来走一遍代码全流程

代码

一个userService接口

public interface IUserService {

    String queryUserInfo();
}
复制代码

其实现类

public class UserService implements IUserService {

    private String uId;
    private String company;
    private String location;
    private UserDao userDao;

    private ApplicationContext applicationContext;

    private BeanFactory beanFactory;

    public String queryUserInfo() {
        try {
            Thread.sleep(new Random(1).nextInt(100));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "89757 鲨鱼辣椒 福建";
    }

   
}
复制代码

测试用例

 /**
     * 测试还没整合到spring的aop
     * @throws NoSuchMethodException
     */
    @Test
    public void testAopWithoutSpring() throws NoSuchMethodException {
        IUserService userService=new UserService();

//        创建通知信息对象
        AdvisedSupport advisedSupport=new AdvisedSupport();
//        设置目标对象
        advisedSupport.setTargetSource(new TargetSource(userService));
//        设置方法拦截器
        advisedSupport.setMethodInterceptor(new UserServiceInterceptor());
//        设置匹配表达式对象
        advisedSupport.setMethodMatcher(new AspectJExpressionPointcut("execution(* cn.shark.springframework.bean.IUserService.*(..))"));


        // 代理对象(JdkDynamicAopProxy)
        IUserService proxy_jdk = (IUserService) new JdkDynamicAopProxy(advisedSupport).getProxy();
        // 测试调用
        System.out.println("测试结果:" + proxy_jdk.queryUserInfo());

        // 代理对象(Cglib2AopProxy)
        IUserService proxy_cglib = (IUserService) new Cglib2AopProxy(advisedSupport).getProxy();
        // 测试调用
        System.out.println("测试结果:" + proxy_cglib.queryUserInfo());

    }
复制代码

通过图解debug走一遍自己实现的AOP

以jdk作为代理走读一遍

图片.png

首先jdk代理初始化

图片.png

通过切面通知信息各种成员属性进行赋值,并将自己作为方法拦截器作为参数InvocationHandler传入

图片.png

创建好代理类后开始使用代理类调用被代理类的方法

图片.png

走到jdk代理自己继承InvocationHandler而编写的拦截逻辑

图片.png

为MethodInvocation初始化

图片.png

拿到方法拦截器调用方法调用器

图片.png

图片.png

图片.png

打完收工

下一篇笔者会将其整合到自研spring框架中

猜你喜欢

转载自juejin.im/post/7042333552557424653