cglib_cglib:缺少的手册

cglib

字节码检测库cglib在许多众所周知的Java框架(例如Hibernate (现在不再 )或Spring )中很受欢迎,它们可以完成肮脏的工作。 字节码检测允许在Java应用程序的编译阶段之后操作或创建类。 由于Java类是在运行时动态链接的,因此可以将新类添加到已经运行的Java程序中。 Hibernate例如使用cglib生成动态代理。 Hibernate不会返回您存储在数据库中的完整对象,而是会返回存储类的检测版本,该版本仅在需要时才从数据库延迟加载某些值。 例如,在向方法调用添加安全约束时,Spring使用了cglib。 Spring安全性不会直接调用您的方法,而是会首先检查指定的安全性检查是否通过,并且仅在此验证之后委托给您的实际方法。 cglib的另一种流行用法是在诸如mockito之类的模拟框架内,其中模拟只不过是插装类 ,在插装类中,方法被空的实现(加上一些跟踪逻辑)所替代。

除了ASM (另一个基于cglib的非常高级的字节代码操作库)之外,cglib还提供了相当低级的字节代码转换器,即使不了解已编译的Java类的详细信息,也可以使用它们。 不幸的是,cglib的文档很短,并不是说基本上没有。 除了2005年发表的一篇博客文章演示了Enhancer类外,没有什么可找的。 这篇博客文章是试图演示cglib及其不幸的是经常尴尬的API。

增强剂

让我们从Enhancer类(cglib库中最常用的类)开始。 增强程序允许为非接口类型创建Java代理。 可以将Enhancer器与Java标准库的Proxy类(在Java 1.3中引入)进行比较。 Enhancer动态创建给定类型的子类,但拦截所有方法调用。 除Proxy类外,它对类和接口类型均适用。 以下示例和下面的一些示例均基于此简单的Java POJO:

public static class SampleClass {public String test(String input) {return "Hello world!";}
}

使用cglib,可以使用EnhancerFixedValue回调轻松地将test(String)方法的返回值替换为另一个值:

@Test
public void testFixedValue() throws Exception {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();assertEquals("Hello cglib!", proxy.test(null));
}

在上面的示例中,增强器将返回SampleClass的已检测子类的实例,其中所有方法调用均返回固定值,该值是由上面的匿名FixedValue实现生成的。 该对象由Enhancer#create(Object...) ,其中该方法采用任意数量的参数,这些参数用于选择增强类的任何构造函数。 (即使构造函数只是Java字节码级别上的方法, Enhancer类也不能构造函数。它也不可以构造staticfinal类。)如果只想创建一个类,而又不创建实例, Enhancer#createClass将创建一个Class实例,可用于动态创建实例。 增强类的所有构造函数都可以在此动态生成的类中用作委托构造函数。

请注意,在上面的示例中,将委派任何方法调用,还应调用java.lang.Object定义的方法。 结果,对proxy.toString()的调用也将返回“ Hello cglib!”。 相比之下,对proxy.hashCode()的调用将导致ClassCastException因为即使Object#hashCode签名需要原始整数, FixedValue拦截器也始终返回String

可以得出的另一个结论是最终方法没有被拦截。 这种方法的一个示例是Object#getClass ,在调用该方法时将返回类似“ SampleClass $$ EnhancerByCGLIB $$ e277c63c”的内容。 此类名称由cglib随机生成,以避免命名冲突。 在程序代码中使用显式类型时,请注意增强型实例的不同类。 但是,由cglib生成的类将与增强类位于同一包中(因此可以覆盖package-private方法)。 与最终方法类似,子类化方法导致无法增强最终类。 因此,像Hibernate这样的框架无法持久化最终类。

接下来,让我们看一个更强大的回调类InvocationHandler ,它也可以与Enhancer一起使用:

@Test
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();assertEquals("Hello cglib!", proxy.test(null));assertNotEquals("Hello cglib!", proxy.toString());
}

