动态代理之Cglib浅析

什么是Cglib

Cglib是一个强大的,高性能,高质量的代码生成类库。它可以在运行期扩展JAVA类与实现JAVA接口。其底层实现是通过ASM字节码处理框架来转换字节码并生成新的类。大部分功能实际上是ASM所提供的,Cglib只是封装了ASM,简化了ASM操作,实现了运行期生成新的class。

Cglib的原理

运行时动态的生成一个被代理类的子类(通过ASM字节码处理框架实现),子类重写了被代理类中所有非final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势植入横切逻辑。

Cglib优缺点

优点:JDK动态代理要求被代理的类必须实现接口,当需要代理的类没有实现接口时Cglib代理是一个很好的选择。另一个优点是Cglib动态代理比使用java反射的JDK动态代理要快(Cglib的FastClass机制,解析参考http://www.cnblogs.com/cruze/p/3865180.html#lable3)

缺点:对于被代理类中的final方法,无法进行代理,因为子类中无法重写final函数

Cglib类库

net.sf.cglib.core:底层字节码处理类,他们大部分与ASM有关系。

net.sf.cglib.transform:编译期或运行期类和类文件的转换

net.sf.cglib.proxy:实现创建代理和方法拦截器的类

net.sf.cglib.reflect:实现快速反射和C#风格代理的类

net.sf.cglib.util:集合排序等工具类

net.sf.cglib.beans:JavaBean相关的工具类

最主要的一个接口CallBack接口,即回调接口,下面的拦截器、过滤器、延迟加载都继承于它,结构图如下:

 

Cglib的拦截器和过滤器

拦截器:实现MethodInterceptor接口的类,在intercept方法中实现对代理目标类的方法拦截。但同时Cglib为简化和提高性能提供了一些专门的回调类型如FixedValue(可以在实现的方法loadObject中指定返回固定值,而不调用目标类函数)、NoOp(把对回调方法的调用直接委派到这个方法的父类,即不进行拦截)

过滤器:实现CallbackFilter接口的类,通过accept方法返回一个下标值,用于指定调用哪个拦截器进行拦截处理

/*** @author longe*    被代理类*/
public class TDao {public void create() {System.out.println("create() is running !");}public void query() {System.out.println("query() is running !");}public void update() {System.out.println("update() is running !");}public void delete() {System.out.println("delete() is running !");}
}/*** @author longe*    过滤器*/
public class TDaoCglibFilter implements CallbackFilter {public final static int NO_RESTRICTION = 0;public final static int RESTRICTION_CREATE = 1;/* (non-Javadoc)*    根据调用的方法返回使用的callbacks下标*/@Overridepublic int accept(Method method) {if(method.getName().startsWith("create")){return RESTRICTION_CREATE;}return NO_RESTRICTION;}
}/*** @author longe* 拦截器*/
public class TDaoCglibProxy implements MethodInterceptor {private String name;private Object objT;public TDaoCglibProxy(String name) {this.name = name;}public TDaoCglibProxy(Object obj,String name) {this.name = name;this.objT = obj;}@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {if(!name.equals("张三")){System.out.println("没有权限!");return null;}
//        return method.invoke(objT, args);    //通过反射进行调用return proxy.invokeSuper(obj, args);    //使用Cglib代理调用
    }public static void main(String[] args) {Enhancer en = new Enhancer();en.setSuperclass(TDao.class);TDaoCglibProxy callBack = new TDaoCglibProxy("张三1");
//        指定两个callback,通过callBackFilter返回的下标值控制调用使用那一个callback处理en.setCallbacks(new Callback[] { NoOp.INSTANCE, callBack });
//        方法过滤器,根据过滤器返回不同的值回调对应下标的Callback方法en.setCallbackFilter(new TDaoCglibFilter());TDao dao = (TDao) en.create();dao.create();dao.query();dao.update();dao.delete();}}
代码示例
执行输出如下:没有权限!
query() is running !
update() is running !
delete() is running !

可以看到只用create方法的调用被TDaoCglibProxy拦截器拦截了,从而判断出没有权限。而其他方法没有被TDaoCglibProxy拦截器拦截,是因为在Filter过滤器中返回的索引为0,即指定的回调callback是NoOp,索引直接调用了父类的实现方法(即被代理类的实现)

Cglib的延迟加载

LazyLoader:当实际对象需要延迟加载时,可以使用LazyLoader回调。一旦实际对象被装载,它将被每一个调用代理对象的方法使用(即实际对象被访问时才调用此回调callback的loadObject方法)

Dispatcher:和LazyLoader回调具有相同的特点,区别是当代理方法被调用时,装载对象的方法也总是要被调用

ProxyRefDispatcher:与Dispatcher一样,只不过可以把代理对象作为参数进行传递

public class CglibLazyTest extends TestCase {public void testLazyBean() {TB tb = new TB();System.out.println("***************************************");System.out.println("cid's value: " + tb.getCid());System.out.println("***************************************");System.out.println("bean's username value: " + tb.getBean());
//        LazyLoader 这里将返回同一个对象System.out.println("bean's username value: " + tb.getBean());System.out.println("***************************************");System.out.println("bean2's username value: " + tb.getBean2());
//        这里将返回一个新的对象System.out.println("bean2's username value: " + tb.getBean2());}/*** @author longe*    通过实现LazyLoader接口延迟加载*/class Lazy implements LazyLoader {@Overridepublic Object loadObject() throws Exception {TestBean tb = new TestBean();System.out.println("lazy load ========= ");tb.setUserName("longe lazy");return tb;}}/*** @author longe*    通过实现Dispatcher接口延迟加载*/class DispacherTest implements Dispatcher {@Overridepublic Object loadObject() throws Exception {TestBean2 tb2 = new TestBean2();System.out.println("dispatcher load =========== ");tb2.setNo("longe dispatcher lazy");return tb2;}}/*** @author longe*    与Dispatcher一样,只不过可以把代理对象作为参数进行传递*/class ProxyRefDispacherTest implements ProxyRefDispatcher {@Overridepublic Object loadObject(Object proxy) throws Exception {return null;}}class TB {private String cid;private TestBean bean;private TestBean2 bean2;LazyLoader lazy = new Lazy();Dispatcher dis = new DispacherTest();public TB() {cid = "no lazy";// Enhancer en = new Enhancer();// en.setSuperclass(TestBean.class);// en.setCallback(lazy);// bean = (TestBean) en.create(new Class[]{CglibLazyTest.class},new// Object[]{new CglibLazyTest()});bean = (TestBean) Enhancer.create(TestBean.class, lazy);bean2 = (TestBean2) Enhancer.create(TestBean2.class, dis);System.out.println("TB construct end...");}public TestBean getBean() {return bean;}public String getCid() {return cid;}public void setCid(String cid) {this.cid = cid;}public void setBean(TestBean bean) {this.bean = bean;}public TestBean2 getBean2() {return bean2;}public void setBean2(TestBean2 bean2) {this.bean2 = bean2;}}}class TestBean {public TestBean() {System.out.println("TestBean construct end...");}private String userName;public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;}
}class TestBean2 {public TestBean2() {System.out.println("TestBean2 construct end...");}private String no;public String getNo() {return no;}public void setNo(String no) {this.no = no;}
}
代码示例
执行输出如下:

TestBean construct end...
TestBean2 construct end...
TB construct end...
***************************************
cid's value: no lazy
***************************************
TestBean construct end...
lazy load =========
bean's username value: practices.model.proxy.cglib.TestBean@77521871
bean's username value: practices.model.proxy.cglib.TestBean@77521871
***************************************
TestBean2 construct end...
dispatcher load ===========
bean2's username value: practices.model.proxy.cglib.TestBean2@2ec2dfea
TestBean2 construct end...
dispatcher load ===========
bean2's username value: practices.model.proxy.cglib.TestBean2@7bfa93a1

上面的示例中TestBean对象使用了LazyLoader回调,TestBean2对象使用了Dispatcher回调。

分析:

  1. 在TB的构造函数中创建了TestBean和TestBean2对象,此时这两个对象分别被创建,但是是空对象。
  2. 当tb.getBean().getUserName()第一次访问TestBean对象时,此时LazyLoader回调被调用,TestBean装载并进行初始化赋值,最终返回装载的新对象。
  3. 当tb.getBean().getUserName()第二次访问TestBean对象时,TestBean已经被装载过,此时LazyLoader回调不被调用。
  4. 当tb.getBean2().getNo()第一次访问TestBean2对象时,此时Dispatcher回调被调用,TestBean2装载并进行初始化赋值,最终返回装载的新对象。
  5. 当tb.getBean2().getNo()第二次访问TestBean2对象时,此时Dispatcher回调还是会被调用,TestBean2装载并进行初始化赋值,最终返回一个新装载的新对象

Cglib中的Mixin

net.sf.cglib.proxy.Mixin允许多个对象被绑定到一个单个的大对象。在代理中对方法的调用委托到下面对应的对象中。

通过指定代理类型和实际的代理对象参数即可直接创建代理。可以绑定多个,但需要注意类型数组与对象数组的一一对应。如:

public static void testCglibMixin() {System.out.println(MixinIfA.class.getName());Class[] ints = new Class[]{MixinIfA.class,MixinIfB.class};Object[] objs = new Object[]{new MixinAImpl(),new MixinBImpl()};Object obj = Mixin.create(ints,    objs);MixinIfA a = (MixinIfA) obj;MixinIfB b = (MixinIfB) obj;a.methodA();b.methodB();}

 public static Mixin create(Class[] interfaces, Object[] delegates)中第一个参数必须是接口类型,第二个参数是接口实现类对象实例

OK,到这Cglib代理了解的差不多了,从Cglib的动态代理知道它可以动态的生成java类,那么这里顺带贴上一段简单的Cglib生成bean的代码.(供后续复习用...)

/*** @author longe*    动态生成bean*/
public class CglibBean {public Object object;public BeanMap beanMap;public CglibBean() {}public CglibBean(Map<String, Class> propertyMap) {this.object = generateBean(propertyMap);this.beanMap = BeanMap.create(this.object);}public void putValue(String property, Object value) {beanMap.put(property, value);}public Object getValue(String property) {return beanMap.get(property);}public Object getObject() {return this.object;}public Object generateBean(Map<String, Class> propertyMap) {BeanGenerator generator = new BeanGenerator();Set<String> keySet = propertyMap.keySet();for (String key : keySet) {generator.addProperty(key, propertyMap.get(key));}return generator.create();}public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {Map<String, Class> propertyMap = new HashMap<>();propertyMap.put("id", Class.forName("java.lang.Integer"));propertyMap.put("name", Class.forName("java.lang.String"));propertyMap.put("address", Class.forName("java.lang.String"));CglibBean cglibBean = new CglibBean(propertyMap);cglibBean.putValue("id", 101);cglibBean.putValue("name", "longe");cglibBean.putValue("address", "beijing");System.out.println(cglibBean.getValue("id"));System.out.println(cglibBean.getValue("name"));System.out.println(cglibBean.getValue("address"));Object object = cglibBean.getObject();Method[] ms = object.getClass().getDeclaredMethods();for (Method method : ms) {System.out.println(method.getName());if (method.getName().startsWith("get")) {System.out.println("=====" + method.invoke(object, null));}}}
}
View Code

Cglib异常

java.lang.IllegalArgumentException: Superclass has no null constructors but no arguments were given
at net.sf.cglib.proxy.Enhancer.emitConstructors(Enhancer.java:721)
at net.sf.cglib.proxy.Enhancer.generateClass(Enhancer.java:499)
at net.sf.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:216)
at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:377)
at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:285)
at practices.model.proxy.cglib.CglibLazyTest.testLazyBean1(CglibLazyTest.java:21)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at junit.framework.TestCase.runTest(TestCase.java:176)
at junit.framework.TestCase.runBare(TestCase.java:141)
at junit.framework.TestResult$1.protect(TestResult.java:122)
at junit.framework.TestResult.runProtected(TestResult.java:142)
at junit.framework.TestResult.run(TestResult.java:125)
at junit.framework.TestCase.run(TestCase.java:129)
at junit.framework.TestSuite.runTest(TestSuite.java:252)
at junit.framework.TestSuite.run(TestSuite.java:247)
at org.junit.internal.runners.JUnit38ClassRunner.run(JUnit38ClassRunner.java:86)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)

