设计模式之代理模式(文末赠书)

简介: JDK动态代理的实现原理是动态创建代理类并通过指定类加载器进行加载,在创建代理对象时将InvocationHandler对象作为构造参数传入。当调用代理对象时,会调用 InvocationHandler.invoke() 方法,从而执行代理逻辑,并最终调用真正业务对象的相应方法。

静态代理


在开始代理模式定义之前我们先看一段常见的业务逻辑,假设你有个接口ISubject,接口有个operator方法,然后有个具体的实现类来实现此方法:


  • 接口类


publicinterfaceISubject {
voidoperator();
}


  • 具体实现类


publicclassRealSubjectimplementsISubject{
@Overridepublicvoidoperator() {
System.out.println("do something");
    }
}


那现在你有个新的需求,就是在执行operator方法之前需要打印一段日志,但是不能修改原RealSubject的业务逻辑,那该怎么实现呢?


这时候大家伙肯定会想到建一个新的实现类来实现ISubject,并将RealSubject组合进来,真正的业务逻辑还是调用RealSubject的operator方法来实现,在执行operator之前实现我们需要的业务逻辑,比如日志打印。


publicclassSubjectProxyimplementsISubject{
privateRealSubjectsubject;
publicSubjectProxy(RealSubjectsubject) {
this.subject=subject;
    }
@Overridepublicvoidoperator() {
System.out.println("this is log");
subject.operator();
    }
}


客户端调用的时候我们直接使用SubjectProxy来实现业务逻辑即可:


publicclassClient {
publicstaticvoidmain(String[] args) {
RealSubjectrealSubject=newRealSubject();
ISubjectproxy=newSubjectProxy(realSubject);
proxy.operator();
    }
}


执行结果如下:


1.png


看到这里你可能会想,这就是代理模式?就这?


是的,这就是代理模式,理解了这个例子你就差不多掌握了代理模式,只不过目前还是静态代理,等会我们再讲讲如何实现动态代理。


让我们先来看看代理模式的定义。


代理模式定义


代理模式的类图结构如下:


2.png


图中的 Subject 是程序中的业务逻辑接口,RealSubject 是实现了 Subject 接口的真正业务类,Proxy 是实现了 Subject 接口的代理类,封装了一个 RealSubject 引用。在程序中不会直接调用 RealSubject 对象的方法,而是使用 Proxy 对象实现相关功能。


Proxy.operator() 方法的实现会调用其中封装的 RealSubject 对象的 operator() 方法,执行真正的业务逻辑。


这就是 “代理模式”。


通过上面的例子我们可以看出,使用代理模式可以在不修改被代理对象的基础上,通过扩展代理类来进行一些功能的附加与增强,值得注意的是,代理类和被代理类应该共同实现一个接口,或者共同继承某个类。


动态代理


上面例子中我们展示的是代理模式中的 “静态代理模式”,这是因为我们需要预先定义好代理类SubjectProxy,当需要代理的类很多时,就会出现很多的Proxy类。


在这种场景下我们就需要使用动态代理了,动态代理的实现方式又有很多种,本章我们来看看JDK原生的动态代理。


JDK动态代理的代码实现:


  • 动态代理类


publicclassSubjectInvokerHandlerimplementsInvocationHandler {
//真正的业务对象privateObjecttarget;
publicSubjectInvokerHandler(Objecttarget) {
this.target=target;
    }
@OverridepublicObjectinvoke(Objectproxy, Methodmethod, Object[] args) throwsThrowable {
System.out.println("执行前置业务逻辑");
//真正的业务逻辑method.invoke(target, args);
System.out.println("执行后置业务逻辑");
returnnull;
    }
publicObjectgetProxy() {
//创建代理对象returnProxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
target.getClass().getInterfaces(),
this        );
    }
}


动态代理的核心就是InvocationHandler 接口,它只有一个invoke()方法,这个方法决定了如何处理传递过来的方法调用。


  • 客户端:


publicclassClient {
publicstaticvoidmain(String[] args) {
ISubjectrealSubject=newRealSubject();
SubjectInvokerHandlerinvokerHandler=newSubjectInvokerHandler(realSubject);
//获取代理对象ISubjectproxy= (ISubject) invokerHandler.getProxy();
proxy.operator();
    }
}


对于需要相同代理逻辑的业务类,只需要提供一个 InvocationHandler 接口实现类即可。在 Java 运行的过程中,JDK会为每个 RealSubject 类动态生成相应的代理类并加载到 JVM 中,然后创建对应的代理实例对象,返回给上层调用者。


  • 执行效果:


3.png


可以看到我们这里并没有像静态代理一样实现具体的代理类,但是最终却实现了同样的效果。


JDK动态代理实现原理


了解了动态代理的基本使用后我们来看看动态代理的实现原理。


创建动态代理的入口类是Proxy.newProxyInstance()这个静态方法,它的三个参数分别是加载动态生成的代理类的类加载器、业务类实现的接口和InvocationHandler对象。(代码有点长,我们截取一下):


publicstaticObjectnewProxyInstance(ClassLoaderloader,
Class<?>[] interfaces,
InvocationHandlerh)
throwsIllegalArgumentException    {
finalClass<?>[] intfs=interfaces.clone();             
//获取代理类Class<?>cl=getProxyClass0(loader, intfs);
try {
          ...
//获取代理类的构造方法finalConstructor<?>cons=cl.getConstructor(constructorParams);
finalInvocationHandlerih=h;
returncons.newInstance(newObject[]{h});
            ...
        } 
        ...
    }