此回调使我们可以就调用的方法进行回答。 但是,在InvocationHandler#invoke方法随附的代理对象上调用方法时应小心。 将使用相同的InvocationHandler调度对此方法的所有调用,因此可能导致无限循环。 为了避免这种情况,我们可以使用另一个回调分配器:

@Test
public void testMethodInterceptor() throws Exception {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 {if(method.getDeclaringClass() != Object.class && method.getReturnType() == String.class) {return "Hello cglib!";} else {proxy.invokeSuper(obj, args);}}});SampleClass proxy = (SampleClass) enhancer.create();assertEquals("Hello cglib!", proxy.test(null));assertNotEquals("Hello cglib!", proxy.toString());proxy.hashCode(); // Does not throw an exception or result in an endless loop.
}

MethodInterceptor允许完全控制所拦截的方法,并提供一些实用程序来以其原始状态调用增强类的方法。 但是为什么仍然要使用其他方法呢? 因为其他方法效率更高,并且cglib通常用于效率起着重要作用的边缘案例框架。 MethodInterceptor的创建和链接需要例如生成不同类型的字节码,以及创建InvocationHandler不需要的某些运行时对象。 因此,增强器还可以使用其他类:

  • LazyLoader :即使LazyLoader的唯一方法具有相同的方法签名FixedValueLazyLoader是在根本不同FixedValue拦截。 LazyLoader实际上应该返回增强类的子类的实例。 仅当在增强型对象上调用方法并将其存储以供将来调用生成的代理时,才请求此实例。 如果您的对象创建昂贵而又不知道该对象是否会被使用,则这是有道理的。 请注意,必须同时为代理对象和延迟加载的对象调用增强类的某些构造函数。 因此,请确保有另一个廉价的(可能是protected )构造函数可用,或为代理使用接口类型。 您可以通过将参数提供给Enhancer#create(Object...)来选择构造的被调用方法。
  • DispatcherDispatcher类似于LazyLoader但将在每次方法调用时调用,而不存储已加载的对象。 这允许更改类的实现而无需更改对它的引用。 同样,请注意必须同时为代理和生成的对象调用某些构造函数。
  • ProxyRefDispatcher :此类包含对在其签名中调用的代理对象的引用。 例如,这允许将方法调用委派给该代理的另一个方法。 请注意,如果从ProxyRefDispatcher#loadObject(Object)内调用相同的方法,这很容易导致无限循环,并且始终会导致无限循环。
  • NoOpNoOp类的名称不代表其名称。 而是将每个方法调用委派给增强类的方法实现。

此时,最后两个拦截器可能对您没有意义。 当总是将方法调用始终委派给增强类时,为什么还要增强类呢? 你是对的。 这些拦截器仅应与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; // A singleton provided by NoOp.}}};enhancer.setSuperclass(MyClass.class);enhancer.setCallbackFilter(callbackHelper);enhancer.setCallbacks(callbackHelper.getCallbacks());SampleClass proxy = (SampleClass) enhancer.create();assertEquals("Hello cglib!", proxy.test(null));assertNotEquals("Hello cglib!", proxy.toString());proxy.hashCode(); // Does not throw an exception or result in an endless loop.
}

Enhancer实例在其Enhancer#setCallbackFilter(CallbackFilter)方法中接受CallbackFilter在此方法中,它希望将增强类的方法映射到Callback实例数组的数组索引。 在创建的代理上调用方法时, Enhancer将选择相应的拦截器,并将调用的方法分派到相应的Callback (这是到目前为止引入的所有拦截器的标记接口)。 为了使该API不再那么笨拙,cglib提供了一个CallbackHelper ,它将代表一个CallbackFilter并可以为您创建一个Callback数组。 上面的增强对象在功能上将与MethodInterceptor示例中的对象等效,但是它使您可以编写专用的拦截器,同时将对这些拦截器的调度逻辑分开。

它是如何工作的?

