cglib is a powerful, high performance and quality Code Generation Library, It is used to extend JAVA classes and implements interfaces at runtime.
cglib是Code Generation Library的缩写。
CGlib是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。
当然这些实际的功能是asm所提供的,cglib依赖于ASM库。
cglib就是封装了asm,简化了asm的操作,实现了在运行期动态生成新的class。
实际上CGlib为spring aop提供了底层的一种实现;为hibernate使用cglib动态生成VO/PO (接口层对象)。
Hibernate主要是利用cglib生成pojo的子类并override get方法来实现lazy loading机制,Spring则是利用cglib来实现动态代理。
而JDK的动态代理机制要求有接口才行,这样就强制我们的pojo实现某个接口。
反射、Proxy和元数据是Java最强的三个特征,再加上CGLib (Code Generation Library)和ASM,使得Java虽然没有Ruby,Python般后生可畏,一样能做出强悍的框架。
Proxy可以看作是微型的AOP,明白提供了在继承和委托之外的第三个代码封装途径,只要有足够的想象力,可以做得非常好玩,Spring的源码里用Proxy就 用得很随便,看得我非常眼红。可惜Proxy必须基于接口。因此Spring的做法,基于接口的用proxy,否则就用cglib。AOP么,一般小事非compoent一级的就不麻烦AspectJ出手了。
cglib的Enhancer说起来神奇,用起来一页纸不到就讲完了。
它的原理就是用Enhancer生成一个原有类的子类,并且设置好callback到proxy, 则原有类的每个方法调用都会转为调用实现了MethodInterceptor接口的proxy的intercept() 函数:
public Object intercept(Object o,Method method,Object[] args,MethodProxy proxy)
在intercept()函数里,你可以在执行Object result=proxy.invokeSuper(o,args);来执行原有函数,在执行前后加入自己的东西,改变它的参数值,也可以瞒天过海,完全 干别的。说白了,就是AOP中的around advice。
AOP没有出现以前,该领域经典的设计模式是Decorator,像Java IO Stream的设计就是如此.不过,如果为每个DAO, 每个方法的写Decorator函数会写死人的,所以用上cglib的好处是一次过拦截所有方法。
另外,cglib除了Enhancer之外,还有BulkBean和Transform,都是Hibernate持久化的基础。
下面我们将通过一个具体的事例来看一下CGlib体验一下CGlib。
* CGlib 2.13
* ASM 2.23
以一个实例在简单介绍下cglib的应用。
我们模拟一个虚拟的场景,模拟对表的操作。
1. 开始我们对表提供了CRUD方法。
我们现在创建一个对Table操作的DAO类。
public class TableDAO { public void create(){ System.out.println("create() is running !"); } public void query(){ System.out.println("query() is running !"); } public void update(){ System.out.println("update() is running !"); } public void delete(){ System.out.println("delete() is running !"); } }
OK,它就是一个javaBean,提供了CRUD方法的javaBean。
下面我们创建一个DAO工厂,用来生成DAO实例。
public class TableDAOFactory { private static TableDAO tDao = new TableDAO(); public static TableDAO getInstance(){ return tDao; } }
接下来我们创建客户端,用来调用CRUD方法。
public class Client { public static void main(String[] args) { TableDAO tableDao = TableDAOFactory.getInstance(); doMethod(tableDao); } public static void doMethod(TableDAO dao){ dao.create(); dao.query(); dao.update(); dao.delete(); } }
OK,完成了,CRUD方法完全被调用了。当然这里并没有CGlib的任何内容。问题不会这么简单的就结束,新的需求来临了。
2. 变化随之而来,Boss告诉我们这些方法不能开放给用户,只有“张三”才有权使用。阿~!怎么办,难道我们要在每个方法上面进行判断吗?
好像这么做也太那啥了吧,对了对了Proxy可能是最好的解决办法。jdk的代理就可以解决了。 好了我们来动手改造吧。等等jdk的代理需要实现接口,这样,
我们的dao类需要改变了。既然不想改动dao又要使用代理,我们这就请出CGlib。
我们只需新增一个权限验证的方法拦截器。
public class AuthProxy implements MethodInterceptor { private String name ; //传入用户名称 public AuthProxy(String name){ this.name = name; } public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable { //用户进行判断 if(!"张三".equals(name)){ System.out.println("你没有权限!"); return null; } return arg3.invokeSuper(arg0, arg2); } }
当然不能忘了对我们的dao工厂进行修改,我们提供一个使用代理的实例生成方法
public static TableDAO getAuthInstance(AuthProxy authProxy){ Enhancer en = new Enhancer(); //进行代理 en.setSuperclass(TableDAO.class); en.setCallback(authProxy); //生成代理实例 return (TableDAO)en.create(); }
我们这就可以看看客户端的实现了。添加了两个方法用来验证不同用户的权限。
public static void haveAuth(){ TableDAO tDao = TableDAOFactory.getAuthInstance(new AuthProxy("张三")); doMethod(tDao); } public static void haveNoAuth(){ TableDAO tDao = TableDAOFactory.getAuthInstance(new AuthProxy("李四")); doMethod(tDao); }
OK,"张三"的正常执行,"李四"的没有执行。
看到了吗?简单的aop就这样实现了
难道就这样结束了么?
3. Boss又来训话了,不行不行,现在除了"张三"其他人都用不了了,现在不可以这样。他们都来向我反映了,必须使用开放查询功能。
哈哈,现在可难不倒我们了,因为我们使用了CGlib。当然最简单的方式是去修改我们的方法拦截器,不过这样会使逻辑变得复杂,且
不利于维护。还好CGlib给我们提供了方法过滤器(CallbackFilter),CallbackFilte可以明确表明,被代理的类中不同的方法,
被哪个拦截器所拦截。下面我们就来做个过滤器用来过滤query方法。
public class AuthProxyFilter implements CallbackFilter{ public int accept(Method arg0) { if(!"query".equalsIgnoreCase(arg0.getName())) return 0; return 1; } }
OK,可能大家会对return 0 or 1感到困惑,用到的时候就会讲解,当然下面就会用到了。
我们在工场中新增一个使用了过滤器的实例生成方法。
public static TableDAO getAuthInstanceByFilter(AuthProxy authProxy){ Enhancer en = new Enhancer(); en.setSuperclass(TableDAO.class); en.setCallbacks(new Callback[]{authProxy,NoOp.INSTANCE}); en.setCallbackFilter(new AuthProxyFilter()); return (TableDAO)en.create(); }
看到了吗setCallbacks中定义了所使用的拦截器,其中NoOp.INSTANCE是CGlib所提供的实际是一个没有任何操作的拦截器,
他们是有序的。一定要和CallbackFilter里面的顺序一致。明白了吗?上面return返回的就是返回的顺序。也就是说如果调用query方法就使用NoOp.INSTANCE进行拦截。
现在看一下客户端代码。
public static void haveAuthByFilter(){ TableDAO tDao = TableDAOFactory.getAuthInstanceByFilter(new AuthProxy("张三")); doMethod(tDao); tDao = TableDAOFactory.getAuthInstanceByFilter(new AuthProxy("李四")); doMethod(tDao); }
ok,现在"李四"也可以使用query方法了,其他方法仍然没有权限。
哈哈,当然这个代理的实现没有任何侵入性,无需强制让dao去实现接口。
转载:http://llying.iteye.com/blog/220452