博客已迁移到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很类似

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方法。

public class EnhancerTest {

    public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Car.class);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy)
throws Throwable {
System.out.println("before");
Object res = methodProxy.invokeSuper(obj, args);
System.out.println("after");
return res;
}
});
Car car = (Car) enhancer.create(); car.print();
} static class Car {
void print() {
System.out.println("I am a car");
}
} }

上面的程序会打印:

before
I am a car
after

2.1.2 Callback-NoOp

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

Callback-LazyLoader

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

public class EnhancerTest {

    public static void main(String[] args) {
CarFactory factory = new CarFactory();
System.out.println("factory built");
System.out.println(factory.car.getName());
System.out.println(factory.car.getName());
} static class Car {
String name;
Car() {
} String getName() {
return name;
}
    }

    static class CarFactory {
Car car; CarFactory() {
car = carLazyProxy();
} Car carLazyProxy() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Car.class);
enhancer.setCallback(new LazyLoader() {
@Override
public Object loadObject() throws Exception {
System.out.println("prepare loading");
Car car = new Car();
car.name = "this is a car";
System.out.println("after loading");
return car;
}
});
return ((Car) enhancer.create());
}
}
}
上面的程序打印情况如下:

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

2.1.3 Callback-Dispatcher

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

public class EnhancerTest {

    public static void main(String[] args) {
CarFactory factory = new CarFactory();
System.out.println("factory built");
System.out.println(factory.car.getName());
System.out.println(factory.car.getName());
} static class Car {
String name;
Car() {
} String getName() {
return name;
}
    }

    static class CarFactory {
Car car; CarFactory() {
car = carLazyProxy();
} Car carLazyProxy() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Car.class);
enhancer.setCallback(new Dispatcher() {
@Override
public Object loadObject() throws Exception {
System.out.println("prepare loading");
Car car = new Car();
car.name = "this is a car";
System.out.println("after loading");
return car;
}
});
return ((Car) enhancer.create());
}
}
}
程序会打印:

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。

public class EnhancerTest {

    public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Car.class);
enhancer.setCallback(new InvocationHandler() {
@Override
public Object invoke(Object obj, Method method, Object[] args) throws Throwable {
if (method.getReturnType() == void.class) {
System.out.println("hack");
}
return null;
}
});
Car car = (Car) enhancer.create();
car.print();
} static class Car {
void print() {
System.out.println("I am a car");
}
}
}

上面的程序会打印:

hack

2.1.5 Callback-FixedValue

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

public class EnhancerTest {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Car.class);
enhancer.setCallback(new FixedValue() {
@Override
public Object loadObject() throws Exception {
return "hack!";
}
}); Car car = (Car) enhancer.create();
System.out.println(car.print1());
System.out.println(car.print2());
} static class Car {
String print1() {
return "car1";
}
String print2() {
return "car2";
}
}
}

上面的代码会打印:

hack!
hack!

2.2 CallbackFilter

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

下面是CallbackFilter的demo程序。

public class EnhancerTest {
    public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Car.class);
CallbackHelper helper = new CallbackHelper(Car.class,new Class[0]) {
@Override
protected Object getCallback(Method method) {
if (method.getReturnType() == void.class) {
return new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy)
throws Throwable {
System.out.println("before invocation");
Object res = methodProxy.invokeSuper(obj, args);
System.out.println("after invocation");
return res;
}
};
} else if (method.getReturnType() == String.class) {
return new FixedValue() {
@Override
public Object loadObject() throws Exception {
return "a hacked car";
}
};
} else return NoOp.INSTANCE;
}
}; enhancer.setCallbacks(helper.getCallbacks());
enhancer.setCallbackFilter(helper); Car car = (Car) enhancer.create();
car.print();
System.out.println(car.getId());
System.out.println(car.getName());
} static class Car {
static int index = 0; int id; Car() {
id = index++;
} String getName() {
return "car";
} int getId() {
return id;
} void print() {
System.out.println("I am a car");
} }
}
程序将打印:

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

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