newProxyInstance()最终返回一个实例,它是通过 cl 这个 Class 文件的构造方法反射生成。cl 则由 getProxyClass0() 方法获取,代码如下:


privatestaticClass<?>getProxyClass0(ClassLoaderloader,
Class<?>... interfaces) {
  ...
// 如果指定的类加载器中已经创建了实现指定接口的代理类,则查找缓存;// 否则通过ProxyClassFactory创建实现指定接口的代理类returnproxyClassCache.get(loader, interfaces);
}


proxyClassCache 是定义在 Proxy 类中的静态字段,主要用于缓存已经创建过的代理类,定义如下:


privatestaticfinalWeakCache<ClassLoader, Class<?>[], Class<?>>proxyClassCache=newWeakCache<>(newKeyFactory(), newProxyClassFactory());


WeakCache.get() 方法会首先尝试从缓存中查找代理类,如果查找不到,则会创建 Factory 对象并调用其 get() 方法获取代理类。Factory 是 WeakCache 中的内部类,Factory.get() 方法会调用 ProxyClassFactory.apply() 方法创建并加载代理类。


ProxyClassFactory.apply() 方法首先会检测代理类需要实现的接口集合,然后确定代理类的名称,之后创建代理类并将其写入文件中,最后加载代理类,返回对应的 Class 对象用于后续的实例化代理类对象。该类的核心代码如下:


privatestaticfinalclassProxyClassFactoryimplementsBiFunction<ClassLoader, Class<?>[], Class<?>>{
// 代理类的前缀是 $ProxyprivatestaticfinalStringproxyClassNamePrefix="$Proxy";
privatestaticfinalAtomicLongnextUniqueNumber=newAtomicLong();
@OverridepublicClass<?>apply(ClassLoaderloader, Class<?>[] interfaces) {
     ...
longnum=nextUniqueNumber.getAndIncrement();
//代理类的名称是通过包名、代理类名称前缀以及编号这三项组成的StringproxyName=proxyPkg+proxyClassNamePrefix+num;
/** 生成代理类,并写入文件*/byte[] proxyClassFile=ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
returndefineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
    } catch (ClassFormatErrore) {
      ...
    }
  }
}


小结


JDK动态代理的实现原理是动态创建代理类并通过指定类加载器进行加载,在创建代理对象时将InvocationHandler对象作为构造参数传入。当调用代理对象时,会调用 InvocationHandler.invoke() 方法,从而执行代理逻辑,并最终调用真正业务对象的相应方法。

目录
相关文章
|
6月前
|
设计模式 缓存 安全
小谈设计模式(8)—代理模式
小谈设计模式(8)—代理模式
|
6月前
|
设计模式 Java
【设计模式系列笔记】装饰者模式
装饰者模式是一种结构型设计模式,它允许你通过将对象放入包含行为的特殊封装类中来为原始对象添加新的行为。这种模式可以动态地将责任附加到对象上,而不影响其它对象。
70 11
|
6月前
|
设计模式
|
设计模式 缓存 安全
深入浅出设计模式 - 代理模式
深入浅出设计模式 - 代理模式
11759 4
深入浅出设计模式 - 代理模式
|
设计模式 缓存 Java
设计模式之代理模式(文末赠书)
设计模式之代理模式(文末赠书)
70 0
|
设计模式 存储 缓存
【设计模式自习室】详解代理模式
《设计模式自习室》系列,顾名思义,本系列文章带你温习常见的设计模式。主要内容有: 该模式的介绍,包括: 引子、意图(大白话解释) 类图、时序图(理论规范) 该模式的代码示例:熟悉该模式的代码长什么样子 该模式的优缺点:模式不是万金油,不可以滥用模式 该模式的应用案例:了解它在哪些重要的源码中被使用
145 0
|
设计模式 Java
设计模式学习——代理模式(2)
上一篇我们简单介绍了什么是代理模式,但我们说的仅仅是静态代理,所谓静态代理即代理类和目标类在代码中是确定的,因此称为静态,这种方式存在以下问题: 由于在使用时需要提前定义好代理类并实现对应主题的接口方法,随着需要代理的主题增加,代理类也会增加,导致项目中出现大量类情况,不易于项目维护。 那么有没有什
120 0
设计模式学习——代理模式(2)
|
设计模式
设计模式学习——代理模式(1)
一、什么是代理模式 为其他对象提供一种代理以控制对这个对象的访问。其类图如下: Subject:抽象主题类,它既可以是抽象类也可以是接口类,是一个最普通的业务类型定义,无特殊要求。 RealSubject:具体主题类,被代理角色,是具体业务逻辑实现执行者。 Proxy:代理类,负责对具体主题的应用,
152 0
设计模式学习——代理模式(1)
|
设计模式 缓存 Java
设计模式最佳套路3 —— 愉快地使用代理模式
代理模式(Proxy Pattern)即为某一个对象提供一个代理对象,由代理对象来接管被代理对象的各个方法的访问。
978 0
设计模式最佳套路3 —— 愉快地使用代理模式
|
设计模式 Java
设计模式—— 十二 :代理模式
设计模式—— 十二 :代理模式
163 0
设计模式—— 十二 :代理模式