一、概述
本文主要讲解的是 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 {
}
NoOpFixedValueInvocationHandlerMethodInterceptorDispatcherLazyLoader
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
InvocationHandler 即 net.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 Exception ,loadObject() 方法会在第一次被代理类的方法调用时触发,它返回一个被代理类的对象,这个对象会被存储起来然后负责所有被代理类方法的调用。
适用于被代理类或者代理类的对象的创建比较麻烦,且不确定它是否会被使用。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 的子类(该子类实现的转换方法中直接使用实例的 Getter 和 Setter 方法),拷贝速度极快(BeanCopier 属性拷贝比直接的 Setter、Getter 稍慢,原因在于首次需要动态生成 BeanCopier 的子类,一旦子类生成完成之后就和直接调用 Setter、Getter 效率一致,但是效率远远高于其他使用反射的工具类库)。示例如下所示:
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 下标的新概念。通过数组存储 method,constructor 等 class 信息,从而将原先的反射调用,转化为 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
从计数器的输出结果可以看到:即使实例化了 PictureAlbum,pictureContentList 的赋值只有在调用它的时候,才会通过 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()); } }

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