Java中的代理是一种设计模式,它允许一个对象(代理对象)代表另一个对象(真实对象)来控制对真实对象的访问。代理对象通常拥有与真实对象相同的接口,这使得客户端可以通过代理来访问真实对象,而不需要知道或直接与真实对象交互。代理模式有多种用途,包括远程代理、虚拟代理、保护代理等。
在Java中,代理可以通过以下两种方式实现:
静态代理:在编译时就已经确定代理关系,代理类需要手动编写。通常使用接口来定义代理类和真实类的共同接口,然后实现代理类和真实类,代理类中调用真实类的方法,并在调用前后可以添加额外的逻辑。
动态代理:在运行时动态生成代理类,无需手动编写代理类。Java提供了java.lang.reflect.Proxy
类和java.lang.reflect.InvocationHandler
接口来支持动态代理。这种方式更灵活,适用于代理多个不同类型的对象。
下面详细介绍动态代理的使用和应用:
使用步骤:
定义接口:首先,您需要定义一个接口,该接口将被代理类和代理对象实现。这个接口定义了真实对象和代理对象的方法。
创建真实对象:实现接口的真实对象,它是您要代理的对象。
实现InvocationHandler接口:创建一个实现java.lang.reflect.InvocationHandler
接口的类,它将用于处理代理对象的方法调用。在invoke
方法中,您可以在调用真实对象方法前后执行额外的逻辑。
创建代理对象:使用java.lang.reflect.Proxy
类的newProxyInstance
方法来创建代理对象。您需要提供类加载器、实现的接口数组和InvocationHandler
的实例。
使用代理对象:现在,您可以使用代理对象来调用方法,它会在调用前后执行您定义的逻辑。
示例:
假设我们有一个简单的接口UserService
,它有一个方法getUser
:
public interface UserService {
User getUser(int userId);
}
public class User {
private int userId;
private String username;
// Getters and setters
}
我们可以创建一个动态代理,记录方法调用的时间,并在真正调用之前打印日志:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class UserServiceProxy implements InvocationHandler {
private UserService realUserService;
public UserServiceProxy(UserService realUserService) {
this.realUserService = realUserService;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在方法调用前记录时间
long startTime = System.currentTimeMillis();
// 调用真正的方法
Object result = method.invoke(realUserService, args);
// 在方法调用后记录时间并打印日志
long endTime = System.currentTimeMillis();
System.out.println("Method " + method.getName() + " took " + (endTime - startTime) + " milliseconds.");
return result;
}
public static void main(String[] args) {
// 创建真实对象
UserService realUserService = new RealUserService();
// 创建代理对象
UserService proxy = (UserService) Proxy.newProxyInstance(
UserService.class.getClassLoader(),
new Class[]{UserService.class},
new UserServiceProxy(realUserService)
);
// 使用代理对象
User user = proxy.getUser(123);
}
}
这个示例中,UserServiceProxy
实现了InvocationHandler
接口,并在invoke
方法中添加了日志记录。然后,我们通过Proxy.newProxyInstance
方法创建了代理对象,并在调用getUser
方法时记录了方法执行时间。
应用场景:
代理模式是一种强大的设计模式,可以提高代码的灵活性、可维护性和可扩展性,尤其是在需要添加额外逻辑或控制对对象的访问时非常有用。