此博客用于个人学习,来源于ssm框架的书籍,对知识点进行一个整理。
2.2 动态代理模式和责任链模式
动态代理的意义在于生成一个占位(又称代理对象),来代理真实对象,从而控制真实对象的访问。
先举个例子,能更好的理解代理模式。你的公司是一家软件公司,你作为一名软件工程师,平时的工作肯定是跟代码打交道。客户来你们公司,肯定不是直接找你谈,而是去找商务谈。此时,对于客户来说,商务就是代表整个公司。

客户是通过商务与软件工程师进行沟通,那么商务(代理对象)的作用是什么呢?商务可以进行谈判,比如项目启动前的商务谈判,软件的价格等等,商务也有可能谈判失败,此时就可以结束与客户的合作关系,这些都不需要软件工程师来处理。因此,代理的作用就是,在真实对象访问之前或者之后加入对应的逻辑,或者根据其他的规则控制是否使用真实对象。
我们需要在调用者调用对象之前产生一个代理对象,而这个代理对象需要和真实对象建立代理关系,所以代理分为两个步骤:
- 代理对象和真实对象建立代理关系;
- 实现代理对象的代理逻辑方法。
Java中,代理技术由许多种,本篇主要讨论 Spring 常用的 JDK 代理技术和 CGLIB 代理技术。
2.2.1 JDK 动态代理
需要借助接口才能创建代理对象,首先定义一个 HelloWorld 接口:
public interface HelloWorld {
public void sayHelloWorld();
}
然后通过 HelloWorldImpl 这个类来实现接口:
public class HelloWorldImpl implements HelloWorld{
@Override
public void sayHelloWorld() {
System.out.println("Hello World!");
}
}
进行动态代理:
public class JdkProxyExample implements InvocationHandler {
//真实对象
private Object target = null;
/**
* 建立代理对象与真实对象的代理关系
* @param target 真实对象
* @return 返回代理对象
*/
public Object bind(Object target){
this.target = target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
/**
* 代理方法逻辑
* @param proxy 代理对象
* @param method 当前调度方法
* @param args 当前方法参数
* @return 代理结果返回
* @throws Throwable 异常
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("进入代理逻辑方法");
System.out.println("在调度真实对象之前的服务");
//相当于调用sayHelloWorld方法
Object obj = method.invoke(target,args);
System.out.println("在调度真实对象之后的服务");
return obj;
}
}
第一步,建立代理对象与真实对象的关系,这里通过 bind 方法去完成,先是通过类的属性 target 保存了真实对象,然后通过下面的代码建立并生成代理对象。
Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
对 newProxyInstance 方法进行解释:
- 第一个是类加载器,我们采用了 target 本身的类加载器。
- 第二个是把生成的动态代理对象下挂在哪些接口下,这个写法就是放在 target 实现的接口下。
- 第三个是定义实现方法逻辑的代理类,this 表示当前对象,它必须实现 InvocationHandler 接口的 invoke 方法,它就是代理逻辑方法的现实方法。
第二步,实现代理逻辑方法。通过 invoke 函数实现:
- proxy,代理对象,就是 bind 方法生成的对象。
- method,当前调度的方法。
- args,调度方法的参数。
Object obj = method.invoke(target,args);
这行代码相当于调度真实对象的方法,只是通过反射实现而已。
进行测试:
JdkProxyExample jdk = new JdkProxyExample();
//由于是挂在接口HelloWorld下,所以声明代理对象HelloWorld proxy
HelloWorld proxy = (HelloWorld) jdk.bind(new HelloWorldImpl());
proxy.sayHelloWorld();
输出结果:
进入代理逻辑方法
在调度真实对象之前的服务
Hello World!
在调度真实对象之后的服务
当我们创建 HelloWorld 对象的时候,就会进入代理的逻辑方法 invoke 中,因为 JDK 生成的代理类,它继承自 Proxy 实现我们定义的 HelloWorld 接口,在实现 HelloWorld 接口方法的内部,通过反射调用了 InvocationHandlerImpl 的 invoke 方法。
2.2.2 CGLIB 动态代理
对于 JDK 动态代理技术来说,必须提供接口才能使用,在一些不能提供接口的环境中,只能采用其他的第三方技术,比如 CGLIB 动态代理。它的优势在于不需要提供接口,只要一个非抽象类就能实现动态代理。
public class CglibProxyExample implements MethodInterceptor{
/**
* 生成 CGLIB 代理对象
* @param cls class类
* @return Class类的 CGLIB 代理对象
*/
public Object getProxy(Class cls){
//CGLIB enhancer 增强类对象
Enhancer enhancer = new Enhancer();
//设置增强类型
enhancer.setSuperclass(cls);
//定义代理逻辑对象为当前对象,要求当前对象实现 MethodInterception 方法
enhancer.setCallback(this);
//生成并返回代理对象
return enhancer.create();
}
/**
* 代理逻辑方法
* @param proxy 代理对象
* @param method 方法
* @param args 方法参数
* @param methodProxy 方法代理
* @return 代理逻辑返回
* @throws Throwable 异常
*/
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)throws Throwable{
System.out.println("调用真实对象前");
//CGLIB 反射调用真实对象方法
Object result = methodProxy.invokeSuper(proxy,args);
System.out.println("调用真实对象后");
return result;
}
}
这里用了 CGLIB 的加强者 Enhancer ,通过设置超类的方法(setSuperclass),然后通过 setCallback 方法设置哪个类成为它的代理类。其中,参数为 this 就意味着是当前对象,那就要求用这个 this 这个对象实现接口 MethodInterceptor 的方法——intercept,然后返回代理对象。在反射真实对象方法前后进行了打印,是通过如下代码完成的。
Object result = methodProxy.invokeSuper(proxy,args);
进行测试:
public void testCGLIBProxy(){
CglibProxyExample cpe = new CglibProxyExample();
ReflectServiceImpl obj = (ReflectServiceImpl)cpe.getProxy(ReflectServiceImpl.class);
obj.sayHello("张三");
}
CGLIB 动态代理和 JDK 动态代理是相似的,它们都是用 getProxy 方法生成代理对象,制定代理的逻辑类,而代理逻辑类要实现一个接口的一个方法,那么这个接口定义的方法就是代理对象的逻辑方法,它可以控制真实对象的方法。
2.2.3 拦截器
设计者会为开发者设计一个拦截器供其使用,开发者只需要知道拦截器接口发方法,含义和作用即可,无须知道动态代理是怎么实现的。用 JDK 动态代理来实现一个拦截器的逻辑,先定义拦截器接口 Interceptor,代码如下:
public interface Interceptor {
public boolean before(Object proxy, Object target, Method method,Object[] args);
public void around(Object proxy, Object target, Method method,Object[] args);
public void after(Object proxy, Object target, Method method,Object[] args);
}
其中要注意的是:
- before 方法返回一个 boolean 值,它在真实对象前调用,当返回为 true 时,则反射真实对象的方法;当返回为 false 时,则调用 around 方法。
- 在 before 方法返回为 false 的情况下,调用 around 方法。
- 在反射真实对象方法或者是 around 方法执行之后,调用 after 方法。
实现这个接口——MyInterceptor,代码如下:
public class MyInterceptor implements Interceptor{
@Override
public boolean before(Object proxy, Object target, Method method, Object[] args) {
System.out.println("反射方法前逻辑");
//不反射被代理对象的原有方法
return false;
}
@Override
public void around(Object proxy, Object target, Method method, Object[] args) {
System.out.println("反射方法后逻辑");
}
@Override
public void after(Object proxy, Object target, Method method, Object[] args) {
System.out.println("取代了被代理对象的方法");
}
}
接下来,使用 JDK 动态代理,就可以去实现这些方法在适当时的调用逻辑,此时,在 JDK 动态代理中使用拦截器。
public class InterceptorJdkProxy implements InvocationHandler {
//真实对象
private Object target;
//拦截器的全限定名
private String interceptorClass = null;
public InterceptorJdkProxy(Object target,String interceptorClass){
this.target = target;
this.interceptorClass = interceptorClass;
}
/**
* 绑定委托对象并返回一个【代理占位】
* @param target 真实对象
* @param interceptorClass 拦截器全限定名
* @return 代理对象【占位】
*/
public static Object bind(Object target,String interceptorClass){
//取得代理对象
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new InterceptorJdkProxy(target,interceptorClass));
}
/**
* 通过代理对象调用方法,首先进入这个方法
* @param proxy 代理对象
* @param method 方法,被调用的方法
* @param args 方法的参数
* @return 代理结果返回
* @throws Throwable 异常
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(interceptorClass == null){
//没有设置拦截器则直接反射原有方法
return method.invoke(target,args);
}
Object result = null;
//通过反射生成拦截器
Interceptor interceptor = (Interceptor) Class.forName(interceptorClass).newInstance();
//调用前置方法
if (interceptor.before(proxy,target,method,args)){
//反射原有对象方法
result = method.invoke(target,args);
}else{
//返回false执行around方法
interceptor.around(proxy,target,method,args);
}
//调用后置方法
interceptor.after(proxy,target,method,args);
return result;
}
}
代码执行步骤:
- 在 bind 方法中使用 JDK 动态代理绑定了一个对象,然后返回代理对象。
- 如果没有设置拦截器,则直接反射真实对象的方法,然后结束,否则进行第三步。
- 通过反射生成拦截器,并准备使用它。
- 调用拦截器的 before 方法,如果返回为 true ,反射原来的方法;否则运行拦截器的 around 方法。
- 调用拦截器的 after 方法。
- 返回结果。

拦截器可以进一步简化动态代理的使用方法,使程序变得更简单,进行测试:
public static void main(String[] args) {
HelloWorld proxy = (HelloWorld) InterceptorJdkProxy.bind(new HelloWorldImpl(),"ssm.learn.chapter2.interceptor.MyInterceptor");
proxy.sayHelloWorld();
}
得到结果:
反射方法前逻辑
反射方法后逻辑
取代了被代理对象的方法
由此可见,拦截器已经生效。
2.2.4 责任链模式
设计者往往会用拦截器去替代动态代理,然后将拦截器的接口提供给开发者,从而简化开发者的开发难度,但是拦截器可能有多个。举个例子,一个程序员需要请假一周,这个时候把请假申请单看成一个对象,那么这个对象会经过项目经理,部门经理,人事部等多个角色的审批,这个时候,每个角色都有机会可以通过拦截这个申请单进行审批或者修改。如图所示:

我们称这种模式为责任链模式,它用于一个对象在多个角色中传递的场景,例如上面这个例子,申请单到了项目经理这里,可以修改申请时间等信息,然后从而影响后面的审批,即,后面的审批需要根据前面的结果进行。这个时候就可以考虑用层层代理来实现,当申请单到项目经理处,使用第一个动态代理 proxy1;当它走到部门经理处,项目经理会得到一个在项目经理的代理 proxy1 基础上生成的 proxy2 来处理部门经理的逻辑;当它走到人事处,会在 proxy2 的基础上生成 proxy3。如果还有其他角色,以此类推即可。
首先,定义三个拦截器:
public class Interceptor1 implements Interceptor {
@Override
public boolean before(Object proxy, Object target, Method method, Object[] args) {
System.out.println("【拦截器1】的 before 方法");
return true;
}
@Override
public void around(Object proxy, Object target, Method method, Object[] args) {
}
@Override
public void after(Object proxy, Object target, Method method, Object[] args) {
System.out.println("【拦截器1】的 after 方法");
}
}
public class Interceptor2 implements Interceptor {
@Override
public boolean before(Object proxy, Object target, Method method, Object[] args) {
System.out.println("【拦截器2】的 before 方法");
return true;
}
@Override
public void around(Object proxy, Object target, Method method, Object[] args) {
}
@Override
public void after(Object proxy, Object target, Method method, Object[] args) {
System.out.println("【拦截器2】的 after 方法");
}
}
public class Interceptor3 implements Interceptor {
@Override
public boolean before(Object proxy, Object target, Method method, Object[] args) {
System.out.println("【拦截器3】的 before 方法");
return true;
}
@Override
public void around(Object proxy, Object target, Method method, Object[] args) {
}
@Override
public void after(Object proxy, Object target, Method method, Object[] args) {
System.out.println("【拦截器3】的 after 方法");
}
}
对拦截器进行测试:
public static void main(String[] args) {
HelloWorld proxy1 = (HelloWorld) InterceptorJdkProxy.bind(new HelloWorldImpl(),"ssm.learn.chapter2.responsibility.Interceptor1");
HelloWorld proxy2 = (HelloWorld) InterceptorJdkProxy.bind(proxy1,"ssm.learn.chapter2.responsibility.Interceptor2");
HelloWorld proxy3 = (HelloWorld) InterceptorJdkProxy.bind(proxy2,"ssm.learn.chapter2.responsibility.Interceptor3");
proxy3.sayHelloWorld();
}
然后就可以得到以下结果,注意观察方法的执行顺序。
【拦截器3】的 before 方法
【拦截器2】的 before 方法
【拦截器1】的 before 方法
Hello World!
【拦截器1】的 after 方法
【拦截器2】的 after 方法
【拦截器3】的 after 方法
可以看出,before 方法是按照从最后一个拦截器到第一个拦截器的加载顺序运行,而 after 方法正好相反。
从代码中可见,责任链模式的优点在于我们可以在传递链上加入新的拦截器,增加拦截逻辑,其缺点是会增加代理和反射,而代理和反射的性能不高。
本文探讨了动态代理模式和责任链模式的概念及其在Java中的应用。动态代理通过生成代理对象来控制对真实对象的访问,包括JDK动态代理和CGLIB动态代理。责任链模式则用于对象在多个角色间传递的场景,通过层层代理实现复杂的业务逻辑。

被折叠的 条评论
为什么被折叠?



