zoukankan      html  css  js  c++  java
  • cglib常用api

    reference:https://blog.csdn.net/weixin_41427129/article/details/113561980

    一、概述

      本文主要讲解的是 CGLIB 的常用 API 及其使用方式。使用的 CGLIB 依赖如下所示:

    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.3.0</version>
    </dependency>
    View Code

      首先创建接口 CglibSampleInterface 和类 CglibSampleClass,如下所示:

    1 public interface CglibSampleInterface {
    2     String show(String name);
    3 }
    4 
    5 public class CglibSampleClass {
    6     public String show(String name) {
    7         return String.format("%s show love to you!", name);
    8     }
    9 }
    View Code

    二、API 详解

    2.1 Enhancer

      Enhancer 即字节码增强器,它和 JDK 动态代理中的 Proxy 类似,是 CGLIB 中最常用的一个类,既能代理普通的 Java 类,也能代理接口。Enhancer 通过创建一个被代理类的子类来拦截所有的方法调用(包括 Object#toString()Object#hashCode()),但是它不能拦截 final 修饰的方法(如 Object#getClass()),也不能代理 final 修饰的类。示例如下所示:

     1 public class EnhancerClass {
     2     public static void main(String[] args) throws Exception {
     3         // 字节码增强器
     4         Enhancer enhancer = new Enhancer();
     5         // 设置代理类的父类
     6         enhancer.setSuperclass(CglibSampleClass.class);
     7         // 使用 FixedValue,拦截返回值,每次返回固定值 "Robin walk to you!"
     8         enhancer.setCallback((FixedValue) () -> "Robin walk to you!");
     9 
    10         // 创建代理对象
    11         CglibSampleClass sampleClass = (CglibSampleClass) enhancer.create();
    12 
    13         System.out.println(sampleClass.show("Robin"));
    14         System.out.println(sampleClass.show("Nami"));
    15         System.out.println(sampleClass.toString());
    16 
    17         // 无法对 final 修饰的 getClass() 方法进行拦截
    18         System.out.println(sampleClass.getClass());
    19         // 因为 hashCode() 需要返回的是 Number 类型,但是 FixedValue 返回值是 String 类型,无法实现类型转换,故会抛出异常
    20         System.out.println(sampleClass.hashCode());
    21     }
    22 }
    View Code

      上述示例使用 FixedValue() 拦截所有的方法调用(包括非 final 修饰的 show()toString()hashCode() 方法)并返回相同的值,但是,由于 hashCode() 方法的返回值类型是 int 型,而我们返回的是一个 String,所以才会抛出 ClassCastException 异常。

      此外,create(Class[] argumentTypes, Object[] arguments) 也可创建代理对象,用来匹配被增强类的不同构造方法,第一参数表示构造方法的参数类型,第二个参数表示构造方法的参数值,这两个参数都是数组类型。也可以使用 Enhancer#createClass() 来创建类的字节码,然后使用字节码加载完成后的类动态生成代理对象。

      结果如下所示:

    public class EnhancerClass {
        public static void main(String[] args) throws Exception {
            // 字节码增强器
            Enhancer enhancer = new Enhancer();
            // 设置代理类的父类
            enhancer.setSuperclass(CglibSampleClass.class);
            // 使用 FixedValue,拦截返回值,每次返回固定值 "Robin walk to you!"
            enhancer.setCallback((FixedValue) () -> "Robin walk to you!");
    
            // 创建代理对象
            CglibSampleClass sampleClass = (CglibSampleClass) enhancer.create();
    
            System.out.println(sampleClass.show("Robin"));
            System.out.println(sampleClass.show("Nami"));
            System.out.println(sampleClass.toString());
    
            // 无法对 final 修饰的 getClass() 方法进行拦截
            System.out.println(sampleClass.getClass());
            // 因为 hashCode() 需要返回的是 Number 类型,但是 FixedValue 返回值是 String 类型,无法实现类型转换,故会抛出异常
            System.out.println(sampleClass.hashCode());
        }
    }
    View Code

      代理接口的示例如下所示,结果与上面的相同。

     1 public class EnhancerInterface {
     2     public static void main(String[] args) throws Exception {
     3         Enhancer enhancer = new Enhancer();
     4 
     5         // 设置被代理的接口
     6         enhancer.setInterfaces(new Class[]{CglibSampleInterface.class});
     7 
     8         enhancer.setCallback((FixedValue) () -> "Robin walk to you!");
     9 
    10         CglibSampleInterface cglibSampleInterface = (CglibSampleInterface) enhancer.create();
    11 
    12         System.out.println(cglibSampleInterface.show("Robin"));
    13         System.out.println(cglibSampleInterface.show("Nami"));
    14         System.out.println(cglibSampleInterface.toString());
    15         System.out.println(cglibSampleInterface.getClass());
    16         System.out.println(cglibSampleInterface.hashCode());
    17     }
    18 }

    2.2 Callback

      Callback 即回调,其接口如下所示,是一个标识接口(不含任何方法)。它的回调时机是被代理类的方法被调用的时候,即被代理类的方法被调用时,Callback 的实现逻辑就会被调用。此外,可通过 Enhancer#setCallback()Enhancer#setCallbacks() 设置 Callback若设置了多个 Callback,则会按照设置的顺序进行回调。CGLIB 提供了以下几种 Callback 的子类:

    1 package net.sf.cglib.proxy;
    2 
    3 public interface Callback {
    4 }
    • NoOp
    • FixedValue
    • InvocationHandler
    • MethodInterceptor
    • Dispatcher
    • LazyLoader
    2.2.1 NoOp

      NoOp 即 No Operation,不做任何操作,该回调实现只是简单地将方法调用委托给被代理类的原始方法,即不加任何操作地调用原始类的原始方法,因此,该回调实现也不能做接口代理。实例如下所示:

    public class NoOpDemo {
        public static void main(String[] args) throws Exception {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(CglibSampleClass.class);
    
            // 设置 Callback 回调为 NoOp
            enhancer.setCallback(NoOp.INSTANCE);
    
            CglibSampleClass sampleClass = (CglibSampleClass) enhancer.create();
            System.out.println(sampleClass.show("Robin"));
        }
    }
    
    // 结果如下所示:
    Robin show love to you!
     
    2.2.2 FixedValue

      FixedValue 即固定值。它提供了一个 loadObject() 方法并返回一个原方法调用想要的固定结果。此外,该 Callback 中看不到任何原方法的信息,也就没有调用原方法的逻辑。需要注意的是,loadObject() 方法的返回值并不能转换成原方法的返回值类型,则会抛出类型转换异常 (ClassCastException)。示例即前面两个 Enhancer 的 Demo。

    2.2.3 InvocationHandler

      InvocationHandlernet.sf.cglib.proxy.InvocationHandler,它和 JDK 动态代理中 java.lang.reflect.InvocationHandler 的功能类似,同样也提供了如下的一个方法:

    Object invoke(Object proxy, Method method, Object[] objects)
     
    • 1

      不过需要注意的是,所有对 proxy 对象的方法调用都会被委托给同一个 InvocationHandler,所以可能会导致无限循环 (因为 invoke 中调用的任何被代理类的方法,均会重新代理到 invoke() 中)

    public class InvocationHandlerDeadLoopDemo {
        public static void main(String[] args) throws Exception {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(CglibSampleClass.class);
    
            // 设置 Callback 的子类 InvocationHandler
            enhancer.setCallback(new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] objects) throws Throwable {
                	// 错误做法,会重新调用 InvocationHandler 的 invoke()
                    // return method.invoke(proxy, objects); 
                    
                    if (!Objects.equals(method.getDeclaringClass(), Object.class) && Objects.equals(String.class, method.getReturnType())) {
                        return "Nami fall in love!";
                    }
                    return "No one fall in love with you!";
                }
            });
    
            CglibSampleClass sampleClass = (CglibSampleClass) enhancer.create();
            System.out.println(sampleClass.show("Robin"));
        }
    }
    
    // 结果如下所示:
    Nami fall in love!
     
    2.2.4 MethodInterceptor

      MethodInterceptor,即方法拦截器,它可以实现类似于 AOP 编程中的环绕增强(Around Advice)。它只有一个方法:

    public Object intercept(Object proxy, // 代理对象
    						java.lang.reflect.Method method, // 方法
    						Object[] args, // 方法参数
    						MethodProxy methodProxy // 方法代理
    						) throws Throwable
     

      设置了 MethodInterceptor 后,代理类的所有方法调用都会转而执行这个接口中的 intercept 方法而不是原方法。若需要在 intercept 方法中执行原方法,有以下两种方式:

    • 使用参数method 基于代理对象 proxy 进行反射调用,但是使用方法代理 methodProxy 效率会更高(methodProxy 基于整数数字的索引来直接调用方法)。
    • 使用 MethodProxy 调用 invokeSuper() 执行原方法,这种方式效率更好,推荐使用这种方式。

      需要注意的是,使用 MethodProxy#invokeSuper() 相当于通过方法代理直接调用原类的对应方法,若调用 MethodProxy#invoke() 会进入死循环导致爆栈,原因跟 InvocationHandler 差不多。

    2.2.5 Dispatcher

      Dispatcher 即分发器,提供了一个 Object loadObject() throws Exception 方法,每次对增强对象进行方法调用都会回调 Dispatcher#loadObject() 方法并返回一个被代理类的对象来调用原方法。Dispatcher 可以类比为 Spring 中的 Prototype 类型。示例如下所示:

    public class DispatcherDemo {
        private static final AtomicInteger COUNTER = new AtomicInteger(0);
    
        public static void main(String[] args) throws Exception {
            // 被代理接口的对象
            CglibSampleInterfaceImpl impl = new CglibSampleInterfaceImpl();
    
            Enhancer enhancer = new Enhancer();
            enhancer.setInterfaces(new Class[]{CglibSampleInterface.class});
            enhancer.setCallback(new Dispatcher() {
                @Override
                public Object loadObject() throws Exception {
                    COUNTER.incrementAndGet();
                    // 返回的被代理接口的对象
                    return impl;
                }
            });
            CglibSampleInterface cglibSampleInterface = (CglibSampleInterface) enhancer.create();
    
            System.out.println(cglibSampleInterface.show("Robin"));
            System.out.println(cglibSampleInterface.show("Nami"));
            System.out.println(COUNTER.get());
        }
    
        private static class CglibSampleInterfaceImpl implements CglibSampleInterface {
    
            public CglibSampleInterfaceImpl() {
                System.out.println("CglibSampleInterfaceImpl init...");
            }
    
            @Override
            public String show(String name) {
                return String.format("%s show love to you!", name);
            }
        }
    }
    
    // 结果如下所示:
    CglibSampleInterfaceImpl init...
    Robin show love to you!
    Nami show love to you!
    2
     

      如输出结果所示,计数器的结果为 2,可以验证该结论:每次调用方法都会回调 Dispatcher 中的实例进行调用。

    2.2.6 LazyLoader

      LazyLoader 即懒加载器,它只提供了一个方法 Object loadObject() throws ExceptionloadObject() 方法会在第一次被代理类的方法调用时触发,它返回一个被代理类的对象,这个对象会被存储起来然后负责所有被代理类方法的调用。

      适用于被代理类或者代理类的对象的创建比较麻烦,且不确定它是否会被使用。LazyLoader 可以类比为 Spring 中 Lazy 模式的 Singleton示例如下所示:

    public class LazyLoaderDemo {
        private static final AtomicInteger COUNTER = new AtomicInteger(0);
    
        public static void main(String[] args) throws Exception {
            // 被代理接口的对象
            CglibSampleInterfaceImpl impl = new CglibSampleInterfaceImpl();
    
            Enhancer enhancer = new Enhancer();
            enhancer.setInterfaces(new Class[]{CglibSampleInterface.class});
            enhancer.setCallback(new LazyLoader() {
                @Override
                public Object loadObject() throws Exception {
                    COUNTER.incrementAndGet();
                    // 返回被代理接口的对象
                    return impl;
                }
            });
            CglibSampleInterface cglibSampleInterface = (CglibSampleInterface) enhancer.create();
            System.out.println(cglibSampleInterface.show("Robin"));
            System.out.println(cglibSampleInterface.show("Nami"));
            System.out.println(COUNTER.get());
        }
    
        private static class CglibSampleInterfaceImpl implements CglibSampleInterface {
    
            public CglibSampleInterfaceImpl() {
                System.out.println("CglibSampleInterfaceImpl init...");
            }
    
            @Override
            public String show(String name) {
                return String.format("%s show love to you!", name);
            }
        }
    }
    
    // 结果如下所示:
    CglibSampleInterfaceImpl init...
    Robin show love to you!
    Nami show love to you!
    1
     

      如输出结果所示,计数器的结果为 1,可以验证该结论:LazyLoader 中的实例只回调了1次。

    2.3 BeanCopier

      BeanCopier 即 JavaBean 属性拷贝器,提供从一个 JavaBean 实例中拷贝属性到另一个 JavaBean 实例中的功能,类型必须完全匹配,属性才能拷贝成功(基本数据类型和其包装类不属于相同类型)。它还提供了一个 net.sf.cglib.core.Converter 转换器回调接口让使用者控制拷贝的过程。

      此外,BeanCopier 内部使用了缓存和基于 ASM 动态生成 BeanCopier 的子类(该子类实现的转换方法中直接使用实例的 GetterSetter 方法),拷贝速度极快BeanCopier 属性拷贝比直接的 SetterGetter 稍慢,原因在于首次需要动态生成 BeanCopier 的子类,一旦子类生成完成之后就和直接调用 SetterGetter 效率一致,但是效率远远高于其他使用反射的工具类库)。示例如下所示:

    public class BeanCopierDemo {
    	
    	// 缓存 BeanCopier 实例,BeanCopier 生成是一个耗时的操作
        private static final Map<String, BeanCopier> CACHE = new ConcurrentHashMap<>();
    
        public static void main(String[] args) throws Exception {
        
            //这里 useConverter 设置为 false,调用 copy 方法的时候不能传入转换器实例
            BeanCopier beanCopier;
            String key = generateCacheKey(Person.class, Person.class);
            if (CACHE.containsKey(key)) {
                beanCopier = CACHE.get(key);
            } else {
                beanCopier = BeanCopier.create(Person.class, Person.class, false);
                CACHE.put(key, beanCopier);
            }
            
            Person person = new Person();
            person.setId(10086L);
            person.setName("Robin");
            person.setAge(25);
            
            Person newPerson = new Person();
            beanCopier.copy(person, newPerson, null); //这里转换器实例要传 null
            System.out.println(newPerson);
        }
    
        private static String generateCacheKey(Class<?> source, Class<?> target) {
            return String.format("%s-%s", source.getName(), target.getName());
        }
    
        @ToString
        @Data
        private static class Person {
            private Long id;
            private String name;
            private Integer age;
        }
    }
    
    // 结果如下所示:
    BeanCopierDemo.Person(id=10086, name=throwable, age=25)
     

    2.4 ImmutableBean

      ImmutableBean 即不可变的 Bean,它可以创建一个对象的包装类,但这个包装类是不可变的,否则会抛出 IllegalStateException 异常,但是可以通过操作底层对象来改变包装类的对象。示例如下所示:

    public class ImmutableBeanDemo {
    
        public static void main(String[] args) throws Exception {
            Person person = new Person();
            person.setName("波雅汉考克");
    
            Person immutablePerson = (Person) ImmutableBean.create(person);
            System.out.println(immutablePerson.getName());
    
    		// 通过修改底层对象来改变包装类的对象
            person.setName("白星公主");
            System.out.println(immutablePerson.getName());
    
    		// 此处修改了包装类的对象,会抛出异常
            immutablePerson.setName("蕾贝卡");
            System.out.println(immutablePerson.getName());
        }
    
        @Data
        private static class Person {
            private String name;
        }
    }
    
    // 结果如下所示:
    波雅汉考克
    白星公主
    Exception in thread "main" java.lang.IllegalStateException: Bean is immutable
     

    2.5 BeanGenerator

      BeanGenerator 即 Bean 生成器,它能够在运行时动态的创建一个JavaBean。可以直接设置父类,生成的 JavaBean 就是父类类型的实例。示例如下所示:

    public class BeanGeneratorDemo {
    
        public static void main(String[] args) throws Exception {
            BeanGenerator beanGenerator = new BeanGenerator();
    
            // 添加 JavaBean 的属性及其类型
            beanGenerator.addProperty("name", String.class);
    
            Object target = beanGenerator.create();
    
            Method setter = target.getClass().getDeclaredMethod("setName", String.class);
            Method getter = target.getClass().getDeclaredMethod("getName");
    
            // 设置属性的值
            setter.invoke(target, "千鹤");
    
            System.out.println(getter.invoke(target));
        }
    }
    
    // 结果如下所示:
    千鹤
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    2.6 BeanMap

      BeanMap 类实现了 JDK 的 java.util.Map 接口,它可以将一个 JavaBean 对象中的所有属性转换为一个 <String, Object> 的Map实例。示例如下所示:

    public class BeanMapDemo {
        public static void main(String[] args) throws Exception {
            Person person = new Person();
            person.setName("Nami");
    
            BeanMap beanMap = BeanMap.create(person);
    
            System.out.println(beanMap);
            System.out.println(beanMap.get("name"));
        }
    
        @Data
        private static class Person {
            private String name;
        }
    }
    
    // 结果如下所示:
    {name=Nami}
    Nami
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    2.7 Mixin

      Mixin 能够将多个接口的多个实现合并到同一个接口的单个实现中。示例如下所示:

    public class MixinDemo {
    
        interface InterfaceFirst {
            String first();
        }
    
        interface InterfaceSecond {
            String second();
        }
    
        static class ImplFirst implements InterfaceFirst {
            @Override
            public String first() {
                return "First one";
            }
        }
    
        static class ImplSecond implements InterfaceSecond {
            @Override
            public String second() {
                return "Second one";
            }
        }
    
        interface MixinImpl extends InterfaceFirst, InterfaceSecond {
    
        }
    
        public static void main(String[] args) throws Exception {
    
            Mixin mixin = Mixin.create(
                    new Class[]{InterfaceFirst.class, InterfaceSecond.class, MixinImpl.class}, // 接口数组
                    new Object[]{new ImplFirst(), new ImplSecond()} // 代理对象数组
                    );
    
            MixinImpl mixinImpl = (MixinImpl) mixin;
    
            System.out.println(mixinImpl.first());
            System.out.println(mixinImpl.second());
        }
    }
    
    // 结果如下所示:
    First one
    Second one
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    2.8 FastClass

      FastClass 就是对 Class 对象进行特定的处理,可以理解为索引类,比如通过数组保存 method 引用,因此 FastClass 引出了一个 index 下标的新概念。通过数组存储 methodconstructorclass 信息,从而将原先的反射调用,转化为 class.index 的直接调用以提高效率,从而体现所谓的 FastClass。此外, 在接口或者代理类的方法比较少的时候,使用 FastClass 进行方法调用有可能比原生反射方法调用 Method#invoke() 的效率高。示例如下所示:

    public class FastClassDemo {
        public static void main(String[] args) throws Exception {
    
            FastClass fastClass = FastClass.create(CglibSampleClass.class);
            FastMethod fastMethod = fastClass.getMethod("show", new Class[]{String.class});
    
            CglibSampleClass cglibSampleClass = new CglibSampleClass();
    
            // 使用 FastMethod 进行调用
            System.out.println(fastMethod.invoke(cglibSampleClass, new Object[]{"Robin"}));
            // 获得方法的下标索引 index
            System.out.println(fastMethod.getIndex());
        }
    }
    
    // 结果如下所示:
    Robin show love to you!
    0
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    三、扩展

    3.1 LazyLoader 实现延迟加载

    public class LazyLoaderExt {
    
        // 计数器
        private static final AtomicInteger COUNTER = new AtomicInteger(0);
    
        public static class PictureAlbum {
            private String topic;
    
            private List<PictureContent> pictureContentList;
    
            public PictureAlbum() {
                this.topic = "海贼王图片集";
                this.pictureContentList = getPictureContentList();
            }
    
            private List<PictureContent> getPictureContentList() {
                Enhancer enhancer = new Enhancer();
                enhancer.setSuperclass(List.class);
    
                enhancer.setCallback(new LazyLoader() {
                    @Override
                    public Object loadObject() throws Exception {
                        List<PictureContent> list = new ArrayList<>();
                        list.add(new PictureContent("Nami"));
                        list.add(new PictureContent("Lufei"));
                        list.add(new PictureContent("波雅汉考克"));
                        COUNTER.getAndIncrement();
                        return list;
                    }
                });
    
                return (List<PictureContent>) enhancer.create();
            }
    
        }
    
        // 图片实体
        public static class PictureContent {
            private String name;
    
            public PictureContent(String name) {
                this.name = name;
            }
    
            public String getName() {
                return name;
            }
    
            public void setName(String name) {
                this.name = name;
            }
        }
    
        public static void main(String[] args) {
        	// 实例化 PictureAlbum
            PictureAlbum pictureAlbum = new PictureAlbum();
            System.out.println(pictureAlbum.topic);
    
            System.out.println("COUNTER ==> " + COUNTER.get());
    
            System.out.println("=====图片名=====");
            for (PictureContent pictureContent : pictureAlbum.pictureContentList) {
                System.out.println(pictureContent.name);
            }
    
            System.out.println("COUNTER ==> " + COUNTER.get());
        }
    }
    
    // 结果如下所示:
    海贼王图片集
    COUNTER ==> 0
    =====图片名=====
    Nami
    Lufei
    波雅汉考克
    COUNTER ==> 1
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77

      从计数器的输出结果可以看到:即使实例化了 PictureAlbumpictureContentList 的赋值只有在调用它的时候,才会通过 LazyLoader#loadObject 方法去赋值。

    3.2 Dispathcer 扩展类的接口

      该示例中有 UserService 类、IMethodInfo 接口以及该接口的实现类 DefaultMethodInfo,在这里,我们通过 CGLIB 创建一个代理类,该代理类的父类是 UserService,且实现了 IMethodInfo 接口,将接口 IMethodInfo 中所有的方法都转发给 DefaultMethodInfo 处理,代理类中的其他方法转发给父类 UserService 处理。

      简而言之,就是对 UserService 进行了增强,使其具有 IMethodInfo 接口中的功能。

    public class DispatcherExt {
    
        public static class UserService {
            public void add() {
                System.out.println("新增用户");
            }
    
            public void update() {
                System.out.println("更新用户信息");
            }
        }
    
        // 用来获取方法信息的接口
        public interface IMethodInfo {
            // 获取方法数量
            int methodCount();
    
            // 获取被代理的对象中方法名称列表
            List<String> methodNames();
        }
    
        // IMethodInfo 的默认实现
        public static class DefaultMethodInfo implements IMethodInfo {
    
            private Class<?> targetClass;
    
            public DefaultMethodInfo(Class<?> targetClass) {
                this.targetClass = targetClass;
            }
    
            @Override
            public int methodCount() {
                return targetClass.getDeclaredMethods().length;
            }
    
            @Override
            public List<String> methodNames() {
                return Arrays.stream(targetClass.getDeclaredMethods()).
                        map(Method::getName).
                        collect(Collectors.toList());
            }
        }
    
        public static void main(String[] args) {
            Class<?> targetClass = UserService.class;
            Enhancer enhancer = new Enhancer();
    
            // 设置代理的父类
            enhancer.setSuperclass(targetClass);
    
            // 设置代理需要实现的接口列表
            enhancer.setInterfaces(new Class[]{IMethodInfo.class});
    
            // 创建一个方法统计器,即 IMethodInfo 的默认实现类
            IMethodInfo methodInfo = new DefaultMethodInfo(targetClass);
    
            // 创建回调用器列表
            Callback[] callbacks = {
                    // 处理 UserService 中所有的方法
                    new MethodInterceptor() {
                        @Override
                        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                            return methodProxy.invokeSuper(o, objects);
                        }
                    },
                    // 处理 IMethodInfo 接口中的方法
                    new Dispatcher() {
                        @Override
                        public Object loadObject() throws Exception {
                            /**
                             * 用来处理代理对象中 IMethodInfo 接口中的所有方法
                             * 所以此处返回的为 IMethodInfo 类型的对象,
                             * 将由这个对象来处理代理对象中 IMethodInfo 接口中的所有方法
                             */
                            return methodInfo;
                        }
                    }
            };
    
            enhancer.setCallbacks(callbacks);
            enhancer.setCallbackFilter(new CallbackFilter() {
                @Override
                public int accept(Method method) {
                    // 当方法在 IMethodInfo 中定义的时候,返回 callbacks 中的第二个元素
                    return method.getDeclaringClass() == IMethodInfo.class ? 1 : 0;
                }
            });
    
            Object proxy = enhancer.create();
    
            //代理的父类是UserService
            UserService userService = (UserService) proxy;
            userService.add();
    
            //代理实现了IMethodInfo接口
            IMethodInfo mf = (IMethodInfo) proxy;
            System.out.println(mf.methodCount());
            System.out.println(mf.methodNames());
        }
    }
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
     
  • 相关阅读:
    Did not find handler method for springMVC资源文件扫描不到---关于spring的那些坑
    mysql中OPTIMIZE TABLE的作用
    Linux环境下apache性能测试工具ab使用详解
    sqlite数据库 adb 从配置到查询表中数据全过程-----献给初学的自己
    c3p0参数解释
    linux下如何启动/停止/重启mysql:
    [MySQL] 变量(参数)的查看和设置
    mysql运行参数详解
    单例模式 理解,简单通透
    this的一些场景
  • 原文地址:https://www.cnblogs.com/sunupo/p/15497528.html
Copyright © 2011-2022 走看看