Browse Source

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
pull/2652/head
Christoph Strobl 4 years ago
parent
commit
423edd21ea
No known key found for this signature in database
GPG Key ID: 8CC1AB53391458C8
  1. 57
      src/main/java/org/springframework/data/aot/AuditingBeanRegistrationAotProcessor.java
  2. 2
      src/main/resources/META-INF/spring/aot.factories
  3. 107
      src/test/java/org/springframework/data/aot/AuditingBeanRegistrationAotProcessorUnitTests.java
  4. 55
      src/test/java/org/springframework/data/aot/BeanRegistrationContributionAssert.java

57
src/main/java/org/springframework/data/aot/AuditingBeanRegistrationAotProcessor.java

@ -0,0 +1,57 @@ @@ -0,0 +1,57 @@
/*
* Copyright 2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.aot;
import org.springframework.beans.factory.aot.BeanRegistrationAotContribution;
import org.springframework.beans.factory.aot.BeanRegistrationAotProcessor;
import org.springframework.beans.factory.support.RegisteredBean;
import org.springframework.data.aot.hint.AuditingHints;
import org.springframework.data.domain.AuditorAware;
import org.springframework.data.domain.ReactiveAuditorAware;
import org.springframework.data.repository.util.ReactiveWrappers;
import org.springframework.data.repository.util.ReactiveWrappers.ReactiveLibrary;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
/**
* @author Christoph Strobl
* @since 3.0
*/
public class AuditingBeanRegistrationAotProcessor implements BeanRegistrationAotProcessor {
@Nullable
@Override
public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) {
if (isAuditingHandler(registeredBean)) {
return (generationContext, beanRegistrationCode) -> new AuditingHints.AuditingRuntimeHints()
.registerHints(generationContext.getRuntimeHints(), registeredBean.getBeanFactory().getBeanClassLoader());
}
if (ReactiveWrappers.isAvailable(ReactiveLibrary.PROJECT_REACTOR) && isReactiveAuditorAware(registeredBean)) {
return (generationContext, beanRegistrationCode) -> new AuditingHints.ReactiveAuditingRuntimeHints()
.registerHints(generationContext.getRuntimeHints(), registeredBean.getBeanFactory().getBeanClassLoader());
}
return null;
}
boolean isAuditingHandler(RegisteredBean bean) {
return ClassUtils.isAssignable(AuditorAware.class, bean.getBeanClass());
}
boolean isReactiveAuditorAware(RegisteredBean bean) {
return ClassUtils.isAssignable(ReactiveAuditorAware.class, bean.getBeanClass());
}
}

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

@ -2,3 +2,5 @@ org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor=\ @@ -2,3 +2,5 @@ org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor=\
org.springframework.data.aot.SpringDataBeanFactoryInitializationAotProcessor
org.springframework.aot.hint.RuntimeHintsRegistrar=\
org.springframework.data.aot.hint.RepositoryRuntimeHints
org.springframework.beans.factory.aot.BeanRegistrationAotProcessor=\
org.springframework.data.aot.AuditingBeanRegistrationAotProcessor

107
src/test/java/org/springframework/data/aot/AuditingBeanRegistrationAotProcessorUnitTests.java

