Generates dynamic subclasses to enable method interception. This
class started as a substitute for the standard Dynamic Proxy support
included with JDK 1.3, but one that allowed the proxies to extend a
concrete base class, in addition to implementing interfaces. The dynamically
generated subclasses override the non-final methods of the superclass and
have hooks which callback to user-defined interceptor
implementations.
The most common uses of this class are embodied in the static helper methods. For
advanced needs, such as customizing the ClassLoader to use, you should create
a new instance of Enhancer. Other classes within CGLIB follow a similar pattern.
| Method from net.sf.cglib.proxy.Enhancer Detail: |
public Object create() {
classOnly = false;
argumentTypes = null;
return createHelper();
}
Generate a new class if necessary and uses the specified
callbacks (if any) to create a new object instance.
Uses the no-arg constructor of the superclass. |
public Object create(Class[] argumentTypes,
Object[] arguments) {
classOnly = false;
if (argumentTypes == null || arguments == null || argumentTypes.length != arguments.length) {
throw new IllegalArgumentException("Arguments must be non-null and of equal length");
}
this.argumentTypes = argumentTypes;
this.arguments = arguments;
return createHelper();
}
Generate a new class if necessary and uses the specified
callbacks (if any) to create a new object instance.
Uses the constructor of the superclass matching the argumentTypes
parameter, with the given arguments. |
public static Object create(Class type,
Callback callback) {
Enhancer e = new Enhancer();
e.setSuperclass(type);
e.setCallback(callback);
return e.create();
}
Helper method to create an intercepted object.
For finer control over the generated instance, use a new instance of Enhancer
instead of this static method. |
public static Object create(Class superclass,
Class[] interfaces,
Callback callback) {
Enhancer e = new Enhancer();
e.setSuperclass(superclass);
e.setInterfaces(interfaces);
e.setCallback(callback);
return e.create();
}
Helper method to create an intercepted object.
For finer control over the generated instance, use a new instance of Enhancer
instead of this static method. |
public static Object create(Class superclass,
Class[] interfaces,
CallbackFilter filter,
Callback[] callbacks) {
Enhancer e = new Enhancer();
e.setSuperclass(superclass);
e.setInterfaces(interfaces);
e.setCallbackFilter(filter);
e.setCallbacks(callbacks);
return e.create();
}
Helper method to create an intercepted object.
For finer control over the generated instance, use a new instance of Enhancer
instead of this static method. |
public Class createClass() {
classOnly = true;
return (Class)createHelper();
}
Generate a new class if necessary and return it without creating a new instance.
This ignores any callbacks that have been set.
To create a new instance you will have to use reflection, and methods
called during the constructor will not be intercepted. To avoid this problem,
use the multi-arg create method. |
protected void filterConstructors(Class sc,
List constructors) {
CollectionUtils.filter(constructors, new VisibilityPredicate(sc, true));
if (constructors.size() == 0)
throw new IllegalArgumentException("No visible constructors in " + sc);
}
Filter the list of constructors from the superclass. The
constructors which remain will be included in the generated
class. The default implementation is to filter out all private
constructors, but subclasses may extend Enhancer to override this
behavior. |
protected Object firstInstance(Class type) throws Exception {
if (classOnly) {
return type;
} else {
return createUsingReflection(type);
}
}
|
public void generateClass(ClassVisitor v) throws Exception {
Class sc = (superclass == null) ? Object.class : superclass;
if (TypeUtils.isFinal(sc.getModifiers()))
throw new IllegalArgumentException("Cannot subclass final class " + sc);
List constructors = new ArrayList(Arrays.asList(sc.getDeclaredConstructors()));
filterConstructors(sc, constructors);
// Order is very important: must add superclass, then
// its superclass chain, then each interface and
// its superinterfaces.
List actualMethods = new ArrayList();
List interfaceMethods = new ArrayList();
final Set forcePublic = new HashSet();
getMethods(sc, interfaces, actualMethods, interfaceMethods, forcePublic);
List methods = CollectionUtils.transform(actualMethods, new Transformer() {
public Object transform(Object value) {
Method method = (Method)value;
int modifiers = Constants.ACC_FINAL
| (method.getModifiers()
& ~Constants.ACC_ABSTRACT
& ~Constants.ACC_NATIVE
& ~Constants.ACC_SYNCHRONIZED);
if (forcePublic.contains(MethodWrapper.create(method))) {
modifiers = (modifiers & ~Constants.ACC_PROTECTED) | Constants.ACC_PUBLIC;
}
return ReflectUtils.getMethodInfo(method, modifiers);
}
});
ClassEmitter e = new ClassEmitter(v);
e.begin_class(Constants.V1_2,
Constants.ACC_PUBLIC,
getClassName(),
Type.getType(sc),
(useFactory ?
TypeUtils.add(TypeUtils.getTypes(interfaces), FACTORY) :
TypeUtils.getTypes(interfaces)),
Constants.SOURCE_FILE);
List constructorInfo = CollectionUtils.transform(constructors, MethodInfoTransformer.getInstance());
e.declare_field(Constants.ACC_PRIVATE, BOUND_FIELD, Type.BOOLEAN_TYPE, null);
if (!interceptDuringConstruction) {
e.declare_field(Constants.ACC_PRIVATE, CONSTRUCTED_FIELD, Type.BOOLEAN_TYPE, null);
}
e.declare_field(Constants.PRIVATE_FINAL_STATIC, THREAD_CALLBACKS_FIELD, THREAD_LOCAL, null);
e.declare_field(Constants.PRIVATE_FINAL_STATIC, STATIC_CALLBACKS_FIELD, CALLBACK_ARRAY, null);
if (serialVersionUID != null) {
e.declare_field(Constants.PRIVATE_FINAL_STATIC, Constants.SUID_FIELD_NAME, Type.LONG_TYPE, serialVersionUID);
}
for (int i = 0; i < callbackTypes.length; i++) {
e.declare_field(Constants.ACC_PRIVATE, getCallbackField(i), callbackTypes[i], null);
}
emitMethods(e, methods, actualMethods);
emitConstructors(e, constructorInfo);
emitSetThreadCallbacks(e);
emitSetStaticCallbacks(e);
emitBindCallbacks(e);
if (useFactory) {
int[] keys = getCallbackKeys();
emitNewInstanceCallbacks(e);
emitNewInstanceCallback(e);
emitNewInstanceMultiarg(e, constructorInfo);
emitGetCallback(e, keys);
emitSetCallback(e, keys);
emitGetCallbacks(e);
emitSetCallbacks(e);
}
e.end_class();
}
|
protected ClassLoader getDefaultClassLoader() {
if (superclass != null) {
return superclass.getClassLoader();
} else if (interfaces != null) {
return interfaces[0].getClassLoader();
} else {
return null;
}
}
|
public static void getMethods(Class superclass,
Class[] interfaces,
List methods) {
getMethods(superclass, interfaces, methods, null, null);
}
Finds all of the methods that will be extended by an
Enhancer-generated class using the specified superclass and
interfaces. This can be useful in building a list of Callback
objects. The methods are added to the end of the given list. Due
to the subclassing nature of the classes generated by Enhancer,
the methods are guaranteed to be non-static, non-final, and
non-private. Each method signature will only occur once, even if
it occurs in multiple classes. |
public static boolean isEnhanced(Class type) {
try {
getCallbacksSetter(type, SET_THREAD_CALLBACKS_NAME);
return true;
} catch (NoSuchMethodException e) {
return false;
}
}
Determine if a class was generated using Enhancer. |
protected Object nextInstance(Object instance) {
Class protoclass = (instance instanceof Class) ? (Class)instance : instance.getClass();
if (classOnly) {
return protoclass;
} else if (instance instanceof Factory) {
if (argumentTypes != null) {
return ((Factory)instance).newInstance(argumentTypes, arguments, callbacks);
} else {
return ((Factory)instance).newInstance(callbacks);
}
} else {
return createUsingReflection(protoclass);
}
}
|
public static void registerCallbacks(Class generatedClass,
Callback[] callbacks) {
setThreadCallbacks(generatedClass, callbacks);
}
Call this method to register the Callback array to use before
creating a new instance of the generated class via reflection. If you are using
an instance of Enhancer or the Factory interface to create
new instances, this method is unnecessary. Its primary use is for when you want to
cache and reuse a generated class yourself, and the generated class does
not implement the Factory interface.
Note that this method only registers the callbacks on the current thread.
If you want to register callbacks for instances created by multiple threads,
use #registerStaticCallbacks .
The registered callbacks are overwritten and subsequently cleared
when calling any of the create methods (such as
#create ), or any Factory newInstance method.
Otherwise they are not cleared, and you should be careful to set them
back to null after creating new instances via reflection if
memory leakage is a concern. |
public static void registerStaticCallbacks(Class generatedClass,
Callback[] callbacks) {
setCallbacksHelper(generatedClass, callbacks, SET_STATIC_CALLBACKS_NAME);
}
Similar to #registerCallbacks , but suitable for use
when multiple threads will be creating instances of the generated class.
The thread-level callbacks will always override the static callbacks.
Static callbacks are never cleared. |
public void setCallback(Callback callback) {
setCallbacks(new Callback[]{ callback });
}
|
public void setCallbackFilter(CallbackFilter filter) {
this.filter = filter;
}
Set the CallbackFilter used to map the generated class' methods
to a particular callback index.
New object instances will always use the same mapping, but may use different
actual callback objects. |
public void setCallbackType(Class callbackType) {
setCallbackTypes(new Class[]{ callbackType });
}
Set the single type of Callback to use.
This may be used instead of #setCallback when calling
#createClass , since it may not be possible to have
an array of actual callback instances. |
public void setCallbackTypes(Class[] callbackTypes) {
if (callbackTypes != null && callbackTypes.length == 0) {
throw new IllegalArgumentException("Array cannot be empty");
}
this.callbackTypes = CallbackInfo.determineTypes(callbackTypes);
}
Set the array of callback types to use.
This may be used instead of #setCallbacks when calling
#createClass , since it may not be possible to have
an array of actual callback instances.
You must use a CallbackFilter to specify the index into this
array for each method in the proxied class. |
public void setCallbacks(Callback[] callbacks) {
if (callbacks != null && callbacks.length == 0) {
throw new IllegalArgumentException("Array cannot be empty");
}
this.callbacks = callbacks;
}
Set the array of callbacks to use.
Ignored if you use #createClass .
You must use a CallbackFilter to specify the index into this
array for each method in the proxied class. |
public void setInterceptDuringConstruction(boolean interceptDuringConstruction) {
this.interceptDuringConstruction = interceptDuringConstruction;
}
Set whether methods called from within the proxy's constructer
will be intercepted. The default value is true. Unintercepted methods
will call the method of the proxy's base class, if it exists. |
public void setInterfaces(Class[] interfaces) {
this.interfaces = interfaces;
}
Set the interfaces to implement. The Factory interface will
always be implemented regardless of what is specified here. |
public void setSerialVersionUID(Long sUID) {
serialVersionUID = sUID;
}
Insert a static serialVersionUID field into the generated class. |
public void setSuperclass(Class superclass) {
if (superclass != null && superclass.isInterface()) {
setInterfaces(new Class[]{ superclass });
} else if (superclass != null && superclass.equals(Object.class)) {
// affects choice of ClassLoader
this.superclass = null;
} else {
this.superclass = superclass;
}
}
Set the class which the generated class will extend. As a convenience,
if the supplied superclass is actually an interface, setInterfaces
will be called with the appropriate argument instead.
A non-interface argument must not be declared as final, and must have an
accessible constructor. |
public void setUseFactory(boolean useFactory) {
this.useFactory = useFactory;
}
Set whether the enhanced object instances should implement
the Factory interface.
This was added for tools that need for proxies to be more
indistinguishable from their targets. Also, in some cases it may
be necessary to disable the Factory interface to
prevent code from changing the underlying callbacks. |