Class ProxyFactory
This factory generates a class that extends the given super class and implements
the given interfaces. The calls of the methods inherited from the super class are
forwarded and then invoke() is called on the method handler
associated with instances of the generated class. The calls of the methods from
the interfaces are also forwarded to the method handler.
For example, if the following code is executed,
ProxyFactory f = new ProxyFactory();
f.setSuperclass(Foo.class);
f.setFilter(new MethodFilter() {
public boolean isHandled(Method m) {
// ignore finalize()
return !m.getName().equals("finalize");
}
});
Class c = f.createClass();
MethodHandler mi = new MethodHandler() {
public Object invoke(Object self, Method m, Method proceed,
Object[] args) throws Throwable {
System.out.println("Name: " + m.getName());
return proceed.invoke(self, args); // execute the original method.
}
};
Foo foo = (Foo)c.newInstance();
((Proxy)foo).setHandler(mi);
Here, Method is java.lang.reflect.Method.
Then, the following method call will be forwarded to MethodHandler
mi and prints a message before executing the originally called method
bar() in Foo.
foo.bar();
The last three lines of the code shown above can be replaced with a call to
the helper method create, which generates a proxy class, instantiates
it, and sets the method handler of the instance:
:
Foo foo = (Foo)f.create(new Class[0], new Object[0], mi);
To change the method handler during runtime, execute the following code:
MethodHandler mi = ... ; // alternative handler ((Proxy)foo).setHandler(mi);
If setHandler is never called for a proxy instance then it will employ the default handler which proceeds by invoking the original method. The behaviour of the default handler is identical to the following handler:
class EmptyHandler implements MethodHandler {
public Object invoke(Object self, Method m,
Method proceed, Object[] args) throws Exception {
return proceed.invoke(self, args);
}
}
A proxy factory caches and reuses proxy classes by default. It is possible to reset
this default globally by setting static field useCache to false.
Caching may also be configured for a specific factory by calling instance method
setUseCache(boolean). It is strongly recommended that new clients
of class ProxyFactory enable caching. Failure to do so may lead to exhaustion of
the heap memory area used to store classes.
Caching is automatically disabled for any given proxy factory if deprecated instance
method setHandler(MethodHandler) is called. This method was
used to specify a default handler which newly created proxy classes should install
when they create their instances. It is only retained to provide backward compatibility
with previous releases of javassist. Unfortunately,this legacy behaviour makes caching
and reuse of proxy classes impossible. The current programming model expects javassist
clients to set the handler of a proxy instance explicitly by calling method
Proxy.setHandler(MethodHandler) as shown in the sample code above. New
clients are strongly recommended to use this model rather than calling
setHandler(MethodHandler).
A proxy object generated by ProxyFactory is serializable
if its super class or any of its interfaces implement java.io.Serializable.
However, a serialized proxy object may not be compatible with future releases.
The serialization support should be used for short-term storage or RMI.
For compatibility with older releases serialization of proxy objects is implemented by
adding a writeReplace method to the proxy class. This allows a proxy to be serialized
to a conventional ObjectOutputStream and deserialized from a corresponding
ObjectInputStream. However this method suffers from several problems, the most
notable one being that it fails to serialize state inherited from the proxy's superclass.
An alternative method of serializing proxy objects is available which fixes these problems. It
requires inhibiting generation of the writeReplace method and instead using instances of
ProxyObjectOutputStream and ProxyObjectInputStream
(which are subclasses of ObjectOutputStream and ObjectInputStream)
to serialize and deserialize, respectively, the proxy. These streams recognise javassist proxies and ensure
that they are serialized and deserialized without the need for the proxy class to implement special methods
such as writeReplace. Generation of the writeReplace method can be disabled globally by setting static field
useWriteReplace to false. Alternatively, it may be
configured per factory by calling instance method setUseWriteReplace(boolean).
- Since:
- 3.1
- See Also:
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic interfaceA provider of class loaders.(package private) static class(package private) static classused to store details of a specific proxy class in the second tier of the proxy cache.static interfaceA unique class name generator. -
Field Summary
FieldsModifier and TypeFieldDescriptionprivate StringA provider used bycreateClass()for obtaining a class loader.private Stringprivate static final Stringprivate booleanper factory setting initialised from current setting for useCache but able to be reset before each create callprivate booleanper factory setting initialised from current setting for useWriteReplace but able to be reset before each create callprivate static final Stringprivate static final Stringprivate Stringprivate MethodHandlerprivate static final Stringprivate static final Stringprivate static final Stringprivate static final Stringprivate static final Stringprivate static final Stringprivate static final Stringprivate booleanprivate static char[]private static final Stringprivate static final Stringprivate Class<?>[]private MethodFilterstatic ProxyFactory.UniqueNameA unique class name generator.private static final Stringprivate static final Class<?>static booleanIf true, only public/protected methods are forwarded to a proxy object.private static final Stringprivate static Map<ClassLoader,Map<String, ProxyFactory.ProxyDetails>> private static final Stringprivate static final Stringprivate static final longprivate byte[]private static Comparator<Map.Entry<String,Method>> private Class<?>private Stringprivate Class<?>static booleanIf true, a generated proxy class is cached and it will be reused when generating the proxy class with the same properties is requested.static booleanIf true, a generated proxy class will implement method writeReplace enabling serialization of its proxies to a conventional ObjectOutputStream.If the value of this variable is not null, the class file of the generated proxy class is written under the directory specified by this variable. -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionprivate static voidaddClassInitializer(ClassFile cf, ConstPool cp, String classname, int size, List<ProxyFactory.Find2MethodsArgs> forwarders) private static voidprivate static intprivate static intaddLoadParameters(Bytecode code, Class<?>[] params, int offset) private static intprivate static voidprivate static voidaddUnwrapper(Bytecode code, Class<?> type) private voidprivate static booleanareParametersSame(Method method, Method targetMethod) private static voidcallFind2Methods(Bytecode code, String superMethod, String thisMethod, int index, String desc, int classVar, int arrayVar) private voidprivate voidcomputeSignature(MethodFilter filter) Creates a proxy class and returns an instance of that class.create(Class<?>[] paramTypes, Object[] args, MethodHandler mh) Creates a proxy class and returns an instance of that class.Class<?>Generates a proxy class using the current filter.(package private) Class<?>createClass(byte[] signature) Generates a proxy class with a specific signature.Class<?>createClass(MethodHandles.Lookup lookup) Generates a proxy class using the current filter.(package private) Class<?>createClass(MethodHandles.Lookup lookup, byte[] signature) Generates a proxy class with a specific signature.Class<?>createClass(MethodHandles.Lookup lookup, MethodFilter filter) Generates a proxy class using the supplied filter.Class<?>createClass(MethodFilter filter) Generates a proxy class using the supplied filter.private Class<?>createClass1(MethodHandles.Lookup lookup) private voidcreateClass2(ClassLoader cl, MethodHandles.Lookup lookup) private voidcreateClass3(ClassLoader cl, MethodHandles.Lookup lookup) private Class<?>Obtains a class belonging to the same package that the created proxy class belongs to.protected ClassLoaderprotected ClassLoaderprotected ProtectionDomainprivate static Object(package private) static byte[]getFilterSignature(Class<?> clazz) static MethodHandlergetHandler(Proxy p) Obtains the method handler of the given proxy object.Class<?>[]Obtains the interfaces set bysetInterfaces.getMethods(Class<?> superClass, Class<?>[] interfaceTypes) private voidprivate static StringgetPackageName(String name) Class<?>Obtains the super class set bysetSuperclass().private voidinstallSignature(byte[] signature) private Class<?>invokespecialTarget(Class<?> declClass) private static booleanprivate static booleanisDuplicated(int index, Method[] methods) static booleanisProxyClass(Class<?> cl) determine if a class is a javassist proxy classbooleantest whether this factory uses the proxy cachebooleantest whether this factory installs a writeReplace method in created classesprivate static booleanReturns true if the method is visible from the package.private static Stringprivate ClassFilemake()private static MethodInfomakeConstructor(String thisClassName, Constructor<?> cons, ConstPool cp, Class<?> superClass, boolean doHandlerInit) private voidmakeConstructors(String thisClassName, ClassFile cf, ConstPool cp, String classname) private MethodInfoprivate static MethodInfomakeForwarder(String thisClassName, Method meth, String desc, ConstPool cp, Class<?> declClass, String delegatorName, int index, List<ProxyFactory.Find2MethodsArgs> forwarders) private static voidmakeParameterList(Bytecode code, Class<?>[] params) private static StringmakeProxyName(String classname) private voidprivate static Stringprivate static booleanprivate static intmakeWrapper(Bytecode code, Class<?> type, int regno) private static MethodInfoprivate voidoverride(String thisClassname, Method meth, String prefix, int index, String desc, ClassFile cf, ConstPool cp, List<ProxyFactory.Find2MethodsArgs> forwarders) private intoverrideMethods(ClassFile cf, ConstPool cp, String className, List<ProxyFactory.Find2MethodsArgs> forwarders) private voidsetBit(byte[] signature, int idx) private voidvoidSets a filter that selects the methods that will be controlled by a handler.voidSets the generic signature for the proxy class.voidDeprecated.since 3.12 use of this method is incompatible with proxy class caching.voidsetInterfaces(Class<?>[] ifs) Sets the interfaces of a proxy class.private static voidsetInterfaces(ClassFile cf, Class<?>[] interfaces, Class<?> proxyClass) voidsetSuperclass(Class<?> clazz) Sets the super class of a proxy class.private static voidsetThrows(MethodInfo minfo, ConstPool cp, Class<?>[] exceptions) private static voidsetThrows(MethodInfo minfo, ConstPool cp, Method orig) voidsetUseCache(boolean useCache) configure whether this factory should use the proxy cachevoidsetUseWriteReplace(boolean useWriteReplace) configure whether this factory should add a writeReplace method to created classesprivate booleantestBit(byte[] signature, int idx)
-
Field Details
-
superClass
-
interfaces
-
methodFilter
-
handler
-
signatureMethods
-
hasGetHandler
private boolean hasGetHandler -
signature
private byte[] signature -
classname
-
basename
-
superName
-
thisClass
-
genericSignature
-
factoryUseCache
private boolean factoryUseCacheper factory setting initialised from current setting for useCache but able to be reset before each create call -
factoryWriteReplace
private boolean factoryWriteReplaceper factory setting initialised from current setting for useWriteReplace but able to be reset before each create call -
onlyPublicMethods
public static boolean onlyPublicMethodsIf true, only public/protected methods are forwarded to a proxy object. The class for that proxy object is loaded by the
defineClassmethod injava.lang.invoke.MethodHandles.Lookup, which is available in Java 9 or later. This works even whensun.misc.Unsafeis not available for some reasons (it is already deprecated in Java 9).To load a class, Javassist first tries to use
sun.misc.Unsafeand, if not available, it uses aprotectedmethod injava.lang.ClassLoaderviaPrivilegedAction. Since the latter approach is not available any longer by default in Java 9 or later, the JVM argument--add-opens java.base/java.lang=ALL-UNNAMEDmust be given to the JVM when it is used (because of lack ofsun.misc.Unsafe). If this argument cannot be given to the JVM,onlyPublicMethodsshould be set totrue. Javassist will try to load by usingjava.lang.invoke.MethodHandles.Lookup.The default value is
false.- Since:
- 3.22
- See Also:
-
writeDirectory
If the value of this variable is not null, the class file of the generated proxy class is written under the directory specified by this variable. For example, if the value is".", then the class file is written under the current directory. This method is for debugging.The default value is null.
-
OBJECT_TYPE
-
HOLDER
- See Also:
-
HOLDER_TYPE
- See Also:
-
FILTER_SIGNATURE_FIELD
- See Also:
-
FILTER_SIGNATURE_TYPE
- See Also:
-
HANDLER
- See Also:
-
NULL_INTERCEPTOR_HOLDER
- See Also:
-
DEFAULT_INTERCEPTOR
- See Also:
-
HANDLER_TYPE
-
HANDLER_SETTER
- See Also:
-
HANDLER_SETTER_TYPE
-
HANDLER_GETTER
- See Also:
-
HANDLER_GETTER_TYPE
-
SERIAL_VERSION_UID_FIELD
- See Also:
-
SERIAL_VERSION_UID_TYPE
- See Also:
-
SERIAL_VERSION_UID_VALUE
private static final long SERIAL_VERSION_UID_VALUE- See Also:
-
useCache
public static volatile boolean useCacheIf true, a generated proxy class is cached and it will be reused when generating the proxy class with the same properties is requested. The default value is true. Note that this value merely specifies the initial setting employed by any newly created proxy factory. The factory setting may be overwritten by calling factory instance methodsetUseCache(boolean)- Since:
- 3.4
-
useWriteReplace
public static volatile boolean useWriteReplaceIf true, a generated proxy class will implement method writeReplace enabling serialization of its proxies to a conventional ObjectOutputStream. this (default) setting retains the old javassist behaviour which has the advantage that it retains compatibility with older releases and requires no extra work on the part of the client performing the serialization. However, it has the disadvantage that state inherited from the superclasses of the proxy is lost during serialization. if false then serialization/deserialization of the proxy instances will preserve all fields. However, serialization must be performed via aProxyObjectOutputStreamand deserialization must be viaProxyObjectInputStream. Any attempt to serialize proxies whose class was created with useWriteReplace set to false via a normalObjectOutputStreamwill fail. Note that this value merely specifies the initial setting employed by any newly created proxy factory. The factory setting may be overwritten by calling factory instance methodsetUseWriteReplace(boolean)- Since:
- 3.4
-
proxyCache
-
hexDigits
private static char[] hexDigits -
classLoaderProvider
A provider used bycreateClass()for obtaining a class loader.get()on thisClassLoaderProviderobject is called to obtain a class loader.The value of this field can be updated for changing the default implementation.
Example:
ProxyFactory.classLoaderProvider = new ProxyFactory.ClassLoaderProvider() { public ClassLoader get(ProxyFactory pf) { return Thread.currentThread().getContextClassLoader(); } };- Since:
- 3.4
-
nameGenerator
A unique class name generator. Replacing this generator changes the algorithm to generate a unique name. Thegetmethod does not have to be asynchronizedmethod since the access to this field is mutually exclusive and thus thread safe. -
packageForJavaBase
- See Also:
-
sorter
-
HANDLER_GETTER_KEY
- See Also:
-
-
Constructor Details
-
ProxyFactory
public ProxyFactory()Constructs a factory of proxy class.
-
-
Method Details
-
isUseCache
public boolean isUseCache()test whether this factory uses the proxy cache- Returns:
- true if this factory uses the proxy cache otherwise false
-
setUseCache
public void setUseCache(boolean useCache) configure whether this factory should use the proxy cache- Parameters:
useCache- true if this factory should use the proxy cache and false if it should not use the cache- Throws:
RuntimeException- if a default interceptor has been set for the factory
-
isUseWriteReplace
public boolean isUseWriteReplace()test whether this factory installs a writeReplace method in created classes- Returns:
- true if this factory installs a writeReplace method in created classes otherwise false
-
setUseWriteReplace
public void setUseWriteReplace(boolean useWriteReplace) configure whether this factory should add a writeReplace method to created classes- Parameters:
useWriteReplace- true if this factory should add a writeReplace method to created classes and false if it should not add a writeReplace method
-
isProxyClass
determine if a class is a javassist proxy class- Parameters:
cl-- Returns:
- true if the class is a javassist proxy class otherwise false
-
setSuperclass
Sets the super class of a proxy class. -
getSuperclass
Obtains the super class set bysetSuperclass().- Since:
- 3.4
-
setInterfaces
Sets the interfaces of a proxy class. -
getInterfaces
Obtains the interfaces set bysetInterfaces.- Since:
- 3.4
-
setFilter
Sets a filter that selects the methods that will be controlled by a handler. -
setGenericSignature
Sets the generic signature for the proxy class.For example,
class NullPtr<T> { T get() { return null; } }The following code makes a proxy for
NullPtr<String>:ProxyFactory factory = new ProxyFactory(); factory.setSuperclass(NullPtr.class); factory.setGenericSignature("LNullPtr<Ljava/lang/String;>;"); NullPtr<?> np = (NullPtr<?>)factory.create(null, null); java.lang.reflect.Type[] x = ((java.lang.reflect.ParameterizedType)np.getClass().getGenericSuperclass()) .getActualTypeArguments(); // x[0] == String.class- Parameters:
sig- a generic signature.- Since:
- 3.26
- See Also:
-
createClass
Generates a proxy class using the current filter. The module or package where a proxy class is created has to be opened to this package or the Javassist module.- See Also:
-
createClass
Generates a proxy class using the supplied filter. The module or package where a proxy class is created has to be opened to this package or the Javassist module. -
createClass
Generates a proxy class with a specific signature. access is package local so ProxyObjectInputStream can use this- Parameters:
signature-
-
createClass
Generates a proxy class using the current filter. It loads a class file by the givenjava.lang.invoke.MethodHandles.Lookupobject, which can be obtained byMethodHandles.lookup()called from somewhere in the package that the loaded class belongs to.- Parameters:
lookup- used for loading the proxy class. It needs an appropriate right to invokedefineClassfor the proxy class.- Since:
- 3.24
-
createClass
Generates a proxy class using the supplied filter.- Parameters:
lookup- used for loading the proxy class. It needs an appropriate right to invokedefineClassfor the proxy class.filter- the filter.- Since:
- 3.24
- See Also:
-
createClass
Generates a proxy class with a specific signature. access is package local so ProxyObjectInputStream can use this.- Parameters:
lookup- used for loading the proxy class. It needs an appropriate right to invokedefineClassfor the proxy class.signature- the signature.
-
createClass1
-
getKey
-
createClass2
-
createClass3
-
getClassInTheSamePackage
Obtains a class belonging to the same package that the created proxy class belongs to. It is used to obtain an appropriatejava.lang.invoke.MethodHandles.Lookup. -
setField
-
getFilterSignature
-
getField
-
getHandler
Obtains the method handler of the given proxy object.- Parameters:
p- a proxy object.- Returns:
- the method handler.
- Since:
- 3.16
-
getClassLoader
-
getClassLoader0
-
getDomain
-
create
public Object create(Class<?>[] paramTypes, Object[] args, MethodHandler mh) throws NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException Creates a proxy class and returns an instance of that class.- Parameters:
paramTypes- parameter types for a constructor.args- arguments passed to a constructor.mh- the method handler for the proxy class.- Throws:
NoSuchMethodExceptionIllegalArgumentExceptionInstantiationExceptionIllegalAccessExceptionInvocationTargetException- Since:
- 3.4
-
create
public Object create(Class<?>[] paramTypes, Object[] args) throws NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException Creates a proxy class and returns an instance of that class.- Parameters:
paramTypes- parameter types for a constructor.args- arguments passed to a constructor.- Throws:
NoSuchMethodExceptionIllegalArgumentExceptionInstantiationExceptionIllegalAccessExceptionInvocationTargetException
-
setHandler
Deprecated.since 3.12 use of this method is incompatible with proxy class caching. instead clients should call methodProxy.setHandler(MethodHandler)to set the handler for each newly created proxy instance. calling this method will automatically disable caching of classes created by the proxy factory.Sets the default invocation handler. This invocation handler is shared among all the instances of a proxy class unless another is explicitly specified. -
makeProxyName
-
make
- Throws:
CannotCompileException
-
checkClassAndSuperName
private void checkClassAndSuperName() -
allocateClassName
private void allocateClassName() -
makeSortedMethodList
private void makeSortedMethodList() -
computeSignature
-
installSignature
private void installSignature(byte[] signature) -
testBit
private boolean testBit(byte[] signature, int idx) -
setBit
private void setBit(byte[] signature, int idx) -
setInterfaces
-
addClassInitializer
private static void addClassInitializer(ClassFile cf, ConstPool cp, String classname, int size, List<ProxyFactory.Find2MethodsArgs> forwarders) throws CannotCompileException - Throws:
CannotCompileException
-
callFind2Methods
private static void callFind2Methods(Bytecode code, String superMethod, String thisMethod, int index, String desc, int classVar, int arrayVar) - Parameters:
thisMethod- might be null.
-
addSetter
private static void addSetter(String classname, ClassFile cf, ConstPool cp) throws CannotCompileException - Throws:
CannotCompileException
-
addGetter
private static void addGetter(String classname, ClassFile cf, ConstPool cp) throws CannotCompileException - Throws:
CannotCompileException
-
overrideMethods
private int overrideMethods(ClassFile cf, ConstPool cp, String className, List<ProxyFactory.Find2MethodsArgs> forwarders) throws CannotCompileException - Throws:
CannotCompileException
-
isBridge
-
override
private void override(String thisClassname, Method meth, String prefix, int index, String desc, ClassFile cf, ConstPool cp, List<ProxyFactory.Find2MethodsArgs> forwarders) throws CannotCompileException - Throws:
CannotCompileException
-
makeConstructors
private void makeConstructors(String thisClassName, ClassFile cf, ConstPool cp, String classname) throws CannotCompileException - Throws:
CannotCompileException
-
makeUniqueName
-
makeUniqueName0
-
isVisible
Returns true if the method is visible from the package.- Parameters:
mod- the modifiers of the method.
-
getPackageName
-
getMethods
-
getMethods
-
isDuplicated
-
areParametersSame
-
keyToDesc
-
makeConstructor
private static MethodInfo makeConstructor(String thisClassName, Constructor<?> cons, ConstPool cp, Class<?> superClass, boolean doHandlerInit) -
makeDelegator
-
invokespecialTarget
-
makeForwarder
private static MethodInfo makeForwarder(String thisClassName, Method meth, String desc, ConstPool cp, Class<?> declClass, String delegatorName, int index, List<ProxyFactory.Find2MethodsArgs> forwarders) - Parameters:
delegatorName- null if the original method is abstract.
-
setThrows
-
setThrows
-
addLoadParameters
-
addLoad
-
addReturn
-
makeParameterList
-
makeWrapper
-
addUnwrapper
-
makeWriteReplace
-