Java中的代理模式是一种结构型设计模式,它允许通过代理对象来控制对另一个对象的访问。这种模式在Java中有着广泛的应用,特别是在需要增强对象功能、控制访问权限或实现某些特定行为时。下面会对Java代理模式进行详细解释。
一、基本概念
代理模式涉及三个主要角色:
抽象角色: 通过接口或抽象类声明真实角色实现的业务方法。
代理角色: 实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
**真实角色:**实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。
二、静态代理
定义:静态代理是在编译时就已经确定代理类和被代理类的关系,代理类需要手动实现和被代理类相同的接口,并调用被代理类的方法。
特点:
代理类和被代理类都实现了相同的接口。
代理类的对象在程序运行之前就已经确定。
代理对象中会包含目标对象的引用。
优点:
可以在不修改目标对象的情况下扩展目标对象的功能。
由于代理类是预先编写好的,所以运行时效率较高。
缺点:
需要为每一个目标对象创建一个对应的代理类,如果目标对象很多,则代理类也会很多,增加了代码的复杂性。
如果目标接口增加了新的方法,则代理类和目标类都需要进行相应的修改,增加了维护成本。
三、动态代理
定义:动态代理是在运行时动态生成代理类,不需要手动实现接口,可以在运行时为任意一个类创建代理对象。
分类:
基于接口的动态代理(JDK动态代理): JDK动态代理是Java自带的动态代理机制,它是在运行时通过反射机制生成代理类,代理类实现了指定接口,从而实现对该接口的代理操作。被代理的类必须实现一个接口。
**基于类的动态代理(CGLIB动态代理):**CGLIB动态代理是基于类的代理,它不需要被代理的类实现一个接口,而是直接继承被代理类,通过子类代理的方式实现对被代理类的代理操作。CGLIB是一个优秀的字节码生成库,通过在运行时动态生成字节码,实现对被代理类的代理操作。
优点:
灵活性高,可以动态地为不同的目标对象创建代理,而无需预先知道目标对象的具体类型。
只需要编写一次代理逻辑,就可以适用于所有的代理对象,降低了代码的重复性。
缺点:
JDK动态代理要求目标对象必须实现一个接口,否则无法使用。
由于涉及到反射,运行时效率相对较低。
CGLIB动态代理只能代理非final类和非final方法,且由于是通过继承实现的,可能会受到Java单继承的限制。
四、应用场景
远程代理: 在远程方法调用中,代理模式可以用于隐藏客户端和服务器之间的网络细节。客户端通过代理对象调用远程服务器的方法,代理对象负责处理网络通信、序列化和反序列化等细节。
虚拟代理: 在创建开销较大的对象时,可以使用代理模式延迟对象的创建,直到真正需要使用它时才进行创建。例如,在图像加载时,可以使用虚拟代理来延迟加载图像资源,避免长时间的等待。
安全代理: 代理模式可以用于控制对真实对象的访问权限。代理对象可以在调用真实对象的方法前进行权限检查,确保只有具有相应权限的用户才能访问真实对象。
缓存代理: 代理模式可以用于缓存对象的方法调用结果。当多个客户端需要调用相同的方法时,代理对象可以在第一次调用时缓存方法的结果,以后的调用都直接返回缓存的结果,避免重复计算。
**日志记录:**代理模式可以用于记录方法的调用日志。代理对象可以在调用真实对象的方法前后记录日志信息,用于调试和分析应用程序的运行情况。
五、示例代码
以下是基于JDK动态代理的一个简单例子:
java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 接口
interface UserService {
void addUser();
}
// 实现类
class UserServiceImpl implements UserService {
@Override
public void addUser() {
System.out.println("添加用户");
}
}
// 代理类
class UserServiceProxy implements InvocationHandler {
private Object target;
public Object getProxy(Object target) {
this.target = target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法调用前");
Object result = method.invoke(target, args);
System.out.println("方法调用后");
return result;
}
}
// 测试类
public class DynamicProxyTest {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
UserService proxy = (UserService) new UserServiceProxy().getProxy(userService);
proxy.addUser();
}
}
运行测试类DynamicProxyTest,你将看到以下输出:
方法调用前
添加用户
方法调用后
我们定义了一个接口UserService和其实现类UserServiceImpl,然后定义了一个代理类UserServiceProxy,该类实现了InvocationHandler接口,并重写了其中的invoke方法。在invoke方法中,我们可以实现对真实对象的代理操作,如添加日志、权限检查等。最后,通过调用Proxy.newProxyInstance方法创建代理对象,并返回该代理对象。