欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

转载:cglib动态代理介绍(一) 博客分类: 扩展包 cglib 

程序员文章站 2024-02-14 11:51:58
...

转载:http://blog.csdn.net/xiaohai0504/article/details/6832990

一、原理

      代理为控制要访问的目标对象提供了一种途径。当访问对象时,它引入了一个间接的层。JDK自从1.3版本开始,就引入了动态代理,并且经常被用来动态地创建代理。JDK的动态代理用起来非常简单,当它有一个限制,就是使用动态代理的对象必须实现一个或多个接口。如果想代理没有实现接口的继承的类,该怎么办?现在我们可以使用CGLIB包。

二、什么是cglib

     CGLIB是一个强大的高性能的代码生成包。

      1>它广泛的被许多AOP的框架使用,例如:Spring AOP和dynaop,为他们提供方法的interception(拦截);

       2>hibernate使用CGLIB来代理单端single-ended(多对一和一对一)关联(对集合的延迟抓取,是采用其他机制实现的);

       3>EasyMock和jMock是通过使用模仿(moke)对象来测试java代码的包。

     它们都通过使用CGLIB来为那些没有接口的类创建模仿(moke)对象。

三、底层

      CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM(Java字节码操控框架),来转换字节码并生成新的类。除了CGLIB包,脚本语言例如 Groovy和BeanShell,也是使用ASM来生成java的字节码。当不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。所以cglib包要依赖于asm包,需要一起导入。下图为cglib与一些框架和语言的关系(CGLIB Library and ASM Bytecode Framework)

转载:cglib动态代理介绍(一)
            
    
    博客分类: 扩展包 cglib 

      Spring AOP和Hibernate同时使用JDK的动态代理和CGLIB包。Spring AOP,如果不强制使用CGLIB包,默认情况是使用JDK的动态代理来代理接口。

四、实例场景模拟

1. 我们创建一个对Table操作的DAO类,提供了CRUD方法。 
    BookServiceBean.java

 

[java] view plaincopy
 
  1. package com.tech.cglibx;  
  2. public class BookServiceBean {  
  3.  public void create(){     
  4.         System.out.println("create() is running !");     
  5.     }     
  6.     public void query(){     
  7.         System.out.println("query() is running !");     
  8.     }     
  9.     public void update(){     
  10.         System.out.println("update() is running !");     
  11.     }     
  12.     public void delete(){     
  13.         System.out.println("delete() is running !");     
  14.     }     
  15. }  

 

 

    OK,它就是一个javaBean,提供了CRUD方法的javaBean。 
    下面我们创建一个DAO工厂,用来生成DAO实例。

 

[java] view plaincopy
 
  1. package com.tech.cglibx;  
  2. public class BookServiceFactory {  
  3.  private static BookServiceBean service = new BookServiceBean();  
  4.  private BookServiceFactory() {  
  5.  }  
  6.  public static BookServiceBean getInstance() {  
  7.   return service;  
  8.  }  
  9. }  

 

    接下来我们创建客户端,用来调用CRUD方法。

 

[java] view plaincopy
 
  1. public class Client {     
  2.     
  3.     public static void main(String[] args) {     
  4.         BookServiceBean service = BookServiceFactory.getInstance();   
  5.         doMethod(service);     
  6.     }     
  7.     public static void doMethod(BookServiceBean service){     
  8.         service.create();  
  9.         service.update();  
  10.         service.query();  
  11.         service.delete();   
  12.     }     
  13. }   

 

    OK,完成了,CRUD方法完全被调用了。

    当然这里并没有CGlib的任何内容。问题不会这么简单的就结束,新的需求来临了。

 
2. one day,Boss告诉我们这些方法不能开放给用户,只有“boss”才有权使用。怎么办,难道我们要在每个方法上面进行判断吗?好像这么做也太那啥了吧?对了,Proxy可能是最好的解决办法。jdk的代理就可以解决了。 好了我们来动手改造吧。等等jdk的代理需要实现接口,这样, 我们的dao类需要改变了。既然不想改动dao又要使用代理,我们这就请出CGlib。
我们只需新增一个权限验证的方法拦截器。

 

