JDK动态代理和CGLIB动态代理的区别及实例

简介: JDK动态代理和CGLIB动态代理的区别及实例

JDK 动态代理和 CGLIB 动态代理是两种常用的 Java 动态代理技术,它们在实现原理和应用场景上存在一些区别。

JDK 动态代理JDK 动态代理是基于接口的代理技术。它利用 Java 的反射机制,在运行时创建代理类和代理实例。JDK 动态代理要求目标对象必须实现至少一个接口,代理类会实现与目标对象相同的接口,并且具有相同的方法签名。当代理对象的方法被调用时,实际上是通过InvocationHandler 将方法调用转发给了目标对象。JDK 动态代理只能代理接口,无法代理类。

优点:

  • 简单易用,无需额外的库或依赖。
  • 适用于代理接口的场景。

缺点:

  • 只能代理实现了接口的类。
  • 代理类的性能相对较低。

CGLIB 动态代理 CGLIBCode Generation Library)动态代理是基于类的代理技术。它通过生成目标类的子类作为代理类,并重写目标方法来实现代理功能。CGLIB 动态代理不要求目标对象实现接口,可以代理普通的类。当代理对象的方法被调用时,实际上是通过MethodInterceptor 将方法调用转发给了目标对象。

优点:

  • 可以代理普通的类,无需实现接口。
  • 生成的代理类性能比 JDK 动态代理高。

缺点:

  • 生成的代理类是目标类的子类,因此无法代理被 final 修饰的类和方法。
  • 需要依赖 CGLIB 库。

选择使用 JDK 动态代理还是 CGLIB 动态代理取决于具体的场景和需求。如果目标对象只实现了接口,并且对性能要求较低,可以选择 JDK 动态代理;如果目标对象为普通类,或者需要更高的性能,可以选择 CGLIB 动态代理。

总结起来,JDK 动态代理适用于代理接口,而 CGLIB 动态代理适用于代理类。

下面是一个使用 JDK 动态代理的示例代码:

首先,定义一个接口UserService,包含了一些方法:

publicinterfaceUserService {
voidaddUser(Stringusername);
voiddeleteUser(Stringusername);
}


接下来,创建一个实现了该接口的目标对象UserServiceImpl

publicclassUserServiceImplimplementsUserService {
@OverridepublicvoidaddUser(Stringusername) {
System.out.println("添加用户:"+username);
    }
@OverridepublicvoiddeleteUser(Stringusername) {
System.out.println("删除用户:"+username);
    }
}


然后,编写一个 InvocationHandler 实现类UserInvocationHandler,用于处理代理对象的方法调用:

importjava.lang.reflect.InvocationHandler;
importjava.lang.reflect.Method;
publicclassUserInvocationHandlerimplementsInvocationHandler {
privatefinalUserServicetarget; // 目标对象publicUserInvocationHandler(UserServicetarget) {
this.target=target;
    }
@OverridepublicObjectinvoke(Objectproxy, Methodmethod, Object[] args) throwsThrowable {
System.out.println("Before method: "+method.getName());
Objectresult=method.invoke(target, args);
System.out.println("After method: "+method.getName());
returnresult;
    }
}


最后,在测试类中使用 JDK 动态代理创建代理对象并调用方法:

importjava.lang.reflect.Proxy;
publicclassMain {
publicstaticvoidmain(String[] args) {
UserServiceuserService=newUserServiceImpl();
// 创建代理对象UserServiceproxy= (UserService) Proxy.newProxyInstance(
userService.getClass().getClassLoader(),
userService.getClass().getInterfaces(),
newUserInvocationHandler(userService)
        );
// 调用代理对象的方法proxy.addUser("Alice");
proxy.deleteUser("Bob");
    }
}

运行上述代码,输出结果如下:

Beforemethod: addUser添加用户:AliceAftermethod: addUserBeforemethod: deleteUser删除用户:BobAftermethod: deleteUser


以上示例演示了使用 JDK 动态代理,通过Proxy.newProxyInstance()创建代理对象,将需要代理的目标对象以及自定义的 InvocationHandler 传入。在 InvocationHandler 中,可以在方法调用前后执行额外的逻辑。

下面是一个使用 CGLIB 动态代理的示例代码:

首先,引入 CGLIB 的相关依赖。在 Maven 中,可以这样添加依赖:

<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.4.0</version></dependency>

定义一个普通的目标类UserService,包含一些方法:

publicclassUserService {
publicvoidaddUser(Stringusername) {
System.out.println("添加用户:"+username);
    }
publicvoiddeleteUser(Stringusername) {
System.out.println("删除用户:"+username);
    }
}


接下来,编写一个 MethodInterceptor 实现类UserMethodInterceptor,用于处理代理对象的方法调用:


importnet.sf.cglib.proxy.MethodInterceptor;
importnet.sf.cglib.proxy.MethodProxy;
importjava.lang.reflect.Method;
publicclassUserMethodInterceptorimplementsMethodInterceptor {
@OverridepublicObjectintercept(Objectobj, Methodmethod, Object[] args, MethodProxyproxy) throwsThrowable {
System.out.println("Before method: "+method.getName());
Objectresult=proxy.invokeSuper(obj, args);
System.out.println("After method: "+method.getName());
returnresult;
    }
}

在测试类中使用 CGLIB 创建代理对象并调用方法:

