Skip to content

Commit 850a0de

Browse files
committed
Suppress deprecated warnings for autowiring
This commit extends our handling of deprecated with AOT to autowiring. Closes gh-33295
1 parent 321e8a5 commit 850a0de

File tree

5 files changed

+261
-38
lines changed

5 files changed

+261
-38
lines changed

spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java

+25-14
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
import org.springframework.beans.factory.aot.BeanRegistrationAotContribution;
6464
import org.springframework.beans.factory.aot.BeanRegistrationAotProcessor;
6565
import org.springframework.beans.factory.aot.BeanRegistrationCode;
66+
import org.springframework.beans.factory.aot.CodeWarnings;
6667
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
6768
import org.springframework.beans.factory.config.DependencyDescriptor;
6869
import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor;
@@ -984,8 +985,11 @@ public void applyTo(GenerationContext generationContext, BeanRegistrationCode be
984985
method.addParameter(RegisteredBean.class, REGISTERED_BEAN_PARAMETER);
985986
method.addParameter(this.target, INSTANCE_PARAMETER);
986987
method.returns(this.target);
987-
method.addCode(generateMethodCode(generatedClass.getName(),
988-
generationContext.getRuntimeHints()));
988+
CodeWarnings codeWarnings = new CodeWarnings();
989+
codeWarnings.detectDeprecation(this.target);
990+
method.addCode(generateMethodCode(codeWarnings,
991+
generatedClass.getName(), generationContext.getRuntimeHints()));
992+
codeWarnings.suppress(method);
989993
});
990994
beanRegistrationCode.addInstancePostProcessor(generateMethod.toMethodReference());
991995

@@ -994,35 +998,37 @@ public void applyTo(GenerationContext generationContext, BeanRegistrationCode be
994998
}
995999
}
9961000

997-
private CodeBlock generateMethodCode(ClassName targetClassName, RuntimeHints hints) {
1001+
private CodeBlock generateMethodCode(CodeWarnings codeWarnings,
1002+
ClassName targetClassName, RuntimeHints hints) {
1003+
9981004
CodeBlock.Builder code = CodeBlock.builder();
9991005
for (AutowiredElement autowiredElement : this.autowiredElements) {
10001006
code.addStatement(generateMethodStatementForElement(
1001-
targetClassName, autowiredElement, hints));
1007+
codeWarnings, targetClassName, autowiredElement, hints));
10021008
}
10031009
code.addStatement("return $L", INSTANCE_PARAMETER);
10041010
return code.build();
10051011
}
10061012

1007-
private CodeBlock generateMethodStatementForElement(ClassName targetClassName,
1008-
AutowiredElement autowiredElement, RuntimeHints hints) {
1013+
private CodeBlock generateMethodStatementForElement(CodeWarnings codeWarnings,
1014+
ClassName targetClassName, AutowiredElement autowiredElement, RuntimeHints hints) {
10091015

10101016
Member member = autowiredElement.getMember();
10111017
boolean required = autowiredElement.required;
10121018
if (member instanceof Field field) {
10131019
return generateMethodStatementForField(
1014-
targetClassName, field, required, hints);
1020+
codeWarnings, targetClassName, field, required, hints);
10151021
}
10161022
if (member instanceof Method method) {
10171023
return generateMethodStatementForMethod(
1018-
targetClassName, method, required, hints);
1024+
codeWarnings, targetClassName, method, required, hints);
10191025
}
10201026
throw new IllegalStateException(
10211027
"Unsupported member type " + member.getClass().getName());
10221028
}
10231029

