JDK 和 CGLib 实现动态代理和区别
在日常的开发中,Spring AOP 是一个非常常用的功能。谈到 AOP,自然离不开动态代理。
那么,基于 JDK 和 CGLib 如何实现动态代理,他们之间的区别和适用场景是什么呢?接下来,我们一起来探讨一下这个问题。
JDK 如何实现动态代理?话不多说,我们直接对照着代码来查看。
代码示例 Hello 接口 public interface HelloInterface { /** * 代理的目标方法 */ void sayHello(); /** * 未被代理处理的方法 */ void noProxyMethod(); } Hello 实现类 public class HelloImpl implements HelloInterface { @Override public void sayHello() { System.out.println("proxyMethod:sayHello"); } @Override public void noProxyMethod() { System.out.println("noProxyMethod"); } } MyInvocationHandler 实现 InvocationHandler 接口类 public class MyInvocationHandler implements InvocationHandler { /** * 目标对象 */ private Object target; /** * 构造方法 * * @param target */ public MyInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName = method.getName(); if ("sayHello".equals(methodName)) { // 比方说,mybaitis 中的 PooledConnection 利用 jdk 动态代理重新实现了 close 方法 System.out.println("change method"); return null; } System.out.println("invoke method"); Object result = method.invoke(target, args); return result; } }动态代理神奇的地方就是:
代理对象是在程序运行时产生的,而不是编译期;
对代理对象的所有接口方法调用都会转发到InvocationHandler.invoke()方法,在invoke()方法里我们可以加入任何逻辑,比如修改方法参数,加入日志功能、安全检查功能等。
⚠️注意:从 Object 中继承的方法,JDK Proxy 会把hashCode()、equals()、toString()这三个非接口方法转发给 InvocationHandler,其余的 Object 方法则不会转发。详见 JDK Proxy官方文档。
代码测试 public class MyDynamicProxyTest { public static void main(String[] args) { HelloInterface hello = new HelloImpl(); MyInvocationHandler handler = new MyInvocationHandler(hello); // 构造代码实例 HelloInterface proxyInstance = (HelloInterface) Proxy.newProxyInstance( HelloImpl.class.getClassLoader(), HelloImpl.class.getInterfaces(), handler); // 代理调用方法 proxyInstance.sayHello(); proxyInstance.noProxyMethod(); } }打印的日志信息如下:
关键要点结合上面的演示,我们小结一下 JDK 动态代理的实现,包括三个步骤:
1.定义一个接口
比如上面的 HelloInterface,Jdk 的动态代理是基于接口,这就是代理接口。
2.编写接口实现类
比如上面的 HelloImpl,这个就是目标对象,也就是被代理的对象类。
3.编写一个实现 InvocationHandler 接口的类,代理类的方法调用会被转发到该类的 invoke() 方法。
比如上面的 MyInvocationHandler。
CGLib 如何实现动态代理? 代码示例 Hello 类无需定义和实现接口。
public class Hello { public String sayHello(String name) { System.out.println("Hello," + name); return "Hello," + name; } } CglibMethodInterceptor 实现 MethodInterceptor /** * 实现一个MethodInterceptor,方法调用会被转发到该类的intercept()方法。 */ public class CglibMethodInterceptor implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("intercept param is " + Arrays.toString(args)); System.out.println("before===============" + method); // 这里可以实现增强的逻辑处理s Object result = methodProxy.invokeSuper(obj, args); // 这里可以实现增强的逻辑处理 System.out.println("after===============" + method); return result; } }⚠️注意:对于从Object中继承的方法,CGLIB代理也会进行代理,如hashCode()、equals()、toString()等,但是getClass()、wait()等方法不会(因为其他方法是 final,无法被代理),CGLIB 无法代理她们。
pom 依赖 <dependencies> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.1_3</version> </dependency> </dependencies> 代码测试 public class CglibTest { /** * 在需要使用 Hello 的时候,通过CGLIB动态代理获取代理对象 */ public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(Hello.class); enhancer.setCallback(new CglibMethodInterceptor()); // 给目标对象创建一个代理对象 Hello hello = (Hello) enhancer.create(); hello.sayHello("Alan"); }打印的日志如下: