代理模式
给其他对象提供一种代理以控制这个对象的访问。
代理模式的角色:
抽象角色:通过接口或者抽象类声明真实角色所要实现的业务方法。
真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用
代理角色:实现抽象角色,是真实角色的代理,调用真实角色实现的方法完成业务,并附加自己的操作。
静态代理
我们先设想这样一个场景:你想要买房,你最关心的是找到自己喜欢的房子然后付钱即可,其他流程你都通过找别人代理来完成,房产中介就是这个代理。接下来抽象出这个场景中所有角色
抽象角色(接口):
public interface BuyHouse {
void buyHouse();
}
真实角色(被代理类):
public class Customer implements BuyHouse{
@Override
public void buyHouse() {
System.out.println("我要买大房子,价格一千万以内");
}
}
代理角色(代理类)
public class HouseProxy implements BuyHouse{
private BuyHouse buyHouse;
HouseProxy(BuyHouse buyHouse){
this.buyHouse = buyHouse;
}
@Override
public void buyHouse() {
System.out.println("我是中介,我来带你看房子了");
//调用顾客的买房方法实现
buyHouse.buyHouse();
System.out.println("过户完毕,房子是你的了");
}
public static void main(String[] args){
new HouseProxy(new Customer()).buyHouse();
}
}
输出
我是中介,我来带你看房子了
我要买大房子,价格一千万以内
过户完毕,房子是你的了
代理类的逻辑比较复杂,最终的买房的所有业务都是在代理类中实现的。
它实现了BuyHouse接口的同时内部聚合了BuyHouse接口,但是具体传参时传的是BuyHouse实现类Customer的对象。
其实直接在内部聚合Customer也是可以的,但你试想者Customer同时需要多个代理呢,内部聚合BuyHouse接口可以让你初始化代理类时传参值为另一个代理类的对象来实现代理嵌套,因为他们都是BuyHouse接口的实现,这也就是多态的牛逼之处。
动态代理
动态代理可以让你的代理类代理一切类型的对象,即代理Object类。
两者的最终实现其实是一致的,不同的是动态代理的代理类是在程序运行时通过反射机制动态生成的,而静态代理的代理类是自己实现的。
下面我们来看jdk提供的动态代理实现:
//动态处理器
public class ProxyHandler implements InvocationHandler {
private Object object;
public ProxyHandler(final Object object){
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("我是中介,我来带你看房子了");
Object result = method.invoke(object,args);
System.out.println("过户完毕,房子是你的了");
return result;
}
public static void main(String[] args){
BuyHouse customer = new Customer();
BuyHouse houseProxy = (BuyHouse)Proxy.newProxyInstance(BuyHouse.class.getClassLoader(),
new Class[]{BuyHouse.class},new ProxyHandler(customer));
houseProxy.buyHouse();
}
}
生成代理类实例的方法newProxyInstance的三个参数分别是ClassLoader指定类加载器,·Class<>[]指定目标对象要实现的接口类型,InvocationHandler指定动态处理器。
输出
我是中介,我来带你看房子了
我要买大房子,价格一千万以内
过户完毕,房子是你的了
我们看到最后调用生成的动态代理类houseProxy对象的buyHouse方法时其实调用了ProxyHandler类的invoker方法。下面我们看下它是如何做到的
先在main方法中加一行代码
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
运行main方法后会在项目根目录出现反编译生成的代理类$Proxy0,这个类继承Proxy类实现BuyHouse接口,主要看它实现的buyHouse方法:
public final void buyHouse() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
super.h.invoke(this, m3, (Object[])null); 点进去后发现其实就是执行了InvocationHandler的invoke方法,参数m3在静态块中初始化如下图:
m3 = Class.forName("com.proxy.example.proxy.myProxy.BuyHouse").getMethod("buyHouse");
最终实现了调用生成的动态代理类houseProxy对象的buyHouse方法时其实调用了ProxyHandler类的invoker方法,完成了代理的工作。