Proxy是"代理人"的意思,它指的是替代别人进行工作的人。当不一定需要本人亲自进行工作时,就可以寻找代理人去完成工作。但代理人毕竟是代理人,能代替本人做的事情终究时有限的,因此,当代理人遇到无法自己解决的事情时就会去找本人解决该问题。
我们平时在租房子的时候,通常不会直接面对房东,而是会找一个房屋中介(赚差价的那种中间商),中介会带我们去看房,我们会和中介签订合同。在这个例子中,中介就扮演着代理的角色。
如果要编码实现代理模式,有两种方式,一种是静态代理,另一种是动态代理。
类图:
1.静态代理
(1)优点:在不修改目标对象的功能前提下,能通过代理对象对目标功能扩展
(2) 缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类
(3)一旦接口增加方法,目标对象与代理对象都要维护
//出租 统一的接口
public interface Rent {
public void rent();
}
//房东 实现了出租接口
public class Host implements Rent{
@Override
public void rent() {
System.out.println("房东要出租房子了!");
}
}
//房屋中介(代理类) 也实现了出租接口
public class HostProxy implements Rent{
private Rent rent;
public HostProxy(Rent rent) {
this.rent=rent;
}
@Override
public void rent() {
seeHouse();
//真正使用的是房东的出租方法
rent.rent();
fare();
}
public void seeHouse() {
System.out.println("中介带看房子!");
}
public void fare() {
System.out.println("中介收费");
}
}
//使用者 测试类
public class Client {
public static void main(String[] args) {
Host host=new Host();
HostProxy proxy=new HostProxy(host);
proxy.rent();
}
}
2.动态代理
一个被代理的类就需要一个代理类,那么当有多个类需要被代理的时候,就要生成多个代理类。这会造成代码剧增,显然这种情况我们是不希望看到的。于是就有了动态代理类。动态代理是spring中AOP实现的底层原理,其重要性不言而喻!
(1)代理对象,不需要实现接口,但是目标对象要实现接口,否则不能用动态代理
(2)代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象
(3)动态代理也叫做:JDK代理,接口代理
//出租 统一的接口
public interface Rent {
public void rent();
}
//房东 实现了出租接口
public class Host implements Rent{
@Override
public void rent() {
System.out.println("房东要出租房子了!");
}
}
//通用的代理类
public class ProxyInvocationHandler implements InvocationHandler{
//被代理的接口
private Object target;
public void setTarget(Object target) {
this.target=target;
}
//生成得到代理类
public Object getProxy() {
/**
* 参数说明
* 1.ClassLoader loader :指定当前目标对象使用的类加载器,获取加载器的方法固定
* 2. Class<?>[] interfaces :目标对象实现的接口类型,使用泛型方法确认类型
* 3.InvocationHandler h :事情处理,执行目标对象的方法时会触发事件处理器方法,
* 会把当前执行的目标对象方法当成参数传入
*/
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
//处理代理实例,并返回结果
@Override
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 Client {
public static void main(String[] args) {
//真实角色
Host host=new Host();
//代理角色:现在没有
RentsProxyInvocationHandler pih=new RentsProxyInvocationHandler();
//通过调用程序处理角色来处理我们要调用的对象接口
pih.setRent(host);
//这里的proxy就是动态生成的,我们并没有写
Rent proxy=(Rent) pih.getProxy();
proxy.rent();
}
}
3.在proxy模式中有一下登场角色
Subject(主体)
subject角色定义了使proxy角色和realSubject角色之间具有一致性的接口。由于存在subject角色,所以client角色不必在意它所使用的究竟是proxy角色还是realSubject角色。在示例程序中,由Rent接口扮演此角色。
Proxy(代理人)
proxy角色会尽量处理来自Client角色的请求。只有当自己不能处理时,它才会将工作交给RealBoject角色。在实例程序中,由HostProxy类扮演此角色。
RealSubject(实际的主体)
“本人”RealSubject角色会在“代理人”无法胜任工作时出场。它与Proxy角色一样,也实现了在Subject角色中定义的接口(API)。在实例程序中,由Host扮演此角色。
Client(请求者)
使用proxy模式的角色,client角色并不包含在proxy模式中。