笔者前几天看了一个面试题 “说一下什么是代理模式?”,于是回忆了一下这个设计模式,并结合一篇旧文 《理理 Java 开发中常见的设计模式》,温故了一下这个知识点。
十年前看了好多遍的设计模式,死活处于学了忘、学不会的状态;如今随便翻到的某个 Java 技术点,都能快速反应出它们的前因后果。时光如梭,毕业已经十年了,这大概算是进入了卖油翁纯熟的技艺阶段了吧!
代理模式类图。面向对象编程语言中,代理对象和委托对象都需要实现相同接口,同时代理对象关联一个真正的委托对象,客户端得到的是一个代理的引用,实则背后调用的是真正委托对象的方法:
代理类型。 对于有大量类需要代理的应用,这就是一种负担。一方面增加了工作量,而且还产生了大量相似的代理类,所以有了动态代理这个解决方案:不需要为每个类都创建一个代理类,只在需要使用代理的时候,通过反射机制动态地生成一个实现代理接口的匿名类的实例。代理的分类:动态代理和静态代理,即生成代理类的方式是什么。
动态代理的两种类型:JDKProxy 和 CGlib 两种,二者比对结果如下
类型 | 原理 | 特点 |
---|---|---|
JDKProxy | 运行时直接写Class字节码 | 生成代理类效率高,反射执行效率低 |
CodeGeneratorLibrary | ASM框架写 Class 字节码 | 生成代理类效率低,FastClass 机制直接调用方法,执行效率高 |
Java 而言,ASM 就是字节码级别的编程,assembly
,即汇编语言。学过汇编的同学,有没有感觉很亲切呢?
JDKProxy。 JDK 是支持动态代理的,核心类为 Proxy
JDK 的 Proxy 实现动态代理,优势是,抽象统一的 InvocationHandler
实现类,完成公共的代理增强动作。然后参数是 targetInstance
。不需要定义 Proxy 实现类,直接通过动态代理生成代理类实例即可。
测试 JDKProxy 。根据这个类图,来编写一个动态代理的测试类如下。
第一步,定义代理接口类:
public interface Delegate {
void log();
}
第二步,定义代理接口实现类:
public class DelegateImp implements Delegate{
@Override
public void log() {
System.out.println("Class is "+this.getClass().getName());
}
}
第三步,编写测试类:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Date;
public class ProxyTest {
public static void main(String[] args) {
//这里定义的是如何对委托类进行访问
DelegateImp imp = new DelegateImp();
InvocationHandler handler = new InvocationHandler() {
// proxy 这个类是最终的代理类
// 自定义的 InvocationHandler 需要指定具体的 target 目标对象的
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//调用委托类的方法之前的操作
System.out.println("Start to call "+method.getName()+",time is:"+new Date());
//调用委托类的方法
method.invoke(imp, args);
//调用委托类之后的操作
System.out.println("Finish to call "+method.getName()+",time is:"+new Date());
return null;
}
};
//创建一个代理类
Delegate proxy = (Delegate)Proxy.newProxyInstance(DelegateImp.class.getClassLoader(), DelegateImp.class.getInterfaces(), handler);
proxy.log();
}
}
运行结果:log
方法执行前后,多了一下增强动作。
Start to call log,time is:Mon Jan 20 10:59:54 CST 2020
Class is javastudy.DelegateImp
Finish to call log,time is:Mon Jan 20 10:59:54 CST 2020
装饰模式指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。(来自百度百科的定义)
《Head first 设计模式》中描绘的该模式的静态结构图为:
具体组件类和装饰类必须实现相同的顶层接口 Component
;
具体装饰实现类包含一个具体组件的引用,即装饰类的构造过程需要传入一个具体的组件类,以便对原组件类进行装饰;
装饰类可以对被装饰者的行为增强,在之前或之后加上自己的动作。
仔细分析了一下,发现代理模式和装饰模式的区别有两点:
第一点,关联的类型不同。代理模式和装饰模式的区别,从类图结构上来看:
第二点,目的不同。代理,着重是控制对委托对象的访问,由代理类本身决定谁是委托对象;装饰,是对被装饰对象的增强,由客户端指定对谁进行装饰。
另外,JDKProxy 动态代理的测试过程中,笔者想到的一些内容是:
JDKProxy 动态代理的核心是
InvokationHandler
的实现,它定义了如何对委托对象进行访问。如果在实际开发中,增强操作可以复用的话,这个接口的实现类并不会很多。但是,如果每个委托对象,都有自己的行为访问控制方式的话,那就跟静态代理没有什么区别了,因为需要为每个委托对象都创建一种InvokationHandler
的实现类。
当然,就日常开发而言,InvokationHandler
基本上能够复用,实现类的数量不会太多。
己亥年农历腊月二十六,春节快到了,祝还在坚守岗位的朋友们,上班愉快……