简介

cglib是一套java动态代理实现框架,cglib被应用到spring app,hibernate等高级业务框架,spring事务在业务实现类未实现接口的情况下也会使用该技术。

实际上,cglib基于继承实现,这也就意味着final,private相关的method无法被代理。基于asm框架对class字节码编辑改动,从而达到动态代理的目的,总之,被代理类没有实现接口的情况下cglib为首选

引用官网介绍的一段话:

Byte Code Generation Library is high level API to generate and transform JAVA byte code. It is used by AOP, testing, data access frameworks to generate dynamic proxy objects and intercept field access.

github地址:https://github.com/cglib/cglib

cglib作者博客:http://mydailyjava.blogspot.no/2013/11/cglib-missing-manual.html

以下案例在我的github中可以clone到,分支为howtocglib

github路径:https://github.com/zhang6622056/cglib

测试用例:cglib-sample/src/main/test/callback/Callbacktest

实现动态代理的几种方案

  • javasisit
  • jdk 动态代理
  • cglib
  • asm(cglib 内部也是用asm更改其字节码)

callbacks简介

这里的callback可以认为是cglib用于生成字节码的实现手段,cglib一共实现了6种callback,用于对代理类目标进行不同手段的代理,非常灵活,分别为:

  • FixedValue
  • InvocationHandler
  • LazyLoader
  • MethodInterceptor
  • Dispatcher
  • NoOp

编写实现案例

callbacks代码编写


Dispatcher

实现Dispatcher接口,要求实现loadObject方法,返回期望的代理类。值的一提的是,loadobject方法在每次调用被拦截方法的时候都会被调用一次

/***** 与lazy不同的是,每一次调用代理方法的时候,都会调用一次Dispatcher的loadObject获取对象* 而lazy则会缓存下来。*/
public class DispatcherCallBack implements Dispatcher {public Object loadObject() throws Exception {CallbackBean callbackBean = new CallbackBean();return callbackBean;}
}

FixedValue

实现FixedValue接口,该callback同样要求实现一个loadobject方法,只不过需要注意的是该loadobject方法相同与重写了被代理类的相应方法,因为在被代理之后,FixedValue callback只会调用loadobject,而不会再调用代理目标类的相应方法!

/****** 该callback相当于重写了相应的函数实现。并不会调用原函数*/
public class FixValueCallback implements FixedValue {/****** 被代理方法的指定函数将会无条件的返回改object,动态的变更返回值* @return* @throws Exception*/public Object loadObject() throws Exception {System.out.println("this is fixvalue callback .....    overwrite the code....");return true;}
}

InvocationHandler

需要实现InvocationHandler接口,实现invoke对象,该拦截传入了proxy对象,用于自定义实现,与MethodInterceptor相似,慎用method的invoke方法。切忌不要造成循环调用

public class InvocationHandlerCallback implements InvocationHandler {/****** invocationHandler的invoke方法传入的method和proxy都是代理本身对象* 切忌重复调用,会循环调用* @param proxy 代理类本身* @param method 代理类内部的方法* @param args  参数* @return* @throws Throwable*/public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("invocationHandlerCallback Before....");method.invoke(proxy.getClass().getSuperclass().newInstance(),args);//会无限循环//method.invoke(proxy,args);System.out.println("invocationHandlerCallback after....");return null;}
}

LazyLoader

实现LazyLoader的loadObject方法,返回对象实例,该实例只有第一次调用的时候进行初始化,之后不再重新调用,proxy类初始化时进行了成员的赋值,之后使用该成员进行调用父类方法

/****** 延迟加载初始化* 类似于spring prototype的singleton ,在第一次调用的时候进行初始化,并且将此实例存储起来,之后都将返回改实例* 可参考资料:* https://shensy.iteye.com/blog/1881277*/
public class LazyLoaderCallback implements LazyLoader {public Object loadObject() throws Exception {CallbackBean callbackBean = new CallbackBean();return callbackBean;}
}

MethodInterceptor

实现MethodInterceptor的intercept,实现被代理对象的逻辑植入。也是最常用的callback

/***** 生成代理类字节码。对方法进行拦截调用*/
public class MethodInterceptorCallback implements MethodInterceptor {public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("before invoke........");proxy.invokeSuper(obj,args);System.out.println("after invoke.........");return null;}
}

NoOp

通过接口声明了一个单例对象,该代理不对被代理类执行任何操作

/*** Methods using this {@link Enhancer} callback will delegate directly to the* default (super) implementation in the base class.*/
public interface NoOp extends Callback
{/*** A thread-safe singleton instance of the <code>NoOp</code> callback.*/public static final NoOp INSTANCE = new NoOp() { };
}

被代理类code


public class CallbackBean {public void methodForDispatcher(){System.out.println("methodForDispatcher...");}public void methodForFixValue(){System.out.println("methodForFixValue...");}public void methodForInvocationHandler(){System.out.println("methodForInvocationHandler...");}public void methodForLazy(){System.out.println("methodForLazy...");}public void methodForInterceptor(){System.out.println("methodForInterceptor...");}public void methodForNoop(){System.out.println("methodForNoop...");}
}

