cglib 是什么?cglib 是一个强大的,高性能,高质量的代码生成类库。它可以在运行期扩展 Java 类或者实现 Java 接口。当然这些实际的功能是 asm 所提供的,asm 又是什么?它是 Java 字节码操控框架,具体是什么大家可以上网查一查,毕竟我们这里所要讨论的是 cglib。cglib 就是封装了 asm,简化了 asm 的操作,实现了在运行期动态生成新的 class 的壹个第三方类库。可能大家还感觉不到它的强大,现在就告诉你。实际上 cglib 为 spring aop 提供了底层的一种实现;为 hibernate 使用 cglib 动态生成 VO/PO(接口层对象)。
下面我们将通过一个具体的事例来看一下 cglib,体验一下 cglib。使用到的 Jar 包有 cglib-2.13.jar 和 asm-2.23.jar。以壹個实例简单介绍下 cglib 的应用。
我们模拟一个虚拟的场景,模拟对表的操作。壹开始我们对表提供了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 的任何内容。问题不会这么简单的就结束,新的需求来临了。
变化随之而来,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,"张三"的正常执行,"李四"的没有执行。
public class AuthProxyFilter implements CallbackFilter{ public int accept(Method arg0) { if(!"query".equalsIgnoreCase(arg0.getName())) return 0; return 1; } }OK,可能大家会对返回值 0 或者 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 去实现接口。