diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AbstractAspectJAdvisorFactory.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AbstractAspectJAdvisorFactory.java index bf2eb3e450..873540f96d 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AbstractAspectJAdvisorFactory.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AbstractAspectJAdvisorFactory.java @@ -87,7 +87,7 @@ public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFac * We need to detect this as "code-style" AspectJ aspects should not be * interpreted by Spring AOP. */ - private boolean compiledByAjc(Class clazz) { + static boolean compiledByAjc(Class clazz) { // The AJTypeSystem goes to great lengths to provide a uniform appearance between code-style and // annotation-style aspects. Therefore there is no 'clean' way to tell them apart. Here we rely on // an implementation detail of the AspectJ compiler. diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AspectJAdvisorBeanRegistrationAotProcessor.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AspectJAdvisorBeanRegistrationAotProcessor.java new file mode 100644 index 0000000000..47b39f7288 --- /dev/null +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AspectJAdvisorBeanRegistrationAotProcessor.java @@ -0,0 +1,58 @@ +/* + * Copyright 2002-2023 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.aop.aspectj.annotation; + +import org.springframework.aot.generate.GenerationContext; +import org.springframework.aot.hint.MemberCategory; +import org.springframework.beans.factory.aot.BeanRegistrationAotContribution; +import org.springframework.beans.factory.aot.BeanRegistrationAotProcessor; +import org.springframework.beans.factory.aot.BeanRegistrationCode; +import org.springframework.beans.factory.support.RegisteredBean; + +/** + * An AOT {@link BeanRegistrationAotProcessor} that detects the presence of + * classes compiled with AspectJ and adds the related required field hints. + * + * @author Sebastien Deleuze + * @since 6.1 + */ +public class AspectJAdvisorBeanRegistrationAotProcessor implements BeanRegistrationAotProcessor { + + @Override + public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) { + Class beanClass = registeredBean.getBeanClass(); + if (AbstractAspectJAdvisorFactory.compiledByAjc(beanClass)) { + return new AspectJAdvisorContribution(beanClass); + } + return null; + } + + private static class AspectJAdvisorContribution implements BeanRegistrationAotContribution { + + private final Class beanClass; + + public AspectJAdvisorContribution(Class beanClass) { + this.beanClass = beanClass; + } + + @Override + public void applyTo(GenerationContext generationContext, BeanRegistrationCode beanRegistrationCode) { + generationContext.getRuntimeHints().reflection().registerType(this.beanClass, MemberCategory.DECLARED_FIELDS); + } + } + +} diff --git a/spring-aop/src/main/resources/META-INF/spring/aot.factories b/spring-aop/src/main/resources/META-INF/spring/aot.factories index e3e7529ad3..c44e9d5d58 100644 --- a/spring-aop/src/main/resources/META-INF/spring/aot.factories +++ b/spring-aop/src/main/resources/META-INF/spring/aot.factories @@ -1,5 +1,6 @@ org.springframework.beans.factory.aot.BeanRegistrationAotProcessor=\ -org.springframework.aop.scope.ScopedProxyBeanRegistrationAotProcessor +org.springframework.aop.scope.ScopedProxyBeanRegistrationAotProcessor,\ +org.springframework.aop.aspectj.annotation.AspectJAdvisorBeanRegistrationAotProcessor org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor= \ org.springframework.aop.aspectj.annotation.AspectJBeanFactoryInitializationAotProcessor diff --git a/spring-aop/src/test/java/org/springframework/aop/aspectj/AspectJAdvisorBeanRegistrationAotProcessorTests.java b/spring-aop/src/test/java/org/springframework/aop/aspectj/AspectJAdvisorBeanRegistrationAotProcessorTests.java new file mode 100644 index 0000000000..80753338fa --- /dev/null +++ b/spring-aop/src/test/java/org/springframework/aop/aspectj/AspectJAdvisorBeanRegistrationAotProcessorTests.java @@ -0,0 +1,84 @@ +/* + * Copyright 2002-2023 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.aop.aspectj; + +import org.junit.jupiter.api.Test; + +import org.springframework.aop.aspectj.annotation.AspectJAdvisorBeanRegistrationAotProcessor; +import org.springframework.aot.generate.GenerationContext; +import org.springframework.aot.hint.MemberCategory; +import org.springframework.aot.hint.RuntimeHints; +import org.springframework.aot.test.generate.TestGenerationContext; +import org.springframework.beans.factory.aot.BeanRegistrationAotContribution; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.beans.factory.support.RegisteredBean; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.lang.Nullable; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.springframework.aot.hint.predicate.RuntimeHintsPredicates.reflection; + +/** + * Tests for {@link AspectJAdvisorBeanRegistrationAotProcessor}. + * + * @author Sebastien Deleuze + */ +public class AspectJAdvisorBeanRegistrationAotProcessorTests { + + private final GenerationContext generationContext = new TestGenerationContext(); + + private final RuntimeHints runtimeHints = this.generationContext.getRuntimeHints(); + + @Test + void shouldProcessesAspectJClass() { + process(AspectJClass.class); + assertThat(reflection().onType(AspectJClass.class).withMemberCategory(MemberCategory.DECLARED_FIELDS)) + .accepts(this.runtimeHints); + } + + @Test + void shouldSkipRegularClass() { + process(RegularClass.class); + assertThat(this.runtimeHints.reflection().typeHints()).isEmpty(); + } + + void process(Class beanClass) { + BeanRegistrationAotContribution contribution = createContribution(beanClass); + if (contribution != null) { + contribution.applyTo(this.generationContext, mock()); + } + } + + @Nullable + private static BeanRegistrationAotContribution createContribution(Class beanClass) { + DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); + beanFactory.registerBeanDefinition(beanClass.getName(), new RootBeanDefinition(beanClass)); + return new AspectJAdvisorBeanRegistrationAotProcessor() + .processAheadOfTime(RegisteredBean.of(beanFactory, beanClass.getName())); + } + + + static class AspectJClass { + private static java.lang.Throwable ajc$initFailureCause; + } + + static class RegularClass { + private static java.lang.Throwable initFailureCause; + } + +}