In the previous part of the article, we introduced the Enhancer of Cglib and the various callbacks used together, and then implemented the dynamic proxy using Enhancer.

In this article, I’ll review some of cGLIb’s other capabilities.

💡
This article is based on OpenJDK11

1. The Bean

Java beans are the most common type, and Cglib provides a number of tools to manipulate these beans to meet various requirements.

Immutable Bean

ImmutableBean is used to generate immutable objects and will throw an IllegalStateException if it is forcibly modified.

All changes to the original baseline are reflected in this immutable object. That is, you can modify an immutable object by modifying the original object.

HelloImpl helloImpl = new HelloImpl();
helloImpl.setValue("ray");
HelloImpl immutableBean = (HelloImpl) ImmutableBean.create(helloImpl);
helloImpl.setValue("hello");
System.out.println(helloImpl.getValue().equals("hello")); // true
System.out.println(immutableBean.getValue().equals("hello")); // true
immutableBean.setValue("Hello ray"); //java.lang.IllegalStateException: Bean is immutable
Copy the code

Bean Generator

The BeanGenerator creates a new Bean at run time. When using third-party libraries, you can use this approach to create beans on the fly without determining the type.

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"); System.out.println("Hello cglib!" .equals(getter.invoke(myBean))); // trueCopy the code

Bean Copier

BeanCopier is used to copy objects, either beans of the same type or beans of different types.

BeanCopier copier = BeanCopier.create(HelloImpl.class, HelloImpl.class, false); HelloImpl bean = new HelloImpl(); bean.setValue("Hello cglib!" ); HelloImpl otherBean = new HelloImpl(); copier.copy(bean, otherBean, null); System.out.println("Hello cglib!" .equals(otherBean.getValue())); // trueCopy the code

It is also possible to implement custom copy rules by passing in the Converter argument by setting the third argument of beancopier.create to true.

BeanCopier copier = BeanCopier.create(HelloImpl.class, HelloImpl.class, true); HelloImpl bean = new HelloImpl(); bean.setValue("Hello cglib!" ); HelloImpl otherBean = new HelloImpl(); copier.copy(bean, otherBean, new Converter() { @Override public Object convert(Object value, Class target, Object context) { return value; }}); System.out.println("Hello cglib!" .equals(otherBean.getValue())); // trueCopy the code

Bulk Bean

BulkBean can access objects by passing in the Bean’s GET and set methods, as well as the types of individual attributes, rather than through method calls.

BulkBean bulkBean = BulkBean.create(HelloImpl.class, new String[]{"getValue"}, new String[]{"setValue"}, new Class[]{String.class}); HelloImpl bean = new HelloImpl(); bean.setValue("Hello world!" ); System.out.println(1 == bulkBean.getPropertyValues(bean).length); System.out.println("Hello world!" .equals(bulkBean.getPropertyValues(bean)[0])); bulkBean.setPropertyValues(bean, new Object[] {"Hello cglib!" }); System.out.println("Hello cglib!" .equals(bean.getValue())); // trueCopy the code

Bean Map

BeanMap implements java.util.Map, which converts a Java Object into a Map of string-to-object key-value pairs.

HelloImpl bean = new HelloImpl(); BeanMap map = BeanMap.create(bean); bean.setValue("Hello cglib!" ); System.out.println("Hello cglib!" .equals(map.get("value")));Copy the code

2. Black magic

Cglib provides a number of utility classes that can be used to implement uncommon, but sometimes important, functions.

Key Factory

A Key Factory can be used to dynamically create objects. The Factory method simply contains a newInstance() method that returns an Object. The resulting Object dynamically implements the Equals and Hashcode methods. This ensures that objects constructed with the same parameters are the same.

The generated object can be used as a Map key.

This utility class is used extensively within Cglib.

public interface SampleKeyFactory { Object newInstance(String first, int second); } SampleKeyFactory keyFactory = (SampleKeyFactory) KeyFactory.create(SampleKeyFactory.class); Object key = keyFactory.newInstance("foo", 42); Map<Object, String> map = new HashMap<Object, String>(); map.put(key, "Hello cglib!" ); System.out.println("Hello cglib!" .equals(map.get(keyFactory.newInstance("foo", 42)))); // If the parameters passed in do not change, the object created each time is the sameCopy the code

Mixin

Mixins are already common in Scala, where multiple objects can be combined into a single object. To support this operation, these objects are required to be implemented based on interfaces. You also need to declare an additional interface to generate composite objects.

public interface Interface2 { String second(); } public class Class1 implements Interface1 { @Override public String first() { return "first"; } } public class Class2 implements Interface2 { @Override public String second() { return "second"; }} public interface extends Interface1, Interface2 {/* empty */} Mixin Mixin = mixin. create(new Class[]{interface1. Class, interface2. Class, MixinInterface.class}, new Object[]{new Class1(), new Class2()}); MixinInterface mixinDelegate = (MixinInterface) mixin; System.out.println("first".equals(mixinDelegate.first())); System.out.println("second".equals(mixinDelegate.second()));Copy the code

