亲宝软件园·资讯

展开

Java结构型模式之代理模式详解

非凡的小笨鱼 人气:0

一.介绍

在代理模式(Proxy Pattern)属于结构型模式。在代理模式中,我们对一个对象提供一个代理对象,使用代理对象控制原对象的引用,目的是为了透明的控制对象访问

二.UML类图

三.代理模式分类

Java中的代理按照代理类生成时机不同分为静态代理和动态代理,静态代理的代理类在编译器就生成,而动态代理的代理类在Java运行时动态生成。动态代理又分为JDK代理和CGLib代理。

四.静态代理

业务代码

/**
 * 静态代理
 */
public interface Pay {
    void pay();
}
//真实类
class Alipay implements Pay {
    @Override
    public void pay() {
        System.out.println("支付宝支付");
    }
}
//代理类
class AlipayProxy implements Pay{
    //组合真实对象
    private final Alipay alipay = new Alipay();
    @Override
    public void pay() {
        long startTime = System.currentTimeMillis();
        alipay.pay();
        System.out.println("执行了" + (System.currentTimeMillis()-startTime) + "毫秒"); //支付宝支付 执行了0毫秒
    }
}

测试代码

public class Client {
    public static void main(String[] args) {
        new AlipayProxy().pay();
    }
}

五.静态代理的优缺点

优点

缺点

六.动态代理

为了弥补静态代理的缺点,引入了动态代理

1.JDK动态代理(利用Java提供的代理机制)

业务代码

/**
 * JDK动态代理
 */
public interface Pay {
    void pay();
}
//真实类
class Alipay implements Pay {
    @Override
    public void pay() {
        System.out.println("支付宝支付");
    }
}
class PayProxy {
    //组合真实对象
    private Pay pay;
    public PayProxy(Pay pay) {
        this.pay = pay;
    }
    public Pay getProxy() {
        return (Pay) Proxy.newProxyInstance(getClass().getClassLoader(), pay.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                long startTime = System.currentTimeMillis();
                Object result = method.invoke(pay, args);
                System.out.println("执行了" + (System.currentTimeMillis() - startTime) + "毫秒");
                return result;
            }
        });
    }
}

测试代码

public class Client {
    public static void main(String[] args) {
        PayProxy payProxy = new PayProxy(new Alipay());
        Pay pay = payProxy.getProxy();
        pay.pay(); //支付宝支付 执行了0毫秒
    }
}

我们通过arthas工具进行反编译,可以找到真正的代理类$Proxy0

//代理对象
public final class $Proxy0 extends Proxy implements Pay {
    private static Method m3;
    public $Proxy0(InvocationHandler invocationHandler) {
        super(invocationHandler);
    }
    static {
        // 通过反射获取名叫pay的menthod
        m3 = Class.forName("com.designpattern.structure.proxy.v2.Pay").getMethod("pay", new Class[0]);
        return;
    }
    public final void pay() {
        // h是invocationHandler对象
        this.h.invoke(this, m3, null);
        return;
    }
}

总结执行流程如下

  1. 测试代码里执行了pay.pay()
  2. 根据多态的特性,执行的是代理类($Proxy0)中的pay方法
  3. 代理类($Proxy0)中的pay方法中执行了invocationHandler对象的invoke方法
  4. invocationHandler对象的invoke方法就是业务代码中传入的匿名内部类中重写的invoke方法
  5. 在重写的invoke方法中通过反射调用真实对象alipay的pay方法

2.CGLib动态代理

JDK动态代理要求必须定义接口,如果没有定义接口,就可以使用CGLib动态代理,CGLib为JDK的动态代理提供了很好的补充

首先引入cglib-3.3.0.jar与asm-9.0.jar

业务代码

//真实对象
class Alipay implements Pay {
    @Override
    public void pay() {
        System.out.println("支付宝支付");
    }
}
class AlipayProxy implements MethodInterceptor {
    //组合真实对象
    private Alipay alipay = new Alipay();
    public Alipay getProxy(){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Alipay.class);
        //设置回调函数
        enhancer.setCallback(this);
        //返回代理对象
        return (Alipay) enhancer.create();
    }
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = method.invoke(alipay, args);
        System.out.println("执行了" + (System.currentTimeMillis() - startTime) + "毫秒");
        return result;
    }
}

测试代码

public class Client {
    public static void main(String[] args) {
        Alipay proxy = new AlipayProxy().getProxy();
        proxy.pay(); //支付宝支付 执行了0毫秒
    }
}

七.JDK代理与CGLIB代理对比

八.代理模式的优缺点

优点

缺点

九.使用场景

1.功能扩展:日志、监控、事务

2.控制管理:权限、限流

3.远程代理:FeignClient、RMI

4.动态逻辑:mybatis mapper、jpa

5.延迟加载:虚代理

十.通用的动态代理实现(拓展)

上文提到静态代理是一个具体类产生一个代理类,可能会造成类爆炸,我们现在反观动态代理则是一个接口产生一个代理类,也可能会造成类爆炸,所以这里给出一个较为通用的实现

业务代码

//记录执行的时间的通用类
public class TimeRecordProxy<T> {
    private final T target;
    public TimeRecordProxy(T target) {
        this.target = target;
    }
    @SuppressWarnings("unchecked")
    public T getProxy() {
        return (T) Proxy.newProxyInstance(this.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                this::invoke);
    }
    private Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
        long startTime = System.currentTimeMillis();
        Object result = method.invoke(target, args);
        System.out.println("执行了" + (System.currentTimeMillis()-startTime) + "毫秒");
        return result;
    }
}

测试代码

public class Client {
    public static void main(String[] args) {
        TimeRecordProxy<Pay> timeRecordProxy = new TimeRecordProxy<>(new Alipay());
        timeRecordProxy.getProxy().pay(); //支付宝支付 执行了0毫秒
    }
}

Spring AOP是代理模式的典型应用

加载全部内容

相关教程
猜你喜欢
用户评论