设计模式-代理模式(Proxy Pattern)

上一篇 <<<Java基础-反射机制
下一篇 >>>Java基础-字节码技术


代理模式:使用代理对象完成用户请求,屏蔽用户对真实对象的访问。

  • 优点:减少代码冗余、提高代码复用性、安全性、隐藏真实角色、非入侵

应用场景

  • Spring AOP
  • 过滤器
  • 自定义注解
  • 全局捕获异常
  • 事务原理
  • 日志收集打印
  • 权限控制
  • RPC远程调用
  • 安全代理可以隐蔽真实角色
  • Mybatis的Mapper
  • 全局ID(LCN/Seata中)

分类

静态代理:实现接口和实现集成两种方式
动态代理:JDK代理(接口实现)和CGLIB代理(继承方式)

区别:静态代理需要自己写代理类,而动态代理不需要写代理类。

静态代理

  • 接口实现
/**静态代理:接口实现*/
OrderService orderService = new OrderServiceProxy1(new OrderServiceImpl());
orderService.order();
/** 
* 接口实现方式
*/
public class OrderServiceProxy1 implements OrderService {
    /**
     * 代理对象
     */
    private OrderService proxiedOrderService;

    public OrderServiceProxy1(OrderService orderService) {
      this.proxiedOrderService=orderService;
    }

    public void order() {
        System.out.println("日志收集开始..");
        proxiedOrderService.order();
        System.out.println("日志收集结束..");
    }
}
  • 继承实现
/**静态代理:继承实现*/
OrderService orderService = new OrderServiceProxy();
orderService.order();
/**
 * 继承方式
 */
public class OrderServiceProxy extends OrderServiceImpl {
    @Override
    public void order() {
        System.out.println("日志收集开始..");
        super.order();
        System.out.println("日志收集结束..");
    }
}

动态代理

1.JDK动态代理

执行步骤
1.创建被代理的接口和类;
2.实现InvocationHandler接口,对目标接口中声明的所有方法进行统一处理;
3.调用Proxy的静态方法,创建代理类并生成相应的代理对象;

1).核心原理 (代理类的生成、编译及加载到jvm中)

a、生成代理类---该类实现了接口,并集成Proxy类【通过编译之后可查看】

Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);

b、执行代理类的order等目标方法,实际上是调用MyInvocationHandel h的invoke方法
缺点:jdk动态代理,必须是面向接口,目标业务类必须实现接口

2).调试源码

JdkInvocationHandler jdkInvocationHandler = new JdkInvocationHandler(new OrderServiceImpl());
OrderService proxy = jdkInvocationHandler.getProxy();
proxy.order();
/**
 * JDK动态代理
 */
public class JdkInvocationHandler implements InvocationHandler {
    /**
     * 目标代理对象
     */
    public Object target;

    public JdkInvocationHandler(Object target) {
        this.target = target;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(">>>日志收集开始>>>>");
        // 执行代理对象方法
        Object reuslt = method.invoke(target, args);
        System.out.println(">>>日志收集结束>>>>");
        return reuslt;
    }

    /**
     * 获取代理对象接口
     *
     */
    public <T> T getProxy() {
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }
}

3).自动生成的代理类源码

public final class $Proxy0 extends Proxy implements OrderService
{
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;
    
    public $Proxy0(final InvocationHandler invocationHandler) {
        super(invocationHandler);
    }
    
    public final void order() {
        try {
            super.h.invoke(this, $Proxy0.m3, null);
        }
        catch (Error | RuntimeException error) {
            throw;
        }
        catch (Throwable t) {
            throw new UndeclaredThrowableException(t);
        }
    }
    
    static {
        try {
            $Proxy0.m3 = Class.forName("cn.jarye.service.OrderService").getMethod("order", (Class<?>[])new Class[0]);
        }
        catch (NoSuchMethodException ex) {
            throw new NoSuchMethodError(ex.getMessage());
        }
        catch (ClassNotFoundException ex2) {
            throw new NoClassDefFoundError(ex2.getMessage());
        }
    }
}

4).手写JDK动态代理思路

