Skip to content

Commit 03420f8

Browse files
committed
Add reflection hints for AspectJ advice methods
Closes gh-28711
1 parent 4565bcd commit 03420f8

File tree

3 files changed

+162
-0
lines changed

3 files changed

+162
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright 2002-2023 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+
17+
package org.springframework.aop.aspectj.annotation;
18+
19+
import java.util.List;
20+
21+
import org.springframework.aop.Advisor;
22+
import org.springframework.aop.aspectj.AbstractAspectJAdvice;
23+
import org.springframework.aot.generate.GenerationContext;
24+
import org.springframework.aot.hint.ExecutableMode;
25+
import org.springframework.aot.hint.ReflectionHints;
26+
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution;
27+
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor;
28+
import org.springframework.beans.factory.aot.BeanFactoryInitializationCode;
29+
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
30+
import org.springframework.lang.Nullable;
31+
32+
/**
33+
* {@link BeanFactoryInitializationAotProcessor} implementation responsible for registering
34+
* hints for AOP advices.
35+
*
36+
* @author Sebastien Deleuze
37+
* @since 6.0.11
38+
*/
39+
class AspectJBeanFactoryInitializationAotProcessor implements BeanFactoryInitializationAotProcessor {
40+
41+
@Nullable
42+
@Override
43+
public BeanFactoryInitializationAotContribution processAheadOfTime(ConfigurableListableBeanFactory beanFactory) {
44+
BeanFactoryAspectJAdvisorsBuilder builder = new BeanFactoryAspectJAdvisorsBuilder(beanFactory);
45+
List<Advisor> advisors = builder.buildAspectJAdvisors();
46+
return advisors.isEmpty() ? null : new AspectContribution(advisors);
47+
}
48+
49+
50+
private static class AspectContribution implements BeanFactoryInitializationAotContribution {
51+
52+
private final List<Advisor> advisors;
53+
54+
public AspectContribution(List<Advisor> advisors) {
55+
this.advisors = advisors;
56+
}
57+
58+
@Override
59+
public void applyTo(GenerationContext generationContext, BeanFactoryInitializationCode beanFactoryInitializationCode) {
60+
ReflectionHints reflectionHints = generationContext.getRuntimeHints().reflection();
61+
for (Advisor advisor : this.advisors) {
62+
if (advisor.getAdvice() instanceof AbstractAspectJAdvice aspectJAdvice) {
63+
reflectionHints.registerMethod(aspectJAdvice.getAspectJAdviceMethod(), ExecutableMode.INVOKE);
64+
}
65+
}
66+
}
67+
68+
}
69+
70+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
11
org.springframework.beans.factory.aot.BeanRegistrationAotProcessor=\
22
org.springframework.aop.scope.ScopedProxyBeanRegistrationAotProcessor
3+
4+
org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor= \
5+
org.springframework.aop.aspectj.annotation.AspectJBeanFactoryInitializationAotProcessor
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
* Copyright 2002-2023 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+
17+
package org.springframework.aop.aspectj.annotation;
18+
19+
import org.aspectj.lang.ProceedingJoinPoint;
20+
import org.aspectj.lang.annotation.Around;
21+
import org.aspectj.lang.annotation.Aspect;
22+
import org.aspectj.lang.annotation.Pointcut;
23+
import org.junit.jupiter.api.Test;
24+
25+
import org.springframework.aot.generate.GenerationContext;
26+
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
27+
import org.springframework.aot.test.generate.TestGenerationContext;
28+
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution;
29+
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
30+
import org.springframework.beans.factory.support.RootBeanDefinition;
31+
import org.springframework.lang.Nullable;
32+
33+
import static org.assertj.core.api.Assertions.assertThat;
34+
import static org.mockito.Mockito.mock;
35+
36+
/**
37+
* Tests for {@link AspectJBeanFactoryInitializationAotProcessor}.
38+
*
39+
* @author Sebastien Deleuze
40+
*/
41+
class AspectJBeanFactoryInitializationAotProcessorTests {
42+
43+
private final GenerationContext generationContext = new TestGenerationContext();
44+
45+
@Test
46+
void shouldSkipEmptyClass() {
47+
assertThat(createContribution(EmptyClass.class)).isNull();
48+
}
49+
50+
@Test
51+
void shouldProcessAspect() {
52+
process(TestAspect.class);
53+
assertThat(RuntimeHintsPredicates.reflection().onMethod(TestAspect.class, "alterReturnValue").invoke())
54+
.accepts(this.generationContext.getRuntimeHints());
55+
}
56+
57+
private void process(Class<?> beanClass) {
58+
BeanFactoryInitializationAotContribution contribution = createContribution(beanClass);
59+
if (contribution != null) {
60+
contribution.applyTo(this.generationContext, mock());
61+
}
62+
}
63+
64+
@Nullable
65+
private static BeanFactoryInitializationAotContribution createContribution(Class<?> beanClass) {
66+
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
67+
beanFactory.registerBeanDefinition(beanClass.getName(), new RootBeanDefinition(beanClass));
68+
return new AspectJBeanFactoryInitializationAotProcessor().processAheadOfTime(beanFactory);
69+
}
70+
71+
72+
static class EmptyClass { }
73+
74+
@Aspect
75+
static class TestAspect {
76+
77+
@Around("pointcut()")
78+
public Object alterReturnValue(ProceedingJoinPoint joinPoint) throws Throwable {
79+
joinPoint.proceed();
80+
return "A-from-aspect";
81+
}
82+
83+
@Pointcut("execution(* com.example.aspect.Test*.methodA(..))")
84+
private void pointcut() {
85+
}
86+
87+
}
88+
89+
}

0 commit comments

Comments
 (0)