Handle deprecation warning in AOT-generated code
Closes gh-29597
This commit is contained in:
@@ -164,11 +164,14 @@ class BeanDefinitionMethodGenerator {
|
||||
|
||||
this.aotContributions.forEach(aotContribution -> aotContribution.applyTo(generationContext, codeGenerator));
|
||||
|
||||
CodeWarnings codeWarnings = new CodeWarnings();
|
||||
codeWarnings.detectDeprecation(this.registeredBean.getBeanClass());
|
||||
return generatedMethods.add("getBeanDefinition", method -> {
|
||||
method.addJavadoc("Get the $L definition for '$L'.",
|
||||
(this.registeredBean.isInnerBean() ? "inner-bean" : "bean"),
|
||||
getName());
|
||||
method.addModifiers(modifier, Modifier.STATIC);
|
||||
codeWarnings.suppress(method);
|
||||
method.returns(BeanDefinition.class);
|
||||
method.addCode(codeGenerator.generateCode(generationContext));
|
||||
});
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* 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.beans.factory.aot;
|
||||
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.javapoet.AnnotationSpec;
|
||||
import org.springframework.javapoet.CodeBlock;
|
||||
import org.springframework.javapoet.MethodSpec;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Helper class to register warnings that the compiler may trigger on
|
||||
* generated code.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @see SuppressWarnings
|
||||
*/
|
||||
class CodeWarnings {
|
||||
|
||||
private final Set<String> warnings = new LinkedHashSet<>();
|
||||
|
||||
/**
|
||||
* Register a warning to be included for this block. Does nothing if
|
||||
* the warning is already registered.
|
||||
* @param warning the warning to register, if it hasn't been already
|
||||
*/
|
||||
public void register(String warning) {
|
||||
this.warnings.add(warning);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect the presence of {@link Deprecated} on the specified elements.
|
||||
* @param elements the elements to check
|
||||
* @return {@code this} instance
|
||||
*/
|
||||
public CodeWarnings detectDeprecation(AnnotatedElement... elements) {
|
||||
for (AnnotatedElement element : elements) {
|
||||
register(element.getAnnotation(Deprecated.class));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect the presence of {@link Deprecated} on the specified elements.
|
||||
* @param elements the elements to check
|
||||
* @return {@code this} instance
|
||||
*/
|
||||
public CodeWarnings detectDeprecation(Stream<AnnotatedElement> elements) {
|
||||
elements.forEach(element -> register(element.getAnnotation(Deprecated.class)));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Include a {@link SuppressWarnings} on the specified method if necessary.
|
||||
* @param method the method to update
|
||||
*/
|
||||
public void suppress(MethodSpec.Builder method) {
|
||||
if (this.warnings.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
method.addAnnotation(buildAnnotationSpec());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the currently registered warnings.
|
||||
* @return the warnings
|
||||
*/
|
||||
protected Set<String> getWarnings() {
|
||||
return Collections.unmodifiableSet(this.warnings);
|
||||
}
|
||||
|
||||
private void register(@Nullable Deprecated annotation) {
|
||||
if (annotation != null) {
|
||||
if (annotation.forRemoval()) {
|
||||
register("removal");
|
||||
}
|
||||
else {
|
||||
register("deprecation");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private AnnotationSpec buildAnnotationSpec() {
|
||||
return AnnotationSpec.builder(SuppressWarnings.class)
|
||||
.addMember("value", generateValueCode()).build();
|
||||
}
|
||||
|
||||
private CodeBlock generateValueCode() {
|
||||
if (this.warnings.size() == 1) {
|
||||
return CodeBlock.of("$S", this.warnings.iterator().next());
|
||||
}
|
||||
CodeBlock values = CodeBlock.join(this.warnings.stream()
|
||||
.map(warning -> CodeBlock.of("$S", warning)).toList(), ", ");
|
||||
return CodeBlock.of("{ $L }", values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new StringJoiner(", ", CodeWarnings.class.getSimpleName(), "")
|
||||
.add(this.warnings.toString())
|
||||
.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.lang.reflect.Executable;
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.Arrays;
|
||||
import java.util.function.Consumer;
|
||||
@@ -189,11 +190,15 @@ public class InstanceSupplierCodeGenerator {
|
||||
private CodeBlock generateCodeForInaccessibleConstructor(String beanName, Class<?> beanClass,
|
||||
Constructor<?> constructor, boolean dependsOnBean, Consumer<ReflectionHints> hints) {
|
||||
|
||||
CodeWarnings codeWarnings = new CodeWarnings();
|
||||
codeWarnings.detectDeprecation(beanClass, constructor)
|
||||
.detectDeprecation(Arrays.stream(constructor.getParameters()).map(Parameter::getType));
|
||||
hints.accept(this.generationContext.getRuntimeHints().reflection());
|
||||
|
||||
GeneratedMethod generatedMethod = generateGetInstanceSupplierMethod(method -> {
|
||||
method.addJavadoc("Get the bean instance supplier for '$L'.", beanName);
|
||||
method.addModifiers(PRIVATE_STATIC);
|
||||
codeWarnings.suppress(method);
|
||||
method.returns(ParameterizedTypeName.get(BeanInstanceSupplier.class, beanClass));
|
||||
int parameterOffset = (!dependsOnBean) ? 0 : 1;
|
||||
method.addStatement(generateResolverForConstructor(beanClass, constructor, parameterOffset));
|
||||
@@ -206,8 +211,12 @@ public class InstanceSupplierCodeGenerator {
|
||||
String beanName, Class<?> beanClass, Constructor<?> constructor, Class<?> declaringClass,
|
||||
boolean dependsOnBean, javax.lang.model.element.Modifier... modifiers) {
|
||||
|
||||
CodeWarnings codeWarnings = new CodeWarnings();
|
||||
codeWarnings.detectDeprecation(beanClass, constructor, declaringClass)
|
||||
.detectDeprecation(Arrays.stream(constructor.getParameters()).map(Parameter::getType));
|
||||
method.addJavadoc("Get the bean instance supplier for '$L'.", beanName);
|
||||
method.addModifiers(modifiers);
|
||||
codeWarnings.suppress(method);
|
||||
method.returns(ParameterizedTypeName.get(BeanInstanceSupplier.class, beanClass));
|
||||
|
||||
int parameterOffset = (!dependsOnBean) ? 0 : 1;
|
||||
@@ -300,9 +309,13 @@ public class InstanceSupplierCodeGenerator {
|
||||
|
||||
String factoryMethodName = factoryMethod.getName();
|
||||
Class<?> suppliedType = ClassUtils.resolvePrimitiveIfNecessary(factoryMethod.getReturnType());
|
||||
CodeWarnings codeWarnings = new CodeWarnings();
|
||||
codeWarnings.detectDeprecation(declaringClass, factoryMethod, suppliedType)
|
||||
.detectDeprecation(Arrays.stream(factoryMethod.getParameters()).map(Parameter::getType));
|
||||
|
||||
method.addJavadoc("Get the bean instance supplier for '$L'.", beanName);
|
||||
method.addModifiers(modifiers);
|
||||
codeWarnings.suppress(method);
|
||||
method.returns(ParameterizedTypeName.get(BeanInstanceSupplier.class, suppliedType));
|
||||
|
||||
CodeBlock.Builder code = CodeBlock.builder();
|
||||
|
||||
@@ -26,6 +26,7 @@ import java.util.function.Supplier;
|
||||
import javax.lang.model.element.Modifier;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aot.generate.GeneratedMethod;
|
||||
@@ -52,6 +53,7 @@ import org.springframework.beans.testfixture.beans.factory.aot.TestHierarchy;
|
||||
import org.springframework.beans.testfixture.beans.factory.aot.TestHierarchy.Implementation;
|
||||
import org.springframework.beans.testfixture.beans.factory.aot.TestHierarchy.One;
|
||||
import org.springframework.beans.testfixture.beans.factory.aot.TestHierarchy.Two;
|
||||
import org.springframework.beans.testfixture.beans.factory.generator.deprecation.DeprecatedBean;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.test.io.support.MockSpringFactoriesLoader;
|
||||
import org.springframework.core.test.tools.CompileWithForkedClassLoader;
|
||||
@@ -66,6 +68,7 @@ import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
import static org.assertj.core.api.Assertions.assertThatNoException;
|
||||
|
||||
/**
|
||||
* Tests for {@link BeanDefinitionMethodGenerator} and
|
||||
@@ -740,6 +743,32 @@ class BeanDefinitionMethodGeneratorTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Nested
|
||||
@SuppressWarnings("deprecation")
|
||||
class DeprecationTests {
|
||||
|
||||
private static final TestCompiler TEST_COMPILER = TestCompiler.forSystem()
|
||||
.withCompilerOptions("-Xlint:all", "-Xlint:-rawtypes", "-Werror");
|
||||
|
||||
@Test
|
||||
void generateBeanDefinitionMethodWithDeprecatedTargetClass() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(DeprecatedBean.class);
|
||||
RegisteredBean registeredBean = registerBean(beanDefinition);
|
||||
BeanDefinitionMethodGenerator generator = new BeanDefinitionMethodGenerator(
|
||||
methodGeneratorFactory, registeredBean, null,
|
||||
Collections.emptyList());
|
||||
MethodReference method = generator.generateBeanDefinitionMethod(
|
||||
generationContext, beanRegistrationsCode);
|
||||
compileAndCheckWarnings(method);
|
||||
}
|
||||
|
||||
private void compileAndCheckWarnings(MethodReference methodReference) {
|
||||
assertThatNoException().isThrownBy(() -> compile(TEST_COMPILER, methodReference,
|
||||
((instanceSupplier, compiled) -> {})));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void testBeanDefinitionMethodInCurrentFile(Class<?> targetType, RootBeanDefinition beanDefinition) {
|
||||
RegisteredBean registeredBean = registerBean(new RootBeanDefinition(beanDefinition));
|
||||
BeanDefinitionMethodGenerator generator = new BeanDefinitionMethodGenerator(
|
||||
@@ -764,6 +793,10 @@ class BeanDefinitionMethodGeneratorTests {
|
||||
}
|
||||
|
||||
private void compile(MethodReference method, BiConsumer<RootBeanDefinition, Compiled> result) {
|
||||
compile(TestCompiler.forSystem(), method, result);
|
||||
}
|
||||
|
||||
private void compile(TestCompiler testCompiler, MethodReference method, BiConsumer<RootBeanDefinition, Compiled> result) {
|
||||
this.beanRegistrationsCode.getTypeBuilder().set(type -> {
|
||||
CodeBlock methodInvocation = method.toInvokeCodeBlock(ArgumentCodeGenerator.none(),
|
||||
this.beanRegistrationsCode.getClassName());
|
||||
@@ -775,7 +808,7 @@ class BeanDefinitionMethodGeneratorTests {
|
||||
.addCode("return $L;", methodInvocation).build());
|
||||
});
|
||||
this.generationContext.writeGeneratedContent();
|
||||
TestCompiler.forSystem().with(this.generationContext).compile(compiled ->
|
||||
testCompiler.with(this.generationContext).compile(compiled ->
|
||||
result.accept((RootBeanDefinition) compiled.getInstance(Supplier.class).get(), compiled));
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* 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.beans.factory.aot;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.lang.model.element.Modifier;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aot.test.generate.TestGenerationContext;
|
||||
import org.springframework.beans.testfixture.beans.factory.aot.DeferredTypeBuilder;
|
||||
import org.springframework.beans.testfixture.beans.factory.generator.deprecation.DeprecatedBean;
|
||||
import org.springframework.beans.testfixture.beans.factory.generator.deprecation.DeprecatedForRemovalBean;
|
||||
import org.springframework.core.test.tools.Compiled;
|
||||
import org.springframework.core.test.tools.TestCompiler;
|
||||
import org.springframework.javapoet.MethodSpec;
|
||||
import org.springframework.javapoet.MethodSpec.Builder;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link CodeWarnings}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class CodeWarningsTests {
|
||||
|
||||
private static final TestCompiler TEST_COMPILER = TestCompiler.forSystem()
|
||||
.withCompilerOptions("-Xlint:all", "-Werror");
|
||||
|
||||
private final CodeWarnings codeWarnings;
|
||||
|
||||
private final TestGenerationContext generationContext;
|
||||
|
||||
CodeWarningsTests() {
|
||||
this.codeWarnings = new CodeWarnings();
|
||||
this.generationContext = new TestGenerationContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerNoWarningDoesNotIncludeAnnotation() {
|
||||
compile(method -> {
|
||||
this.codeWarnings.suppress(method);
|
||||
method.addStatement("$T bean = $S", String.class, "Hello");
|
||||
}, compiled -> assertThat(compiled.getSourceFile()).doesNotContain("@SuppressWarnings"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("deprecation")
|
||||
void registerWarningSuppressesIt() {
|
||||
this.codeWarnings.register("deprecation");
|
||||
compile(method -> {
|
||||
this.codeWarnings.suppress(method);
|
||||
method.addStatement("$T bean = new $T()", DeprecatedBean.class, DeprecatedBean.class);
|
||||
}, compiled -> assertThat(compiled.getSourceFile())
|
||||
.contains("@SuppressWarnings(\"deprecation\")"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings({ "deprecation", "removal" })
|
||||
void registerSeveralWarningsSuppressesThem() {
|
||||
this.codeWarnings.register("deprecation");
|
||||
this.codeWarnings.register("removal");
|
||||
compile(method -> {
|
||||
this.codeWarnings.suppress(method);
|
||||
method.addStatement("$T bean = new $T()", DeprecatedBean.class, DeprecatedBean.class);
|
||||
method.addStatement("$T another = new $T()", DeprecatedForRemovalBean.class, DeprecatedForRemovalBean.class);
|
||||
}, compiled -> assertThat(compiled.getSourceFile())
|
||||
.contains("@SuppressWarnings({ \"deprecation\", \"removal\" })"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("deprecation")
|
||||
void detectDeprecationOnAnnotatedElementWithDeprecated() {
|
||||
this.codeWarnings.detectDeprecation(DeprecatedBean.class);
|
||||
assertThat(this.codeWarnings.getWarnings()).containsExactly("deprecation");
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("removal")
|
||||
void detectDeprecationOnAnnotatedElementWithDeprecatedForRemoval() {
|
||||
this.codeWarnings.detectDeprecation(DeprecatedForRemovalBean.class);
|
||||
assertThat(this.codeWarnings.getWarnings()).containsExactly("removal");
|
||||
}
|
||||
|
||||
@Test
|
||||
void toStringIncludeWarnings() {
|
||||
this.codeWarnings.register("deprecation");
|
||||
this.codeWarnings.register("rawtypes");
|
||||
assertThat(this.codeWarnings).hasToString("CodeWarnings[deprecation, rawtypes]");
|
||||
}
|
||||
|
||||
private void compile(Consumer<Builder> method,
|
||||
Consumer<Compiled> result) {
|
||||
DeferredTypeBuilder typeBuilder = new DeferredTypeBuilder();
|
||||
this.generationContext.getGeneratedClasses().addForFeature("TestCode", typeBuilder);
|
||||
typeBuilder.set(type -> {
|
||||
type.addModifiers(Modifier.PUBLIC);
|
||||
Builder methodBuilder = MethodSpec.methodBuilder("apply")
|
||||
.addModifiers(Modifier.PUBLIC);
|
||||
method.accept(methodBuilder);
|
||||
type.addMethod(methodBuilder.build());
|
||||
});
|
||||
this.generationContext.writeGeneratedContent();
|
||||
TEST_COMPILER.with(this.generationContext).compile(result);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -23,6 +23,8 @@ import java.util.function.Supplier;
|
||||
import javax.lang.model.element.Modifier;
|
||||
|
||||
import org.assertj.core.api.ThrowingConsumer;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aot.generate.GeneratedClass;
|
||||
@@ -44,6 +46,12 @@ import org.springframework.beans.testfixture.beans.factory.generator.InnerCompon
|
||||
import org.springframework.beans.testfixture.beans.factory.generator.InnerComponentConfiguration.EnvironmentAwareComponent;
|
||||
import org.springframework.beans.testfixture.beans.factory.generator.InnerComponentConfiguration.NoDependencyComponent;
|
||||
import org.springframework.beans.testfixture.beans.factory.generator.SimpleConfiguration;
|
||||
import org.springframework.beans.testfixture.beans.factory.generator.deprecation.DeprecatedBean;
|
||||
import org.springframework.beans.testfixture.beans.factory.generator.deprecation.DeprecatedConstructor;
|
||||
import org.springframework.beans.testfixture.beans.factory.generator.deprecation.DeprecatedForRemovalBean;
|
||||
import org.springframework.beans.testfixture.beans.factory.generator.deprecation.DeprecatedForRemovalConstructor;
|
||||
import org.springframework.beans.testfixture.beans.factory.generator.deprecation.DeprecatedForRemovalMemberConfiguration;
|
||||
import org.springframework.beans.testfixture.beans.factory.generator.deprecation.DeprecatedMemberConfiguration;
|
||||
import org.springframework.beans.testfixture.beans.factory.generator.factory.NumberHolder;
|
||||
import org.springframework.beans.testfixture.beans.factory.generator.factory.NumberHolderFactoryBean;
|
||||
import org.springframework.beans.testfixture.beans.factory.generator.factory.SampleFactory;
|
||||
@@ -57,6 +65,7 @@ import org.springframework.javapoet.ParameterizedTypeName;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatNoException;
|
||||
|
||||
/**
|
||||
* Tests for {@link InstanceSupplierCodeGenerator}.
|
||||
@@ -68,18 +77,20 @@ class InstanceSupplierCodeGeneratorTests {
|
||||
|
||||
private final TestGenerationContext generationContext;
|
||||
|
||||
private final DefaultListableBeanFactory beanFactory;
|
||||
|
||||
|
||||
InstanceSupplierCodeGeneratorTests() {
|
||||
this.generationContext = new TestGenerationContext();
|
||||
this.beanFactory = new DefaultListableBeanFactory();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void generateWhenHasDefaultConstructor() {
|
||||
BeanDefinition beanDefinition = new RootBeanDefinition(TestBean.class);
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
compile(beanFactory, beanDefinition, (instanceSupplier, compiled) -> {
|
||||
TestBean bean = getBean(beanFactory, beanDefinition, instanceSupplier);
|
||||
compile(beanDefinition, (instanceSupplier, compiled) -> {
|
||||
TestBean bean = getBean(beanDefinition, instanceSupplier);
|
||||
assertThat(bean).isInstanceOf(TestBean.class);
|
||||
assertThat(compiled.getSourceFile())
|
||||
.contains("InstanceSupplier.using(TestBean::new)");
|
||||
@@ -91,11 +102,9 @@ class InstanceSupplierCodeGeneratorTests {
|
||||
@Test
|
||||
void generateWhenHasConstructorWithParameter() {
|
||||
BeanDefinition beanDefinition = new RootBeanDefinition(InjectionComponent.class);
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerSingleton("injected", "injected");
|
||||
compile(beanFactory, beanDefinition, (instanceSupplier, compiled) -> {
|
||||
InjectionComponent bean = getBean(beanFactory, beanDefinition,
|
||||
instanceSupplier);
|
||||
this.beanFactory.registerSingleton("injected", "injected");
|
||||
compile(beanDefinition, (instanceSupplier, compiled) -> {
|
||||
InjectionComponent bean = getBean(beanDefinition, instanceSupplier);
|
||||
assertThat(bean).isInstanceOf(InjectionComponent.class).extracting("bean")
|
||||
.isEqualTo("injected");
|
||||
});
|
||||
@@ -107,10 +116,9 @@ class InstanceSupplierCodeGeneratorTests {
|
||||
void generateWhenHasConstructorWithInnerClassAndDefaultConstructor() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(
|
||||
NoDependencyComponent.class);
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerSingleton("configuration", new InnerComponentConfiguration());
|
||||
compile(beanFactory, beanDefinition, (instanceSupplier, compiled) -> {
|
||||
NoDependencyComponent bean = getBean(beanFactory, beanDefinition,
|
||||
this.beanFactory.registerSingleton("configuration", new InnerComponentConfiguration());
|
||||
compile(beanDefinition, (instanceSupplier, compiled) -> {
|
||||
NoDependencyComponent bean = getBean(beanDefinition,
|
||||
instanceSupplier);
|
||||
assertThat(bean).isInstanceOf(NoDependencyComponent.class);
|
||||
assertThat(compiled.getSourceFile()).contains(
|
||||
@@ -124,11 +132,10 @@ class InstanceSupplierCodeGeneratorTests {
|
||||
void generateWhenHasConstructorWithInnerClassAndParameter() {
|
||||
BeanDefinition beanDefinition = new RootBeanDefinition(
|
||||
EnvironmentAwareComponent.class);
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerSingleton("configuration", new InnerComponentConfiguration());
|
||||
beanFactory.registerSingleton("environment", new StandardEnvironment());
|
||||
compile(beanFactory, beanDefinition, (instanceSupplier, compiled) -> {
|
||||
EnvironmentAwareComponent bean = getBean(beanFactory, beanDefinition,
|
||||
this.beanFactory.registerSingleton("configuration", new InnerComponentConfiguration());
|
||||
this.beanFactory.registerSingleton("environment", new StandardEnvironment());
|
||||
compile(beanDefinition, (instanceSupplier, compiled) -> {
|
||||
EnvironmentAwareComponent bean = getBean(beanDefinition,
|
||||
instanceSupplier);
|
||||
assertThat(bean).isInstanceOf(EnvironmentAwareComponent.class);
|
||||
assertThat(compiled.getSourceFile()).contains(
|
||||
@@ -142,10 +149,9 @@ class InstanceSupplierCodeGeneratorTests {
|
||||
void generateWhenHasConstructorWithGeneric() {
|
||||
BeanDefinition beanDefinition = new RootBeanDefinition(
|
||||
NumberHolderFactoryBean.class);
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerSingleton("number", 123);
|
||||
compile(beanFactory, beanDefinition, (instanceSupplier, compiled) -> {
|
||||
NumberHolder<?> bean = getBean(beanFactory, beanDefinition, instanceSupplier);
|
||||
this.beanFactory.registerSingleton("number", 123);
|
||||
compile(beanDefinition, (instanceSupplier, compiled) -> {
|
||||
NumberHolder<?> bean = getBean(beanDefinition, instanceSupplier);
|
||||
assertThat(bean).isInstanceOf(NumberHolder.class);
|
||||
assertThat(bean).extracting("number").isNull(); // No property actually set
|
||||
assertThat(compiled.getSourceFile()).contains("NumberHolderFactoryBean::new");
|
||||
@@ -158,9 +164,8 @@ class InstanceSupplierCodeGeneratorTests {
|
||||
void generateWhenHasPrivateConstructor() {
|
||||
BeanDefinition beanDefinition = new RootBeanDefinition(
|
||||
TestBeanWithPrivateConstructor.class);
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
compile(beanFactory, beanDefinition, (instanceSupplier, compiled) -> {
|
||||
TestBeanWithPrivateConstructor bean = getBean(beanFactory, beanDefinition,
|
||||
compile(beanDefinition, (instanceSupplier, compiled) -> {
|
||||
TestBeanWithPrivateConstructor bean = getBean(beanDefinition,
|
||||
instanceSupplier);
|
||||
assertThat(bean).isInstanceOf(TestBeanWithPrivateConstructor.class);
|
||||
assertThat(compiled.getSourceFile())
|
||||
@@ -175,11 +180,10 @@ class InstanceSupplierCodeGeneratorTests {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(String.class)
|
||||
.setFactoryMethodOnBean("stringBean", "config").getBeanDefinition();
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerBeanDefinition("config", BeanDefinitionBuilder
|
||||
this.beanFactory.registerBeanDefinition("config", BeanDefinitionBuilder
|
||||
.genericBeanDefinition(SimpleConfiguration.class).getBeanDefinition());
|
||||
compile(beanFactory, beanDefinition, (instanceSupplier, compiled) -> {
|
||||
String bean = getBean(beanFactory, beanDefinition, instanceSupplier);
|
||||
compile(beanDefinition, (instanceSupplier, compiled) -> {
|
||||
String bean = getBean(beanDefinition, instanceSupplier);
|
||||
assertThat(bean).isInstanceOf(String.class);
|
||||
assertThat(bean).isEqualTo("Hello");
|
||||
assertThat(compiled.getSourceFile()).contains(
|
||||
@@ -195,11 +199,10 @@ class InstanceSupplierCodeGeneratorTests {
|
||||
.rootBeanDefinition(String.class)
|
||||
.setFactoryMethodOnBean("privateStaticStringBean", "config")
|
||||
.getBeanDefinition();
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerBeanDefinition("config", BeanDefinitionBuilder
|
||||
this.beanFactory.registerBeanDefinition("config", BeanDefinitionBuilder
|
||||
.genericBeanDefinition(SimpleConfiguration.class).getBeanDefinition());
|
||||
compile(beanFactory, beanDefinition, (instanceSupplier, compiled) -> {
|
||||
String bean = getBean(beanFactory, beanDefinition, instanceSupplier);
|
||||
compile(beanDefinition, (instanceSupplier, compiled) -> {
|
||||
String bean = getBean(beanDefinition, instanceSupplier);
|
||||
assertThat(bean).isInstanceOf(String.class);
|
||||
assertThat(bean).isEqualTo("Hello");
|
||||
assertThat(compiled.getSourceFile())
|
||||
@@ -215,11 +218,10 @@ class InstanceSupplierCodeGeneratorTests {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(Integer.class)
|
||||
.setFactoryMethodOnBean("integerBean", "config").getBeanDefinition();
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerBeanDefinition("config", BeanDefinitionBuilder
|
||||
this.beanFactory.registerBeanDefinition("config", BeanDefinitionBuilder
|
||||
.genericBeanDefinition(SimpleConfiguration.class).getBeanDefinition());
|
||||
compile(beanFactory, beanDefinition, (instanceSupplier, compiled) -> {
|
||||
Integer bean = getBean(beanFactory, beanDefinition, instanceSupplier);
|
||||
compile(beanDefinition, (instanceSupplier, compiled) -> {
|
||||
Integer bean = getBean(beanDefinition, instanceSupplier);
|
||||
assertThat(bean).isInstanceOf(Integer.class);
|
||||
assertThat(bean).isEqualTo(42);
|
||||
assertThat(compiled.getSourceFile())
|
||||
@@ -236,13 +238,12 @@ class InstanceSupplierCodeGeneratorTests {
|
||||
.setFactoryMethodOnBean("create", "config").getBeanDefinition();
|
||||
beanDefinition.setResolvedFactoryMethod(ReflectionUtils
|
||||
.findMethod(SampleFactory.class, "create", Number.class, String.class));
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerBeanDefinition("config", BeanDefinitionBuilder
|
||||
this.beanFactory.registerBeanDefinition("config", BeanDefinitionBuilder
|
||||
.genericBeanDefinition(SampleFactory.class).getBeanDefinition());
|
||||
beanFactory.registerSingleton("number", 42);
|
||||
beanFactory.registerSingleton("string", "test");
|
||||
compile(beanFactory, beanDefinition, (instanceSupplier, compiled) -> {
|
||||
String bean = getBean(beanFactory, beanDefinition, instanceSupplier);
|
||||
this.beanFactory.registerSingleton("number", 42);
|
||||
this.beanFactory.registerSingleton("string", "test");
|
||||
compile(beanDefinition, (instanceSupplier, compiled) -> {
|
||||
String bean = getBean(beanDefinition, instanceSupplier);
|
||||
assertThat(bean).isInstanceOf(String.class);
|
||||
assertThat(bean).isEqualTo("42test");
|
||||
assertThat(compiled.getSourceFile()).contains("SampleFactory.create(");
|
||||
@@ -257,11 +258,10 @@ class InstanceSupplierCodeGeneratorTests {
|
||||
.rootBeanDefinition(Integer.class)
|
||||
.setFactoryMethodOnBean("throwingIntegerBean", "config")
|
||||
.getBeanDefinition();
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerBeanDefinition("config", BeanDefinitionBuilder
|
||||
this.beanFactory.registerBeanDefinition("config", BeanDefinitionBuilder
|
||||
.genericBeanDefinition(SimpleConfiguration.class).getBeanDefinition());
|
||||
compile(beanFactory, beanDefinition, (instanceSupplier, compiled) -> {
|
||||
Integer bean = getBean(beanFactory, beanDefinition, instanceSupplier);
|
||||
compile(beanDefinition, (instanceSupplier, compiled) -> {
|
||||
Integer bean = getBean(beanDefinition, instanceSupplier);
|
||||
assertThat(bean).isInstanceOf(Integer.class);
|
||||
assertThat(bean).isEqualTo(42);
|
||||
assertThat(compiled.getSourceFile()).doesNotContain(") throws Exception {");
|
||||
@@ -270,6 +270,108 @@ class InstanceSupplierCodeGeneratorTests {
|
||||
.satisfies(hasMethodWithMode(ExecutableMode.INTROSPECT));
|
||||
}
|
||||
|
||||
@Nested
|
||||
@SuppressWarnings("deprecation")
|
||||
class DeprecationTests {
|
||||
|
||||
private static final TestCompiler TEST_COMPILER = TestCompiler.forSystem()
|
||||
.withCompilerOptions("-Xlint:all", "-Xlint:-rawtypes", "-Werror");
|
||||
|
||||
@Test
|
||||
@Disabled("Need to move to a separate method so that the warning can be suppressed")
|
||||
void generateWhenTargetClassIsDeprecated() {
|
||||
compileAndCheckWarnings(new RootBeanDefinition(DeprecatedBean.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenTargetConstructorIsDeprecated() {
|
||||
compileAndCheckWarnings(new RootBeanDefinition(DeprecatedConstructor.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenTargetFactoryMethodIsDeprecated() {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(String.class)
|
||||
.setFactoryMethodOnBean("deprecatedString", "config").getBeanDefinition();
|
||||
beanFactory.registerBeanDefinition("config", BeanDefinitionBuilder
|
||||
.genericBeanDefinition(DeprecatedMemberConfiguration.class).getBeanDefinition());
|
||||
compileAndCheckWarnings(beanDefinition);
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenTargetFactoryMethodParameterIsDeprecated() {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(String.class)
|
||||
.setFactoryMethodOnBean("deprecatedParameter", "config").getBeanDefinition();
|
||||
beanFactory.registerBeanDefinition("config", BeanDefinitionBuilder
|
||||
.genericBeanDefinition(DeprecatedMemberConfiguration.class).getBeanDefinition());
|
||||
beanFactory.registerBeanDefinition("parameter", new RootBeanDefinition(DeprecatedBean.class));
|
||||
compileAndCheckWarnings(beanDefinition);
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenTargetFactoryMethodReturnTypeIsDeprecated() {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(DeprecatedBean.class)
|
||||
.setFactoryMethodOnBean("deprecatedReturnType", "config").getBeanDefinition();
|
||||
beanFactory.registerBeanDefinition("config", BeanDefinitionBuilder
|
||||
.genericBeanDefinition(DeprecatedMemberConfiguration.class).getBeanDefinition());
|
||||
compileAndCheckWarnings(beanDefinition);
|
||||
}
|
||||
|
||||
private void compileAndCheckWarnings(BeanDefinition beanDefinition) {
|
||||
assertThatNoException().isThrownBy(() -> compile(TEST_COMPILER, beanDefinition,
|
||||
((instanceSupplier, compiled) -> {})));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
@SuppressWarnings("removal")
|
||||
class DeprecationForRemovalTests {
|
||||
|
||||
private static final TestCompiler TEST_COMPILER = TestCompiler.forSystem()
|
||||
.withCompilerOptions("-Xlint:all", "-Xlint:-rawtypes", "-Werror");
|
||||
|
||||
@Test
|
||||
@Disabled("Need to move to a separate method so that the warning can be suppressed")
|
||||
void generateWhenTargetClassIsDeprecatedForRemoval() {
|
||||
compileAndCheckWarnings(new RootBeanDefinition(DeprecatedForRemovalBean.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenTargetConstructorIsDeprecatedForRemoval() {
|
||||
compileAndCheckWarnings(new RootBeanDefinition(DeprecatedForRemovalConstructor.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenTargetFactoryMethodIsDeprecatedForRemoval() {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(String.class)
|
||||
.setFactoryMethodOnBean("deprecatedString", "config").getBeanDefinition();
|
||||
beanFactory.registerBeanDefinition("config", BeanDefinitionBuilder
|
||||
.genericBeanDefinition(DeprecatedForRemovalMemberConfiguration.class).getBeanDefinition());
|
||||
compileAndCheckWarnings(beanDefinition);
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenTargetFactoryMethodParameterIsDeprecatedForRemoval() {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(String.class)
|
||||
.setFactoryMethodOnBean("deprecatedParameter", "config").getBeanDefinition();
|
||||
beanFactory.registerBeanDefinition("config", BeanDefinitionBuilder
|
||||
.genericBeanDefinition(DeprecatedForRemovalMemberConfiguration.class).getBeanDefinition());
|
||||
beanFactory.registerBeanDefinition("parameter", new RootBeanDefinition(DeprecatedForRemovalBean.class));
|
||||
compileAndCheckWarnings(beanDefinition);
|
||||
}
|
||||
|
||||
private void compileAndCheckWarnings(BeanDefinition beanDefinition) {
|
||||
assertThatNoException().isThrownBy(() -> compile(TEST_COMPILER, beanDefinition,
|
||||
((instanceSupplier, compiled) -> {})));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private ReflectionHints getReflectionHints() {
|
||||
return this.generationContext.getRuntimeHints().reflection();
|
||||
}
|
||||
@@ -287,17 +389,20 @@ class InstanceSupplierCodeGeneratorTests {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T getBean(DefaultListableBeanFactory beanFactory,
|
||||
BeanDefinition beanDefinition, InstanceSupplier<?> instanceSupplier) {
|
||||
private <T> T getBean(BeanDefinition beanDefinition, InstanceSupplier<?> instanceSupplier) {
|
||||
((RootBeanDefinition) beanDefinition).setInstanceSupplier(instanceSupplier);
|
||||
beanFactory.registerBeanDefinition("testBean", beanDefinition);
|
||||
return (T) beanFactory.getBean("testBean");
|
||||
this.beanFactory.registerBeanDefinition("testBean", beanDefinition);
|
||||
return (T) this.beanFactory.getBean("testBean");
|
||||
}
|
||||
|
||||
private void compile(DefaultListableBeanFactory beanFactory, BeanDefinition beanDefinition,
|
||||
private void compile(BeanDefinition beanDefinition, BiConsumer<InstanceSupplier<?>, Compiled> result) {
|
||||
compile(TestCompiler.forSystem(), beanDefinition, result);
|
||||
}
|
||||
|
||||
private void compile(TestCompiler testCompiler, BeanDefinition beanDefinition,
|
||||
BiConsumer<InstanceSupplier<?>, Compiled> result) {
|
||||
|
||||
DefaultListableBeanFactory freshBeanFactory = new DefaultListableBeanFactory(beanFactory);
|
||||
DefaultListableBeanFactory freshBeanFactory = new DefaultListableBeanFactory(this.beanFactory);
|
||||
freshBeanFactory.registerBeanDefinition("testBean", beanDefinition);
|
||||
RegisteredBean registeredBean = RegisteredBean.of(freshBeanFactory, "testBean");
|
||||
DeferredTypeBuilder typeBuilder = new DeferredTypeBuilder();
|
||||
@@ -317,8 +422,8 @@ class InstanceSupplierCodeGeneratorTests {
|
||||
.addStatement("return $L", generatedCode).build());
|
||||
});
|
||||
this.generationContext.writeGeneratedContent();
|
||||
TestCompiler.forSystem().with(this.generationContext).compile(compiled ->
|
||||
result.accept((InstanceSupplier<?>) compiled.getInstance(Supplier.class).get(), compiled));
|
||||
testCompiler.with(this.generationContext).compile(compiled -> result.accept(
|
||||
(InstanceSupplier<?>) compiled.getInstance(Supplier.class).get(), compiled));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.beans.testfixture.beans.factory.generator.deprecation;
|
||||
|
||||
/**
|
||||
* A sample bean that's fully deprecated.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@Deprecated
|
||||
public class DeprecatedBean {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.beans.testfixture.beans.factory.generator.deprecation;
|
||||
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
/**
|
||||
* A sample whose factory method (constructor) is deprecated.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class DeprecatedConstructor {
|
||||
|
||||
@Deprecated
|
||||
public DeprecatedConstructor(Environment environment) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.beans.testfixture.beans.factory.generator.deprecation;
|
||||
|
||||
/**
|
||||
* A sample bean that's fully deprecated for removal.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public class DeprecatedForRemovalBean {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.beans.testfixture.beans.factory.generator.deprecation;
|
||||
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
/**
|
||||
* A sample whose factory method (constructor) is deprecated for removal
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class DeprecatedForRemovalConstructor {
|
||||
|
||||
@Deprecated(forRemoval = true)
|
||||
public DeprecatedForRemovalConstructor(Environment environment) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.beans.testfixture.beans.factory.generator.deprecation;
|
||||
|
||||
/**
|
||||
* A class with deprecated members for removal to test various use cases.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class DeprecatedForRemovalMemberConfiguration {
|
||||
|
||||
@Deprecated(forRemoval = true)
|
||||
public String deprecatedString() {
|
||||
return "deprecated";
|
||||
}
|
||||
|
||||
public String deprecatedParameter(DeprecatedForRemovalBean bean) {
|
||||
return bean.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.beans.testfixture.beans.factory.generator.deprecation;
|
||||
|
||||
/**
|
||||
* A class with deprecated members to test various use cases.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class DeprecatedMemberConfiguration {
|
||||
|
||||
@Deprecated
|
||||
public String deprecatedString() {
|
||||
return "deprecated";
|
||||
}
|
||||
|
||||
public String deprecatedParameter(DeprecatedBean bean) {
|
||||
return bean.toString();
|
||||
}
|
||||
|
||||
public DeprecatedBean deprecatedReturnType() {
|
||||
return new DeprecatedBean();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.UnaryOperator;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.annotation.processing.Processor;
|
||||
import javax.tools.Diagnostic;
|
||||
@@ -41,6 +42,7 @@ import org.springframework.lang.Nullable;
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Scott Frederick
|
||||
* @author Stephane Nicoll
|
||||
* @since 6.0
|
||||
* @see #forSystem()
|
||||
*/
|
||||
@@ -59,10 +61,12 @@ public final class TestCompiler {
|
||||
|
||||
private final List<Processor> processors;
|
||||
|
||||
private final List<String> compilerOptions;
|
||||
|
||||
|
||||
private TestCompiler(@Nullable ClassLoader classLoader, JavaCompiler compiler,
|
||||
SourceFiles sourceFiles, ResourceFiles resourceFiles, ClassFiles classFiles,
|
||||
List<Processor> processors) {
|
||||
List<Processor> processors, List<String> compilerOptions) {
|
||||
|
||||
this.classLoader = classLoader;
|
||||
this.compiler = compiler;
|
||||
@@ -70,6 +74,7 @@ public final class TestCompiler {
|
||||
this.resourceFiles = resourceFiles;
|
||||
this.classFiles = classFiles;
|
||||
this.processors = processors;
|
||||
this.compilerOptions = compilerOptions;
|
||||
}
|
||||
|
||||
|
||||
@@ -88,7 +93,7 @@ public final class TestCompiler {
|
||||
*/
|
||||
public static TestCompiler forCompiler(JavaCompiler javaCompiler) {
|
||||
return new TestCompiler(null, javaCompiler, SourceFiles.none(),
|
||||
ResourceFiles.none(), ClassFiles.none(), Collections.emptyList());
|
||||
ResourceFiles.none(), ClassFiles.none(), Collections.emptyList(), Collections.emptyList());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -108,7 +113,7 @@ public final class TestCompiler {
|
||||
public TestCompiler withSources(SourceFile... sourceFiles) {
|
||||
return new TestCompiler(this.classLoader, this.compiler,
|
||||
this.sourceFiles.and(sourceFiles), this.resourceFiles,
|
||||
this.classFiles, this.processors);
|
||||
this.classFiles, this.processors, this.compilerOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -119,7 +124,7 @@ public final class TestCompiler {
|
||||
public TestCompiler withSources(Iterable<SourceFile> sourceFiles) {
|
||||
return new TestCompiler(this.classLoader, this.compiler,
|
||||
this.sourceFiles.and(sourceFiles), this.resourceFiles,
|
||||
this.classFiles, this.processors);
|
||||
this.classFiles, this.processors, this.compilerOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -130,7 +135,7 @@ public final class TestCompiler {
|
||||
public TestCompiler withSources(SourceFiles sourceFiles) {
|
||||
return new TestCompiler(this.classLoader, this.compiler,
|
||||
this.sourceFiles.and(sourceFiles), this.resourceFiles,
|
||||
this.classFiles, this.processors);
|
||||
this.classFiles, this.processors, this.compilerOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -140,7 +145,8 @@ public final class TestCompiler {
|
||||
*/
|
||||
public TestCompiler withResources(ResourceFile... resourceFiles) {
|
||||
return new TestCompiler(this.classLoader, this.compiler, this.sourceFiles,
|
||||
this.resourceFiles.and(resourceFiles), this.classFiles, this.processors);
|
||||
this.resourceFiles.and(resourceFiles), this.classFiles, this.processors,
|
||||
this.compilerOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -150,7 +156,8 @@ public final class TestCompiler {
|
||||
*/
|
||||
public TestCompiler withResources(Iterable<ResourceFile> resourceFiles) {
|
||||
return new TestCompiler(this.classLoader, this.compiler, this.sourceFiles,
|
||||
this.resourceFiles.and(resourceFiles), this.classFiles, this.processors);
|
||||
this.resourceFiles.and(resourceFiles), this.classFiles, this.processors,
|
||||
this.compilerOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -160,7 +167,8 @@ public final class TestCompiler {
|
||||
*/
|
||||
public TestCompiler withResources(ResourceFiles resourceFiles) {
|
||||
return new TestCompiler(this.classLoader, this.compiler, this.sourceFiles,
|
||||
this.resourceFiles.and(resourceFiles), this.classFiles, this.processors);
|
||||
this.resourceFiles.and(resourceFiles), this.classFiles, this.processors,
|
||||
this.compilerOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -170,7 +178,8 @@ public final class TestCompiler {
|
||||
*/
|
||||
public TestCompiler withClasses(Iterable<ClassFile> classFiles) {
|
||||
return new TestCompiler(this.classLoader, this.compiler, this.sourceFiles,
|
||||
this.resourceFiles, this.classFiles.and(classFiles), this.processors);
|
||||
this.resourceFiles, this.classFiles.and(classFiles), this.processors,
|
||||
this.compilerOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -182,7 +191,7 @@ public final class TestCompiler {
|
||||
List<Processor> mergedProcessors = new ArrayList<>(this.processors);
|
||||
mergedProcessors.addAll(Arrays.asList(processors));
|
||||
return new TestCompiler(this.classLoader, this.compiler, this.sourceFiles,
|
||||
this.resourceFiles, this.classFiles, mergedProcessors);
|
||||
this.resourceFiles, this.classFiles, mergedProcessors, this.compilerOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -194,7 +203,32 @@ public final class TestCompiler {
|
||||
List<Processor> mergedProcessors = new ArrayList<>(this.processors);
|
||||
processors.forEach(mergedProcessors::add);
|
||||
return new TestCompiler(this.classLoader, this.compiler, this.sourceFiles,
|
||||
this.resourceFiles, this.classFiles, mergedProcessors);
|
||||
this.resourceFiles, this.classFiles, mergedProcessors, this.compilerOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link TestCompiler} instance with the additional compiler options.
|
||||
* @param options the additional compiler options
|
||||
* @return a new {@code TestCompiler} instance
|
||||
* @since 6.1
|
||||
*/
|
||||
public TestCompiler withCompilerOptions(String... options) {
|
||||
List<String> mergedCompilerOptions = Stream.concat(this.compilerOptions.stream(),
|
||||
Arrays.stream(options)).distinct().toList();
|
||||
return new TestCompiler(this.classLoader, this.compiler, this.sourceFiles,
|
||||
this.resourceFiles, this.classFiles, this.processors, mergedCompilerOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link TestCompiler} instance that fails if any warning is
|
||||
* encountered. This sets the {@code -Xlint:all} and {@code -Werror} compiler
|
||||
* options.
|
||||
* @return a new {@code TestCompiler} instance
|
||||
* @since 6.1
|
||||
* @see #withCompilerOptions(String...)
|
||||
*/
|
||||
public TestCompiler failOnWarning() {
|
||||
return withCompilerOptions("-Xlint:all", "-Werror");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -275,8 +309,8 @@ public final class TestCompiler {
|
||||
standardFileManager, classLoaderToUse, this.classFiles, this.resourceFiles);
|
||||
if (!this.sourceFiles.isEmpty()) {
|
||||
Errors errors = new Errors();
|
||||
CompilationTask task = this.compiler.getTask(null, fileManager, errors, null,
|
||||
null, compilationUnits);
|
||||
CompilationTask task = this.compiler.getTask(null, fileManager, errors,
|
||||
this.compilerOptions, null, compilationUnits);
|
||||
if (!this.processors.isEmpty()) {
|
||||
task.setProcessors(this.processors);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* 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.
|
||||
@@ -43,6 +43,7 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
* @author Scott Frederick
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class TestCompilerTests {
|
||||
|
||||
@@ -87,6 +88,20 @@ class TestCompilerTests {
|
||||
}
|
||||
""";
|
||||
|
||||
private static final String HELLO_DEPRECATED = """
|
||||
package com.example;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class Hello implements Supplier<String> {
|
||||
|
||||
@Deprecated
|
||||
public String get() {
|
||||
return "Hello Deprecated";
|
||||
}
|
||||
|
||||
}
|
||||
""";
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -119,6 +134,70 @@ class TestCompilerTests {
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void compileWhenSourceUseDeprecateCodeAndNoOptionSet() {
|
||||
SourceFile main = SourceFile.of("""
|
||||
package com.example;
|
||||
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) {
|
||||
new Hello().get();
|
||||
}
|
||||
|
||||
}
|
||||
""");
|
||||
TestCompiler.forSystem().withSources(
|
||||
SourceFile.of(HELLO_DEPRECATED), main).compile(compiled -> {
|
||||
Supplier<String> supplier = compiled.getInstance(Supplier.class,
|
||||
"com.example.Hello");
|
||||
assertThat(supplier.get()).isEqualTo("Hello Deprecated");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void compileWhenSourceUseDeprecateCodeAndFailOnWarningIsSet() {
|
||||
SourceFile main = SourceFile.of("""
|
||||
package com.example;
|
||||
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) {
|
||||
new Hello().get();
|
||||
}
|
||||
|
||||
}
|
||||
""");
|
||||
assertThatExceptionOfType(CompilationException.class).isThrownBy(
|
||||
() -> TestCompiler.forSystem().failOnWarning().withSources(
|
||||
SourceFile.of(HELLO_DEPRECATED), main).compile(compiled -> {
|
||||
})).withMessageContaining("warnings found and -Werror specified");
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void compileWhenSourceUseDeprecateCodeAndFailOnWarningWithSuppressWarnings() {
|
||||
SourceFile main = SourceFile.of("""
|
||||
package com.example;
|
||||
|
||||
public class Main {
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public static void main(String[] args) {
|
||||
new Hello().get();
|
||||
}
|
||||
|
||||
}
|
||||
""");
|
||||
TestCompiler.forSystem().failOnWarning().withSources(
|
||||
SourceFile.of(HELLO_DEPRECATED), main).compile(compiled -> {
|
||||
Supplier<String> supplier = compiled.getInstance(Supplier.class,
|
||||
"com.example.Hello");
|
||||
assertThat(supplier.get()).isEqualTo("Hello Deprecated");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void withSourcesArrayAddsSource() {
|
||||
SourceFile sourceFile = SourceFile.of(HELLO_WORLD);
|
||||
|
||||
Reference in New Issue
Block a user