public CallbackHelper(Class superclass, Class[] interfaces)
{
List methods = new ArrayList();
Enhancer.getMethods(superclass, interfaces, methods);
Map indexes = new HashMap();
for (int i = 0, size = methods.size(); i < size; i++) {
Method method = (Method)methods.get(i); // getCallback就是我们编写的根据method返回callback的策略方法。
Object callback = getCallback(method);
if (callback == null)
throw new IllegalStateException("getCallback cannot return null");
boolean isCallback = callback instanceof Callback;
if (!(isCallback || (callback instanceof Class)))
throw new IllegalStateException("getCallback must return a Callback or a Class");
if (i > 0 && ((callbacks.get(i - 1) instanceof Callback) ^ isCallback))
throw new IllegalStateException("getCallback must return a Callback or a Class consistently for every Method"); // 从callback与编号的map中获取编号。
Integer index = (Integer)indexes.get(callback);
// 如果map中没有对应callback,则插入到map中。
if (index == null) {
index = new Integer(callbacks.size());
indexes.put(callback, index);
}
// 维护bean的method与callback编号的映射。
methodMap.put(method, index);
// 维护callback列表。
callbacks.add(callback);
}
}

可以看到在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. vue中插槽(slot)的使用

    刚学vue的时候,曾经学习过slot插槽的使用,但是后面接触的不多,因为之前我还没使用element-ui... 但是使用了element-ui之后,里面的许多组件,有时候会使用插槽,为了巩固一下插槽 ...

  2. phpstorm+xdebug安装配置

    这个问题也困惑了我好久 烦死了 今天看了qing师傅的博客 跟着安装 运行环境: phpStorm 2018 PHP 5.45 nts VC9 Xdebug 2.4.1 0x01 PHP安装xdebu ...

  3. Maven Web项目出现org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException错误

    1. 问题描述 初学Maven,新建了一个基于Web骨架的Web项目,jar 包也导好了,作用域也设置正确了,Tomcat也正常运行了,可是就是说编译错误. 2. 问题原因 虽然我配置了Tomcat ...

  4. 笔记四(Competitor Analysis Test小结)

    1.关机后启动电脑,测试BIOS的POST time 2.进入睡眠模式后,按任意键,通过Windows logs查看bios的init时间 3.进入BIOS setup的快捷键,一般为F2 4.进入B ...

  5. python中文显示乱码,已经在开头有了coding: utf-8

    乱码原因:因为你的文件声明为 utf-8,并且也应该是用 utf-8 的编码保存的源文件.但是 windows 的本地默认编码是 cp936,也就是 gbk 编码,所以在控制台直接打印 utf-8 的 ...

  6. 调用subprocess 使用logging打印日志

    #!/usr/bin/env python # -*- coding:utf-8 -*- # Author:Henry 17607168727@163.com import sys import lo ...

  7. 如何丧心病狂的使用python爬虫读小说

    写在前边 其实一直想入门python很久了,慕课网啊,菜鸟教程啊python的基础的知识被我翻了很多遍了,但是一直没有什么实践.刚好,这两天被别人一直安利一本小说<我可能修的是假仙>,还在 ...

  8. SQL-W3School-高级:SQL LEFT JOIN 关键字

    ylbtech-SQL-W3School-高级:SQL LEFT JOIN 关键字 1.返回顶部 1. SQL LEFT JOIN 关键字 LEFT JOIN 关键字会从左表 (table_name1 ...

  9. smarty 模板几个例子(变量调节器)

    一.assign和display方法的使用以及几个变量调节器 header("content-type:text/html;charset=utf-8");//加载Smarty引擎 ...

  10. fastjson在将Map<Integer, String>转换成JSON字符串时,出现中文乱码问题

    fastjson在将Map<Integer, String>转换成JSON字符串时,出现中文乱码问题. 先记下这个坑,改天在看看是怎么导致的,暂时通过避免使用Integer作为键(使用St ...