Enhancer创建类时,它将为创建后为增强类注册为Callback每个拦截器设置一个创建private static字段。 这也意味着用cglib创建的类定义在创建后就不能重用,因为回调的注册不会成为所生成类的初始化阶段的一部分,而是由JVM初始化该类后由cglib手动准备的。 这也意味着用cglib创建的类在初始化后在技术上还没有准备好,例如由于目标计算机中加载的类不存在回调,因此无法通过电线发送。

取决于注册拦截器,CGLIB可能记录附加字段,诸如例如用于MethodInterceptor其中两个private static字段(一个保持的反射Method和另一保持MethodProxy是在增强类或任何的截取)的每方法注册它的子类。 请注意, MethodProxy过度使用了FastClass ,这会触发其他类的创建,下面将对其进行详细描述。

由于所有这些原因,使用Enhancer时要小心。 并且始终要防御性地注册回调类型,因为例如MethodInterceptor将触发创建其他类并在增强类中注册其他static字段。 这特别危险,因为回调变量也作为static变量存储在增强类中:这意味着回调实例永远不会被垃圾回收(除非它们的ClassLoader是异常的)。 当使用匿名类对它们的外部类进行静默引用时,这尤其危险。 回想一下上面的例子:

@Test
public void testFixedValue() throws Exception {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();assertEquals("Hello cglib!", proxy.test(null));
}

FixedValue的匿名子类几乎无法从增强的SampleClass引用,因此匿名的FixedValue实例或包含@Test方法的类都不会被垃圾回收。 这会在您的应用程序中引入讨厌的内存泄漏。 因此,请勿将非static内部类与cglib一起使用。 (为了使示例简短,我仅在此博客条目中使用它们。)

最后,永远不要拦截Object#finalize() 。 由于cglib的子类化方法,截取finalize函数是通过覆盖它来实现的,通常这是一个坏主意 。 拦截终结器的增强实例将被垃圾收集器以不同的方式对待,并且还将导致这些对象在JVM的终结器队列中排队。 另外,如果(偶然)在截获的finalize调用中创建了对增强类的硬引用,则实际上已经创建了一个不可收集的实例。 一般来说,这是您所不需要的。 请注意, final方法永远不会被cglib拦截。 因此, Object#waitObject#notifyObject#notifyAll不会带来相同的问题。 但是请注意, Object#clone可能会被拦截,这是您可能不想执行的操作。

不变的豆

cglib的ImmutableBean允许您创建一个不可变包装器,类似于Collections#immutableSetIllegalStateException (但是,不是Java API建议的UnsupportedOperationException可防止对基础bean进行所有更改。 看着一些豆

public class SampleBean {private String value;public String getValue() {return value;}public void setValue(String value) {this.value = value;}
}

我们可以使这个bean不可变:

@Test(expected = IllegalStateException.class)
public void testImmutableBean() throws Exception {SampleBean bean = new SampleBean();bean.setValue("Hello world!");SampleBean immutableBean = (SampleBean) ImmutableBean.create(bean);assertEquals("Hello world!", immutableBean.getValue());bean.setValue("Hello world, again!");assertEquals("Hello world, again!", immutableBean.getValue());immutableBean.setValue("Hello cglib!"); // Causes exception.
}

从该示例可以明显看出,不可变bean通过抛出IllegalStateException防止所有状态更改。 但是,可以通过更改原始对象来更改Bean的状态。 所有这些更改都将通过ImmutableBean反映出来。

豆产生器

BeanGenerator是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");assertEquals("Hello cglib!", getter.invoke(myBean));
}

从该示例可以明显BeanGeneratorBeanGenerator首先将一些属性用作名称/值对。 创建时, BeanGenerator创建访问器

  • <type> get<name>()
  • void set<name>(<type>)

为了你。 当另一个库需要通过反射解决的bean,但您在运行时不知道这些bean时,这可能很有用。 (一个示例是Apache Wicket ,它可以与bean一起使用。)

豆复印机

BeanCopier是另一个Bean实用程序,可通过其属性值复制Bean。 考虑另一个与SampleBean具有相似属性的bean:

