public final class ProxyAssembler
extends java.lang.Object
implements org.objectweb.asm.Opcodes
ProxyFactory
, which
exposes a public API for the definition of new proxy objects.
This mechanism is used in preference to the dynamic proxy implementation
exposed by java.lang.reflect.Proxy
because the latter does not
allow one to specify a base class to extend. In situations where a common
interface and implementation combination is used for many dynamically
specified classes, this can cause memory problems. Specifically, the
amount of PermGen memory required for java.lang.reflect.Method
objects is duplicated across each proxy class which uses the common
interface/implementation, because the methods in the common interface are
duplicated in every proxy class.
The approach used by this assembler is to allow the specification of a base
class which implements the common interface, so that these common methods
are defined only in a single class, rather than duplicated across every
dynamically defined proxy class. A user would define all common method
implementations in this base class, which each proxy class would then
inherit "for free". All unique methods are still specified by an array of
interfaces passed to this factory's static getProxy
method.
The architecture of this dynamic proxy implementation retains the use of
java.lang.reflect.InvocationHandler
. All calls to the methods
specified in the array of interfaces passed to getProxy
are
routed to the invocation handler, as with the standard, dynamic proxy
implementation. This requires that an InvocationHandler
instance is accessible to the runtime instance of the dynamically generated
class. In the standard implementation, the base class of all proxy classes
is java.lang.reflect.Proxy
, which holds a reference to the
invocation handler. However, since there are no restrictions or contracts
regarding the base class in this implementation, the invocation handler
is stored in each proxy instance. This adds a single, redundant field to
each proxy class definition, which is the trade-off for being able to
define all common methods in the base class of the user's choice. So long
as there is at least one such common method in the base class to offset the
presence of this redundant field, this implementation should be more more
memory efficient than the standard proxy implementation.
An optional (@link ProxyAssemblerPlugin plugin} can be provided to the constructor. The plugin may override the assembly of any method, and may implement additional methods after the primary proxy methods have been assembled.
Please note that generics and annotations are not supported at this time.
Note: the CGLib bytecode engineering library was considered as a simpler alternative to this implementation; however, the closest substitute in that library (the Enhancer class) automatically overrides all non-final methods of the base class in order to instrument callbacks in the overridden methods. This behavior was considered too heavyweight. A workaround would have been to require all such methods be marked as final to avoid this override behavior; however, this requirement was rejected.
The following pseudo-code representation is intended to convey the general structure of the generated proxy class. However, please note that no intermediate source code representation is produced.
public final class [ProxyClassName] implements [Interface List] { // list of delegate methods private static Method[] ma; // invocation handler private final InvocationHandler h; public [ProxyClassName](InvocationHandler handler) { this.h = handler; } public static void __initializeMethods(Method[] methods) { if (ma != null) { throw new IllegalStateException( "Proxy methods may not be set more than once"); } [ProxyClassName].ma = methods; } ... [implemented methods -- see inner class ProxyMethod for description] ... }
Modifier and Type | Class and Description |
---|---|
static class |
ProxyAssembler.ProxyMethod
Helper class to assemble a proxy method.
|
Modifier and Type | Field and Description |
---|---|
private org.objectweb.asm.ClassWriter |
classWriter
Object used to create class fields, methods, etc.
|
private static ProxyAssemblerPlugin |
defaultPlugin
No-op default plugin
|
(package private) static java.lang.String |
INIT_METHODS_METHOD
Name of static method to initialize methods upon loading proxy class
|
private java.lang.Class<?>[] |
interfaces
Interfaces which the proxy class will implement
|
private java.lang.reflect.Method[] |
methods
Method objects which will be passed to invocation handler
|
private boolean |
parentIsHandler
Does parent class implement
InvocationHandler ? |
private java.lang.String |
parentName
Name of the proxy class' parent class
|
private ProxyAssemblerPlugin |
plugin
Plugin used to augment the final proxy class
|
private java.lang.String |
proxyName
Name of the proxy class to be assembled
|
AALOAD, AASTORE, ACC_ABSTRACT, ACC_ANNOTATION, ACC_BRIDGE, ACC_DEPRECATED, ACC_ENUM, ACC_FINAL, ACC_INTERFACE, ACC_MANDATED, ACC_NATIVE, ACC_PRIVATE, ACC_PROTECTED, ACC_PUBLIC, ACC_STATIC, ACC_STRICT, ACC_SUPER, ACC_SYNCHRONIZED, ACC_SYNTHETIC, ACC_TRANSIENT, ACC_VARARGS, ACC_VOLATILE, ACONST_NULL, ALOAD, ANEWARRAY, ARETURN, ARRAYLENGTH, ASM4, ASM5, ASTORE, ATHROW, BALOAD, BASTORE, BIPUSH, CALOAD, CASTORE, CHECKCAST, D2F, D2I, D2L, DADD, DALOAD, DASTORE, DCMPG, DCMPL, DCONST_0, DCONST_1, DDIV, DLOAD, DMUL, DNEG, DOUBLE, DREM, DRETURN, DSTORE, DSUB, DUP, DUP_X1, DUP_X2, DUP2, DUP2_X1, DUP2_X2, F_APPEND, F_CHOP, F_FULL, F_NEW, F_SAME, F_SAME1, F2D, F2I, F2L, FADD, FALOAD, FASTORE, FCMPG, FCMPL, FCONST_0, FCONST_1, FCONST_2, FDIV, FLOAD, FLOAT, FMUL, FNEG, FREM, FRETURN, FSTORE, FSUB, GETFIELD, GETSTATIC, GOTO, H_GETFIELD, H_GETSTATIC, H_INVOKEINTERFACE, H_INVOKESPECIAL, H_INVOKESTATIC, H_INVOKEVIRTUAL, H_NEWINVOKESPECIAL, H_PUTFIELD, H_PUTSTATIC, I2B, I2C, I2D, I2F, I2L, I2S, IADD, IALOAD, IAND, IASTORE, ICONST_0, ICONST_1, ICONST_2, ICONST_3, ICONST_4, ICONST_5, ICONST_M1, IDIV, IF_ACMPEQ, IF_ACMPNE, IF_ICMPEQ, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, IF_ICMPLT, IF_ICMPNE, IFEQ, IFGE, IFGT, IFLE, IFLT, IFNE, IFNONNULL, IFNULL, IINC, ILOAD, IMUL, INEG, INSTANCEOF, INTEGER, INVOKEDYNAMIC, INVOKEINTERFACE, INVOKESPECIAL, INVOKESTATIC, INVOKEVIRTUAL, IOR, IREM, IRETURN, ISHL, ISHR, ISTORE, ISUB, IUSHR, IXOR, JSR, L2D, L2F, L2I, LADD, LALOAD, LAND, LASTORE, LCMP, LCONST_0, LCONST_1, LDC, LDIV, LLOAD, LMUL, LNEG, LONG, LOOKUPSWITCH, LOR, LREM, LRETURN, LSHL, LSHR, LSTORE, LSUB, LUSHR, LXOR, MONITORENTER, MONITOREXIT, MULTIANEWARRAY, NEW, NEWARRAY, NOP, NULL, POP, POP2, PUTFIELD, PUTSTATIC, RET, RETURN, SALOAD, SASTORE, SIPUSH, SWAP, T_BOOLEAN, T_BYTE, T_CHAR, T_DOUBLE, T_FLOAT, T_INT, T_LONG, T_SHORT, TABLESWITCH, TOP, UNINITIALIZED_THIS, V1_1, V1_2, V1_3, V1_4, V1_5, V1_6, V1_7, V1_8
Constructor and Description |
---|
ProxyAssembler(java.lang.String proxyName,
java.lang.String parentName,
java.lang.Class<?>[] interfaces,
java.lang.reflect.Method[] methods,
ProxyAssemblerPlugin plugin,
boolean parentIsHandler)
Constructor.
|
Modifier and Type | Method and Description |
---|---|
(package private) byte[] |
assembleClass()
Assemble the class in accordance with the Java class file format as
specified in The Java Virtual Machine Specification, Second
Edition by Tim Lindholm and Frank Yellin.
|
private void |
assembleDefaultConstructor()
Assemble the proxy class' single, default constructor.
|
private void |
assembleHandlerConstructor()
Assemble the proxy class' single constructor, which accepts and stores
the
InvocationHandler to which method calls will be
dispatched. |
private void |
assembleInitializeMethods()
Assemble the
__initializeMethods(Method[]) method. |
(package private) java.lang.reflect.Method[] |
getProxiedMethods()
Get the final list of proxied methods, which may differ from the methods passed to this
object's constructor, due to plugin intervention.
|
static final java.lang.String INIT_METHODS_METHOD
private static final ProxyAssemblerPlugin defaultPlugin
private final java.lang.String proxyName
private final java.lang.String parentName
private final java.lang.Class<?>[] interfaces
private final java.lang.reflect.Method[] methods
private final ProxyAssemblerPlugin plugin
private final boolean parentIsHandler
InvocationHandler
?private final org.objectweb.asm.ClassWriter classWriter
ProxyAssembler(java.lang.String proxyName, java.lang.String parentName, java.lang.Class<?>[] interfaces, java.lang.reflect.Method[] methods, ProxyAssemblerPlugin plugin, boolean parentIsHandler)
proxyName
- Name of the proxy class to be assembled. This is the external
name known to the class loader.parentName
- Name of the proxy class' parent class. This is the external
name known to the class loader. This class must not use
generics or annotations.interfaces
- Interfaces which the proxy class will implement. These
interfaces must not use generics or annotations.methods
- Methods to be proxied. Must not use generics or annotations.plugin
- Optional plugin. May be null
.parentIsHandler
- true
if the parent class implements the InvocationHandler
interface, else false
. If true
, no separate instance
field will be assembled to store the invocation handler reference, as the instance
of this class itself will act as the handler.java.lang.reflect.Method[] getProxiedMethods()
byte[] assembleClass()
See the class description above for details regarding the content of the assembled class.
private void assembleInitializeMethods()
__initializeMethods(Method[])
method. This is
a static method which is called once, by the ProxyFactory
,
immediately after loading the class. It is assembled such that it will
throw an IllegalStateException
if it is invoked again.private void assembleDefaultConstructor()
private void assembleHandlerConstructor()
InvocationHandler
to which method calls will be
dispatched.