编写Enhancer调用


import callbacks.*;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.NoOp;
import org.junit.Test;
import proxybean.CallbackBean;
import java.lang.reflect.Method;public class CallbackTest {@Testpublic void testCallback(){Enhancer enhancer = new Enhancer();enhancer.setSuperclass(CallbackBean.class);enhancer.setCallbacks(initCallBacks());enhancer.setCallbackFilter(initCallbackFilter());CallbackBean callbackBean = (CallbackBean) enhancer.create();callbackBean.methodForNoop();callbackBean.methodForInterceptor();callbackBean.methodForLazy();callbackBean.methodForDispatcher();callbackBean.methodForInvocationHandler();callbackBean.methodForFixValue();}/***** 初始化callback拦截数组* @return*/private static final Callback[] initCallBacks(){MethodInterceptorCallback methodInterceptorCallback = new MethodInterceptorCallback();LazyLoaderCallback lazyLoaderCallback = new LazyLoaderCallback();InvocationHandlerCallback invocationHandlerCallback = new InvocationHandlerCallback();FixValueCallback fixValueCallback = new FixValueCallback();DispatcherCallBack dispatcherCallBack = new DispatcherCallBack();Callback[] callbacks = new Callback[]{NoOp.INSTANCE,methodInterceptorCallback,lazyLoaderCallback,dispatcherCallBack,invocationHandlerCallback,fixValueCallback};return callbacks;}private static final CallbackFilter initCallbackFilter(){return new CallbackFilter() {public int accept(Method method) {if (method.getName().equals("methodForNoop")){return 0;}if (method.getName().equals("methodForInterceptor")){return 1;}if (method.getName().equals("methodForLazy")){return 2;}if (method.getName().equals("methodForDispatcher")){return 3;}if (method.getName().equals("methodForInvocationHandler")){return 4;}if (method.getName().equals("methodForFixValue")){return 5;}return 0;}};}
}

proxy class反编译分析(请看关键注释)

Dispatcher

public final void methodForDispatcher() {Dispatcher var10000 = this.CGLIB$CALLBACK_3;if (var10000 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_3;}//每次都调用一次loadObject,获取对象,并调用对象的相应方法//这样的实现,相当于loadObject可以很灵活的返回相应的实现类或者子类((CallbackBean)var10000.loadObject()).methodForDispatcher();
}

FixedValue

public final void methodForFixValue() {FixedValue var10000 = this.CGLIB$CALLBACK_5;if (var10000 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_5;}//直接调用了FixValue callback的loadObject,相当于重写逻辑var10000.loadObject();
}

InvocationHandler