1024-
private CodeBlock generateMethodStatementForField(ClassName targetClassName,
1025-
Field field, boolean required, RuntimeHints hints) {
1030+
private CodeBlock generateMethodStatementForField(CodeWarnings codeWarnings,
1031+
ClassName targetClassName, Field field, boolean required, RuntimeHints hints) {
10261032

10271033
hints.reflection().registerField(field);
10281034
CodeBlock resolver = CodeBlock.of("$T.$L($S)",
@@ -1033,18 +1039,22 @@ private CodeBlock generateMethodStatementForField(ClassName targetClassName,
10331039
return CodeBlock.of("$L.resolveAndSet($L, $L)", resolver,
10341040
REGISTERED_BEAN_PARAMETER, INSTANCE_PARAMETER);
10351041
}
1036-
return CodeBlock.of("$L.$L = $L.resolve($L)", INSTANCE_PARAMETER,
1037-
field.getName(), resolver, REGISTERED_BEAN_PARAMETER);
1042+
else {
1043+
codeWarnings.detectDeprecation(field);
1044+
return CodeBlock.of("$L.$L = $L.resolve($L)", INSTANCE_PARAMETER,
1045+
field.getName(), resolver, REGISTERED_BEAN_PARAMETER);
1046+
}
10381047
}
10391048

1040-
private CodeBlock generateMethodStatementForMethod(ClassName targetClassName,
1041-
Method method, boolean required, RuntimeHints hints) {
1049+
private CodeBlock generateMethodStatementForMethod(CodeWarnings codeWarnings,
1050+
ClassName targetClassName, Method method, boolean required, RuntimeHints hints) {
10421051

10431052
CodeBlock.Builder code = CodeBlock.builder();
10441053
code.add("$T.$L", AutowiredMethodArgumentsResolver.class,
10451054
(!required ? "forMethod" : "forRequiredMethod"));
10461055
code.add("($S", method.getName());
10471056
if (method.getParameterCount() > 0) {
1057+
codeWarnings.detectDeprecation(method.getParameterTypes());
10481058
code.add(", $L", generateParameterTypesCode(method.getParameterTypes()));
10491059
}
10501060
code.add(")");
@@ -1054,6 +1064,7 @@ private CodeBlock generateMethodStatementForMethod(ClassName targetClassName,
10541064
code.add(".resolveAndInvoke($L, $L)", REGISTERED_BEAN_PARAMETER, INSTANCE_PARAMETER);
10551065
}
10561066
else {
1067+
codeWarnings.detectDeprecation(method);
10571068
hints.reflection().registerMethod(method, ExecutableMode.INTROSPECT);
10581069
CodeBlock arguments = new AutowiredArgumentsCodeGenerator(this.target,
10591070
method).generateCode(method.getParameterTypes());

spring-beans/src/main/java/org/springframework/beans/factory/aot/CodeWarnings.java

+29-9
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,16 @@
2121
import java.util.LinkedHashSet;
2222
import java.util.Set;
2323
import java.util.StringJoiner;
24+
import java.util.function.Consumer;
2425
import java.util.stream.Stream;
2526

2627
import org.springframework.core.ResolvableType;
2728
import org.springframework.javapoet.AnnotationSpec;
29+
import org.springframework.javapoet.AnnotationSpec.Builder;
2830
import org.springframework.javapoet.CodeBlock;
31+
import org.springframework.javapoet.FieldSpec;
2932
import org.springframework.javapoet.MethodSpec;
33+
import org.springframework.javapoet.TypeSpec;
3034
import org.springframework.lang.Nullable;
3135
import org.springframework.util.ClassUtils;
3236

@@ -38,7 +42,7 @@
3842
* @since 6.1
3943
* @see SuppressWarnings
4044
*/
41-
class CodeWarnings {
45+
public class CodeWarnings {
4246

4347
private final Set<String> warnings = new LinkedHashSet<>();
4448

@@ -99,10 +103,31 @@ public CodeWarnings detectDeprecation(ResolvableType resolvableType) {
99103
* @param method the method to update
100104
*/
101105
public void suppress(MethodSpec.Builder method) {
102-
if (this.warnings.isEmpty()) {
103-
return;
106+
suppress(annotationBuilder -> method.addAnnotation(annotationBuilder.build()));
107+
}
108+
109+
/**
110+
* Include {@link SuppressWarnings} on the specified type if necessary.
111+
* @param type the type to update
112+
*/
113+
public void suppress(TypeSpec.Builder type) {
114+
suppress(annotationBuilder -> type.addAnnotation(annotationBuilder.build()));
115+
}
116+
117+
/**
118+
* Consume the builder for {@link SuppressWarnings} if necessary. If this
119+
* instance has no warnings registered, the consumer is not invoked.
120+
* @param annotationSpec a consumer of the {@link AnnotationSpec.Builder}
121+
* @see MethodSpec.Builder#addAnnotation(AnnotationSpec)
122+
* @see TypeSpec.Builder#addAnnotation(AnnotationSpec)
123+
* @see FieldSpec.Builder#addAnnotation(AnnotationSpec)
124+
*/
125+
protected void suppress(Consumer<AnnotationSpec.Builder> annotationSpec) {
126+
if (!this.warnings.isEmpty()) {
127+
Builder annotation = AnnotationSpec.builder(SuppressWarnings.class)
128+
.addMember("value", generateValueCode());
129+
annotationSpec.accept(annotation);
104130
}
105-
method.addAnnotation(buildAnnotationSpec());
106131
}
107132

108133
/**
@@ -134,11 +159,6 @@ private void register(@Nullable Deprecated annotation) {
134159
}
135160
}
136161

137-
private AnnotationSpec buildAnnotationSpec() {
138-
return AnnotationSpec.builder(SuppressWarnings.class)
139-
.addMember("value", generateValueCode()).build();
140-
}
141-
142162
private CodeBlock generateValueCode() {
143163
if (this.warnings.size() == 1) {
144164
return CodeBlock.of("$S", this.warnings.iterator().next());

spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanRegistrationAotContributionTests.java

+84-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -21,16 +21,25 @@
2121

2222
import javax.lang.model.element.Modifier;
2323

24+
import org.junit.jupiter.api.Nested;
2425
import org.junit.jupiter.api.Test;
2526

2627
import org.springframework.aot.generate.MethodReference;
2728
import org.springframework.aot.generate.MethodReference.ArgumentCodeGenerator;
2829
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
2930
import org.springframework.aot.test.generate.TestGenerationContext;
3031
import org.springframework.beans.factory.aot.BeanRegistrationAotContribution;
32+
import org.springframework.beans.factory.aot.CodeWarnings;
3133
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
3234
import org.springframework.beans.factory.support.RegisteredBean;
3335
import org.springframework.beans.factory.support.RootBeanDefinition;
36+
import org.springframework.beans.testfixture.beans.factory.annotation.DeprecatedInjectionSamples.DeprecatedFieldInjectionPointSample;
37+
import org.springframework.beans.testfixture.beans.factory.annotation.DeprecatedInjectionSamples.DeprecatedFieldInjectionTypeSample;
38+
import org.springframework.beans.testfixture.beans.factory.annotation.DeprecatedInjectionSamples.DeprecatedMethodInjectionPointSample;
39+
import org.springframework.beans.testfixture.beans.factory.annotation.DeprecatedInjectionSamples.DeprecatedMethodInjectionTypeSample;
40+
import org.springframework.beans.testfixture.beans.factory.annotation.DeprecatedInjectionSamples.DeprecatedPrivateFieldInjectionTypeSample;
41+
import org.springframework.beans.testfixture.beans.factory.annotation.DeprecatedInjectionSamples.DeprecatedPrivateMethodInjectionTypeSample;
42+
import org.springframework.beans.testfixture.beans.factory.annotation.DeprecatedInjectionSamples.DeprecatedSample;
3443
import org.springframework.beans.testfixture.beans.factory.annotation.PackagePrivateFieldInjectionSample;
3544
import org.springframework.beans.testfixture.beans.factory.annotation.PackagePrivateMethodInjectionSample;
3645
import org.springframework.beans.testfixture.beans.factory.annotation.PrivateFieldInjectionSample;
@@ -49,6 +58,7 @@
4958
import org.springframework.javapoet.ParameterizedTypeName;
5059

5160
import static org.assertj.core.api.Assertions.assertThat;
61+
import static org.assertj.core.api.Assertions.assertThatNoException;
5262

5363
/**
5464
* Tests for {@link AutowiredAnnotationBeanPostProcessor} for AOT contributions.
@@ -199,6 +209,69 @@ void contributeWhenMethodInjectionHasMatchingPropertyValue() {
199209
assertThat(contribution).isNull();
200210
}
201211

212+
@Nested
213+
@SuppressWarnings("deprecation")
214+
class DeprecationTests {
215+
216+
private static final TestCompiler TEST_COMPILER = TestCompiler.forSystem()
217+
.withCompilerOptions("-Xlint:all", "-Xlint:-rawtypes", "-Werror");
218+
219+
@Test
220+
void contributeWhenTargetClassIsDeprecated() {
221+
RegisteredBean registeredBean = getAndApplyContribution(DeprecatedSample.class);
222+
compileAndCheckWarnings(registeredBean);
223+
}
224+
225+
@Test
226+
void contributeWhenFieldInjectionsUsesADeprecatedType() {
227+
RegisteredBean registeredBean = getAndApplyContribution(
228+
DeprecatedFieldInjectionTypeSample.class);
229+
compileAndCheckWarnings(registeredBean);
230+
}
231+
232+
@Test
233+
void contributeWhenFieldInjectionsUsesADeprecatedTypeWithReflection() {
234+
RegisteredBean registeredBean = getAndApplyContribution(
235+
DeprecatedPrivateFieldInjectionTypeSample.class);
236+
compileAndCheckWarnings(registeredBean);
237+
}
238+
239+
@Test
240+
void contributeWhenFieldInjectionsIsDeprecated() {
241+
RegisteredBean registeredBean = getAndApplyContribution(
242+
DeprecatedFieldInjectionPointSample.class);
243+
compileAndCheckWarnings(registeredBean);
244+
}
245+
246+
@Test
247+
void contributeWhenMethodInjectionsUsesADeprecatedType() {
248+
RegisteredBean registeredBean = getAndApplyContribution(
249+
DeprecatedMethodInjectionTypeSample.class);
250+
compileAndCheckWarnings(registeredBean);
251+
}
252+
253+
@Test
254+
void contributeWhenMethodInjectionsUsesADeprecatedTypeWithReflection() {
255+
RegisteredBean registeredBean = getAndApplyContribution(
256+
DeprecatedPrivateMethodInjectionTypeSample.class);
257+
compileAndCheckWarnings(registeredBean);
258+
}
259+
260+
@Test
261+
void contributeWhenMethodInjectionsIsDeprecated() {
262+
RegisteredBean registeredBean = getAndApplyContribution(
263+
DeprecatedMethodInjectionPointSample.class);
264+
compileAndCheckWarnings(registeredBean);
265+
}
266+
267+
268+
private void compileAndCheckWarnings(RegisteredBean registeredBean) {
269+
assertThatNoException().isThrownBy(() -> compile(TEST_COMPILER, registeredBean,
270+
((instanceSupplier, compiled) -> {})));
271+
}
272+
273+
}
274+
202275
private RegisteredBean getAndApplyContribution(Class<?> beanClass) {
203276
RegisteredBean registeredBean = registerBean(beanClass);
204277
BeanRegistrationAotContribution contribution = this.beanPostProcessor.processAheadOfTime(registeredBean);
@@ -218,11 +291,17 @@ private static SourceFile getSourceFile(Compiled compiled, Class<?> sample) {
218291
return compiled.getSourceFileFromPackage(sample.getPackageName());
219292
}
220293

221-
@SuppressWarnings("unchecked")
222294
private void compile(RegisteredBean registeredBean,
223295
BiConsumer<BiFunction<RegisteredBean, Object, Object>, Compiled> result) {
296+
compile(TestCompiler.forSystem(), registeredBean, result);
297+
}
298+
299+
@SuppressWarnings("unchecked")
300+
private void compile(TestCompiler testCompiler, RegisteredBean registeredBean,
301+
BiConsumer<BiFunction<RegisteredBean, Object, Object>, Compiled> result) {
224302
Class<?> target = registeredBean.getBeanClass();
225303
MethodReference methodReference = this.beanRegistrationCode.getInstancePostProcessors().get(0);
304+
CodeWarnings codeWarnings = new CodeWarnings();
226305
this.beanRegistrationCode.getTypeBuilder().set(type -> {
227306
CodeBlock methodInvocation = methodReference.toInvokeCodeBlock(
228307
ArgumentCodeGenerator.of(RegisteredBean.class, "registeredBean").and(target, "instance"),
@@ -235,10 +314,11 @@ private void compile(RegisteredBean registeredBean,
235314
.addParameter(target, "instance").returns(target)
236315
.addStatement("return $L", methodInvocation)
237316
.build());
238-
317+
codeWarnings.detectDeprecation(target);
318+
codeWarnings.suppress(type);
239319
});
240320
this.generationContext.writeGeneratedContent();
241-
TestCompiler.forSystem().with(this.generationContext).compile(compiled ->
321+
testCompiler.with(this.generationContext).printFiles(System.out).compile(compiled ->
242322
result.accept(compiled.getInstance(BiFunction.class), compiled));
243323
}
244324

0 commit comments

Comments
 (0)