Java 代理

一、基本概念

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代理。

  1. JDK动态代理
    JDK动态代理是Java标准库提供的一种代理机制,基于接口实现。它通过java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口来实现动态代理。
    • 核心组件
      • Proxy类:用于创建代理对象的工具类。
      • InvocationHandler接口:代理对象的调用处理器,用于在代理对象的方法调用前后插入自定义逻辑。
    • 实现原理
      • JDK动态代理通过反射机制生成一个实现了指定接口的代理类。
      • 代理类的方法调用会被转发到InvocationHandler的invoke方法中。
      • 在invoke方法中,可以执行额外的逻辑(如日志记录、权限检查等),然后通过反射调用被代理对象的方法。
    • 特点
      • 优点:
        • 无需引入第三方库,直接使用JDK标准库。
        • 代码简洁,易于理解。
      • 缺点:
        • 只能代理接口,不能代理类。
        • 性能略低于CGLIB代理(因为基于反射)。
    • 使用步骤
      1. 定义一个接口。
      2. 实现该接口的被代理类。
      3. 实现InvocationHandler接口,定义代理逻辑。
      4. 使用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();
    	}
    }
    
  2. CGLIB代理
    CGLIB(Code Generation Library)是一个强大的第三方库,用于在运行时生成类的代理。与JDK动态代理不同,CGLIB可以直接代理类,而不需要接口。
    • 核心组件
      • Enhancer类:用于创建代理对象的工具类。
      • MethodInterceptor接口:代理对象的方法拦截器,用于在方法调用前后插入自定义逻辑。
    • 实现原理
      • CGLIB通过字节码技术生成被代理类的子类。
      • 代理类重写了被代理类的方法,并在方法调用前后插入拦截逻辑。
      • 由于是继承方式,CGLIB可以代理没有实现接口的类。
    • 特点
      • 优点:
        • 可以代理类,无需接口。
        • 性能优于JDK动态代理(因为直接生成字节码,而非基于反射)。
      • 缺点:
        • 需要引入第三方库(CGLIB)。
        • 无法代理final类或final方法(因为基于继承)。
    • 使用步骤
      1. 定义一个类(无需接口)。
      2. 实现MethodInterceptor接口,定义代理逻辑。
      3. 使用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");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值