原因:

  • 代理目标对象不能是内部类(因为内部类的创建依赖外部类),如果是内部类,cglib代理内部会获取到一个有参构造函数(参数是外部类对象,如果实在需要代理一个内部类,可以通过传递构造参数实现)
  • Cglib代理默认创建一个缺省构造函数的目标对象,如果目标对象存在有参构造函数,Cglib进行代理时需要指定构造函数的参数,或者在目标对象上必须存在缺省构造函数,否则抛出异常(可以通过传递构造参数创建代理类)

转载于:https://www.cnblogs.com/mr-long/p/5889054.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/256348.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

机器学习——深度学习之卷积神经网络(CNN)——AlexNet卷积神经网络结构

目录 一、AlexNet卷积神经网络结构模型 1、数据库ImageNet 2、AlexNet第一层卷积层 二、AlexNet卷积神经网络的改进 1、非线性变化函数的改变——ReLU 2、最大池化&#xff08;Max Pooling&#xff09;概念的提出——卷积神经网络通用 1&#xff09;池化层 2&#xff0…

C#委托——基础2

在上一篇随笔中&#xff0c;简要说明了怎样定义委托&#xff0c;定义事件&#xff0c;订阅事件&#xff0c;最后也实现了效果&#xff0c;就是当员工类的某个对象&#xff0c;执行某个事件时&#xff0c;委托事件被触发&#xff0c;后面也得到了结果&#xff0c;但是想象一下实…

