代理模式可以在目标对象的基础上,增加额外的功能,代理对象相当于对真实对象进行封装。
代理模式由三部分组成:
ISubject:抽象主题角色,是一个接口。该接口是对象和它的代理共用的接口。RealSubject:真实主题角色,是实现抽象主题接口的类。
Proxy:代理角色,内部含有对真实对象RealSubject的引用,从而可以操作真实对象。代理对象提供与真实对象相同的接口,以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。
下面我们以房屋中介为例说明静态代理模式:
共用的接口:
package proxy;
/**
* Created by liuliu on 2017/6/9.
*/
public Interface HousingSales{
void showings();//看房
void reserve();//预定
void SignContract();//前合同
}
房东类:
package proxy;
/**
* Created by liuliu on 2017/6/9.
*/
public class Landlord implements HousingSales {
public void showings() {
System.out.println("正在看房");
}
public void reserve() {
System.out.println("正在预定");
}
public void SignContract() {
System.out.println("正在签合同")
}
}
中介类:
package proxy;
/**
* Created by liuliu on 2017/6/9.
*/
public class Proxy implements HousingSables {
private Landlord landlord;
public Proxy(Landlord landlord) {
this.landlord = landlord;
}
public void showings() {
landlord.showings();
}
public void reserve() {
landlord.reserve();
}
public void SignContract() {
landlord.SignContract();
}
//中介新扩展的功能
public void serve() {
//中介服务于租房者
}
public void CollectExtraFees() {
//中介收取多余的费用
}
}
中介包含了房东的所有功能,当调用中介的方法时,会转去调用房东的相应方法;此外,中介还扩展了新的功能,中介对房东进行了封装。上面是利用组合实现代理类,使用继承亦可。
package proxy;
/**
* Created by liuliu on 2017/6/9.
*/
public class Proxy extends Landlord {
public void showings() {
super.showings();
}
public void reserve() {
super.reserve();
}
public void SignContract() {
super.SignContract();
}
//中介新扩展的功能
public void serve() {
//中介服务于租房者
}
public void CollectExtraFees() {
//中介收取多余的费用
}
}
静态代理总结:
1.可以做到在不修改目标对象的功能前提下,对目标功能扩展.
2.缺点:
因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护.
如何解决静态代理中的缺点呢?答案是可以使用动态代理方式。
动态代理有以下特点:
1.代理对象,不需要实现接口
2.代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象
生成代理对象的API是Proxy类中的静态方法:
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )
三个参数依次为:
ClassLoader loader : 被代理类的类加载器
Class<?>[] interfaces : 被代理类实现的接口
InvocationHandler h : 事件处理器,调用代理类的方法时,会触发invoke方法,转去执行被代理类的方法。
invoke方法中第一个参数是被代理的对象,第二个参数是被代理对象的方法,第三个参数是被代理对象的方法的参数。
静态代理的接口、房东类、中介类都没变,我们增加InvocationHandler ,测试动态代理:
public class Handler implements InvocationHandler {
private Object target;
public TimeHandler(Object target) {
super();
this.target = target;
}
/**
* 参数:
* proxy 被代理的对象
* method 被代理对象的方法
* args 方法的参数
* 返回:
* Object 方法返回值
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
method.invoke(target, args);
return null;
}
}
测试:
public class Test {
public static void main(String[] args) throws Exception {
Landlord landlord = new Landlord();
InvocationHandler h = new Handler(landlord);
Class<?> cls = landlord.getClass();
/**
*loader 类加载器
*interfaces 实现接口
*h InvocationHandler
*/
HousingSales housingSales = (HousingSales) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), h);
housingSales.showings();
housingSales.reserve();
housingSales.SignContract();
}
}
JDK动态代理步骤
1.创建一个实现InvocationHandler接口的类,它必须实现invoke()方法
2.创建被代理的类及接口
3.调用Proxy的静态方法,创建一个代理类
4.通过代理调用方法