利用Cglib和JDK动态代理实现AOP

前提:本文假设你已经了解AOP切面编程的基础概念

项目地址:github.com/zexho994/IO…

AOP的逻辑

首先我们定义一个被代理类

@Bean
public class A interface I{

    public void print() {
        System.out.println("aaa");
    }

}
复制代码

定义切点和切面

@Bean
@Aspect
public class AdviceSample {

    @Before
    @Pointcut(beanName = "A")
    public void enhance2Before() {
        System.out.println("before");
    }


}
复制代码

后续我们使用A的时候,都会先打印切面的内容,也就是:

before
aaa
复制代码

image.png

如何将切面的内容添加到代理的方法里面去呢?

切面切入实现

JDK动态代理实现

提到代理,最先想到的是JDK提供的动态代理类InvocationHandler,下面尝试使用JDK动态代理实现 ,切入的方式假设使用before,而afteraround看完就懂如何实现了。

public class JDKDynamicProxy implements InvocationHandler {
    
    private Method before = null;
    private Object beforeObj = null;

    
    /**
     * @param proxy  代理的真实对象
     * @param method 要调用的真实对象的方法
     * @param args   方法的参数
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		// 执行before方法
        if (before != null) {
            before.invoke(beforeObj);
        }

        // 执行本体方法
        Object invoke = method.invoke(target, args);

        return invoke;
    }
    
    /**
     * 获取动态代理对象
     *
     * @param obj 被代理类
     * @return obj的父接口实现子类, 因为返回的是T父接口重写的类
     */
    @SuppressWarnings("unchecked")
    public <T> T getDynamicProxyImpl(T obj,Method before, Object beforeObj) {
        assert obj != null;
        this.before = before;
        this.beforeObj = beforeObj;

        // newProxyInstance() 会创建类$A,$A实现了obj的父接口,所有接口方法内容都为 {@link #invoke()} 。
        return (T) newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }
    
}
复制代码

统一调用getDynamicProxyImpl()来获取新的代理类,参数里面传入before的Method对象,invoke()方法定义了在执行本体方法之前执行before方法,这样新的代理类都会先执行before方法再执行本体方法了。

实现非常简单易懂,那么要实现after方法就是可以传入after的Method对象,然后在invoke()的本体方法之后执行,around的实现不用再增加新的逻辑了,只要同时传入beforeafterMethod对象,这样新的代理类就会同时执行before()after()了。

image.png

Cglib实现

JDK动态代理可以实现,但是有约束条件就是被代理类必须要有父接口,因为JDK动态实现的原理就是实现接口生成增强的子类。那么如果一个类没有父接口该如何进行切面增强呢?Spring中使用了cglib这个库来实现。

public class CglibProxy implements MethodInterceptor {

    public static final Enhancer ENHANCER = new Enhancer();

    private Method before = null;
    private Object beforeObj = null;

    private Method after = null;
    private Object afterObj = null;

    /**
     * @param obj         cglib动态代理生成的实例
     * @param method      被调用的方法的引用
     * @param params      参数列表
     * @param methodProxy 代理类对方法的代理引用
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] params, MethodProxy methodProxy) throws Throwable {
        if (before != null) {
            before.invoke(beforeObj);
        }
        Object result = methodProxy.invokeSuper(obj, params);
        if (after != null) {
            after.invoke(afterObj);
        }
        return result;
    }

    public void setBefore(Method before, Object beforeObj) {
        this.before = before;
        this.beforeObj = beforeObj;
    }

    public void setAfter(Method after, Object afterObj) {
        this.after = after;
        this.afterObj = afterObj;
    }

    @SuppressWarnings("unchecked")
    public static <T> T getProxy(T target, Method before, Object beforeObj, Method after, Object afterObj) {
        ENHANCER.setSuperclass(target.getClass());
        CglibProxy cglibProxy = new CglibProxy();
        cglibProxy.setBefore(before, beforeObj);
        cglibProxy.setAfter(after, afterObj);

        ENHANCER.setCallback(cglibProxy);
        return (T) ENHANCER.create();
    }

}
复制代码

实现的思路和JDK动态代理几乎一致,通过调用getProxy()方法传入before()after()方法就可以获得代理类了。

为什么cglib的实现不需要接口呢?cglib的实现原理是为被代理类创建一个子类,然后将切面通过ASM提供的字节码技术织入到目标方法中,这样子类的方法就包含了本体和切面的逻辑了。

image.png

猜你喜欢

转载自juejin.im/post/7068068901354995720
今日推荐