机器学习——深度学习之编程工具、流行网络结构、卷积神经网络结构的应用

目录 一、编程工具 caffe实现LENET-5 二、流行的网络结构 1、VGGNET 2、Googlenet ​ 3、ResNet​ ​ 三、卷积神经网络的应用 1、人脸识别 ​ 2、人脸验证 3、人脸特征点检测 4、卷积神经网络压缩 一、编程工具 caffe的优点&#xff1a;模型标准化&#xff0c;源代码…

Halcon例程详解(激光三角系统标定)—— calibrate_sheet_of_light_calplate.hdev

前言 1 激光三角测距 激光三角测距法原理很简单,是通过一束激光以一定的入射角度照射被测目标,激光在目标表面会产生漫反射,在另一角度利用透镜对反射激光汇聚成像,光斑成像在CCD(Charge-coupled Device,感光耦合组件)位置传感器上。当被测物体沿激光方向发生移动时,…

【tenserflow】——数据类型以及常用属性

目录 一、什么是Tensor&#xff1f; 二、Tensorflow常见数据类型 三、Tensorflow常见属性device\cpu\gpu\ndim\shape\rank等 1、创建一个tensor 1&#xff09;tf.constant() 2)tf.Variable() 2、判断一个变量是否为tensor张量 3、生成不同设备&#xff08;cpu,gpu&#x…