a.使用java反射机制拼接$Proxy.java类的源代码---参考Proxy.newProxyInstance方法
b.需要将$Proxy.java编译成$Proxy.class
c.程序中直接读取该class文件到内存中

2.CGLIB动态代理

1).核心原理

利用asm字节码开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

2).调试源码

CglibMethodInterceptor cglibMethodInterceptor = new CglibMethodInterceptor();
//相当于生成代理类
Enhancer enhancer = new Enhancer();
// 设置代理类的付类
enhancer.setSuperclass(OrderServiceImpl.class);
// 设置回调对象
enhancer.setCallback(cglibMethodInterceptor);
// 创建代理对象
OrderServiceImpl orderServiceImpl = (OrderServiceImpl) enhancer.create();
orderServiceImpl.order();
public class CglibMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("<<<<<日志收集开始...>>>>>>>");
        Object reuslt = proxy.invokeSuper(obj, args);
        System.out.println("<<<<<日志收集结束...>>>>>>>");
        return reuslt;
    }
}

3).自动生成的代理类源码

  • 索引机制
    使用字节码技术获取当前所有的方法,对每个方法加上一个索引,直接根据索引调用到目标方法效率是比反射机制要高。
MethodProxy类的源码:
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
        try {
            this.init();
            MethodProxy.FastClassInfo fci = this.fastClassInfo;
            return fci.f2.invoke(fci.i2, obj, args);
        } catch (InvocationTargetException var4) {
            throw var4.getTargetException();
        }
    }
  • 索引计算方法
    使用方法名称+参数类型计算hash值,在根据hash值得出索引
  • 代理类 (使用继承方法)
public class OrderServiceImpl$$EnhancerByCGLIB$$279607fc extends OrderServiceImpl implements Factory
{
    private static Object CGLIB$CALLBACK_FILTER;
    private static final Method CGLIB$order$0$Method;
    private static final MethodProxy CGLIB$order$0$Proxy;
    
    static void CGLIB$STATICHOOK1() {
        final Class<?> forName3;
        CGLIB$order$0$Method = ReflectUtils.findMethods(new String[] { "order", "()V" }, (forName3 = Class.forName("com.jarye.service.impl.OrderServiceImpl")).getDeclaredMethods())[0];
        CGLIB$order$0$Proxy = MethodProxy.create((Class)forName3, (Class)forName, "()V", "order", "CGLIB$order$0");
    }
    
    final void CGLIB$order$0() {
        super.order();
    }
    
    public final void order() {
        MethodInterceptor cglib$CALLBACK_2;
        MethodInterceptor cglib$CALLBACK_0;
        if ((cglib$CALLBACK_0 = (cglib$CALLBACK_2 = this.CGLIB$CALLBACK_0)) == null) {
            CGLIB$BIND_CALLBACKS(this);
            cglib$CALLBACK_2 = (cglib$CALLBACK_0 = this.CGLIB$CALLBACK_0);
        }
        if (cglib$CALLBACK_0 != null) {
            cglib$CALLBACK_2.intercept((Object)this, OrderServiceImpl$$EnhancerByCGLIB$$279607fc.CGLIB$order$0$Method, OrderServiceImpl$$EnhancerByCGLIB$$279607fc.CGLIB$emptyArgs, OrderServiceImpl$$EnhancerByCGLIB$$279607fc.CGLIB$order$0$Proxy);
            return;
        }
        super.order();
    }
// 设置回调
    public void setCallbacks(final Callback[] array) {
        this.CGLIB$CALLBACK_0 = (MethodInterceptor)array[0];
    }
    
    static {
        CGLIB$STATICHOOK1();
    }
}
  • 方法索引
