Skip to content

Commit 423edd2

Browse files
Use BeanRegistrationAotProcessor to provide hints for auditing.
This avoids having to touch modules to add something like ImportRuntimeHints(AuditingHints.ReactiveAuditingRuntimeHints.class) to the enabled auditing annotations. Original Pull Request: #2624
1 parent 0a423ac commit 423edd2

File tree

4 files changed

+221
-0
lines changed

4 files changed

+221
-0
lines changed
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
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.aot;
17+
18+
import org.springframework.beans.factory.aot.BeanRegistrationAotContribution;
19+
import org.springframework.beans.factory.aot.BeanRegistrationAotProcessor;
20+
import org.springframework.beans.factory.support.RegisteredBean;
21+
import org.springframework.data.aot.hint.AuditingHints;
22+
import org.springframework.data.domain.AuditorAware;
23+
import org.springframework.data.domain.ReactiveAuditorAware;
24+
import org.springframework.data.repository.util.ReactiveWrappers;
25+
import org.springframework.data.repository.util.ReactiveWrappers.ReactiveLibrary;
26+
import org.springframework.lang.Nullable;
27+
import org.springframework.util.ClassUtils;
28+
29+
/**
30+
* @author Christoph Strobl
31+
* @since 3.0
32+
*/
33+
public class AuditingBeanRegistrationAotProcessor implements BeanRegistrationAotProcessor {
34+
35+
@Nullable
36+
@Override
37+
public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) {
38+
39+
if (isAuditingHandler(registeredBean)) {
40+
return (generationContext, beanRegistrationCode) -> new AuditingHints.AuditingRuntimeHints()
41+
.registerHints(generationContext.getRuntimeHints(), registeredBean.getBeanFactory().getBeanClassLoader());
42+
}
43+
if (ReactiveWrappers.isAvailable(ReactiveLibrary.PROJECT_REACTOR) && isReactiveAuditorAware(registeredBean)) {
44+
return (generationContext, beanRegistrationCode) -> new AuditingHints.ReactiveAuditingRuntimeHints()
45+
.registerHints(generationContext.getRuntimeHints(), registeredBean.getBeanFactory().getBeanClassLoader());
46+
}
47+
return null;
48+
}
49+
50+
boolean isAuditingHandler(RegisteredBean bean) {
51+
return ClassUtils.isAssignable(AuditorAware.class, bean.getBeanClass());
52+
}
53+
54+
boolean isReactiveAuditorAware(RegisteredBean bean) {
55+
return ClassUtils.isAssignable(ReactiveAuditorAware.class, bean.getBeanClass());
56+
}
57+
}

