CGLib Enhancer 类介绍

本文将介绍 CGLib 中 Enhancer 类的作用,以及怎样使用该类(这里只是提供一个简单的例子,更多 Enhancer 的知识点后续章节逐一介绍)。

Enhancer 类是 CGLib 中最常用的一个类,和 JDK 1.3 动态代理中引入的 Proxy 类差不多(Proxy 类是 Java 动态代理机制的主类,它提供了一组静态方法来为一组接口动态地生成代理类及其对象)。和 Proxy 不同的是,Enhancer 类既能够代理普通的 class,也能够代理接口。Enhancer 创建一个被代理对象的子类并且拦截所有的方法调用(包括从 Object 中继承的 toString() 和 hashCode() 方法)。

注意:Enhancer 类不能够拦截 final 方法,例如:Object.getClass() 方法,这是由于 Java final 方法语义决定的。基于同样的道理,Enhancer 也不能对 fianl 类进行代理操作,这也是Hibernate为什么不能持久化 final class 的原因。

Enhancer 类

使用 Enhancer 类生成动态子类以启用方法拦截。这个类最初是作为 JDK 1.3 中包含的标准动态代理支持的替代品,但是除了实现接口之外,它还允许代理扩展一个具体的基类。动态生成的子类覆盖超类的非 final 方法,并具有回调到用户定义的拦截器实现的钩子。

CGLib 原始且最通用的回调类型是 MethodInterceptor,在 AOP 术语中,它支持 “around advice” -- 也就是说,您可以在调用 “super” 方法之前和之后调用自定义代码。此外,您可以在调用 super 方法之前修改参数,也可以根本不调用它。

尽管 MethodInterceptor 足够通用,可以满足任何拦截需要,但它往往过犹不及。为了简单和提高性能,还可以使用其他专用回调类型,如 LazyLoader。通常每个增强的类都会使用一个回调,但是您可以通过 CallbackFilter 控制每个方法使用哪个回调。

Enhancer 类的最常见用法体现在静态的帮助方法中。对于高级需求,例如自定义要使用的 ClassLoader,您应该创建一个新的 Enhancer 实例。CGLIB 中的其他类遵循类似的模式。

所有增强的对象都实现 Factory 接口,除非使用 setUseFactory(Boolean) 显式禁用此功能。Factory 接口提供了更改现有对象的回调的API,以及创建相同类型的新实例的更快、更简单的方法。

示例

使用 Enhancer 类动态创建 HelloWorld 类的代理对象,然后使用 Enhancer 的 setCallback() 方法设置一个回调钩子(见 FixedValue 类),对所有调用的非 final 且有返回值的方法返回一个固定值。

(1)HelloWorld.java 被代理的类

/**
 * 被代理的类
 * @author hxstrive.com 2021/12/29
 */
public class HelloWorld {

    public String test(){
        return "hello world";
    }

}

(2)CglibDemo.java 客户端实现

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.FixedValue;

/**
 * 验证 setCallback() 设置 FixedValue 类
 * @author hxstrive.com 2021/12/28
 */
public class CglibDemo {

    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(HelloWorld.class);
        enhancer.setCallback(new FixedValue() {
            @Override
            public Object loadObject() throws Exception {
                return "fixed Value";
            }
        });

        HelloWorld sample = (HelloWorld) enhancer.create();
        System.out.println("sample.test():\n" + sample.test());
        System.out.println("sample.toString():\n" + sample.toString());
        System.out.println("sample.getClass():\n" + sample.getClass());
        System.out.println("sample.hashCode():\n" + sample.hashCode());
    }

}

运行上面代码,输出结果如下:

sample.test():
fixed Value
sample.toString():
fixed Value
sample.getClass():
class com.hxstrive.cglib.demo1.HelloWorld$$EnhancerByCGLIB$$e01b5f18
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Number
at com.hxstrive.cglib.demo1.HelloWorld$$EnhancerByCGLIB$$e01b5f18.hashCode(<generated>)
at com.hxstrive.cglib.demo1.CglibDemo.main(CglibDemo.java:26)

下面将对上面结果进行逐一分析:

(1)调用 test() 方法,test() 方法返回一个字符串,因此这里直接返回 FixedValue 匿名类 loadObject() 方法返回的 “fixed Value” 字符串,运行正常。

(2)调用 toString() 方法,这里直接返回 FixedValue 匿名类 loadObject() 方法返回的 “fixed Value” 字符串,运行正常。

(3)调用 getClass() 方法,由于该方法没有返回值,因此在控制台没有输出 FixedValue 匿名类 loadObject() 方法返回的 “fixed Value” 字符串。

(4)调用 hashCode() 方法,由于该方法要求返回一个 int 类型的值。然而,FixedValue 匿名类 loadObject() 方法返回 “fixed Value” 字符串,直接抛出 “字符串不能转成数字” 错误。

关于更多 Enhancer 类的用法,请关注后续文章。

学习必须与实干相结合。 —— 泰戈尔
0 不喜欢
说说我的看法 -
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
公众号