CGLib简单入门

CGLib简单入门一、CGlib简介        CGlib是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。 当然这些实际的功能是asm所提供的,asm又是什么?Java字节码操控框架,具体是什么大家可以上网查一查,毕竟我们这里所要讨论的是cglib。cglib就是封装了asm,简化了asm的操作,实现了在运行期动态生成新的class。 可能大家还感觉不到它的

大家好,又见面了,我是你们的朋友全栈君。

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

二、CGlib之Enhancer和MethodInterceptor类

       Enhancer可以用来动态的生成一个类,这个类可以继承指定的一个类,实现指定的一些接口。
同时,Enhancer在生成一个类之前需要指定一个Callback,当类方法调用时,方法的执行被分配给这个Callback。
       MethodInterceptor是一个使用比较多的继承自Callback的接口,它只有一个方法声明。源码如下:

public interface MethodInterceptor extends Callback { 
  public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,MethodProxy proxy) throws  Throwable;  
} 
       我们再看一下 JDK自带的Invocationhandler接口中的方法声明:
public interface InvocationHandler {  
     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;  
} 


从参数构成上,methodInterceptor的输入参数比Invocationhandler多1个,其实前3个参数对象的含义与Invocationhandler的含义是相同的。
第一个参数表示调用方法来自哪个对象;
第二个参数表示调用方法的Method对象;
第三个参数表示此次调用的输入参数列表;
methodInterceptor多出来的参数是MethodProxy 类型的,它是cglib生成用来代替Method对象的一个对象,使用MethodProxy比调用JDK自身的Method直接执行方法效率会有提升。

 
代码示例:
Java代码  
收藏代码

  1. import java.lang.reflect.Method;  
  2.   
  3. import net.sf.cglib.proxy.Enhancer;  
  4. import net.sf.cglib.proxy.MethodInterceptor;  
  5. import net.sf.cglib.proxy.MethodProxy;  
  6.   
  7.   
  8. public class MyMethodInterceptor implements MethodInterceptor {  
  9.      
  10.     // 接口1  
  11.     static interface Inter1{  
  12.         public void fun1();  
  13.     }  
  14.     // 接口2  
  15.     static interface Inter2{  
  16.         public String fun2(String arg0);  
  17.     }  
  18.      
  19.     // 内部方法  
  20.     public String myFun1(String arg0){  
  21.         return “hello,” + arg0 ;  
  22.     }  
  23.      
  24.     public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {  
  25.         String methodName = method.getName();  
  26.          
  27.         if“fun1” .equals(methodName) ){  
  28.             System. out .println( “[intercept] fun1 invoked” );  
  29.             return null;  
  30.         } else if ( “fun2” .equals(methodName) ){  
  31.             System. out .println( “[intercept] fun2 invoked before” );  
  32.             String result = (String)args[0] + “…” ;  
  33.             System. out .println( result );  
  34.             System. out .println( “[intercept] fun2 invoked after” );  
  35.             return result;  
  36.         } else if ( “myFun1” .equals(methodName) ){  
  37.             System. out .println( “[intercept] myFun1 invoked before” );  
  38.             Object result = proxy. invokeSuper(obj, args);  
  39.             System. out .println( result );  
  40.             System. out .println( “[intercept] myFun1 invoked after” );  
  41.             return result;  
  42.         }  
  43.              
  44.         return null;  
  45.     }  
  46.      
  47.     public Object createProxy(){  
  48.         Enhancer enhancer = new Enhancer();  
  49.         enhancer.setSuperclass(MyMethodInterceptor. class );  
  50.         enhancer.setInterfaces( new Class[]{Inter1. class,Inter2. class});  
  51.         enhancer.setCallback( this );  
  52.         return enhancer.create();  
  53.     }  
  54.      
  55.      
  56.     public static void main(String[] args) {  
  57.         MyMethodInterceptor ss = new MyMethodInterceptor();  
  58.         Object proxy = ss.createProxy();  
  59.          
  60.         // 接口  
  61.         Inter1 inter1 = (Inter1)proxy;  
  62.         inter1.fun1();  
  63.          
  64.         Inter2 inter2 = (Inter2)proxy;  
  65.         inter2.fun2( “code generate library” );  
  66.          
  67.         // 类  
  68.         MyMethodInterceptor c1 = (MyMethodInterceptor)proxy;  
  69.         c1.myFun1( “cglib” );  
  70.          
  71.     }  
  72.   
  73. }  
  74.  