src/main/resources/META-INF/spring/aot.factories

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,5 @@ org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor=\
22
org.springframework.data.aot.SpringDataBeanFactoryInitializationAotProcessor
33
org.springframework.aot.hint.RuntimeHintsRegistrar=\
44
org.springframework.data.aot.hint.RepositoryRuntimeHints
5+
org.springframework.beans.factory.aot.BeanRegistrationAotProcessor=\
6+
org.springframework.data.aot.AuditingBeanRegistrationAotProcessor
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
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.aot;
17+
18+
import static org.assertj.core.api.Assertions.*;
19+
import static org.springframework.data.aot.BeanRegistrationContributionAssert.*;
20+
21+
import reactor.core.publisher.Mono;
22+
23+
import java.util.Optional;
24+
25+
import org.junit.jupiter.api.BeforeEach;
26+
import org.junit.jupiter.api.Test;
27+
import org.springframework.beans.factory.aot.BeanRegistrationAotContribution;
28+
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
29+
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
30+
import org.springframework.beans.factory.support.RegisteredBean;
31+
import org.springframework.data.domain.AuditorAware;
32+
import org.springframework.data.domain.ReactiveAuditorAware;
33+
34+
/**
35+
* @author Christoph Strobl
36+
*/
37+
class AuditingBeanRegistrationAotProcessorUnitTests {
38+
39+
DefaultListableBeanFactory beanFactory;
40+
41+
@BeforeEach
42+
void beforeEach() {
43+
beanFactory = new DefaultListableBeanFactory();
44+
}
45+
46+
@Test // GH-2593
47+
void contributesProxyForAuditorAwareInterface() {
48+
49+
String beanName = "auditorAware";
50+
beanFactory.registerBeanDefinition("auditorAware",
51+
BeanDefinitionBuilder.rootBeanDefinition(MyAuditorAware.class).getBeanDefinition());
52+
53+
BeanRegistrationAotContribution beanRegistrationAotContribution = new AuditingBeanRegistrationAotProcessor()
54+
.processAheadOfTime(RegisteredBean.of(beanFactory, beanName));
55+
assertThatAotContribution(beanRegistrationAotContribution).codeContributionSatisfies(code -> {
56+
code.contributesJdkProxyFor(AuditorAware.class);
57+
});
58+
}
59+
60+
@Test // GH-2593
61+
void contributesProxyForReactriveAuditorAwareInterface() {
62+
63+
String beanName = "auditorAware";
64+
beanFactory.registerBeanDefinition("auditorAware",
65+
BeanDefinitionBuilder.rootBeanDefinition(MyReactiveAuditorAware.class).getBeanDefinition());
66+
67+
BeanRegistrationAotContribution beanRegistrationAotContribution = new AuditingBeanRegistrationAotProcessor()
68+
.processAheadOfTime(RegisteredBean.of(beanFactory, beanName));
69+
assertThatAotContribution(beanRegistrationAotContribution).codeContributionSatisfies(code -> {
70+
code.contributesJdkProxyFor(ReactiveAuditorAware.class);
71+
});
72+
}
73+
74+
@Test // GH-2593
75+
void ignoresNonAuditorAware() {
76+
77+
String beanName = "auditorAware";
78+
beanFactory.registerBeanDefinition("auditorAware",
79+
BeanDefinitionBuilder.rootBeanDefinition(Nothing.class).getBeanDefinition());
80+
81+
BeanRegistrationAotContribution beanRegistrationAotContribution = new AuditingBeanRegistrationAotProcessor()
82+
.processAheadOfTime(RegisteredBean.of(beanFactory, beanName));
83+
assertThat(beanRegistrationAotContribution).isNull();
84+
}
85+
86+
static class Nothing {
87+
public Optional getCurrentAuditor() {
88+
return Optional.empty();
89+
}
90+
}
91+
92+
static class MyAuditorAware implements AuditorAware {
93+
94+
@Override
95+
public Optional getCurrentAuditor() {
96+
return Optional.empty();
97+
}
98+
}
99+
100+
static class MyReactiveAuditorAware implements ReactiveAuditorAware {
101+
102+
@Override
103+
public Mono getCurrentAuditor() {
104+
return null;
105+
}
106+
}
107+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
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.aot;
17+
18+
import static org.mockito.Mockito.*;
19+
20+
import java.util.function.Consumer;
21+
22+
import org.assertj.core.api.AbstractAssert;
23+
import org.springframework.aot.generate.ClassNameGenerator;
24+
import org.springframework.aot.generate.DefaultGenerationContext;
25+
import org.springframework.aot.generate.InMemoryGeneratedFiles;
26+
import org.springframework.beans.factory.aot.BeanRegistrationAotContribution;
27+
import org.springframework.beans.factory.aot.BeanRegistrationCode;
28+
29+
/**
30+
* @author Christoph Strobl
31+
*/
32+
public class BeanRegistrationContributionAssert extends AbstractAssert<BeanRegistrationContributionAssert, BeanRegistrationAotContribution> {
33+
34+
protected BeanRegistrationContributionAssert(BeanRegistrationAotContribution beanRegistrationAotContribution) {
35+
super(beanRegistrationAotContribution, BeanRegistrationContributionAssert.class);
36+
}
37+
38+
public static BeanRegistrationContributionAssert assertThatAotContribution(BeanRegistrationAotContribution actual) {
39+
return new BeanRegistrationContributionAssert(actual);
40+
}
41+
public BeanRegistrationContributionAssert codeContributionSatisfies(
42+
Consumer<CodeContributionAssert> assertWith) {
43+
44+
BeanRegistrationCode mockBeanRegistrationCode = mock(BeanRegistrationCode.class);
45+
46+
DefaultGenerationContext generationContext = new DefaultGenerationContext(new ClassNameGenerator(Object.class),
47+
new InMemoryGeneratedFiles());
48+
49+
this.actual.applyTo(generationContext, mockBeanRegistrationCode);
50+
51+
assertWith.accept(new CodeContributionAssert(generationContext));
52+
53+
return this;
54+
}
55+
}

0 commit comments

Comments
 (0)