动态代理会了,但JDK Proxy和CGLib有啥区别呢?

什么是动态代理?

先说说什么是代理模式,代理模式就是为某个对象提供一个代理对象,并且由代理对象控制对原对象的访问。代理模式通俗来讲就是我们生活中常见的中介。

而动态代理就是,在程序运行期,创建目标对象的代理对象,并对目标对象中的方法进行功能性增强的一种技术。在生成代理对象的过程中,目标对象不变,代理对象中的方法是目标对象方法的增强方法。可以理解为运行期间,对象中方法的动态拦截,在拦截方法的前后执行功能操作。

动态代理有什么用?

其实无论是日志框架或 Spring 框架,它们都包含了动态代理的实现代码,Java动态代理的优势是实现无侵入式的代码扩展,也就是方法的增强,可以在不用修改源码的情况下,增强一些方法,增强就是在方法的前后做我们任何想做的事情,甚至不去执行这个方法。

我们知道大名鼎鼎的AOP的思想是:不去动原来的代码,而是基于原来代码产生代理对象,通过代理的方法,去包装原来的方法,完成对以前方法的增强。换句话说,AOP的底层原理就是动态代理的实现。

动态代理如何实现?

实现动态代理有两种办法,JDK自带的Proxy类和CGLib框架。

另外在Spring 框架中同时使用了两种动态代理 JDK Proxy 和 CGLib,当 Bean 实现了接口时,Spring 就会使用 JDK Proxy,在没有实现接口时就会使用 CGLib,我们也可以在配置中指定强制使用 CGLib

JDK Proxy和CGLib有什么区别?

  1. JDK Proxy 是 Java 语言自带的功能,无需通过加载第三方类实现;
  2. Java 对 JDK Proxy 提供了稳定的支持,并且会持续的升级和更新 JDK Proxy,例如 java 8 版本中的 JDK Proxy 性能相比于之前版本提升了很多;
  3. JDK Proxy 是通过拦截器加反射的方式实现的;
  4. JDK Proxy 只能代理继承接口的类;
  5. JDK Proxy 实现和调用起来比较简单;
  6. CGLib 是第三方提供的工具,基于 ASM 实现的,性能比较高;
  7. CGLib 无需通过接口来实现,它是通过实现子类的方式来完成调用的。

什么是ASM

ASM是一个通用的Java字节码操作和分析框架。 它可以用于修改现有类或直接以二进制形式动态生成类。 ASM提供了一些常见的字节码转换和分析算法,可以从中构建自定义复杂转换和代码分析工具。 ASM提供与其他Java字节码框架类似的功能,但专注于性能。 因为它的设计和实现尽可能小而且快,所以它非常适合在动态系统中使用。

JDK Proxy实现

public interface IAnimal {
    public void eat();

}
public class Dog implements IAnimal {
    @Override
    public void eat() {
        System.out.println("Dog吃饭");
    }
}

public class Main {
    public static void main(String[] args) {

        IAnimal dog = (IAnimal) Proxy.newProxyInstance(Dog.class.getClassLoader(), Dog.class.getInterfaces(), new DogInvocationHandler(new Dog()));

        dog.eat();

    }

    static class DogInvocationHandler implements InvocationHandler {
        private Object object;

        public DogInvocationHandler(Object object) {
            this.object = object;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("Dog 说谢谢");
            Object invoke = method.invoke(object, args);
            System.out.println("狗吃完了");
            return invoke;
        }
    }
}

原先Dog只会吃饭,现在Dog会在吃饭前说谢谢了。
在这里插入图片描述
可以看出 JDK Proxy 实现动态代理的核心是实现 Invocation 接口,至于JDK怎么实现,可以查看以前的博客。

JDK动态代理的代理类字节码在创建时,需要业务实现类所实现的接口作为参数。如果业务实现类是没有实现接口而是直接定义业务方法的话,就无法使用JDK动态代理了。

CGLib 的实现

加入依赖

<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

CGLib 在初始化被代理类时,是通过 Enhancer 对象把代理对象设置为被代理类的子类来实现动态代理的。因此被代理类不能被关键字 final 修饰,如果被 final 修饰,再使用 Enhancer 设置父类时会报错,动态代理的构建会失败。

public class Dog  {
    public void eat() {
        System.out.println("Dog吃饭");
    }
}

public class Main {
    public static void main(String[] args) {

        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Dog.class);
        enhancer.setCallback((MethodInterceptor) (obj, method, args1, proxy) -> {
            System.out.println("DOg说谢谢");
            Object result = proxy.invokeSuper(obj, args1);
            System.out.println("Dog吃完了");
            return result;
        });
        Dog dog = (Dog) enhancer.create();
        dog.eat();

    }
   
}

我们可以通过设置调试class输出地址,来查看生成的代理类。

  System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"/home/HouXinLin/cglib");

在这里插入图片描述

CGlib创建代理的速度比较慢,但创建代理之后运行的速度却非常快,而JDK动态代理刚好相反。如果在运行的时候不断地用CGlib去创建代理,系统的性能会大打折扣,所以建议一般在系统初始化的时候用CGlib去创建代理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值