`

ASM 和 cglib

阅读更多

ASM
我们知道Java是静态语言,而python、ruby是动态语言,Java程序一旦写好很难在运行时更改类的行为,而python、ruby可以。
不过基于bytecode层面上我们可以做一些手脚,来使Java程序多一些灵活性和Magic,ASM就是这样一个应用广泛的开源库。

ASM is a Java bytecode manipulation framework. It can be used to dynamically generate stub classes or other proxy classes,
directly in binary form, or to dynamically modify classes at load time, i.e., just before they are loaded into the Java
Virtual Machine.

ASM完成了BCELSERP 同样的功能,但ASM
只有30多k,而后两者分别是350k和150k。apache真是越来越过气了。

让我们来看一个ASM的简单例子Helloworld.java,它生成一个Example类和一个main方法,main方法打印"Hello world!"语句:

Java代码  收藏代码
  1. import  java.io.FileOutputStream;  
  2. import  java.io.PrintStream;  
  3.   
  4. import  org.objectweb.asm.ClassWriter;  
  5. import  org.objectweb.asm.MethodVisitor;  
  6. import  org.objectweb.asm.Opcodes;  
  7. import  org.objectweb.asm.Type;  
  8. import  org.objectweb.asm.commons.GeneratorAdapter;  
  9. import  org.objectweb.asm.commons.Method;  
  10.   
  11. public   class  Helloworld  extends  ClassLoader  implements  Opcodes {  
  12.   
  13.   public   static   void  main( final  String args[])  throws  Exception {  
  14.   
  15.     // creates a ClassWriter for the Example public class,   
  16.     // which inherits from Object   
  17.   
  18.     ClassWriter cw = new  ClassWriter( 0 );  
  19.     cw.visit(V1_1, ACC_PUBLIC, "Example" null "java/lang/Object" null );  
  20.     MethodVisitor mw = cw.visitMethod(ACC_PUBLIC, "<init>" "()V" null ,  
  21.         null );  
  22.     mw.visitVarInsn(ALOAD, 0 );  
  23.     mw.visitMethodInsn(INVOKESPECIAL, "java/lang/Object" "<init>" "()V" );  
  24.     mw.visitInsn(RETURN);  
  25.     mw.visitMaxs(1 1 );  
  26.     mw.visitEnd();  
  27.     mw = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main" ,  
  28.         "([Ljava/lang/String;)V" null null );  
  29.     mw.visitFieldInsn(GETSTATIC, "java/lang/System" "out" ,  
  30.         "Ljava/io/PrintStream;" );  
  31.     mw.visitLdcInsn("Hello world!" );  
  32.     mw.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream" "println" ,  
  33.         "(Ljava/lang/String;)V" );  
  34.     mw.visitInsn(RETURN);  
  35.     mw.visitMaxs(2 2 );  
  36.     mw.visitEnd();  
  37.     byte [] code = cw.toByteArray();  
  38.     FileOutputStream fos = new  FileOutputStream( "Example.class" );  
  39.     fos.write(code);  
  40.     fos.close();  
  41.     Helloworld loader = new  Helloworld();  
  42.     Class exampleClass = loader  
  43.         .defineClass("Example" , code,  0 , code.length);  
  44.     exampleClass.getMethods()[0 ].invoke( null new  Object[] {  null  });  
  45.   
  46.     // ------------------------------------------------------------------------   
  47.     // Same example with a GeneratorAdapter (more convenient but slower)   
  48.     // ------------------------------------------------------------------------   
  49.   
  50.     cw = new  ClassWriter(ClassWriter.COMPUTE_MAXS);  
  51.     cw.visit(V1_1, ACC_PUBLIC, "Example" null "java/lang/Object" null );  
  52.     Method m = Method.getMethod("void <init> ()" );  
  53.     GeneratorAdapter mg = new  GeneratorAdapter(ACC_PUBLIC, m,  null null ,  
  54.         cw);  
  55.     mg.loadThis();  
  56.     mg.invokeConstructor(Type.getType(Object.class ), m);  
  57.     mg.returnValue();  
  58.     mg.endMethod();  
  59.     m = Method.getMethod("void main (String[])" );  
  60.     mg = new  GeneratorAdapter(ACC_PUBLIC + ACC_STATIC, m,  null null , cw);  
  61.     mg.getStatic(Type.getType(System.class ),  "out" , Type  
  62.         .getType(PrintStream.class ));  
  63.     mg.push("Hello world!" );  
  64.     mg.invokeVirtual(Type.getType(PrintStream.class ), Method  
  65.         .getMethod("void println (String)" ));  
  66.     mg.returnValue();  
  67.     mg.endMethod();  
  68.     cw.visitEnd();  
  69.     code = cw.toByteArray();  
  70.     loader = new  Helloworld();  
  71.     exampleClass = loader.defineClass("Example" , code,  0 , code.length);  
  72.     exampleClass.getMethods()[0 ].invoke( null new  Object[] {  null  });  
  73.   }  
  74. }  


我们看到上面的例子分别使用ASM的MethodVisitor和GeneratorAdapter两种方式来动态生成Example类并调用打印语句。

 

 

 

cglib
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依赖于ASM库。
Hibernate主要是利用cglib生成pojo的子类并override get方法来实现lazy loading机制,Spring则是利用cglib来实现动态代理。
而JDK的动态代理机制要求有接口才行,这样就强制我们的pojo实现某个接口。

这里还是提供一个cglib的入门级的示例:
MyClass.java:

Java代码  收藏代码
  1. public   class  MyClass {  
  2.   
  3.   public   void  print() {  
  4.     System.out.println("I'm in MyClass.print!" );  
  5.   }  
  6.   
  7. }  


Main.java:

Java代码  收藏代码
  1. import  java.lang.reflect.Method;  
  2. import  net.sf.cglib.proxy.Enhancer;  
  3. import  net.sf.cglib.proxy.MethodInterceptor;  
  4. import  net.sf.cglib.proxy.MethodProxy;  
  5.   
  6. public   class  Main {  
  7.   
  8.   public   static   void  main(String[] args) {  
  9.   
  10.     Enhancer enhancer = new  Enhancer();  
  11.     enhancer.setSuperclass(MyClass.class );  
  12.     enhancer.setCallback(new  MethodInterceptorImpl());  
  13.     MyClass my = (MyClass) enhancer.create();  
  14.     my.print();  
  15.   }  
  16.   
  17.   private   static   class  MethodInterceptorImpl  implements  MethodInterceptor {  
  18.     public  Object intercept(Object obj, Method method, Object[] args,  
  19.         MethodProxy proxy) throws  Throwable {  
  20.       // log something   
  21.       System.out.println(method + " intercepted!" );  
  22.   
  23.       proxy.invokeSuper(obj, args);  
  24.       return   null ;  
  25.     }  
  26.   }  
  27. }  


打印结果为:

Java代码  收藏代码
  1. public   void  MyClass.print() intercepted!  
  2. I'm in MyClass.print!  


这个示例就基本上实现了日志AOP的功能,很简单吧。

 

 

 

 

 

反射、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持久化的基础,但文档贫乏,一时还没去看怎么用。

1.AOP里讲了一百遍阿一百遍的log aspect在cglib是这样做的:


   
public   class  LogDAOProxy  implements  MethodInterceptor
   {
       
private  Logger log = Logger.getLogger(LogDAOProxy. class );
       
private  Enhancer enhancer = new  Enhancer();
        
// 返回DAO的子类
        public  Object getDAO(Class clz)
       {
           enhancer.setSuperclass(clz);
           enhancer.setCallback(
this );
           
return  enhancer.create();
       }
       
//默认 的拦截方法
       public  Object intercept(Object o,Method method,Object[] args,MethodProxy proxy)  throws  Throwable
      {
           log.info(
" 调用日志方法 " + method.getName());
           Object result
= proxy.invokeSuper(o,args);
           
return  result;
      }
   }


    应用的代码:

    LogDAOProxy proxy  =   new  LogDAOProxy();
    GoodsDAO  dao 
=  (GoodsDAO)proxy.getDAO(GoodsDAO. class );
    dao.insert(goods);


2.而在Spring的管理下应该略加修改的高级Decorator
   上面的例子用return  enhancer.create();创建子类实例,但在Spring管理下,一些Bean的实例必须由Spring来创建和管理,而不由enhancer来创建的。所以我对上述用法略加修改,使它真正当一个Proxy的角色,请对比黑体字的部分

   public   class  LogDAOProxy  implements  MethodInterceptor
  {
       
private  Logger log = Logger.getLogger(LogDAOProxy. class );
       
private  Object dao = null ;
       
private  Enhancer enhancer = new  Enhancer();
        
// 返回DAO的子类
        public  Object getDAO(Class clz,Object dao)
       {
           
this .dao  =  dao;
           enhancer.setSuperclass(clz);
           enhancer.setCallback(
this );
           
return  enhancer.create();
       }      
       
// 默认的拦截方法
       public  Object intercept(Object o,Method method,Object[] args,MethodProxy proxy)  throws  Throwable
      {
           log.info(
" 调用日志方法 " + method.getName());
           Object result
= proxy.invoke(dao, args);
           
return  result;
      }
  }


可见,原来模式里在getDao()时由enhancer创建dao,而 调用intercept时则将enhancer创建的dao以Object o参数传回。
而新模式里,dao在getDao()时从外面传入,enhancer.create()返回的是一个proxy. 而调用intercept时,实际会用之前传入的dao进行操作,而忽略Object o参数传入的proxy.

有点遗憾, intercept函数里MethodProxy的Signature是固定的 , 即客户如果调用foo(String),你不可以用proxy.invoke偷换成foo(String,String);

分享到:
评论
1 楼 paramore 2012-04-25  
给力  哇咔咔

相关推荐

    Asm和cglibjar包

    Asm和CGLIB是Java开发中的两个重要库,它们主要用于动态字节码操作和增强。在Java领域,字节码操作技术对于实现诸如代理、AOP(面向切面编程)、性能优化等高级功能至关重要。 **Asm库** Asm是一个Java字节码操控和...

    cglib和asm

    CGlib和ASM是Java开发中两个非常重要的库,主要用于字节码操作和动态代理。这两个库在Java生态系统中扮演着不可或缺的角色,特别是在AOP(面向切面编程)和ORM(对象关系映射)框架中。 CGlib是一个强大的、高性能...

    asm + cglib demo

    ASM 和 CGlib 都是Java字节码操作框架,它们在Java编程中有着重要的应用,尤其是在动态代理、AOP(面向切面编程)等领域。ASM是一个底层的字节码操作库,而CGlib是基于ASM的更高层次的库,简化了对Java字节码的操作...

    asm-cglib开发包

    ASM 和 CGLIB 是在Java开发中常用的两个库,它们主要与字节码操作和动态代理技术相关。这里我们将深入探讨这两个库的核心概念、功能以及它们在实际开发中的应用。 **ASM 库** ASM 是一个Java字节码操控和分析框架...

    Spring源码导入Eclipse缺失Jar包spring-asm-repack-5.0.4+spring-cglib-repack-3.1

    然而,将Spring源码导入Eclipse这样的集成开发环境(IDE)时,可能会遇到一些依赖问题,特别是对于某些特定的库如ASM和CGLIB。在本案例中,标题和描述提到的"Spring源码导入Eclipse缺失Jar包spring-asm-repack-5.0.4...

    java操作Excel环境easyExcel-2.0.5最新版本环境(自动适配属性),解决asm及cglib冲突等。亲测可用。

    java操作Excel环境easyExcel-2.0.5最新版本环境(自动适配属性),解决asm及cglib冲突等。亲测可用。asm.jar cglib-2.2.jar cglib-3.1.jar cglib-nodep-2.2.jar commons-collections4-4.1.jar easyexcel-2.0.5.jar ...

    cglib.jar | asm.jar对应版本

    其中,`cglib.jar` 和 `asm.jar` 是实现Java动态代理的两个关键库,它们在许多框架和库中都有广泛的应用,比如Spring AOP和Hibernate。 `cglib.jar` 是一个强大的代码生成库,全称为Code Generation Library。它...

    asm-2.2.3.jar,asm-commons-2.2.3.jar,asm-util-2.2.3.jar,cglib-nodep-2.1_3.jar

    这些文件是Java编程语言中用于动态代码生成和字节码操作的重要库,主要涉及ASM和CGLIB两个框架。ASM是一个轻量级的Java字节码操控和分析框架,而CGLIB是一个强大的代码生成库,它在许多情况下作为Spring框架的依赖...

    Java动态代理机制详解(JDK 和CGLIB,Javassist,ASM)

    CGLIB使用ASM库来生成和转换字节码,其核心类是`net.sf.cglib.proxy.Enhancer`,我们可以设置Enhancer的callback来实现方法拦截。 **Javassist**: Javassist是一个开源的分析、操作和转换Java字节码的库。相比于...

    CGLIB需要的asm-2.2.3.jar和cglib-nodep-2.2.jar

    总的来说,CGLIB和ASM是Java开发中的重要工具,它们提供了一种强大的方式来动态地创建和修改类,对于提升软件的灵活性和可扩展性有着不可忽视的作用。了解并熟练掌握这两个库的使用,对于提升Java开发者的技能水平和...

    Cglib和asm的jar包

    Cglib和ASM是Java开发中的两个重要库,主要用于字节码操作和动态代理。这两个库在处理高性能、低级别的代码生成和优化时扮演着重要角色。以下是对这两个库的详细解释: **Cglib(Code Generation Library)** Cglib...

    cglib2.2.2.jar和asm3.3.1.jar

    CGGLIB 2.2.2 和 ASM 3.3.1 是两个在Java开发中常用的库,尤其在处理动态代理和字节码操作时。它们在Java生态系统中扮演着重要角色,尤其对于那些需要深入操作类和方法的框架或库。 首先,让我们了解一下CGGLIB。...

    cglib包及依赖汉cglib3.1和asm4.2

    cglib包及依赖汉cglib3.1和asm4.2,主要作用是用户代理,代理为控制要访问的目标对象提供了一种途径。当访问对象时,它引入了一个间接的层。JDK自从1.3版本开始,就引入了动态代理,并且经常被用来动态地创建代理。...

    cglib-3.3.0.jar,asm-7.0.jar

    这里提到的"cglib-3.3.0.jar"和"asm-7.0.jar"是两个与Java动态代理密切相关的库。 首先,`cglib-3.3.0.jar`是Code Generation Library的简称,它是一个强大的高性能的代码生成库,广泛用于Java AOP(面向切面编程)...

    cglib 和asm jar包

    jdk动态代理是由Java内部的反射机制来实现的,cglib动态代理底层则是借助asm来实现的。总的来说,反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效(可以通过将asm生成的类进行缓存,...

    一套匹配的cglib和asm的jar包。

    本套"匹配的cglib和asm包"正是为了解决这个问题。它包含了已经经过验证的CGILib和ASM的兼容版本,确保在使用CGILib时不会出现由于版本不匹配导致的错误。这为开发者提供了一个方便的解决方案,避免了在寻找和测试...

    cglib.jar和cglib代理必备的asm所有jar

    总的来说,Cglib和ASM是Java开发中的利器,它们为我们提供了动态生成和修改字节码的能力,使得我们可以实现一些在编译时无法完成的功能,增强了代码的灵活性和可扩展性。对于想要深入理解Java运行机制以及提升开发...

    Cglib和ASM打包合集

    Cglib和ASM是Java开发中的两个重要库,主要用于动态代码生成和字节码操作。在Java中,当你需要在运行时创建新类或者增强已有类的功能时,这两个库就能发挥巨大作用。以下是对这两个库及其相关文件的详细解释: **...

    cglib.jar和asm.jar

    CGlib和ASM是两个在Java开发中至关重要的库,尤其在动态代理和字节码操作领域。本篇文章将深入探讨这两个库以及它们在代理模式中的应用。 首先,CGlib(Code Generation Library)是一个强大的高性能的代码生成库,...

Global site tag (gtag.js) - Google Analytics