public int getIndex(final String s, final Class[] array) {
        Label_1079: {
            switch (s.hashCode()) {
                case 106006350: {
                    if (!s.equals("order")) {
                        break;
                    }
                    switch (array.length) {
                        case 0: {
                            return 7;
                        }
                        default: {
                            break Label_1079;
                        }
                    }
                    break;
                }
            }
        }
        return -1;
    }
    
    public Object invoke(final int n, final Object o, final Object[] array) throws InvocationTargetException {
        final OrderServiceImpl$$EnhancerByCGLIB$$279607fc orderServiceImpl$$EnhancerByCGLIB$$279607fc = (OrderServiceImpl$$EnhancerByCGLIB$$279607fc)o;
        try {
            switch (n) {
                case 7: {
                    orderServiceImpl$$EnhancerByCGLIB$$279607fc.order();
                    return null;
                }
            }
        }
        catch (Throwable t) {
            throw new InvocationTargetException(t);
        }
        throw new IllegalArgumentException("Cannot find matching method/constructor");
    }

4).手写CGLIB动态代理思路

/**
* 直接生成代理类,然后设置callback方法
*/
MemberServiceImpl$$EnhancerByCGLIB$$600aa7a2 memberServiceImpl=
    new MemberServiceImpl$$EnhancerByCGLIB$$600aa7a2();
// 设置回调
memberServiceImpl.setCallbacks(new CglibMethodInterceptor());
memberServiceImpl.addMember("jarye");

3.JDK和CGLIB动态代理

1).差异对比

对比点 JDK动态代理 CGLIB动态代理
代码上 实现InvocationHandler接口,对目标接口中声明的所有方法进行统一处理 实现MethodInterceptor接口的intercept方法
原理上 使用Java的反射技术生成继承了Proxy类的动态匿名类,只能代理实现了接口的类, 没有实现接口的类不能实现动态代理。 通过ASM字节码处理框架来转换字节码并生成代理类的子类,子类重写了被代理类中所有非final的方法,在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势植入横切逻辑。大部分功能实际上是ASM所提供的,Cglib只是封装了ASM,简化了ASM操作,实现了运行期生成新的class。
缺陷 jdk动态代理,必须是面向接口,目标业务类必须实现接口 对于被代理类中的final方法,无法进行代理,因为子类中无法重写final函数
总结 jdk动态代理最终使用反射机制执行目标方法 cglib根据索引找到目标方法,然后通过super.目标方法执行

2).Cglib的效率比Jdk动态代理效率要高原因【jdk7开始JDK动态代理更加高效】

  • Jdk动态动态代理 走回调拦截 实现接口接口生成带了类 使用反射技术执行我们的目标方法
    a.拼接java源代码
    b.编译为class文件
    c.读取去class文件到内存中
  • Cglib动态代理 采用继承的模式生成代理类 底层基于Asm字节码技术实现生成代理类
    a.生成class文件
    b.读取去class文件到内存中
    c、采用fastClass索引的机制执行我们的目标方法

2).代理模式在Spring中的应用

  • 如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP,但可以强制使用CGLIB实现
  • 如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
    Spring中的@Async注解使用不当会导致失效,请参考:@Async失效之谜

相关文章链接:
<<<23种常用设计模式总览
<<<装饰模式(Decorator Pattern)
<<<观察者模式(Observer Pattern)
<<<单例模式(Singleton Pattern)
<<<责任链模式(Chain of Responsibility Pattern)
<<<策略模式(Strategy Pattern)
<<<模板方法模式(Template Pattern)
<<<外观/门面模式(Facade Pattern)
<<<建造者模式(Builder Pattern)
<<<适配器模式(Adapter Pattern)
<<<原型模式(Prototype Pattern)
<<<工厂相关模式(Factory Pattern)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容

  • 一、代理模式定义 给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常...
    AC编程阅读 527评论 0 0
  • 代理模式(Proxy Pattern)是指由于某些原因需要给某个对象提供一个代理以控制对该对象的访问,这时访问对象...
    吉他手_c156阅读 51评论 0 0
  • What: 为其他对象提供一种代理以控制对这个对象的访问。 Why: 优点: 1.增强目标对象。可以在执行目标对象...
    爱打乒乓的程序员阅读 2,776评论 0 1
  • 1.介绍 1.1定义 代理模式的定义:代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的...
    luoqiang108阅读 193评论 0 0
  • 久违的晴天,家长会。 家长大会开好到教室时,离放学已经没多少时间了。班主任说已经安排了三个家长分享经验。 放学铃声...
    飘雪儿5阅读 7,480评论 16 22