cglib(Code Generation Library)是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。
cglib封装了asm,可以在运行期动态生成新的class。
cglib用于AOP,jdk中的proxy必须基于接口,cglib却没有这个限制。
这里必须说一下ASM
ASM 是一个 Java 字节码操控框架。它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class 文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 Java 字节码(指令)。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。
与 BCEL 和 SERL 不同,ASM 提供了更为现代的编程模型。对于 ASM 来说,Java class 被描述为一棵树;使用 “Visitor” 模式遍历整个二进制结构;事件驱动的处理方式使得用户只需要关注于对其编程有意义的部分,而不必了解 Java 类文件格式的所有细节:ASM 框架提供了默认的 “response taker”处理这一切。
为什么要动态生成 Java 类?
动态生成 Java 类与 AOP 密切相关的。AOP 的初衷在于软件设计世界中存在这么一类代码,零散而又耦合:零散是由于一些公有的功能(诸如著名的 log 例子)分散在所有模块之中;同时改变 log 功能又会影响到所有的模块。出现这样的缺陷,很大程度上是由于传统的 面向对象编程注重以继承关系为代表的“纵向”关系,而对于拥有相同功能或者说方面 (Aspect)的模块之间的“横向”关系不能很好地表达。例如,目前有一个既有的银行管理系统,包括 Bank、Customer、Account、Invoice 等对象,现在要加入一个安全检查模块, 对已有类的所有操作之前都必须进行一次安全检查。
ASM 能够通过改造既有类,直接生成需要的代码。增强的代码是硬编码在新生成的类文件内部的,没有反射带来性能上的付出。同时,ASM 与 Proxy 编程不同,不需要为增强代码而新定义一个接口,生成的代码可以覆盖原来的类,或者是原始类的子类。它是一个普通的 Java 类而不是 proxy 类,甚至可以在应用程序的类框架中拥有自己的位置,派生自己的子类。
相比于其他流行的 Java 字节码操纵工具,ASM 更小更快。ASM 具有类似于 BCEL 或者 SERP 的功能,而只有 33k 大小,而后者分别有 350k 和 150k。同时,同样类转换的负载,如果 ASM 是 60% 的话,BCEL 需要 700%,而 SERP 需要 1100% 或者更多。
ASM 已经被广泛应用于一系列 Java 项目:AspectWerkz、AspectJ、BEA WebLogic、IBM AUS、OracleBerkleyDB、Oracle TopLink、Terracotta、RIFE、EclipseME、Proactive、Speedo、Fractal、EasyBeans、BeanShell、Groovy、Jamaica、CGLIB、dynaop、Cobertura、JDBCPersistence、JiP、SonarJ、Substance L&F、Retrotranslator 等。Hibernate 和 Spring 也通过 cglib,另一个更高层一些的自动代码生成工具使用了 ASM。
下面看一个简单的例子,具体的体会一下CGLIB的实现方式:
Option操作类含义基本的操作类型增删改查
package cglib;
import java.io.Serializable;
/**
* 操作集合实现类
* @author zhangjuwei
*
*/
public class Option implements Serializable {
public void add(){
System.out.println(" add opt");
}
public void update(){
System.out.println(" update opt");
}
public void query(){
System.out.println(" query opt");
}
public void del(){
System.out.println(" del opt");
}
}
import net.sf.cglib.proxy.Enhancer;
/**
* 操作工厂类
* @author zhangjuwei
*
*/
public class OptionFactory {
private static Option option = new Option();
public static Option getInstance() {
return option;
}
}
这时 基本上可以满足常见的需求了。即调用操作类中的四个方法。
详见 Client 的 7 8 行。
但如果要实现比较复杂的需求如:某些方法某些人的访问权限的限制。
看下面的代码:OptionFactory4Auth
package cglib;
import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.NoOp;
/**
* 操作工厂类
* @author zhangjuwei
*
*/
public class OptionFactory4Auth {
public static Option getAuthInstance(AuthOptProxy auth) {
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "C://temp"); //这里将生成的class文件输出至指定的文件夹下,方便查看。
Enhancer enhancer = new Enhancer(); //底层是ASM
enhancer.setSuperclass(Option.class);
// enhancer.setCallback(auth);
enhancer.setCallbacks(new Callback[] { auth, NoOp.INSTANCE });
enhancer.setCallbackFilter(new AuthProxyFilter());
return (Option) enhancer.create();
}
}
package cglib;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.CallbackFilter;
public class AuthProxyFilter implements CallbackFilter {
private static final int AUTH_NEED = 0;
private static final int AUTH_NOT_NEED = 1;
@Override
public int accept(Method method) {
if ("query".equals(method.getName())) {
return AUTH_NOT_NEED;
}
return AUTH_NEED;
}
}
package cglib;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
* 用户权限校验类
* @author zhangjuwei
*
*/
public class AuthOptProxy implements MethodInterceptor{
private String name;
public AuthOptProxy(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
if (!"maurice".equals(this.name)) {
System.out.println("AuthProxy:you have no permits to do manager!");
return null;
}
return proxy.invokeSuper(obj, args);
}
}
package cglib;
public class Client {
public static void main(String[] args) {
Client c = new Client();
// c.optManage();
c.haveNoAuthManager();
c.haveAuthManager();
}
/**
* 模拟:登录会员没有权限
*
*/
public void haveNoAuthManager() {
System.out.println("the loginer's name is not maurice,so have no permits do manager");
Option noAuthManager = OptionFactory4Auth.getAuthInstance(new AuthOptProxy("maurice1"));
doCRUD(noAuthManager);
}
/**
* 模拟:登录会员有权限
*/
public void haveAuthManager() {
System.out
.println("the loginer's name is maurice,so have permits do manager");
Option noAuthManager = OptionFactory4Auth
.getAuthInstance(new AuthOptProxy("maurice"));
doCRUD(noAuthManager);
}
public void optManage() {
System.out.println("any one can do manager");
Option option = OptionFactory.getInstance();
doCRUD(option);
}
public void doCRUD(Option opt) {
opt.add();
opt.update();
opt.del();
opt.query();
}
}
cglib在代码运行期,动态生成了Option的子类,并且根据CallbackFilter的accept方法,覆写了Option中的所有方法--去执行相应的MethodInterceptor的intercept方法。