CGLib简单入门

 执行结果:
[intercept] fun1 invoked
[intercept] fun2 invoked before
code generate library…
[intercept] fun2 invoked after
[intercept] myFun1 invoked before
hello,cglib
[intercept] myFun1 invoked after

        我们看到,在intercept()方法中,对调用函数名进行了判断,并进行了不同处理。可以再方法执行之前、之后做一些我们想做的事情,甚至是修改输入参数、输出参数。

        MethodProxy在对执行函数的时候,提供了2个方法
Java代码  
收藏代码

  1. public Object invoke (Object obj, Object[] args) throws Throwable  
  2. public Object invokeSuper(Object obj, Object[] args) throws Throwable  
      我们看到,在例子中使用的是invokeSuper()方法,因为动态生成的类是子类或者是实现类,因此invokeSuper就是执行父类中方法的意思。
那么invoke()方法是做什么的。javadoc上说这个方法可以用于相同类中的其他对象的方法执行,也就是说这个方法中的obj需要传入相同一个类的另一个对象,否则会进入无限递归循环。
       通过这个简单的例子,我们可以看到cglib可以动态的生成一个代理,而且这种方法比JDK的动态代理更强大,因为JDK的动态代理,需要代理的类实现某个接口,而cglib没有这个要求,因为它可以直接生成指定类的子类,同时支持实现接口的方式。cglib提供的MethodProxy的执行效率高于JDK自带的反射。

 
三、 CallbackFilter
        一个Enhancer生成类可以指定多个Callback,这样对于每次调用有哪个Callback,就需要指定一个CallbackFilter的策略。
Java代码  
收藏代码

  1. import java.lang.reflect.Method;  
  2. import net.sf.cglib.proxy.Callback;  
  3. import net.sf.cglib.proxy.CallbackFilter;  
  4. import net.sf.cglib.proxy.Enhancer;  
  5. import net.sf.cglib.proxy.MethodInterceptor;  
  6. import net.sf.cglib.proxy.MethodProxy;  
  7.   
  8. public class CallbackTest {  
  9.      
  10.     public void fun1(){  
  11.         System. out .println( “fun1 invoekd” );  
  12.     }  
  13.      
  14.     public void fun2(){  
  15.         System. out .println( “fun2 invoekd” );  
  16.     }  
  17.      
  18.     static class ClassA implements MethodInterceptor{  
  19.         public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {  
  20.             System. out .println( “ClassA intercept invoked…” );  
  21.             return proxy.invokeSuper(obj, args);  
  22.         }  
  23.     }  
  24.      
  25.     static class ClassB implements MethodInterceptor{  
  26.         public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {  
  27.             System. out .println( “ClassB intercept invoked…” );  
  28.             return proxy.invokeSuper(obj, args);  
  29.         }  
  30.     }  
  31.      
  32.     public Object createProxy(){  
  33.         Enhancer enhancer = new Enhancer();  
  34.         enhancer.setSuperclass(getClass());  
  35.         enhancer.setCallbacks( new Callback[]{ new ClassA(), new ClassB() });  
  36.         enhancer.setCallbackFilter( new CallbackFilter() {  
  37.              
  38.             public int accept(Method method) {  
  39.                 String methodName = method.getName();  
  40.                 if“fun1” .equals(methodName) ){  
  41.                     return 0;  
  42.                 } else {  
  43.                     return 1;  
  44.                 }  
  45.             }  
  46.         });  
  47.         return enhancer.create();  
  48.     }  
  49.      
  50.     public static void main(String[] args) {  
  51.         CallbackTest test = new CallbackTest();  
  52.          
  53.         CallbackTest obj = (CallbackTest)test.createProxy();  
  54.          
  55.         obj.fun1();  
  56.         obj.fun2();  
  57.          
  58.     }  
  59.   
  60. }  
  61.   
