一、基本概念
Java中的代理(Proxy)是一种设计模式,允许通过代理对象控制对另一个对象的访问。代理模式常用于延迟初始化、访问控制、日志记录等场景。Java提供了两种主要的代理机制:静态代理和动态代理。
1.1 静态代理
静态代理在编译时就已经确定代理类和被代理类的关系。代理类和被代理类实现相同的接口,代理类内部持有被代理类的引用,并在调用被代理类的方法前后执行额外的操作。
// 接口
interface Subject {
void request();
}
// 被代理类
class RealSubject implements Subject {
public void request() {
System.out.println("RealSubject: Handling request.");
}
}
// 代理类
class ProxySubject implements Subject {
private RealSubject realSubject;
public ProxySubject(RealSubject realSubject) {
this.realSubject = realSubject;
}
public void request() {
System.out.println("ProxySubject: Before request.");
realSubject.request();
System.out.println("ProxySubject: After request.");
}
}
// 使用
public class StaticProxyDemo {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
ProxySubject proxySubject = new ProxySubject(realSubject);
proxySubject.request();
}
}
1.2 动态代理
动态代理在运行时动态生成代理类,Java提供了两种实现动态代理的方式:JDK动态代理和CGLIB代理。
- JDK动态代理
JDK动态代理是Java标准库提供的一种代理机制,基于接口实现。它通过java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口来实现动态代理。- 核心组件
- Proxy类:用于创建代理对象的工具类。
- InvocationHandler接口:代理对象的调用处理器,用于在代理对象的方法调用前后插入自定义逻辑。
- 实现原理
- JDK动态代理通过反射机制生成一个实现了指定接口的代理类。
- 代理类的方法调用会被转发到InvocationHandler的invoke方法中。
- 在invoke方法中,可以执行额外的逻辑(如日志记录、权限检查等),然后通过反射调用被代理对象的方法。
- 特点
- 优点:
- 无需引入第三方库,直接使用JDK标准库。
- 代码简洁,易于理解。
- 缺点:
- 只能代理接口,不能代理类。
- 性能略低于CGLIB代理(因为基于反射)。
- 优点:
- 使用步骤
- 定义一个接口。
- 实现该接口的被代理类。
- 实现InvocationHandler接口,定义代理逻辑。
- 使用Proxy.newProxyInstance方法创建代理对象。
- 示例:
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; // 接口 interface Subject { void request(); } // 被代理类 class RealSubject implements Subject { public void request() { System.out.println("RealSubject: Handling request."); } } // 调用处理器 class DynamicProxyHandler implements InvocationHandler { private Object realSubject; public DynamicProxyHandler(Object realSubject) { this.realSubject = realSubject; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("DynamicProxy: Before request."); Object result = method.invoke(realSubject, args); System.out.println("DynamicProxy: After request."); return result; } } // 使用 public class JDKDynamicProxyDemo { public static void main(String[] args) { RealSubject realSubject = new RealSubject(); Subject proxySubject = (Subject) Proxy.newProxyInstance( realSubject.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), new DynamicProxyHandler(realSubject) ); proxySubject.request(); } }
- 核心组件
- CGLIB代理
CGLIB(Code Generation Library)是一个强大的第三方库,用于在运行时生成类的代理。与JDK动态代理不同,CGLIB可以直接代理类,而不需要接口。- 核心组件
- Enhancer类:用于创建代理对象的工具类。
- MethodInterceptor接口:代理对象的方法拦截器,用于在方法调用前后插入自定义逻辑。
- 实现原理
- CGLIB通过字节码技术生成被代理类的子类。
- 代理类重写了被代理类的方法,并在方法调用前后插入拦截逻辑。
- 由于是继承方式,CGLIB可以代理没有实现接口的类。
- 特点
- 优点:
- 可以代理类,无需接口。
- 性能优于JDK动态代理(因为直接生成字节码,而非基于反射)。
- 缺点:
- 需要引入第三方库(CGLIB)。
- 无法代理final类或final方法(因为基于继承)。
- 优点:
- 使用步骤
- 定义一个类(无需接口)。
- 实现MethodInterceptor接口,定义代理逻辑。
- 使用Enhancer类创建代理对象。
- 示例:
import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; // 被代理类 class RealSubject { public void request() { System.out.println("RealSubject: Handling request."); } } // 方法拦截器 class CglibProxyInterceptor implements MethodInterceptor { public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("CglibProxy: Before request."); Object result = proxy.invokeSuper(obj, args); System.out.println("CglibProxy: After request."); return result; } } // 使用 public class CglibProxyDemo { public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(RealSubject.class); // 设置被代理类 enhancer.setCallback(new CglibProxyInterceptor()); // 设置拦截器 RealSubject proxySubject = (RealSubject) enhancer.create(); // 创建代理对象 proxySubject.request(); } }
- 核心组件
1.3 JDK动态代理 vs CGLIB代理
特性 | JDK动态代理 | CGLIB代理 |
---|---|---|
代理方式 | 基于接口实现 | 基于类继承 |
是否需要接口 | 需要 | 不需要 |
性能 | 较低(基于反射) | 较高(基于字节码生成) |
适用范围 | 只能代理实现了接口的类 | 可以代理普通类(除final类和方法) |
依赖 | JDK标准库 | 第三方库(CGLIB) |
实现复杂度 | 简单 | 较复杂 |
二、使用场景
2.1 延迟初始化(Lazy Initialization)
代理可以用于延迟初始化一个资源密集型对象,只有在真正需要时才创建该对象。
- 场景案例
- 数据库连接:在应用程序启动时,可能不需要立即创建所有数据库连接。可以通过代理在第一次访问时创建连接。
- 大文件加载:加载一个大文件时,可以使用代理在真正需要时加载文件内容。
- 示例代码
interface Image {
void display();
}
class RealImage implements Image {
private String filename;
public RealImage(String filename) {
this.filename = filename;
loadFromDisk();
}
private void loadFromDisk() {
System.out.println("Loading image: " + filename);
}
public void display() {
System.out.println("Displaying image: " + filename);
}
}
class ProxyImage implements Image {
private RealImage realImage;
private String filename;
public ProxyImage(String filename) {
this.filename = filename;
}
public void display() {
if (realImage == null) {
realImage = new RealImage(filename); // 延迟初始化
}
realImage.display();
}
}
// 使用
public class LazyInitializationDemo {
public static void main(String[] args) {
Image image = new ProxyImage("large_image.jpg");
image.display(); // 第一次调用时加载
image.display(); // 直接使用已加载的对象
}
}
2.2 访问控制(Access Control)
代理可以用于控制对敏感资源的访问权限,例如检查用户是否有权限执行某个操作。
- 场景案例
- 权限验证:在调用某个方法前,检查用户是否有权限。
- API访问限制:限制某些API的调用频率或次数。
- 代码示例
interface Database {
void query(String sql);
}
class RealDatabase implements Database {
public void query(String sql) {
System.out.println("Executing query: " + sql);
}
}
class ProxyDatabase implements Database {
private RealDatabase realDatabase;
private String userRole;
public ProxyDatabase(String userRole) {
this.userRole = userRole;
}
public void query(String sql) {
if (userRole.equals("admin")) {
if (realDatabase == null) {
realDatabase = new RealDatabase();
}
realDatabase.query(sql);
} else {
System.out.println("Access denied: You do not have permission to execute queries.");
}
}
}
// 使用
public class AccessControlDemo {
public static void main(String[] args) {
Database db = new ProxyDatabase("user");
db.query("SELECT * FROM users"); // 无权限
}
}
2.3 日志记录(Logging)
代理可以用于在方法调用前后记录日志,方便调试和监控。
- 场景案例
- 方法调用日志:记录方法的调用时间、参数和返回值。
- 性能监控:记录方法的执行时间。
- 代码示例
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
interface Service {
void serve();
}
class RealService implements Service {
public void serve() {
System.out.println("RealService: Serving request.");
}
}
class LoggingProxy implements InvocationHandler {
private Object target;
public LoggingProxy(Object target) {
this.target = target;
}
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 LoggingDemo {
public static void main(String[] args) {
Service realService = new RealService();
Service proxyService = (Service) Proxy.newProxyInstance(
realService.getClass().getClassLoader(),
realService.getClass().getInterfaces(),
new LoggingProxy(realService)
);
proxyService.serve();
}
}
2.4 缓存代理(Caching Proxy)
代理可以用于缓存方法的返回值,避免重复计算或重复调用。
- 场景案例
- API调用缓存:缓存外部API的调用结果,减少网络请求。
- 计算结果缓存:缓存复杂计算的结果,提高性能。
- 代码示例
import java.util.HashMap;
import java.util.Map;
interface Calculator {
int calculate(int input);
}
class RealCalculator implements Calculator {
public int calculate(int input) {
System.out.println("Calculating result for input: " + input);
return input * input; // 假设这是一个耗时的计算
}
}
class CachingProxy implements Calculator {
private RealCalculator realCalculator;
private Map<Integer, Integer> cache = new HashMap<>();
public int calculate(int input) {
if (cache.containsKey(input)) {
System.out.println("Returning cached result for input: " + input);
return cache.get(input);
} else {
if (realCalculator == null) {
realCalculator = new RealCalculator();
}
int result = realCalculator.calculate(input);
cache.put(input, result);
return result;
}
}
}
// 使用
public class CachingDemo {
public static void main(String[] args) {
Calculator calculator = new CachingProxy();
System.out.println(calculator.calculate(5)); // 第一次计算
System.out.println(calculator.calculate(5)); // 返回缓存结果
}
}
2.5 远程代理(Remote Proxy)
代理可以用于隐藏远程调用的复杂性,使客户端像调用本地方法一样调用远程服务。
- 场景案例
- RPC调用:通过代理调用远程服务。
- 分布式系统:在分布式系统中,代理可以封装网络通信细节。
- 代码示例
interface RemoteService {
String getData();
}
class RemoteServiceProxy implements RemoteService {
public String getData() {
// 模拟远程调用
System.out.println("Calling remote service...");
return "Data from remote service";
}
}
// 使用
public class RemoteProxyDemo {
public static void main(String[] args) {
RemoteService remoteService = new RemoteServiceProxy();
System.out.println(remoteService.getData());
}
}
2.6 事务管理(Transaction Management)
代理可以用于管理事务,例如在方法调用前后开启和提交事务。
- 场景案例
- 数据库事务:在方法执行前开启事务,在方法执行后提交或回滚事务。
- 分布式事务:管理跨多个服务的分布式事务。
- 代码示例
interface UserService {
void saveUser(String user);
}
class RealUserService implements UserService {
public void saveUser(String user) {
System.out.println("Saving user: " + user);
}
}
class TransactionProxy implements UserService {
private RealUserService realUserService;
public TransactionProxy(RealUserService realUserService) {
this.realUserService = realUserService;
}
public void saveUser(String user) {
System.out.println("Starting transaction...");
try {
realUserService.saveUser(user);
System.out.println("Committing transaction...");
} catch (Exception e) {
System.out.println("Rolling back transaction...");
}
}
}
// 使用
public class TransactionDemo {
public static void main(String[] args) {
UserService userService = new TransactionProxy(new RealUserService());
userService.saveUser("John Doe");
}
}