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

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

静态代理


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

  • 接口类
public interface ISubject {
    void operator();
}
  • 具体实现类
public class RealSubject implements ISubject{
    @Override
    public void operator() {
        System.out.println("do something");
    }
}

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

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

public class SubjectProxy implements ISubject{
    private RealSubject subject;
    public SubjectProxy(RealSubject subject) {
        this.subject = subject;
    }
    @Override
    public void operator() {
        System.out.println("this is log");
        subject.operator();
    }
}

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

public class Client {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        ISubject proxy = new SubjectProxy(realSubject);
        proxy.operator();
    }
}

执行结果如下:

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

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

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


代理模式定义


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

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

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

这就是 “代理模式”。

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


动态代理


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

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

JDK动态代理的代码实现:

  • 动态代理类
public class SubjectInvokerHandler implements InvocationHandler {
    //真正的业务对象
    private Object target;
    public SubjectInvokerHandler(Object target) {
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("执行前置业务逻辑");
        //真正的业务逻辑
        method.invoke(target, args);
        System.out.println("执行后置业务逻辑");
        return null;
    }
    public Object getProxy() {
        //创建代理对象
        return Proxy.newProxyInstance(
                Thread.currentThread().getContextClassLoader(),
                target.getClass().getInterfaces(),
                this
        );
    }
}

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

  • 客户端:
public class Client {
    public static void main(String[] args) {
        ISubject realSubject = new RealSubject();
        SubjectInvokerHandler invokerHandler = new SubjectInvokerHandler(realSubject);
        //获取代理对象
        ISubject proxy = (ISubject) invokerHandler.getProxy();
        proxy.operator();
    }
}

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

  • 执行效果:

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


JDK动态代理实现原理

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

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

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        final Class<?>[] intfs = interfaces.clone();             
        //获取代理类
        Class<?> cl = getProxyClass0(loader, intfs);
        try {
         ...
         //获取代理类的构造方法
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            return cons.newInstance(new Object[]{h});
            ...
        } 
        ...
    }

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

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

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

private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

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

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

private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>{
 // 代理类的前缀是 $Proxy
 private static final String proxyClassNamePrefix = "$Proxy";
 private static final AtomicLong nextUniqueNumber = new AtomicLong();
 @Override
 public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
    ...
  long num = nextUniqueNumber.getAndIncrement();
  //代理类的名称是通过包名、代理类名称前缀以及编号这三项组成的
  String proxyName = proxyPkg + proxyClassNamePrefix + num;
  /*
   * 生成代理类,并写入文件
   */
  byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
   proxyName, interfaces, accessFlags);
  try {
   return defineClass0(loader, proxyName,
        proxyClassFile, 0, proxyClassFile.length);
  } catch (ClassFormatError e) {
    ...
  }
 }
}


小结

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


总结


本章内容主要是讲设计模式中的代理模式,代理模式的作用就是在不修改被代理对象的源码上,进行功能增强。这种开发模式在AOP面向切面编程领域很常见。

代理模式中静态代理需要自己编写代理类,动态代理中代理类通过Proxy.newInstance()方法生成,他们的实质都是面向接口编程。

目录
相关文章
|
14天前
|
设计模式 缓存 监控
【设计模式系列笔记】代理模式
代理模式是一种结构型设计模式,它允许一个对象(代理对象)控制另一个对象的访问。代理对象通常充当客户端和实际对象之间的中介,用于对实际对象的访问进行控制、监控或其他目的。
43 1
|
14天前
|
设计模式 缓存 安全
小谈设计模式(8)—代理模式
小谈设计模式(8)—代理模式
|
12天前
|
设计模式 Java
Java一分钟之-设计模式:装饰器模式与代理模式
【5月更文挑战第17天】本文探讨了装饰器模式和代理模式,两者都是在不改变原有对象基础上添加新功能。装饰器模式用于动态扩展对象功能,但过度使用可能导致类数量过多;代理模式用于控制对象访问,可能引入额外性能开销。文中通过 Java 代码示例展示了两种模式的实现。理解并恰当运用这些模式能提升代码的可扩展性和可维护性。
28 1
|
2天前
|
设计模式 Java 数据库连接
【重温设计模式】代理模式及其Java示例
【重温设计模式】代理模式及其Java示例
|
14天前
|
设计模式 安全 网络协议
【设计模式】代理模式例子解析
【设计模式】代理模式例子解析
18 2
|
14天前
|
设计模式 JavaScript 算法
js设计模式-策略模式与代理模式的应用
策略模式和代理模式是JavaScript常用设计模式。策略模式通过封装一系列算法,使它们可互换,让算法独立于客户端,提供灵活的选择。例如,定义不同计算策略并用Context类执行。代理模式则为对象提供代理以控制访问,常用于延迟加载或权限控制。如创建RealSubject和Proxy类,Proxy在调用RealSubject方法前可执行额外操作。这两种模式在复杂业务逻辑中发挥重要作用,根据需求选择合适模式解决问题。
|
14天前
|
设计模式 JavaScript Java
[设计模式Java实现附plantuml源码~结构型]对象的间接访问——代理模式
[设计模式Java实现附plantuml源码~结构型]对象的间接访问——代理模式
|
14天前
|
设计模式 Go 网络安全
[设计模式 Go实现] 结构型~代理模式
[设计模式 Go实现] 结构型~代理模式
|
14天前
|
设计模式 缓存 Java
23种设计模式,代理模式的概念优缺点以及JAVA代码举例
4月更文挑战第7天】代理模式是一种常用的软件设计模式,它为其他对象提供一种代理以控制对这个对象的访问。这种模式创建具有原始对象相同接口的对象,从而使代理对象在访问者和目标对象之间作为一个中介。
18 0
|
14天前
|
设计模式 uml
设计模式之代理模式
设计模式之代理模式