代理模式与Spring动态代理

一 代理 Proxy

代理在我们的生活中无处不在。例如小周忙于工作,把找对象委托给“婚恋中介”,亦或是将游戏委托给“游戏代练”,让“钟点工”帮忙打扫房子。

以上出现的“婚恋中介”,“游戏代练”,“钟点工”都是某种意义上的代理。代理的好处是帮助你做一些你不想做或者不能做的事情。例如,你没有时间自己去认识美丽女孩儿👧🏻,你自己不想清理脏脏的马桶🚽,有了代理,你可以委托他们来帮你做这样的事情。

在实际项目中,代理的作用是什么呢? 同样,在客户端不想或不能直接引用一个对象时,代理可以帮助其间接地去引用一个对象。代理对象是”真实对象(realObject)“ 和 ”目标对象(target Object) “的一个中介者,将他们俩联系起来。

例如你在隧道的地铁中,由于信号的紊乱,你此时只能预览高清图的缩略图。[1]
在这里插入图片描述
小图片便是大图片的代理。

亦或是本地需要访问远程服务器内存中的某个对象,本地是无法直接访问的,这时可以建立一个远程代理,帮助本地访问远程内存中的对象。
在这里插入图片描述
总结:代理是某个对象的替代对象,通过引用代理或控制代理,实现对真实对象的引用。
例如《红楼梦》中,赵姨娘扎小人让贾宝玉和王熙凤患病。赵姨娘便是通过宝玉和熙凤的代理(傀儡小人)来控制他们姐弟。

二 代理模式

2.1 模式结构

因为Proxy需要能够反映真实对象的性质,所以代理类和真实类需要有相同的接口。例如赵姨娘扎小人需要贴上宝玉和熙风的生辰八字,生辰八字便是傀儡小人与他姐弟二人的共同接口。
在这里插入图片描述

  • Subject: 代理和真实对象共同接口 (生辰八字)
  • Client: 客户端 (赵姨娘)
  • Proxy: 代理对象 (傀儡小人)
  • RealSubject: 真实对象 (贾宝玉、王熙凤)
    由于赵姨娘是贾政的妾室,没法直接对宝玉和熙风下手,因此她聪明地采用了代理模式作案。

2.2 简化代码

public class Proxy implements Subject { // 实现共同接口
	private RealSubject realSubject  = new RealSubject();
	
	public void preRequest(){...}
	
	// 只要调用代理对象的request()方法,不仅实现了调用真实对象的request(), 还实现了增强功能
	public void request(){ 
		preRequest();
		realSubjectRequest();
		postRequest();
	}
	
	public void preRequest(){...}
}

2.3 代理模式的优缺点

优点
(1)代理模式通过协调客户端和真实对象,降低了系统的耦合度。
(2)远程代理:使客户端可以访问远程机器上的对象,远程机器可能性能更好,提高了我们的办公效率。
(3)虚拟代理:利用小的对象来代理大的对象,减小了对系统资源的消耗,可以提高运行速度。
(4)保护代理:控制客户端对真实对象的使用权限,避免重要信息泄露。

缺点
(1) 由于代理的存在,会增加一步代理的步骤,若代理产生的优化还不如代理产生的延迟,反而会聪明反被聪明误 make yourself the victim of your own success
(2) 实现代理很麻烦的!!!

2.4 常见代理

(1) 远程代理
远程代理有时被叫做 Ambessador, 即大使。中国驻某国领事馆的大使便是我们外交部的远程代理,协助国内外交部解决外交事务。远程代理可以让位于不同物理内存中的对象取得联系。
(2)虚拟代理
虚拟即virtual,利用小的资源代替大的资源。
(3)保护代理
控制客户对真实对象的访问,提供了不同的权限。例如会员制度便是通过代理模式实现,在代理类中,增加不同的访问等级,给用户提供不同的使用权限。
(4)防火墙代理
因为代理并不是真实对象本身,这样可以避免真实对象被攻击。例如张庭的传销公司便采用了防火墙代理,持股人不是她自己,她通过代理操控公司,面对法律追责时则可以规避风险。
(5)缓冲代理
为一个请求的结果提供临时储存的空间,下次其他请求便可以共享这个请求的结果。

三 动态代理

动态代理的典型应用是Spring AOP。

传统的静态代理,客户端通过代理对象ProxySubject来调用真实对象RealSubject中的request()方法。这种模式必须是真实对象实现已经存在的。但如果每个真实对象都要有一个自己的代理类来实现代理,那么代理类将会急剧增加,我们实现代理和后期维护代理会非常困难。那么,有没有一个通用的代理类呢?

Java为我们提供了java.refelct.lang包,使得我们可以在事先不知道真实对象的情况下,对真实对象进行代理,这就是动态代理。

接下来,我们一起看看java的 动态代理是怎么实现的吧!

InvocationHandler接口

public interface InvocationHandler {
	/**
	 * @ proxy : 代理类
	 * @ method : 需要代理的方法
	 * @ args: method方法的参数数组
	 */
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

动态代理类需要实现InvocationHandler接口,实现invoke()方法,以此来调用真实对象的方法,并对真实对象的方法进行增强(即增加功能)。

Proxy类

 public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException

客户端通过Proxy类的静态方法,newProxyInstance(), 运行时动态地识别需要代理哪个类。

动态代理使得灵活性更好了,是多态(polymorphism)的一种体现。

3.1 动态代理两种方式

(1)JDK Proxy

JDK Proxy是java的默认代理方式,最大特点是需要被代理类具有要被实现的接口。见 target.getClass().getInterfaces()

public class JDKProxy implements InvocationHandler {
	private Object target;
	
	public JDKProxy(Object target) {
		this.target = target;
	}
	
	public <T> T getProxy(){
	// 传入真实类的类加载器,真实类的接口,JDKProxy
		return (T)Proxy.newProxyInstance(target.getClass().getClassLoader(),
		target.getClass().getInterfaces(), this)
	}
}

(2) CGLib (Code Generation Library)
如果某个类没有接口,那么就无法使用JDK Proxy了。 CGLIB,顾名思义,是一个可以生产code的库,可以在运行时扩展Java类和实现Java接口。

CGLib会动态查找类中没有被final修饰的public方法,将其转换为字节码,然后将字节码转化为相应的代理对象class文件。

[1] 参考文献:《Java设计模式》第二版,刘伟。清华大学出版社,248-258。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值