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:
Stephane Nicoll
2022-10-03 10:49:25 +02:00
parent df58c00bf5
commit 3b2b36d0b8
19 changed files with 816 additions and 440 deletions

View File

@@ -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) {

View File

@@ -1,26 +0,0 @@
/*
* 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;
}

View File

@@ -1,31 +0,0 @@
/*
* 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;
}
}

View File

@@ -1,27 +0,0 @@
/*
* 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;
}

View File

@@ -1,31 +0,0 @@
/*
* 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;
}
}