代理模式:为其它对象提供一种代理以控制对这个对象的访问控制;在某些情况下,客户不想或者不能直接引用另一个对象,这时候代理对象可以在客户端和目标对象之间起到中介的作用。
一、静态代理
角色分析:
- 抽象事务 :一般会使用接口或者抽象类
- 真实角色 :被代理的角色
- 代理角色 :代理真实角色 , 代理真实角色后,我们一般会做一些附属操作
- 客户 : 访问代理对象
下面以租房为例,学习静态代理模式
- 抽象事务:出租房,对应Rent 接口
- 真实角色:房东,他需要出租房,即要实现Rent 接口,并实现出租的 rent()方法
- 代理角色:房屋中介,他需要帮房东出租房子,同样需要实现Rent 接口,实现 rent() 方法,并对此方法做出一些扩展操作
- 客户:需要租房的人
代码实现:
- 接口
public interface Rent {
void rent();
}
- 真实角色
public class Host implements Rent{
public void rent() {
System.out.println("【Host】房东要出租房子!");
}
}
- 代理角色
public class Proxy implements Rent{
private Host host;
public Proxy() {
}
public Proxy(Host host) {
this.host = host;
}
public void rent() {
lookHouse();
host.rent();
contract();
rentRent();
}
public void contract() {
System.out.println("【Proxy】签合同!");
}
public void rentRent() {
System.out.println("【Proxy】收房租!");
}
public void lookHouse() {
System.out.println("【Proxy】看房!");
}
}
- 客户 (测试类)
public class Client {
public static void main(String[] args) {
Host host = new Host();
Proxy proxy = new Proxy(host);
proxy.rent();
}
}
- 测试结果:
【Proxy】看房!
【Host】房东要出租房子!
【Proxy】签合同!
【Proxy】收房租!
静态代理模式的利弊:
优点:
- 可以使真实角色的操作更加纯粹,不用去关注一些公共的业务;
- 公共的业务交给代理角色,实现了业务的分工;
- 公共业务发生扩展的时候,方便集中管理。
缺点:
-
必须事先知道要代理的是什么;
-
一个真实角色就会产生一个代理角色,代码量会翻倍,开发效率会变低!
二、动态代理
-
动态代理的实现方式:
- 基于接口实现 【JDK动态代理】
- 基于类实现:cglib
- 基于java字节码实现:Javassist
-
动态代理和静态代理的角色一样,代理类是动态生成的,不是直接写好的
-
学习动态代理需要用到的两个类:
- Proxy:提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。
//返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。 static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
- InvocationHandler:是代理实例的调用处理程序 实现的接口。 每个代理实例都具有一个关联的调用处理程序。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的
invoke
方法。
//在代理实例上处理方法调用并返回结果。 Object invoke(Object proxy, Method method, Object[] args)
JDK动态代理代码实现:
- 仍以上面租房为例,接口和真实角色都不变!
- 动态代理类
public class InvocationHandlerProxy implements InvocationHandler {
//需要代理的接口的实例
private Object target;
public void setObject(Object object) {
this.target = object;
}
//获取代理实例
public Object getProxy(){
/* 参数:
ClassLoader loader: 代理类的类加载器
Class<?>[] interfaces: 代理类要实现的接口列表
InvocationHandler h:指派方法调用的调用处理程序,即InvocationHandler的实例,也就是当前对象
*/
return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
/* 参数:
* proxy:代理实例
* method:对应于在代理实例上调用的接口方法的 Method 实例。
* args:方法的参数列表
* */
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
lookHouse();
//执行接口的方法
//target:调用此方法的对象 args:参数列表
Object invoke = method.invoke(target,args);
contract();
rentRent();
return invoke;
}
public void contract() {
System.out.println("【Proxy】签合同!");
}
public void rentRent() {
System.out.println("【Proxy】收房租!");
}
public void lookHouse() {
System.out.println("【Proxy】看房!");
}
}
- 测试类
public class Client {
public static void main(String[] args) {
Rent rent = new Host();
InvocationHandlerProxy ihp = new InvocationHandlerProxy();
ihp.setObject(rent);
Rent proxy = (Rent) ihp.getProxy();
proxy.rent();
}
}
- 动态代理的优点:
- 包含静态代理的所有优点,且解决了其缺点;
- 一个动态代理类可以代理一个接口下的所有类;
- 动态代理事先不知道要代理的是什么,在运行的时候才确定;
- 实现无侵入式的代码扩展,也就是方法的增强;让你可以在不用修改源码的情况下,增强一些方法;在方法的前后你可以做你任何想做的事情。