CGLIB 常用的 API 详解

CGLIB 常用的 API 详解一 概述 本文主要讲解的是 CGLIB 的常用 API 及其使用方式 使用的 CGLIB 依赖如下所示 lt dependency gt lt groupId gt cglib lt groupId gt lt artifactId gt cglib lt artifactId gt lt

大家好,我是讯享网,很高兴认识大家。

一、概述

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

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

讯享网

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

讯享网public interface CglibSampleInterface { 
    String show(String name); } public class CglibSampleClass { 
    public String show(String name) { 
    return String.format("%s show love to you!", name); } } 

二、API 详解

2.1 Enhancer

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

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()); } } 

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

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

  结果如下所示:

讯享网Robin walk to you! Robin walk to you! Robin walk to you! class club.wadreamer.cglib.CglibSampleClass$$EnhancerByCGLIB$$a2f7539 Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Number at club.wadreamer.cglib.CglibSampleClass$$EnhancerByCGLIB$$a2f7539.hashCode(<generated>) at club.wadreamer.cglib.demo.enhancer.EnhancerClass.main(EnhancerClass.java:35) 

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

public class EnhancerInterface { 
    public static void main(String[] args) throws Exception { 
    Enhancer enhancer = new Enhancer(); // 设置被代理的接口 enhancer.setInterfaces(new Class[]{ 
   CglibSampleInterface.class}); enhancer.setCallback((FixedValue) () -> "Robin walk to you!"); CglibSampleInterface cglibSampleInterface = (CglibSampleInterface) enhancer.create(); System.out.println(cglibSampleInterface.show("Robin")); System.out.println(cglibSampleInterface.show("Nami")); System.out.println(cglibSampleInterface.toString()); System.out.println(cglibSampleInterface.getClass()); System.out.println(cglibSampleInterface.hashCode()); } } 
2.2 Callback

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

讯享网package net.sf.cglib.proxy; public interface Callback { 
    } 
  • 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) 

  不过需要注意的是,所有对 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)); } } // 结果如下所示: 千鹤 
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 
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 
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 

三、扩展

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 

  从计数器的输出结果可以看到:即使实例化了 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()); } } 
小讯
上一篇 2025-02-14 22:05
下一篇 2025-03-28 11:11

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/17705.html