1.产生背景
- 为什么需要代理模式
假如你是一个业主,有一套500万的房子需要出售;你希望找一个合适的客户,签个合同,收了钱,然后把房子过户给客户就可以了;
但是,有太多的客户天天找你需要看房,询问,而且大部分都是没有诚意的客户,让你不胜其烦;
业主: 只需要签字收钱就好了,剩下的事情,如果有人能帮我处理就发了;
2.概念
为其他对象提供一种代理以控制对这个对象的访问;
本质: 引入一个中介类, 这个类实现了被代理类相同的接口或继承自被代理类,对外假装成被代理对象,帮被代理对象处理一些非必要或不能做的事情;但核心功能,仍然通过调用被代理对象来实现;
3.目的
不修改原来代码的情况下,动态地给一个对象添加一些额外的职责和功能,或者添加一些控制功能,以限制对原对象的访问
本质:动态增加功能;把需要新增加的功能以代理的方式动态的添加到原对象上;
4.解决方案
本质:引入一个第三方中介类,这个类实现被代理类的接口或直接继承被代理,以方便代理类可以假装成原类;并通过依赖方式引用原类;这个代理类处于调用方和被代理之间,以便对被代理类进行访问控制和增加新功能;
- 引入中介对象
- 中介对象实现被装饰类的接口或直接继承(对外假装成被代理类)
- 中介对象内部引用被代理对象(并把真实功能委托给被代理对象);
- 中介对象在调用被代理对象前后:增加特殊功能或访问控制
5. 类图
图片转自:runoob
6.优缺点
优点:
松耦合
在不修改原来代码的情况下,动态的为原类增加新功能扩展性高
只需要增加新的代理功能类,就可以对原类不断增加新功能灵活
通过动态代理,不修改调用方和被调用方的情况下动态增加新功能
缺点:
- 代理实现起来可能会比较复杂
- 增加了中介类,可能会对性能有影响
最核心的目的:在不修改原代码的情况下,动态的为对象增加或减少某些功能,以及控制对被代理类的访问
7.应用场景
- A、远程代理。
- B、虚拟代理。
- C、Copy-on-Write 代理。
- D、保护(Protect or Access)代理。
- E、Cache代理。
- F、防火墙(Firewall)代理。
- G、同步化(Synchronization)代理。
- H、智能引用(Smart Reference)代理。
“与装饰器的区别”
在学习代理模式时,会发现它与装饰器模式无论从实现结构,还是功能目的都非常接近;
装饰器模式侧重的是对功能的增强,不改变原功能;
装饰器模式使用方明确知道自己需要什么的增强功能,硬编码使用;
代理模式侧重于对原功能的改变(特别是访问权限的控制)
代理模式分为静态和动态代理,动态代理是在调用方和被调用都不知情的情况下使用,代理功能增强由第三方进行维护(如:spring 框加维护代理);
如果代理模式使用静态代理实现,而且也是侧重对功能的增强,那么他们之间没有任何区别(如:spring中通过动态代理实现缓存或日志)
因此:代理在java中,代理更多的应用在动态代理上(如:spring aop);
8.代理种类
远程代理
如:微服务,通过对远程接口创建一个代理对象,代理内部使用RPC实现功能委托给远程服务;虚拟代理
如:hibernet 中,lazy加载的对象,可以先创建一个代理对象,等到实际使用时,再加载实际对象;保护代理
如:通过代理实现认证控制,如果未登录,则不允许对服务的访问;缓存代理
如:spring-cache,对原对象进行代理,在访问原对象时,先从缓存中取数据,如果缓存中存在则直接返回,否则,再调用原对象的功能,并把返回值放入缓存;等等
9.现实案例
spring @Aspect aop对应的实现
10. 注意事项
java中代理分为:静态代理和动态代理;
-静态代理
略
-动态代理
jdk动态代理
//被代理接口
public interface Owner {
void sellHouse(String customerName);
}
//被代理类(真正的业务对象)
public class OwnerImpl implements Owner {
@Override
public void sellHouse(String customerName) {
System.out.println("我是业主,我把房子卖给了:" + customerName);
}
}
//代理后的:增强功能
public class AgentInvocationHandler implements InvocationHandler {
//被代理的原对象:引用了实际的业务对象,以方便把真正的业务功能委托给它
Object srcObject;
//绑定原对象
public AgentInvocationHandler(Object srcObject) {
this.srcObject = srcObject;
}
/**
*调用代理对象方法时,全部委托给此方法
*@Param proxy :代理对象,即假装成原对象的对象
**/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//访问控制:在调用
if(args == null || args[0] == null || args[0].toString().startsWith("杨")){
System.out.println("不卖给没有名字的人,也不卖给姓杨的人");
return null;
}
//真正的业务功能,委托给原对象
return method.invoke(this.srcObject, args);
}
}
//执行
public class ProxyMain {
public static void main(String args[]){
//被代理对象
Owner owner = new OwnerImpl();
//代理功能(增强的功能)
AgentInvocationHandler agentInvocationHandler = new AgentInvocationHandler(owner);
//根据被代理对象的接口、增强功能、原对象一起:生成一个新的对象:代理对象
Owner agent = (Owner) Proxy.newProxyInstance(owner.getClass().getClassLoader(), new Class[]{Owner.class},agentInvocationHandler);
agent.sellHouse("杨先生");
}
}
- 创建流程:
- 编写业务接口
- 编写业务类
- 编写增强功能类:InvocationHandler
- 创建业务对象
- 创建增强功能对象,并引用业务对象
- 创建代理对象(实现了业务接口),并引用增强功能对象
- 执行流程:
- 通过创建代理对象,获得代理对象,并假装成业务对象(实现了业务接口)
- 调用代理对象
- 代理对象的任何方法调用,都委托给增强功能对象(invoke)
- 根据invoke方法中的method,判断代理对象调用了哪个方法,进行不同的控制;
- InvocationHandler.invoke中,根据各种判断增强或控制原对象访问
- 调用原对象实际方法,把核心功能委托给原对象
- InvocationHandler.invoke中,在调用原对象方法后,进行二次处理;
- 返回结果