设计模式(2)代理模式
发布日期:2021-05-25 14:48:20 浏览次数:18 分类:精选文章

本文共 4241 字,大约阅读时间需要 14 分钟。

代理模式详解:从静态到动态,再到CGLIB

在软件开发中,我们经常会遇到一个问题:一个类或对象的行为需要通过中介来间接实现,而不是直接操作该对象。这种设计模式就叫做代理模式(Proxy Pattern)。通过本文的深入探讨,你将彻底理解代理模式的核心思想、实现方法以及适用场景。

什么是代理模式?

代理模式可以简单理解为为某一个对象提供一个代理对象,通过代理对象控制对目标对象的引用,最终达到间接调用目标对象的目的。这个概念在现实生活中有着类似的体现。在之前提到的买房例子中,中介公司充当了买房者和卖方之间的桥梁,协助完成整个买房流程。

为什么要使用代理模式?

使用代理模式的主要原因包括:

  • 中介隔离作用:在某些情况下,客户端不应该或无法直接引用目标对象,而代理模式通过中间的代理对象,隔离了目标对象,使其更加安全和便捷。

  • 开闭原则,增加功能:代理模式允许我们在不修改目标对象的情况下,扩展功能。例如,可以在调用目标对象的方法之前或之后,注入额外的功能(比如缓存、日志记录等)。

  • 方便日志和追踪:代理模式使得我们能够在不修改目标对象的情况下,在方法执行前记录日志,或者在返回值后对结果进行处理。

  • 代理模式的实现方式

    代理模式可以根据代理对象的创建时间分为两种典型实现方式:

    1. 静态代理

    静态代理的特点是需要提前为目标对象创建代理类。其主要步骤如下:

  • 创建服务接口

    服务接口定义了目标对象的操作规范。例如,二手车交易接口如下:

    package main.java.proxy;
    public interface BuyHouse {
    void buyHouse();
    }
  • 实现服务接口

    目标对象实现该接口,具体行为如下:

    package main.java.proxy.impl;
    public class BuyHouseImpl implements BuyHouse {
    @Override
    public void buyHouse() {
    System.out.println("我要买房");
    }
    }
  • 创建代理类

    代理类实现同样的接口,并控制对目标对象的调用。例如,房产中介的代理类如下:

    package main.java.proxy.impl;
    public class BuyHouseProxy implements BuyHouse {
    private BuyHouse buyHouse;
    public BuyHouseProxy(BuyHouse buyHouse) {
    this.buyHouse = buyHouse;
    }
    @Override
    public void buyHouse() {
    System.out.println("买房前准备");
    buyHouse.buyHouse();
    System.out.println("买房后装修");
    }
    }
  • 编写测试类

    通过测试类验证代理模式的效果:

    package main.java.proxy.test;
    import main.java.proxy.BuyHouse;
    import main.java.proxy.impl.BuyHouseImpl;
    public class ProxyTest {
    public static void main(String[] args) {
    BuyHouse buyHouse = new BuyHouseImpl();
    buyHouse.buyHouse();
    BuyHouseProxy buyHouseProxy = new BuyHouseProxy(buyHouse);
    buyHouseProxy.buyHouse();
    }
    }
  • 优点:开闭原则的应用,通过扩展代理类而非目标类,实现功能的灵活扩展。

    缺点:每个服务接口都需要手动创建代理类,工作量大,并且接口变更时,代理类也需要相应修改。

    2. 动态代理

    动态代理则通过代码生成工具或反射机制,在程序运行时动态创建代理对象。它适用于那些接口不容易变更的情况,且需要灵活扩展功能的场景。

    动态代理的实现方式主要通过InvocationHandler来处理目标对象的方法调用。例如,动态房产中介代理类如下:

    package main.java.proxy.impl;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    public class DynamicProxyHandler implements InvocationHandler {
    private Object target;
    public DynamicProxyHandler(Object target) {
    this.target = target;
    }
    public Object invoke(Object proxy, Method method, Object[] args)
    throws Throwable {
    System.out.println("买房前准备");
    Object result = method.invoke(target, args);
    System.out.println("买房后装修");
    return result;
    }
    }

    测试类如下:

    package main.java.proxy.test;
    import main.java.proxy.BuyHouse;
    import main.java.proxy.impl.BuyHouseImpl;
    import java.lang.reflect.Proxy;
    public class DynamicProxyTest {
    public static void main(String[] args) {
    BuyHouse buyHouse = new BuyHouseImpl();
    BuyHouse proxy = (BuyHouse) Proxy.newProxyInstance(
    BuyHouse.class.getClassLoader(),
    new Class[]{BuyHouse.class},
    new DynamicProxyHandler(buyHouse)
    );
    proxy.buyHouse();
    }
    }

    优点:减少手动代理类开发代码量,灵活可扩展。

    缺点:动态代理只能代理接口,无法代理本类中的方法,这种限制在实际开发中也带有一定的不便。

    3. CGLIB代理(增强代理)

    CGLIB(Commons-Gateway Language Instrumentation Bridge)通过生成目标对象的子类,动态拦截目标方法进行处理。这种方式适用于需要代理非接口类的对象,但不会对资源密集型的业务逻辑产生性能上的负担。

    CGLIB代理类示例如下:

    package dan.proxy.impl;
    import net.sf.cglib-proxy.CGLib;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    import java.lang.reflect.Method;
    public class CglibProxy implements MethodInterceptor {
    private Object target;
    public Object getInstance(Object target) {
    this.target = target;
    return CGLib.create.getMethodInterceptor(this);
    }
    public Object intercept(Object obj, Method method, Object[] args,
    MethodProxy methodProxy) throws Throwable {
    System.out.println("买房前准备");
    Object result = methodProxy.invoke(obj, args);
    System.out.println("买房后装修");
    return result;
    }
    }

    测试类:

    package dan.proxy.test;
    import dan.proxy.BuyHouse;
    import dan.proxy.impl.BuyHouseImpl;
    public class CglibProxyTest {
    public static void main(String[] args) {
    BuyHouse house = new BuyHouseImpl();
    CglibProxy proxy = new CglibProxy();
    BuyHouse proxyHouse = (BuyHouse) proxy.getInstance(house);
    proxyHouse.buyHouse();
    }
    }

    优点:性能优于JDK动态代理,支持非接口类的代理。

    缺点:生成的代理类为目标对象的子类,可能存在性能上的开销,且无法代理final方法。

    上一篇:设计模式(3)工厂模式
    下一篇:JDK 1.8新特性整理-日期时间 API (七)

    发表评论

    最新留言

    表示我来过!
    [***.240.166.169]2025年04月20日 20时11分43秒