Java 代理模式详解
约 825 字大约 3 分钟
1. 代理模式
代理模式允许我们使用代理对象代替真实对象的访问,以便在不修改目标对象的前提下,为其提供额外的功能,通常用于扩展目标对象的功能,例如在方法执行前后增加自定义操作。
2. 静态代理
静态代理是在编译时就确定了目标类与代理类的关系。每个目标类都需要一个单独的代理类,这使得静态代理灵活性较差。
静态代理实现步骤
- 定义接口及其实现类。
- 创建代理类实现该接口,并在代理类中注入目标对象。
- 在代理类中实现增强逻辑。
public interface SmsService {
String send(String message);
}
public class SmsServiceImpl implements SmsService {
public String send(String message) {
System.out.println("send message:" + message);
return message;
}
}
public class SmsProxy implements SmsService {
private final SmsService smsService;
public SmsProxy(SmsService smsService) {
this.smsService = smsService;
}
@Override
public String send(String message) {
System.out.println("before method send()");
smsService.send(message);
System.out.println("after method send()");
return null;
}
}
public class Main {
public static void main(String[] args) {
SmsService smsService = new SmsServiceImpl();
SmsProxy smsProxy = new SmsProxy(smsService);
smsProxy.send("java");
}
}
3. 动态代理
动态代理在运行时创建代理类,不需要为每个目标类都写代理类,且可以代理没有实现接口的类。Java 提供了两种主要的动态代理实现:JDK 动态代理和 CGLIB 动态代理。
3.1 JDK 动态代理
JDK 动态代理只能代理实现了接口的类。核心类为 Proxy
和 InvocationHandler
,通过 Proxy.newProxyInstance()
方法生成代理对象。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class DebugInvocationHandler implements InvocationHandler {
private final Object target;
public DebugInvocationHandler(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;
}
}
public class JdkProxyFactory {
public static Object getProxy(Object target) {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new DebugInvocationHandler(target)
);
}
}
SmsService smsService = (SmsService) JdkProxyFactory.getProxy(new SmsServiceImpl());
smsService.send("java");
3.2 CGLIB 动态代理
CGLIB 动态代理通过生成目标类的子类来实现代理,适用于没有实现接口的类。核心类为 Enhancer
和 MethodInterceptor
。
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import net.sf.cglib.proxy.Enhancer;
public class DebugMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("before method " + method.getName());
Object result = methodProxy.invokeSuper(o, args);
System.out.println("after method " + method.getName());
return result;
}
}
public class CglibProxyFactory {
public static Object getProxy(Class<?> clazz) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(new DebugMethodInterceptor());
return enhancer.create();
}
}
AliSmsService aliSmsService = (AliSmsService) CglibProxyFactory.getProxy(AliSmsService.class);
aliSmsService.send("java");
4. JDK 动态代理与 CGLIB 动态代理对比
- 代理对象类型:JDK 动态代理只能代理接口,CGLIB 可以代理类。
- 性能:JDK 动态代理通常性能更好,尤其是随着 JDK 版本更新,CGLIB 的效率较低。
- 不可代理类型:CGLIB 无法代理声明为
final
的类和方法。
5. 静态代理与动态代理的对比
- 灵活性:动态代理更灵活,能够无需为每个目标类单独创建代理类。
- JVM 层面:静态代理在编译时生成类,动态代理在运行时生成类字节码。
6. 总结
代理模式提供了两种实现方式:静态代理和动态代理。静态代理适用于目标对象较少的场景,而动态代理在框架中得到了广泛应用,尤其是在需要代理大量类时。理解代理模式对学习和使用框架非常有帮助。