什么是CGlib
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.
所以, cglib是一个功能强大、高性能和高质量的代码生成库,它用于扩展JAVA类并在运行时实现接口。
AOP, SPring AOP, Hibernate等都在使用CGlib, EasyMock和jMock是使用模拟对象测试Java代码的库。它们都使用CGLIB库为没有接口的类创建模拟对象。
CGLIB库使用ASM(一个小型但快速的字节码操作框架)转换现有的字节码并生成新类。除了CGLIB库,脚本语言(如Groovy和BeanShell)也使用ASM生成Java字节码。ASM使用类似SAX的解析器机制来实现高性能。
图中显示了CGLIB库相关框架和语言之间的关系。注意,一些框架,如Spring AOP和Hibernate,经常同时使用CGLIB库和JDK动态代理来满足它们的需求。Hibernate使用JDK动态代理为WebShere应用服务器实现事务管理器适配器;默认情况下,Spring AOP使用JDK动态代理来代理接口,除非强制使用CGLIB代理。
下面举例介绍下CGLib的典型应用:
准备工作, 创建一个Bean类:
package com.xqljj;
public class Dao {
public Dao() {
update();
}
public void update() {
System.out.println("PeopleDao.update()");
}
public void select() {
System.out.println("PeopleDao.select()");
}
}
1.简单的代理模式实现
我们首先实现一个DaoProxy
package com.xqljj;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class DaoProxy implements MethodInterceptor {
@Override
public Object intercept(Object object, Method method, Object[] objects, MethodProxy proxy) throws Throwable {
System.out.println("Before Method Invoke");
proxy.invokeSuper(object, objects);
System.out.println("After Method Invoke");
return object;
}
}
那么在调用的时候,直接调用
public static void testCglib() {
DaoProxy daoProxy = new DaoProxy();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Dao.class);
enhancer.setCallback(daoProxy);
Dao dao = (Dao)enhancer.create();
dao.update();
dao.select();
}
通过setCallback, 实现代理.
- 实现一个带拦截器的Proxy
package com.xqljj;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class DaoAnotherProxy implements MethodInterceptor {
@Override
public Object intercept(Object object, Method method, Object[] objects, MethodProxy proxy) throws Throwable {
System.out.println("StartTime=[" + System.currentTimeMillis() + "]");
//method.invoke(object, objects);
proxy.invokeSuper(object, objects);
System.out.println("EndTime=[" + System.currentTimeMillis() + "]");
return object;
}
}
在Enhancer里面set filter, filter可以根据需求来定制:
package com.xqljj;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.CallbackFilter;
public class DaoFilter implements CallbackFilter {
@Override
public int accept(Method method) {
if ("select".equals(method.getName())) {
return 0;
}
return 1;
}
}
- 构造函数不拦截方法
那么在enhancer.setInterceptDuringConstruction(false);
public static void testCglib3() {
DaoProxy daoProxy = new DaoProxy();
DaoAnotherProxy daoAnotherProxy = new DaoAnotherProxy();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Dao.class);
enhancer.setCallbacks(new Callback[]{daoProxy, daoAnotherProxy, NoOp.INSTANCE});
enhancer.setCallbackFilter(new DaoFilter());
enhancer.setInterceptDuringConstruction(false);
Dao dao = (Dao)enhancer.create();
dao.update();
dao.select();
}
- 返回相同的值
Bean类:
package com.xqljj;
public class PersonService {
public String sayHello(String name) {
return "Hello " + name;
}
public Integer lengthOfName(String name) {
return name.length();
}
}
对于上面的类,我们创建一个拦截器,来拦截SayHello方法.Cglib里面Enhancer,我们可以利用它来创建一个动态代理. enhancer.setSuperclass(PersonService.class);
我们想要每次都返回相同的值,实现方法如下:
public static void fixvalue() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(PersonService.class);
enhancer.setCallback((FixedValue) () -> "Hello Tom!");
PersonService proxy = (PersonService) enhancer.create();
String res = proxy.sayHello("HAHA");
System.out.println(res);
}
- 不同方法实现不同的拦截
实现代码如下:
public static void methodDepend() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(PersonService.class);
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
if (method.getDeclaringClass() != Object.class && method.getReturnType() == String.class) {
return “Hello Tom!”;
} else {
return proxy.invokeSuper(obj, args);
}
});
PersonService proxy = (PersonService) enhancer.create();
System.out.println(proxy.sayHello(null));
//assertEquals("Hello Tom!", proxy.sayHello(null));
int lengthOfName = proxy.lengthOfName("Mary");
//assertEquals(4, lengthOfName);
System.out.println(lengthOfName);
}
- Cglib实现BeanGenerator
public static void beanGenerator() {
BeanGenerator beanGenerator = new BeanGenerator();
beanGenerator.addProperty("name", String.class);
Object myBean = beanGenerator.create();
try {
Method setter = myBean.getClass().getMethod("setName", String.class);
setter.invoke(myBean, "some string value set by a cglib");
Method getter = myBean.getClass().getMethod("getName");
System.out.println(getter.invoke(myBean));
} catch (Exception e) {
}
}
- 创建Mixin
mixin 就是可以merge多个对象成一个,Cglib支持把多个对象合并成一个, 当然,这些对象需要实现Interface.
举例如下:
public interface Interface1 {
String first();
}
public interface Interface2 {
String second();
}
public class Class1 implements Interface1 {
@Override
public String first() {
return "first behaviour";
}
}
public class Class2 implements Interface2 {
@Override
public String second() {
return "second behaviour";
}
}
为了合并这两个Interface, 我们需要定义一个mergeinterface
public interface MixinInterface extends Interface1, Interface2 { }
合并后, 调用Mixin.create
public static void testMiXin() {
Mixin mixin = Mixin.create(
new Class[]{ Interface1.class, Interface2.class, MixinInterface.class },
new Object[]{ new Class1(), new Class2() }
);
MixinInterface mixinDelegate = (MixinInterface) mixin;
System.out.println(mixinDelegate.first());
System.out.println(mixinDelegate.second());
}
运行,打印的结果是:
first behaviour
second behaviour
总结:
Cglib广泛用于Spring,Hibernate,Mock等,是一个高性能的代码生成库,还有很多使用场景,例如Java Security, Java序列化等,需要仔细摸索.
另外,以上代码,好多没有写打印结果,但作者都一一编译运行过了,你不妨也试试,可以有很深的体会.