CGLIB 是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为 JDK 的动态代理提供了很好的补充。通常可以使用Java的动态代理创建代理,但当要代理的类没有实现接口或者为了更好的性能,CGLIB 是一个好的选择。
CGLIB 作为一个开源项目,其代码托管在 Github,地址为:https://github.com/cglib/cglib
| 名称 | 解释 |
|---|---|
| CGLIB 原理 | 动态生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。它比使用java反射的JDK动态代理要快。 |
| CGLIB 底层 | 使用字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。 |
| CGLIB 缺点 | 对于final方法,无法进行代理。 |
广泛的被许多 AOP 的框架使用,例如 Spring AOP 和 dynaop 。Hibernate 使用 CGLIB 来代理单端single-ended(多对一和一对一)关联。
CGLIB 代理主要通过对字节码的操作,为对象引入间接级别,以控制对象的访问。我们知道 Java 中有一个动态代理也是做这个事情的,那我们为什么不直接使用 Java 动态代理,而要使用 CGLIB 呢?
答案是 CGLIB 相比于 JDK动态代理 更加强大,JDK动态代理 虽然简单易用,但是其有一个致命缺陷是,只能对接口进行代理。如果要代理的类为一个普通类、没有接口,那么 Java动态代理 就没法使用了。
Java动态代理机制 的出现,使得 Java开发人员 不用手工编写代理类,只要简单地制定一组接口及委托类对象,便能动态地获得代理类。代理类会负责将所有的方法调用分配到委托对象上反射执行,配置执行过程中,开发人员还可以进行修改。
代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息、过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。
要了解 Java 动态代理的机制,首先需要了解以下相关的类或接口:
java.lang.reflect.Proxy :这是 Java 动态代理机制的主类,它提供了一组静态方法来为一组接口动态地生成代理类及其对象java.lang.reflect.InvocationHandler :这是调用处理器接口,它自定义了一个 invoke 方法,用于几种处理在动态代理类对象上的方法调用。通常在该方法中实现对委托类的代理访问。java.lang.ClassLoader :Proxy 静态方法生成动态代理类同样需要通过类装载器来进行装载才能使用,它与普通类的唯一区别就是其字节码是由 JVM 在运行时动态生成的而非预存在于任何一个 .class 文件中。首先让我们来了解一下如何使用 Java 动态代理。具体有如下四步骤:
InvocationHandler 接口创建自己的调用处理器;ClassLoader 对象和一组 interface 来创建动态代理类;// InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发
// 其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用
InvocationHandler handler = new InvocationHandlerImpl(..); // 通过 Proxy 为包括 Interface 接口在内的一组接口动态创建代理类的类对象
Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... }); // 通过反射从生成的类对象获得构造函数对象
Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class }); // 通过构造函数对象创建动态代理类实例
Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler });
实际使用过程更加简单,因为 Proxy 的静态方法 newProxyInstance 已经为我们封装了步骤 2 到步骤 4 的过程,所以简化后的过程如下:
// InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发
InvocationHandler handler = new InvocationHandlerImpl(..); // 通过 Proxy 直接创建动态代理类实例
Interface proxy = (Interface)Proxy.newProxyInstance( classLoader, new Class[] { Interface.class }, handler );
动态生成的代理类本身的一些特点
public 的,那么它将被定义在顶层包(即包路径为空),如果所代理的接口中有 非 public 的接口(因为接口不能被定义为 protect 或 private ,所以除 public 之外就是默认的 package 访问级别,那么它将被定义在该接口所在包,这样设计的目的是为了最大程度的保证动态代理类不会因为包管理的问题而无法被成功定义并访问;final 和 public 修饰符,意味着它可以被所有的类访问,但是不能被再度继承;$ProxyN ,其中 N 是一个逐一递增的阿拉伯数字,代表 Proxy 类 第 N 次 生成的动态代理类,值得注意的一点是,并不是每次调用 Proxy 的静态方法创建动态代理类都会使得 N 值增加,原因是如果对同一组接口(包括接口排列的顺序相同)试图重复创建动态代理类,它会很聪明地返回先前已经创建好的代理类的类对象,而不会再尝试去创建一个全新的代理类,这样可以节省不必要的代码重复生成,提高了代理类的创建效率。
代理类实例的一些特点:
InvocationHandler (调用处理器对象),在代理类实例上调用其代理接口中声明的方法时,最终都会由 InvocationHandler 的 invoke方法 执行;java.lang.Object 中有三个方法也同样会被分派到调用处理器的 invoke 方法 执行,它们是 hashCode ,equals 和 toString;被代理接口的一组特点
Proxy 只能对 interface 进行代理,无法实现对 class 的动态代理。观察动态生成的代理继承关系图可知原因,他们已经有一个固定的父类叫做 Proxy ,Java语法 限定其不能再继承其他的父类。
最后以一个简单的动态代理例子结束
public class DynamicProxy {interface IHello{void sayHello();}static class Hello implements IHello{public void sayHello() {System.out.println("hello world");}}static class DynamicProxyTest implements InvocationHandler{Object originalObj;Object bind(Object originalObj){this.originalObj = originalObj;return Proxy.newProxyInstance(originalObj.getClass().getClassLoader(),originalObj.getClass().getInterfaces(), this);}public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Welcome");return method.invoke(originalObj, args);}}public static void main(String[] args){//设置这个值,在程序运行完成后,可以生成代理类System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");IHello hello = (IHello) new DynamicProxyTest().bind(new Hello());hello.sayHello();}
}
程序输出为:
Welcome
hello world