CGLib简单入门

 输出结果:

ClassA intercept invoked…
fun1 invoekd
ClassB intercept invoked…
fun2 invoekd
      我们可以看到,CallbackFilter类的accept()方法的返回值是int类型的,它用来指示此次指派的Callback的次序,从0开始,注意这个返回值必须小于当前指定的Callback的总个数。
四、Mixin
        Mixin可以对多个对象进行代理,需要同时指定多个接口或者多个接口对应的代理对象。

Java代码  
收藏代码

  1. import net.sf.cglib.proxy.Mixin;  
  2.   
  3. public class MixinTest {  
  4.   
  5.     static interface Inter1 {  
  6.   
  7.         void fun1(String arg0);  
  8.     }  
  9.   
  10.     static interface Inter2 {  
  11.         void fun1(String arg0);  
  12.         void fun2(int arg0);  
  13.     }  
  14.   
  15.     public static void main(String[] args) {  
  16.         Mixin mixin = Mixin. create( new Class[]{Inter1. class ,Inter2.class },  
  17.                                    new Object[]{  
  18.                                       new Inter1() {  
  19.                                         public void fun1(String arg0) {  
  20.                                             System.out .println(“Inter1 – “ + arg0);  
  21.                                         }  
  22.                                     },  
  23.                                     new Inter2() {  
  24.                                         public void fun1(String arg0) {  
  25.                                             System.out .println(“Inter1 – “ + arg0);  
  26.                                         }  
  27.                                         public void fun2( int arg0) {  
  28.                                             System.out .println(“Inter2 – “ + arg0);  
  29.                                         }  
  30.                                     }  
  31.                                    });  
  32.          
  33.         Inter1 inter1 = (Inter1) mixin;  
  34.         inter1.fun1( “hello” );  
  35.          
  36.         Inter2 inter2 = (Inter2) mixin;  
  37.         inter2.fun1( “world” );  
  38.         inter2.fun2(999);  
  39.          
  40.     }  
  41. }  

 输出结果:
Inter1 – hello
Inter1 – world
Inter2 – 999

 我们查看一下classpath下编译之后的class文件
CGLib简单入门
        其中,Mixintest$inter1.class和Mixintest$inter2.class是2个内部接口,
生成的2个类是MixinTest$1.class和MixinTest$2.class,也就是说其实CGLIB没有为这2个代理对象生成1个类,而是生成了2个类,
反编译一下这2个类,我们可以看到生成的Mixin对象其实是引用了这两个类对象。

Java代码  
收藏代码

  1. class MixinTest$1  implements MixinTest.Inter1  
  2. {  
  3.   public void fun1(String arg0)  
  4.   {  
  5.     System.out.println(“Inter1 – “ + arg0);  
  6.   }  
  7. }  
  8.   
  9. class MixinTest$2  
  10.   implements MixinTest.Inter2  
  11. {  
  12.   public void fun1(String arg0)  
  13.   {  
  14.     System.out.println(“Inter1 – “ + arg0);  
  15.   }  
  16.   public void fun2(int arg0) {  
  17.     System.out.println(“Inter2 – “ + arg0);  
  18.   }  
  19. }    

 

五、 BeanCopier
        BeanCopier可以实现Bean之间的属性同名属性拷贝。

