A library that provides efficient field/property access and instance creation for main Hibernate projects.
It provides a single API (AccessorFactory) with pluggable strategies for reading
and writing object state and for instantiating objects via constructors.
Requires Java 17 or later.
Add the core module to your project:
<dependency>
<groupId>org.hibernate.accessor</groupId>
<artifactId>hibernate-accessor</artifactId>
<version>0.1.0.Alpha2</version>
</dependency>The core module includes the reflection-, lambda-, and method-handle-based strategies. For the bytecode-generating strategies, add the corresponding module instead (each transitively includes the core):
<!-- ASM-based strategy -->
<dependency>
<groupId>org.hibernate.accessor</groupId>
<artifactId>hibernate-accessor-asm</artifactId>
<version>0.1.0.Alpha2</version>
</dependency>
<!-- ByteBuddy-based strategy -->
<dependency>
<groupId>org.hibernate.accessor</groupId>
<artifactId>hibernate-accessor-bytebuddy</artifactId>
<version>0.1.0.Alpha2</version>
</dependency>Built-in strategies are available via static factory methods:
// Reflection-based (simplest, no extra setup)
AccessorFactory factory = AccessorFactory.reflection();
// Method-handle-based (mostly better performance, requires a MethodHandles.Lookup)
AccessorFactory factory = AccessorFactory.methodHandle(MethodHandles.lookup());
// Lambda-based (better performance, requires a MethodHandles.Lookup)
AccessorFactory factory = AccessorFactory.lambda(MethodHandles.lookup());
// ASM-based (generates one bulk accessor class per entity with switch-based dispatch)
AccessorFactory factory = AsmAccessorFactory.factory(MethodHandles.lookup());
// ByteBuddy-based (same approach as ASM, using ByteBuddy's shaded ASM)
AccessorFactory factory = ByteBuddyAccessorFactory.factory(MethodHandles.lookup());Field nameField = Person.class.getDeclaredField("name");
ValueReader<?> reader = factory.valueReader(nameField);
ValueWriter writer = factory.valueWriter(nameField);
Person person = new Person();
writer.set(person, "Alice");
Object name = reader.get(person); // "Alice"Method getter = Person.class.getDeclaredMethod("getName");
Method setter = Person.class.getDeclaredMethod("setName", String.class);
ValueReader<?> reader = factory.valueReader(getter);
ValueWriter writer = factory.valueWriter(setter);
writer.set(person, "Bob");
Object name = reader.get(person); // "Bob"Read or write several members in a single call. Each member may be a Field or a getter/setter
Method, and values are read/written in the order the members are specified. The first argument is
the concrete class whose instances will be accessed; each member must be declared on that class or
one of its supertypes.
Field name = Person.class.getDeclaredField("name");
Field age = Person.class.getDeclaredField("age");
MultiValueReader reader = factory.multiValueReader(Person.class, name, age);
MultiValueWriter writer = factory.multiValueWriter(Person.class, name, age);
writer.set(person, new Object[] { "Alice", 30 });
Object[] values = reader.get(person); // { "Alice", 30 }For the ASM and ByteBuddy strategies this generates a single bulk accessor class per entity with
TABLESWITCH dispatch, so all members are handled by one generated class.
Constructor<Person> ctor = Person.class.getDeclaredConstructor();
Instantiator<Person> instantiator = factory.instantiator(ctor);
Person person = instantiator.create();For constructors that take arguments:
Constructor<Person> ctor = Person.class.getDeclaredConstructor(String.class, int.class);
Instantiator<Person> instantiator = factory.instantiator(ctor);
Person person = instantiator.create("Alice", 30);Every factory method also accepts a AccessorConfiguration, which carries the
MethodHandles.Lookup along with additional properties:
AccessorConfiguration configuration = new AccessorConfiguration(
MethodHandles.lookup(),
Map.of(AccessorConfiguration.DUMP_BYTECODE_DIR, "build/generated-accessors")
);
AccessorFactory factory = AsmAccessorFactory.factory(configuration);Supported properties:
| Property | Constant | Purpose |
|---|---|---|
hibernate.accessor.lookup |
AccessorConfiguration.LOOKUP |
The MethodHandles.Lookup used for access control. |
hibernate.accessor.bytecode.dump.dir |
AccessorConfiguration.DUMP_BYTECODE_DIR |
Directory to dump generated bytecode to (for debugging the ASM/ByteBuddy strategies). |
| Strategy | Factory method | Mechanism | Notes |
|---|---|---|---|
| Reflection | AccessorFactory.reflection() |
java.lang.reflect |
Simplest. Shared singleton instance. |
| Lambda | AccessorFactory.lambda(lookup) |
LambdaMetafactory |
Better throughput after warm-up. Requires a MethodHandles.Lookup with appropriate access. |
| Method handle | AccessorFactory.methodHandle(lookup) |
java.lang.invoke.MethodHandle |
Better throughput than reflection. Requires a MethodHandles.Lookup with appropriate access. |
| ASM | AsmAccessorFactory.factory(lookup) |
ASM bytecode generation | Generates one class per entity with TABLESWITCH dispatch on field/method index. Requires a MethodHandles.Lookup and a dependency on hibernate-accessor-asm (which brings org.ow2.asm:asm). |
| ByteBuddy | ByteBuddyAccessorFactory.factory(lookup) |
ByteBuddy bytecode generation | Same generated-class approach as ASM, using ByteBuddy's shaded ASM. Requires a MethodHandles.Lookup and a dependency on hibernate-accessor-bytebuddy. |