Java的动态代理机制允许在运行时创建代理类和代理对象,用于在调用实际对象的方法之前和之后执行额外的逻辑。这种代理机制有助于实现一些横切关注点(cross-cutting concerns),如日志记录、性能监测、事务管理等。
Java的动态代理主要有两种实现方式:基于接口的动态代理(JDK动态代理)和基于类的动态代理(CGLIB动态代理)。
-
基于接口的动态代理(JDK动态代理):
- 首先,定义一个接口
UserService
,其中包含需要代理的方法。
public interface UserService { void save(); void update(); }
- 然后,创建一个实现该接口的类
UserServiceImpl
,用于实际执行业务逻辑。
public class UserServiceImpl implements UserService { @Override public void save() { System.out.println("保存用户信息"); } @Override public void update() { System.out.println("更新用户信息"); } }
- 接下来,创建一个实现
InvocationHandler
接口的类UserInvocationHandler
,用于处理代理类的方法调用,并在调用被代理对象的方法之前和之后执行额外的逻辑。
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class UserInvocationHandler implements InvocationHandler { private final UserService target; public UserInvocationHandler(UserService target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("调用方法前的额外逻辑"); // 调用被代理对象的方法 Object result = method.invoke(target, args); System.out.println("调用方法后的额外逻辑"); return result; } }
- 最后,通过使用
Proxy
类的newProxyInstance()
方法创建代理对象,并调用代理对象的方法。
import java.lang.reflect.Proxy; public class Main { public static void main(String[] args) { UserService userService = new UserServiceImpl(); UserInvocationHandler handler = new UserInvocationHandler(userService); // 创建代理对象 UserService proxy = (UserService) Proxy.newProxyInstance( userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), handler); // 调用代理对象的方法 proxy.save(); proxy.update(); } }
- 运行以上代码,输出结果为:
调用方法前的额外逻辑 保存用户信息 调用方法后的额外逻辑 调用方法前的额外逻辑 更新用户信息 调用方法后的额外逻辑
JDK动态代理的核心原理是通过反射机制在运行时创建代理类,并通过实现被代理接口来实现代理。代理对象的方法调用会被转发给
InvocationHandler
接口的invoke()
方法进行处理。 - 首先,定义一个接口
-
基于类的动态代理(CGLIB动态代理):
- 首先,引入 CGLIB 库作为依赖。
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> </dependency>
- 然后,创建一个类
UserServiceImpl
,用于实际执行业务逻辑。
public class UserServiceImpl { public void save() { System.out.println("保存用户信息"); } public void update() { System.out.println("更新用户信息"); } }
- 接下来,创建一个类
MethodInterceptorImpl
,实现MethodInterceptor
接口并重写intercept()
方法,用于处理代理类的方法调用,并在调用被代理对象的方法之前和之后执行额外的逻辑。
import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class MethodInterceptorImpl implements MethodInterceptor { @Override public Object intercept(Object object, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("调用方法前的额外逻辑"); // 调用被代理对象的方法 Object result = proxy.invokeSuper(object, args); System.out.println("调用方法后的额外逻辑"); return result; } }
- 最后,通过使用
Enhancer
类创建代理对象,并调用代理对象的方法。
import net.sf.cglib.proxy.Enhancer; public class Main { public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(UserServiceImpl.class); enhancer.setCallback(new MethodInterceptorImpl()); // 创建代理对象 UserServiceImpl proxy = (UserServiceImpl) enhancer.create(); // 调用代理对象的方法 proxy.save(); proxy.update(); } }
- 运行以上代码,输出结果为:
调用方法前的额外逻辑 保存用户信息 调用方法后的额外逻辑 调用方法前的额外逻辑 更新用户信息 调用方法后的额外逻辑
CGLIB动态代理的核心原理是通过继承被代理类并重写其方法来实现代理。代理对象的方法调用会被转发给
MethodInterceptor
接口的intercept()
方法进行处理。
基于接口的动态代理(JDK动态代理)和基于类的动态代理(CGLIB动态代理)在不同的场景下有不同的使用情况和原理分析。
- 基于接口的动态代理(JDK动态代理)的使用场景和原理分析:
- 使用场景:JDK动态代理适用于已经定义了接口的情况。代理对象必须实现一个或多个接口,并且代理方法与被代理对象的方法具有相同的方法签名。
- 原理分析:JDK动态代理通过反射机制在运行时创建代理类,代理类实现了被代理接口,并在代理类中的方法调用中添加了额外的逻辑。当代理对象的方法被调用时,实际执行的是
InvocationHandler
接口的invoke()
方法,该方法中会处理代理对象的方法调用以及额外逻辑的执行。
- 基于类的动态代理(CGLIB动态代理)的使用场景和原理分析:
- 使用场景:CGLIB动态代理适用于没有定义接口的情况。代理对象是被代理类的子类,并且可以代理类中的所有方法,包括final方法。
- 原理分析:CGLIB动态代理通过继承被代理类并重写其方法来实现代理。在代理对象的方法调用中,会首先执行代理类中的逻辑,然后通过
MethodInterceptor
接口的intercept()
方法处理被代理对象的方法调用以及额外逻辑的执行。
使用场景的选择取决于具体的需求和情况。一般来说,如果已经定义了接口,可以使用JDK动态代理,而如果没有定义接口或者需要代理非final方法,可以使用CGLIB动态代理。
总体来说,动态代理机制为我们提供了一种灵活且可扩展的方式来在运行时为对象添加额外的逻辑。无论是JDK动态代理还是CGLIB动态代理,都是通过动态创建代理类来实现代理对象的生成,并在代理类中拦截方法调用,执行额外的逻辑。这种机制在实现一些横切关注点时非常有用,如日志记录、性能监测、事务管理等。