1. 基本概念
Java 中的动态代理是一种在运行时创建代理对象的机制,这些代理对象在运行时动态地实现特定接口或者继承特定类,并将方法调用转发到一个处理程序(handler)。
动态代理是在程序运行时创建的,而不是在编译时创建的,因此可以根据需要动态地创建代理对象。
Java 中的动态代理通常使用 java.lang.reflect.Proxy 类来实现。通过 Proxy.newProxyInstance() 方法,你可以创建一个动态代理对象,该方法接受三个参数:类加载器、要实现的接口列表和一个 InvocationHandler 对象。
动态代理的实现基于 Java 的反射机制。在运行时,当客户端调用代理对象的方法时,这些方法调用会被转发到InvocationHandler 的 invoke() 方法中,开发者可以在 invoke()
方法中编写自定义逻辑来处理方法调用。
2. 代码演示
首先我们需要有一个接口,及实现了该接口的实现类. 具体的接口和实现类略…
2.1 基于JDK动态代理
我们知道,经过代理的方法的实际执行者是 InvocationHandler 的 invoke() 方法. 我们首先定义一个实现了 InvocationHandler 接口的实现类作为我们的代理类,并实现其invoke() 方法.
public class MyInvocationHandler implements InvocationHandler { // 代理目标对象 private Object target = null; public MyInvocationHandler(Object target) { this.target = target; } // 实现增强逻辑 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println(method.getName() + " 方法执行前."); Object invoke = method.invoke(target, args); System.out.println(method.getName() + " 方法执行后."); return invoke; } // 构造代理对象 并返回 public Object getProxy(){ return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } }
实现了上面的接口后,我们来实测:
public static void main(String[] args) { TeacherService teacherService = new TeacherServiceImpl(); MyInvocationHandler myInvocationHandler = new MyInvocationHandler(teacherService); TeacherService proxy = (TeacherService) myInvocationHandler.getProxy(); proxy.add(); }
执行代码,打印如下:
可以看到,我们的原始方法.已经被增强了.
2.2 基于CGlib的动态代理
import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class TestCglib { public static void main(String[] args) { //1 创建原始对象 UserService userService = new UserService(); /* 2 通过cglib⽅式创建动态代理对象 Enhancer.setClassLoader() Enhancer.setSuperClass() Enhancer.setCallback(); ---> MethodInterceptor(cglib) Enhancer.create() ---> 代理 */ Enhancer enhancer = new Enhancer(); enhancer.setClassLoader(TestCglib.class.getClassLoader()); enhancer.setSuperclass(userService.getClass()); MethodInterceptor interceptor = new MethodInterceptor() { //等同于 InvocationHandler --- invoke @Override public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("---cglib log----"); Object ret = method.invoke(userService, args); return ret; } }; enhancer.setCallback(interceptor); UserService userServiceProxy = (UserService) enhancer.create(); userServiceProxy.login("suns", "123345"); userServiceProxy.register(new User()); } }
3. JDK代理和CGlib代理的区别
JDK 代理(也称为接口代理)和 CGLib 代理(也称为子类代理)是两种常见的代理方式,它们在实现上有一些区别:
- JDK 代理:
- JDK 代理要求目标对象必须实现一个接口,代理对象和目标对象都实现了这个接口。
- JDK 代理通过 java.lang.reflect.Proxy 类实现,动态地生成一个实现了目标接口的代理类,然后通过这个代理类创建代理对象。
- JDK 代理只能代理实现了接口的类,无法代理未实现接口的类。
- JDK 代理在运行时通过反射来调用目标对象的方法,因此会有一定的性能开销。
- CGLib 代理:
- CGLib 代理不要求目标对象实现接口,它直接继承目标对象并重写其中的方法。
- CGLib 代理通过字节码技术,在运行时动态地生成目标对象的子类作为代理对象。
- CGLib 代理可以代理未实现接口的类。
- CGLib 代理在运行时通过直接调用目标对象的方法来实现代理,因此性能上比 JDK 代理略高。
JDK 代理和 CGLib 代理各有其适用的场景:
- 如果目标对象实现了接口,推荐使用 JDK 代理,因为它更简单、更稳定,而且在 Java 标准库中提供了支持。
- 如果目标对象没有实现接口,或者你想要在运行时动态生成代理类,可以使用 CGLib 代理。
- 在性能要求较高的场景下,CGLib 代理通常比 JDK 代理更适合,但需要注意 CGLib 代理对目标对象的要求较多,比如不能是 final 类型,且目标方法不能是 final 类型的。
4. 动态代理的作用
动态代理的主要用途包括:
AOP(面向切面编程):动态代理是 AOP 的核心实现机制之一,通过动态代理可以在方法执行前后进行一些横切逻辑的处理,例如日志记录、性能监控、事务管理等。
远程方法调用(RMI):动态代理可以在客户端和服务端之间进行通信,客户端通过代理对象调用远程方法,代理对象负责将方法调用序列化并发送到服务端,服务端收到请求后执行相应的方法并返回结果给客户端。
延迟加载(Lazy Loading):动态代理可以在需要时延迟加载对象,例如在访问某个对象的属性时,可以通过代理对象在需要时再去加载真实对象,从而提高程序的性能。
权限控制:动态代理可以在方法执行前进行权限验证,根据权限决定是否允许执行该方法。
总的来说,动态代理为 Java 提供了一种灵活的机制,可以在运行时动态地创建代理对象,并在代理对象中实现特定的逻辑,从而实现各种功能。