CGLIB是一个强大的高性能的代码生成包。它广泛的被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的interception(拦截)。最流行的OR Mapping工具hibernate也使用CGLIB来代理单端single-ended(多对一和一对一)关联(对集合的延迟抓取,是采用其他机制实现的)。EasyMock和jMock是通过使用模仿(mock)对象来测试java代码的包。它们都通过使用CGLIB来为那些没有接口的类创建模仿(mock)对象。
CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。除了CGLIB包,脚本语言例如Groovy和BeanShell,也是使用ASM来生成java的字节码。当然不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉
cglib实现代理时使用Enhancer的setCallback设置回调方法,对方法进行拦截,从而实现拦截,下面的例子是使用MethodInterceptor拦截器
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
class Base {
/**
* 一个模拟的add方法
*/
public void add() {
System.out.println("add ------------");
}
}
class Factory {
/**
* 获得增强之后的目标类,即添加了切入逻辑advice之后的目标类
*
* @param proxy
* @return
*/
public static Base getInstance(CglibProxy proxy) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Base.class);
//回调方法的参数为代理类对象CglibProxy,最后增强目标类调用的是代理类对象CglibProxy中的intercept方法
enhancer.setCallback(proxy);
// 此刻,base不是单纯的目标类,而是增强过的目标类
Base base = (Base) enhancer.create();
return base;
}
}
class CglibProxy implements MethodInterceptor{
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before...");
methodProxy.invokeSuper(o,objects);
System.out.println("after....");
return null;
}
}
public class CgProxy {
public static void main(String[] args) {
CglibProxy cgProxy = new CglibProxy();
Base base = Factory.getInstance(cgProxy);
base.add();
}
}
需要cglib和asm的包
maven pom.xml
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.0</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-all</artifactId>
<version>5.0.4</version>
</dependency>
cglic的拦截器种类
Callback有6个子接口,意味着回调方法中可以配置6中拦截器
1. FixedValue
定义一个Person类
class Person{
public String sayHello(String name){
System.out.println("say hello...");
return "hello "+ name;
}
}
FixedValue拦截器会将所有的方法的返回值替换为固定了一个值,包括从Object类的方法
自定一个FixdValue,需要实现FixedValue,下面的MyFixedValue实现了FixedValue,并实现了loadObject,返回”who are you?”,这样当掉用代理类的所有方法的时候,都会返回”who are you ?”
class MyFixedValue implements FixedValue{
@Override
public Object loadObject() throws Exception {
return "who are you ? ";
}
}
调用sayHello方法
public class CglibStudy {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Person.class);
enhancer.setCallback(new MyFixValue());
Person person = (Person)enhancer.create();
String hello = person.sayHello("xiaoming");
System.out.println(hello); //hello=who are you ?
System.out.println(person.toString());//也打印出who are you ?
}
}
输出结果
如果调用person.hashCode()
, 会抛出异常 ClassCastException
,因为FixedValue永远返回String 类型
只能调用Person的非final方法和非static方法
2. InvocationHandler
class MyInvocationHandler implements InvocationHandler{
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
return "Hello pig";
}
}
此时所有的方法调用时也会返回固定的值Hello Pig,在invoke中,不能使用method.invoke
,因为调用的时候又会被拦截,导致死循环
class MyInvocationHandler implements InvocationHandler{
@Override
public Object invoke(Object o, Method method, Object[] args) throws Throwable {
method.invoke(o,args); //调用会导致死循环
}
}
输出结果
可以看出invoke中的method方法正是Person类的sayHello方法,如果使用method.invoke(o,args),将会被MyInvocationHandler拦截,从而导致死循环
3. MethodInterceptor
MethodInterceptor允许完全控制被截取的方法,并提供一些实用程序来调用增强类处于原始状态的方法。 但是为什么会想使用其他方法呢? 因为其他方法更有效率,而且cglib经常用于效率起重要作用的边缘案例框架中。 MethodInterceptor的创建和链接需要例如生成不同类型的字节代码和创建InvocationHandler不需要的一些运行时对象。 因此,还有其他类可以与Enhancer一起使用
class MyMethodInterceptor implements MethodInterceptor{
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
if(method.getDeclaringClass() != Object.class && method.getReturnType() == String.class) {
return "Hello cglib!";
} else {
return methodProxy.invokeSuper(o, args);
}
}
}
methodProxy.invokeSuper
方法将会调用被拦截的方法,这样我们就可以在调用之前和之后做一些其他的事情,实现代理的目的,这个用法类似于JDK动态代理,上面的intercept方法中,先判断被拦截的方法不是Object类,并且返回类型是String的话,就直接返回Hello cglib,否则执行methodProxy.invokeSuper(o,args)
,这句将会执行被代理类的方法,而return "Hello cglib"
,并没有调用Person的被拦截的方法,直接就终止了
4. LazyLoader
lazyLoader 可以用来做一些延迟加载,对象并没有被加载,而是在使用的时候才创建,像Herbernate就是用cglib来做延迟加载,这个loadObject只会调用一次
定义一个LazyLoader的实现类
class MyLazyLoader implements LazyLoader{
@Override
public Object loadObject() throws Exception {
System.out.println("创建Person对象");
Person person = new Person();
return person;
}
}
调用的时候才会调用loadObject创建对象
public class CglibStudy {
public static void main(String[] args) throws ClassNotFoundException {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Person.class);
Person person = (Person)enhancer.create(Person.class,new MyLazyLoader());
System.out.println(person);
String hello = person.sayHello("xiaoming");
System.out.println(hello);
hello = person.sayHello("xiaowang");
System.out.println(person);
System.out.println(hello);
}
}
输出,看到两次打印person对象是一样的,因此loadObject只会被调用一次
5. Dispatcher
class MyDispatcher implements Dispatcher{
@Override
public Object loadObject() throws Exception {
System.out.println("创建Person对象");
Person person = new Person();
return person;
}
}
调用
public class CglibStudy {
public static void main(String[] args) throws ClassNotFoundException {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Person.class);
Person person = (Person)enhancer.create(Person.class,new MyDispatcher());
System.out.println(person);
String hello = person.sayHello("xiaoming");
System.out.println(hello);
hello = person.sayHello("xiaowang");//这个peson对象是被第二次创建的
System.out.println(person);
System.out.println(hello);
}
}
输出结果,两次打印的person对象不一样,说明loadObject会每次都调用
6. ProxyRefDispatcher
ProxyRefDispatcher用来和setCallbackFilter使用
7. NoOp
NoOp.INSTANCE返回的没有任何操作的拦截器
public class CglibStudy {
public static void main(String[] args) throws ClassNotFoundException {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Person.class);
CallbackHelper callbackHelper = new CallbackHelper(Person.class,new Class[0]) {
@Override
protected Object getCallback(Method method) {
if(method.getReturnType() == String.class) {
return new FixedValue() {
@Override
public Object loadObject() throws Exception {
return "hello cglib";
}
};
}else{
return NoOp.INSTANCE;
}
}
};
enhancer.setCallbackFilter(callbackHelper);
enhancer.setCallbacks(callbackHelper.getCallbacks());
Person person = (Person)enhancer.create();
System.out.println(person);
String hello = person.sayHello("xiaoming");
System.out.println(hello);
hello = person.sayHello("xiaowang");
System.out.println(person);
System.out.println(hello);
}
}
cglib也是数据代理模式,关于代理模式,可以看我的另外一篇博客