设计模式——代理模式
代理模式(Proxy Pattern)分为静态代理,动态代理。在Spring AOP,Mybatis中都得到了广泛的应用。
如图所示,房产中介就是一个典型的代理,先写一个简单的demo。
1 静态代理
-
抽象角色:用接口或者抽象类,抽象出代理角色和真实角色的相同功能。
public interface Rent { public void rent(); }
-
真实角色:被代理的角色
public class Host implements Rent{ public void rent(){ System.out.println("我要出租房子!"); } }
-
代理角色:代理真实角色,还会做一些额外的操作。
public class Proxy implements Rent{ private Host host; public Proxy() {} public Proxy(Host host) { this.host = host; } @Override public void rent() { showHouse(); signContract(); fare(); host.rent(); } public void showHouse(){ System.out.println("带客户看房子!"); } public void fare(){ System.out.println("收取中介费用!"); } public void signContract(){ System.out.println("签租赁合同!"); } }
-
访问的人:客户,访问代理角色。
public class Client { public static void main(String[] args) { Host host=new Host(); Proxy proxy=new Proxy(host); proxy.rent(); } }
优点:
- 真实角色功能纯粹,不用关心公共规则
- 公共规则交给代理去做,实现了业务的分工
- 公共规则发生变化,只需在代理中集中管理。
2 动态代理
- 动态代理角色跟静态代理一样。
-
抽象角色:用接口或者抽象类,抽象出代理角色和真实角色的相同功能。
public interface Rent { public void rent(); }
-
真实的角色:被代理的角色
public class Host implements Rent{ public void rent(){ System.out.println("我要出租房子!"); } }
-
代理角色,在动态代理中,是指jvm运行时动态生成的一个对象,类型为com.sun.proxy.$Proxy0仅数字作为标志号会变化。
$Proxy0实现了真实角色实现的所有接口,可以强转为其中任意一个接口的类型(此处为Rent)。
基于reflect包下面的Proxy类和InvocationHandler接口实现。
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class ProxyInvocationHandler implements InvocationHandler { //真实的角色 public Rent target; public void setTarget(Rent target) { this.target = target; } //通过反射获取真实角色实现的接口,相当于代理的角色 public Object getProxy(){ Object result= Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(),this); return result; } //通过反射调用真实角色的接口实现方法 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { log(method.getName());//此处可增加定制化操作 Object result=method.invoke(target,args);//调用目标接口指定方法 fare(); //此处可增加定制化操作 return result; } //增加打印日志特殊操作 public void log(String msg){ System.out.println("执行了"+msg+"方法"); } //增加执行完成后特殊操作 public void fare(){ System.out.println("执行成功!"); } } }
-
访问的人:客户,访问代理角色。
public class Client { public static void main(String[] args) { //真实的角色 Host host=new Host(); //获取代理的真实角色的方法处理类 ProxyInvocationHandler proxyInvocationHandler=new ProxyInvocationHandler(); //设置要代理的真实角色 proxyInvocationHandler.setTarget(host); //获取真实角色实现的接口类,相当于代理的角色 Rent rent= (Rent)proxyInvocationHandler.getProxy(); //调用com.sun.proxy.$Proxy0的接口实现方法 rent.rent(); }
通过 Proxy.newProxyInstance 创建的代理对象是在jvm运行时动态生成的一个对象,它不是InvocationHandler类型,也不是我们定义的那组接口的类型,而是在运行时动态生成的一个对象com.sun.proxy.$Proxy0,并且命名方式都是这样的形式,以$开头,proxy为中,最后一个数字表示对象的标号。
- 动态代理分为两类:基于接口的动态代理,基于类的动态代理。
- 基于接口的动态代理:JDK
- 基于类的动态代理:cglib
- JAVA字节码实现:javasist
- 优点:
在静态代理基础上,还拥有如下优点:
- 一个动态代理类代理的是一个接口,一般对应的是一类业务。
- 一个动态代理类可以代理多个类,只要这些类实现同一个接口即可。