zoukankan      html  css  js  c++  java
  • CGLIB动态代理

    1、什么是CGlib

    CGlib是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。然这些实际的功能是asm所提供的,asm又是什么?Java字节码操控框架,具体是什么大家可以上网查一查,毕竟我们这里所要讨论的是cglib,cglib就是封装了asm,简化了asm的操作,实现了在运行期动态生成新的class。可能大家还感觉不到它的强大,现在就告诉你。实际上CGlib为spring aop提供了底层的一种实现;为hibernate使用cglib动态生成VO/PO (接口层对象)。

    它的原理就是用Enhancer生成一个原有类的子类,并且设置好callback , 则原有类的每个方法调用都会转成调用实现了MethodInterceptor接口的proxy的intercept() 函数:
    public Object intercept(Object o,Method method,Object[] args,MethodProxy proxy)
    在intercept()函数里,你可以在执行Object result=proxy.invokeSuper(o,args);来执行原有函数,在执行前后加入自己的东西,改变它的参数,也可以瞒天过海,完全干别的。说白了,就是AOP中的around advice。

    2、如何使用CGlib

    举个例子:比如DAO层有对表的增、删、改、查操作,如果要对原有的DAO层的增、删、改、查增加权限控制的话,修改代码是非常痛苦的。所以可以用AOP来实现。但是DAO层没有使用接口,动态代理不可用。这时候CGlib是个很好的选择。

    TableDao.java:

    package com.cglib; 
      
     public class TableDao { 
         public void create(){ 
             System.out.println("create() is running..."); 
         } 
         public void delete(){ 
             System.out.println("delete() is running..."); 
         } 
         public void update(){ 
             System.out.println("update() is running..."); 
         } 
         public void query(){ 
             System.out.println("query() is running..."); 
         } 
     }
    

      

    实现了MethodInterceptor接口的AuthProxy.java:用来对方法进行拦截,增加方法访问的权限控制,这里只允许张三访问。

    package com.cglib; 
      
     import java.lang.reflect.Method; 
      
     import net.sf.cglib.proxy.MethodInterceptor; 
     import net.sf.cglib.proxy.MethodProxy; 
     //方法拦截器 
     public class AuthProxy implements MethodInterceptor { 
         private String userName; 
         AuthProxy(String userName){ 
             this.userName = userName; 
         } 
         //用来增强原有方法 
         public Object intercept(Object arg0, Method arg1, Object[] arg2, 
                 MethodProxy arg3) throws Throwable { 
             //权限判断 
             if(!"张三".equals(userName)){ 
                 System.out.println("你没有权限!"); 
                 return null; 
             } 
             return arg3.invokeSuper(arg0, arg2); 
         } 
     } 
    

      

    TableDAOFactory.java:用来创建TableDao的子类的工厂类

    package com.cglib; 
      
     import net.sf.cglib.proxy.Callback; 
     import net.sf.cglib.proxy.Enhancer; 
     import net.sf.cglib.proxy.NoOp; 
      
     public class TableDAOFactory { 
         private static TableDao tDao = new TableDao();   
         public static TableDao getInstance(){   
             return tDao;   
         }   
         public static TableDao getAuthInstance(AuthProxy authProxy){   
             Enhancer en = new Enhancer();  //Enhancer用来生成一个原有类的子类 
             //进行代理   
             en.setSuperclass(TableDao.class);  
             //设置织入逻辑 
             en.setCallback(authProxy);   
             //生成代理实例   
             return (TableDao)en.create();   
         }  
      } 
    

      

    测试类Client.java:

    package com.cglib; 
      
     public class Client { 
      
         public static void main(String[] args) {   
     //        haveAuth();  
             haveNoAuth(); 
         }   
         public static void doMethod(TableDao dao){   
             dao.create();   
             dao.query();   
             dao.update();   
             dao.delete();   
         }   
         //模拟有权限 
         public static void haveAuth(){   
             TableDao tDao = TableDAOFactory.getAuthInstance(new AuthProxy("张三"));   
             doMethod(tDao);   
         }   
         //模拟无权限 
         public static void haveNoAuth(){   
             TableDao tDao = TableDAOFactory.getAuthInstance(new AuthProxy("李四"));   
             doMethod(tDao);   
         } 
     } 
    

      

    这样就能够对DAO层的方法进行权限控制了。但是如果又改需求了,要把DAO层的query方法让所有用户都可以访问,而其他方法照样有权限控制,该如何实现呢?这可难不倒我们了,因为我们使用了CGlib。当然最简单的方式是去修改我们的方法拦截器,不过这样会使逻辑变得复杂,且不利于维护。还好CGlib给我们提供了方法过滤器(CallbackFilter),CallbackFilte可以明确表明,被代理的类中不同的方法,被哪个拦截器所拦截。下面我们就来做个过滤器用来过滤query方法。

    AuthProxyFilter.java:

    package com.cglib; 
      
     import java.lang.reflect.Method; 
      
     import net.sf.cglib.proxy.CallbackFilter; 
     import net.sf.cglib.proxy.NoOp; 
      
     public class AuthProxyFilter implements CallbackFilter { 
      
         public int accept(Method arg0) { 
             /* 
              * 如果调用的不是query方法,则要调用authProxy拦截器去判断权限 
              */ 
             if(!"query".equalsIgnoreCase(arg0.getName())){ 
                 return 0; //调用第一个方法拦截器,即authProxy 
             } 
             /* 
              * 调用第二个方法拦截器,即NoOp.INSTANCE,NoOp.INSTANCE是指不做任何事情的拦截器 
              * 在这里就是任何人都有权限访问query方法,所以调用默认拦截器不做任何处理 
              */ 
             return 1;   
         } 
      
     } 
    

      

    至于为什么返回0或者1,注释讲的很详细。

    在TableDAOFactory.java里添加如下方法:

    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();   
        }   
     
    

      

    这里得注意,en.setCallbacks()方法里的数组参数顺序就是上面方法的返回值所代表的方法拦截器,如果return 0则使用authProxy拦截器,return 1则使用NoOp.INSTANCE拦截器,NoOp.INSTANCE是默认的方法拦截器,不做什么处理。

    下面在测试类中添加如下方法:

    //模拟权限过滤器 
        public static void haveAuthByFilter(){   
            TableDao tDao = TableDAOFactory.getAuthInstanceByFilter(new AuthProxy("张三"));   
            doMethod(tDao);   
            tDao = TableDAOFactory.getAuthInstanceByFilter(new AuthProxy("李四"));   
            doMethod(tDao);   
        }   
    

      

    在main方法中调用该方法,程序运行结果如下:

    create() is running...
    query() is running...
    update() is running...
    delete() is running...
    你没有权限!
    query() is running...
    你没有权限!
    你没有权限!

    这样的话,所有用户都对query方法有访问权限了,而其他方法只允许张三访问。

  • 相关阅读:
    [MSDN] How to Debug a Release Build
    抽象成员 虚方法
    强制类型转换符 和 as 运算符
    一份超长的MySQL学习笔记
    Java反射基础
    c3p0config.xml
    一个JDBC封装工具类
    Spring5——IOC操作Bean管理(基于xml文件)
    Android游戏开发大全
    移除项目里的所有.svn命令
  • 原文地址:https://www.cnblogs.com/deepbreath/p/4138999.html
Copyright © 2011-2022 走看看