From 77c5a6f18da263dd40a812ab171ea685b0d39a7f Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Fri, 15 Apr 2022 10:36:05 -0700 Subject: [PATCH] Update AutowiredAnnotationBeanPostProcessor AOT support Update `AutowiredAnnotationBeanPostProcessor` so that it provides AOT contributions via the `BeanRegistrationAotProcessor` interface. See gh-28414 --- .../AutowiredAnnotationBeanPostProcessor.java | 211 +++++++++++++++++- ...nBeanRegistrationAotContributionTests.java | 202 +++++++++++++++++ .../PackagePrivateFieldInjectionSample.java | 26 +++ .../PackagePrivateMethodInjectionSample.java | 31 +++ .../PrivateFieldInjectionSample.java | 27 +++ .../PrivateMethodInjectionSample.java | 31 +++ 6 files changed, 516 insertions(+), 12 deletions(-) create mode 100644 spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanRegistrationAotContributionTests.java create mode 100644 spring-beans/src/test/java/org/springframework/beans/factory/annotation/PackagePrivateFieldInjectionSample.java create mode 100644 spring-beans/src/test/java/org/springframework/beans/factory/annotation/PackagePrivateMethodInjectionSample.java create mode 100644 spring-beans/src/test/java/org/springframework/beans/factory/annotation/PrivateFieldInjectionSample.java create mode 100644 spring-beans/src/test/java/org/springframework/beans/factory/annotation/PrivateMethodInjectionSample.java diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java index 00072d0a67..e6fb72123f 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java @@ -35,12 +35,19 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Consumer; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.aot.generate.AccessVisibility; +import org.springframework.aot.generate.GenerationContext; +import org.springframework.aot.generate.MethodReference; import org.springframework.aot.generator.CodeContribution; +import org.springframework.aot.hint.ExecutableHint; import org.springframework.aot.hint.ExecutableMode; +import org.springframework.aot.hint.FieldHint; +import org.springframework.aot.hint.RuntimeHints; import org.springframework.beans.BeanUtils; import org.springframework.beans.BeansException; import org.springframework.beans.PropertyValues; @@ -53,6 +60,9 @@ import org.springframework.beans.factory.InjectionPoint; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.UnsatisfiedDependencyException; import org.springframework.beans.factory.annotation.InjectionMetadata.InjectedElement; +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.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.DependencyDescriptor; import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor; @@ -61,6 +71,7 @@ import org.springframework.beans.factory.generator.BeanInstantiationContribution import org.springframework.beans.factory.generator.InjectionGenerator; import org.springframework.beans.factory.support.LookupOverride; import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor; +import org.springframework.beans.factory.support.RegisteredBean; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.core.BridgeMethodResolver; import org.springframework.core.MethodParameter; @@ -70,6 +81,11 @@ import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.annotation.MergedAnnotation; import org.springframework.core.annotation.MergedAnnotations; +import org.springframework.javapoet.ClassName; +import org.springframework.javapoet.CodeBlock; +import org.springframework.javapoet.JavaFile; +import org.springframework.javapoet.MethodSpec; +import org.springframework.javapoet.TypeSpec; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -134,13 +150,15 @@ import org.springframework.util.StringUtils; * @author Stephane Nicoll * @author Sebastien Deleuze * @author Sam Brannen + * @author Phillip Webb * @since 2.5 * @see #setAutowiredAnnotationType * @see Autowired * @see Value */ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor, - MergedBeanDefinitionPostProcessor, AotContributingBeanPostProcessor, PriorityOrdered, BeanFactoryAware { + MergedBeanDefinitionPostProcessor, AotContributingBeanPostProcessor, BeanRegistrationAotProcessor, + PriorityOrdered, BeanFactoryAware { protected final Log logger = LogFactory.getLog(getClass()); @@ -276,6 +294,25 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA : null); } + @Override + public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) { + Class beanClass = registeredBean.getBeanClass(); + String beanName = registeredBean.getBeanName(); + RootBeanDefinition beanDefinition = registeredBean.getMergedBeanDefinition(); + InjectionMetadata metadata = findInjectionMetadata(beanName, beanClass, beanDefinition); + Collection autowiredElements = getAutowiredElements(metadata); + if (!ObjectUtils.isEmpty(autowiredElements)) { + return new AotContribution(beanClass, autowiredElements); + } + return null; + } + + + @SuppressWarnings({ "rawtypes", "unchecked" }) + private Collection getAutowiredElements(InjectionMetadata metadata) { + return (Collection) metadata.getInjectedElements(); + } + private InjectionMetadata findInjectionMetadata(String beanName, Class beanType, RootBeanDefinition beanDefinition) { InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null); metadata.checkConfigMembers(beanDefinition); @@ -465,7 +502,6 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA } } - private InjectionMetadata findAutowiringMetadata(String beanName, Class clazz, @Nullable PropertyValues pvs) { // Fall back to class name as cache key, for backwards compatibility with custom callers. String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName()); @@ -630,12 +666,25 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA } + /** + * Base class representing injection information. + */ + private abstract class AutowiredElement extends InjectionMetadata.InjectedElement { + + protected final boolean required; + + protected AutowiredElement(Member member, PropertyDescriptor pd, boolean required) { + super(member, pd); + this.required = required; + } + + } + + /** * Class representing injection information about an annotated field. */ - private class AutowiredFieldElement extends InjectionMetadata.InjectedElement { - - private final boolean required; + private class AutowiredFieldElement extends AutowiredElement { private volatile boolean cached; @@ -643,8 +692,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA private volatile Object cachedFieldValue; public AutowiredFieldElement(Field field, boolean required) { - super(field, null); - this.required = required; + super(field, null, required); } @Override @@ -710,9 +758,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA /** * Class representing injection information about an annotated method. */ - private class AutowiredMethodElement extends InjectionMetadata.InjectedElement { - - private final boolean required; + private class AutowiredMethodElement extends AutowiredElement { private volatile boolean cached; @@ -720,8 +766,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA private volatile Object[] cachedMethodArguments; public AutowiredMethodElement(Method method, boolean required, @Nullable PropertyDescriptor pd) { - super(method, pd); - this.required = required; + super(method, pd, required); } @Override @@ -890,4 +935,146 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA } } + + /** + * {@link BeanRegistrationAotContribution} to autowire fields and methods. + */ + private class AotContribution implements BeanRegistrationAotContribution { + + private static final String APPLY_METHOD = "apply"; + + private static final String REGISTERED_BEAN_PARAMETER = "registeredBean"; + + private static final String INSTANCE_PARAMETER = "instance"; + + private static final Consumer INTROSPECT = builder -> builder + .withMode(ExecutableMode.INTROSPECT); + + private static final Consumer ALLOW_WRITE = builder -> builder + .allowWrite(true); + + + private final Class target; + + private final Collection autowiredElements; + + + AotContribution(Class target, Collection autowiredElements) { + this.target = target; + this.autowiredElements = autowiredElements; + } + + + @Override + public void applyTo(GenerationContext generationContext, + BeanRegistrationCode beanRegistrationCode) { + + ClassName className = generationContext.getClassNameGenerator() + .generateClassName(this.target, "Autowiring"); + TypeSpec.Builder classBuilder = TypeSpec.classBuilder(className); + classBuilder.addJavadoc("Autowiring for {@link $T}.", this.target); + classBuilder.addModifiers(javax.lang.model.element.Modifier.PUBLIC); + classBuilder.addMethod(generateMethod(generationContext.getRuntimeHints())); + JavaFile javaFile = JavaFile + .builder(className.packageName(), classBuilder.build()).build(); + generationContext.getGeneratedFiles().addSourceFile(javaFile); + beanRegistrationCode.addInstancePostProcessor( + MethodReference.ofStatic(className, APPLY_METHOD)); + } + + private MethodSpec generateMethod(RuntimeHints hints) { + MethodSpec.Builder builder = MethodSpec.methodBuilder(APPLY_METHOD); + builder.addJavadoc("Apply the autowiring."); + builder.addModifiers(javax.lang.model.element.Modifier.PUBLIC, + javax.lang.model.element.Modifier.STATIC); + builder.addParameter(RegisteredBean.class, REGISTERED_BEAN_PARAMETER); + builder.addParameter(this.target, INSTANCE_PARAMETER); + builder.returns(this.target); + builder.addCode(generateMethodCode(hints)); + return builder.build(); + } + + private CodeBlock generateMethodCode(RuntimeHints hints) { + CodeBlock.Builder builder = CodeBlock.builder(); + for (AutowiredElement autowiredElement : this.autowiredElements) { + builder.addStatement( + generateMethodStatementForElement(autowiredElement, hints)); + } + builder.addStatement("return $L", INSTANCE_PARAMETER); + return builder.build(); + } + + private CodeBlock generateMethodStatementForElement( + AutowiredElement autowiredElement, RuntimeHints hints) { + + Member member = autowiredElement.getMember(); + boolean required = autowiredElement.required; + if (member instanceof Field field) { + return generateMethodStatementForField(field, required, hints); + } + if (member instanceof Method method) { + return generateMethodStatementForMethod(method, required, hints); + } + throw new IllegalStateException( + "Unsupported member type " + member.getClass().getName()); + } + + private CodeBlock generateMethodStatementForField(Field field, boolean required, + RuntimeHints hints) { + + CodeBlock resolver = CodeBlock.of("$T.$L($S)", + AutowiredFieldValueResolver.class, + (!required) ? "forField" : "forRequiredField", field.getName()); + AccessVisibility visibility = AccessVisibility.forMember(field); + if (visibility == AccessVisibility.PRIVATE + || visibility == AccessVisibility.PROTECTED) { + hints.reflection().registerField(field, ALLOW_WRITE); + return CodeBlock.of("$L.resolveAndSet($L, $L)", resolver, + REGISTERED_BEAN_PARAMETER, INSTANCE_PARAMETER); + } + return CodeBlock.of("$L.$L = $L.resolve($L)", INSTANCE_PARAMETER, + field.getName(), resolver, REGISTERED_BEAN_PARAMETER); + } + + private CodeBlock generateMethodStatementForMethod(Method method, + boolean required, RuntimeHints hints) { + + CodeBlock.Builder builder = CodeBlock.builder(); + builder.add("$T.$L", AutowiredMethodArgumentsResolver.class, + (!required) ? "forMethod" : "forRequiredMethod"); + builder.add("($S", method.getName()); + if (method.getParameterCount() > 0) { + builder.add(", $L", + generateParameterTypesCode(method.getParameterTypes())); + } + builder.add(")"); + AccessVisibility visibility = AccessVisibility.forMember(method); + if (visibility == AccessVisibility.PRIVATE + || visibility == AccessVisibility.PROTECTED) { + hints.reflection().registerMethod(method); + builder.add(".resolveAndInvoke($L, $L)", REGISTERED_BEAN_PARAMETER, + INSTANCE_PARAMETER); + } + else { + hints.reflection().registerMethod(method, INTROSPECT); + CodeBlock arguments = new AutowiredArgumentsCodeGenerator(this.target, + method).generateCode(method.getParameterTypes()); + CodeBlock injectionCode = CodeBlock.of("args -> $L.$L($L)", + INSTANCE_PARAMETER, method.getName(), arguments); + builder.add(".resolve($L, $L)", REGISTERED_BEAN_PARAMETER, injectionCode); + } + return builder.build(); + } + + private CodeBlock generateParameterTypesCode(Class[] parameterTypes) { + CodeBlock.Builder builder = CodeBlock.builder(); + for (int i = 0; i < parameterTypes.length; i++) { + builder.add(i != 0 ? ", " : ""); + builder.add("$T.class", parameterTypes[i]); + } + return builder.build(); + } + + } + } diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanRegistrationAotContributionTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanRegistrationAotContributionTests.java new file mode 100644 index 0000000000..252a7deccb --- /dev/null +++ b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanRegistrationAotContributionTests.java @@ -0,0 +1,202 @@ +/* + * Copyright 2002-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.beans.factory.annotation; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; + +import javax.lang.model.element.Modifier; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import org.springframework.aot.generate.DefaultGenerationContext; +import org.springframework.aot.generate.GenerationContext; +import org.springframework.aot.generate.InMemoryGeneratedFiles; +import org.springframework.aot.generate.MethodGenerator; +import org.springframework.aot.generate.MethodReference; +import org.springframework.aot.test.generator.compile.CompileWithTargetClassAccess; +import org.springframework.aot.test.generator.compile.Compiled; +import org.springframework.aot.test.generator.compile.TestCompiler; +import org.springframework.beans.factory.aot.BeanRegistrationAotContribution; +import org.springframework.beans.factory.aot.BeanRegistrationCode; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.beans.factory.support.RegisteredBean; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.core.env.Environment; +import org.springframework.core.env.StandardEnvironment; +import org.springframework.javapoet.ClassName; +import org.springframework.javapoet.CodeBlock; +import org.springframework.javapoet.JavaFile; +import org.springframework.javapoet.MethodSpec; +import org.springframework.javapoet.ParameterizedTypeName; +import org.springframework.javapoet.TypeSpec; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link AutowiredAnnotationBeanRegistrationAotContribution}. + * + * @author Phillip Webb + * @author Stephane Nicoll + * @since 6.0 + */ +class AutowiredAnnotationBeanRegistrationAotContributionTests { + + private InMemoryGeneratedFiles generatedFiles; + + private GenerationContext generationContext; + + private MockBeanRegistrationCode beanRegistrationCode; + + private DefaultListableBeanFactory beanFactory; + + @BeforeEach + void setup() { + this.generatedFiles = new InMemoryGeneratedFiles(); + this.generationContext = new DefaultGenerationContext(this.generatedFiles); + this.beanRegistrationCode = new MockBeanRegistrationCode(); + this.beanFactory = new DefaultListableBeanFactory(); + } + + @Test + void contributeWhenPrivateFieldInjectionInjectsUsingReflection() { + Environment environment = new StandardEnvironment(); + this.beanFactory.registerSingleton("environment", environment); + RegisteredBean registeredBean = getAndApplyContribution( + PrivateFieldInjectionSample.class); + testCompiledResult(registeredBean, (postProcessor, compiled) -> { + PrivateFieldInjectionSample instance = new PrivateFieldInjectionSample(); + postProcessor.apply(registeredBean, instance); + assertThat(instance).extracting("environment").isSameAs(environment); + assertThat(compiled.getSourceFileFromPackage(getClass().getPackageName())) + .contains("resolveAndSet("); + }); + } + + @Test + @CompileWithTargetClassAccess(classes = PackagePrivateFieldInjectionSample.class) + void contributeWhenPackagePrivateFieldInjectionInjectsUsingConsumer() { + Environment environment = new StandardEnvironment(); + this.beanFactory.registerSingleton("environment", environment); + RegisteredBean registeredBean = getAndApplyContribution( + PackagePrivateFieldInjectionSample.class); + testCompiledResult(registeredBean, (postProcessor, compiled) -> { + PackagePrivateFieldInjectionSample instance = new PackagePrivateFieldInjectionSample(); + postProcessor.apply(registeredBean, instance); + assertThat(instance).extracting("environment").isSameAs(environment); + assertThat(compiled.getSourceFileFromPackage(getClass().getPackageName())) + .contains("instance.environment ="); + }); + } + + @Test + void contributeWhenPrivateMethodInjectionInjectsUsingReflection() { + Environment environment = new StandardEnvironment(); + this.beanFactory.registerSingleton("environment", environment); + RegisteredBean registeredBean = getAndApplyContribution( + PrivateMethodInjectionSample.class); + testCompiledResult(registeredBean, (postProcessor, compiled) -> { + PrivateMethodInjectionSample instance = new PrivateMethodInjectionSample(); + postProcessor.apply(registeredBean, instance); + assertThat(instance).extracting("environment").isSameAs(environment); + assertThat(compiled.getSourceFileFromPackage(getClass().getPackageName())) + .contains("resolveAndInvoke("); + }); + } + + @Test + @CompileWithTargetClassAccess(classes = PackagePrivateMethodInjectionSample.class) + void contributeWhenPackagePrivateMethodInjectionInjectsUsingConsumer() { + Environment environment = new StandardEnvironment(); + this.beanFactory.registerSingleton("environment", environment); + RegisteredBean registeredBean = getAndApplyContribution( + PackagePrivateMethodInjectionSample.class); + testCompiledResult(registeredBean, (postProcessor, compiled) -> { + PackagePrivateMethodInjectionSample instance = new PackagePrivateMethodInjectionSample(); + postProcessor.apply(registeredBean, instance); + assertThat(instance).extracting("environment").isSameAs(environment); + assertThat(compiled.getSourceFileFromPackage(getClass().getPackageName())) + .contains("args -> instance.setTestBean("); + }); + } + + private RegisteredBean getAndApplyContribution(Class beanClass) { + RegisteredBean registeredBean = registerBean(beanClass); + BeanRegistrationAotContribution contribution = new AutowiredAnnotationBeanPostProcessor() + .processAheadOfTime(registeredBean); + contribution.applyTo(this.generationContext, this.beanRegistrationCode); + return registeredBean; + } + + private RegisteredBean registerBean(Class beanClass) { + String beanName = "testBean"; + this.beanFactory.registerBeanDefinition(beanName, + new RootBeanDefinition(beanClass)); + return RegisteredBean.of(this.beanFactory, beanName); + } + + @SuppressWarnings("unchecked") + private void testCompiledResult(RegisteredBean registeredBean, + BiConsumer, Compiled> result) { + JavaFile javaFile = createJavaFile(registeredBean.getBeanClass()); + TestCompiler.forSystem().withFiles(this.generatedFiles).compile(javaFile::writeTo, + compiled -> result.accept(compiled.getInstance(BiFunction.class), + compiled)); + } + + private JavaFile createJavaFile(Class target) { + MethodReference methodReference = this.beanRegistrationCode.instancePostProcessors + .get(0); + TypeSpec.Builder builder = TypeSpec.classBuilder("TestPostProcessor"); + builder.addModifiers(Modifier.PUBLIC); + builder.addSuperinterface(ParameterizedTypeName.get(BiFunction.class, + RegisteredBean.class, target, target)); + builder.addMethod(MethodSpec.methodBuilder("apply").addModifiers(Modifier.PUBLIC) + .addParameter(RegisteredBean.class, "registeredBean") + .addParameter(target, "instance").returns(target) + .addStatement("return $L", methodReference.toInvokeCodeBlock( + CodeBlock.of("registeredBean"), CodeBlock.of("instance"))) + .build()); + return JavaFile.builder("__", builder.build()).build(); + } + + + private static class MockBeanRegistrationCode implements BeanRegistrationCode { + + private final List instancePostProcessors = new ArrayList<>(); + + @Override + public void addInstancePostProcessor(MethodReference methodReference) { + this.instancePostProcessors.add(methodReference); + } + + @Override + public ClassName getClassName() { + return null; + } + + @Override + public MethodGenerator getMethodGenerator() { + return null; + } + + } + +} diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/PackagePrivateFieldInjectionSample.java b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/PackagePrivateFieldInjectionSample.java new file mode 100644 index 0000000000..9616e8c322 --- /dev/null +++ b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/PackagePrivateFieldInjectionSample.java @@ -0,0 +1,26 @@ +/* + * Copyright 2002-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.beans.factory.annotation; + +import org.springframework.core.env.Environment; + +public class PackagePrivateFieldInjectionSample { + + @Autowired + Environment environment; + +} diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/PackagePrivateMethodInjectionSample.java b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/PackagePrivateMethodInjectionSample.java new file mode 100644 index 0000000000..010065d122 --- /dev/null +++ b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/PackagePrivateMethodInjectionSample.java @@ -0,0 +1,31 @@ +/* + * Copyright 2002-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.beans.factory.annotation; + +import org.springframework.core.env.Environment; + +public class PackagePrivateMethodInjectionSample { + + @SuppressWarnings("unused") + private Environment environment; + + @Autowired + void setTestBean(Environment environment) { + this.environment = environment; + } + +} diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/PrivateFieldInjectionSample.java b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/PrivateFieldInjectionSample.java new file mode 100644 index 0000000000..bbe50557cc --- /dev/null +++ b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/PrivateFieldInjectionSample.java @@ -0,0 +1,27 @@ +/* + * Copyright 2002-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.beans.factory.annotation; + +import org.springframework.core.env.Environment; + +public class PrivateFieldInjectionSample { + + @Autowired + @SuppressWarnings("unused") + private Environment environment; + +} diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/PrivateMethodInjectionSample.java b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/PrivateMethodInjectionSample.java new file mode 100644 index 0000000000..bdd48f2dc5 --- /dev/null +++ b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/PrivateMethodInjectionSample.java @@ -0,0 +1,31 @@ +/* + * Copyright 2002-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.beans.factory.annotation; + +import org.springframework.core.env.Environment; + +public class PrivateMethodInjectionSample { + + @SuppressWarnings("unused") + private Environment environment; + + @Autowired + private void setTestBean(Environment environment) { + this.environment = environment; + } + +}