public class OtherSampleBean {private String value;public String getValue() {return value;}public void setValue(String value) {this.value = value;}
}

现在,您可以将属性从一个bean复制到另一个:

@Test
public void testBeanCopier() throws Exception {BeanCopier copier = BeanCopier.create(SampleBean.class, OtherSampleBean.class, false);SampleBean bean = new SampleBean();myBean.setValue("Hello cglib!");OtherSampleBean otherBean = new OtherSampleBean();copier.copy(bean, otherBean, null);assertEquals("Hello cglib!", otherBean.getValue()); 
}

不受特定类型的限制。 BeanCopier#copy方法可以(最终)选择Converter ,允许对每个bean属性进行进一步的操作。 如果BeanCopier是使用false作为第三个构造函数参数创建的,则Converter被忽略,因此可以为null

散装豆

BulkBean允许通过数组而不是方法调用使用一组指定的bean访问器:

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

BulkBean将getter名称数组,setter名称数组和属性类型数组作为其构造函数参数。 然后,可以通过BulkBean#getPropertyBalues(Object)将生成的检测类提取为数组。 同样,可以通过BulkBean#setPropertyBalues(Object, Object[])设置bean的属性。

豆地图

这是cglib库中的最后一个bean实用程序。 BeanMap将bean的所有属性转换为String to- Object Java Map

@Test
public void testBeanGenerator() throws Exception {SampleBean bean = new SampleBean();BeanMap map = BeanMap.create(bean);bean.setValue("Hello cglib!");assertEquals("Hello cglib", map.get("value"));
}

另外, BeanMap#newInstance(Object)方法允许通过重用相同的Class为其他bean创建映射。

重点工厂

KeyFactory工厂允许动态创建由多个值组成的键,这些键可在例如Map实现中使用。 为此, KeyFactory需要一些接口,该接口定义应在此类键中使用的值。 此接口必须包含一个名为newInstance的方法,该方法返回Object 。 例如:

public interface SampleKeyFactory {Object newInstance(String first, int second);
}

现在可以通过以下方式创建aa密钥的实例:

@Test
public void testKeyFactory() throws Exception {SampleKeyFactory keyFactory = (SampleKeyFactory) KeyFactory.create(Key.class);Object key = keyFactory.newInstance("foo", 42);Map<Object, String> map = new HashMap<Object, String>();map.put(key, "Hello cglib!");assertEquals("Hello cglib!", map.get(keyFactory.newInstance("foo", 42)));
}

KeyFactory将确保Object#equals(Object)Object#hashCode方法的正确实现,以便可以在MapSet使用生成的键对象。 在cglib库中, KeyFactory在内部也有很多使用。

混合蛋白

有些人可能已经从其他编程语言(例如Ruby或Scala,其中mixin称为trait)中了解了Mixin类的概念。 cglib Mixin允许将多个对象组合成一个对象。 但是,为此,这些对象必须由接口支持:

public interface Interface1 {String first();
}public interface Interface2 {String second();
}public class Class1 implements Interface1 {@Overridepublic String first() {return "first";}
}public class Class2 implements Interface2 {@Overridepublic String second() {return "second";}
}

现在,可以通过其他接口将Class1Class2类合并为一个类:

public interface MixinInterface extends Interface1, Interface2 { /* empty */ }@Test
public 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 API相当尴尬,因为它需要用于Mixin的类来实现某些接口,这样非仪表Java也可以解决该问题。

字符串切换器

StringSwitcherString模拟为int Java Map

@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"));
}

StringSwitcher允许在String上模拟switch命令,例如从Java 7开始就可以使用内置的Java switch语句来实现。如果在Java 6或更低StringSwitcher中使用StringSwitcher确实为您的代码增加了好处,但是仍然值得怀疑,我会个人不建议使用它。

接口制造商

InterfaceMaker会执行其名称所建议的操作:它动态创建一个新接口。

@Test
public void testInterfaceMaker() 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());
}

