Skip to content

Commit 6943cfa

Browse files
Add AOT jdbc repository support.
We now use the AOT infrastructure of Spring Framework 6 and data commons to provide AOT support building the foundation for native image compilation. Additionally we register hints for GraalVM native image. Update auditing configuration to avoid inner bean definitions. See: #1269
1 parent dd3a69c commit 6943cfa

File tree

6 files changed

+125
-25
lines changed

6 files changed

+125
-25
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright 2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.jdbc.aot;
17+
18+
import java.util.Arrays;
19+
20+
import org.springframework.aot.hint.MemberCategory;
21+
import org.springframework.aot.hint.RuntimeHints;
22+
import org.springframework.aot.hint.RuntimeHintsRegistrar;
23+
import org.springframework.aot.hint.TypeReference;
24+
import org.springframework.data.jdbc.repository.support.SimpleJdbcRepository;
25+
import org.springframework.data.relational.auditing.RelationalAuditingCallback;
26+
import org.springframework.data.relational.core.mapping.event.AfterConvertCallback;
27+
import org.springframework.data.relational.core.mapping.event.AfterDeleteCallback;
28+
import org.springframework.data.relational.core.mapping.event.AfterSaveCallback;
29+
import org.springframework.data.relational.core.mapping.event.BeforeConvertCallback;
30+
import org.springframework.data.relational.core.mapping.event.BeforeDeleteCallback;
31+
import org.springframework.data.relational.core.mapping.event.BeforeSaveCallback;
32+
import org.springframework.lang.Nullable;
33+
34+
/**
35+
* @author Christoph Strobl
36+
* @since 3.0
37+
*/
38+
public class DataJdbcRuntimeHints implements RuntimeHintsRegistrar {
39+
40+
@Override
41+
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
42+
43+
hints.reflection().registerTypes(
44+
Arrays.asList(TypeReference.of(SimpleJdbcRepository.class), TypeReference.of(AfterConvertCallback.class),
45+
TypeReference.of(AfterDeleteCallback.class), TypeReference.of(AfterSaveCallback.class),
46+
TypeReference.of(BeforeConvertCallback.class), TypeReference.of(BeforeDeleteCallback.class),
47+
TypeReference.of(BeforeSaveCallback.class), TypeReference.of(RelationalAuditingCallback.class)),
48+
builder -> builder.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
49+
MemberCategory.INVOKE_PUBLIC_METHODS));
50+
51+
hints.proxies().registerJdkProxy(TypeReference.of("org.springframework.data.jdbc.core.convert.RelationResolver"),
52+
TypeReference.of("org.springframework.aop.SpringProxy"),
53+
TypeReference.of("org.springframework.aop.framework.Advised"),
54+
TypeReference.of("org.springframework.core.DecoratingProxy"));
55+
}
56+
}

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/JdbcAuditingRegistrar.java

+54-18
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,19 @@
1717

1818
import java.lang.annotation.Annotation;
1919

20+
import org.springframework.beans.factory.ListableBeanFactory;
2021
import org.springframework.beans.factory.config.BeanDefinition;
2122
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
23+
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
2224
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
2325
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
2426
import org.springframework.data.auditing.IsNewAwareAuditingHandler;
2527
import org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport;
2628
import org.springframework.data.auditing.config.AuditingConfiguration;
29+
import org.springframework.data.config.ParsingUtils;
30+
import org.springframework.data.mapping.context.PersistentEntities;
2731
import org.springframework.data.relational.auditing.RelationalAuditingCallback;
28-
import org.springframework.data.repository.config.PersistentEntitiesFactoryBean;
32+
import org.springframework.lang.Nullable;
2933
import org.springframework.util.Assert;
3034