CGLIB 底层使用了ASM(一个短小精悍的字节码操作框架)来操作字节码生成新的类。除了 CGLIB库 外,脚本语言(如 Groovy 和 BeanShell )也使用 ASM 生成字节码。ASM 使用类似 SAX 的解析器来实现高性能。
| 名称 | 描述 |
|---|---|
| net.sf.cglib.core | 底层字节码处理类,他们大部分与 ASM 有关系 |
| net.sf.cglib.transform | 编译期或运行期类和类文件的转换 |
| net.sf.cglib.proxy | 实现创建代理和方法拦截器的类 |
| net.sf.cglib.reflect | 实现快速反射和C#风格代理的类 |
| net.sf.cglib.util | 集合排序等工具类 |
| net.sf.cglib.beans | JavaBean相关的工具类 |
说了这么多,可能大家还是不知道 CGLIB 是干什么用的。下面我们将使用一个简单的例子来演示如何使用 CGLIB 对一个方法进行拦截。
首先,我们需要在工程的 POM 文件中引入 CGLIB 的 dependency,这里我们使用的是 2.2.2 版本。
cglib cglib 2.2.2
依赖包下载后,我们就可以干活了,按照国际惯例,写个 hello world
public class SampleClass {public void test(){System.out.println("hello world");}public static void main(String[] args) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(SampleClass.class);enhancer.setCallback(new MethodInterceptor() {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("before method run...");Object result = proxy.invokeSuper(obj, args);System.out.println("after method run...");return result;}});SampleClass sample = (SampleClass) enhancer.create();sample.test();}
}
在 mian 函数中,我们通过一个 Enhancer 和一个 MethodInterceptor 来实现对方法的拦截,运行程序后输出为:
before method run...
hello world
after method run...
Object.getClass() 方法,这是由于 Java final 方法语义决定的。public class SampleClass {public String test(String input){return "hello world";}
}
下面我们将以这个类作为主要的测试类,来测试调用各种方法
@Test
public void testFixedValue(){Enhancer enhancer = new Enhancer();enhancer.setSuperclass(SampleClass.class);enhancer.setCallback(new FixedValue() {@Overridepublic Object loadObject() throws Exception {return "Hello cglib";}});SampleClass proxy = (SampleClass) enhancer.create();System.out.println(proxy.test(null)); //拦截test,输出Hello cglibSystem.out.println(proxy.toString()); System.out.println(proxy.getClass());System.out.println(proxy.hashCode());
}
输出:
Hello cglib
Hello cglib
class com.zeus.cglib.SampleClass$$EnhancerByCGLIB$$e3ea9b7java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Numberat com.zeus.cglib.SampleClass$$EnhancerByCGLIB$$e3ea9b7.hashCode()...
上述代码中,FixedValue 用来对所有拦截的方法返回相同的值,从输出我们可以看出来:
Enhancer 对 非final方法test()、toString()、hashCode()进行了拦截,没有对getClass进行拦截。
由于 hashCode() 方法需要返回一个 Number,但是我们返回的是一个 String,这解释了上面的程序中为什么会抛出异常。
Enhancer.setSuperclass 用来设置父类型,从 toString() 可以看出,使用 CGLIB 生成的类为被代理类的一个子类,形如:SampleClass$$EnhancerByCGLIB$$e3ea9b7
Enhancer.create(Object…) 方法是用来创建增强对象的,其提供了很多不同参数的方法用来匹配被增强类的不同构造方法。(虽然类的构造方法只是 Java字节码 层面的函数,但是 Enhancer 却不能对其进行操作。Enhancer 同样不能操作 static 或者 final 类)。
Enhancer.createClass() 来创建字节码( .class ),然后用字节码动态的生成增强后的对象。可以使用一个 InvocationHandler 作为回调,使用 invoke 方法来替换直接访问类的方法,但是你必须注意死循环。因为 invoke 中调用的任何原代理类方法,均会重新代理到 invoke 方法中。
public void testInvocationHandler() throws Exception{Enhancer enhancer = new Enhancer();enhancer.setSuperclass(SampleClass.class);enhancer.setCallback(new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if(method.getDeclaringClass() != Object.class && method.getReturnType() == String.class){return "hello cglib";}else{throw new RuntimeException("Do not know what to do");}}});SampleClass proxy = (SampleClass) enhancer.create();Assert.assertEquals("hello cglib", proxy.test(null));Assert.assertNotEquals("Hello cglib", proxy.toString());
}
为了避免这种死循环,我们可以使用 MethodInterceptor ,MethodInterceptor 的例子在前面的 hello world 中已经介绍过了,这里就不浪费时间了。
有些时候我们可能只想对特定的方法进行拦截,对其他的方法直接放行,不做任何操作,使用Enhancer处理这种需求同样很简单,只需要一个 CallbackFilter 即可:
@Test
public void testCallbackFilter() throws Exception{Enhancer enhancer = new Enhancer();CallbackHelper callbackHelper = new CallbackHelper(SampleClass.class, new Class[0]) {@Overrideprotected Object getCallback(Method method) {if(method.getDeclaringClass() != Object.class && method.getReturnType() == String.class){return new FixedValue() {@Overridepublic Object loadObject() throws Exception {return "Hello cglib";}};}else{return NoOp.INSTANCE;}}};enhancer.setSuperclass(SampleClass.class);enhancer.setCallbackFilter(callbackHelper);enhancer.setCallbacks(callbackHelper.getCallbacks());SampleClass proxy = (SampleClass) enhancer.create();Assert.assertEquals("Hello cglib", proxy.test(null));Assert.assertNotEquals("Hello cglib",proxy.toString());System.out.println(proxy.hashCode());
}
在编写程序时,有时候会调用许多 API 中实现实现的函数,但某些方法需要我们传入一个方法,以便在需要的时候调用我们传入进去的函数。这个被传入的函数称为回调函数(Callback function)。
打个比方,有一个餐馆,提供炒菜的服务,但是会让我们选择做菜的方式,我们去这家餐馆里面吃饭,想吃小龙虾,我们告诉他想吃小龙虾后,他询问我们要以何种方式去进行烹饪,是煎炒烹炸还是避风塘。
在上面的例子中,炒菜是我们需要调用的方法,也是 API 库中所提供的,而炒菜的方式,则是我们去选择的,可以我们自己去定义的。

