Skip to content

Commit

Permalink
Generate Hibernate ORM InstantiationOptimizers to avoid reflection
Browse files Browse the repository at this point in the history
  • Loading branch information
mbladel committed Oct 8, 2024
1 parent 6543030 commit 1fe3bdc
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
import static io.quarkus.deployment.annotations.ExecutionTime.RUNTIME_INIT;
import static io.quarkus.deployment.annotations.ExecutionTime.STATIC_INIT;
import static io.quarkus.hibernate.orm.deployment.HibernateConfigUtil.firstPresent;
import static io.quarkus.hibernate.orm.runtime.service.bytecodeprovider.QuarkusRuntimeBytecodeProviderInitiator.INSTANTIATOR_SUFFIX;
import static org.hibernate.cfg.AvailableSettings.JAKARTA_SHARED_CACHE_MODE;
import static org.hibernate.cfg.AvailableSettings.USE_DIRECT_REFERENCE_CACHE_ENTRIES;
import static org.hibernate.cfg.AvailableSettings.USE_QUERY_CACHE;
import static org.hibernate.cfg.AvailableSettings.USE_SECOND_LEVEL_CACHE;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;

import java.io.IOException;
import java.net.URL;
Expand Down Expand Up @@ -44,6 +46,7 @@
import org.hibernate.boot.archive.scan.spi.ClassDescriptor;
import org.hibernate.boot.archive.scan.spi.PackageDescriptor;
import org.hibernate.boot.beanvalidation.BeanValidationIntegrator;
import org.hibernate.bytecode.spi.ReflectionOptimizer;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.id.SequenceMismatchStrategy;
import org.hibernate.integrator.spi.Integrator;
Expand Down Expand Up @@ -80,6 +83,7 @@
import io.quarkus.datasource.common.runtime.DatabaseKind;
import io.quarkus.deployment.Capabilities;
import io.quarkus.deployment.Capability;
import io.quarkus.deployment.GeneratedClassGizmoAdaptor;
import io.quarkus.deployment.IsDevelopment;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
Expand Down Expand Up @@ -108,6 +112,10 @@
import io.quarkus.deployment.recording.RecorderContext;
import io.quarkus.deployment.util.IoUtil;
import io.quarkus.deployment.util.ServiceUtil;
import io.quarkus.gizmo.ClassCreator;
import io.quarkus.gizmo.MethodCreator;
import io.quarkus.gizmo.MethodDescriptor;
import io.quarkus.gizmo.ResultHandle;
import io.quarkus.hibernate.orm.PersistenceUnit;
import io.quarkus.hibernate.orm.deployment.HibernateOrmConfigPersistenceUnit.IdentifierQuotingStrategy;
import io.quarkus.hibernate.orm.deployment.integration.HibernateOrmIntegrationRuntimeConfiguredBuildItem;
Expand Down Expand Up @@ -612,6 +620,29 @@ public HibernateModelClassCandidatesForFieldAccessBuildItem candidatesForFieldAc
return new HibernateModelClassCandidatesForFieldAccessBuildItem(jpaModel.getManagedClassNames());
}

@BuildStep
void instantiationOptimizers(JpaModelBuildItem jpaModel,
CombinedIndexBuildItem index,
BuildProducer<GeneratedClassBuildItem> generatedClasses) {
GeneratedClassGizmoAdaptor classOutput = new GeneratedClassGizmoAdaptor(generatedClasses, true);
// Generate InstantiationOptimizers for all managed classes to avoid reflection
for (String i : jpaModel.getManagedClassNames()) {
ClassInfo classInfo = index.getIndex().getClassByName(i);
if (classInfo != null && !classInfo.isAbstract() && classInfo.hasNoArgsConstructor()) {
String className = classInfo.name() + INSTANTIATOR_SUFFIX;
try (ClassCreator classCreator = ClassCreator.builder().classOutput(classOutput)
.interfaces(ReflectionOptimizer.InstantiationOptimizer.class).className(className).build()) {
MethodCreator method = classCreator
.getMethodCreator("newInstance", Object.class)
.setModifiers(ACC_PUBLIC);
ResultHandle constructorHandle = method
.newInstance(MethodDescriptor.ofConstructor(classInfo.name().toString()));
method.returnValue(constructorHandle);
}
}
}
}

@BuildStep
@Record(RUNTIME_INIT)
public void build(HibernateOrmRecorder recorder, HibernateOrmConfig hibernateOrmConfig,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import io.quarkus.hibernate.orm.runtime.customized.QuarkusRuntimeProxyFactoryFactory;

public final class QuarkusRuntimeBytecodeProviderInitiator implements StandardServiceInitiator<BytecodeProvider> {
public static final String INSTANTIATOR_SUFFIX = "$QuarkusInstantiationOptimizer";

private final QuarkusRuntimeProxyFactoryFactory statefulProxyFactory;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package io.quarkus.hibernate.orm.runtime.service.bytecodeprovider;

import static io.quarkus.hibernate.orm.runtime.service.bytecodeprovider.QuarkusRuntimeBytecodeProviderInitiator.INSTANTIATOR_SUFFIX;

import java.util.Map;

import org.hibernate.bytecode.enhance.spi.EnhancementContext;
Expand Down Expand Up @@ -35,6 +37,27 @@ public ReflectionOptimizer getReflectionOptimizer(

@Override
public ReflectionOptimizer getReflectionOptimizer(Class<?> clazz, Map<String, PropertyAccess> propertyAccessMap) {
try {
Class<?> instantiatorClass = Class.forName(clazz.getName() + INSTANTIATOR_SUFFIX, true,
Thread.currentThread().getContextClassLoader());
ReflectionOptimizer.InstantiationOptimizer optimizer = (ReflectionOptimizer.InstantiationOptimizer) instantiatorClass
.getDeclaredConstructor().newInstance();
return new ReflectionOptimizer() {
@Override
public InstantiationOptimizer getInstantiationOptimizer() {
return optimizer;
}

@Override
public AccessOptimizer getAccessOptimizer() {
return null;
}
};
} catch (Exception e) {
// Not all mapped classes will have access-optimizers, integrators might contribute additional mappings
// to Hibernate that do not get picked up by the Jandex index at build time and won't have an
// InstantiationOptimizer to load - just ignore the exception in those cases
}
return null;
}

Expand Down

0 comments on commit 1fe3bdc

Please sign in to comment.