Allow AccessControl to determine visibility from a given type
This commit adapts AccessVisibility so that it can determine if the member or type signature is accessible from a given package. This lets implementers figure out if reflection is necessary without assuming that package private visibility is OK. Closes gh-29245
This commit is contained in:
@@ -40,7 +40,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.aot.generate.AccessVisibility;
|
||||
import org.springframework.aot.generate.AccessControl;
|
||||
import org.springframework.aot.generate.GeneratedClass;
|
||||
import org.springframework.aot.generate.GeneratedMethod;
|
||||
import org.springframework.aot.generate.GenerationContext;
|
||||
@@ -81,6 +81,7 @@ 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.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -941,7 +942,8 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
method.addParameter(RegisteredBean.class, REGISTERED_BEAN_PARAMETER);
|
||||
method.addParameter(this.target, INSTANCE_PARAMETER);
|
||||
method.returns(this.target);
|
||||
method.addCode(generateMethodCode(generationContext.getRuntimeHints()));
|
||||
method.addCode(generateMethodCode(generatedClass.getName(),
|
||||
generationContext.getRuntimeHints()));
|
||||
});
|
||||
beanRegistrationCode.addInstancePostProcessor(generateMethod.toMethodReference());
|
||||
|
||||
@@ -950,41 +952,42 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
}
|
||||
}
|
||||
|
||||
private CodeBlock generateMethodCode(RuntimeHints hints) {
|
||||
private CodeBlock generateMethodCode(ClassName targetClassName, RuntimeHints hints) {
|
||||
CodeBlock.Builder code = CodeBlock.builder();
|
||||
for (AutowiredElement autowiredElement : this.autowiredElements) {
|
||||
code.addStatement(
|
||||
generateMethodStatementForElement(autowiredElement, hints));
|
||||
code.addStatement(generateMethodStatementForElement(
|
||||
targetClassName, autowiredElement, hints));
|
||||
}
|
||||
code.addStatement("return $L", INSTANCE_PARAMETER);
|
||||
return code.build();
|
||||
}
|
||||
|
||||
private CodeBlock generateMethodStatementForElement(
|
||||
private CodeBlock generateMethodStatementForElement(ClassName targetClassName,
|
||||
AutowiredElement autowiredElement, RuntimeHints hints) {
|
||||
|
||||
Member member = autowiredElement.getMember();
|
||||
boolean required = autowiredElement.required;
|
||||
if (member instanceof Field field) {
|
||||
return generateMethodStatementForField(field, required, hints);
|
||||
return generateMethodStatementForField(
|
||||
targetClassName, field, required, hints);
|
||||
}
|
||||
if (member instanceof Method method) {
|
||||
return generateMethodStatementForMethod(method, required, hints);
|
||||
return generateMethodStatementForMethod(
|
||||
targetClassName, method, required, hints);
|
||||
}
|
||||
throw new IllegalStateException(
|
||||
"Unsupported member type " + member.getClass().getName());
|
||||
}
|
||||
|
||||
private CodeBlock generateMethodStatementForField(Field field, boolean required,
|
||||
RuntimeHints hints) {
|
||||
private CodeBlock generateMethodStatementForField(ClassName targetClassName,
|
||||
Field field, boolean required, RuntimeHints hints) {
|
||||
|
||||
hints.reflection().registerField(field);
|
||||
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) {
|
||||
AccessControl accessControl = AccessControl.forMember(field);
|
||||
if (!accessControl.isAccessibleFrom(targetClassName)) {
|
||||
return CodeBlock.of("$L.resolveAndSet($L, $L)", resolver,
|
||||
REGISTERED_BEAN_PARAMETER, INSTANCE_PARAMETER);
|
||||
}
|
||||
@@ -992,8 +995,8 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
field.getName(), resolver, REGISTERED_BEAN_PARAMETER);
|
||||
}
|
||||
|
||||
private CodeBlock generateMethodStatementForMethod(Method method,
|
||||
boolean required, RuntimeHints hints) {
|
||||
private CodeBlock generateMethodStatementForMethod(ClassName targetClassName,
|
||||
Method method, boolean required, RuntimeHints hints) {
|
||||
|
||||
CodeBlock.Builder code = CodeBlock.builder();
|
||||
code.add("$T.$L", AutowiredMethodArgumentsResolver.class,
|
||||
@@ -1004,9 +1007,8 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
generateParameterTypesCode(method.getParameterTypes()));
|
||||
}
|
||||
code.add(")");
|
||||
AccessVisibility visibility = AccessVisibility.forMember(method);
|
||||
if (visibility == AccessVisibility.PRIVATE
|
||||
|| visibility == AccessVisibility.PROTECTED) {
|
||||
AccessControl accessControl = AccessControl.forMember(method);
|
||||
if (!accessControl.isAccessibleFrom(targetClassName)) {
|
||||
hints.reflection().registerMethod(method, ExecutableMode.INVOKE);
|
||||
code.add(".resolveAndInvoke($L, $L)", REGISTERED_BEAN_PARAMETER,
|
||||
INSTANCE_PARAMETER);
|
||||
|
||||
@@ -21,7 +21,7 @@ import java.lang.reflect.Executable;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.springframework.aot.generate.AccessVisibility;
|
||||
import org.springframework.aot.generate.AccessControl;
|
||||
import org.springframework.aot.generate.GenerationContext;
|
||||
import org.springframework.aot.generate.MethodReference;
|
||||
import org.springframework.aot.generate.MethodReference.ArgumentCodeGenerator;
|
||||
@@ -85,9 +85,9 @@ class DefaultBeanRegistrationCodeFragments implements BeanRegistrationCodeFragme
|
||||
|
||||
private Class<?> extractDeclaringClass(ResolvableType beanType, Executable executable) {
|
||||
Class<?> declaringClass = ClassUtils.getUserClass(executable.getDeclaringClass());
|
||||
if (executable instanceof Constructor<?> &&
|
||||
AccessVisibility.forMember(executable) == AccessVisibility.PUBLIC &&
|
||||
FactoryBean.class.isAssignableFrom(declaringClass)) {
|
||||
if (executable instanceof Constructor<?>
|
||||
&& AccessControl.forMember(executable).isPublic()
|
||||
&& FactoryBean.class.isAssignableFrom(declaringClass)) {
|
||||
return extractTargetClassFromFactoryBean(declaringClass, beanType);
|
||||
}
|
||||
return executable.getDeclaringClass();
|
||||
|
||||
@@ -24,7 +24,8 @@ import java.lang.reflect.Modifier;
|
||||
import java.util.Arrays;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.springframework.aot.generate.AccessVisibility;
|
||||
import org.springframework.aot.generate.AccessControl;
|
||||
import org.springframework.aot.generate.AccessControl.Visibility;
|
||||
import org.springframework.aot.generate.GeneratedMethod;
|
||||
import org.springframework.aot.generate.GeneratedMethods;
|
||||
import org.springframework.aot.generate.GenerationContext;
|
||||
@@ -111,10 +112,9 @@ class InstanceSupplierCodeGenerator {
|
||||
Class<?> declaringClass = ClassUtils
|
||||
.getUserClass(constructor.getDeclaringClass());
|
||||
boolean dependsOnBean = ClassUtils.isInnerClass(declaringClass);
|
||||
AccessVisibility accessVisibility = getAccessVisibility(registeredBean,
|
||||
constructor);
|
||||
if (accessVisibility == AccessVisibility.PUBLIC
|
||||
|| accessVisibility == AccessVisibility.PACKAGE_PRIVATE) {
|
||||
Visibility accessVisibility = getAccessVisibility(registeredBean, constructor);
|
||||
if (accessVisibility == Visibility.PUBLIC
|
||||
|| accessVisibility == Visibility.PACKAGE_PRIVATE) {
|
||||
return generateCodeForAccessibleConstructor(beanName, beanClass, constructor,
|
||||
dependsOnBean, declaringClass);
|
||||
}
|
||||
@@ -207,10 +207,9 @@ class InstanceSupplierCodeGenerator {
|
||||
Class<?> declaringClass = ClassUtils
|
||||
.getUserClass(factoryMethod.getDeclaringClass());
|
||||
boolean dependsOnBean = !Modifier.isStatic(factoryMethod.getModifiers());
|
||||
AccessVisibility accessVisibility = getAccessVisibility(registeredBean,
|
||||
factoryMethod);
|
||||
if (accessVisibility == AccessVisibility.PUBLIC
|
||||
|| accessVisibility == AccessVisibility.PACKAGE_PRIVATE) {
|
||||
Visibility accessVisibility = getAccessVisibility(registeredBean, factoryMethod);
|
||||
if (accessVisibility == Visibility.PUBLIC
|
||||
|| accessVisibility == Visibility.PACKAGE_PRIVATE) {
|
||||
return generateCodeForAccessibleFactoryMethod(beanName, beanClass, factoryMethod,
|
||||
declaringClass, dependsOnBean);
|
||||
}
|
||||
@@ -314,13 +313,13 @@ class InstanceSupplierCodeGenerator {
|
||||
return code.build();
|
||||
}
|
||||
|
||||
protected AccessVisibility getAccessVisibility(RegisteredBean registeredBean,
|
||||
private Visibility getAccessVisibility(RegisteredBean registeredBean,
|
||||
Member member) {
|
||||
|
||||
AccessVisibility beanTypeAccessVisibility = AccessVisibility
|
||||
AccessControl beanTypeAccessControl = AccessControl
|
||||
.forResolvableType(registeredBean.getBeanType());
|
||||
AccessVisibility memberAccessVisibility = AccessVisibility.forMember(member);
|
||||
return AccessVisibility.lowest(beanTypeAccessVisibility, memberAccessVisibility);
|
||||
AccessControl memberAccessControl = AccessControl.forMember(member);
|
||||
return AccessControl.lowest(beanTypeAccessControl, memberAccessControl).getVisibility();
|
||||
}
|
||||
|
||||
private CodeBlock generateParameterTypesCode(Class<?>[] parameterTypes, int offset) {
|
||||
|
||||
@@ -31,11 +31,18 @@ import org.springframework.beans.factory.aot.BeanRegistrationAotContribution;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.RegisteredBean;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.testfixture.beans.factory.annotation.PackagePrivateFieldInjectionSample;
|
||||
import org.springframework.beans.testfixture.beans.factory.annotation.PackagePrivateMethodInjectionSample;
|
||||
import org.springframework.beans.testfixture.beans.factory.annotation.PrivateFieldInjectionSample;
|
||||
import org.springframework.beans.testfixture.beans.factory.annotation.PrivateMethodInjectionSample;
|
||||
import org.springframework.beans.testfixture.beans.factory.annotation.subpkg.PackagePrivateFieldInjectionFromParentSample;
|
||||
import org.springframework.beans.testfixture.beans.factory.annotation.subpkg.PackagePrivateMethodInjectionFromParentSample;
|
||||
import org.springframework.beans.testfixture.beans.factory.aot.MockBeanRegistrationCode;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
import org.springframework.core.test.tools.CompileWithForkedClassLoader;
|
||||
import org.springframework.core.test.tools.Compiled;
|
||||
import org.springframework.core.test.tools.SourceFile;
|
||||
import org.springframework.core.test.tools.TestCompiler;
|
||||
import org.springframework.javapoet.CodeBlock;
|
||||
import org.springframework.javapoet.MethodSpec;
|
||||
@@ -79,7 +86,7 @@ class AutowiredAnnotationBeanRegistrationAotContributionTests {
|
||||
PrivateFieldInjectionSample instance = new PrivateFieldInjectionSample();
|
||||
postProcessor.apply(registeredBean, instance);
|
||||
assertThat(instance).extracting("environment").isSameAs(environment);
|
||||
assertThat(compiled.getSourceFileFromPackage(getClass().getPackageName()))
|
||||
assertThat(getSourceFile(compiled, PrivateFieldInjectionSample.class))
|
||||
.contains("resolveAndSet(");
|
||||
});
|
||||
}
|
||||
@@ -98,11 +105,30 @@ class AutowiredAnnotationBeanRegistrationAotContributionTests {
|
||||
PackagePrivateFieldInjectionSample instance = new PackagePrivateFieldInjectionSample();
|
||||
postProcessor.apply(registeredBean, instance);
|
||||
assertThat(instance).extracting("environment").isSameAs(environment);
|
||||
assertThat(compiled.getSourceFileFromPackage(getClass().getPackageName()))
|
||||
assertThat(getSourceFile(compiled, PackagePrivateFieldInjectionSample.class))
|
||||
.contains("instance.environment =");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@CompileWithForkedClassLoader
|
||||
void contributeWhenPackagePrivateFieldInjectionOnParentClassInjectsUsingReflection() {
|
||||
Environment environment = new StandardEnvironment();
|
||||
this.beanFactory.registerSingleton("environment", environment);
|
||||
RegisteredBean registeredBean = getAndApplyContribution(
|
||||
PackagePrivateFieldInjectionFromParentSample.class);
|
||||
assertThat(RuntimeHintsPredicates.reflection()
|
||||
.onField(PackagePrivateFieldInjectionSample.class, "environment"))
|
||||
.accepts(this.generationContext.getRuntimeHints());
|
||||
compile(registeredBean, (postProcessor, compiled) -> {
|
||||
PackagePrivateFieldInjectionFromParentSample instance = new PackagePrivateFieldInjectionFromParentSample();
|
||||
postProcessor.apply(registeredBean, instance);
|
||||
assertThat(instance).extracting("environment").isSameAs(environment);
|
||||
assertThat(getSourceFile(compiled, PackagePrivateFieldInjectionFromParentSample.class))
|
||||
.contains("resolveAndSet");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void contributeWhenPrivateMethodInjectionInjectsUsingReflection() {
|
||||
Environment environment = new StandardEnvironment();
|
||||
@@ -116,7 +142,7 @@ class AutowiredAnnotationBeanRegistrationAotContributionTests {
|
||||
PrivateMethodInjectionSample instance = new PrivateMethodInjectionSample();
|
||||
postProcessor.apply(registeredBean, instance);
|
||||
assertThat(instance).extracting("environment").isSameAs(environment);
|
||||
assertThat(compiled.getSourceFileFromPackage(getClass().getPackageName()))
|
||||
assertThat(getSourceFile(compiled, PrivateMethodInjectionSample.class))
|
||||
.contains("resolveAndInvoke(");
|
||||
});
|
||||
}
|
||||
@@ -134,12 +160,31 @@ class AutowiredAnnotationBeanRegistrationAotContributionTests {
|
||||
compile(registeredBean, (postProcessor, compiled) -> {
|
||||
PackagePrivateMethodInjectionSample instance = new PackagePrivateMethodInjectionSample();
|
||||
postProcessor.apply(registeredBean, instance);
|
||||
assertThat(instance).extracting("environment").isSameAs(environment);
|
||||
assertThat(compiled.getSourceFileFromPackage(getClass().getPackageName()))
|
||||
assertThat(instance.environment).isSameAs(environment);
|
||||
assertThat(getSourceFile(compiled, PackagePrivateMethodInjectionSample.class))
|
||||
.contains("args -> instance.setTestBean(");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@CompileWithForkedClassLoader
|
||||
void contributeWhenPackagePrivateMethodInjectionOnParentClassInjectsUsingReflection() {
|
||||
Environment environment = new StandardEnvironment();
|
||||
this.beanFactory.registerSingleton("environment", environment);
|
||||
RegisteredBean registeredBean = getAndApplyContribution(
|
||||
PackagePrivateMethodInjectionFromParentSample.class);
|
||||
assertThat(RuntimeHintsPredicates.reflection()
|
||||
.onMethod(PackagePrivateMethodInjectionSample.class, "setTestBean"))
|
||||
.accepts(this.generationContext.getRuntimeHints());
|
||||
compile(registeredBean, (postProcessor, compiled) -> {
|
||||
PackagePrivateMethodInjectionFromParentSample instance = new PackagePrivateMethodInjectionFromParentSample();
|
||||
postProcessor.apply(registeredBean, instance);
|
||||
assertThat(instance.environment).isSameAs(environment);
|
||||
assertThat(getSourceFile(compiled, PackagePrivateMethodInjectionFromParentSample.class))
|
||||
.contains("resolveAndInvoke(");
|
||||
});
|
||||
}
|
||||
|
||||
private RegisteredBean getAndApplyContribution(Class<?> beanClass) {
|
||||
RegisteredBean registeredBean = registerBean(beanClass);
|
||||
BeanRegistrationAotContribution contribution = new AutowiredAnnotationBeanPostProcessor()
|
||||
@@ -156,6 +201,10 @@ class AutowiredAnnotationBeanRegistrationAotContributionTests {
|
||||
return RegisteredBean.of(this.beanFactory, beanName);
|
||||
}
|
||||
|
||||
private static SourceFile getSourceFile(Compiled compiled, Class<?> sample) {
|
||||
return compiled.getSourceFileFromPackage(sample.getPackageName());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void compile(RegisteredBean registeredBean,
|
||||
BiConsumer<BiFunction<RegisteredBean, Object, Object>, Compiled> result) {
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.testfixture.beans;
|
||||
|
||||
public class TestBeanWithPackagePrivateField {
|
||||
|
||||
int age;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.testfixture.beans;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class TestBeanWithPackagePrivateMethod {
|
||||
|
||||
private int age;
|
||||
|
||||
void setAge(int age) {
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,8 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.beans.factory.annotation;
|
||||
package org.springframework.beans.testfixture.beans.factory.annotation;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
public class PackagePrivateFieldInjectionSample {
|
||||
@@ -14,14 +14,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.beans.factory.annotation;
|
||||
package org.springframework.beans.testfixture.beans.factory.annotation;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
public class PackagePrivateMethodInjectionSample {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private Environment environment;
|
||||
public Environment environment;
|
||||
|
||||
@Autowired
|
||||
void setTestBean(Environment environment) {
|
||||
@@ -14,8 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.beans.factory.annotation;
|
||||
package org.springframework.beans.testfixture.beans.factory.annotation;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
public class PrivateFieldInjectionSample {
|
||||
@@ -14,8 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.beans.factory.annotation;
|
||||
package org.springframework.beans.testfixture.beans.factory.annotation;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
public class PrivateMethodInjectionSample {
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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.testfixture.beans.factory.annotation.subpkg;
|
||||
|
||||
import org.springframework.beans.testfixture.beans.factory.annotation.PackagePrivateFieldInjectionSample;
|
||||
|
||||
public class PackagePrivateFieldInjectionFromParentSample extends PackagePrivateFieldInjectionSample {
|
||||
|
||||
// see environment from parent
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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.testfixture.beans.factory.annotation.subpkg;
|
||||
|
||||
import org.springframework.beans.testfixture.beans.factory.annotation.PackagePrivateMethodInjectionSample;
|
||||
|
||||
public class PackagePrivateMethodInjectionFromParentSample extends PackagePrivateMethodInjectionSample {
|
||||
|
||||
// see setTestBean from parent
|
||||
}
|
||||
Reference in New Issue
Block a user