JDK 动态代理和 CGLIB 动态代理是两种常用的 Java 动态代理技术,它们在实现原理和应用场景上存在一些区别。
JDK 动态代理:JDK 动态代理是基于接口的代理技术。它利用 Java 的反射机制,在运行时创建代理类和代理实例。JDK 动态代理要求目标对象必须实现至少一个接口,代理类会实现与目标对象相同的接口,并且具有相同的方法签名。当代理对象的方法被调用时,实际上是通过InvocationHandler 将方法调用转发给了目标对象。JDK 动态代理只能代理接口,无法代理类。
优点:
- 简单易用,无需额外的库或依赖。
- 适用于代理接口的场景。
缺点:
- 只能代理实现了接口的类。
- 代理类的性能相对较低。
CGLIB 动态代理: CGLIB(Code Generation Library)动态代理是基于类的代理技术。它通过生成目标类的子类作为代理类,并重写目标方法来实现代理功能。CGLIB 动态代理不要求目标对象实现接口,可以代理普通的类。当代理对象的方法被调用时,实际上是通过MethodInterceptor 将方法调用转发给了目标对象。
优点:
- 可以代理普通的类,无需实现接口。
- 生成的代理类性能比 JDK 动态代理高。
缺点:
- 生成的代理类是目标类的子类,因此无法代理被 final 修饰的类和方法。
- 需要依赖 CGLIB 库。
选择使用 JDK 动态代理还是 CGLIB 动态代理取决于具体的场景和需求。如果目标对象只实现了接口,并且对性能要求较低,可以选择 JDK 动态代理;如果目标对象为普通类,或者需要更高的性能,可以选择 CGLIB 动态代理。
总结起来,JDK 动态代理适用于代理接口,而 CGLIB 动态代理适用于代理类。
下面是一个使用 JDK 动态代理的示例代码:
首先,定义一个接口UserService
,包含了一些方法:
publicinterfaceUserService { voidaddUser(Stringusername); voiddeleteUser(Stringusername); }
接下来,创建一个实现了该接口的目标对象UserServiceImpl
:
publicclassUserServiceImplimplementsUserService { publicvoidaddUser(Stringusername) { System.out.println("添加用户:"+username); } publicvoiddeleteUser(Stringusername) { System.out.println("删除用户:"+username); } }
然后,编写一个 InvocationHandler 实现类UserInvocationHandler
,用于处理代理对象的方法调用:
importjava.lang.reflect.InvocationHandler; importjava.lang.reflect.Method; publicclassUserInvocationHandlerimplementsInvocationHandler { privatefinalUserServicetarget; // 目标对象publicUserInvocationHandler(UserServicetarget) { this.target=target; } publicObjectinvoke(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 { publicObjectintercept(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
接口的自定义拦截器,在拦截器中可以在方法调用前后执行额外的逻辑。