这个就可以回调函数,有库函数(Librart function)来执行我们传入的回调函数(Callback function)
Callable接口
Interface Callable
在 Java1.8 官方文档中给出的内容为
参数类型:V - 回调方法的返回值类型
已经实现的子接口:DocumentationTool.DocumentationTask,JavaCompiler.CompilationTask
这个接口位函数试接口
@FunctionalInterface
public interface Callable
返回结果可能引发异常,这个接口与 Runnable 非常相似,这两个接口的设计可以在实例化后,开启新的线程,与 Runnable 的差别是,Runnable 不能返回参数也不能抛出异常。
例子:
import java.util.Random;
import java.util.concurrent.Callable;public class CallableExample implements Callable {@Overridepublic Object call() throws Exception {Random generator = new Random();Integer randomNumber = generator.nextInt(5);Thread.sleep(randomNumber * 1000);return randomNumber;}
}
测试:
@Test
public void callabledTest(){ExecutorService executorService = Executors.newCachedThreadPool();CallableExample callableExample = new CallableExample();Future
返回值:
3
Callback接口
已知实现此接口的类 AuthorizeCallback, ChoiceCallback, ConfirmationCallback, LanguageCallback, NameCallback, PasswordCallback, RealmCallback, RealmChoiceCallback, TextInputCallback, TextOutputCallback
这个接口的实现了会被传递给 CallbackHandler,允许有能力的底层服务去回应这个回调方法,已便进行诸如数据检索等信息。回调函数不检索或显示底层安全服务请求的信息。回调实现只是提供了将这些请求传递给应用程序的方法,并且对于应用程序,如果合适的话,可以将请求的信息返回给底层的安全服务。
这个接口是可以自己定义的,定制适用于当前业务的callback 接口类型来表示不同类型的回调函数。
callback接口的源码
public interface Callback { }
CallbackHandler 接口
方法:handle(Callback [] callbacks) ,这个方法是用来处理处理 callback 类型的
官方实例:
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {for (int i = 0; i < callbacks.length; i++) {if (callbacks[i] instanceof TextOutputCallback) {// display the message according to the specified typeTextOutputCallback toc = (TextOutputCallback)callbacks[i];switch (toc.getMessageType()) {case TextOutputCallback.INFORMATION:System.out.println(toc.getMessage());break;case TextOutputCallback.ERROR:System.out.println("ERROR: " + toc.getMessage());break;case TextOutputCallback.WARNING:System.out.println("WARNING: " + toc.getMessage());break;default:throw new IOException("Unsupported message type: " +toc.getMessageType());}} else if (callbacks[i] instanceof NameCallback) {// prompt the user for a usernameNameCallback nc = (NameCallback)callbacks[i];// ignore the provided defaultNameSystem.err.print(nc.getPrompt());System.err.flush();nc.setName((new BufferedReader(new InputStreamReader(System.in))).readLine());} else if (callbacks[i] instanceof PasswordCallback) {// prompt the user for sensitive informationPasswordCallback pc = (PasswordCallback)callbacks[i];System.err.print(pc.getPrompt());System.err.flush();pc.setPassword(readPassword(System.in));} else {throw new UnsupportedCallbackException(callbacks[i], "Unrecognized Callback");}}}// Reads user password from given input stream.private char[] readPassword(InputStream in) throws IOException {// insert code to read a user password from the input stream}
通过传入不同的已经实现了 Callback 接口的实现类,通过分析不同实现类的类型来进行不同的处理,调用形参实现类内的方法(回调)。
在一般工作中,我们都是自己定义接口,写实现类,来进行回调的。
自定义的回调函数实例
import java.util.List;public interface CallBackInterface {Object process(List list);
}
CallBackInterface 类型中的方法,来对字表进行操作import lombok.Data;import java.util.ArrayList;
import java.util.List;
@Data
public class WorldListHandler {List stringList = new ArrayList<>();public void execute(CallBackInterface callBackInterface){Object process = callBackInterface.process(stringList);System.out.println(process);}}
CallBackInterface 接口并实现它,来让 Handler 来调用它其中的 process 方法来完成对字表的筛选@Test
public void callableTest2(){List list = Arrays.asList("123","asd","1432","fsd","543","987","tre");WorldListHandler worldListHandler = new WorldListHandler();worldListHandler.setStringList(list);worldListHandler.execute(new CallBackInterface() {@Overridepublic Object process(List list) {List collect = list.stream().filter(e -> e.contains("1")).collect(Collectors.toList());worldListHandler.setStringList(collect);return true;}});worldListHandler.getStringList().forEach(e-> System.out.println(e));
}
结果:
true
123
1432
true 为 process 的返回值,剩下的为我们筛选出字表中包含有 1 的字符串。
通过名字就可以知道,不可变的 Bean 。ImmutableBean 允许创建一个原来对象的包装类,这个包装类是不可变的,任何改变底层对象的包装类操作都会抛出 IllegalStateException 。但是我们可以通过直接操作底层对象来改变包装类对象。这有点类似于 Guava 中的不可变视图。为了对 ImmutableBean 进行测试,这里需要再引入一个bean:
public class SampleBean {private String value;public SampleBean() {}public SampleBean(String value) {this.value = value;}public String getValue() {return value;}public void setValue(String value) {this.value = value;}
}
然后编写测试类如下:
@Test(expected = IllegalStateException.class)
public void testImmutableBean() throws Exception{SampleBean bean = new SampleBean();bean.setValue("Hello world");SampleBean immutableBean = (SampleBean) ImmutableBean.create(bean); //创建不可变类Assert.assertEquals("Hello world", immutableBean.getValue()); bean.setValue("Hello world, again"); // 可以通过底层对象来进行修改Assert.assertEquals("Hello world, again", immutableBean.getValue());immutableBean.setValue("Hello cglib"); // 直接修改将throw exception
}
要是报以下的错误:
java.lang.Exception: Unexpected exception, expected but wasat org.junit.internal.runners.statements.ExpectException.evaluate(ExpectException.java:30)at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)at org.junit.runners.ParentRunner.run(ParentRunner.java:413)at org.junit.runner.JUnitCore.run(JUnitCore.java:137)at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
Caused by: net.sf.cglib.core.CodeGenerationException: java.lang.reflect.InaccessibleObjectException-->Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @108c4c35at net.sf.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:464)at net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:339)at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:96)at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:94)at net.sf.cglib.core.internal.LoadingCache$2.call(LoadingCache.java:54)at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)at net.sf.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:61)at net.sf.cglib.core.internal.LoadingCache.get(LoadingCache.java:34)at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:119)at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:294)at net.sf.cglib.beans.ImmutableBean$Generator.create(ImmutableBean.java:70)at net.sf.cglib.beans.ImmutableBean.create(ImmutableBean.java:42)at cn.bugstack.springframework.test.ApiTest.testImmutableBean(ApiTest.java:39)at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:78)at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.base/java.lang.reflect.Method.invoke(Method.java:567)at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)at org.junit.internal.runners.statements.ExpectException.evaluate(ExpectException.java:19)... 17 more
请更换 JDK 的版本,这是由于 JDK 8 中有关反射相关的功能自从 JDK 9 开始就已经被限制了,为了兼容原先的版本,需要在运行项目时添加 --add-opens java.base/java.lang=ALL-UNNAMED 选项来开启这种默认不被允许的行为。如果是通过 IDEA 来运行项目,那么可以在 “Edit Configurations” 中 ——> “VM options” 输入框中输入该选项来完成,最终结果如下图所示:

