Class ProxyFactory

java.lang.Object
javassist.util.proxy.ProxyFactory

public class ProxyFactory extends Object
Factory of dynamic proxy classes.

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:
  • Field Details

    • superClass

      private Class<?> superClass
    • interfaces

      private Class<?>[] interfaces
    • methodFilter

      private MethodFilter methodFilter
    • handler

      private MethodHandler handler
    • signatureMethods

      private List<Map.Entry<String,Method>> signatureMethods
    • hasGetHandler

      private boolean hasGetHandler
    • signature

      private byte[] signature
    • classname

      private String classname
    • basename

      private String basename
    • superName

      private String superName
    • thisClass

      private Class<?> thisClass
    • genericSignature

      private String genericSignature
    • factoryUseCache

      private boolean factoryUseCache
      per factory setting initialised from current setting for useCache but able to be reset before each create call
    • factoryWriteReplace

      private boolean factoryWriteReplace
      per factory setting initialised from current setting for useWriteReplace but able to be reset before each create call
    • onlyPublicMethods

      public static boolean onlyPublicMethods

      If true, only public/protected methods are forwarded to a proxy object. The class for that proxy object is loaded by the defineClass method in java.lang.invoke.MethodHandles.Lookup, which is available in Java 9 or later. This works even when sun.misc.Unsafe is not available for some reasons (it is already deprecated in Java 9).

      To load a class, Javassist first tries to use sun.misc.Unsafe and, if not available, it uses a protected method in java.lang.ClassLoader via PrivilegedAction. 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-UNNAMED must be given to the JVM when it is used (because of lack of sun.misc.Unsafe). If this argument cannot be given to the JVM, onlyPublicMethods should be set to true. Javassist will try to load by using java.lang.invoke.MethodHandles.Lookup.

      The default value is false.

      Since:
      3.22
      See Also:
    • writeDirectory

      public 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. 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

      private static final Class<?> OBJECT_TYPE
    • HOLDER

      private static final String HOLDER
      See Also:
    • HOLDER_TYPE

      private static final String HOLDER_TYPE
      See Also:
    • FILTER_SIGNATURE_FIELD

      private static final String FILTER_SIGNATURE_FIELD
      See Also:
    • FILTER_SIGNATURE_TYPE

      private static final String FILTER_SIGNATURE_TYPE
      See Also:
    • HANDLER

      private static final String HANDLER
      See Also:
    • NULL_INTERCEPTOR_HOLDER

      private static final String NULL_INTERCEPTOR_HOLDER
      See Also:
    • DEFAULT_INTERCEPTOR

      private static final String DEFAULT_INTERCEPTOR
      See Also:
    • HANDLER_TYPE

      private static final String HANDLER_TYPE
    • HANDLER_SETTER

      private static final String HANDLER_SETTER
      See Also:
    • HANDLER_SETTER_TYPE

      private static final String HANDLER_SETTER_TYPE
    • HANDLER_GETTER

      private static final String HANDLER_GETTER
      See Also:
    • HANDLER_GETTER_TYPE

      private static final String HANDLER_GETTER_TYPE
    • SERIAL_VERSION_UID_FIELD

      private static final String SERIAL_VERSION_UID_FIELD
      See Also:
    • SERIAL_VERSION_UID_TYPE

      private static final String 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 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. 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 method setUseCache(boolean)
      Since:
      3.4
    • useWriteReplace

      public static volatile boolean useWriteReplace
      If 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 a 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)
      Since:
      3.4
    • proxyCache

      private static Map<ClassLoader,Map<String,ProxyFactory.ProxyDetails>> proxyCache
    • hexDigits

      private static char[] hexDigits
    • classLoaderProvider

      public static ProxyFactory.ClassLoaderProvider classLoaderProvider
      A provider used by 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();
           }
       };
       
      Since:
      3.4
    • nameGenerator

      public static ProxyFactory.UniqueName nameGenerator
      A unique class name generator. Replacing this generator changes the algorithm to generate a unique name. The get method does not have to be a synchronized method since the access to this field is mutually exclusive and thus thread safe.
    • packageForJavaBase

      private static final String packageForJavaBase
      See Also:
    • sorter

      private static Comparator<Map.Entry<String,Method>> sorter
    • HANDLER_GETTER_KEY

      private static final String 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

      public static boolean isProxyClass(Class<?> cl)
      determine if a class is a javassist proxy class
      Parameters:
      cl -
      Returns:
      true if the class is a javassist proxy class otherwise false
    • setSuperclass

      public void setSuperclass(Class<?> clazz)
      Sets the super class of a proxy class.
    • getSuperclass

      public Class<?> getSuperclass()
      Obtains the super class set by setSuperclass().
      Since:
      3.4
    • setInterfaces

      public void setInterfaces(Class<?>[] ifs)
      Sets the interfaces of a proxy class.
    • getInterfaces

      public Class<?>[] getInterfaces()
      Obtains the interfaces set by setInterfaces.
      Since:
      3.4
    • setFilter

      public void setFilter(MethodFilter mf)
      Sets a filter that selects the methods that will be controlled by a handler.
    • setGenericSignature

      public void setGenericSignature(String sig)
      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

      public Class<?> 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

      public Class<?> createClass(MethodFilter filter)
      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

      Class<?> createClass(byte[] signature)
      Generates a proxy class with a specific signature. access is package local so ProxyObjectInputStream can use this
      Parameters:
      signature -
    • createClass

      public Class<?> createClass(MethodHandles.Lookup lookup)
      Generates a proxy class using the current filter. It loads a class file by the given java.lang.invoke.MethodHandles.Lookup object, which can be obtained by MethodHandles.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 invoke defineClass for the proxy class.
      Since:
      3.24
    • createClass

      public Class<?> createClass(MethodHandles.Lookup lookup, MethodFilter filter)
      Generates a proxy class using the supplied filter.
      Parameters:
      lookup - used for loading the proxy class. It needs an appropriate right to invoke defineClass for the proxy class.
      filter - the filter.
      Since:
      3.24
      See Also:
    • createClass

      Class<?> createClass(MethodHandles.Lookup lookup, byte[] signature)
      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 invoke defineClass for the proxy class.
      signature - the signature.
    • createClass1

      private Class<?> createClass1(MethodHandles.Lookup lookup)
    • getKey

      public String getKey(Class<?> superClass, Class<?>[] interfaces, byte[] signature, boolean useWriteReplace)
    • createClass2

      private void createClass2(ClassLoader cl, MethodHandles.Lookup lookup)
    • createClass3

      private void createClass3(ClassLoader cl, MethodHandles.Lookup lookup)
    • getClassInTheSamePackage

      private Class<?> getClassInTheSamePackage()
      Obtains a class belonging to the same package that the created proxy class belongs to. It is used to obtain an appropriate java.lang.invoke.MethodHandles.Lookup.
    • setField

      private void setField(String fieldName, Object value)
    • getFilterSignature

      static byte[] getFilterSignature(Class<?> clazz)
    • getField

      private static Object getField(Class<?> clazz, String fieldName)
    • getHandler

      public static MethodHandler getHandler(Proxy p)
      Obtains the method handler of the given proxy object.
      Parameters:
      p - a proxy object.
      Returns:
      the method handler.
      Since:
      3.16
    • getClassLoader

      protected ClassLoader getClassLoader()
    • getClassLoader0

      protected ClassLoader getClassLoader0()
    • getDomain

      protected ProtectionDomain getDomain()
    • create

      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:
      NoSuchMethodException
      IllegalArgumentException
      InstantiationException
      IllegalAccessException
      InvocationTargetException
      Since:
      3.4
    • create

      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:
      NoSuchMethodException
      IllegalArgumentException
      InstantiationException
      IllegalAccessException
      InvocationTargetException
    • setHandler

      @Deprecated public 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.
      Sets the default invocation handler. This invocation handler is shared among all the instances of a proxy class unless another is explicitly specified.
    • makeProxyName

      private static String makeProxyName(String classname)
    • make

      private ClassFile make() throws CannotCompileException
      Throws:
      CannotCompileException
    • checkClassAndSuperName

      private void checkClassAndSuperName()
    • allocateClassName

      private void allocateClassName()
    • makeSortedMethodList

      private void makeSortedMethodList()
    • computeSignature

      private void computeSignature(MethodFilter filter)
    • installSignature

      private void installSignature(byte[] signature)
    • testBit

      private boolean testBit(byte[] signature, int idx)
    • setBit

      private void setBit(byte[] signature, int idx)
    • setInterfaces

      private static void setInterfaces(ClassFile cf, Class<?>[] interfaces, Class<?> proxyClass)
    • 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

      private static boolean isBridge(Method m)
    • 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

      private static String makeUniqueName(String name, List<Map.Entry<String,Method>> sortedMethods)
    • makeUniqueName0

      private static boolean makeUniqueName0(String name, Iterator<Map.Entry<String,Method>> it)
    • isVisible

      private static boolean isVisible(int mod, String from, Member meth)
      Returns true if the method is visible from the package.
      Parameters:
      mod - the modifiers of the method.
    • getPackageName

      private static String getPackageName(String name)
    • getMethods

      private Map<String,Method> getMethods(Class<?> superClass, Class<?>[] interfaceTypes)
    • getMethods

      private void getMethods(Map<String,Method> hash, Class<?> clazz, Set<Class<?>> visitedClasses)
    • isDuplicated

      private static boolean isDuplicated(int index, Method[] methods)
    • areParametersSame

      private static boolean areParametersSame(Method method, Method targetMethod)
    • keyToDesc

      private static String keyToDesc(String key, Method m)
    • makeConstructor

      private static MethodInfo makeConstructor(String thisClassName, Constructor<?> cons, ConstPool cp, Class<?> superClass, boolean doHandlerInit)
    • makeDelegator

      private MethodInfo makeDelegator(Method meth, String desc, ConstPool cp, Class<?> declClass, String delegatorName)
    • invokespecialTarget

      private Class<?> invokespecialTarget(Class<?> declClass)
    • 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

      private static void setThrows(MethodInfo minfo, ConstPool cp, Method orig)
    • setThrows

      private static void setThrows(MethodInfo minfo, ConstPool cp, Class<?>[] exceptions)
    • addLoadParameters

      private static int addLoadParameters(Bytecode code, Class<?>[] params, int offset)
    • addLoad

      private static int addLoad(Bytecode code, int n, Class<?> type)
    • addReturn

      private static int addReturn(Bytecode code, Class<?> type)
    • makeParameterList

      private static void makeParameterList(Bytecode code, Class<?>[] params)
    • makeWrapper

      private static int makeWrapper(Bytecode code, Class<?> type, int regno)
    • addUnwrapper

      private static void addUnwrapper(Bytecode code, Class<?> type)
    • makeWriteReplace

      private static MethodInfo makeWriteReplace(ConstPool cp)