在 Java 编程中,代理模式是一种重要的设计模式,通常用于控制对某个对象的访问。代理模式可以分为三种主要类型:静态代理、JDK 动态代理和 CGLIB 代理。下面我们将分别解析这三种代理模式及其优缺点,并提供代码示例。
一、静态代理
静态代理是在编译时创建代理类。在使用静态代理时,我们需要为每一个被代理的类编写一个代理类。静态代理的优点是比较简单易懂,但缺点在于针对每个被代理的类都要写一个代理类,增加了代码的重复性。
示例代码
// 被代理类
interface RealSubject {
void request();
}
class RealSubjectImpl implements RealSubject {
@Override
public void request() {
System.out.println("RealSubject: Request.");
}
}
// 代理类
class ProxySubject implements RealSubject {
private RealSubject realSubject;
public ProxySubject(RealSubject realSubject) {
this.realSubject = realSubject;
}
@Override
public void request() {
System.out.println("ProxySubject: Before request.");
realSubject.request();
System.out.println("ProxySubject: After request.");
}
}
// 测试
public class StaticProxyTest {
public static void main(String[] args) {
RealSubject realSubject = new RealSubjectImpl();
ProxySubject proxySubject = new ProxySubject(realSubject);
proxySubject.request();
}
}
二、JDK 动态代理
JDK 动态代理支持在运行时创建代理类。它要求被代理的类必须实现接口。JDK 动态代理是通过 java.lang.reflect.Proxy
类实现的,灵活性较高。
示例代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 被代理类接口
interface Subject {
void request();
}
// 被代理类实现
class SubjectImpl implements Subject {
@Override
public void request() {
System.out.println("SubjectImpl: Request.");
}
}
// 动态代理处理器
class DynamicProxyHandler implements InvocationHandler {
private Object target;
public DynamicProxyHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("DynamicProxy: Before request.");
Object result = method.invoke(target, args);
System.out.println("DynamicProxy: After request.");
return result;
}
}
// 测试
public class DynamicProxyTest {
public static void main(String[] args) {
Subject realSubject = new SubjectImpl();
Subject proxySubject = (Subject) Proxy.newProxyInstance(
realSubject.getClass().getClassLoader(),
realSubject.getClass().getInterfaces(),
new DynamicProxyHandler(realSubject));
proxySubject.request();
}
}
三、CGLIB 代理
CGLIB(Code Generation Library)是一个强大的、高性能的代码生成包,可以在运行时动态创建被代理类的子类。这种代理方式不要求被代理类实现接口,适合于不使用接口的场景。
示例代码
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
// 被代理类
class CglibSubject {
public void request() {
System.out.println("CglibSubject: Request.");
}
}
// CGLIB 代理处理器
class CglibProxy implements MethodInterceptor {
private Object target;
public CglibProxy(Object target) {
this.target = target;
}
@Override
public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("CglibProxy: Before request.");
Object result = proxy.invoke(target, args);
System.out.println("CglibProxy: After request.");
return result;
}
}
// 测试
public class CglibProxyTest {
public static void main(String[] args) {
CglibSubject subject = new CglibSubject();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(CglibSubject.class);
enhancer.setCallback(new CglibProxy(subject));
CglibSubject proxySubject = (CglibSubject) enhancer.create();
proxySubject.request();
}
}
四、总结对比
- 静态代理:
- 优点:简单易懂;实施成本低。
-
缺点:类的数量增加;缺乏灵活性。
-
JDK 动态代理:
- 优点:灵活性高;可以在运行时生成代理。
-
缺点:只能代理实现了接口的类;性能相对较低。
-
CGLIB 代理:
- 优点:可以代理没有实现接口的类;性能高于 JDK 动态代理。
- 缺点:会生成子类,不会代理 final 类;引入了外部依赖。
综上所述,选择合适的代理方式需要根据具体的应用场景和需求来决定。对于简单的场景,静态代理就足够用了;对于需要动态生成代理的场景,JDK 动态代理是较好的选择;而在需要代理没有接口的类时,CGLIB 代理表现较佳。