结果:

CGLIB 提供的一个操作 bean 的工具,使用它能够在运行时动态的创建一个 bean 。
@Test
public void testBeanGenerator() throws Exception{BeanGenerator beanGenerator = new BeanGenerator();beanGenerator.addProperty("value", String.class);Object myBean = beanGenerator.create();Method setter = myBean.getClass().getMethod("setValue", String.class);setter.invoke(myBean, "Hello cglib");Method getter = myBean.getClass().getMethod("getValue");Assert.assertEquals("Hello cglib", getter.invoke(myBean));
}
在上面的代码中,我们使用 CGLIB 动态的创建了一个和 SampleBean 相同的 Bean 对象,包含一个属性 value 以及 getter 、 setter 方法。
结果:
CGLIB 提供的能够从一个 bean 复制到另一个 bean 中,而且其还提供了一个转换器,用来在转换的时候对 bean 的属性进行操作。
public class OtherSampleBean {private String value;public String getValue() {return value;}public void setValue(String value) {this.value = value;}
}
测试:
@Test
public void testBeanCopier() throws Exception{BeanCopier copier = BeanCopier.create(SampleBean.class, OtherSampleBean.class, false); //设置为true,则使用converterSampleBean myBean = new SampleBean();myBean.setValue("Hello cglib");OtherSampleBean otherBean = new OtherSampleBean();copier.copy(myBean, otherBean, null); //设置为true,则传入converter指明怎么进行转换assertEquals("Hello cglib", otherBean.getValue());
}
相比于 BeanCopier ,BulkBean 将 copy 的动作拆分为 getPropertyValues 和 setPropertyValues 两个方法,允许自定义处理属性
@Test
public void testBulkBean() throws Exception{BulkBean bulkBean = BulkBean.create(SampleBean.class, new String[]{"getValue"}, new String[]{"setValue"}, new Class[]{String.class});SampleBean bean = new SampleBean();bean.setValue("Hello world");Object[] propertyValues = bulkBean.getPropertyValues(bean);assertEquals(1, bulkBean.getPropertyValues(bean).length);assertEquals("Hello world", bulkBean.getPropertyValues(bean)[0]);bulkBean.setPropertyValues(bean, new Object[]{"Hello cglib"});assertEquals("Hello cglib", bean.getValue());
}
使用注意:
BukBean.create 创建对象,一般将其声明为 static 的get, set 操作,一般适用通过 xml 配置注入和注出的属性,运行时才确定处理的 Source, Target 类,只需要关注属性名即可。BeanMap 类实现了Java Map,将一个bean对象中的所有属性转换为一个 String-to-Obejct 的 Java Map
@Test
public void testBeanMap() throws Exception{BeanGenerator generator = new BeanGenerator();generator.addProperty("username", String.class);generator.addProperty("password", String.class);Object bean = generator.create();Method setUserName = bean.getClass().getMethod("setUsername", String.class);Method setPassword = bean.getClass().getMethod("setPassword", String.class);setUserName.invoke(bean, "admin");setPassword.invoke(bean, "password");BeanMap map = BeanMap.create(bean);Assert.assertEquals("admin", map.get("username"));Assert.assertEquals("password", map.get("password"));
}
keyFactory 类用来动态生成接口的实例,接口需要只包含一个 newInstance 方法,返回一个 Object 。keyFactory 为构造出来的实例动态生成了 Object.equals 和 Object.hashCode 方法,能够确保相同的参数构造出的实例为单例的。
public interface SampleKeyFactory {Object newInstance(String first, int second);
}
我们首先构造一个满足条件的接口,然后进行测试
@Test
public void testKeyFactory() throws Exception{SampleKeyFactory keyFactory = (SampleKeyFactory) KeyFactory.create(SampleKeyFactory.class);Object key = keyFactory.newInstance("foo", 42);Object key1 = keyFactory.newInstance("foo", 42);Assert.assertEquals(key,key1);//测试参数相同,结果是否相等
}
Mixin 能够让我们将多个对象整合到一个对象中去,前提是这些对象必须是接口的实现。可能这样说比较晦涩,以代码为例:
public class MixinInterfaceTest {interface Interface1{String first();}interface Interface2{String second();}class Class1 implements Interface1{@Overridepublic String first() {return "first";}}class Class2 implements Interface2{@Overridepublic String second() {return "second";}}interface MixinInterface extends Interface1, Interface2{}@Testpublic void testMixin() throws Exception{Mixin mixin = Mixin.create(new Class[]{Interface1.class, Interface2.class, MixinInterface.class}, new Object[]{new Class1(), new Class2()});MixinInterface mixinDelegate = (MixinInterface) mixin;assertEquals("first", mixinDelegate.first());assertEquals("second", mixinDelegate.second());}
}
Mixin 类比较尴尬,因为他要求 Minix 的类(例如 MixinInterface )实现一些接口。既然被 Minix 的类已经实现了相应的接口,那么我就直接可以通过纯 Java 的方式实现,没有必要使用 Minix 类。
用来模拟一个 String 到 int 类型的 Map 类型。如果在 Java7 以后的版本中,类似一个 switch 语句。
@Test
public void testStringSwitcher() throws Exception{String[] strings = new String[]{"one", "two"};int[] values = new int[]{10,20};StringSwitcher stringSwitcher = StringSwitcher.create(strings, values, true);assertEquals(10, stringSwitcher.intValue("one"));assertEquals(20, stringSwitcher.intValue("two"));assertEquals(-1, stringSwitcher.intValue("three"));
}
正如名字所言,Interface Maker 用来创建一个新的 Interface
@Test
public void testInterfaceMarker() throws Exception{Signature signature = new Signature("foo", Type.DOUBLE_TYPE, new Type[]{Type.INT_TYPE});InterfaceMaker interfaceMaker = new InterfaceMaker();interfaceMaker.add(signature, new Type[0]);Class iface = interfaceMaker.create();assertEquals(1, iface.getMethods().length);assertEquals("foo", iface.getMethods()[0].getName());assertEquals(double.class, iface.getMethods()[0].getReturnType());
}
上述的 Interface Maker 创建的接口中只含有一个方法,签名为 double foo(int)。Interface Maker与上面介绍的其他类不同,它依赖 ASM 中的 Type 类型。由于接口仅仅只用做在编译时期进行类型检查,因此在一个运行的应用中动态的创建接口没有什么作用。但是 InterfaceMaker 可以用来自动生成代码,为以后的开发做准备。
MethodDelegate 主要用来对方法进行代理
interface BeanDelegate{String getValueFromDelegate();
}
测试:
@Test
public void testMethodDelegate() throws Exception{SampleBean bean = new SampleBean();bean.setValue("Hello cglib");BeanDelegate delegate = (BeanDelegate) MethodDelegate.create(bean, "getValue", BeanDelegate.class);assertEquals("Hello cglib", delegate.getValueFromDelegate());
}
关于 Method.create 的参数说明:
缺点:
public interface DelegatationProvider {void setValue(String value);
}public class SimpleMulticastBean implements DelegatationProvider {private String value;@Overridepublic void setValue(String value) {this.value = value;}public String getValue() {return value;}
}
测试:
@Test
public void testMulticastDelegate() throws Exception{MulticastDelegate multicastDelegate = MulticastDelegate.create(DelegatationProvider.class);SimpleMulticastBean first = new SimpleMulticastBean();SimpleMulticastBean second = new SimpleMulticastBean();multicastDelegate = multicastDelegate.add(first);multicastDelegate = multicastDelegate.add(second);DelegatationProvider provider = (DelegatationProvider) multicastDelegate;provider.setValue("Hello world");assertEquals("Hello world", first.getValue());assertEquals("Hello world", second.getValue());
}
为了对构造函数进行代理,我们需要一个接口,这个接口只含有一个 Object newInstance(…) 方法,用来调用相应的构造函数
interface SampleBeanConstructorDelegate{Object newInstance(String value);
}
测试:
/*** 对构造函数进行代理* @throws Exception*/
@Test
public void testConstructorDelegate() throws Exception{SampleBeanConstructorDelegate constructorDelegate = (SampleBeanConstructorDelegate) ConstructorDelegate.create(SampleBean.class, SampleBeanConstructorDelegate.class);SampleBean bean = (SampleBean) constructorDelegate.newInstance("Hello world");assertTrue(SampleBean.class.isAssignableFrom(bean.getClass()));System.out.println(bean.getValue());
}
能够对多个数组同时进行排序,目前实现的算法有归并排序和快速排序
@Test
public void testParallelSorter() throws Exception{Integer[][] value = {{4, 3, 9, 0},{2, 1, 6, 0}};ParallelSorter.create(value).mergeSort(0);for(Integer[] row : value){int former = -1;for(int val : row){assertTrue(former < val);former = val;}}
}
顾明思义,FastClass 就是对 Class 对象进行特定的处理,比如通过数组保存 method 引用,因此 FastClass 引出了一个 index 下标的新概念,比如 getIndex(String name, Class[] parameterTypes) 就是以前的获取method的方法。通过数组存储method,constructor等class信息,从而将原先的反射调用,转化为 class.index 的直接调用,从而体现所谓的 FastClass
@Test
public void testFastClass() throws Exception{FastClass fastClass = FastClass.create(SampleBean.class);FastMethod fastMethod = fastClass.getMethod("getValue",new Class[0]);SampleBean bean = new SampleBean();bean.setValue("Hello world");assertEquals("Hello world",fastMethod.invoke(bean, new Object[0]));
}
由于 CGLIB 的大部分类是直接对Java字节码进行操作,这样生成的类会在Java的永久堆中。如果动态代理操作过多,容易造成永久堆满,触发 OutOfMemory 异常。
CGLIB相关的文章: