代理模式

通过代理控制对对象的访问,可以详细控制访问某个(某类)对象的方法,在调用这个方法之前做前置处理,调用这个方法后做后置处理。

与装饰者模式的简单区别

两者都是对类的方法(能力进行拓展),但是:

  • 装饰者模式强调的还是本身功能的增强。仍然和被装饰者做相同的事情,职责上并没有进行延伸。
  • 代理模式强调的是代理和“中介”的能力。在代理中去做一些与本身业务不相关的事情而不对对象本身的功能做增强或改造,也可以对被代理对象进行控制或包装,避免暴露。

JDK动态代理

接口:

public interface Animal
{void run();void fly();
}

一个会飞的狗实现:

public class Dog implements Animal
{@Overridevoid run(){System.out.println("Dog run");}@Overridevoid fly(){System.out.println("Dog fly");}
}

为了记录狗的日常动作,需要一个记录仪。这项工作,狗自己是完成不了的。但是要理解,这个记录仪并不是一个代理类,因为它并不能进行run或者fly,无法提供服务,只能算是一个包装类。这个包装类是一个InvocationHandler。


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;public class DogProxy implements InvocationHandler
{// 目标类,也就是被代理对象。包装类是持有这个被代理对象的private Object target;public void setTarget(Object target){this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable{// 这里可以做控制 做记录等System.out.println("开始" + method.getDeclaringClass().getName() + method.getName());Object result = method.invoke(target, args);System.out.println("结束" + method.getDeclaringClass().getName() + method.getName());return result;}// 生成代理类public Object CreatProxyedObj(){return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);}  }

真正的代理类是CreatProxyedObj()返回的对象。它的意思就是,使用相同的classLoader,通过被包装类实现的相同的接口,来创建一个实现对象,而这个对象的方法invoke通过this来触发,this就是这个InvocationHandler。

可以看到,一般代理套路:

step1: new 被代理的对象A。
step2: new 一个InvocationHandler B,并持有被代理对象。重写invoke方法,本身的方法执行需要交给被代理对象去执行,并在invoke中添加自己想要的操作。
step3: 创建真正的代理对象 C,并转换为所实现的接口类型。Proxy.newProxyInstance()方法所生成的对象已经实现了目标接口。生成的类为 $Proxy+数字 的“新的类型”。

实际的内涵是这样的:C代理了InvocationHandler B,InvocationHandler B代理了我们的类A,两级代理。其实Proxy.newProxyInstance()的作用,就是把一个实际的内涵是这样的:C代理了InvocationHandler,“变成”一个实现A所实现的interface的新的对象,便于使用接口通用的方式进行调用。这里ABC都是一个新的对象了。

在最终的类C中,对于方法的调用,是一次调用的分发,实质都是通过InvocationHandler的invoke来调用的。

从原理可以看出,JDK动态代理是“对象”的代理。

CGLIB动态代理

JDK动态代理的方法是持有被代理的类,但是代理的方法并不是只能这样实现。CGLIB的思想是,通过“继承”可以继承父类所有的公开方法,然后可以重写这些方法,在重写时对这些方法增强。

import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;public class CglibProxy implements MethodInterceptor
{// 根据一个类型产生代理类,此方法不要求一定放在MethodInterceptor中public Object CreatProxyedObj(Class<?> clazz){Enhancer enhancer = new Enhancer();enhancer.setSuperclass(clazz);enhancer.setCallback(this);return enhancer.create();}@Overridepublic Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable{// 这里是扩展内容System.out.println("开始");return arg3.invokeSuper(arg0, arg2);}
}

CGLIB中只需要一个class就可以产生一个代理对象,不像jdk动态代理需要根据一个实现了接口的对象来生成一个代理对象。所以CGLIB可以理解为是“类”的代理。

cglib最终生成一个继承B(被代理的类型)的类型C(代理类),这个代理类持有一个MethodInterceptor,enhancer.setCallback(this);传入。 C重写所有B中的方法(方法名一致),然后在C中,构建名叫“CGLIB”+“ 父 类 方 法 名 父类方法名 父类方法名”的方法(下面叫cglib方法,所有非private的方法都会被构建),方法体里只有一句话super.方法名(),可以简单的认为保持了对父类方法的一个引用,方便调用。

这样的话,C中就有了重写方法、cglib方法、父类方法(不可见),还有一个统一的拦截方法(增强方法intercept)。其中重写方法和cglib方法肯定是有映射关系的。

C的重写方法是外界调用的入口(LSP原则),它调用MethodInterceptor的intercept方法,调用时会传递四个参数,第一个参数传递的是this,代表代理类本身,第二个参数标示拦截的方法,第三个参数是入参,第四个参数是cglib方法,intercept方法完成增强后,我们调用cglib方法间接调用父类方法完成整个方法链的调用。

这里有个疑问就是intercept的四个参数,为什么我们使用的是arg3而不是arg1?

    @Overridepublic Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable{System.out.println("开始");return arg3.invokeSuper(arg0, arg2);}

因为如果我们通过反射 arg1.invoke(arg0, …)这种方式是无法调用到父类的方法的,子类有方法重写,隐藏了父类的方法,父类的方法已经不可见,如果硬调arg1.invoke(arg0, …)很明显会死循环。

所以调用的是cglib开头的方法,但是,我们使用arg3也不是简单的invoke,而是用的invokeSuper方法,这是因为cglib采用了fastclass机制,不仅巧妙的避开了调不到父类方法的问题,还加速了方法的调用。

fastclass基本原理是,给每个方法编号,通过编号找到方法执行避免了通过反射调用。

对比JDK动态代理,cglib依然需要一个第三者分发请求,只不过jdk动态代理分发给了目标对象,cglib最终分发给了自己,通过给method编号完成调用。cglib是继承的极致发挥,本身还是很简单的,只是fastclass需要另行理解。

此博文为记录+思考。原博文地址:地址

java 动态代理相关记录相关推荐

  1. JAVA 动态代理学习记录

    打算用JAVA实现一个简单的RPC框架,看完RPC参考代码之后,感觉RPC的实现主要用到了两个方面的JAVA知识:网络通信和动态代理.因此,先补补动态代理的知识.---多看看代码中写的注释 参考:Ja ...

  2. Java动态代理的实现

    动态代理作为代理模式的一种扩展形式,广泛应用于框架(尤其是基于AOP的框架)的设计与开发,本文将通过实例来讲解Java动态代理的实现过程. 友情提示:本文略有难度,读者需具备代理模式相关基础知识,. ...

  3. Java 动态代理机制分析及扩展--转

    http://www.ibm.com/developerworks/cn/java/j-lo-proxy1/#icomments http://www.ibm.com/developerworks/c ...

  4. 彻底理解JAVA动态代理

    代理设计模式 定义:为其他对象提供一种代理以控制对这个对象的访问. 代理模式的结构如下图所示. 动态代理使用 java动态代理机制以巧妙的方式实现了代理模式的设计理念. 代理模式示例代码 public ...

  5. Java 动态代理机制分析及扩展,第 1 部分

    引言 Java 动态代理机制的出现,使得 Java 开发人员不用手工编写代理类,只要简单地指定一组接口及委托类对象,便能动态地获得代理类.代理类会负责将所有的方法调用分派到委托对象上反射执行,在分派执 ...

  6. Java 动态代理机制分析及扩展

    简介: 本文通过分析 Java 动态代理的机制和特点,解读动态代理类的源代码,并且模拟推演了动态代理类的可能实现,向读者阐述了一个完整的 Java 动态代理运作过程,希望能帮助读者加深对 Java 动 ...

  7. Java 动态代理及 RPC 框架介绍

    所谓动态代理,指的是语言提供的一种语法,能够将对对象中不同方法的调用重定向到一个统一的处理函数中来. python重写__getattr__函数能够做到这一点,就连世界上最好的语言也提供称为魔术方法的 ...

  8. java动态代理原理及解析

    转载自 http://blog.csdn.net/scplove/article/details/52451899 代理:设计模式 代理模式是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制 ...

  9. Java动态代理全面分析

    代理模式 解说:给某一个对象提供一个代理,并由代理对象控制对原对象的引用: 代理模式需要以下几个角色: 1  主题:规定代理类和真实对象共同对外暴露的接口: 2  代理类:专门代理真实对象的类: 3 ...

最新文章

  1. c语言输入不为空时循环,scanf循环输入的时候第二个数据输入有问题,相当于是多出来的...
  2. 二十七、Node.js搭建第一个Express应用框架
  3. Python中为什么推荐使用isinstance来进行类型判断?而不是type
  4. 阿里云胡晓明:数据智能将为城市生活带来真正价值
  5. Java中string.equalsIgnoreCase(0)与0.equalsIgnoreCase(string)的区别:
  6. DPDK如何释放大页内存(巨页内存hugepage)
  7. Android通话记录CallLog简单摘要
  8. xp系统怎么看计算机内存条,XP环境下怎么查看虚拟内存?XP系统虚拟内存过低怎么设置?...
  9. python批量转换音频格式,flac转mp3等
  10. 字符串转换成UTF8编码
  11. 【Elasticsearch】利用kibana调整索引mapping结构
  12. Picasso加载圆角图片
  13. ibox (bootstrap)
  14. I.MX6ULL开发板基于阿里云项目实战 3 :阿里云iot-SDK 移植到arm开发板
  15. 连续函数:函数在区间上连续
  16. 近10年明星意外死亡全记录
  17. 全国计算机一级报名科目怎么选,计算机一级考试4个科目是四选一吗
  18. Multisim14彻底卸载清除/NI Error Reporting Server
  19. mysql筛选英文字符_MySQL:字符串中的数字、英文字符、汉字提取
  20. 基于qt+halcon实现视觉定位模板匹配【附部分源码】

热门文章

  1. tilemap 菱形_Tilemap
  2. 深耕“有效私域”,雀巢集团携手腾讯重塑零售数字化体验
  3. tig只看某个作者的提交
  4. Kile5安装教程和创建一个工程举例【图文STM32F407ZE芯片为例】
  5. Python random模块(获取随机数)常用方法和使用例子
  6. 服务器系统做个备份吗,服务器操作系统能做备份吗
  7. Gluster分布式文件系统
  8. stm32f429之多通道ADC通过DMA数据采集
  9. 设置CFLAGS后,编译无效
  10. Kafka Consumer的两种接口(高阶低阶)