代理模式:
首先来看代理模式的典型类图:
代理模式也叫委托模式, 状态模式,策略模式,访问者模式也都采用了这种模式。
代理模式具有如下优点:
- 职责清晰。 真实的角色只实现实际的业务逻辑,不关心非本职责的事务,通过后期的代理完成一件事务
- 易扩展。
public interface Subject{
public void request();
}
public class RealSubject implements Subject{
@Override
public void request(){
//TODO
}
}
public class ProxySubject implements Subject{
private Subject realSubject;
@Override
public void request(){
//TODO
this.before();
this.realSubject.request();
this.after();
}
private void before(){}
private void after(){}
}
-
普通代理模式
普通代理的代理者
public class GamePlayerProxy implements IGamePlayer{private IGamePlayer gamePlayer=null;
public GamePlayerProxy(String name){try{ gamePlayer=new GamePlayer(this,name); }catch(Exception e){ //TODO
}
}
public void killBoss(){
this.gamePlayer.killBoss();
}
public void login(String user,String name){
this.gamePlayer.login(user,password);
}
public void upgrade(){
this.gamePlayer.upgrade();
}
}普通代理就如上面代码所示。
-
强制代理模式
强制代理和普通代理不同。 强制代理是,代理类必须是真实类指定的,也就是说,代理类不能是自己new的,而必须是先创建真实类,从真实类创建代理类。这样才是有效的代理。 真实类在执行前,会检验,是否存在指定代理类。
- 动态代理模式
利用java 的Proxy 类。
IGamePlayer.java
public interface IGamePlayer{
public void hello();
}
GamePlayer.java
public class GamePlayer implements IGamePlayer {
@Override
public void hello() {
System.out.println(“this is GamePlayer”);
}
}
GamePlayerInvocationHandler.java
public class GamePlayerInvocationHandler implements InvocationHandler{
private IGamePlayer gamePlayer;
public GamePlayerInvocationHandler(IGamePlayer gamePlayer) {
this.gamePlayer = gamePlayer;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before call real game player");
Object result= method.invoke(gamePlayer,args);
System.out.println("after call the real");
return result;
}
}
调用场景:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class ProxyMain{
public static void main(String[] args){
IGamePlayer player=new GamePlayer();
InvocationHandler playerHandler=new GamePlayerInvocationHandler(player);
IGamePlayer proxyPlayer=(IGamePlayer) Proxy.newProxyInstance(player.getClass().getClassLoader(),
new Class[]{IGamePlayer.class},playerHandler);
proxyPlayer.hello();
}
}
动态代理在实现阶段不关心代理谁,在运行阶段才指定代理哪一个对象,相对来说之前写的代理属于静态代理。
spring中非常重要的AOP就是采用动态代理机制。
利用java的proxy实现动态代理,要求被代理类必须实现一个接口,cglib可以实现不需要接口也能实现动态代理。
使用代理模式的场景:
- 远程代理。也就是为一个对象在不同的地址空间提供局部代表,这样可以隐藏一个对象存在于不同地址空间的事实。比如说 WebService,当我们在应用程序的项目中加入一个 Web 引用,引用一个 WebService,此时会在项目中声称一个 WebReference 的文件夹和一些文件,这个就是起代理作用的,这样可以让那个客户端程序调用代理解决远程访问的问题
- 虚拟代理.是根据需要创建开销很大的对象,通过它来存放实例化需要很长时间的真实对象。这样就可以达到性能的最优化,比如打开一个网页,这个网页里面包含了大量的文字和图片,但我们可以很快看到文字,但是图片却是一张一张地下载后才能看到,那些未打开的图片框,就是通过虚拟代里来替换了真实的图片,此时代理存储了真实图片的路径和尺寸
- 保护代理.用来控制真实对象访问时的权限。一般用于对象应该有不同的访问权限的时候;
- cache代理。 利用代理缓存热点数据,达到cache的效果。
一种重要场景,延迟加载
使用代理模式来实现延迟加载作为优化启动速度的一种手段。
假设某客户端软件有根据用户请求去数据库查询数据的功能。在查询数据前,需要获得数据库连接,软件开启时初始化系统的所有类,此时尝试获得数据库连接。当系统有大量的类似操作存在时 (比如 XML 解析等),所有这些初始化操作的叠加会使得系统的启动速度变得非常缓慢。为此,使用代理模式的代理类封装对数据库查询中的初始化操作,当系统启动时,初始化这个代理类,而非真实的数据库查询类,而代理类什么都没有做。因此,它的构造是相当迅速的。
在系统启动时,将消耗资源最多的方法都使用代理模式分离,可以加快系统的启动速度,减少用户的等待时间。而在用户真正做查询操作时再由代理类单独去加载真实的数据库查询类,完成用户的请求。这个过程就是使用代理模式实现了延迟加载。
延迟加载的核心思想是:如果当前并没有使用这个组件,则不需要真正地初始化它,使用一个代理对象替代它的原有的位置,只要在真正需要的时候才对它进行加载。使用代理模式的延迟加载是非常有意义的,首先,它可以在时间轴上分散系统压力,尤其在系统启动时,不必完成所有的初始化工作,从而加速启动时间;其次,对很多真实主题而言,在软件启动直到被关闭的整个过程中,可能根本不会被调用,初始化这些数据无疑是一种资源浪费。例如使用代理类封装数据库查询类后,系统的启动过程这个例子。若系统不使用代理模式,则在启动时就要初始化 DBQuery 对象,而使用代理模式后,启动时只需要初始化一个轻量级的对象 DBQueryProxy