目录
一 代理模式
1.1 简介
1.2 代理模式角色定义
二 静态代理
2.1 介绍和实例
2.2 静态代理的缺点
三 动态代理
3.1 基于JDK原生动态代理实现
四 小结
一 代理模式
1.1 简介
Java的动态代理在实践中有着广泛的使用场景,比如最场景的Spring AOP、Java注解的获取、日志、用户鉴权等。
先看百度百科的定义:
代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
代理模式解释起来就是,设定指定的“代理人(Proxy代理类)”执行RealSubject类中的具体方法。我们对RealSubject类中的左右操作都通过“代理人(Proxy代理类)调用RealSubject类进行执行。
通过代理模式,我们可以做到两点:
1、隐藏代理类的具体实现(RealSubject类中才是具体实现)。
2、实现客户与代理类的解耦,可以在不改变代理类代码的情况下添加一些额外的功能(日志、权限)等。
1.2 代理模式角色定义
在上述的过程中在编程的过程中我们可以定义为三类对象:
- Subject(抽象主题角色):一般为接口,定义代理类和真实主题的公共对外方法,也是代理类代理真实主题的方法。比如:出售商品等。
- RealSubject(真实主题角色):真正实现业务逻辑的类。比如实现了广告、出售等方法的厂家(Factoryer)。
- Proxy(代理主题角色):用来代理和封装真实主题。比如,同样实现了广告、出售等方法的超时(Shop)。
以上三个角色对应的类图如下:
二 静态代理
2.1 介绍和实例
静态代理是指代理类在程序运行前就已经存在,这种情况下的代理类通常都是我们在Java代码中定义的。
静态代理例子:工厂主(Factoryer)需要执行的工厂(IFactory)的主要function就是卖工厂的货(sell方法),我们现在需要委托商店(shop)这个代理来代卖货物。顾客也就是client是通过商店shop这个代理买东西的。
IFactory接口定义如下:
Vendor类定义如下
Shop类定义如下:
其中代理类Shop通过聚合的方式持有了被代理类Factoryer的引用,并在对应的方法中调用Factoryer对应的方法。
下面看看在客户端中如何使用代理类,即客人怎么通过这套代理体系买东西。
在上述代码中,我们可以在Shop中修改或新增一些内容,而不影响被代理类Factoryer 。比如我们可以在Shop类中新增一些额外的处理,类似于筛选购买用户、记录日志等操作。
2.2 静态代理的缺点
静态代理实现简单且不侵入原代码,但当场景复杂时,静态代理会有以下缺点:
1、当需要代理多个类时,代理对象要实现与目标对象一致的接口。我们只有两个选择,1:只维护一个代理类来实现多个接口,但这样会导致代理类过于庞大;2:新建多个代理类,但这样会产生过多的代理类。
2、当接口(IFactoryer)需要增加、删除、修改方法时,目标对象(ClientBuy)与代理类(shop)都要同时修改,不易维护。
于是,动态代理便派上用场了。
三 动态代理
动态代理是指代理类在程序运行时进行创建的代理方式。这种情况下,代理类并不是在Java代码中定义的,而是在运行时根据Java代码中的“指示”动态生成的。
相比于静态代理,动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类的函数。
3.1 基于JDK原生动态代理实现
实现动态代理通常有两种方式:JDK原生动态代理和CGLIB动态代理。这里,我们以JDK原生动态代理为例来进行讲解。
JDK动态代理主要涉及两个类:java.lang.reflect.Proxy和java.lang.reflect.InvocationHandler。
InvocationHandler接口定义了如下方法:
顾名思义,实现了该接口的中介类用做“调用处理器”。当调用代理类对象的方法时,这个“调用”会转送到invoke方法中。动态代理中,Proxy 动态产生的代理会调用 InvocationHandler 实现类,所以 InvocationHandler 是实际执行者。
InvocationHandler方法的,第一个参数proxy参数为代理类对象,第二个参数为method,表示具体调用的是代理类的哪个方法,第三个参数args为为第二个参数该方法的参数。
这样对代理类中的所有方法的调用都会变为对invoke的调用,可以在invoke方法中添加统一的处理逻辑(也可以根据method参数对不同的代理类方法做不同的处理)。
下面以添加日志为例来演示一下动态代理。
客户端编写程序使用动态代理代码如下:
Proxy.newProxyInstance的三个参数含义为:
- loader 自然是类加载器
- interfaces 代码要用来代理的接口
- h 一个 InvocationHandler 对象
执行之后,打印日志如下:
经过上述验证,我们发现已经成功为我们的被代理类统一添加了执行方法之前和执行方法之后的日志。
四 小结
了解代理模式可以让我们的系统设计的更加具有可扩展性。而动态代理的应用就更广了,各类框架及业务场景都在使用。
Java动态代理的优势是实现无侵入式的代码扩展,也就是方法的增强:不改变这个方法的前提下去丰富它;你可以在不用修改源码的情况下,增强一些方法;在方法的前后你可以做你任何想做的事情(甚至不去执行这个方法就可以)。老功能用原来的方法不会有问题,新功能通过代理丰富了原来的方法也能被广泛试用。
动态代理类:在程序运行时,通过反射机制动态生成。
动态代理类通常代理接口下的所有类。
动态代理事先不知道要代理的是什么,只有在运行的时候才能确定。
动态代理的调用处理程序必须事先InvocationHandler接口,及使用Proxy类中的newProxyInstance方法动态的创建代理类。
Java动态代理只能代理接口,要代理类需要使用第三方的CGLIB等类库
掌握到这里其实已经差不多了,关于更多一些例子包括源码的一些分析,可以看我的参考文章。
参考:
Java代理模式及动态代理详解 - 程序新视界
从代理模式再出发!Proxy.newProxyInstance的秘密_葵续浅笑的博客
Java动态代理的作用及好处_明洋的专栏-
详解java动态代理机制以及使用场景(一)_远方和诗 的博客-
轻松学,Java 中的代理模式及动态代理_frank 的专栏