However, Mixin is not a complete simulation, because in order to combine objects, you need to declare a separate interface, so why not just use Java methods to implement it.

String Switcher

This utility class emulates the Switch in Java and accepts strings, which has been supported since Java7 and is still useful for versions prior to Java7.

String[] strings = new String[]{"one", "two"};
int[] values = new int[]{10, 20};
StringSwitcher stringSwitcher = StringSwitcher.create(strings, values, true);
System.out.println(10 == stringSwitcher.intValue("one")); // true
System.out.println(20 == stringSwitcher.intValue("two")); // true
System.out.println(-1 == stringSwitcher.intValue("three")); // true
Copy the code

Interface Maker

InterfaceMaker can be used to dynamically generate an interface.

// Create interface 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(); System.out.println(1 == iface.getmethods ().length); // true System.out.println("foo".equals(iface.getMethods()[0].getName())); // true System.out.println(double.class == iface.getMethods()[0].getReturnType()); // trueCopy the code

Fast Class and Fast Members

FastClass provides faster execution than reflection in Java.

FastClass fastClass = FastClass.create(HelloImpl.class); FastMethod fastMethod = fastClass.getMethod(HelloImpl.class.getMethod("getValue")); HelloImpl myBean = new HelloImpl(); myBean.setValue("Hello cglib!" ); System.out.println("Hello cglib!" .equals(fastMethod.invoke(myBean, new Object[0]))); // trueCopy the code

In addition to FastMethod above, you can use FastConstructor, but not FastField. This is easy to understand, and for a property, there is no need for acceleration.

Reflection in Java is code that executes reflection through JNI native calls, whereas FastClass generates bytecode files directly for execution by the JVM.

However, since Java1.5, the performance of reflection code execution has improved so much that FastClass is no longer necessary on newer JVMS, but the performance improvement on older JVMS is still significant.

3. Method delegation

The concept of a method delegate comes from C# and is similar to a function pointer in C++. You can change the value of a delegate at run time. An implementation is also provided in Cglib.

Method Delegate

Method Delagate allows you to construct C# style Method delegates, create a new delegate interface, and then generate a new object from the HelloImpl instance and the getValue Method from which the Method can be invoked.

public interface BeanDelegate { String getValueFromDelegate(); } HelloImpl bean = new HelloImpl(); bean.setValue("Hello cglib!" ); BeanDelegate delegate = (BeanDelegate) MethodDelegate.create( bean, "getValue", BeanDelegate.class); System.out.println("Hello cglib!" .equals(delegate.getValueFromDelegate())); // trueCopy the code

When using the methodDelegate.create factory method, note that it can only delegate methods that have no parameters.

Multicast Delegate

MulticastDelegate can receive delegates from multiple object methods, and methods can have parameters.

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; } } 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 cglib!" ); System.out.println("Hello cglib!" .equals(first.getValue())); // true System.out.println("Hello cglib!" .equals(second.getValue())); // trueCopy the code

MulticastDelegate requires that the interface that provides delegates have only one method, which makes it difficult to implement delegating to third-party libraries because there are so many delegating interfaces to create.

Also, if the delegated method has a return value, only the return value of the last object will be accepted, and all other returns will be lost.

Constructor Delegate

The delegate of the constructor is relatively simple. You just define an interface with a newInstance() method that returns Object and can take any arguments.

public interface SampleBeanConstructorDelegate {
    Object newInstance();
}

SampleBeanConstructorDelegate constructorDelegate = (SampleBeanConstructorDelegate) ConstructorDelegate.create(
        HelloImpl.class, SampleBeanConstructorDelegate.class);
HelloImpl bean = (HelloImpl) constructorDelegate.newInstance();
System.out.print(HelloImpl.class.isAssignableFrom(bean.getClass()));
Copy the code

4. Other

Parallel Sorter

Cglib even provides a collator that promises to be more efficient than Java’s own sorting tools.

This collator can sort multi-dimensional arrays and can use different collation rules for different rows, either merge sort or quicksort.

The usage is as follows:

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) { System.out.println(former < val); // true former = val; }}Copy the code

In the case of mergeSort, there are four overloaded methods that can take up to four arguments.

public void mergeSort(int index, int lo, int hi, Comparator cmp) {
    chooseComparer(index, cmp);
    super.mergeSort(lo, hi - 1);
}
Copy the code

The first parameter indicates which column to start merging sort from, the second indicates which line (inclusive) to start from, the third indicates which line to end on (not included), and the fourth parameter is a custom comparator.

It doesn’t seem to work very well. In fact, Java’s built-in sorting is already pretty good, and this sorting tool is not recommended.

And it has an obvious bug, if the above Integer [] [] to int [] [], will be submitted to the Java. Lang. ClassCastException.

REF

[1] dzone.com/articles/cg…

The text/Rayjun

                                               Â