在 spring boot 项目中,jdk 动态代理和 cglib 动态代理都是实现 aop (面向切面编程) 的重要技术。 它们的主要区别在于代理对象的生成方式和适用范围。
下面详细介绍它们的使用场景:
1. jdk 动态代理 (jdk dynamic proxy)
原理:
- jdk 动态代理是 java 提供的内置代理机制,它通过反射在运行时动态地生成代理类。
- 代理类会实现目标类实现的接口,并将接口中的方法调用委托给一个
invocationhandler
对象来处理。 invocationhandler
接口负责实现具体的增强逻辑,例如日志记录、安全检查、事务管理等。
使用场景:
- 目标类实现了接口: 如果目标类实现了接口,则 spring aop 默认使用 jdk 动态代理。
- 简单 aop 场景: 适用于简单的 aop 场景,例如只需要对接口方法进行增强的情况。
- 不需要代理类的构造函数: jdk 动态代理创建代理对象时,不需要调用目标类的构造函数。
优点:
- 简单易用: jdk 动态代理是 java 内置的代理机制,使用起来比较简单。
- 不需要第三方库: 不需要依赖第三方库。
- 对接口友好: 代理类实现了目标类实现的接口,符合面向接口编程的思想。
缺点:
- 必须实现接口: 目标类必须实现接口,否则无法使用 jdk 动态代理。
- 只能代理接口方法: 只能代理接口中定义的方法,无法代理类自身定义的方法。
示例代码:
// 接口 interface myinterface { void dosomething(); } // 实现类 class myclass implements myinterface { @override public void dosomething() { system.out.println("myclass is doing something..."); } } // 调用处理器 class myinvocationhandler implements invocationhandler { private object target; // 被代理的对象 public myinvocationhandler(object target) { this.target = target; } @override 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; } } // 使用 jdk 动态代理 public class jdkdynamicproxyexample { public static void main(string[] args) { myinterface target = new myclass(); // 创建目标对象 myinvocationhandler handler = new myinvocationhandler(target); // 创建调用处理器 // 创建代理对象 myinterface proxy = (myinterface) proxy.newproxyinstance( myinterface.class.getclassloader(), new class[] {myinterface.class}, handler ); proxy.dosomething(); // 调用代理对象的方法,会被拦截 } }
2. cglib 动态代理 (cglib dynamic proxy)
原理:
- cglib (code generation library) 是一个强大的、高性能的代码生成库。
- cglib 动态代理通过在运行时动态地生成目标类的子类来实现代理。
- 代理类会继承目标类,并重写目标类的方法,在重写的方法中实现增强逻辑。
- cglib 动态代理不需要目标类实现接口,可以直接代理类。
使用场景:
- 目标类没有实现接口: 如果目标类没有实现接口,则 spring aop 使用 cglib 动态代理。
- 需要代理类自身定义的方法: 需要代理类自身定义的方法,而不仅仅是接口方法。
- 需要更高的性能: 在某些情况下,cglib 动态代理的性能可能比 jdk 动态代理更好。
优点:
- 不需要实现接口: 目标类不需要实现接口,可以直接代理类。
- 可以代理类自身定义的方法: 可以代理类自身定义的方法,而不仅仅是接口方法。
- 性能较好: 在某些情况下,cglib 动态代理的性能可能比 jdk 动态代理更好。
缺点:
- 需要第三方库: 需要依赖 cglib 库。
- 实现复杂: cglib 动态代理的实现比较复杂,需要生成目标类的子类。
- final 方法无法代理: 无法代理被
final
修饰的方法。 - 对构造函数有要求: cglib 创建代理对象时,需要调用目标类的构造函数。 如果目标类没有无参构造函数,则需要手动指定构造函数。
示例代码:
import net.sf.cglib.proxy.enhancer; import net.sf.cglib.proxy.methodinterceptor; import net.sf.cglib.proxy.methodproxy; import java.lang.reflect.method; // 目标类 class myclass { public void dosomething() { system.out.println("myclass is doing something..."); } } // 方法拦截器 class mymethodinterceptor implements methodinterceptor { @override 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; } } // 使用 cglib 动态代理 public class cglibdynamicproxyexample { public static void main(string[] args) { enhancer enhancer = new enhancer(); // 创建 enhancer 对象 enhancer.setsuperclass(myclass.class); // 设置超类 enhancer.setcallback(new mymethodinterceptor()); // 设置回调 myclass proxy = (myclass) enhancer.create(); // 创建代理对象 proxy.dosomething(); // 调用代理对象的方法,会被拦截 } }
3. spring boot 中的 aop 配置
在 spring boot 项目中,可以通过以下方式配置 aop:
添加 aop 依赖:
<dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-aop</artifactid> </dependency>
编写切面类:
@aspect @component public class loggingaspect { @before("execution(* com.example.demo.service.*.*(..))") public void beforeadvice(joinpoint joinpoint) { system.out.println("before executing method: " + joinpoint.getsignature()); } }
配置代理方式:
- 默认情况下,spring aop 会根据目标类是否实现了接口来选择使用 jdk 动态代理或 cglib 动态代理。
- 可以通过
@enableaspectjautoproxy
注解的proxytargetclass
属性来强制使用 cglib 动态代理。
@configuration @enableaspectjautoproxy(proxytargetclass = true) // 强制使用 cglib 动态代理 public class aopconfig { // ... }
总结:
特性 | jdk 动态代理 | cglib 动态代理 |
---|---|---|
目标类要求 | 必须实现接口 | 不需要实现接口 |
代理对象生成方式 | 实现接口 | 继承类 |
性能 | 一般 | 较好 |
易用性 | 简单 | 复杂 |
是否需要第三方库 | 否 | 是 (net.sf.cglib) |
适用场景 | 目标类实现了接口,简单 aop 场景 | 目标类没有实现接口,需要代理类自身定义的方法,性能要求较高 |
@enableaspectjautoproxy | 默认值:false | proxytargetclass = true |
在实际开发中,spring aop 会自动选择合适的代理方式。 如果没有特殊需求,可以使用默认配置。
如果需要强制使用 cglib 动态代理,可以设置
@enableaspectjautoproxy(proxytargetclass = true)
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论