博客已迁移到CSDN《https://blog.csdn.net/qq_33375499

转载自   cglib之Enhancer

1. 背景

cglib库的Enhancer在Spring AOP中作为一种生成代理的方式被广泛使用。本文针对Enhancer的用法以实际代码为例作一些介绍。

2. Enhancer是啥

Enhancer是cglib中使用频率很高的一个类,它是一个字节码增强器,可以用来为无接口的类创建代理。它的功能与java自带的Proxy类挺相似的。它会根据某个给定的类创建子类,并且所有非final的方法都带有回调钩子。

2.1 Callback

那么Enhancer使用的Callback具体有哪些呢?下面介绍以下这几种Callback。在cglib中Callback是一个标记接口,Enhancer使用的回调就是cglib中Callback接口的子接口。

2.1.1 Callback-MethodInterceptor

方法拦截器。这个东西和JDK自带的InvocationHandler很类似

  1. Object intercept(Object obj, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable

这其中MethodProxy proxy参数一般是用来调用原来的对应方法的。比如可以proxy.invokeSuper(obj, args)。那么为什么不能像InvocationHandler那样用method来调用呢?因为如果用method调用会再次进入拦截器。为了避免这种情况,应该使用接口方法中第四个参数methodProxy调用invokeSuper方法。

  1. public class EnhancerTest {
  2.  
  3. public static void main(String[] args) {
  4. Enhancer enhancer = new Enhancer();
  5. enhancer.setSuperclass(Car.class);
  6. enhancer.setCallback(new MethodInterceptor() {
  7. @Override
  8. public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy)
  9. throws Throwable {
  10. System.out.println("before");
  11. Object res = methodProxy.invokeSuper(obj, args);
  12. System.out.println("after");
  13. return res;
  14. }
  15. });
  16. Car car = (Car) enhancer.create();
  17.  
  18. car.print();
  19. }
  20.  
  21. static class Car {
  22. void print() {
  23. System.out.println("I am a car");
  24. }
  25. }
  26.  
  27. }

上面的程序会打印:

before
I am a car
after

2.1.2 Callback-NoOp

这个回调相当简单,就是啥都不干的意思。

Callback-LazyLoader

LazyLoader是cglib用于实现懒加载的callback。当被增强bean的方法初次被调用时,会触发回调,之后每次再进行方法调用都是对LazyLoader第一次返回的bean调用。

  1. public class EnhancerTest {
  2. public static void main(String[] args) {
  3. CarFactory factory = new CarFactory();
  4. System.out.println("factory built");
  5. System.out.println(factory.car.getName());
  6. System.out.println(factory.car.getName());
  7. }
  8. static class Car {
  9. String name;
  10. Car() {
  11. }
  12. String getName() {
  13. return name;
  14. }
  1. }
  2.  
  3. static class CarFactory {
  4. Car car;
  5.  
  6. CarFactory() {
  7. car = carLazyProxy();
  8. }
  9.  
  10. Car carLazyProxy() {
  11. Enhancer enhancer = new Enhancer();
  12. enhancer.setSuperclass(Car.class);
  13. enhancer.setCallback(new LazyLoader() {
  14. @Override
  15. public Object loadObject() throws Exception {
  16. System.out.println("prepare loading");
  17. Car car = new Car();
  18. car.name = "this is a car";
  19. System.out.println("after loading");
  20. return car;
  21. }
  22. });
  23. return ((Car) enhancer.create());
  24. }
  25. }
  26. }
  1. 上面的程序打印情况如下:

factory built
prepare loading
after loading
this is a car
this is a car

2.1.3 Callback-Dispatcher

Dispatcher和LazyLoader作用很相似,区别是用Dispatcher的话每次对增强bean进行方法调用都会触发回调。

  1. public class EnhancerTest {
  2. public static void main(String[] args) {
  3. CarFactory factory = new CarFactory();
  4. System.out.println("factory built");
  5. System.out.println(factory.car.getName());
  6. System.out.println(factory.car.getName());
  7. }
  8. static class Car {
  9. String name;
  10. Car() {
  11. }
  12. String getName() {
  13. return name;
  14. }
  1. }
  2.  
  3. static class CarFactory {
  4. Car car;
  5.  
  6. CarFactory() {
  7. car = carLazyProxy();
  8. }
  9.  
  10. Car carLazyProxy() {
  11. Enhancer enhancer = new Enhancer();
  12. enhancer.setSuperclass(Car.class);
  13. enhancer.setCallback(new Dispatcher() {
  14. @Override
  15. public Object loadObject() throws Exception {
  16. System.out.println("prepare loading");
  17. Car car = new Car();
  18. car.name = "this is a car";
  19. System.out.println("after loading");
  20. return car;
  21. }
  22. });
  23. return ((Car) enhancer.create());
  24. }
  25. }
  26. }
  1. 程序会打印:

factory built
prepare loading
after loading
this is a car
prepare loading
after loading
this is a car

2.1.4 Callback-InvocationHandler

