1.代理模式
前置知识
1.1什么是代理模式
代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
1.2代理分几种
代理分两种
- 静态代理
- 动态代理
1.2为什么要用代理模式(代理模式解决了什么问题)?
在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。
以卖房(Subject的request方法)为例,卖房者(RealSubject)想卖房,但是不想搞那么多麻烦事,不想天天招买房的骚扰,就委托中介(ProxySubject)去帮忙卖房,中介帮忙卖房,当然不会简单的卖房,可能还需要收取中介费(preRequest()和postRequest()方法)等等附加收费,最终买房者(客户端Main)买房,找到的是中介,不会与房主接触,与房主接触的是中介,买房者最终跟中介买房,完成操作。
这个是静态代理。真实角色必须是事先已经存在的,并将其作为代理对象的内部属性。但是实际使用时,一个真实角色必须对应一个代理角色,如果大量使用会导致类的急剧膨胀;此外,如果事先并不知道真实角色,该如何使用代理呢?这个问题可以通过Java的动态代理类来解决。
1.3什么情况下用到代理模式?
- 远程代理:隐藏一个对象存在于不同地址空间的事实;远程机器可能具有更好的计算性能与处理速度,可以快速响应并处理客户端请求。
- 虚拟代理:是根据需要创建开销很大的对象,通过它来存放实例化需要很长时间的真实对象。这样就可以达到性能的最优化,比如打开一个网页,这个网页里面包含了大量的文字和图片,但我们可以很快看到文字,但是图片却是一张一张地下载后才能看到,那些未打开的图片框,就是通过虚拟代里来替换了真实的图片,此时代理存储了真实图片的路径和尺寸;
- 安全代理:用来控制真实对象访问时的权限。一般用于对象应该有不同的访问权限的时候;
- 指针引用:是指当调用真实的对象时,代理处理另外一些事。比如计算真实对象的引用次数,这样当该对象没有引用时,可以自动释放它,或当第一次引用一个持久对象时,将它装入内存,或是在访问一个实际对象前,检查是否已经释放它,以确保其他对象不能改变它。这些都是通过代理在访问一个对象时附加一些内务处理;
-------------------
1.静态代理
- Subject: 抽象角色,声明真实对象和代理对象的共同接口
- RealSubject: 真实角色,真实的调用角色
- Proxy: 代理角色,在执行真实角色之前可以进行其他的附加操作。
代理角色与真实角色都要继承自抽象角色。
下面通过代码实现
Subject.java
/** * 抽象角色 * 抽象方法,只提供方法,不提供具体实现,具体实现由子类提供 */ public abstract class Subject { public abstract void request(); }
RealSubject.java
/** * 真实角色 */ public class RealSubject extends Subject { @Override public void request() { System.out.println("真实的业务"); } }
ProxySubject.java
/** * 代理角色 * 调用真实角色的同时可以执行附加操作 */ public class ProxySubject extends Subject { private RealSubject realSubject = new RealSubject(); @Override public void request() { preRequest(); realSubject.request();// 真实角色执行的操作 postRequest(); } private void preRequest() { System.out.println("真实角色执行前的操作"); } private void postRequest() { System.out.println("真实角色执行前的操作"); } }
执行测试一下
不加代理的情况
public static void main(String[] args) { Subject proxySubject = new RealSubject(); proxySubject.request(); }
执行结果
加上代理
public static void main(String[] args) { Subject proxySubject = new ProxySubject(); proxySubject.request(); }
执行结果
2.动态代理
有了静态代理,为什么还需要动态代理?
如果使用静态代理,那么真实角色必须是事先已经存在的,并将其作为代理对象的内部成员属性。如果一个真实角色必须对应一个代理角色,这将导致系统中的类个数急剧增加,因此需要想办法减少系统中类的个数,此外,如何在事先不知道真实角色的情况下使用代理角色,这都是动态代理需要解决的问题。
Java代码实现
Subject.java
/** * 抽象角色 * 这里必须为接口 */ public interface Subject { void request(); }
RealSubject.java
/** * 真实角色 * 实现接口 */ public class RealSubject implements Subject { @Override public void request() { System.out.println("真实的业务"); } }
DynamicProxy.java
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; /** * 动态代理,在运行时生成class,在生成它时必须提供一组interface给它 * 可以代理Object对象,也就是任何对象 */ public class DynamicProxy implements InvocationHandler { private Object object; //真实对象的引用 public DynamicProxy(Object object) { this.object = object; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { preInvoke(); method.invoke(object, args);// 真实的调用对象 afterInvoke(); return null; } private void preInvoke() { System.out.println("调用前做点操作"); } private void afterInvoke() { System.out.println("调用后做点操作"); } }
下面来测试一下
public class Main { public static void main(String[] args) { Subject realSubject = new RealSubject(); InvocationHandler handler = new DynamicProxy(realSubject); Subject subject = (Subject) Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), handler); subject.request(); }
运行结果