3135
/**
@@ -39,7 +43,6 @@
3943
class JdbcAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport {
4044

4145
private static final String AUDITING_HANDLER_BEAN_NAME = "jdbcAuditingHandler";
42-
private static final String JDBC_MAPPING_CONTEXT_BEAN_NAME = "jdbcMappingContext";
4346

4447
/**
4548
* {@inheritDoc}
@@ -63,36 +66,69 @@ protected String getAuditingHandlerBeanName() {
6366
return AUDITING_HANDLER_BEAN_NAME;
6467
}
6568

69+
@Override
70+
protected void postProcess(BeanDefinitionBuilder builder, AuditingConfiguration configuration,
71+
BeanDefinitionRegistry registry) {
72+
potentiallyRegisterJdbcPersistentEntities(builder, registry);
73+
}
74+
6675
@Override
6776
protected BeanDefinitionBuilder getAuditHandlerBeanDefinitionBuilder(AuditingConfiguration configuration) {
6877

6978
Assert.notNull(configuration, "AuditingConfiguration must not be null");
7079

71-
BeanDefinitionBuilder builder = configureDefaultAuditHandlerAttributes(configuration,
80+
return configureDefaultAuditHandlerAttributes(configuration,
7281
BeanDefinitionBuilder.rootBeanDefinition(IsNewAwareAuditingHandler.class));
82+
}
7383

84+
@Override
85+
protected void registerAuditListenerBeanDefinition(BeanDefinition auditingHandlerDefinition,
86+
BeanDefinitionRegistry registry) {
87+
88+
Assert.notNull(auditingHandlerDefinition, "BeanDefinition must not be null");
89+
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
7490

75-
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(PersistentEntitiesFactoryBean.class);
76-
definition.addConstructorArgReference(JDBC_MAPPING_CONTEXT_BEAN_NAME);
91+
BeanDefinitionBuilder listenerBeanDefinitionBuilder = BeanDefinitionBuilder
92+
.rootBeanDefinition(RelationalAuditingCallback.class);
93+
listenerBeanDefinitionBuilder
94+
.addConstructorArgValue(ParsingUtils.getObjectFactoryBeanDefinition(AUDITING_HANDLER_BEAN_NAME, registry));
7795

78-
return builder.addConstructorArgValue(definition.getBeanDefinition());
96+
registerInfrastructureBeanWithId(listenerBeanDefinitionBuilder.getBeanDefinition(),
97+
RelationalAuditingCallback.class.getName(), registry);
7998
}
8099

81-
/**
82-
* Register the bean definition of {@link RelationalAuditingCallback}. {@inheritDoc}
83-
*
84-
* @see AuditingBeanDefinitionRegistrarSupport#registerAuditListenerBeanDefinition(BeanDefinition,
85-
* BeanDefinitionRegistry)
86-
*/
87-
@Override
88-
protected void registerAuditListenerBeanDefinition(BeanDefinition auditingHandlerDefinition,
100+
static void potentiallyRegisterJdbcPersistentEntities(BeanDefinitionBuilder builder,
89101
BeanDefinitionRegistry registry) {
90102

91-
Class<?> listenerClass = RelationalAuditingCallback.class;
92-
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(listenerClass) //
93-
.addConstructorArgReference(AUDITING_HANDLER_BEAN_NAME);
103+
String persistentEntitiesBeanName = JdbcAuditingRegistrar.detectPersistentEntitiesBeanName(registry);
104+
105+
if (persistentEntitiesBeanName == null) {
106+
107+
persistentEntitiesBeanName = BeanDefinitionReaderUtils.uniqueBeanName("jdbcPersistentEntities", registry);
108+
109+
// TODO: https://github.com/spring-projects/spring-framework/issues/28728
110+
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(PersistentEntities.class) //
111+
.setFactoryMethod("of") //
112+
.addConstructorArgReference("jdbcMappingContext");
113+
114+
registry.registerBeanDefinition(persistentEntitiesBeanName, definition.getBeanDefinition());
115+
}
116+
117+
builder.addConstructorArgReference(persistentEntitiesBeanName);
118+
}
119+
120+
@Nullable
121+
private static String detectPersistentEntitiesBeanName(BeanDefinitionRegistry registry) {
122+
123+
if (registry instanceof ListableBeanFactory beanFactory) {
124+
for (String bn : beanFactory.getBeanNamesForType(PersistentEntities.class)) {
125+
if (bn.startsWith("jdbc")) {
126+
return bn;
127+
}
128+
}
129+
}
94130

95-
registerInfrastructureBeanWithId(builder.getRawBeanDefinition(), listenerClass.getName(), registry);
131+
return null;
96132
}
97133

98134
}

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/JdbcRepositoryConfigExtension.java

+5
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ protected String getModulePrefix() {
5656
return getModuleName().toLowerCase(Locale.US);
5757
}
5858

59+
@Override
60+
public String getModuleIdentifier() {
61+
return getModulePrefix();
62+
}
63+
5964
@Override
6065
public void postProcess(BeanDefinitionBuilder builder, RepositoryConfigurationSource source) {
6166

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
org.springframework.aot.hint.RuntimeHintsRegistrar=\
2+
org.springframework.data.jdbc.aot.DataJdbcRuntimeHints

spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/TestConfiguration.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,11 @@ public class TestConfiguration {
7272
@Bean
7373
JdbcRepositoryFactory jdbcRepositoryFactory(
7474
@Qualifier("defaultDataAccessStrategy") DataAccessStrategy dataAccessStrategy, RelationalMappingContext context,
75-
Dialect dialect, JdbcConverter converter, Optional<NamedQueries> namedQueries) {
75+
Dialect dialect, JdbcConverter converter, Optional<List<NamedQueries>> namedQueries) {
7676

7777
JdbcRepositoryFactory factory = new JdbcRepositoryFactory(dataAccessStrategy, context, converter, dialect,
7878
publisher, namedParameterJdbcTemplate());
79-
namedQueries.ifPresent(factory::setNamedQueries);
79+
namedQueries.map(it -> it.iterator().next()).ifPresent(factory::setNamedQueries);
8080
return factory;
8181
}
8282

spring-data-relational/src/main/java/org/springframework/data/relational/auditing/RelationalAuditingCallback.java

+6-5
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package org.springframework.data.relational.auditing;
1717

18+
import org.springframework.beans.factory.ObjectFactory;
1819
import org.springframework.context.ApplicationListener;
1920
import org.springframework.core.Ordered;
2021
import org.springframework.data.auditing.IsNewAwareAuditingHandler;
@@ -41,13 +42,13 @@ public class RelationalAuditingCallback implements BeforeConvertCallback<Object>
4142
*/
4243
public static final int AUDITING_ORDER = 100;
4344

44-
private final IsNewAwareAuditingHandler handler;
45+
private final ObjectFactory<IsNewAwareAuditingHandler> auditingHandlerFactory;
4546

46-
public RelationalAuditingCallback(IsNewAwareAuditingHandler handler) {
47+
public RelationalAuditingCallback(ObjectFactory<IsNewAwareAuditingHandler> auditingHandlerFactory) {
4748

48-
Assert.notNull(handler, "Handler must not be null;");
49+
Assert.notNull(auditingHandlerFactory, "IsNewAwareAuditingHandler must not be null;");
4950

50-
this.handler = handler;
51+
this.auditingHandlerFactory = auditingHandlerFactory;
5152
}
5253

5354
@Override
@@ -57,6 +58,6 @@ public int getOrder() {
5758

5859
@Override
5960
public Object onBeforeConvert(Object entity) {
60-
return handler.markAudited(entity);
61+
return auditingHandlerFactory.getObject().markAudited(entity);
6162
}
6263
}

0 commit comments

Comments
 (0)