[java] view plaincopy
 
  1. package com.tech.cglibx;  
  2. import java.lang.reflect.Method;  
  3. import net.sf.cglib.proxy.Enhancer;  
  4. import net.sf.cglib.proxy.MethodInterceptor;  
  5. import net.sf.cglib.proxy.MethodProxy;  
  6. import org.apache.log4j.Logger;  
  7. public class MyCglibProxy implements MethodInterceptor{  
  8.  private Logger log=Logger.getLogger(MyCglibProxy.class);  
  9.  public Enhancer enhancer = new Enhancer();  
  10.  private String name;  
  11.    
  12.  public MyCglibProxy(String name) {  
  13.        this.name = name ;  
  14.  }  
  15.  /** 
  16.   * 根据class对象创建该对象的代理对象 
  17.   * 1、设置父类;2、设置回调 
  18.   * 本质:动态创建了一个class对象的子类 
  19.   *  
  20.   * @param cls 
  21.   * @return 
  22.   */  
  23.  public Object getDaoBean(Class cls) {  
  24.   enhancer.setSuperclass(cls);  
  25.   enhancer.setCallback(this);  
  26.   return enhancer.create();  
  27.  }  
  28.    
  29.  @Override  
  30.  public Object intercept(Object object, Method method, Object[] args,  
  31.    MethodProxy methodProxy) throws Throwable {  
  32.   log.info("调用的方法是:" + method.getName());  
  33.   //用户进行判断  
  34.   if(!"张三".equals(name)){   
  35.    System.out.println("你没有权限!");   
  36.    return null;   
  37.   }   
  38.   Object result = methodProxy.invokeSuper(object, args);  
  39.     
  40.   return result;  
  41.  }  
  42. }  

 

        当然不能忘了对我们的dao工厂进行修改,我们提供一个使用代理的实例生成方法。上面的类中已经提供了一个通用的获取代理实例的方法,没有特殊需求(如下3)的方式可以使用上面的方式获取代理对象。

 

[java] view plaincopy
 
  1. public static BookServiceBean getProxyInstance(MyCglibProxy myProxy){    
  2.      Enhancer en = new Enhancer();     
  3.      //进行代理     
  4.      en.setSuperclass(BookServiceBean.class);     
  5.      en.setCallback(myProxy);     
  6.      //生成代理实例     
  7.      return (BookServiceBean)en.create();     
  8.  } <span style="font-family: Arial, Verdana, sans-serif; white-space: normal; "> </span>  

 

     我们这就可以看看客户端的实现了。添加了两个方法用来验证不同用户的权限

 

[java] view plaincopy
 
  1. BookServiceBean service = BookServiceFactory.getProxyInstance(new MyCglibProxy("boss"));  
  2. service.create();  
  3. BookServiceBean service2 = BookServiceFactory.getProxyInstance(new MyCglibProxy("john"));  
  4. service2.create();  

 

 

转载:cglib动态代理介绍(一)
            
    
    博客分类: 扩展包 cglib 

OK,"boss"的正常执行,"john"的没有执行。 
看到了吗?简单的aop就这样实现了 
难道就这样结束了么? 

3.grd Boss又来训话了,不行不行,现在除了"boss"其他人都用不了了,现在不可以这样。必须使用开放查询功能。 
 哈哈,现在可难不倒我们了,因为我们使用了CGlib。当然最简单的方式是去修改我们的方法拦截器,不过这样会使逻辑变得复杂,且不利于维护。

还好CGlib给我们提供了方法过滤器(CallbackFilter),CallbackFilte可以明确表明,被代理的类中不同的方法,被哪个拦截器所拦截。

下面我们就来做个过滤器用来过滤query方法。 

 

[java] view plaincopy
 
  1. package com.tech.cglibx;  
  2. import java.lang.reflect.Method;  
  3. import net.sf.cglib.proxy.CallbackFilter;  
  4. public class MyProxyFilter implements CallbackFilter {  
  5.  @Override  
  6.  public int accept(Method arg0) {     
  7.         if(!"query".equalsIgnoreCase(arg0.getName()))     
  8.             return 0;     
  9.         return 1;     
  10.     }  
  11. }  

 

我们在工场中新增一个使用了过滤器的实例生成方法。 

 

[java] view plaincopy
 
  1. public static BookServiceBean getAuthInstanceByFilter(MyCglibProxy myProxy){    
  2.      Enhancer en = new Enhancer();     
  3.      en.setSuperclass(BookServiceBean.class);     
  4.      en.setCallbacks(new Callback[]{myProxy,NoOp.INSTANCE});     
  5.      en.setCallbackFilter(new MyProxyFilter());     
  6.      return (BookServiceBean)en.create();     
  7.  }  

 

   setCallbacks中定义了所使用的拦截器,其中NoOp.INSTANCE是CGlib所提供的实际是一个没有任何操作的拦截器, 
   他们是有序的,一定要和CallbackFilter里面的顺序一致。上面return返回(0/1)的就是返回的顺序。也就是说如果调用query方法就使用NoOp.INSTANCE进行拦截。
现在看一下客户端代码。

 

[java] view plaincopy
 
  1. BookServiceBean service = BookServiceFactory.getProxyInstanceByFilter(new MyCglibProxy("jhon"));  
  2.  service.create();  
  3.  BookServiceBean service2 = BookServiceFactory.getProxyInstanceByFilter(new MyCglibProxy("jhon"));  
  4.  service2.query();  

ok,现在"李四"也可以使用query方法了,其他方法仍然没有权限。 

当然这个代理的实现没有任何侵入性,无需强制让dao去实现接口。

相关标签: cglib