Spring笔记六: AOP的底层实现--JDK动态代理和CGLIB动态代理

代理模式

定义:给某个对象提供一个代理对象,并由代理对象控制对于原对象的访问,即客户不直接操控原对象,而是通过代理对象间接地操控原对象。
这里写图片描述
• RealSubject 是原对象(本文把原对象称为”委托对象”),Proxy 是代理对象。
• Subject 是委托对象和代理对象都共同实现的接口。
• Request() 是委托对象和代理对象共同拥有的方法。

代理的实现分为:
• 静态代理:代理类是在编译时就实现好的。也就是说 Java 编译完成后代理类是一个实际的 class 文件。
• 动态代理:代理类是在运行时生成的。也就是说 Java 编译完之后并没有实际的 class 文件,而是在运行时动态生成的类字节码,并加载到JVM中。

JDK动态代理

这是计算的接口,里面有加减乘除方法

public interface Calculate {
    int add(int i ,int j);
    int sub(int i,int j);
    int multiply(int i,int j);
    int divide(int i,int j);
}

这是一个正整数计算器的简单实现。

public class CalculateImpl implements Calculate {
    @Override
    public int add(int i, int j) {
        return i+j;
    }

    @Override
    public int sub(int i, int j) {
        return i-j;
    }

    @Override
    public int multiply(int i, int j) {
        return i*j;
    }

    @Override
    public int divide(int i, int j) {
        return i/j;
    }
}

现在我们打算在计算功能前后添加打印日志的功能,为了不影响业务的核心代码我们采用动态代理实现。从顶层向底层看,动态代理是怎样实现拦截切入功能(加入我们的切面逻辑,比如日志通知)的呢?
创建处理器对象(实现InvocationHandler接口),并且实现接口中的invoke方法。就是在invoke方法中加入切面逻辑的。目标类方法的执行是mehod.invoke(target,args)这条语句完成

public class MyproxyHandler implements InvocationHandler {
    private Calculate target;
    public MyproxyHandler(Calculate target) {
        this.target = target;
    }

    @Override
    /**
     * proxy: 指哪个对象调用了method方法 ;一般在invoke中不使用;
     * method: 正在被调用的方法
     * args: 调用方法时传入的参数
     * 一般是在业务逻辑代码前后加上“通知”
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("The method "+method.getName()+"begins with"+ Arrays.asList(args));
        Object result = method.invoke(target, args);
        System.out.println("The method "+method.getName()+" ends with "+ result);
        return result;
    }
}

上面的整段代码就是用来加入切面逻辑的,是动态代理的核心代码。

public class Main {
    public static void main(String[] args) {
    //创建要被代理(委托)的对象
        CalculateImpl target = new CalculateImpl();
    //创建一个处理器,将委托对象传入
        MyproxyHandler handler = new MyproxyHandler(target);
    //生成代理对象,要传入代理对象的类加载器和代理对象的类型(有哪些方法)和调用方法时执行的代码
        Calculate proxy =(Calculate) Proxy.newProxyInstance(target.getClass().getClassLoader(),CalculateImpl.class.getInterfaces(),handler);
    //通过代理对象调用方法
        int result=proxy.add(1,4);
        System.out.println(result);
    }
}

不足:

JDK动态代理只能代理接口类,如上的Calculate接口。这种局限性导致了必须要引入Cglib

CGLIB动态代理

如果说jdk代理对象实质上是目标对象接口的实现类,那么Cglib代理对象实质上是目标对象的子类,因为采用的是继承,所以不能对final修饰的类进行代理。
jdk动态代理是由java内部的反射机制来实现的,cglib动态代理底层则是借助asm来实现的。

现在假设我们没有Calculate这个接口,现在只能选择Cglib了。

实现动态代理类CglibLoggingProxy,需要实现MethodInterceptor接口,实现intercept方法。该代理中在proxy.invokeSuper(object, args);方法前后加入了自定义的切面逻辑。

public class CglibLoggingProxy implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("In cglib The method "+method.getName()+" begins with "+ Arrays.asList(objects)); 
        // 执行目标类add,sub,multiply,divide等方法
        int result=(Integer) methodProxy.invokeSuper(o,objects);
        System.out.println("The method "+method.getName()+" ends with "+ result);
        return result;
    }
}

获取织入了切面逻辑的目标类,其中增强的方法类对象是有Enhancer来实现的:

public class Factory {
   /**
    * 获得增强后的目标对象,即加入了切面逻辑的对象
    */
    public static CalculateImpl getProxyInstance(CglibLoggingProxy proxy){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(CalculateImpl.class);
        //设置回调方法,calculateImpl调用的是 methodProxy.invokeSuper方法
        enhancer.setCallback(proxy);
        // 得到了代理对象calculateImpl,不是单纯的目标类,而是增强过的目标类
        CalculateImpl calculateImpl = (CalculateImpl) enhancer.create();
        return calculateImpl;
    }
}

Main2:

public class Main2 {
    public static void main(String[] args) {
        //代理类,用于在pointcut处添加advise
        CglibLoggingProxy proxy = new CglibLoggingProxy();
        //获取加入了切面逻辑的目标类。
        CalculateImpl real = Factory.getProxyInstance(proxy);
        int result=real.add(1,4);
        System.out.println(result);
    }
}

参考博客:
https://blog.csdn.net/dreamrealised/article/details/12885739

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值