Java代码  
收藏代码

  1. import java.util.Arrays;  
  2. import java.util.List;  
  3. import net.sf.cglib.beans.BeanCopier;  
  4. import org.apache.commons.lang.builder.ToStringBuilder;  
  5. import org.apache.commons.lang.builder.ToStringStyle;  
  6.   
  7. public class BeanCopierTest {  
  8.      
  9.     static class ClassA{  
  10.         private String username ;  
  11.         private String password ;  
  12.         private String score ;  
  13.          
  14.         private List<String> list ;  
  15.          
  16.         public String getUsername() {  
  17.             return username ;  
  18.         }  
  19.         public void setUsername(String username) {  
  20.             this .username = username;  
  21.         }  
  22.         public String getPassword() {  
  23.             return password ;  
  24.         }  
  25.         public void setPassword(String password) {  
  26.             this .password = password;  
  27.         }  
  28.         public String getScore() {  
  29.             return score ;  
  30.         }  
  31.         public void setScore(String score) {  
  32.             this .score = score;  
  33.         }  
  34.         public List<String> getList() {  
  35.             return list ;  
  36.         }  
  37.         public void setList(List<String> list) {  
  38.             this .list = list;  
  39.         }  
  40.         @Override  
  41.         public String toString() {  
  42.             return ToStringBuilder.reflectionToString( this , ToStringStyle.MULTI_LINE_STYLE );  
  43.         }  
  44.     }  
  45.      
  46.     static class ClassB{  
  47.         private String username ;  
  48.         private String password ;  
  49.         private String address ;  
  50.         private List<Integer> list ;  
  51.          
  52.         public String getUsername() {  
  53.             return username ;  
  54.         }  
  55.         public void setUsername(String username) {  
  56.             this .username = username;  
  57.         }  
  58.         public String getPassword() {  
  59.             return password ;  
  60.         }  
  61.         public void setPassword(String password) {  
  62.             this .password = password;  
  63.         }  
  64.         public String getAddress() {  
  65.             return address ;  
  66.         }  
  67.         public void setAddress(String address) {  
  68.             this .address = address;  
  69.         }  
  70.         public List<Integer> getList() {  
  71.             return list ;  
  72.         }  
  73.         public void setList(List<Integer> list) {  
  74.             this .list = list;  
  75.         }  
  76.         @Override  
  77.         public String toString() {  
  78.             return ToStringBuilder.reflectionToString( this , ToStringStyle.MULTI_LINE_STYLE );  
  79.         }  
  80.     }  
  81.      
  82.     public static void main(String[] args) {  
  83.          
  84.         BeanCopier beanCopier = BeanCopier.create(ClassA. class,ClassB. class ,false );  
  85.          
  86.         List<String> list = Arrays. asList( new String[]{ “a” ,“b” ,“c” } );  
  87.          
  88.         ClassA a = new ClassA();  
  89.         a.setUsername( “hello” );  
  90.         a.setPassword( “world” );  
  91.         a.setScore( “99” );  
  92.         a.setList(list);  
  93.          
  94.         ClassB b = new ClassB();  
  95.         b.setUsername( “hello” );  
  96.         b.setPassword( “world” );  
  97.         b.setAddress( “beijing” );  
  98.          
  99.         beanCopier.copy(a, b, null );  
  100.          
  101.         System. out .println( a );  
  102.         System. out .println( b );  
  103.          
  104.     }  
  105.   
  106. }    
CGLib简单入门

       我们可以看到,对于2个对象中的同名属性 username 和 password进行了拷贝,并且对内部的符合属性List a 也进行了复制,但是,通过debug我们可以发现,内部的符合属性其实并没有实现copy。
因为他们在内存中实现上是同一个对象,对于内置复合对象的拷贝,需要寻找其他途径。而且,虽然ClassA中是List<String>,ClassB中是List<Integer>,在赋值的时候并没有抛出异常,也就是仅仅是内存上的赋值成功,并没有进行繁星检查,这也充分说明Java的泛型为伪泛型,在运行时会“泛型”会消失。但是如果此后在调用赋值之后的泛型不支持的方法时,就可能会遇到运行时异常。这会是一个安全隐患。

CGLib简单入门
 
 
 

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/137889.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号