网页开发浏览器兼容性问题

1、在ie6下的双margin问题 在ie6下&#xff0c;设置了float的元素&#xff0c;以float:left为例&#xff0c;如图所示。会出现第一个浮动元素&#xff0c;即相对于父级元素浮动的&#xff0c;会出现双倍margin的问题。 注意仅仅是相对于父级元素浮动的&#xff0c;即第一个会出…

【tensorflow】——创建tensor的方法

目录 1、tf.constant() 2、tf.Variable() 3、tf.zeros():用0去填充指定形状的数组 4、tf.convert_to_tensor(a,dtypetf.int32) 5、tf.ones():用1去填充指定形状的数组 6、tf.fill():用指定的元素去填充指定形状的数组 7、随机化初始化进行创建 1&#xff09;normal正态分…

Halcon —— 图像像素类型与转换

图像类型 就目前工业领域主流的图像处理工具halcon来讲&#xff0c;有以下几种图像类型&#xff1a;‘byte’, ‘complex’, ‘cyclic’, ‘direction’, ‘int1’, ‘int2’, ‘int4’, ‘int8’, ‘real’, ‘uint2’&#xff0c;具体含义如下图所示。 ‘byte’ 每像素1字节…

Halcon例程详解(基于卡尺工具的匹配测量方法) —— measure_stamping_part.hdev

前言 1卡尺工具介绍 Halcon中的Metrology方法即为卡尺工具&#xff0c;可用来拟合线&#xff0c;圆&#xff0c;这种方法对于目标比背景很明显的图像尺寸测量是很方便的&#xff0c;不需要用blob进行边缘提取等&#xff0c;但缺点也很明显&#xff0c;需要目标的相对位置基本…

【TensorFlow】——不同shape的tensor在神经网络中的应用(scalar,vector,matrix)