cglib的InvocationHandler和JDK自带的InvocationHandler作用基本相同。使用的时候要注意,如果对参数中的method再次调用,会重复进入InvocationHandler。

  1. public class EnhancerTest {
  2.  
  3. public static void main(String[] args) {
  4. Enhancer enhancer = new Enhancer();
  5. enhancer.setSuperclass(Car.class);
  6. enhancer.setCallback(new InvocationHandler() {
  7. @Override
  8. public Object invoke(Object obj, Method method, Object[] args) throws Throwable {
  9. if (method.getReturnType() == void.class) {
  10. System.out.println("hack");
  11. }
  12. return null;
  13. }
  14. });
  15. Car car = (Car) enhancer.create();
  16. car.print();
  17. }
  18.  
  19. static class Car {
  20. void print() {
  21. System.out.println("I am a car");
  22. }
  23. }
  24. }

上面的程序会打印:

hack

2.1.5 Callback-FixedValue

FixedValue一般用于替换方法的返回值为回调方法的返回值,但必须保证返回类型是兼容的,否则会出转换异常。

  1. public class EnhancerTest {
  2. public static void main(String[] args) {
  3. Enhancer enhancer = new Enhancer();
  4. enhancer.setSuperclass(Car.class);
  5. enhancer.setCallback(new FixedValue() {
  6. @Override
  7. public Object loadObject() throws Exception {
  8. return "hack!";
  9. }
  10. });
  11.  
  12. Car car = (Car) enhancer.create();
  13. System.out.println(car.print1());
  14. System.out.println(car.print2());
  15. }
  16.  
  17. static class Car {
  18. String print1() {
  19. return "car1";
  20. }
  21. String print2() {
  22. return "car2";
  23. }
  24. }
  25. }

上面的代码会打印:

hack!
hack!

2.2 CallbackFilter

上面已经介绍了Enhancer的几种常见callback,这里再介绍一下CallbackFilter。
上面都是为增强bean配置了一种代理callback,但是当需要作一些定制化的时候,CallbackFilter就派上用处了。
当通过设置CallbackFilter增强bean之后,bean中原方法都会根据设置的filter与一个特定的callback映射。我们通常会使用cglib中CallbackFilter的默认实现CallbackHelper,它的getCallbacks方法可以返回生成的callback数组。

下面是CallbackFilter的demo程序。

  1. public class EnhancerTest {
  1. public static void main(String[] args) {
  2. Enhancer enhancer = new Enhancer();
  3. enhancer.setSuperclass(Car.class);
  4. CallbackHelper helper = new CallbackHelper(Car.class,new Class[0]) {
  5. @Override
  6. protected Object getCallback(Method method) {
  7. if (method.getReturnType() == void.class) {
  8. return new MethodInterceptor() {
  9. @Override
  10. public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy)
  11. throws Throwable {
  12. System.out.println("before invocation");
  13. Object res = methodProxy.invokeSuper(obj, args);
  14. System.out.println("after invocation");
  15. return res;
  16. }
  17. };
  18. } else if (method.getReturnType() == String.class) {
  19. return new FixedValue() {
  20. @Override
  21. public Object loadObject() throws Exception {
  22. return "a hacked car";
  23. }
  24. };
  25. } else return NoOp.INSTANCE;
  26. }
  27. };
  28.  
  29. enhancer.setCallbacks(helper.getCallbacks());
  30. enhancer.setCallbackFilter(helper);
  31.  
  32. Car car = (Car) enhancer.create();
  33. car.print();
  34. System.out.println(car.getId());
  35. System.out.println(car.getName());
  36. }
  37.  
  38. static class Car {
  39. static int index = 0;
  40.  
  41. int id;
  42.  
  43. Car() {
  44. id = index++;
  45. }
  46.  
  47. String getName() {
  48. return "car";
  49. }
  50.  
  51. int getId() {
  52. return id;
  53. }
  54.  
  55. void print() {
  56. System.out.println("I am a car");
  57. }
  58.  
  59. }
  60. }
  1. 程序将打印:

before invocation
I am a car
after invocation
0
a hacked car

我们可以看看CallbackHelper的源码在做什么事情:

  1. public CallbackHelper(Class superclass, Class[] interfaces)
  2. {
  3. List methods = new ArrayList();
  4. Enhancer.getMethods(superclass, interfaces, methods);
  5. Map indexes = new HashMap();
  6. for (int i = 0, size = methods.size(); i < size; i++) {
  7. Method method = (Method)methods.get(i);
  8.  
  9. // getCallback就是我们编写的根据method返回callback的策略方法。
  10. Object callback = getCallback(method);
  11. if (callback == null)
  12. throw new IllegalStateException("getCallback cannot return null");
  13. boolean isCallback = callback instanceof Callback;
  14. if (!(isCallback || (callback instanceof Class)))
  15. throw new IllegalStateException("getCallback must return a Callback or a Class");
  16. if (i > 0 && ((callbacks.get(i - 1) instanceof Callback) ^ isCallback))
  17. throw new IllegalStateException("getCallback must return a Callback or a Class consistently for every Method");
  18.  
  19. // 从callback与编号的map中获取编号。
  20. Integer index = (Integer)indexes.get(callback);
  21. // 如果map中没有对应callback,则插入到map中。
  22. if (index == null) {
  23. index = new Integer(callbacks.size());
  24. indexes.put(callback, index);
  25. }
  26. // 维护bean的method与callback编号的映射。
  27. methodMap.put(method, index);
  28. // 维护callback列表。
  29. callbacks.add(callback);
  30. }
  31. }