除了cglib的任何其他公共API类之外,接口制造商还依赖于ASM类型。 在运行的应用程序中创建接口几乎没有意义,因为接口仅表示一种类型,编译器可以使用该类型来检查类型。 但是,当您生成要在以后的开发中使用的代码时,这可能很有意义。

方法委托

通过将方法调用绑定到某个接口, MethodDelegate可以模拟MethodDelegate C#的委托到特定方法。 例如,以下代码会将SampleBean#getValue方法绑定到委托:

public 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 world!", delegate.getValueFromDelegate());
}

但是,有一些注意事项:

  • 工厂方法MethodDelegate#create恰好将一个方法名称作为第二个参数。 这是MethodDelegate将为您代理的方法。
  • 必须有一个没有为对象定义参数的方法,该方法作为第一个参数提供给工厂方法。 因此, MethodDelegate强度不如可能强。
  • 第三个参数必须是仅包含一个参数的接口。 MethodDelegate实现此接口,并且可以MethodDelegate为该接口。 调用该方法时,它将在作为第一个参数的对象上调用代理方法。

此外,请考虑以下缺点:

  • cglib为每个代理创建一个新类。 最终,这会浪费您永久的一代堆空间
  • 您不能代理带有参数的方法。
  • 如果您的接口带有参数,则在没有引发异常的情况下方法委派将根本无法工作(返回值始终为null )。 如果您的接口需要其他返回类型(即使更通用),则将收到IllegalArgumentException

组播代表

MulticastDelegate工作方式与MethodDelegate略有不同,尽管它的目标是类似的功能。 为了使用MulticastDelegate ,我们需要一个实现接口的对象:

public interface DelegatationProvider {void setValue(String value);
}public class SimpleMulticastBean implements DelegatationProvider {private String value;public String getValue() {return value;}public void setValue(String value) {this.value = value;}
}

基于此接口支持的bean,我们可以创建一个MulticastDelegate ,将对setValue(String)所有调用分派到实现DelegationProvider接口的几个类:

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

同样,还有一些缺点:

  • 这些对象需要实现单方法接口。 这对于第三方库来说很糟糕,并且当您使用CGlib进行某些魔术操作 (该魔术暴露于常规代码)时很尴尬。 另外,您可以轻松实现自己的委托(尽管没有字节码,但是我怀疑您在手动委托方面是否能赢得如此之多)。
  • 当您的代表返回值时,您将仅收到添加的最后一个代表的值。 所有其他返回值都将丢失(但在某些时候由多播委托检索)。

建设者代表

ConstructorDelegate允许创建字节仪表工厂方法 。 为此,我们首先需要一个具有单一方法newInstance的接口,该方法返回一个Object并采用任意数量的参数以用于指定类的构造函数调用。 例如,为了为SampleBean创建一个ConstructorDelegate ,我们需要以下代码来调用SampleBean的默认(无参数)构造函数:

public interface SampleBeanConstructorDelegate {Object newInstance();
}@Test
public void testConstructorDelegate() throws Exception {SampleBeanConstructorDelegate constructorDelegate = (SampleBeanConstructorDelegate) ConstructorDelegate.create(SampleBean.class, SampleBeanConstructorDelegate.class);SampleBean bean = (SampleBean) constructorDelegate.newInstance();assertTrue(SampleBean.class.isAssignableFrom(bean.getClass()));
}

平行分选机

当对数组数组进行排序时, ParallelSorter声称是Java标准库的数组排序器的更快替代方法:

@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;}}
}