目录 ​ 1、scalar——标量 1&#xff09;在神经网络中存在的场景 2&#xff09;one_hot编码 3&#xff09;举例应用 2、vector——向量 ​ 3、matrixs——矩阵 4、dim3的tensor 5、dim4的tensor 6、dim5的tensor 本文主要的目的是让初学者对tensor的各种形式的使用场…

Java - I/O

File类 java.io操作文件和目录&#xff0c;与平台无关。具体的常用实例方法&#xff1a; File file new File("."); // 以当前路径创建名为 "." 的 File 对象   文件目录信息函数     -   String getName/Path/Parent()&#xff1a; 文件名/路径…

Halcon —— 边缘检测算子详解

一、算子介绍 1.1 种类 halcon内常用的边缘检测算子包括如下几种&#xff1a; 1.edges_image: 提取2D 图像边缘 2.edges_sub_pix&#xff1a;提取2D图像亚像素边缘 3.edges_object_model_3d &#xff1a;提取3D图像边缘 4.edges_color和edges_color_sub_pix&#xff1a;提取彩…

【TensorFlow】——索引与切片

目录 1、利用index进行索引 2、利用“&#xff1a;”和“...”进行索引与切片 3、tf.gather&#xff08;&#xff09;——对一个维度进行乱序索引 优势&#xff1a; 缺点&#xff1a; 例子 4、tf.gather_nd()——同时对多个维度进行索引 5、tf.boolean_mask()——通过布…

华硕(ASUS)X554LP笔记本一开机就进入aptio setup utility 问题的解决

某次因大意一直未插电&#xff0c;华硕&#xff08;ASUS&#xff09;X554LP笔记本后来没电关机。后来每次一开机就进入aptio setup utility界面&#xff0c;按F9调入默认配置&#xff0c;F10保存后退出&#xff0c;重启仍然进入aptio setup utility。 网上查了一下&#xff0c;…

【TensorFlow】——broadcast_to(在不复制内存的情况下自动扩张tensor)

目录 作用&#xff1a; 内在的思路 优点 什么时候可以broadcast ​ tf.boradcast_to .VS tf.tile 作用&#xff1a; 在不会实际意义上复制数据的情况下进行tensor的维度和形状的扩张&#xff0c;使得两个tensor维度和形状一致 对两个维度不一致的tensor进行加减前进行br…

20145212 《信息安全系统设计基础》第2周学习总结

20145212 《信息安全系统设计基础》第2周学习总结 教材学习内容总结 Vim基本操作 1.使用vim命令进入vim界面vim后面加上你要打开的已存在的文件名或者不存在&#xff08;则作为新建文件&#xff09;的文件名。 打开Xfce终端&#xff0c;输入以下命令$ vim practice_1.txt 直接使…

Opencv—— 拟合直线

概念 最小二乘法是勒让德( A. M. Legendre)于1805年在其著作《计算慧星轨道的新方法》中提出的。 最小二乘法就是通过最小化误差的平方和&#xff0c;使得拟合对象无限接近目标对象。在图像处理中主要用于拟合线&#xff0c;通过求采样点距离误差最小的线&#xff0c;可以是直…

本地搭建Dubbo监控中心的安装步骤

Dubbo监控中心的安装步骤 参考链接&#xff1a;http://blog.csdn.net/lichunan/article/details/40349645 一、从github上下载dubbo源码进行编译&#xff1a; 1、下载地址为&#xff1a; https://github.com/alibaba/dubbo.git2、编译源码命令&#xff1a;首先进入~/dubbo&am…

【TensorFlow】——实现minist数据集分类的前向传播(常规神经网络非卷积神经网络)

目录 一、常规神经网络模型 二、TensorFlow实现前向传播步骤 1、读取数据集 2、batch划分 3、根据神经网络每一层的神经元个数来初始化参数w,b 4、进行每一层输入输出的计算 5、对每一层的输出进行非线性relu函数变换 6、计算一个batch训练后的误差loss 7、计算每一次…

神经网络(11)--具体实现:unrolling parameters

我们需要将parameters从矩阵unrolling到向量&#xff0c;这样我们就可以使用adanced optimization routines. unroll into vectors costFunction与fminunc里面的theta都是n1维的向量&#xff0c;costFunction的返回值gradient也是n1维的向量。 但是当我们使用神经网络时&#x…