代理模式
一个对象A需要给某个对象B提供一个代理以控制对对象A的访问。这个时候访问对象C不适合或者不能直接引用目标对象,所以需要代理对象A作为一个访问对象C和被访问对象B的中介。(比如买电脑,我们都是从电脑城的经销商那里买,不可能直接从华硕的工厂拿货)。
代理模式分为两种:
- 静态代理:静态代理的代理类在程序的编译期间就生成了。
- 动态代理:动态代理的代理类在程序运行的时候才会动态的生成。
结构
- 抽象主题类(Subject):通过接口或者抽象类来声明真实主题和代理对象实现的业务逻辑。
- 真实主题类(Real Subject):实现了抽象主题类的方法,是代理对象所代表的真实对象,是最终被引用的对象
- 代理类(Proxy):提供了和真实主题相同的接口,内部有对真实主题的引用。可以访问、控制和扩展真实主题的功能
静态代理
电脑厂家华硕按照出厂价卖给经销商电脑城,然后电脑城扩展了这个卖的功能加价2000再卖给消费者。
动态代理
在动态代理的方式中,代理类是在程序运行的过程中自动生成的。因此我们是无法在文件中找到一个代表代理类的Java文件的。我们使用了一个ProxyFactory
类来生成代理类。要注意ProxyFactory并不是代理类,而是一个用来生产代理类的工厂,真正的代理类只会在运行过程中在内存中产生。
JDK提供了Proxy
类的newProxyInstance()
方法来返回代理对象。这个方法有三个参数:
ClassLoader loader
, 真实主题类的类加载器Class<?>[] interfaces
, 真实主题类的类所实现的接口InvocationHandler
, 代理对象要调用的方法
其中InvocationHandler
接口的实现子类表示了代理对象的业务逻辑,他需要重写的invoke方法也有三个参数:
proxy
代理对象,就是最终返回的那个代理对象。method
反射出来的方法对象args
调用方法的参数
public class ProxyFactory {
private Asus asus = new Asus();
public ComputerOrz getProxyObj() {
ComputerOrz proxyObj = (ComputerOrz) Proxy.newProxyInstance(
asus.getClass().getClassLoader(),
asus.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
method.invoke(asus, args);
System.out.println("电脑城加价2000卖给消费者(动态代理)");
return null;
}
});
return proxyObj;
}
}
动态代理和静态代理的区别
- Java的动态代理都是对接口进行代理的,也就是说如果你没有定义抽象主题类的话就没法使用动态代理。动态代理把接口中所有的方法都集中到了
invoke()
中,这样在接口中方法很多的时候也可以灵活的处理。而静态代理就需要一个方法一个方法的进行中转。 - 当接口中增加了一个方法以后,静态代理所有的实现类和代理类都要实现这个方法。而动态代理压根没有代理类,我们只需要修改
invoke()
里面的一部分代码即可。 - 静态代理的代理类在程序的编译期间就生成了,动态代理的代理类在程序运行的时候才会动态的生成。
优缺点
优点:
- 代理模式在客户端和目标对象之间起到一个中介和保护的作用
- 代理对象可以扩展目标对象的功能
- 代理模式可以将目标对象和客户端分离,一定程度上降低了耦合度
缺点:
- 系统复杂度提升了
使用场景
- 远程代理
本地连接远程服务器的时候,要实现网络的通信。但是会将网络通信部分2隐藏起来,只给本地服务一个接口,不必过多关心通信部分的细节。 - 防火墙代理
当浏览器使用代理时,防火墙就会将浏览器的请求转给互联网。互联网响应时,代理服务器在把相应装给浏览器 - 保护代理
控制对象的访问,可以根据需要给不用用户提供不同级别的使用权限。