CGLIB(Code Generation Library)是一个强大的、高性能的、高质量的Code生成类库,它可以在运行期扩展Java类和Java接口。它封装了asm,可以在运行期动态生成class,是Spring、Hibernate等框架依赖的核心包。Java动态代理基于接口,CGLIB没有此限制。

BookManager类:

  1. public class BookManager {  
  2.  
  3.     private void create() {  
  4.         System.out.println("create...\n");  
  5.     }  
  6.  
  7.     private void query() {  
  8.         System.out.println("query...\n");  
  9.     }  
  10.  
  11.     private void update() {  
  12.         System.out.println("update...\n");  
  13.     }  
  14.  
  15.     private void delete() {  
  16.         System.out.println("delete...\n");  
  17.     }  
  18.  
  19.     // 为方便调用  
  20.     public void doCRUD() {  
  21.         create();  
  22.         query();  
  23.         update();  
  24.         delete();  
  25.     }  

BookFactory类:

  1. public class BookFactory {  
  2.  
  3.     private static BookManager manager = new BookManager();  
  4.  
  5.     public static BookManager getInstance() {  
  6.         return manager;  
  7.     }  

写一单元测试方法:

  1. @Test 
  2. public void test1() {  
  3.     System.out.println("any one can do!\n");  
  4.     BookManager manager = BookFactory.getInstance();  
  5.     manager.doCRUD();  

上述是对CRUD操作无限制时的代码,未涉及CGLIB。但接下来需求发生变化:要求只有用户xxx才能进行CRUD操作。此时若在每个方法前都加上权限判断,会使重复逻辑太多,这时可使用代理模式,但原先的BookManager未实现接口,不能使用Java的动态代理,此时可使用CGLIB。使用CGLIB,需添加一个实现MethodInterceptor接口的类,代码如下:

  1. // 权限校验代理类,用户名为xxx时才有权限操作  
  2. public class PermissionProxy implements MethodInterceptor {  
  3.  
  4.     private String name;  
  5.  
  6.     public PermissionProxy(String name) {  
  7.         this.name = name;  
  8.     }  
  9.  
  10.     public String getName() {  
  11.         return name;  
  12.     }  
  13.  
  14.     public void setName(String name) {  
  15.         this.name = name;  
  16.     }  
  17.  
  18.     @Override 
  19.     public Object intercept(Object obj, Method method, Object[] args,  
  20.             MethodProxy proxy) throws Throwable {  
  21.         if (!"xxx".equals(name)) {  
  22.             return null;  
  23.         }  
  24.         return proxy.invokeSuper(obj, args);  
  25.     }  
  26.  

此时在BookFactory类中添加如下代码:

  1. /**  
  2. * 创建带有权限的BookManager  
  3. */ 
  4. public static BookManager getPermissionInstance(PermissionProxy proxy) {  
  5.     Enhancer enhancer = new Enhancer();  
  6.     enhancer.setSuperclass(BookManager.class);  
  7.     enhancer.setCallback(proxy);  
  8.     return (BookManager) enhancer.create();  

单元测试方法代码如下:

  1. @Test 
  2. public void test2() {  
  3.     System.out.println("恭喜您,您有权限进行操作!!!");  
  4.     BookManager permission = BookFactory  
  5.             .getPermissionInstance(new PermissionProxy("xxx"));  
  6.     permission.doCRUD();  
  7.  
  8.     System.out.println("不存在的用户名,您没有权限进行操作!!!");  
  9.     BookManager noPermission = BookFactory  
  10.             .getPermissionInstance(new PermissionProxy("xyz"));  
  11.     noPermission.doCRUD();  

上述代码使用到CGLIB中的Enhancer功能,下面模拟需求再次发生变化:query任何人都可以操作,其余操作只有用户xxx具有权限。此时,若采用PermissionProxy,使得BookManager中所有方法都被代理,最容易想到的办法是,在PermissionProxy中的intercept方法中再做下判断,如果methodquery方法,不需要权限验证。这么做,一旦逻辑比较复杂,intercept方法要做的事情会很多,使得逻辑非常复杂。CGLIB提供了CallBackFilter接口,使用此接口,可明确表明被代理的类(BookManager)中的不同方法,被哪个拦截器拦截。写一实现CallBackFilter接口的类:

  1. public class PermissionProxyFilter implements CallbackFilter {  
  2.  
  3.     private static final int NEED_PERMISSION = 0;  
  4.     private static final int NO_NEED_PERMISSION = 1;  
  5.  
  6.     @Override 
  7.     public int accept(Method method) {  
  8.         if ("query".equals(method.getName())) {  
  9.             return NO_NEED_PERMISSION;  
  10.         }  
  11.         return NEED_PERMISSION;  
  12.     }  
  13.  

此时在BookFactory类中添加如下代码:

  1. /**  
  2. * 创建仅有查询权限的BookManager  
  3. */ 
  4. public static BookManager getQueryInstance(PermissionProxy proxy) {  
  5.     Enhancer enhancer = new Enhancer();  
  6.     enhancer.setSuperclass(BookManager.class);  
  7.     enhancer.setCallbacks(new Callback[] { proxy, NoOp.INSTANCE });  
  8.     enhancer.setCallbackFilter(new PermissionProxyFilter());  
  9.     return (BookManager) enhancer.create();  

注意:setCallBacks中的拦截器(interceptor)的顺序要和CallBackFilter中指定的顺序一致,NoOp.INSTANCECGLIB中的空拦截器。再写一单元测试方法:

  1. @Test 
  2. public void test3() {  
  3.     System.out.println("您仅有查询权限!!!");  
  4.     BookManager manager = BookFactory  
  5.         .getQueryInstance(new PermissionProxy("xyy"));  
  6.     manager.doCRUD();  

上面的例子,CGLIB在代码运行期,动态生成了BookManager的子类,并根据CallBackFilteraccept方法,覆盖了BookManager中的所有方法,去执行相应的MethodInterceptorintecept方法。