public final void methodForInvocationHandler() {try {InvocationHandler var10000 = this.CGLIB$CALLBACK_4;if (var10000 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_4;}//调用InvocationHandler相应的invoke实现var10000.invoke(this, CGLIB$methodForInvocationHandler$4, new Object[0]);} catch (Error | RuntimeException var1) {throw var1;} catch (Throwable var2) {throw new UndeclaredThrowableException(var2);}
}

LazyLoader

public final void methodForLazy() {//CGLIB$LOAD_PRIVATE_2在proxy初始化的时候初始化一次,并维护了该成员//与Dispatcher不同,每次调用都是运用proxy成员变量进行调用((CallbackBean)this.CGLIB$LOAD_PRIVATE_2()).methodForLazy();
}

MethodInterceptor

public final void methodForInterceptor() {MethodInterceptor var10000 = this.CGLIB$CALLBACK_1;if (var10000 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_1;}if (var10000 != null) {//调用callback的intercept方法,用于实现拦截var10000.intercept(this, CGLIB$methodForInterceptor$1$Method, CGLIB$emptyArgs, CGLIB$methodForInterceptor$1$Proxy);} else {super.methodForInterceptor();}
}

NoOp


此处不做NoOp的讨论,理解为调用原类方法即可

运行结果

反编译遇到问题

如jd-gui反编译遇到错误,请尝试直接将class文件拖进idea

总结

以上即为本篇的全部内容,主要简单介绍了cglib与几种callback的使用。比较简单、基础。

当然,cglib不只是这些内容。在实现的生成proxy的同时,作者抽象了很多东西,这包括

  • 缓存机制

  • 生成策略

  • 弱引用WeakHashMap,WeakReference

  • LoadingCache搭配FutureTask线程竞争模型

  • 对反射的封装

  • 对asm的封装

  • 类加载的ClassLoader

  • 灵活的Function设计

总之,先会用,我们在逐一深入,下一篇正式开始撸源码,敬请期待。

死磕cglib系列之一 cglib简介与callback解析相关推荐

  1. tomcat lifecyclelistener_大公司程序员带你死磕Tomcat系列(五)——容器

    死磕Tomcat系列(5)--容器 回顾 在死磕Tomcat系列(1)--整体架构中我们简单介绍了容器的概念,并且说了在容器中所有子容器的父接口是Container.在死磕Tomcat系列(2)--E ...

  2. 死磕shell系列-shell介绍

    写的比较好的博客推荐: 博主介绍:-纸短情长 死磕shell系列-shell介绍

  3. 死磕YOLO系列,YOLOv1 的大脑、躯干和手脚

    系列文章: 死磕YOLO系列,YOLOv2的自我修养 YOLO 是我非常喜欢的目标检测算法,堪称工业级的目标检测,能够达到实时的要求,它帮我解决了许多实际问题. 这就是 YOLO 的目标检测效果.它定 ...

  4. 什么?还在用delete删除数据《死磕MySQL系列 九》

    别再用delete删除数据 系列文章 一.表空间 二.数据删除流程 三.实践全表删除表文件大小不改变 四.如何正确的减少磁盘文件 五.实践是检验认识是否具有真理性的唯一标准 六.开发建议 七.总结 系 ...

  5. MySQL统计总数就用count,别花里胡哨的《死磕MySQL系列 十》

    有一个问题是这样的统计数据总数用count(*).count(主键ID).count(字段).count(1)那个效率高. 先说结论,不用那么花里胡哨遇到统计总数全部使用count(*). 但是有很多 ...

  6. 死磕数据库系列(二十六):MySQL 高可用之单主、双主模型组复制配置实践

    点关注公众号,回复"1024"获取2TB学习资源! 前面我们学习:MySQL 高可用之组复制(MGR)技术的相关原理知识,今天我将详细的为大家介绍 MySQL 高可用技术组复制的单 ...

  7. 死磕数据库系列(三十三):MySQL 性能分析与相关工具的使用

    点关注公众号,回复"1024"获取2TB学习资源! 今天我将详细的为大家介绍 MySQL 性能分析以及相关工具使用的相关知识,希望大家能够从中收获多多!如有帮助,请点在看.转发支持 ...

  8. S 锁与 X 锁的爱恨情仇《死磕MySQL系列 四》

    一网打尽MySQL的各种锁 系列文章 `获取MySQL各种学习资料可以联系咔咔` 前言 一.行锁 二.两阶段锁 三.理解死锁 四.优化你的代码尽量防止死锁 五.解释死锁的两种方案 六.如何解决热点数据 ...

  9. 死磕数据库系列(二十):MySQL 数据库 DDL、DML、DQL、DCL 语言理论与实践(sql 8.0 版)...

    点关注公众号,回复"1024"获取2TB学习资源! 今天,民工哥带大家一起来学习一下 MySQL 数据库的 DDL.DML.DQL.DCL 这几种语言的理论知识与实践.如有帮助,请 ...

  10. 死磕Tomcat系列(6)——Tomcat如何做到热加载和热部署的

    死磕Tomcat系列(6)--Tomcat如何做到热加载和热部署的 热部署就是在服务器运行时重新部署项目,热加载即在在运行时重新加载class,从而升级应用. 通常情况下在开发环境中我们使用的是热加载 ...

最新文章

  1. 基于可见光(LIFI)通信系统 的机动车智能辅助装置(课程设计)
  2. 使用Oracle创建图书馆数据库
  3. 8.Vue 事件处理
  4. 使用createObject(createObjectEx)创建silverlight对象
  5. linux7空闲内存,centos7 内存占用率高处理问题
  6. Struts2后台使用Request和Session方法
  7. jzoj3348. 【NOI2013模拟】秘密任务
  8. Java缓存技术-google guava cache
  9. 无法读源文件或磁盘_文件、文件夹、磁盘加密 -我们推荐这个便宜的解决方案!...
  10. 必修三计算机选修三知识点总结,高中生物必修三知识点总结
  11. Codeforces Round #734 (Div. 3)_B2. Wonderful Coloring - 2(贪心)
  12. 【正点原子Linux连载】第二十章 V4L2摄像头应用编程-摘自【正点原子】I.MX6U嵌入式Linux C应用编程指南V1.1
  13. JDK下载应该选择哪个版本?教你选择最好的JDK版本
  14. 脉脉热帖:数仓真的是太无聊了...
  15. 系统降级熔断限流和排队
  16. Acpi 和 apm
  17. 计算机辅助药物设计自学,《计算机辅助药物分子设计》教学大纲
  18. A First Course in Database Systems(数据库基础教程 第三版)课后答案——2.3.1\2.3.2\2.4.1
  19. 谈谈低延迟对音质的负面影响,顺便谈谈WASAPI
  20. 英国AI研究员揭开真相,中国人工智能为何能发展迅猛?

热门文章

  1. JavaWeb是什么?总结一下JavaWeb的体系
  2. Ira and Flamenco
  3. 多项式回归与模型泛化
  4. 朴素贝叶斯基本原理和预测过程、先验概率、后验概率、似然概率概念
  5. 查找算法1——顺序查找
  6. Splash的使用入门
  7. ShopNum1网店系统:组建电子商务运营团队
  8. iOS关闭自动锁屏和距离感应
  9. python IDLE 的调试器
  10. 刚开通微博。。。。WEBview设置