ParallelSorter接受一个数组数组,并允许对数组的每一行应用合并排序或快速排序。 使用时请务必小心:

  • 当使用基本数组时,您必须在示例中调用具有明确排序范围的合并排序(例如, ParallelSorter.create(value).mergeSort(0, 0, 3) ,否则, ParallelSorter出现一个非常明显的错误,即试图将原始数组转换为Object[]数组将导致ClassCastException
  • 如果数组行不均匀,则第一个参数将确定要考虑的行的长度。 不均匀的行将导致不考虑多余的值进行排序,或者导致ArrayIndexOutOfBoundException

就我个人而言,我怀疑ParallelSorter确实具有时间优势。 诚然,我还没有尝试对其进行基准测试。 如果您尝试过,很高兴在评论中听到它。

快速班和快速成员

通过包装Java类并提供与反射API类似的方法, FastClass承诺比Java反射API更快地调用方法:

@Test
public void testFastClass() throws Exception {FastClass fastClass = FastClass.create(SampleBean.class);FastMethod fastMethod = fastClass.getMethod(SampleBean.class.getMethod("getValue"));MyBean myBean = new MyBean();myBean.setValue("Hello cglib!");assertTrue("Hello cglib!", fastMethod.invoke(myBean, new Object[0]));
}

除了演示的FastMethodFastClass还可以创建FastConstructor但不能创建快速字段。 但是FastClass如何比正常反射更快? Java反射由JNI执行,其中方法调用由某些C代码执行。 FastClass创建一些字节代码,这些代码直接从JVM内部调用该方法。 但是,HotSpot JVM的较新版本(可能还有许多其他现代JVM)都知道一个称为膨胀的概念,在该概念中,当反射方法经常执行时,JVM会将反射方法调用转换为FastClass 本机版本 。 您甚至可以通过将sun.reflect.inflationThreshold属性设置为较低的值来控制此行为(至少在HotSpot JVM上)。 (默认值为15。)此属性确定在执行了几次反射调用后,应使用字节码检测版本替换JNI调用。 因此,我建议不要在现代JVM上使用FastClass ,但是它可以调整旧Java虚拟机上的性能。

cglib代理

cglib Proxy是本文开头提到的Java Proxy类的重新实现。 旨在允许在Java 1.3之前的Java版本中使用Java库的代理,并且仅在次要细节上有所不同。 但是,可以在Java标准库的Proxy javadoc中找到cglib Proxy的更好文档,其中提供了其用法示例。 因此,我将在这里跳过对cglib Proxy的更详细的讨论。

最后的警告

在对cglib功能进行了概述之后,我想说最后一个警告。 所有cglib类都会生成字节码,这会导致其他类存储在JVM内存的特殊部分中:所谓的烫发空间。 顾名思义,该永久空间用于通常不收集垃圾的永久对象。 但是,这不是完全正确的:加载Class ,在加载的ClassLoader可用于垃圾回收之前,无法将其卸载。 仅在用自定义ClassLoader加载Class的情况下,该ClassLoader不是本机JVM系统ClassLoader 。 这个ClassLoader可以,如果本身,都被垃圾收集Class ES IT不断加载,并且所有的所有实例Class ES IT负载曾经成为可进行垃圾回收。 这意味着:如果您在Java应用程序的整个生命周期中创建了越来越多的类,并且如果您不照顾这些类的删除,那么您将早晚运行烫发空间,这将导致应用程序的死亡。 OutOfMemoryError手 。 因此,请谨慎使用cglib。 但是,如果您明智而谨慎地使用cglib,则可以用它做真正令人惊奇的事情,这超出了非仪器化Java应用程序可以做的事情。

最后,在创建依赖于cglib的项目时,考虑到它的流行性,您应该意识到cglib项目没有得到应有的维护和活动。 缺少的文档是第一个提示。 通常是一团糟的公共API。 但随后,cglib到Maven Central的部署也出现了中断。 邮件列表的阅读方式就像垃圾邮件的存档一样。 并且释放周期相当不稳定。 因此,您可能想看看javassist ,它是cglib的唯一真正的低级替代品。 Javassist捆绑了一个伪Java编译器,该编译器无需了解Java字节码就可以创建非常惊人的字节码工具。 如果您想弄脏手,您可能还喜欢在cglib之上构建的ASM 。 ASM附带了关于库和Java类文件及其字节码的出色文档。

参考: cglib:我们的JCG合作伙伴 Rafael Winterhalter在My daily Java blog上缺少的手册 。

翻译自: https://www.javacodegeeks.com/2013/12/cglib-the-missing-manual.html

cglib

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/345386.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【渝粤题库】广东开放大学 现代服务学概论 形成性考核 (2)

选择题 题目&#xff1a;为什么需要课本&#xff1f; 题目&#xff1a;以下哪种考核方式可以多次尝试&#xff0c;多次取得成绩&#xff0c;直到取得理想的分数&#xff1f; 题目&#xff1a;你可以通过哪些方法联系到本课程的责任教师&#xff1f; 题目&#xff1a;可以通过哪…

latex-bib参考文献人名特殊字符

https://blog.csdn.net/weixin_43413198/article/details/105468843

【渝粤题库】广东开放大学 网络营销基础与实践 形成性考核 (2)

选择题 题目&#xff1a;下列关于网络营销的观点正确的是&#xff08; &#xff09; 题目&#xff1a;B2B2B模式是商业模式的一种。 题目&#xff1a;市场细分的方法有4种方法&#xff0c;分别为完全细分、按一个影响需求的因素细分、按两个以上影响需求的因素细分、按市场需求…

Java Lambda流和Groovy Clouse的比较

这篇博客文章将探讨List数据结构上的一些谚语操作&#xff0c;并对Java 8/9和Groovy语法进行一些比较。 因此&#xff0c;首先是数据结构。 这只是一个简单的橄榄球球员&#xff0c;有名字和等级。 Java class RugbyPlayer {private String name;private Integer rating;Rugb…

【渝粤题库】广东开放大学 跨境电商实务之搜索引擎 形成性考核

选择题 题目&#xff1a; I.匹配题:将下列缩略语与全称进行匹配。&#xff08;每题1分&#xff0c;共15分&#xff09; 2) WTO A. Value added tax B. Shipping order C. Cable transfer D. Free on Board E. Central Business District F. World Trade Organization …

Matlab RGB 颜色对照表(0-1之间取值)

https://blog.csdn.net/qq_38882446/article/details/100886087

【渝粤题库】陕西师范大学111119 统计学作业

《统计学》作业 一、单项选择题 1、“统计”一词的三种含义是&#xff08; &#xff09;。 A. 统计调查、统计整理、统计分析 B. 统计工作、统计资料、统计学 C. 统计信息、统计咨询、统计监督 D. 统计理论、统计方法、统计技能 2、社会统计的研究对象是&#xff08; &#xff…

MATLAB下数组随机打乱顺序的方法

一&#xff1a;问题 有两个规模相同的数组&#xff0c;两个数组相同位置的元素一一对应&#xff0c;现在要将两数组的元素同时打乱顺序&#xff0c;并且乱序后的两数组对应位置元素要保持乱序前的对应关系。 二&#xff1a;方法 采用randperm&#xff08;&#xff09;函数&a…

【渝粤题库】陕西师范大学152102 管理学原理作业(高起专)

《管理学原理》作业 一、选择题 概念技能对&#xff08; &#xff09;最为重要。 A. 高层管理者 B. 中层管理者 C. 基层管理者 D. 非管理雇员 2.管理者决定什么任务将被完成&#xff0c;谁来完成这些任务&#xff0c;这些任务将如何组合&#xff0c;以及将在哪里做出决策是&am…

空间点过程(Point Processes)和随机测度(Random Measure)

1 空间点过程与随机测度&#xff08;一&#xff09;&#xff1a;从数星星说起 1.1 数星星的数学 小时候&#xff0c;在晴朗的夜里&#xff0c;我喜欢仰望星空&#xff0c;去数天上的星星——那是无忧无虑的快乐童年。长大后&#xff0c;当我们再度仰望苍穹&#xff0c;也许会思…

【渝粤题库】陕西师范大学163204 旅游规划学

一、单选题 1.具有美学、历史、科学价值的山、河、湖、海、动植物等自然景物和园林建筑、文物、古迹、革命地等 人文景观所构成的空间环境属于旅游地域系统中的&#xff08;  &#xff09;。 A景区 B名胜区 C风景名胜区 D风景区 2.创意构想作为影响旅游项目设计的一个主因子&…

【渝粤题库】陕西师范大学164212 国际贸易实务 作业(专升本)

《国际贸易实务》作业 名词解释 1. 凭样品买卖 2. 净价 3. 法定检验 4. 询价 5. 良好平均品质 6. 船舶受载期 7. 背书 8. 发价 9. 溢短装条款 10. 滞期费 11. 领事发票 12. 接受 13. 指示性标志 14. 预借提单 15. 付款交单 16. 还价 17. 贸易术语 18. 救助费用 19. 通知银行 20…

【渝粤题库】陕西师范大学180109 小学教师专业发展 作业

《小学教师专业发展》作业 一、单项选择题 1.职业是人获取社会资源与&#xff08; &#xff09;的媒介。 A. 自我发展 B. 自我成长 C. 自我成熟 D.自我实现 2.具备一定专业知识是教师胜任教师工作的基本条件&#xff0c;而非&#xff08; &#xff09;&#xff1b;一定的专业知…

add a legend to a boxplot in MATLAB

1. 给同组数据分类加legend figure; colors [1 0 0; 1 0 0; 0 0 1; 0 0.5 0; 0 0.5 0; 0 0.5 0]; x boxplot(rand(100,6),Colors,colors); legend(Group A,Group B,Group C)2.给不同组数据加legend box_vars findall(gca,Tag,Box); hLegend legend(box_vars([8,6]), {N…

oppo人岗匹配测评_在测试中使用匹配器

oppo人岗匹配测评我们被迫在测试代码中写太多断言行的日子已经一去不复返了。 镇上有一个新的警长&#xff1a;assertThat和他的代理人&#xff1a;匹配者。 好吧&#xff0c;这不是什么新东西&#xff0c;但是无论如何&#xff0c;我想向您介绍匹配器的使用方式&#xff0c;然…

【渝粤题库】陕西师范大学200041 文学概论 作业

《文学概论》作业 一、名词解释 1.文艺学 2.狭义文学 3.推陈出新 4.创作动机 5.文学本体论 6&#xff0e;艺术发现 7&#xff0e;艺术形式 8&#xff0e;现实型文学 9&#xff0e;典型 10&#xff0e;本文时间与故事时间 11&#xff0e;陌生化 12&#xff0e;剧本 13&#xf…

LaTeX indicator function

问题说明&#xff1a; \mathbb字符的空心化显示仅对字符有效&#xff0c;对数字无效。 解决方法&#xff1a; 1.使用 bbm 包 \documentclass{article} \usepackage{bbm} \begin{document}\[ \mathbbm{1} \]\end{document}2.使用 dsfont&#xff08;double stroke&#xff0…

【渝粤题库】陕西师范大学200471 英汉翻译

《英汉翻译》作业 I. Please translate the following phrases and the underlined part in each sentence. 1.break the record 2.armed to the teeth 3.sour grapes 4.a gentleman’s agreement 5.open door policy 6.the cold war 7.soft voice 8.soft fire 9. The poor acc…

JavaFX技巧31:遮罩/剪切/ Alpha通道

选择条 最近&#xff0c;我不得不实现一个自定义控件&#xff0c;该控件使用户可以从项目列表中选择一个项目。 此“ SelectionStrip”控件必须水平放置项目&#xff0c;并且在项目过多的情况下&#xff0c;允许用户左右水平滚动。 该控件将在空间受限的区域中使用&#xff0c;…

【渝粤题库】陕西师范大学200831 编译原理 作业

一、填空 1、预测分析方法LL&#xff08;1&#xff09;方法&#xff0c;它由一个栈&#xff0c;一个 和一个 组成。其中构造 是该分析方法的关键。 2、下面图 是一个DFA&#xff0c;而图 是一个NFA 3、词法分析器的任务是对 进行扫描&#xff0c;以 为依据识别出一个个单词符号…