@ -0,0 +1,107 @@ @@ -0,0 +1,107 @@
/*
* Copyright 2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.aot;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.aot.BeanRegistrationContributionAssert.*;
import reactor.core.publisher.Mono;
import java.util.Optional;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.aot.BeanRegistrationAotContribution;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RegisteredBean;
import org.springframework.data.domain.AuditorAware;
import org.springframework.data.domain.ReactiveAuditorAware;
/**
* @author Christoph Strobl
*/
class AuditingBeanRegistrationAotProcessorUnitTests {
DefaultListableBeanFactory beanFactory;
@BeforeEach
void beforeEach() {
beanFactory = new DefaultListableBeanFactory();
}
@Test // GH-2593
void contributesProxyForAuditorAwareInterface() {
String beanName = "auditorAware";
beanFactory.registerBeanDefinition("auditorAware",
BeanDefinitionBuilder.rootBeanDefinition(MyAuditorAware.class).getBeanDefinition());
BeanRegistrationAotContribution beanRegistrationAotContribution = new AuditingBeanRegistrationAotProcessor()
.processAheadOfTime(RegisteredBean.of(beanFactory, beanName));
assertThatAotContribution(beanRegistrationAotContribution).codeContributionSatisfies(code -> {
code.contributesJdkProxyFor(AuditorAware.class);
});
}
@Test // GH-2593
void contributesProxyForReactriveAuditorAwareInterface() {
String beanName = "auditorAware";
beanFactory.registerBeanDefinition("auditorAware",
BeanDefinitionBuilder.rootBeanDefinition(MyReactiveAuditorAware.class).getBeanDefinition());
BeanRegistrationAotContribution beanRegistrationAotContribution = new AuditingBeanRegistrationAotProcessor()
.processAheadOfTime(RegisteredBean.of(beanFactory, beanName));
assertThatAotContribution(beanRegistrationAotContribution).codeContributionSatisfies(code -> {
code.contributesJdkProxyFor(ReactiveAuditorAware.class);
});
}
@Test // GH-2593
void ignoresNonAuditorAware() {
String beanName = "auditorAware";
beanFactory.registerBeanDefinition("auditorAware",
BeanDefinitionBuilder.rootBeanDefinition(Nothing.class).getBeanDefinition());
BeanRegistrationAotContribution beanRegistrationAotContribution = new AuditingBeanRegistrationAotProcessor()
.processAheadOfTime(RegisteredBean.of(beanFactory, beanName));
assertThat(beanRegistrationAotContribution).isNull();
}
static class Nothing {
public Optional getCurrentAuditor() {
return Optional.empty();
}
}
static class MyAuditorAware implements AuditorAware {
@Override
public Optional getCurrentAuditor() {
return Optional.empty();
}
}
static class MyReactiveAuditorAware implements ReactiveAuditorAware {
@Override
public Mono getCurrentAuditor() {
return null;
}
}
}

55
src/test/java/org/springframework/data/aot/BeanRegistrationContributionAssert.java

@ -0,0 +1,55 @@ @@ -0,0 +1,55 @@
/*
* Copyright 2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.aot;
import static org.mockito.Mockito.*;
import java.util.function.Consumer;
import org.assertj.core.api.AbstractAssert;
import org.springframework.aot.generate.ClassNameGenerator;
import org.springframework.aot.generate.DefaultGenerationContext;
import org.springframework.aot.generate.InMemoryGeneratedFiles;
import org.springframework.beans.factory.aot.BeanRegistrationAotContribution;
import org.springframework.beans.factory.aot.BeanRegistrationCode;
/**
* @author Christoph Strobl
*/
public class BeanRegistrationContributionAssert extends AbstractAssert<BeanRegistrationContributionAssert, BeanRegistrationAotContribution> {
protected BeanRegistrationContributionAssert(BeanRegistrationAotContribution beanRegistrationAotContribution) {
super(beanRegistrationAotContribution, BeanRegistrationContributionAssert.class);
}
public static BeanRegistrationContributionAssert assertThatAotContribution(BeanRegistrationAotContribution actual) {
return new BeanRegistrationContributionAssert(actual);
}
public BeanRegistrationContributionAssert codeContributionSatisfies(
Consumer<CodeContributionAssert> assertWith) {
BeanRegistrationCode mockBeanRegistrationCode = mock(BeanRegistrationCode.class);
DefaultGenerationContext generationContext = new DefaultGenerationContext(new ClassNameGenerator(Object.class),
new InMemoryGeneratedFiles());
this.actual.applyTo(generationContext, mockBeanRegistrationCode);
assertWith.accept(new CodeContributionAssert(generationContext));
return this;
}
}
Loading…
Cancel
Save