# 代理模式 ——Proxy
代理模式怎么说呢,就是代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。然后就是非常经典的案例就说中介出租房屋。我们如果去一个一个找房东很麻烦,所以我们大多时候都是去找的中介公司,中介公司去找房东来交易,然后我们就只需要交钱,同时中介公司也可以给我们带来一些服务,从而省去了我们中间不必要的麻烦。
# 静态代理
静态代理角色分析
- 抽象角色:一般使用接口或者抽象类来实现
- 真实角色:被代理的角色
- 代理角色:代理真实角色;代理真实角色后,一般会做一些附属的操作 .
- 客户:使用代理角色来进行一些操作 .
代码实现
Rent.java 即抽象角色
// 抽象角色:租房 | |
public interface Rent { | |
public void rent(); | |
} |
Host.java 即真实角色
// 真实角色:房东,房东要出租房子 | |
public class Host implements Rent{ | |
public void rent() { | |
System.out.println("房屋出租"); | |
} | |
} |
Proxy.java 即代理角色
// 代理角色:中介 | |
public class Proxy implements Rent { | |
private Host host; | |
public Proxy() { } | |
public Proxy(Host host) { | |
this.host = host; | |
} | |
// 租房 | |
public void rent(){ | |
seeHouse(); | |
host.rent(); | |
fare(); | |
} | |
// 看房 | |
public void seeHouse(){ | |
System.out.println("带房客看房"); | |
} | |
// 收中介费 | |
public void fare(){ | |
System.out.println("收中介费"); | |
} | |
} |
Client.java 即客户(实际上就是调用者)
// 客户类,一般客户都会去找代理! | |
public class Client { | |
public static void main(String[] args) { | |
// 房东要租房 | |
Host host = new Host(); | |
// 中介帮助房东 | |
Proxy proxy = new Proxy(host); | |
// 你去找中介! | |
proxy.rent(); | |
} | |
} |
然后如何评价?
优点:1. 的确让真实角色的工作更加纯粹,不需要去管公共的事情。
2. 公共事务的业务变得更加集中,并且维护只需针对代理对象。
缺点: 代码还就那个多的 1p。
所以说,如果我们又想要实现代理,然后还要不想写很多的代码,我们就可以使用动态代理。
# 动态代理
动态代理思想其实和静态代理一样,都是利用代理类实现代理功能
但是为什么叫做动态代理呢,就是利用动态的生成代理类,不需要我们手动的去写。
动态代理分为两类:一类是基于接口动态代理,一类是基于类的动态代理
基于接口动态代理 ——JDK
基于类的动态代理 --cglib
然后现在听说还有通过 java 字节码实现的 javassist。cglib 和 javassist 考虑在之后补全,这里讲述 JDK 的动态代理。
# JDK 动态代理
jdk 动态代理有两个关键的类
java.lang.reflect.InvocationHandler |
每一个动态代理类的调用处理程序都必须实现 InvocationHandler 接口。
并且每个代理类的实例都关联到了实现该接口的动态代理类调用处理程序中,当我们通过动态代理对象调用一个方法时候,这个方法的调用就会被转发到实现 InvocationHandler 接口类的 invoke 方法。
所以说看看这个 invoke 方法:
public Object invoke(Object proxy, Method method, Object[] args) | |
throws Throwable; | |
/* | |
参数 | |
proxy - 调用该方法的代理实例 | |
method - 所述方法对应于调用代理实例上的接口方法的实例。方法对象的声明类将是该方法声明的接口,它可以是代理类继承该方法的代理接口的超级接口。 | |
args - 包含的方法调用传递代理实例的参数值的对象的阵列,或 null 如果接口方法没有参数。原始类型的参数包含在适当的原始包装器类的实例中,例如 java.lang.Integer 或 java.lang.Boolean。(就别填什么 int 和 boolean,好像与反射有关) | |
*/ |
java.lang.reflect.Proxy |
public static Object newProxyInstance(ClassLoader loader, | |
Class<?>[] interfaces, | |
InvocationHandler h) | |
throws IllegalArgumentException | |
/* | |
loader 类加载器用来定义代理类 | |
interfaces 代理类实现的接口列表 | |
h 调度方法调用的调用处理函数。动态代理方法在执行时,会调用 h 里面的 invoke 方法去执行,所以都是填实现了 InvocationHandler 的类 (还有这个 h,设计这个的真是懒得 1) | |
*/ |
代码实现
Rent.java 即抽象角色
// 抽象角色:租房 | |
public interface Rent { | |
public void rent(); | |
} |
Host.java 即真实角色
// 真实角色:房东,房东要出租房子 | |
public class Host implements Rent{ | |
public void rent() { | |
System.out.println("房屋出租"); | |
} | |
} |
ProxyInvocationHandler.java 即代理角色
public class ProxyInvocationHandler implements InvocationHandler { | |
private Rent rent; | |
public void setRent(Rent rent) { | |
this.rent = rent; | |
} | |
// 生成代理类,重点是第二个参数,获取要代理的抽象角色!之前都是一个角色,现在可以代理一类角色 | |
public Object getProxy(){ | |
return Proxy.newProxyInstance(this.getClass().getClassLoader(), | |
rent.getClass().getInterfaces(),this); | |
} | |
//proxy : 代理类 | |
//method : 代理类的调用处理程序的方法对象. | |
// 处理代理实例上的方法调用并返回结果 | |
@Override | |
public Object invoke(Object proxy, Method method, Object[] args) throwsThrowable { | |
seeHouse(); | |
Object result = method.invoke(rent, args); | |
fare(); | |
return result; | |
} | |
// 以下是中介提供的一些服务,可以拿这些来搞一些日志之类的 | |
public void seeHouse(){ | |
System.out.println("带房客看房"); | |
} | |
public void fare(){ | |
System.out.println("收中介费"); | |
} | |
} |
Client.java
public class Client { | |
public static void main(String[] args) { | |
// 真实角色 | |
Host host = new Host(); | |
// 代理实例的调用处理程序 | |
ProxyInvocationHandler pih = new ProxyInvocationHandler(); | |
pih.setRent(host); // 将真实角色放置进去! | |
Rent proxy = (Rent)pih.getProxy(); // 动态生成对应的代理类! | |
proxy.rent(); | |
} | |
} |
为了实现一个 InvocationHandler 实现真正的代理多个,我们可以将代码中的 Rent 换为 Object 实现泛型的通用
ProxyInvocationHandler.java
public class ProxyInvocationHandler implements InvocationHandler { | |
// 被代理的接口 | |
private Object target; | |
public void setRent(Object target){ | |
this.target = target; | |
} | |
// 生成得到代理类 | |
public Object getProxy(){ | |
return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this); | |
} | |
// 处理代理实例并返回结果 | |
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { | |
// 动态代理的本质就是使用反射。 | |
log(method.getName()); | |
Object result=method.invoke(target,args); | |
return result; | |
} | |
public void log( String msg){ | |
System.out.println("执行了"+msg+"方法"); | |
} | |
} |
测试程序
public class Test { | |
public static void main(String[] args) { | |
// 真实对象 | |
UserServiceImpl userService = new UserServiceImpl(); | |
// 代理对象的调用处理程序 | |
ProxyInvocationHandler pih = new ProxyInvocationHandler(); | |
pih.setTarget(userService); // 设置要代理的对象 | |
UserService proxy = (UserService)pih.getProxy(); // 动态生成代理类! | |
proxy.delete(); | |
} | |
} |
总结
我们可以直接从包名里看出 JDK 的动态代理是极大依赖于反射的,要深入了解的话就必须要理解类加载机制和反射机制,这里埋个坑为后来再写的反射博文吧。
然后再回到动态代理本身,一个动态代理可以代理多个类,代理的是接口,弥补了静态代理代码过多的缺点,同时也拥有静态代理拥有的全部内容,简直就是 God forever。