public class ProxyFactory
extends java.lang.Object
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)
.
MethodHandler
Modifier and Type | Class and Description |
---|---|
static interface |
ProxyFactory.ClassLoaderProvider
A provider of class loaders.
|
(package private) static class |
ProxyFactory.Find2MethodsArgs |
(package private) static class |
ProxyFactory.ProxyDetails
used to store details of a specific proxy class in the second tier of the proxy cache.
|
static interface |
ProxyFactory.UniqueName
A unique class name generator.
|
Modifier and Type | Field and Description |
---|---|
private java.lang.String |
basename |
static ProxyFactory.ClassLoaderProvider |
classLoaderProvider
A provider used by
createClass() for obtaining
a class loader. |
private java.lang.String |
classname |
private static java.lang.String |
DEFAULT_INTERCEPTOR |
private boolean |
factoryUseCache
per factory setting initialised from current setting for useCache but able to be reset before each create call
|
private boolean |
factoryWriteReplace
per factory setting initialised from current setting for useWriteReplace but able to be reset before each create call
|
private static java.lang.String |
FILTER_SIGNATURE_FIELD |
private static java.lang.String |
FILTER_SIGNATURE_TYPE |
private MethodHandler |
handler |
private static java.lang.String |
HANDLER |
private static java.lang.String |
HANDLER_GETTER |
private static java.lang.String |
HANDLER_GETTER_KEY |
private static java.lang.String |
HANDLER_GETTER_TYPE |
private static java.lang.String |
HANDLER_SETTER |
private static java.lang.String |
HANDLER_SETTER_TYPE |
private static java.lang.String |
HANDLER_TYPE |
private boolean |
hasGetHandler |
private static char[] |
hexDigits |
private static java.lang.String |
HOLDER |
private static java.lang.String |
HOLDER_TYPE |
private java.lang.Class[] |
interfaces |
private MethodFilter |
methodFilter |
static ProxyFactory.UniqueName |
nameGenerator
A unique class name generator.
|
private static java.lang.String |
NULL_INTERCEPTOR_HOLDER |
private static java.lang.Class |
OBJECT_TYPE |
private static java.util.WeakHashMap |
proxyCache |
private static java.lang.String |
SERIAL_VERSION_UID_FIELD |
private static java.lang.String |
SERIAL_VERSION_UID_TYPE |
private static long |
SERIAL_VERSION_UID_VALUE |
private byte[] |
signature |
private java.util.List |
signatureMethods |
private static java.util.Comparator |
sorter |
private java.lang.Class |
superClass |
private java.lang.String |
superName |
private java.lang.Class |
thisClass |
static boolean |
useCache
If true, a generated proxy class is cached and it will be reused
when generating the proxy class with the same properties is requested.
|
static boolean |
useWriteReplace
If true, a generated proxy class will implement method writeReplace enabling
serialization of its proxies to a conventional ObjectOutputStream.
|
java.lang.String |
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.
|
Constructor and Description |
---|
ProxyFactory()
Constructs a factory of proxy class.
|
Modifier and Type | Method and Description |
---|---|
private static void |
addClassInitializer(ClassFile cf,
ConstPool cp,
java.lang.String classname,
int size,
java.util.ArrayList forwarders) |
private static void |
addGetter(java.lang.String classname,
ClassFile cf,
ConstPool cp) |
private static int |
addLoad(Bytecode code,
int n,
java.lang.Class type) |
private static int |
addLoadParameters(Bytecode code,
java.lang.Class[] params,
int offset) |
private static int |
addReturn(Bytecode code,
java.lang.Class type) |
private static void |
addSetter(java.lang.String classname,
ClassFile cf,
ConstPool cp) |
private static void |
addUnwrapper(Bytecode code,
java.lang.Class type) |
private void |
allocateClassName() |
private static void |
callFind2Methods(Bytecode code,
java.lang.String superMethod,
java.lang.String thisMethod,
int index,
java.lang.String desc,
int classVar,
int arrayVar) |
private void |
checkClassAndSuperName() |
private void |
computeSignature(MethodFilter filter) |
java.lang.Object |
create(java.lang.Class[] paramTypes,
java.lang.Object[] args)
Creates a proxy class and returns an instance of that class.
|
java.lang.Object |
create(java.lang.Class[] paramTypes,
java.lang.Object[] args,
MethodHandler mh)
Creates a proxy class and returns an instance of that class.
|
java.lang.Class |
createClass()
Generates a proxy class using the current filter.
|
(package private) java.lang.Class |
createClass(byte[] signature)
Generates a proxy class with a specific signature.
|
java.lang.Class |
createClass(MethodFilter filter)
Generates a proxy class using the supplied filter.
|
private java.lang.Class |
createClass1() |
private void |
createClass2(java.lang.ClassLoader cl) |
private void |
createClass3(java.lang.ClassLoader cl) |
protected java.lang.ClassLoader |
getClassLoader() |
protected java.lang.ClassLoader |
getClassLoader0() |
protected java.security.ProtectionDomain |
getDomain() |
private static java.lang.Object |
getField(java.lang.Class clazz,
java.lang.String fieldName) |
(package private) static byte[] |
getFilterSignature(java.lang.Class clazz) |
static MethodHandler |
getHandler(Proxy p)
Obtains the method handler of the given proxy object.
|
java.lang.Class[] |
getInterfaces()
Obtains the interfaces set by
setInterfaces . |
java.lang.String |
getKey(java.lang.Class superClass,
java.lang.Class[] interfaces,
byte[] signature,
boolean useWriteReplace) |
private java.util.HashMap |
getMethods(java.lang.Class superClass,
java.lang.Class[] interfaceTypes) |
private void |
getMethods(java.util.HashMap hash,
java.lang.Class clazz,
java.util.Set visitedClasses) |
private static java.lang.String |
getPackageName(java.lang.String name) |
java.lang.Class |
getSuperclass()
Obtains the super class set by
setSuperclass() . |
private void |
installSignature(byte[] signature) |
private static boolean |
isBridge(java.lang.reflect.Method m) |
static boolean |
isProxyClass(java.lang.Class cl)
determine if a class is a javassist proxy class
|
boolean |
isUseCache()
test whether this factory uses the proxy cache
|
boolean |
isUseWriteReplace()
test whether this factory installs a writeReplace method in created classes
|
private static boolean |
isVisible(int mod,
java.lang.String from,
java.lang.reflect.Member meth)
Returns true if the method is visible from the package.
|
private static java.lang.String |
keyToDesc(java.lang.String key,
java.lang.reflect.Method m) |
private ClassFile |
make() |
private static MethodInfo |
makeConstructor(java.lang.String thisClassName,
java.lang.reflect.Constructor cons,
ConstPool cp,
java.lang.Class superClass,
boolean doHandlerInit) |
private void |
makeConstructors(java.lang.String thisClassName,
ClassFile cf,
ConstPool cp,
java.lang.String classname) |
private static MethodInfo |
makeDelegator(java.lang.reflect.Method meth,
java.lang.String desc,
ConstPool cp,
java.lang.Class declClass,
java.lang.String delegatorName) |
private static MethodInfo |
makeForwarder(java.lang.String thisClassName,
java.lang.reflect.Method meth,
java.lang.String desc,
ConstPool cp,
java.lang.Class declClass,
java.lang.String delegatorName,
int index,
java.util.ArrayList forwarders) |
private static void |
makeParameterList(Bytecode code,
java.lang.Class[] params) |
private static java.lang.String |
makeProxyName(java.lang.String classname) |
private void |
makeSortedMethodList() |
private static java.lang.String |
makeUniqueName(java.lang.String name,
java.util.List sortedMethods) |
private static boolean |
makeUniqueName0(java.lang.String name,
java.util.Iterator it) |
private static int |
makeWrapper(Bytecode code,
java.lang.Class type,
int regno) |
private static MethodInfo |
makeWriteReplace(ConstPool cp) |
private void |
override(java.lang.String thisClassname,
java.lang.reflect.Method meth,
java.lang.String prefix,
int index,
java.lang.String desc,
ClassFile cf,
ConstPool cp,
java.util.ArrayList forwarders) |
private int |
overrideMethods(ClassFile cf,
ConstPool cp,
java.lang.String className,
java.util.ArrayList forwarders) |
private void |
setBit(byte[] signature,
int idx) |
private void |
setField(java.lang.String fieldName,
java.lang.Object value) |
void |
setFilter(MethodFilter mf)
Sets a filter that selects the methods that will be controlled by a handler.
|
void |
setHandler(MethodHandler mi)
Deprecated.
since 3.12
use of this method is incompatible with proxy class caching.
instead clients should call method
Proxy.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. |
void |
setInterfaces(java.lang.Class[] ifs)
Sets the interfaces of a proxy class.
|
private static void |
setInterfaces(ClassFile cf,
java.lang.Class[] interfaces,
java.lang.Class proxyClass) |
void |
setSuperclass(java.lang.Class clazz)
Sets the super class of a proxy class.
|
private static void |
setThrows(MethodInfo minfo,
ConstPool cp,
java.lang.Class[] exceptions) |
private static void |
setThrows(MethodInfo minfo,
ConstPool cp,
java.lang.reflect.Method orig) |
void |
setUseCache(boolean useCache)
configure whether this factory should use the proxy cache
|
void |
setUseWriteReplace(boolean useWriteReplace)
configure whether this factory should add a writeReplace method to created classes
|
private boolean |
testBit(byte[] signature,
int idx) |
private java.lang.Class superClass
private java.lang.Class[] interfaces
private MethodFilter methodFilter
private MethodHandler handler
private java.util.List signatureMethods
private boolean hasGetHandler
private byte[] signature
private java.lang.String classname
private java.lang.String basename
private java.lang.String superName
private java.lang.Class thisClass
private boolean factoryUseCache
private boolean factoryWriteReplace
public java.lang.String writeDirectory
"."
, then the class file is written under the current
directory. This method is for debugging.
The default value is null.
private static final java.lang.Class OBJECT_TYPE
private static final java.lang.String HOLDER
private static final java.lang.String HOLDER_TYPE
private static final java.lang.String FILTER_SIGNATURE_FIELD
private static final java.lang.String FILTER_SIGNATURE_TYPE
private static final java.lang.String HANDLER
private static final java.lang.String NULL_INTERCEPTOR_HOLDER
private static final java.lang.String DEFAULT_INTERCEPTOR
private static final java.lang.String HANDLER_TYPE
private static final java.lang.String HANDLER_SETTER
private static final java.lang.String HANDLER_SETTER_TYPE
private static final java.lang.String HANDLER_GETTER
private static final java.lang.String HANDLER_GETTER_TYPE
private static final java.lang.String SERIAL_VERSION_UID_FIELD
private static final java.lang.String SERIAL_VERSION_UID_TYPE
private static final long SERIAL_VERSION_UID_VALUE
public static volatile boolean useCache
setUseCache(boolean)
public static volatile boolean useWriteReplace
ProxyObjectOutputStream
and deserialization must be via ProxyObjectInputStream
. Any attempt to serialize
proxies whose class was created with useWriteReplace set to false via a normal
ObjectOutputStream
will 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 method
setUseWriteReplace(boolean)
private static java.util.WeakHashMap proxyCache
private static char[] hexDigits
public static ProxyFactory.ClassLoaderProvider classLoaderProvider
createClass()
for obtaining
a class loader.
get()
on this ClassLoaderProvider
object
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(); } };
public static ProxyFactory.UniqueName nameGenerator
get
method does not have to be
a synchronized
method since the access to this field
is mutually exclusive and thus thread safe.private static java.util.Comparator sorter
private static final java.lang.String HANDLER_GETTER_KEY
public boolean isUseCache()
public void setUseCache(boolean useCache)
useCache
- true if this factory should use the proxy cache and false if it should not use the cachejava.lang.RuntimeException
- if a default interceptor has been set for the factorypublic boolean isUseWriteReplace()
public void setUseWriteReplace(boolean useWriteReplace)
useWriteReplace
- true if this factory should add a writeReplace method to created classes and false if it
should not add a writeReplace methodpublic static boolean isProxyClass(java.lang.Class cl)
cl
- public void setSuperclass(java.lang.Class clazz)
public java.lang.Class getSuperclass()
setSuperclass()
.public void setInterfaces(java.lang.Class[] ifs)
public java.lang.Class[] getInterfaces()
setInterfaces
.public void setFilter(MethodFilter mf)
public java.lang.Class createClass()
public java.lang.Class createClass(MethodFilter filter)
java.lang.Class createClass(byte[] signature)
signature
- private java.lang.Class createClass1()
public java.lang.String getKey(java.lang.Class superClass, java.lang.Class[] interfaces, byte[] signature, boolean useWriteReplace)
private void createClass2(java.lang.ClassLoader cl)
private void createClass3(java.lang.ClassLoader cl)
private void setField(java.lang.String fieldName, java.lang.Object value)
static byte[] getFilterSignature(java.lang.Class clazz)
private static java.lang.Object getField(java.lang.Class clazz, java.lang.String fieldName)
public static MethodHandler getHandler(Proxy p)
p
- a proxy object.protected java.lang.ClassLoader getClassLoader()
protected java.lang.ClassLoader getClassLoader0()
protected java.security.ProtectionDomain getDomain()
public java.lang.Object create(java.lang.Class[] paramTypes, java.lang.Object[] args, MethodHandler mh) throws java.lang.NoSuchMethodException, java.lang.IllegalArgumentException, java.lang.InstantiationException, java.lang.IllegalAccessException, java.lang.reflect.InvocationTargetException
paramTypes
- parameter types for a constructor.args
- arguments passed to a constructor.mh
- the method handler for the proxy class.java.lang.NoSuchMethodException
java.lang.IllegalArgumentException
java.lang.InstantiationException
java.lang.IllegalAccessException
java.lang.reflect.InvocationTargetException
public java.lang.Object create(java.lang.Class[] paramTypes, java.lang.Object[] args) throws java.lang.NoSuchMethodException, java.lang.IllegalArgumentException, java.lang.InstantiationException, java.lang.IllegalAccessException, java.lang.reflect.InvocationTargetException
paramTypes
- parameter types for a constructor.args
- arguments passed to a constructor.java.lang.NoSuchMethodException
java.lang.IllegalArgumentException
java.lang.InstantiationException
java.lang.IllegalAccessException
java.lang.reflect.InvocationTargetException
public void setHandler(MethodHandler mi)
Proxy.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.private static java.lang.String makeProxyName(java.lang.String classname)
private ClassFile make() throws CannotCompileException
CannotCompileException
private void checkClassAndSuperName()
private void allocateClassName()
private void makeSortedMethodList()
private void computeSignature(MethodFilter filter)
private void installSignature(byte[] signature)
private boolean testBit(byte[] signature, int idx)
private void setBit(byte[] signature, int idx)
private static void setInterfaces(ClassFile cf, java.lang.Class[] interfaces, java.lang.Class proxyClass)
private static void addClassInitializer(ClassFile cf, ConstPool cp, java.lang.String classname, int size, java.util.ArrayList forwarders) throws CannotCompileException
CannotCompileException
private static void callFind2Methods(Bytecode code, java.lang.String superMethod, java.lang.String thisMethod, int index, java.lang.String desc, int classVar, int arrayVar)
thisMethod
- might be null.private static void addSetter(java.lang.String classname, ClassFile cf, ConstPool cp) throws CannotCompileException
CannotCompileException
private static void addGetter(java.lang.String classname, ClassFile cf, ConstPool cp) throws CannotCompileException
CannotCompileException
private int overrideMethods(ClassFile cf, ConstPool cp, java.lang.String className, java.util.ArrayList forwarders) throws CannotCompileException
CannotCompileException
private static boolean isBridge(java.lang.reflect.Method m)
private void override(java.lang.String thisClassname, java.lang.reflect.Method meth, java.lang.String prefix, int index, java.lang.String desc, ClassFile cf, ConstPool cp, java.util.ArrayList forwarders) throws CannotCompileException
CannotCompileException
private void makeConstructors(java.lang.String thisClassName, ClassFile cf, ConstPool cp, java.lang.String classname) throws CannotCompileException
CannotCompileException
private static java.lang.String makeUniqueName(java.lang.String name, java.util.List sortedMethods)
private static boolean makeUniqueName0(java.lang.String name, java.util.Iterator it)
private static boolean isVisible(int mod, java.lang.String from, java.lang.reflect.Member meth)
mod
- the modifiers of the method.private static java.lang.String getPackageName(java.lang.String name)
private java.util.HashMap getMethods(java.lang.Class superClass, java.lang.Class[] interfaceTypes)
private void getMethods(java.util.HashMap hash, java.lang.Class clazz, java.util.Set visitedClasses)
private static java.lang.String keyToDesc(java.lang.String key, java.lang.reflect.Method m)
private static MethodInfo makeConstructor(java.lang.String thisClassName, java.lang.reflect.Constructor cons, ConstPool cp, java.lang.Class superClass, boolean doHandlerInit)
private static MethodInfo makeDelegator(java.lang.reflect.Method meth, java.lang.String desc, ConstPool cp, java.lang.Class declClass, java.lang.String delegatorName)
private static MethodInfo makeForwarder(java.lang.String thisClassName, java.lang.reflect.Method meth, java.lang.String desc, ConstPool cp, java.lang.Class declClass, java.lang.String delegatorName, int index, java.util.ArrayList forwarders)
delegatorName
- null if the original method is abstract.private static void setThrows(MethodInfo minfo, ConstPool cp, java.lang.reflect.Method orig)
private static void setThrows(MethodInfo minfo, ConstPool cp, java.lang.Class[] exceptions)
private static int addLoadParameters(Bytecode code, java.lang.Class[] params, int offset)
private static int addLoad(Bytecode code, int n, java.lang.Class type)
private static int addReturn(Bytecode code, java.lang.Class type)
private static void makeParameterList(Bytecode code, java.lang.Class[] params)
private static int makeWrapper(Bytecode code, java.lang.Class type, int regno)
private static void addUnwrapper(Bytecode code, java.lang.Class type)
private static MethodInfo makeWriteReplace(ConstPool cp)