动态代理会了,但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去创建代理。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
cglib实现动态代理jdk实现动态代理之间有以下不同之处[^1][^2]: 1. 实现方式:JDK动态代理是通过Java的反射机制来实现的,而CGLib动态代理是通过继承目标类并生成子类来实现的。 2. 代理对象类型:JDK动态代理只能为接口创建代理对象,而CGLib动态代理可以为类创建代理对象。 3. 性能:JDK动态代理在生成代理对象时需要使用反射,因此在调用代理方法时有一定的性能损耗。而CGLib动态代理通过生成子类来实现代理,因此在调用代理方法时性能更高。 4. 依赖:JDK动态代理Java标准库的一部分,因此不需要额外的依赖。而CGLib动态代理需要引入CGLib库作为依赖。 5. 对象创建:JDK动态代理创建代理对象时需要传入一个InvocationHandler对象,该对象负责处理代理方法的调用。而CGLib动态代理创建代理对象时不需要传入任何参数。 6. 目标类的要求:JDK动态代理要求目标类实现一个或多个接口,才能为其创建代理对象。而CGLib动态代理可以为任意类创建代理对象,包括没有实现接口的类。 下面是一个使用JDK动态代理CGLib动态代理的示例代码: JDK动态代理: ```java import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; interface UserService { void addUser(String username); } class UserServiceImpl implements UserService { public void addUser(String username) { System.out.println("Add user: " + username); } } class MyInvocationHandler implements InvocationHandler { private Object target; public MyInvocationHandler(Object target) { this.target = target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Before method: " + method.getName()); Object result = method.invoke(target, args); System.out.println("After method: " + method.getName()); return result; } } public class Main { public static void main(String[] args) { UserService userService = new UserServiceImpl(); InvocationHandler handler = new MyInvocationHandler(userService); UserService proxy = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), handler); proxy.addUser("Alice"); } } ``` CGLib动态代理: ```java import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; class UserService { public void addUser(String username) { System.out.println("Add user: " + username); } } class MyMethodInterceptor implements MethodInterceptor { public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("Before method: " + method.getName()); Object result = proxy.invokeSuper(obj, args); System.out.println("After method: " + method.getName()); return result; } } public class Main { public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(UserService.class); enhancer.setCallback(new MyMethodInterceptor()); UserService proxy = (UserService) enhancer.create(); proxy.addUser("Alice"); } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值