zl程序教程

您现在的位置是:首页 >  其他

当前栏目

CGLib简单入门

入门 简单 cglib
2023-06-13 09:11:20 时间

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

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

执行结果:

[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. public class CallbackTest {
  8. public void fun1(){
  9. System. out .println( “fun1 invoekd” );
  10. }
  11. public void fun2(){
  12. System. out .println( “fun2 invoekd” );
  13. }
  14. static class ClassA implements MethodInterceptor{
  15. public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
  16. System. out .println( “ClassA intercept invoked…” );
  17. return proxy.invokeSuper(obj, args);
  18. }
  19. }
  20. static class ClassB implements MethodInterceptor{
  21. public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
  22. System. out .println( “ClassB intercept invoked…” );
  23. return proxy.invokeSuper(obj, args);
  24. }
  25. }
  26. public Object createProxy(){
  27. Enhancer enhancer = new Enhancer();
  28. enhancer.setSuperclass(getClass());
  29. enhancer.setCallbacks( new Callback[]{ new ClassA(), new ClassB() });
  30. enhancer.setCallbackFilter( new CallbackFilter() {
  31. public int accept(Method method) {
  32. String methodName = method.getName();
  33. if( “fun1” .equals(methodName) ){
  34. return 0;
  35. } else {
  36. return 1;
  37. }
  38. }
  39. });
  40. return enhancer.create();
  41. }
  42. public static void main(String[] args) {
  43. CallbackTest test = new CallbackTest();
  44. CallbackTest obj = (CallbackTest)test.createProxy();
  45. obj.fun1();
  46. obj.fun2();
  47. }
  48. }

输出结果:

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. public class MixinTest {
  3. static interface Inter1 {
  4. void fun1(String arg0);
  5. }
  6. static interface Inter2 {
  7. void fun1(String arg0);
  8. void fun2(int arg0);
  9. }
  10. public static void main(String[] args) {
  11. Mixin mixin = Mixin. create( new Class[]{Inter1. class ,Inter2.class },
  12. new Object[]{
  13. new Inter1() {
  14. public void fun1(String arg0) {
  15. System.out .println(“Inter1 – “ + arg0);
  16. }
  17. },
  18. new Inter2() {
  19. public void fun1(String arg0) {
  20. System.out .println(“Inter1 – “ + arg0);
  21. }
  22. public void fun2( int arg0) {
  23. System.out .println(“Inter2 – “ + arg0);
  24. }
  25. }
  26. });
  27. Inter1 inter1 = (Inter1) mixin;
  28. inter1.fun1( “hello” );
  29. Inter2 inter2 = (Inter2) mixin;
  30. inter2.fun1( “world” );
  31. inter2.fun2(999);
  32. }
  33. }

输出结果:

Inter1 – hello Inter1 – world Inter2 – 999

我们查看一下classpath下编译之后的class文件

其中,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. class MixinTest$2
  9. implements MixinTest.Inter2
  10. {
  11. public void fun1(String arg0)
  12. {
  13. System.out.println(“Inter1 – “ + arg0);
  14. }
  15. public void fun2(int arg0) {
  16. System.out.println(“Inter2 – “ + arg0);
  17. }
  18. }

五、 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. public class BeanCopierTest {
  7. static class ClassA{
  8. private String username ;
  9. private String password ;
  10. private String score ;
  11. private List<String> list ;
  12. public String getUsername() {
  13. return username ;
  14. }
  15. public void setUsername(String username) {
  16. this .username = username;
  17. }
  18. public String getPassword() {
  19. return password ;
  20. }
  21. public void setPassword(String password) {
  22. this .password = password;
  23. }
  24. public String getScore() {
  25. return score ;
  26. }
  27. public void setScore(String score) {
  28. this .score = score;
  29. }
  30. public List<String> getList() {
  31. return list ;
  32. }
  33. public void setList(List<String> list) {
  34. this .list = list;
  35. }
  36. @Override
  37. public String toString() {
  38. return ToStringBuilder.reflectionToString( this , ToStringStyle.MULTI_LINE_STYLE );
  39. }
  40. }
  41. static class ClassB{
  42. private String username ;
  43. private String password ;
  44. private String address ;
  45. private List<Integer> list ;
  46. public String getUsername() {
  47. return username ;
  48. }
  49. public void setUsername(String username) {
  50. this .username = username;
  51. }
  52. public String getPassword() {
  53. return password ;
  54. }
  55. public void setPassword(String password) {
  56. this .password = password;
  57. }
  58. public String getAddress() {
  59. return address ;
  60. }
  61. public void setAddress(String address) {
  62. this .address = address;
  63. }
  64. public List<Integer> getList() {
  65. return list ;
  66. }
  67. public void setList(List<Integer> list) {
  68. this .list = list;
  69. }
  70. @Override
  71. public String toString() {
  72. return ToStringBuilder.reflectionToString( this , ToStringStyle.MULTI_LINE_STYLE );
  73. }
  74. }
  75. public static void main(String[] args) {
  76. BeanCopier beanCopier = BeanCopier.create(ClassA. class,ClassB. class ,false );
  77. List<String> list = Arrays. asList( new String[]{ “a” ,“b” ,“c” } );
  78. ClassA a = new ClassA();
  79. a.setUsername( “hello” );
  80. a.setPassword( “world” );
  81. a.setScore( “99” );
  82. a.setList(list);
  83. ClassB b = new ClassB();
  84. b.setUsername( “hello” );
  85. b.setPassword( “world” );
  86. b.setAddress( “beijing” );
  87. beanCopier.copy(a, b, null );
  88. System. out .println( a );
  89. System. out .println( b );
  90. }
  91. }

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

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