importnet.sf.cglib.proxy.Enhancer;
publicclassMain {
publicstaticvoidmain(String[] args) {
UserServiceuserService=newUserService();
// 创建 Enhancer 对象Enhancerenhancer=newEnhancer();
// 设置目标类为父类enhancer.setSuperclass(UserService.class);
// 设置回调对象enhancer.setCallback(newUserMethodInterceptor());
// 创建代理对象UserServiceproxy= (UserService) enhancer.create();
// 调用代理对象的方法proxy.addUser("Alice");
proxy.deleteUser("Bob");
    }
}

运行上述代码,输出结果如下:

Beforemethod: addUser添加用户:AliceAftermethod: addUserBeforemethod: deleteUser删除用户:BobAftermethod: deleteUser

以上示例演示了使用 CGLIB 动态代理,通过Enhancer创建代理对象。在创建代理对象时,需要设置目标类和回调对象。回调对象即实现了MethodInterceptor接口的自定义拦截器,在拦截器中可以在方法调用前后执行额外的逻辑。

 

相关文章
|
3月前
|
存储 算法 Java
JvM JDK JRE 三者区别与联系详解
本文深入解析了Java编程中的三个核心概念:JVM(Java虚拟机)、JDK(Java开发工具包)和JRE(Java运行环境)。JVM是执行Java字节码的虚拟计算机,实现“一次编译,到处运行”;JDK包含JRE及开发工具,用于编写和调试Java程序;JRE负责运行已编译的Java程序。文章详细阐述了它们的功能、组成及应用场景,并通过实例说明其在实际开发中的作用,帮助开发者理解三者联系与区别,提升开发效率与问题解决能力。适合Java初学者及进阶开发者学习参考。
514 3
|
6月前
|
Java Spring
JDK动态代理和CGLIB动态代理的区别
Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理: ● JDK动态代理只提供接口的代理,不支持类的代理Proxy.newProxyInstance(类加载器, 代理对象实现的所有接口, 代理执行器) ● CGLIB是通过继承的方式做的动态代理 , 如果某个类被标记为final,那么它是无法使用 CGLIB做动态代理的。Enhancer.create(父类的字节码对象, 代理执行器)
|
6月前
|
监控 Java API
JDK动态代理和CGLIB动态代理
Java动态代理允许在运行时创建代理对象,增强或拦截目标类方法的执行。主要通过两种方式实现:JDK动态代理和CGLIB动态代理。JDK动态代理基于接口,利用`java.lang.reflect.Proxy`类和`InvocationHandler`接口;CGLIB则通过字节码技术生成目标类的子类作为代理,适用于未实现接口的类。两者均用于在方法执行前后添加额外逻辑,如日志记录、权限控制等,广泛应用于AOP框架中。
207 2
|
7月前
|
监控 Java API
JDK动态代理和CGLIB动态代理
Java动态代理允许在运行时创建代理对象,增强或拦截目标类的方法调用,无需修改原代码。它有两种主要实现方式:JDK动态代理和CGLIB动态代理。 - **JDK动态代理**:通过`java.lang.reflect.Proxy`类和`InvocationHandler`接口实现,适用于实现了接口的类。它在方法调用前后插入额外逻辑,如日志记录、权限控制等。 - **CGLIB动态代理**:基于字节码技术,为未实现接口的类生成子类作为代理,重写父类方法以添加增强逻辑。适用于没有接口的类,但要求目标类不能是`final`类或方法。
|
7月前
|
Java API 数据安全/隐私保护
探索Java动态代理的奥秘:JDK vs CGLIB
动态代理是一种在 运行时动态生成代理类的技术,无需手动编写代理类代码。它通过拦截目标方法的调用,实现对核心逻辑的 无侵入式增强(如日志、事务、权限控制等)。
193 0
探索Java动态代理的奥秘:JDK vs CGLIB
|
2月前
|
存储 Ubuntu 安全
在Ubuntu 16.04上安装openjdk-6/7/8-jdk的步骤
在整个安装过程中,你可能需要管理员权限,因此你可能要使用 `sudo` 来获取必要的权限。记得做完每一个步骤后,都要检查输出,以确保没有发生错误,并且每项操作都成功完成。如果在安装过程中遇到问题,查看 `/var/log/` 下的日志文件对于问题的解决可能是有帮助的。
143 21
|
2月前
|
IDE Ubuntu Java
在Ubuntu18.04安装兼容JDK 8的Eclipse集成开发环境的指南。
完成以上步骤后,您将在Ubuntu 18.04系统上成功安装并配置了Eclipse IDE,它将与JDK 8兼容,可以开始进行Java开发工作。如果遇到任何问题,请确保每一步骤都正确执行,并检查是否所有路径都与您的具体情况相匹配。
112 11
|
1月前
|
Ubuntu Java Android开发
在Ubuntu 18.04上安装与JDK 8兼容的Eclipse版本的步骤。
安装过程结束后,您就可以开始使用Eclipse来开发您的Java项目了,并且确保它与JDK 8兼容无误。这个过程涉及的是一个基本的安装流程,针对使用Java 8的用户,Eclipse的其他配置和插件安装根据个人开发环境和需求来定制。
149 0
|
4月前
|
Java 关系型数据库 MySQL
在Linux平台上进行JDK、Tomcat、MySQL的安装并部署后端项目
现在,你可以通过访问http://Your_IP:Tomcat_Port/Your_Project访问你的项目了。如果一切顺利,你将看到那绚烂的胜利之光照耀在你的项目之上!
297 41
|
4月前
|
开发框架 Java 关系型数据库
在Linux系统中安装JDK、Tomcat、MySQL以及部署J2EE后端接口
校验时,浏览器输入:http://[your_server_IP]:8080/myapp。如果你看到你的应用的欢迎页面,恭喜你,一切都已就绪。
389 17