可以看到在CallbackHelper源码中也是维护了一个methodMap用于保存method和callback编号的映射,一个callbacks用于保存callback集合(方便getCallbacks方法导出)。

3. 参考

CGLib: The Missing Manual

java CGLib代理的更多相关文章

  1. Java动态代理与Cglib库

    JDK动态代理 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代理类与委托类之间通常会存在 ...

  2. [转]java动态代理(JDK和cglib)

    转自:http://www.cnblogs.com/jqyp/archive/2010/08/20/1805041.html java动态代理(JDK和cglib) JAVA的动态代理 代理模式 代理 ...

  3. (转)Java动态代理与CGLib代理

    <br>public class UserDAOImpl{ <br><br>    public void save() { <br>        / ...

  4. 09Spring_AOP介绍和java本身的动态代理以及cglib代理

    Aspect Oriented Programming 面向切面编程 1. 业界 AOP 实际上 OOP (面向对象编程 ) 延伸 ----  OOP编程语言. AOP设计思想,下面给出一张AOP的设 ...

  5. java 笔记(3) —— 动态代理,静态代理,cglib代理

    0.代理模式 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口. 代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等. 代理类与委托类之间通常会存 ...

  6. java静态代理,动态代理,cglib代理

    代理模式在我们的应用中是很常见的,例如拦截器,spring的事务管理等.之所以能被代理,是因为java允许我们通过反射机制动态构造目标对象,并调用相应的方法. 就好像拿到了目标对象的引用,自然可以在目 ...

  7. java cglib动态代理原理及样例

     cglib动态代理: http://blog.csdn.net/xiaohai0504/article/details/6832990 一.原理 代理为控制要访问的目标对象提供了一种途径.当访问 ...

  8. Java动态代理机制——Cglib

    上一篇说过JDK动态代理机制,只能代理实现了接口的类,这就造成了限制.对于没有实现接口的类,我们可以用Cglib动态代理机制来实现. Cglib是针对类生成代理,主要是对用户类生成一个子类.因为有继承 ...

  9. IT忍者神龟之Java动态代理与CGLib代理

    <br>public class UserDAOImpl{ <br><br>    public void save() { <br>        / ...

随机推荐

  1. [Luogu] 校园网Network of Schools

    https://www.luogu.org/problemnew/show/2746 Tarjan 缩点 判断入度为0的点的个数与出度为0的点的个数的关系 注意全缩为一个点的情况 #include & ...

  2. RX232串口发送

    在进行工程调试的时候有时候需要对变量进行观察,SingnaTap II Logic Analyzer 只能对管脚进行观察,所以要观察内部的变量必须把内部的变量进行输出.一种方法是直接把变量定义成管脚通 ...

  3. hive的两种使用方式

    hive的两种使用方式 1,hive shell的方式 启动命令: bin/hive 2.beeline客户端方式 首先在一个机器上启动hive thrift服务 bin/hiveserver2 在其 ...

  4. slax中改变终端字体

    修改~/.Xresources文件 ! English font      Xterm*faceName: DejaVu Sans Mono=True:size=16 修改颜色: ! colos XT ...

  5. 使用 Linux Mint 作为主要操作系统的一些个人常用软件

    本篇文章讲一下一些 Linux 上的应用,多数为日常生活娱乐用的软件,同时也会讲一点开发工具,对于有兴趣继续研究 Linux 的可以参考一下. 目录 软件的安装方式 1.Software Manage ...

  6. 服务器 Web服务器 应用服务器区别联系

    服务器: 通俗的讲,我们访问一个网站就相当于访问一个服务器的文件,如果想要通过自己的域名来访问一个网站,首先得将域名部署到你的服务器上,然后就可以通过域名访问到你服务器上的网 页文件.ip地址就相当于 ...

  7. legend3---14、所有增删改的前置操作可以是什么

    legend3---14.所有增删改的前置操作可以是什么 一.总结 一句话总结: 先查询数据有没有,这样既起到了服务端验证的功能,也避免了很多增删改可能的错误 1.用户收藏视频,收藏课程有必要建立用户 ...

  8. mfc递归删除文件夹

    BOOL myDeleteDirectory(CString directory_path) //删除一个文件夹下的所有内容 { BOOL ret=TRUE; CFileFind finder; CS ...

  9. 用ConfigMap管理配置(10)

    一.ConfigMap介绍管理配置:   ConfigMap介绍 Secret 可以为 Pod 提供密码.Token.私钥等敏感数据:对于一些非敏感数据,比如应用的配置信息,则可以用 ConfigMa ...

  10. expect实现免交互

    如果想写一个能够自动处理输入输出的脚本又不想面对C或Perl,那么expect是最好的选择.它可以用来做一些Linux下无法做到交互的一些命令操作. (1).安装和使用expect expect是不会 ...