Complete refactor of AOT concepts
Remove the AOT code that now has an alternative API. Closes gh-28414
This commit is contained in:
@@ -1,170 +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.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aot.generator.CodeContribution;
|
||||
import org.springframework.aot.generator.DefaultCodeContribution;
|
||||
import org.springframework.aot.hint.ExecutableMode;
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessorTests.ResourceInjectionBean;
|
||||
import org.springframework.beans.factory.generator.BeanInstantiationContribution;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.testfixture.beans.TestBean;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.javapoet.support.CodeSnippet;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for code contribution of {@link AutowiredAnnotationBeanPostProcessor}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class AutowiredAnnotationBeanInstantiationContributionTests {
|
||||
|
||||
@Test
|
||||
void contributeWithPackageProtectedFieldInjection() {
|
||||
CodeContribution contribution = contribute(PackageProtectedFieldInjectionSample.class);
|
||||
assertThat(CodeSnippet.process(contribution.statements().toLambdaBody())).isEqualTo("""
|
||||
instanceContext.field("environment")
|
||||
.invoke(beanFactory, (attributes) -> bean.environment = attributes.get(0))""");
|
||||
assertThat(contribution.runtimeHints().reflection().typeHints()).singleElement().satisfies(typeHint -> {
|
||||
assertThat(typeHint.getType()).isEqualTo(TypeReference.of(PackageProtectedFieldInjectionSample.class));
|
||||
assertThat(typeHint.fields()).singleElement().satisfies(fieldHint -> {
|
||||
assertThat(fieldHint.getName()).isEqualTo("environment");
|
||||
assertThat(fieldHint.isAllowWrite()).isTrue();
|
||||
assertThat(fieldHint.isAllowUnsafeAccess()).isFalse();
|
||||
});
|
||||
});
|
||||
assertThat(contribution.protectedAccess().getPrivilegedPackageName("com.example"))
|
||||
.isEqualTo(PackageProtectedFieldInjectionSample.class.getPackageName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void contributeWithPrivateFieldInjection() {
|
||||
CodeContribution contribution = contribute(PrivateFieldInjectionSample.class);
|
||||
assertThat(CodeSnippet.process(contribution.statements().toLambdaBody())).isEqualTo("""
|
||||
instanceContext.field("environment")
|
||||
.invoke(beanFactory, (attributes) -> {
|
||||
Field environmentField = ReflectionUtils.findField(AutowiredAnnotationBeanInstantiationContributionTests.PrivateFieldInjectionSample.class, "environment");
|
||||
ReflectionUtils.makeAccessible(environmentField);
|
||||
ReflectionUtils.setField(environmentField, bean, attributes.get(0));
|
||||
})""");
|
||||
assertThat(contribution.runtimeHints().reflection().typeHints()).singleElement().satisfies(typeHint -> {
|
||||
assertThat(typeHint.getType()).isEqualTo(TypeReference.of(PrivateFieldInjectionSample.class));
|
||||
assertThat(typeHint.fields()).singleElement().satisfies(fieldHint -> {
|
||||
assertThat(fieldHint.getName()).isEqualTo("environment");
|
||||
assertThat(fieldHint.isAllowWrite()).isTrue();
|
||||
assertThat(fieldHint.isAllowUnsafeAccess()).isFalse();
|
||||
});
|
||||
});
|
||||
assertThat(contribution.protectedAccess().isAccessible("com.example")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void contributeWithPublicMethodInjection() {
|
||||
CodeContribution contribution = contribute(PublicMethodInjectionSample.class);
|
||||
assertThat(CodeSnippet.process(contribution.statements().toLambdaBody())).isEqualTo("""
|
||||
instanceContext.method("setTestBean", TestBean.class)
|
||||
.invoke(beanFactory, (attributes) -> bean.setTestBean(attributes.get(0)))""");
|
||||
assertThat(contribution.runtimeHints().reflection().typeHints()).singleElement().satisfies(typeHint -> {
|
||||
assertThat(typeHint.getType()).isEqualTo(TypeReference.of(PublicMethodInjectionSample.class));
|
||||
assertThat(typeHint.methods()).singleElement().satisfies(methodHint -> {
|
||||
assertThat(methodHint.getName()).isEqualTo("setTestBean");
|
||||
assertThat(methodHint.getModes()).contains(ExecutableMode.INTROSPECT);
|
||||
});
|
||||
});
|
||||
assertThat(contribution.protectedAccess().isAccessible("com.example")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void contributeWithInjectionPoints() {
|
||||
CodeContribution contribution = contribute(ResourceInjectionBean.class);
|
||||
assertThat(CodeSnippet.process(contribution.statements().toLambdaBody())).isEqualTo("""
|
||||
instanceContext.field("testBean")
|
||||
.resolve(beanFactory, false).ifResolved((attributes) -> {
|
||||
Field testBeanField = ReflectionUtils.findField(AutowiredAnnotationBeanPostProcessorTests.ResourceInjectionBean.class, "testBean");
|
||||
ReflectionUtils.makeAccessible(testBeanField);
|
||||
ReflectionUtils.setField(testBeanField, bean, attributes.get(0));
|
||||
});
|
||||
instanceContext.method("setTestBean2", TestBean.class)
|
||||
.invoke(beanFactory, (attributes) -> bean.setTestBean2(attributes.get(0)));""");
|
||||
assertThat(contribution.runtimeHints().reflection().typeHints()).singleElement().satisfies(typeHint -> {
|
||||
assertThat(typeHint.fields()).singleElement().satisfies(fieldHint ->
|
||||
assertThat(fieldHint.getName()).isEqualTo("testBean"));
|
||||
assertThat(typeHint.methods()).singleElement().satisfies(methodHint ->
|
||||
assertThat(methodHint.getName()).isEqualTo("setTestBean2"));
|
||||
});
|
||||
assertThat(contribution.protectedAccess().isAccessible("com.example")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void contributeWithoutInjectionPoints() {
|
||||
BeanInstantiationContribution contributor = createContribution(String.class);
|
||||
assertThat(contributor).isNull();
|
||||
}
|
||||
|
||||
private DefaultCodeContribution contribute(Class<?> type) {
|
||||
BeanInstantiationContribution contributor = createContribution(type);
|
||||
assertThat(contributor).isNotNull();
|
||||
DefaultCodeContribution contribution = new DefaultCodeContribution(new RuntimeHints());
|
||||
contributor.applyTo(contribution);
|
||||
return contribution;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private BeanInstantiationContribution createContribution(Class<?> type) {
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(type);
|
||||
return bpp.contribute(beanDefinition, type, "test");
|
||||
}
|
||||
|
||||
|
||||
public static class PackageProtectedFieldInjectionSample {
|
||||
|
||||
@Autowired
|
||||
Environment environment;
|
||||
|
||||
}
|
||||
|
||||
public static class PrivateFieldInjectionSample {
|
||||
|
||||
@Autowired
|
||||
@SuppressWarnings("unused")
|
||||
private Environment environment;
|
||||
|
||||
}
|
||||
|
||||
public static class PublicMethodInjectionSample {
|
||||
|
||||
@Autowired
|
||||
public void setTestBean(TestBean testBean) {
|
||||
|
||||
}
|
||||
|
||||
public void setUnrelated(String unrelated) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -18,7 +18,6 @@ package org.springframework.beans.factory.annotation;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.generator.BeanInstantiationContribution;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.RegisteredBean;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
@@ -26,11 +25,8 @@ import org.springframework.beans.testfixture.beans.factory.generator.lifecycle.D
|
||||
import org.springframework.beans.testfixture.beans.factory.generator.lifecycle.Init;
|
||||
import org.springframework.beans.testfixture.beans.factory.generator.lifecycle.InitDestroyBean;
|
||||
import org.springframework.beans.testfixture.beans.factory.generator.lifecycle.MultiInitDestroyBean;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
|
||||
/**
|
||||
* Tests for {@link InitDestroyAnnotationBeanPostProcessor}.
|
||||
@@ -42,60 +38,6 @@ class InitDestroyAnnotationBeanPostProcessorTests {
|
||||
|
||||
private DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
|
||||
@Test
|
||||
void contributeWithNoCallbackDoesNotMutateRootBeanDefinition() {
|
||||
RootBeanDefinition beanDefinition = mock(RootBeanDefinition.class);
|
||||
assertThat(createAotBeanPostProcessor().contribute(
|
||||
beanDefinition, String.class, "test")).isNull();
|
||||
verifyNoInteractions(beanDefinition);
|
||||
}
|
||||
|
||||
@Test
|
||||
void contributeWithInitDestroyCallback() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(InitDestroyBean.class);
|
||||
assertThat(createContribution(beanDefinition)).isNull();
|
||||
assertThat(beanDefinition.getInitMethodNames()).containsExactly("initMethod");
|
||||
assertThat(beanDefinition.getDestroyMethodNames()).containsExactly("destroyMethod");
|
||||
}
|
||||
|
||||
@Test
|
||||
void contributeWithInitDestroyCallbackRetainCustomMethods() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(InitDestroyBean.class);
|
||||
beanDefinition.setInitMethodName("customInitMethod");
|
||||
beanDefinition.setDestroyMethodNames("customDestroyMethod");
|
||||
assertThat(createContribution(beanDefinition)).isNull();
|
||||
assertThat(beanDefinition.getInitMethodNames())
|
||||
.containsExactly("customInitMethod", "initMethod");
|
||||
assertThat(beanDefinition.getDestroyMethodNames())
|
||||
.containsExactly("customDestroyMethod", "destroyMethod");
|
||||
}
|
||||
|
||||
@Test
|
||||
void contributeWithInitDestroyCallbackFilterDuplicates() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(InitDestroyBean.class);
|
||||
beanDefinition.setInitMethodName("initMethod");
|
||||
beanDefinition.setDestroyMethodNames("destroyMethod");
|
||||
assertThat(createContribution(beanDefinition)).isNull();
|
||||
assertThat(beanDefinition.getInitMethodNames()).containsExactly("initMethod");
|
||||
assertThat(beanDefinition.getDestroyMethodNames()).containsExactly("destroyMethod");
|
||||
}
|
||||
|
||||
@Test
|
||||
void contributeWithMultipleInitDestroyCallbacks() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(MultiInitDestroyBean.class);
|
||||
assertThat(createContribution(beanDefinition)).isNull();
|
||||
assertThat(beanDefinition.getInitMethodNames())
|
||||
.containsExactly("initMethod", "anotherInitMethod");
|
||||
assertThat(beanDefinition.getDestroyMethodNames())
|
||||
.containsExactly("anotherDestroyMethod", "destroyMethod");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private BeanInstantiationContribution createContribution(RootBeanDefinition beanDefinition) {
|
||||
InitDestroyAnnotationBeanPostProcessor bpp = createAotBeanPostProcessor();
|
||||
return bpp.contribute(beanDefinition, beanDefinition.getResolvableType().toClass(), "test");
|
||||
}
|
||||
|
||||
@Test
|
||||
void processAheadOfTimeWhenNoCallbackDoesNotMutateRootBeanDefinition() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(String.class);
|
||||
|
||||
@@ -1,216 +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.generator;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.function.BiPredicate;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentMatchers;
|
||||
import org.mockito.BDDMockito;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import org.springframework.aot.generator.DefaultGeneratedTypeContext;
|
||||
import org.springframework.aot.generator.GeneratedType;
|
||||
import org.springframework.aot.generator.GeneratedTypeContext;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.javapoet.ClassName;
|
||||
import org.springframework.javapoet.support.CodeSnippet;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
/**
|
||||
* Tests for {@link BeanDefinitionsContribution}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class BeanDefinitionsContributionTests {
|
||||
|
||||
@Test
|
||||
void loadContributorWithConstructorArgumentOnBeanFactory() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.setBeanClassLoader(new TestSpringFactoriesClassLoader(
|
||||
"bean-registration-contribution-provider-constructor.factories"));
|
||||
BeanDefinitionsContribution contribution = new BeanDefinitionsContribution(beanFactory);
|
||||
assertThat(contribution).extracting("contributionProviders").asList()
|
||||
.anySatisfy(provider -> assertThat(provider).isInstanceOfSatisfying(TestConstructorBeanRegistrationContributionProvider.class,
|
||||
testProvider -> assertThat(testProvider.beanFactory).isSameAs(beanFactory)))
|
||||
.anySatisfy(provider -> assertThat(provider).isInstanceOf(DefaultBeanRegistrationContributionProvider.class))
|
||||
.hasSize(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void contributeThrowsContributionNotFoundIfNoContributionIsAvailable() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerBeanDefinition("test", new RootBeanDefinition());
|
||||
BeanDefinitionsContribution contribution = new BeanDefinitionsContribution(beanFactory,
|
||||
List.of(Mockito.mock(BeanRegistrationContributionProvider.class)));
|
||||
BeanFactoryInitialization initialization = new BeanFactoryInitialization(createGenerationContext());
|
||||
assertThatThrownBy(() -> contribution.applyTo(initialization))
|
||||
.isInstanceOfSatisfying(BeanRegistrationContributionNotFoundException.class, ex -> {
|
||||
assertThat(ex.getBeanName()).isEqualTo("test");
|
||||
assertThat(ex.getBeanDefinition()).isSameAs(beanFactory.getMergedBeanDefinition("test"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void contributeThrowsBeanRegistrationExceptionIfContributionThrowsException() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerBeanDefinition("test", new RootBeanDefinition());
|
||||
BeanFactoryContribution testContribution = Mockito.mock(BeanFactoryContribution.class);
|
||||
IllegalStateException testException = new IllegalStateException();
|
||||
BDDMockito.willThrow(testException).given(testContribution).applyTo(ArgumentMatchers.any(BeanFactoryInitialization.class));
|
||||
BeanDefinitionsContribution contribution = new BeanDefinitionsContribution(beanFactory,
|
||||
List.of(new TestBeanRegistrationContributionProvider("test", testContribution)));
|
||||
BeanFactoryInitialization initialization = new BeanFactoryInitialization(createGenerationContext());
|
||||
assertThatThrownBy(() -> contribution.applyTo(initialization))
|
||||
.isInstanceOfSatisfying(BeanDefinitionGenerationException.class, ex -> {
|
||||
assertThat(ex.getBeanName()).isEqualTo("test");
|
||||
assertThat(ex.getBeanDefinition()).isSameAs(beanFactory.getMergedBeanDefinition("test"));
|
||||
assertThat(ex.getCause()).isEqualTo(testException);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void contributeGeneratesBeanDefinitionsInOrder() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerBeanDefinition("counter", BeanDefinitionBuilder
|
||||
.rootBeanDefinition(Integer.class, "valueOf").addConstructorArgValue(42).getBeanDefinition());
|
||||
beanFactory.registerBeanDefinition("name", BeanDefinitionBuilder
|
||||
.rootBeanDefinition(String.class).addConstructorArgValue("Hello").getBeanDefinition());
|
||||
CodeSnippet code = contribute(beanFactory, createGenerationContext());
|
||||
assertThat(code.getSnippet()).isEqualTo("""
|
||||
BeanDefinitionRegistrar.of("counter", Integer.class).withFactoryMethod(Integer.class, "valueOf", int.class)
|
||||
.instanceSupplier((instanceContext) -> instanceContext.create(beanFactory, (attributes) -> Integer.valueOf(attributes.get(0, int.class)))).customize((bd) -> bd.getConstructorArgumentValues().addIndexedArgumentValue(0, 42)).register(beanFactory);
|
||||
BeanDefinitionRegistrar.of("name", String.class).withConstructor(String.class)
|
||||
.instanceSupplier((instanceContext) -> instanceContext.create(beanFactory, (attributes) -> new String(attributes.get(0, String.class)))).customize((bd) -> bd.getConstructorArgumentValues().addIndexedArgumentValue(0, "Hello")).register(beanFactory);
|
||||
""");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getBeanDefinitionWithNoUnderlyingContributorReturnFalseByDefault() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
BiPredicate<String, BeanDefinition> excludeFilter = new BeanDefinitionsContribution(beanFactory)
|
||||
.getBeanDefinitionExcludeFilter();
|
||||
assertThat(excludeFilter.test("foo", new RootBeanDefinition())).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void getBeanDefinitionExcludeFilterWrapsUnderlyingFilter() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerBeanDefinition("bean1", new RootBeanDefinition());
|
||||
beanFactory.registerBeanDefinition("bean2", new RootBeanDefinition());
|
||||
BiPredicate<String, BeanDefinition> excludeFilter1 = Mockito.mock(BiPredicate.class);
|
||||
BDDMockito.given(excludeFilter1.test(ArgumentMatchers.eq("bean1"), ArgumentMatchers.any(BeanDefinition.class))).willReturn(Boolean.TRUE);
|
||||
BDDMockito.given(excludeFilter1.test(ArgumentMatchers.eq("bean2"), ArgumentMatchers.any(BeanDefinition.class))).willReturn(Boolean.FALSE);
|
||||
BiPredicate<String, BeanDefinition> excludeFilter2 = Mockito.mock(BiPredicate.class);
|
||||
BDDMockito.given(excludeFilter2.test(ArgumentMatchers.eq("bean2"), ArgumentMatchers.any(BeanDefinition.class))).willReturn(Boolean.TRUE);
|
||||
BiPredicate<String, BeanDefinition> excludeFilter = new BeanDefinitionsContribution(beanFactory, List.of(
|
||||
new TestBeanRegistrationContributionProvider("bean1", mockExcludeFilter(excludeFilter1)),
|
||||
new TestBeanRegistrationContributionProvider("bean2", mockExcludeFilter(excludeFilter2)))
|
||||
).getBeanDefinitionExcludeFilter();
|
||||
assertThat(excludeFilter.test("bean2", new RootBeanDefinition())).isTrue();
|
||||
Mockito.verify(excludeFilter1).test(ArgumentMatchers.eq("bean2"), ArgumentMatchers.any(BeanDefinition.class));
|
||||
Mockito.verify(excludeFilter2).test(ArgumentMatchers.eq("bean2"), ArgumentMatchers.any(BeanDefinition.class));
|
||||
assertThat(excludeFilter.test("bean1", new RootBeanDefinition())).isTrue();
|
||||
Mockito.verify(excludeFilter1).test(ArgumentMatchers.eq("bean1"), ArgumentMatchers.any(BeanDefinition.class));
|
||||
Mockito.verifyNoMoreInteractions(excludeFilter2);
|
||||
}
|
||||
|
||||
private CodeSnippet contribute(DefaultListableBeanFactory beanFactory, GeneratedTypeContext generationContext) {
|
||||
BeanDefinitionsContribution contribution = new BeanDefinitionsContribution(beanFactory);
|
||||
BeanFactoryInitialization initialization = new BeanFactoryInitialization(generationContext);
|
||||
contribution.applyTo(initialization);
|
||||
return CodeSnippet.of(initialization.toCodeBlock());
|
||||
}
|
||||
|
||||
private GeneratedTypeContext createGenerationContext() {
|
||||
return new DefaultGeneratedTypeContext("com.example", packageName ->
|
||||
GeneratedType.of(ClassName.get(packageName, "Test")));
|
||||
}
|
||||
|
||||
private BeanFactoryContribution mockExcludeFilter(BiPredicate<String, BeanDefinition> excludeFilter) {
|
||||
BeanFactoryContribution contribution = Mockito.mock(BeanFactoryContribution.class);
|
||||
BDDMockito.given(contribution.getBeanDefinitionExcludeFilter()).willReturn(excludeFilter);
|
||||
return contribution;
|
||||
}
|
||||
|
||||
static class TestBeanRegistrationContributionProvider implements BeanRegistrationContributionProvider {
|
||||
|
||||
private final String beanName;
|
||||
|
||||
private final BeanFactoryContribution contribution;
|
||||
|
||||
public TestBeanRegistrationContributionProvider(String beanName, BeanFactoryContribution contribution) {
|
||||
this.beanName = beanName;
|
||||
this.contribution = contribution;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BeanFactoryContribution getContributionFor(String beanName, RootBeanDefinition beanDefinition) {
|
||||
return (beanName.equals(this.beanName) ? this.contribution : null);
|
||||
}
|
||||
}
|
||||
|
||||
static class TestConstructorBeanRegistrationContributionProvider implements BeanRegistrationContributionProvider {
|
||||
|
||||
private final ConfigurableListableBeanFactory beanFactory;
|
||||
|
||||
TestConstructorBeanRegistrationContributionProvider(ConfigurableListableBeanFactory beanFactory) {
|
||||
Assert.notNull(beanFactory, "BeanFactory must not be null");
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BeanFactoryContribution getContributionFor(String beanName, RootBeanDefinition beanDefinition) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class TestSpringFactoriesClassLoader extends ClassLoader {
|
||||
|
||||
private final String factoriesName;
|
||||
|
||||
TestSpringFactoriesClassLoader(String factoriesName) {
|
||||
super(BeanDefinitionsContributionTests.class.getClassLoader());
|
||||
this.factoriesName = factoriesName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Enumeration<URL> getResources(String name) throws IOException {
|
||||
if ("META-INF/spring.factories".equals(name)) {
|
||||
return super.getResources("org/springframework/beans/factory/generator/" + this.factoriesName);
|
||||
}
|
||||
return super.getResources(name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,79 +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.generator;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.javapoet.CodeBlock;
|
||||
import org.springframework.javapoet.support.CodeSnippet;
|
||||
import org.springframework.javapoet.support.MultiStatement;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link BeanFieldGenerator}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class BeanFieldGeneratorTests {
|
||||
|
||||
private final BeanFieldGenerator generator = new BeanFieldGenerator();
|
||||
|
||||
@Test
|
||||
void generateSetFieldWithPublicField() {
|
||||
MultiStatement statement = this.generator.generateSetValue("bean",
|
||||
field(SampleBean.class, "one"), CodeBlock.of("$S", "test"));
|
||||
assertThat(CodeSnippet.process(statement.toCodeBlock())).isEqualTo("""
|
||||
bean.one = "test";
|
||||
""");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateSetFieldWithPrivateField() {
|
||||
MultiStatement statement = this.generator.generateSetValue("example",
|
||||
field(SampleBean.class, "two"), CodeBlock.of("42"));
|
||||
CodeSnippet code = CodeSnippet.of(statement.toCodeBlock());
|
||||
assertThat(code.getSnippet()).isEqualTo("""
|
||||
Field twoField = ReflectionUtils.findField(BeanFieldGeneratorTests.SampleBean.class, "two");
|
||||
ReflectionUtils.makeAccessible(twoField);
|
||||
ReflectionUtils.setField(twoField, example, 42);
|
||||
""");
|
||||
assertThat(code.hasImport(ReflectionUtils.class)).isTrue();
|
||||
assertThat(code.hasImport(BeanFieldGeneratorTests.class)).isTrue();
|
||||
}
|
||||
|
||||
|
||||
private Field field(Class<?> type, String name) {
|
||||
Field field = ReflectionUtils.findField(type, name);
|
||||
assertThat(field).isNotNull();
|
||||
return field;
|
||||
}
|
||||
|
||||
|
||||
public static class SampleBean {
|
||||
|
||||
public String one;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private int two;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,267 +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.generator;
|
||||
|
||||
import java.io.StringWriter;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanReference;
|
||||
import org.springframework.beans.factory.support.ManagedList;
|
||||
import org.springframework.beans.factory.support.ManagedSet;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.javapoet.CodeBlock;
|
||||
import org.springframework.javapoet.support.CodeSnippet;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link BeanParameterGenerator}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class BeanParameterGeneratorTests {
|
||||
|
||||
private final BeanParameterGenerator generator = new BeanParameterGenerator();
|
||||
|
||||
@Test
|
||||
void generateCharArray() {
|
||||
char[] value = new char[] { 'v', 'a', 'l', 'u', 'e' };
|
||||
assertThat(generate(value, ResolvableType.forArrayComponent(ResolvableType.forClass(char.class))))
|
||||
.isEqualTo("new char[] { 'v', 'a', 'l', 'u', 'e' }");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateStringArray() {
|
||||
String[] value = new String[] { "a", "test" };
|
||||
assertThat(generate(value, ResolvableType.forArrayComponent(ResolvableType.forClass(String.class))))
|
||||
.isEqualTo("new String[] { \"a\", \"test\" }");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateStringList() {
|
||||
List<String> value = List.of("a", "test");
|
||||
CodeSnippet code = codeSnippet(value, ResolvableType.forClassWithGenerics(List.class, String.class));
|
||||
assertThat(code.getSnippet()).isEqualTo(
|
||||
"List.of(\"a\", \"test\")");
|
||||
assertThat(code.hasImport(List.class)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateStringManagedList() {
|
||||
ManagedList<String> value = ManagedList.of("a", "test");
|
||||
CodeSnippet code = codeSnippet(value, ResolvableType.forClassWithGenerics(List.class, String.class));
|
||||
assertThat(code.getSnippet()).isEqualTo(
|
||||
"ManagedList.of(\"a\", \"test\")");
|
||||
assertThat(code.hasImport(ManagedList.class)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateEmptyList() {
|
||||
List<String> value = List.of();
|
||||
CodeSnippet code = codeSnippet(value, ResolvableType.forClassWithGenerics(List.class, String.class));
|
||||
assertThat(code.getSnippet()).isEqualTo("Collections.emptyList()");
|
||||
assertThat(code.hasImport(Collections.class)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateStringSet() {
|
||||
Set<String> value = Set.of("a", "test");
|
||||
CodeSnippet code = codeSnippet(value, ResolvableType.forClassWithGenerics(Set.class, String.class));
|
||||
assertThat(code.getSnippet()).startsWith("Set.of(").contains("a").contains("test");
|
||||
assertThat(code.hasImport(Set.class)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateStringManagedSet() {
|
||||
Set<String> value = ManagedSet.of("a", "test");
|
||||
CodeSnippet code = codeSnippet(value, ResolvableType.forClassWithGenerics(Set.class, String.class));
|
||||
assertThat(code.getSnippet()).isEqualTo(
|
||||
"ManagedSet.of(\"a\", \"test\")");
|
||||
assertThat(code.hasImport(ManagedSet.class)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateEmptySet() {
|
||||
Set<String> value = Set.of();
|
||||
CodeSnippet code = codeSnippet(value, ResolvableType.forClassWithGenerics(Set.class, String.class));
|
||||
assertThat(code.getSnippet()).isEqualTo("Collections.emptySet()");
|
||||
assertThat(code.hasImport(Collections.class)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateMap() {
|
||||
Map<String, Object> value = new LinkedHashMap<>();
|
||||
value.put("name", "Hello");
|
||||
value.put("counter", 42);
|
||||
assertThat(generate(value)).isEqualTo("Map.of(\"name\", \"Hello\", \"counter\", 42)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateMapWithEnum() {
|
||||
Map<String, Object> value = new HashMap<>();
|
||||
value.put("unit", ChronoUnit.DAYS);
|
||||
assertThat(generate(value)).isEqualTo("Map.of(\"unit\", ChronoUnit.DAYS)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateEmptyMap() {
|
||||
assertThat(generate(Map.of())).isEqualTo("Map.of()");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateString() {
|
||||
assertThat(generate("test", ResolvableType.forClass(String.class))).isEqualTo("\"test\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateCharEscapeBackslash() {
|
||||
assertThat(generate('\\', ResolvableType.forType(char.class))).isEqualTo("'\\\\'");
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("primitiveValues")
|
||||
void generatePrimitiveValue(Object value, String parameter) {
|
||||
assertThat(generate(value, ResolvableType.forClass(value.getClass()))).isEqualTo(parameter);
|
||||
}
|
||||
|
||||
private static Stream<Arguments> primitiveValues() {
|
||||
return Stream.of(Arguments.of((short) 0, "0"), Arguments.of((1), "1"), Arguments.of(2L, "2"),
|
||||
Arguments.of(2.5d, "2.5"), Arguments.of(2.7f, "2.7"), Arguments.of('c', "'c'"),
|
||||
Arguments.of((byte) 1, "1"), Arguments.of(true, "true"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateEnum() {
|
||||
assertThat(generate(ChronoUnit.DAYS, ResolvableType.forClass(ChronoUnit.class)))
|
||||
.isEqualTo("ChronoUnit.DAYS");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateClass() {
|
||||
assertThat(generate(Integer.class, ResolvableType.forClass(Class.class)))
|
||||
.isEqualTo("Integer.class");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateResolvableType() {
|
||||
ResolvableType type = ResolvableType.forClassWithGenerics(Consumer.class, Integer.class);
|
||||
assertThat(generate(type, type))
|
||||
.isEqualTo("ResolvableType.forClassWithGenerics(Consumer.class, Integer.class)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateExecutableParameterTypesWithConstructor() {
|
||||
Constructor<?> constructor = TestSample.class.getDeclaredConstructors()[0];
|
||||
assertThat(CodeSnippet.process(this.generator.generateExecutableParameterTypes(constructor)))
|
||||
.isEqualTo("String.class, ResourceLoader.class");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateExecutableParameterTypesWithNoArgConstructor() {
|
||||
Constructor<?> constructor = BeanParameterGeneratorTests.class.getDeclaredConstructors()[0];
|
||||
assertThat(CodeSnippet.process(this.generator.generateExecutableParameterTypes(constructor)))
|
||||
.isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateExecutableParameterTypesWithMethod() {
|
||||
Method method = ReflectionUtils.findMethod(TestSample.class, "createBean", String.class, Integer.class);
|
||||
assertThat(CodeSnippet.process(this.generator.generateExecutableParameterTypes(method)))
|
||||
.isEqualTo("String.class, Integer.class");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateNull() {
|
||||
assertThat(generate(null)).isEqualTo("null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateBeanReference() {
|
||||
BeanReference beanReference = mock(BeanReference.class);
|
||||
given(beanReference.getBeanName()).willReturn("testBean");
|
||||
assertThat(generate(beanReference)).isEqualTo("new RuntimeBeanReference(\"testBean\")");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateBeanDefinitionCallsConsumer() {
|
||||
BeanParameterGenerator customGenerator = new BeanParameterGenerator(
|
||||
beanDefinition -> CodeBlock.of("test"));
|
||||
assertThat(CodeSnippet.process(customGenerator.generateParameterValue(
|
||||
new RootBeanDefinition()))).isEqualTo("test");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateBeanDefinitionWithoutConsumerFails() {
|
||||
BeanParameterGenerator customGenerator = new BeanParameterGenerator();
|
||||
assertThatIllegalStateException().isThrownBy(() -> customGenerator
|
||||
.generateParameterValue(new RootBeanDefinition()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateUnsupportedParameter() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> generate(new StringWriter()))
|
||||
.withMessageContaining(StringWriter.class.getName());
|
||||
}
|
||||
|
||||
private String generate(@Nullable Object value) {
|
||||
return CodeSnippet.process(this.generator.generateParameterValue(value));
|
||||
}
|
||||
|
||||
private String generate(Object value, ResolvableType resolvableType) {
|
||||
return codeSnippet(value, resolvableType).getSnippet();
|
||||
}
|
||||
|
||||
private CodeSnippet codeSnippet(Object value, ResolvableType resolvableType) {
|
||||
return CodeSnippet.of(this.generator.generateParameterValue(value, () -> resolvableType));
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
static class TestSample {
|
||||
|
||||
public TestSample(String test, ResourceLoader resourceLoader) {
|
||||
}
|
||||
|
||||
String createBean(String name, Integer counter) {
|
||||
return "test";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,736 +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.generator;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Executable;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.lang.model.element.Modifier;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aot.generator.DefaultGeneratedTypeContext;
|
||||
import org.springframework.aot.generator.GeneratedType;
|
||||
import org.springframework.aot.hint.ExecutableHint;
|
||||
import org.springframework.aot.hint.ExecutableMode;
|
||||
import org.springframework.aot.hint.ReflectionHints;
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
import org.springframework.aot.test.generator.compile.TestCompiler;
|
||||
import org.springframework.aot.test.generator.file.SourceFile;
|
||||
import org.springframework.aot.test.generator.file.SourceFiles;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.testfixture.beans.factory.generator.BeanFactoryInitializer;
|
||||
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.factory.SampleFactory;
|
||||
import org.springframework.beans.testfixture.beans.factory.generator.injection.InjectionComponent;
|
||||
import org.springframework.beans.testfixture.beans.factory.generator.lifecycle.InitDestroyBean;
|
||||
import org.springframework.beans.testfixture.beans.factory.generator.property.ConfigurableBean;
|
||||
import org.springframework.beans.testfixture.beans.factory.generator.visibility.ProtectedConstructorComponent;
|
||||
import org.springframework.beans.testfixture.beans.factory.generator.visibility.ProtectedFactoryMethod;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.testfixture.aot.generator.visibility.PublicFactoryBean;
|
||||
import org.springframework.javapoet.ClassName;
|
||||
import org.springframework.javapoet.CodeBlock;
|
||||
import org.springframework.javapoet.CodeBlock.Builder;
|
||||
import org.springframework.javapoet.JavaFile;
|
||||
import org.springframework.javapoet.MethodSpec;
|
||||
import org.springframework.javapoet.support.CodeSnippet;
|
||||
import org.springframework.javapoet.support.MultiStatement;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
|
||||
/**
|
||||
* Tests for {@link BeanRegistrationBeanFactoryContribution}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class BeanRegistrationBeanFactoryContributionTests {
|
||||
|
||||
private final DefaultGeneratedTypeContext generatedTypeContext = new DefaultGeneratedTypeContext("com.example", packageName -> GeneratedType.of(ClassName.get(packageName, "Test")));
|
||||
|
||||
private final BeanFactoryInitialization initialization = new BeanFactoryInitialization(this.generatedTypeContext);
|
||||
|
||||
@Test
|
||||
void generateUsingConstructor() {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(InjectionComponent.class).getBeanDefinition();
|
||||
CodeSnippet registration = beanRegistration(beanDefinition, singleConstructor(InjectionComponent.class), code -> code.add("() -> test"));
|
||||
assertThat(registration.getSnippet()).isEqualTo("""
|
||||
BeanDefinitionRegistrar.of("test", InjectionComponent.class).withConstructor(String.class)
|
||||
.instanceSupplier(() -> test).register(beanFactory);
|
||||
""");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateUsingConstructorWithNoArgument() {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(SimpleConfiguration.class).getBeanDefinition();
|
||||
CodeSnippet registration = beanRegistration(beanDefinition, singleConstructor(SimpleConfiguration.class), code -> code.add("() -> test"));
|
||||
assertThat(registration.getSnippet()).isEqualTo("""
|
||||
BeanDefinitionRegistrar.of("test", SimpleConfiguration.class)
|
||||
.instanceSupplier(() -> test).register(beanFactory);
|
||||
""");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateUsingConstructorOnInnerClass() {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(EnvironmentAwareComponent.class).getBeanDefinition();
|
||||
CodeSnippet registration = beanRegistration(beanDefinition, singleConstructor(EnvironmentAwareComponent.class), code -> code.add("() -> test"));
|
||||
assertThat(registration.getSnippet()).isEqualTo("""
|
||||
BeanDefinitionRegistrar.of("test", InnerComponentConfiguration.EnvironmentAwareComponent.class).withConstructor(InnerComponentConfiguration.class, Environment.class)
|
||||
.instanceSupplier(() -> test).register(beanFactory);
|
||||
""");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateUsingConstructorOnInnerClassWithNoExtraArg() {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(NoDependencyComponent.class).getBeanDefinition();
|
||||
CodeSnippet registration = beanRegistration(beanDefinition, singleConstructor(NoDependencyComponent.class), code -> code.add("() -> test"));
|
||||
assertThat(registration.getSnippet()).isEqualTo("""
|
||||
BeanDefinitionRegistrar.of("test", InnerComponentConfiguration.NoDependencyComponent.class)
|
||||
.instanceSupplier(() -> test).register(beanFactory);
|
||||
""");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateUsingFactoryMethod() {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(String.class).getBeanDefinition();
|
||||
CodeSnippet registration = beanRegistration(beanDefinition, method(SampleFactory.class, "create", String.class), code -> code.add("() -> test"));
|
||||
assertThat(registration.hasImport(SampleFactory.class)).isTrue();
|
||||
assertThat(registration.getSnippet()).isEqualTo("""
|
||||
BeanDefinitionRegistrar.of("test", String.class).withFactoryMethod(SampleFactory.class, "create", String.class)
|
||||
.instanceSupplier(() -> test).register(beanFactory);
|
||||
""");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateUsingFactoryMethodWithNoArgument() {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Integer.class).getBeanDefinition();
|
||||
CodeSnippet registration = beanRegistration(beanDefinition, method(SampleFactory.class, "integerBean"), code -> code.add("() -> test"));
|
||||
assertThat(registration.hasImport(SampleFactory.class)).isTrue();
|
||||
assertThat(registration.getSnippet()).isEqualTo("""
|
||||
BeanDefinitionRegistrar.of("test", Integer.class).withFactoryMethod(SampleFactory.class, "integerBean")
|
||||
.instanceSupplier(() -> test).register(beanFactory);
|
||||
""");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateUsingPublicAccessDoesNotAccessAnotherPackage() {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(SimpleConfiguration.class).getBeanDefinition();
|
||||
getContributionFor(beanDefinition, singleConstructor(SimpleConfiguration.class)).applyTo(this.initialization);
|
||||
assertThat(this.generatedTypeContext.toJavaFiles()).hasSize(1);
|
||||
assertThat(CodeSnippet.of(this.initialization.toCodeBlock()).getSnippet()).isEqualTo("""
|
||||
BeanDefinitionRegistrar.of("test", SimpleConfiguration.class)
|
||||
.instanceSupplier(SimpleConfiguration::new).register(beanFactory);
|
||||
""");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateUsingProtectedConstructorWritesToBlessedPackage() {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(ProtectedConstructorComponent.class).getBeanDefinition();
|
||||
getContributionFor(beanDefinition, singleConstructor(ProtectedConstructorComponent.class)).applyTo(this.initialization);
|
||||
assertThat(this.generatedTypeContext.hasGeneratedType(ProtectedConstructorComponent.class.getPackageName())).isTrue();
|
||||
GeneratedType generatedType = this.generatedTypeContext.getGeneratedType(ProtectedConstructorComponent.class.getPackageName());
|
||||
assertThat(removeIndent(codeOf(generatedType), 1)).containsSequence("""
|
||||
public static void registerTest(DefaultListableBeanFactory beanFactory) {
|
||||
BeanDefinitionRegistrar.of("test", ProtectedConstructorComponent.class)
|
||||
.instanceSupplier(ProtectedConstructorComponent::new).register(beanFactory);
|
||||
}""");
|
||||
assertThat(CodeSnippet.of(this.initialization.toCodeBlock()).getSnippet()).isEqualTo(
|
||||
ProtectedConstructorComponent.class.getPackageName() + ".Test.registerTest(beanFactory);\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateUsingProtectedFactoryMethodWritesToBlessedPackage() {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(String.class).getBeanDefinition();
|
||||
getContributionFor(beanDefinition, method(ProtectedFactoryMethod.class, "testBean", Integer.class))
|
||||
.applyTo(this.initialization);
|
||||
assertThat(this.generatedTypeContext.hasGeneratedType(ProtectedFactoryMethod.class.getPackageName())).isTrue();
|
||||
GeneratedType generatedType = this.generatedTypeContext.getGeneratedType(ProtectedConstructorComponent.class.getPackageName());
|
||||
assertThat(removeIndent(codeOf(generatedType), 1)).containsSequence("""
|
||||
public static void registerProtectedFactoryMethod_test(DefaultListableBeanFactory beanFactory) {
|
||||
BeanDefinitionRegistrar.of("test", String.class).withFactoryMethod(ProtectedFactoryMethod.class, "testBean", Integer.class)
|
||||
.instanceSupplier((instanceContext) -> instanceContext.create(beanFactory, (attributes) -> beanFactory.getBean(ProtectedFactoryMethod.class).testBean(attributes.get(0)))).register(beanFactory);
|
||||
}""");
|
||||
assertThat(CodeSnippet.of(this.initialization.toCodeBlock()).getSnippet()).isEqualTo(
|
||||
ProtectedConstructorComponent.class.getPackageName() + ".Test.registerProtectedFactoryMethod_test(beanFactory);\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateUsingProtectedGenericTypeWritesToBlessedPackage() {
|
||||
RootBeanDefinition beanDefinition = (RootBeanDefinition) BeanDefinitionBuilder.rootBeanDefinition(
|
||||
PublicFactoryBean.class).getBeanDefinition();
|
||||
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, String.class);
|
||||
// This resolve the generic parameter to a protected type
|
||||
beanDefinition.setTargetType(PublicFactoryBean.resolveToProtectedGenericParameter());
|
||||
getContributionFor(beanDefinition, singleConstructor(PublicFactoryBean.class)).applyTo(this.initialization);
|
||||
assertThat(this.generatedTypeContext.hasGeneratedType(PublicFactoryBean.class.getPackageName())).isTrue();
|
||||
GeneratedType generatedType = this.generatedTypeContext.getGeneratedType(PublicFactoryBean.class.getPackageName());
|
||||
assertThat(removeIndent(codeOf(generatedType), 1)).containsSequence("""
|
||||
public static void registerTest(DefaultListableBeanFactory beanFactory) {
|
||||
BeanDefinitionRegistrar.of("test", ResolvableType.forClassWithGenerics(PublicFactoryBean.class, ProtectedType.class)).withConstructor(Class.class)
|
||||
.instanceSupplier((instanceContext) -> instanceContext.create(beanFactory, (attributes) -> new PublicFactoryBean(attributes.get(0)))).customize((bd) -> bd.getConstructorArgumentValues().addIndexedArgumentValue(0, String.class)).register(beanFactory);
|
||||
}""");
|
||||
assertThat(CodeSnippet.of(this.initialization.toCodeBlock()).getSnippet()).isEqualTo(
|
||||
PublicFactoryBean.class.getPackageName() + ".Test.registerTest(beanFactory);\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWithBeanDefinitionHavingInitMethodName() {
|
||||
compile(simpleConfigurationRegistration(bd -> bd.setInitMethodName("someMethod")),
|
||||
hasBeanDefinition(generatedBd -> assertThat(generatedBd.getInitMethodNames()).containsExactly("someMethod")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWithBeanDefinitionHavingInitMethodNames() {
|
||||
compile(simpleConfigurationRegistration(bd -> bd.setInitMethodNames("i1", "i2")),
|
||||
hasBeanDefinition(generatedBd -> assertThat(generatedBd.getInitMethodNames()).containsExactly("i1", "i2")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWithBeanDefinitionHavingDestroyMethodName() {
|
||||
compile(simpleConfigurationRegistration(bd -> bd.setDestroyMethodName("someMethod")),
|
||||
hasBeanDefinition(generatedBd -> assertThat(generatedBd.getDestroyMethodNames()).containsExactly("someMethod")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWithBeanDefinitionHavingDestroyMethodNames() {
|
||||
compile(simpleConfigurationRegistration(bd -> bd.setDestroyMethodNames("d1", "d2")),
|
||||
hasBeanDefinition(generatedBd -> assertThat(generatedBd.getDestroyMethodNames()).containsExactly("d1", "d2")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWithBeanDefinitionHavingSyntheticFlag() {
|
||||
compile(simpleConfigurationRegistration(bd -> bd.setSynthetic(true)),
|
||||
hasBeanDefinition(generatedBd -> assertThat(generatedBd.isSynthetic()).isTrue()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWithBeanDefinitionHavingDependsOn() {
|
||||
compile(simpleConfigurationRegistration(bd -> bd.setDependsOn("test")),
|
||||
hasBeanDefinition(generatedBd -> assertThat(generatedBd.getDependsOn()).containsExactly("test")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWithBeanDefinitionHavingLazyInit() {
|
||||
compile(simpleConfigurationRegistration(bd -> bd.setLazyInit(true)),
|
||||
hasBeanDefinition(generatedBd -> assertThat(generatedBd.isLazyInit()).isTrue()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWithBeanDefinitionHavingRole() {
|
||||
compile(simpleConfigurationRegistration(bd -> bd.setRole(BeanDefinition.ROLE_INFRASTRUCTURE)),
|
||||
hasBeanDefinition(generatedBd -> assertThat(generatedBd.getRole())
|
||||
.isEqualTo(BeanDefinition.ROLE_INFRASTRUCTURE)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWithBeanDefinitionHavingScope() {
|
||||
compile(simpleConfigurationRegistration(bd -> bd.setScope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)),
|
||||
hasBeanDefinition(generatedBd -> assertThat(generatedBd.getScope())
|
||||
.isEqualTo(ConfigurableBeanFactory.SCOPE_PROTOTYPE)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWithBeanDefinitionHavingAutowiredCandidate() {
|
||||
compile(simpleConfigurationRegistration(bd -> bd.setAutowireCandidate(false)),
|
||||
hasBeanDefinition(generatedBd -> assertThat(generatedBd.isAutowireCandidate()).isFalse()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWithBeanDefinitionHavingDefaultKeepsThem() {
|
||||
compile(simpleConfigurationRegistration(bd -> {}), hasBeanDefinition(generatedBd -> {
|
||||
assertThat(generatedBd.isSynthetic()).isFalse();
|
||||
assertThat(generatedBd.getDependsOn()).isNull();
|
||||
assertThat(generatedBd.isLazyInit()).isFalse();
|
||||
assertThat(generatedBd.getRole()).isEqualTo(BeanDefinition.ROLE_APPLICATION);
|
||||
assertThat(generatedBd.getScope()).isEqualTo(ConfigurableBeanFactory.SCOPE_SINGLETON);
|
||||
assertThat(generatedBd.isAutowireCandidate()).isTrue();
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWithBeanDefinitionHavingMultipleAttributes() {
|
||||
compile(simpleConfigurationRegistration(bd -> {
|
||||
bd.setSynthetic(true);
|
||||
bd.setPrimary(true);
|
||||
}), hasBeanDefinition(generatedBd -> {
|
||||
assertThat(generatedBd.isSynthetic()).isTrue();
|
||||
assertThat(generatedBd.isPrimary()).isTrue();
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWithBeanDefinitionHavingProperty() {
|
||||
compile(simpleConfigurationRegistration(bd -> bd.getPropertyValues().addPropertyValue("test", "Hello")),
|
||||
hasBeanDefinition(generatedBd -> {
|
||||
assertThat(generatedBd.getPropertyValues().contains("test")).isTrue();
|
||||
assertThat(generatedBd.getPropertyValues().get("test")).isEqualTo("Hello");
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWithBeanDefinitionHavingSeveralProperties() {
|
||||
compile(simpleConfigurationRegistration(bd -> {
|
||||
bd.getPropertyValues().addPropertyValue("test", "Hello");
|
||||
bd.getPropertyValues().addPropertyValue("counter", 42);
|
||||
}), hasBeanDefinition(generatedBd -> {
|
||||
assertThat(generatedBd.getPropertyValues().contains("test")).isTrue();
|
||||
assertThat(generatedBd.getPropertyValues().get("test")).isEqualTo("Hello");
|
||||
assertThat(generatedBd.getPropertyValues().contains("counter")).isTrue();
|
||||
assertThat(generatedBd.getPropertyValues().get("counter")).isEqualTo(42);
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWithBeanDefinitionHavingPropertyReference() {
|
||||
compile(simpleConfigurationRegistration(bd -> bd.getPropertyValues().addPropertyValue(
|
||||
"myService", new RuntimeBeanReference("test"))), hasBeanDefinition(generatedBd -> {
|
||||
assertThat(generatedBd.getPropertyValues().contains("myService")).isTrue();
|
||||
assertThat(generatedBd.getPropertyValues().get("myService"))
|
||||
.isInstanceOfSatisfying(RuntimeBeanReference.class, ref ->
|
||||
assertThat(ref.getBeanName()).isEqualTo("test"));
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWithBeanDefinitionHavingPropertyAsBeanDefinition() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
BeanDefinition innerBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(SimpleConfiguration.class, "stringBean")
|
||||
.getBeanDefinition();
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(ConfigurableBean.class)
|
||||
.addPropertyValue("name", innerBeanDefinition).getBeanDefinition();
|
||||
compile(getDefaultContribution(beanFactory, beanDefinition), hasBeanDefinition(generatedBd -> {
|
||||
assertThat(generatedBd.getPropertyValues().contains("name")).isTrue();
|
||||
assertThat(generatedBd.getPropertyValues().get("name")).isInstanceOfSatisfying(RootBeanDefinition.class, innerGeneratedBd ->
|
||||
assertThat(innerGeneratedBd.getResolvedFactoryMethod()).isEqualTo(method(SimpleConfiguration.class, "stringBean")));
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWithBeanDefinitionHavingPropertyAsListOfBeanDefinitions() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
BeanDefinition innerBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(SimpleConfiguration.class, "stringBean")
|
||||
.getBeanDefinition();
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(ConfigurableBean.class)
|
||||
.addPropertyValue("names", List.of(innerBeanDefinition, innerBeanDefinition)).getBeanDefinition();
|
||||
compile(getDefaultContribution(beanFactory, beanDefinition), hasBeanDefinition(generatedBd -> {
|
||||
assertThat(generatedBd.getPropertyValues().contains("names")).isTrue();
|
||||
assertThat(generatedBd.getPropertyValues().get("names")).asList().hasSize(2);
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWithBeanDefinitionHavingPropertyAsBeanDefinitionUseDedicatedVariableNames() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
BeanDefinition innerBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(SimpleConfiguration.class, "stringBean")
|
||||
.setRole(2).getBeanDefinition();
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(ConfigurableBean.class)
|
||||
.addPropertyValue("name", innerBeanDefinition).getBeanDefinition();
|
||||
getDefaultContribution(beanFactory, beanDefinition).applyTo(this.initialization);
|
||||
CodeSnippet registration = CodeSnippet.of(this.initialization.toCodeBlock());
|
||||
assertThat(registration.getSnippet()).isEqualTo("""
|
||||
BeanDefinitionRegistrar.of("test", ConfigurableBean.class)
|
||||
.instanceSupplier(ConfigurableBean::new).customize((bd) -> bd.getPropertyValues().addPropertyValue("name", BeanDefinitionRegistrar.inner(SimpleConfiguration.class).withFactoryMethod(SimpleConfiguration.class, "stringBean")
|
||||
.instanceSupplier(() -> beanFactory.getBean(SimpleConfiguration.class).stringBean()).customize((bd_) -> bd_.setRole(2)).toBeanDefinition())).register(beanFactory);
|
||||
""");
|
||||
assertThat(registration.hasImport(SimpleConfiguration.class)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateUsingSingleConstructorArgument() {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(String.class).getBeanDefinition();
|
||||
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, "hello");
|
||||
compile(getContributionFor(beanDefinition, method(SampleFactory.class, "create", String.class)), beanFactory ->
|
||||
assertThat(beanFactory.getBean(String.class)).isEqualTo("hello"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateUsingSeveralConstructorArguments() {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(String.class)
|
||||
.addConstructorArgValue(42).addConstructorArgValue("testBean")
|
||||
.getBeanDefinition();
|
||||
compile(getContributionFor(beanDefinition, method(SampleFactory.class, "create", Number.class, String.class)), beanFactory ->
|
||||
assertThat(beanFactory.getBean(String.class)).isEqualTo("42testBean"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWithBeanDefinitionHavingAttributesDoesNotWriteThemByDefault() {
|
||||
compile(simpleConfigurationRegistration(bd -> {
|
||||
bd.setAttribute("test", "value");
|
||||
bd.setAttribute("counter", 42);
|
||||
}), hasBeanDefinition(generatedBd -> {
|
||||
assertThat(generatedBd.getAttribute("test")).isNull();
|
||||
assertThat(generatedBd.getAttribute("counter")).isNull();
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWithBeanDefinitionHavingAttributesUseCustomFilter() {
|
||||
RootBeanDefinition bd = new RootBeanDefinition(SimpleConfiguration.class);
|
||||
bd.setAttribute("test", "value");
|
||||
bd.setAttribute("counter", 42);
|
||||
DefaultBeanInstantiationGenerator beanInstantiationGenerator = new DefaultBeanInstantiationGenerator(
|
||||
singleConstructor(SimpleConfiguration.class), Collections.emptyList());
|
||||
compile(new BeanRegistrationBeanFactoryContribution("test", bd, beanInstantiationGenerator) {
|
||||
@Override
|
||||
protected Predicate<String> getAttributeFilter() {
|
||||
return candidate -> candidate.equals("counter");
|
||||
}
|
||||
}, hasBeanDefinition(generatedBd -> {
|
||||
assertThat(generatedBd.getAttribute("test")).isNull();
|
||||
assertThat(generatedBd.getAttribute("counter")).isNotNull().isEqualTo(42);
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerRuntimeHintsWithInitMethodNames() {
|
||||
RootBeanDefinition bd = new RootBeanDefinition(InitDestroyBean.class);
|
||||
bd.setInitMethodNames("customInitMethod", "initMethod");
|
||||
RuntimeHints runtimeHints = new RuntimeHints();
|
||||
getDefaultContribution(new DefaultListableBeanFactory(), bd).registerRuntimeHints(runtimeHints);
|
||||
assertThat(runtimeHints.reflection().getTypeHint(InitDestroyBean.class)).satisfies(hint ->
|
||||
assertThat(hint.methods()).anySatisfy(invokeMethodHint("customInitMethod"))
|
||||
.anySatisfy(invokeMethodHint("initMethod")).hasSize(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerRuntimeHintsWithDestroyMethodNames() {
|
||||
RootBeanDefinition bd = new RootBeanDefinition(InitDestroyBean.class);
|
||||
bd.setDestroyMethodNames("customDestroyMethod", "destroyMethod");
|
||||
RuntimeHints runtimeHints = new RuntimeHints();
|
||||
getDefaultContribution(new DefaultListableBeanFactory(), bd).registerRuntimeHints(runtimeHints);
|
||||
assertThat(runtimeHints.reflection().getTypeHint(InitDestroyBean.class)).satisfies(hint ->
|
||||
assertThat(hint.methods()).anySatisfy(invokeMethodHint("customDestroyMethod"))
|
||||
.anySatisfy(invokeMethodHint("destroyMethod")).hasSize(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerRuntimeHintsWithNoPropertyValuesDoesNotAccessRuntimeHints() {
|
||||
RootBeanDefinition bd = new RootBeanDefinition(String.class);
|
||||
RuntimeHints runtimeHints = mock(RuntimeHints.class);
|
||||
getDefaultContribution(new DefaultListableBeanFactory(), bd).registerRuntimeHints(runtimeHints);
|
||||
verifyNoInteractions(runtimeHints);
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerRuntimeHintsWithInvalidProperty() {
|
||||
BeanDefinition bd = BeanDefinitionBuilder.rootBeanDefinition(ConfigurableBean.class)
|
||||
.addPropertyValue("notAProperty", "invalid").addPropertyValue("name", "hello")
|
||||
.getBeanDefinition();
|
||||
RuntimeHints runtimeHints = new RuntimeHints();
|
||||
getDefaultContribution(new DefaultListableBeanFactory(), bd).registerRuntimeHints(runtimeHints);
|
||||
assertThat(runtimeHints.reflection().getTypeHint(ConfigurableBean.class)).satisfies(hint -> {
|
||||
assertThat(hint.fields()).isEmpty();
|
||||
assertThat(hint.constructors()).isEmpty();
|
||||
assertThat(hint.methods()).singleElement().satisfies(methodHint -> {
|
||||
assertThat(methodHint.getName()).isEqualTo("setName");
|
||||
assertThat(methodHint.getParameterTypes()).containsExactly(TypeReference.of(String.class));
|
||||
assertThat(methodHint.getModes()).containsOnly(ExecutableMode.INVOKE);
|
||||
});
|
||||
assertThat(hint.getMemberCategories()).isEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerRuntimeHintsForPropertiesUseDeclaringClass() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerSingleton("environment", mock(Environment.class));
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(IntegerFactoryBean.class)
|
||||
.addConstructorArgReference("environment")
|
||||
.addPropertyValue("name", "Hello").getBeanDefinition();
|
||||
getDefaultContribution(beanFactory, beanDefinition).applyTo(this.initialization);
|
||||
ReflectionHints reflectionHints = this.initialization.generatedTypeContext().runtimeHints().reflection();
|
||||
assertThat(reflectionHints.typeHints()).anySatisfy(typeHint -> {
|
||||
assertThat(typeHint.getType()).isEqualTo(TypeReference.of(BaseFactoryBean.class));
|
||||
assertThat(typeHint.constructors()).isEmpty();
|
||||
assertThat(typeHint.methods()).singleElement()
|
||||
.satisfies(invokeMethodHint("setName", String.class));
|
||||
assertThat(typeHint.fields()).isEmpty();
|
||||
}).anySatisfy(typeHint -> {
|
||||
assertThat(typeHint.getType()).isEqualTo(TypeReference.of(IntegerFactoryBean.class));
|
||||
assertThat(typeHint.constructors()).singleElement()
|
||||
.satisfies(introspectConstructorHint(Environment.class));
|
||||
assertThat(typeHint.methods()).isEmpty();
|
||||
assertThat(typeHint.fields()).isEmpty();
|
||||
}).hasSize(2);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void registerRuntimeHintsForProperties() {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(NameAndCountersComponent.class)
|
||||
.addPropertyValue("name", "Hello").addPropertyValue("counter", 42).getBeanDefinition();
|
||||
getDefaultContribution(new DefaultListableBeanFactory(), beanDefinition).applyTo(this.initialization);
|
||||
ReflectionHints reflectionHints = this.initialization.generatedTypeContext().runtimeHints().reflection();
|
||||
assertThat(reflectionHints.typeHints()).singleElement().satisfies(typeHint -> {
|
||||
assertThat(typeHint.getType()).isEqualTo(TypeReference.of(NameAndCountersComponent.class));
|
||||
assertThat(typeHint.constructors()).isEmpty();
|
||||
assertThat(typeHint.methods()).anySatisfy(invokeMethodHint("setName", String.class))
|
||||
.anySatisfy(invokeMethodHint("setCounter", Integer.class)).hasSize(2);
|
||||
assertThat(typeHint.fields()).isEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void registerReflectionEntriesForInnerBeanDefinition() {
|
||||
AbstractBeanDefinition innerBd = BeanDefinitionBuilder.rootBeanDefinition(IntegerFactoryBean.class)
|
||||
.addPropertyValue("name", "test").getBeanDefinition();
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(NameAndCountersComponent.class)
|
||||
.addPropertyValue("counter", innerBd).getBeanDefinition();
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerSingleton("environment", Environment.class);
|
||||
getDefaultContribution(beanFactory, beanDefinition).applyTo(this.initialization);
|
||||
ReflectionHints reflectionHints = this.initialization.generatedTypeContext().runtimeHints().reflection();
|
||||
assertThat(reflectionHints.typeHints()).anySatisfy(typeHint -> {
|
||||
assertThat(typeHint.getType()).isEqualTo(TypeReference.of(NameAndCountersComponent.class));
|
||||
assertThat(typeHint.constructors()).isEmpty();
|
||||
assertThat(typeHint.methods()).singleElement().satisfies(invokeMethodHint("setCounter", Integer.class));
|
||||
assertThat(typeHint.fields()).isEmpty();
|
||||
}).anySatisfy(typeHint -> {
|
||||
assertThat(typeHint.getType()).isEqualTo(TypeReference.of(BaseFactoryBean.class));
|
||||
assertThat(typeHint.methods()).singleElement().satisfies(invokeMethodHint("setName", String.class));
|
||||
}).anySatisfy(typeHint -> {
|
||||
assertThat(typeHint.getType()).isEqualTo(TypeReference.of(IntegerFactoryBean.class));
|
||||
assertThat(typeHint.constructors()).singleElement().satisfies(introspectConstructorHint(Environment.class));
|
||||
}).hasSize(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerReflectionEntriesForListOfInnerBeanDefinition() {
|
||||
AbstractBeanDefinition innerBd1 = BeanDefinitionBuilder.rootBeanDefinition(IntegerFactoryBean.class)
|
||||
.addPropertyValue("name", "test").getBeanDefinition();
|
||||
AbstractBeanDefinition innerBd2 = BeanDefinitionBuilder.rootBeanDefinition(AnotherIntegerFactoryBean.class)
|
||||
.addPropertyValue("name", "test").getBeanDefinition();
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(NameAndCountersComponent.class)
|
||||
.addPropertyValue("counters", List.of(innerBd1, innerBd2)).getBeanDefinition();
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerSingleton("environment", Environment.class);
|
||||
getDefaultContribution(beanFactory, beanDefinition).applyTo(this.initialization);
|
||||
ReflectionHints reflectionHints = this.initialization.generatedTypeContext().runtimeHints().reflection();
|
||||
assertThat(reflectionHints.typeHints()).anySatisfy(typeHint -> {
|
||||
assertThat(typeHint.getType()).isEqualTo(TypeReference.of(NameAndCountersComponent.class));
|
||||
assertThat(typeHint.constructors()).isEmpty();
|
||||
assertThat(typeHint.methods()).singleElement().satisfies(invokeMethodHint("setCounters", List.class));
|
||||
assertThat(typeHint.fields()).isEmpty();
|
||||
}).anySatisfy(typeHint -> {
|
||||
assertThat(typeHint.getType()).isEqualTo(TypeReference.of(BaseFactoryBean.class));
|
||||
assertThat(typeHint.methods()).singleElement().satisfies(invokeMethodHint("setName", String.class));
|
||||
}).anySatisfy(typeHint -> {
|
||||
assertThat(typeHint.getType()).isEqualTo(TypeReference.of(IntegerFactoryBean.class));
|
||||
assertThat(typeHint.constructors()).singleElement().satisfies(introspectConstructorHint(Environment.class));
|
||||
}).anySatisfy(typeHint -> {
|
||||
assertThat(typeHint.getType()).isEqualTo(TypeReference.of(AnotherIntegerFactoryBean.class));
|
||||
assertThat(typeHint.constructors()).singleElement().satisfies(introspectConstructorHint(Environment.class));
|
||||
}).hasSize(4);
|
||||
}
|
||||
|
||||
private Consumer<ExecutableHint> invokeMethodHint(String name, Class<?>... parameterTypes) {
|
||||
return executableHint(ExecutableMode.INVOKE, name, parameterTypes);
|
||||
}
|
||||
|
||||
private Consumer<ExecutableHint> introspectConstructorHint(Class<?>... parameterTypes) {
|
||||
return executableHint(ExecutableMode.INTROSPECT, "<init>", parameterTypes);
|
||||
}
|
||||
|
||||
private Consumer<ExecutableHint> executableHint(ExecutableMode mode, String name, Class<?>... parameterTypes) {
|
||||
return executableHint -> {
|
||||
assertThat(executableHint.getName()).isEqualTo(name);
|
||||
assertThat(executableHint.getParameterTypes()).containsExactly(Arrays.stream(parameterTypes)
|
||||
.map(TypeReference::of).toArray(TypeReference[]::new));
|
||||
assertThat(executableHint.getModes()).containsExactly(mode);
|
||||
};
|
||||
}
|
||||
|
||||
private Consumer<DefaultListableBeanFactory> hasBeanDefinition(Consumer<RootBeanDefinition> bd) {
|
||||
return beanFactory -> {
|
||||
assertThat(beanFactory.getBeanDefinitionNames()).contains("test");
|
||||
RootBeanDefinition beanDefinition = (RootBeanDefinition) beanFactory.getMergedBeanDefinition("test");
|
||||
bd.accept(beanDefinition);
|
||||
};
|
||||
}
|
||||
|
||||
private BeanFactoryContribution simpleConfigurationRegistration(Consumer<RootBeanDefinition> bd) {
|
||||
RootBeanDefinition beanDefinition = (RootBeanDefinition) BeanDefinitionBuilder
|
||||
.rootBeanDefinition(SimpleConfiguration.class).getBeanDefinition();
|
||||
bd.accept(beanDefinition);
|
||||
return getDefaultContribution(new DefaultListableBeanFactory(), beanDefinition);
|
||||
}
|
||||
|
||||
private BeanRegistrationBeanFactoryContribution getDefaultContribution(DefaultListableBeanFactory beanFactory, BeanDefinition beanDefinition) {
|
||||
BeanRegistrationBeanFactoryContribution contribution = new DefaultBeanRegistrationContributionProvider(beanFactory)
|
||||
.getContributionFor("test", (RootBeanDefinition) beanDefinition);
|
||||
assertThat(contribution).isNotNull();
|
||||
return contribution;
|
||||
}
|
||||
|
||||
private BeanRegistrationBeanFactoryContribution getContributionFor(BeanDefinition beanDefinition, Executable instanceCreator) {
|
||||
return new BeanRegistrationBeanFactoryContribution("test", (RootBeanDefinition) beanDefinition,
|
||||
new DefaultBeanInstantiationGenerator(instanceCreator, Collections.emptyList()));
|
||||
}
|
||||
|
||||
private CodeSnippet beanRegistration(BeanDefinition beanDefinition, Executable instanceCreator, Consumer<Builder> instanceSupplier) {
|
||||
BeanRegistrationBeanFactoryContribution generator = new BeanRegistrationBeanFactoryContribution(
|
||||
"test", (RootBeanDefinition) beanDefinition,
|
||||
new DefaultBeanInstantiationGenerator(instanceCreator, Collections.emptyList()));
|
||||
return CodeSnippet.of(generator.generateBeanRegistration(new RuntimeHints(),
|
||||
toMultiStatements(instanceSupplier)));
|
||||
}
|
||||
|
||||
private Constructor<?> singleConstructor(Class<?> type) {
|
||||
return type.getDeclaredConstructors()[0];
|
||||
}
|
||||
|
||||
private Method method(Class<?> type, String name, Class<?>... parameterTypes) {
|
||||
Method method = ReflectionUtils.findMethod(type, name, parameterTypes);
|
||||
assertThat(method).isNotNull();
|
||||
return method;
|
||||
}
|
||||
|
||||
private MultiStatement toMultiStatements(Consumer<Builder> instanceSupplier) {
|
||||
Builder code = CodeBlock.builder();
|
||||
instanceSupplier.accept(code);
|
||||
MultiStatement statements = new MultiStatement();
|
||||
statements.add(code.build());
|
||||
return statements;
|
||||
}
|
||||
|
||||
private String codeOf(GeneratedType type) {
|
||||
try {
|
||||
StringWriter out = new StringWriter();
|
||||
type.toJavaFile().writeTo(out);
|
||||
return out.toString();
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private String removeIndent(String content, int indent) {
|
||||
return content.lines().map(line -> {
|
||||
for (int i = 0; i < indent; i++) {
|
||||
if (line.startsWith("\t")) {
|
||||
line = line.substring(1);
|
||||
}
|
||||
}
|
||||
return line;
|
||||
}).collect(Collectors.joining("\n"));
|
||||
}
|
||||
|
||||
private void compile(BeanFactoryContribution contribution, Consumer<DefaultListableBeanFactory> beanFactory) {
|
||||
contribution.applyTo(this.initialization);
|
||||
GeneratedType generatedType = this.generatedTypeContext.getMainGeneratedType();
|
||||
generatedType.customizeType(type -> {
|
||||
type.addModifiers(Modifier.PUBLIC);
|
||||
type.addSuperinterface(BeanFactoryInitializer.class);
|
||||
});
|
||||
generatedType.addMethod(MethodSpec.methodBuilder("initializeBeanFactory")
|
||||
.addModifiers(Modifier.PUBLIC).addAnnotation(Override.class)
|
||||
.addParameter(DefaultListableBeanFactory.class, "beanFactory")
|
||||
.addCode(this.initialization.toCodeBlock()));
|
||||
SourceFiles sourceFiles = SourceFiles.none();
|
||||
for (JavaFile javaFile : this.generatedTypeContext.toJavaFiles()) {
|
||||
sourceFiles = sourceFiles.and(SourceFile.of((javaFile::writeTo)));
|
||||
}
|
||||
TestCompiler.forSystem().withSources(sourceFiles).compile(compiled -> {
|
||||
BeanFactoryInitializer initializer = compiled.getInstance(BeanFactoryInitializer.class,
|
||||
generatedType.getClassName().canonicalName());
|
||||
DefaultListableBeanFactory freshBeanFactory = new DefaultListableBeanFactory();
|
||||
initializer.initializeBeanFactory(freshBeanFactory);
|
||||
beanFactory.accept(freshBeanFactory);
|
||||
});
|
||||
}
|
||||
|
||||
static abstract class BaseFactoryBean {
|
||||
|
||||
public void setName(String name) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
static class IntegerFactoryBean extends BaseFactoryBean implements FactoryBean<Integer> {
|
||||
|
||||
public IntegerFactoryBean(Environment environment) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return Integer.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getObject() {
|
||||
return 42;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
static class AnotherIntegerFactoryBean extends IntegerFactoryBean {
|
||||
|
||||
public AnotherIntegerFactoryBean(Environment environment) {
|
||||
super(environment);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class NameAndCountersComponent {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private String name;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private List<Integer> counters;
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setCounter(Integer counter) {
|
||||
setCounters(List.of(counter));
|
||||
}
|
||||
|
||||
public void setCounters(List<Integer> counters) {
|
||||
this.counters = counters;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,255 +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.generator;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Executable;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aot.generator.CodeContribution;
|
||||
import org.springframework.aot.hint.ExecutableHint;
|
||||
import org.springframework.aot.hint.ExecutableMode;
|
||||
import org.springframework.aot.hint.MemberCategory;
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.TypeHint;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
import org.springframework.beans.testfixture.beans.TestBean;
|
||||
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.factory.NumberHolderFactoryBean;
|
||||
import org.springframework.beans.testfixture.beans.factory.generator.factory.SampleFactory;
|
||||
import org.springframework.beans.testfixture.beans.factory.generator.injection.InjectionComponent;
|
||||
import org.springframework.beans.testfixture.beans.factory.generator.visibility.ProtectedConstructorComponent;
|
||||
import org.springframework.beans.testfixture.beans.factory.generator.visibility.ProtectedFactoryMethod;
|
||||
import org.springframework.javapoet.CodeBlock;
|
||||
import org.springframework.javapoet.support.CodeSnippet;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link DefaultBeanInstantiationGenerator}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class DefaultBeanInstantiationGeneratorTests {
|
||||
|
||||
@Test
|
||||
void generateUsingDefaultConstructorUsesMethodReference() {
|
||||
CodeContribution contribution = generate(SimpleConfiguration.class.getDeclaredConstructors()[0]);
|
||||
assertThat(code(contribution)).isEqualTo("SimpleConfiguration::new");
|
||||
assertThat(reflectionHints(contribution, SimpleConfiguration.class)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateUsingConstructorWithoutParameterAndMultipleCandidatesDoesNotUseMethodReference() throws NoSuchMethodException {
|
||||
CodeContribution contribution = generate(TestBean.class.getConstructor());
|
||||
assertThat(code(contribution)).isEqualTo("() -> new TestBean()");
|
||||
assertThat(reflectionHints(contribution, TestBean.class)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateUsingConstructorWithParameter() {
|
||||
Constructor<?> constructor = InjectionComponent.class.getDeclaredConstructors()[0];
|
||||
CodeContribution contribution = generate(constructor);
|
||||
assertThat(code(contribution).lines()).containsOnly(
|
||||
"(instanceContext) -> instanceContext.create(beanFactory, (attributes) -> "
|
||||
+ "new InjectionComponent(attributes.get(0)))");
|
||||
assertThat(reflectionHints(contribution, InjectionComponent.class))
|
||||
.satisfies(hasSingleQueryConstructor(constructor));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateUsingConstructorWithInnerClassAndNoExtraArg() {
|
||||
CodeContribution contribution = generate(NoDependencyComponent.class.getDeclaredConstructors()[0]);
|
||||
assertThat(code(contribution).lines()).containsOnly(
|
||||
"() -> beanFactory.getBean(InnerComponentConfiguration.class).new NoDependencyComponent()");
|
||||
assertThat(reflectionHints(contribution, NoDependencyComponent.class)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateUsingConstructorWithInnerClassAndExtraArg() {
|
||||
Constructor<?> constructor = EnvironmentAwareComponent.class.getDeclaredConstructors()[0];
|
||||
CodeContribution contribution = generate(constructor);
|
||||
assertThat(code(contribution).lines()).containsOnly(
|
||||
"(instanceContext) -> instanceContext.create(beanFactory, (attributes) -> "
|
||||
+ "beanFactory.getBean(InnerComponentConfiguration.class).new EnvironmentAwareComponent(attributes.get(1)))");
|
||||
assertThat(reflectionHints(contribution, EnvironmentAwareComponent.class))
|
||||
.satisfies(hasSingleQueryConstructor(constructor));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateUsingConstructorOfTypeWithGeneric() {
|
||||
CodeContribution contribution = generate(NumberHolderFactoryBean.class.getDeclaredConstructors()[0]);
|
||||
assertThat(code(contribution)).isEqualTo("NumberHolderFactoryBean::new");
|
||||
assertThat(reflectionHints(contribution, NumberHolderFactoryBean.class)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateUsingNoArgConstructorAndContributionsDoesNotUseMethodReference() {
|
||||
CodeContribution contribution = generate(SimpleConfiguration.class.getDeclaredConstructors()[0],
|
||||
contrib -> contrib.statements().add(CodeBlock.of("// hello\n")),
|
||||
contrib -> {});
|
||||
assertThat(code(contribution)).isEqualTo("""
|
||||
(instanceContext) -> {
|
||||
SimpleConfiguration bean = new SimpleConfiguration();
|
||||
// hello
|
||||
return bean;
|
||||
}""");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateUsingContributionsRegisterHints() {
|
||||
CodeContribution contribution = generate(SimpleConfiguration.class.getDeclaredConstructors()[0],
|
||||
contrib -> {
|
||||
contrib.statements().add(CodeBlock.of("// hello\n"));
|
||||
contrib.runtimeHints().resources().registerPattern("com/example/*.properties");
|
||||
},
|
||||
contrib -> contrib.runtimeHints().reflection().registerType(TypeReference.of(String.class),
|
||||
hint -> hint.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS)));
|
||||
assertThat(code(contribution)).isEqualTo("""
|
||||
(instanceContext) -> {
|
||||
SimpleConfiguration bean = new SimpleConfiguration();
|
||||
// hello
|
||||
return bean;
|
||||
}""");
|
||||
assertThat(contribution.runtimeHints().resources().resourcePatterns()).singleElement().satisfies(hint ->
|
||||
assertThat(hint.getIncludes()).containsOnly("com/example/*.properties"));
|
||||
assertThat(contribution.runtimeHints().reflection().getTypeHint(String.class)).satisfies(hint -> {
|
||||
assertThat(hint.getType()).isEqualTo(TypeReference.of(String.class));
|
||||
assertThat(hint.getMemberCategories()).containsOnly(MemberCategory.INVOKE_PUBLIC_METHODS);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateUsingMethodWithNoArg() {
|
||||
Method method = method(SimpleConfiguration.class, "stringBean");
|
||||
CodeContribution contribution = generate(method);
|
||||
assertThat(code(contribution)).isEqualTo("() -> beanFactory.getBean(SimpleConfiguration.class).stringBean()");
|
||||
assertThat(reflectionHints(contribution, SimpleConfiguration.class))
|
||||
.satisfies(hasSingleQueryMethod(method));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateUsingStaticMethodWithNoArg() {
|
||||
Method method = method(SampleFactory.class, "integerBean");
|
||||
CodeContribution contribution = generate(method);
|
||||
assertThat(code(contribution)).isEqualTo("() -> SampleFactory.integerBean()");
|
||||
assertThat(reflectionHints(contribution, SampleFactory.class))
|
||||
.satisfies(hasSingleQueryMethod(method));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateUsingMethodWithArg() {
|
||||
Method method = method(SampleFactory.class, "create", Number.class, String.class);
|
||||
CodeContribution contribution = generate(method);
|
||||
assertThat(code(contribution)).isEqualTo("(instanceContext) -> instanceContext.create(beanFactory, (attributes) -> "
|
||||
+ "SampleFactory.create(attributes.get(0), attributes.get(1)))");
|
||||
assertThat(reflectionHints(contribution, SampleFactory.class))
|
||||
.satisfies(hasSingleQueryMethod(method));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateUsingMethodAndContributions() {
|
||||
CodeContribution contribution = generate(method(SimpleConfiguration.class, "stringBean"),
|
||||
contrib -> {
|
||||
contrib.statements().add(CodeBlock.of("// hello\n"));
|
||||
contrib.runtimeHints().resources().registerPattern("com/example/*.properties");
|
||||
},
|
||||
contrib -> contrib.runtimeHints().reflection().registerType(TypeReference.of(String.class),
|
||||
hint -> hint.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS)));
|
||||
assertThat(code(contribution)).isEqualTo("""
|
||||
(instanceContext) -> {
|
||||
String bean = beanFactory.getBean(SimpleConfiguration.class).stringBean();
|
||||
// hello
|
||||
return bean;
|
||||
}""");
|
||||
assertThat(contribution.runtimeHints().resources().resourcePatterns()).singleElement().satisfies(hint ->
|
||||
assertThat(hint.getIncludes()).containsOnly("com/example/*.properties"));
|
||||
assertThat(contribution.runtimeHints().reflection().getTypeHint(String.class)).satisfies(hint -> {
|
||||
assertThat(hint.getType()).isEqualTo(TypeReference.of(String.class));
|
||||
assertThat(hint.getMemberCategories()).containsOnly(MemberCategory.INVOKE_PUBLIC_METHODS);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateUsingProtectedConstructorRegistersProtectedAccess() {
|
||||
CodeContribution contribution = generate(ProtectedConstructorComponent.class.getDeclaredConstructors()[0]);
|
||||
assertThat(contribution.protectedAccess().isAccessible("com.example")).isFalse();
|
||||
assertThat(contribution.protectedAccess().getPrivilegedPackageName("com.example"))
|
||||
.isEqualTo(ProtectedConstructorComponent.class.getPackageName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateUsingProtectedMethodRegistersProtectedAccess() {
|
||||
CodeContribution contribution = generate(method(ProtectedFactoryMethod.class, "testBean", Integer.class));
|
||||
assertThat(contribution.protectedAccess().isAccessible("com.example")).isFalse();
|
||||
assertThat(contribution.protectedAccess().getPrivilegedPackageName("com.example"))
|
||||
.isEqualTo(ProtectedFactoryMethod.class.getPackageName());
|
||||
}
|
||||
|
||||
private String code(CodeContribution contribution) {
|
||||
return CodeSnippet.process(contribution.statements().toLambdaBody());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private TypeHint reflectionHints(CodeContribution contribution, Class<?> type) {
|
||||
return contribution.runtimeHints().reflection().getTypeHint(type);
|
||||
}
|
||||
|
||||
private Consumer<TypeHint> hasSingleQueryConstructor(Constructor<?> constructor) {
|
||||
return typeHint -> assertThat(typeHint.constructors()).singleElement()
|
||||
.satisfies(match(constructor, "<init>", ExecutableMode.INTROSPECT));
|
||||
}
|
||||
|
||||
private Consumer<TypeHint> hasSingleQueryMethod(Method method) {
|
||||
return typeHint -> assertThat(typeHint.methods()).singleElement()
|
||||
.satisfies(match(method, method.getName(), ExecutableMode.INTROSPECT));
|
||||
}
|
||||
|
||||
private Consumer<ExecutableHint> match(Executable executable, String name, ExecutableMode... modes) {
|
||||
return hint -> {
|
||||
assertThat(hint.getName()).isEqualTo(name);
|
||||
assertThat(hint.getParameterTypes()).hasSameSizeAs(executable.getParameterTypes());
|
||||
for (int i = 0; i < hint.getParameterTypes().size(); i++) {
|
||||
assertThat(hint.getParameterTypes().get(i))
|
||||
.isEqualTo(TypeReference.of(executable.getParameterTypes()[i]));
|
||||
}
|
||||
assertThat(hint.getModes()).containsOnly(modes);
|
||||
};
|
||||
}
|
||||
|
||||
private CodeContribution generate(Executable executable,
|
||||
BeanInstantiationContribution... beanInstantiationContributions) {
|
||||
DefaultBeanInstantiationGenerator generator = new DefaultBeanInstantiationGenerator(executable,
|
||||
Arrays.asList(beanInstantiationContributions));
|
||||
return generator.generateBeanInstantiation(new RuntimeHints());
|
||||
}
|
||||
|
||||
private static Method method(Class<?> type, String methodName, Class<?>... parameterTypes) {
|
||||
Method method = ReflectionUtils.findMethod(type, methodName, parameterTypes);
|
||||
assertThat(method).isNotNull();
|
||||
return method;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,67 +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.generator;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.testfixture.beans.factory.generator.SimpleConfiguration;
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
/**
|
||||
* Tests for {@link DefaultBeanRegistrationContributionProvider}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class DefaultBeanRegistrationContributionProviderTests {
|
||||
|
||||
@Test
|
||||
void aotContributingBeanPostProcessorsAreIncluded() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
AotContributingBeanPostProcessor first = mockNoOpPostProcessor(-1);
|
||||
AotContributingBeanPostProcessor second = mockNoOpPostProcessor(5);
|
||||
beanFactory.registerBeanDefinition("second", BeanDefinitionBuilder.rootBeanDefinition(
|
||||
AotContributingBeanPostProcessor.class, () -> second).getBeanDefinition());
|
||||
beanFactory.registerBeanDefinition("first", BeanDefinitionBuilder.rootBeanDefinition(
|
||||
AotContributingBeanPostProcessor.class, () -> first).getBeanDefinition());
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(SimpleConfiguration.class);
|
||||
new DefaultBeanRegistrationContributionProvider(beanFactory).getContributionFor(
|
||||
"test", beanDefinition);
|
||||
verify((Ordered) second).getOrder();
|
||||
verify((Ordered) first).getOrder();
|
||||
verify(first).contribute(beanDefinition, SimpleConfiguration.class, "test");
|
||||
verify(second).contribute(beanDefinition, SimpleConfiguration.class, "test");
|
||||
verifyNoMoreInteractions(first, second);
|
||||
}
|
||||
|
||||
|
||||
private AotContributingBeanPostProcessor mockNoOpPostProcessor(int order) {
|
||||
AotContributingBeanPostProcessor postProcessor = mock(AotContributingBeanPostProcessor.class);
|
||||
given(postProcessor.contribute(any(), any(), any())).willReturn(null);
|
||||
given(postProcessor.getOrder()).willReturn(order);
|
||||
return postProcessor;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,336 +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.generator;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Executable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aot.generator.ProtectedAccess;
|
||||
import org.springframework.aot.generator.ProtectedAccess.Options;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.generator.InjectionGeneratorTests.SimpleConstructorBean.InnerClass;
|
||||
import org.springframework.beans.testfixture.beans.factory.generator.factory.SampleFactory;
|
||||
import org.springframework.javapoet.support.CodeSnippet;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link InjectionGenerator}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class InjectionGeneratorTests {
|
||||
|
||||
private final ProtectedAccess protectedAccess = new ProtectedAccess();
|
||||
|
||||
@Test
|
||||
void generateInstantiationForConstructorWithNoArgUseShortcut() {
|
||||
Constructor<?> constructor = SimpleBean.class.getDeclaredConstructors()[0];
|
||||
assertThat(generateInstantiation(constructor).lines())
|
||||
.containsExactly("new InjectionGeneratorTests.SimpleBean()");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateInstantiationForConstructorWithNonGenericParameter() {
|
||||
Constructor<?> constructor = SimpleConstructorBean.class.getDeclaredConstructors()[0];
|
||||
assertThat(generateInstantiation(constructor).lines()).containsExactly(
|
||||
"instanceContext.create(beanFactory, (attributes) -> new InjectionGeneratorTests.SimpleConstructorBean(attributes.get(0), attributes.get(1)))");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateInstantiationForConstructorWithGenericParameter() {
|
||||
Constructor<?> constructor = GenericConstructorBean.class.getDeclaredConstructors()[0];
|
||||
assertThat(generateInstantiation(constructor).lines()).containsExactly(
|
||||
"instanceContext.create(beanFactory, (attributes) -> new InjectionGeneratorTests.GenericConstructorBean(attributes.get(0)))");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateInstantiationForAmbiguousConstructor() throws Exception {
|
||||
Constructor<?> constructor = AmbiguousConstructorBean.class.getDeclaredConstructor(String.class, Number.class);
|
||||
assertThat(generateInstantiation(constructor).lines()).containsExactly(
|
||||
"instanceContext.create(beanFactory, (attributes) -> new InjectionGeneratorTests.AmbiguousConstructorBean(attributes.get(0, String.class), attributes.get(1, Number.class)))");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateInstantiationForConstructorInInnerClass() {
|
||||
Constructor<?> constructor = InnerClass.class.getDeclaredConstructors()[0];
|
||||
assertThat(generateInstantiation(constructor).lines()).containsExactly(
|
||||
"beanFactory.getBean(InjectionGeneratorTests.SimpleConstructorBean.class).new InnerClass()");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateInstantiationForMethodWithNoArgUseShortcut() {
|
||||
assertThat(generateInstantiation(method(SimpleBean.class, "name")).lines()).containsExactly(
|
||||
"beanFactory.getBean(InjectionGeneratorTests.SimpleBean.class).name()");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateInstantiationForStaticMethodWithNoArgUseShortcut() {
|
||||
assertThat(generateInstantiation(method(SimpleBean.class, "number")).lines()).containsExactly(
|
||||
"InjectionGeneratorTests.SimpleBean.number()");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateInstantiationForMethodWithNonGenericParameter() {
|
||||
assertThat(generateInstantiation(method(SampleBean.class, "source", Integer.class)).lines()).containsExactly(
|
||||
"instanceContext.create(beanFactory, (attributes) -> beanFactory.getBean(InjectionGeneratorTests.SampleBean.class).source(attributes.get(0)))");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateInstantiationForStaticMethodWithNonGenericParameter() {
|
||||
assertThat(generateInstantiation(method(SampleBean.class, "staticSource", Integer.class)).lines()).containsExactly(
|
||||
"instanceContext.create(beanFactory, (attributes) -> InjectionGeneratorTests.SampleBean.staticSource(attributes.get(0)))");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateInstantiationForMethodWithGenericParameters() {
|
||||
assertThat(generateInstantiation(method(SampleBean.class, "sourceWithProvider", ObjectProvider.class)).lines()).containsExactly(
|
||||
"instanceContext.create(beanFactory, (attributes) -> beanFactory.getBean(InjectionGeneratorTests.SampleBean.class).sourceWithProvider(attributes.get(0)))");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateInstantiationForAmbiguousMethod() {
|
||||
assertThat(generateInstantiation(method(SampleFactory.class, "create", String.class)).lines()).containsExactly(
|
||||
"instanceContext.create(beanFactory, (attributes) -> SampleFactory.create(attributes.get(0, String.class)))");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateInjectionForUnsupportedMember() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> generateInjection(mock(Member.class), false));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateInjectionForNonRequiredMethodWithNonGenericParameters() {
|
||||
Method method = method(SampleBean.class, "sourceAndCounter", String.class, Integer.class);
|
||||
assertThat(generateInjection(method, false)).isEqualTo("""
|
||||
instanceContext.method("sourceAndCounter", String.class, Integer.class)
|
||||
.resolve(beanFactory, false).ifResolved((attributes) -> bean.sourceAndCounter(attributes.get(0), attributes.get(1)))""");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateInjectionForRequiredMethodWithGenericParameter() {
|
||||
Method method = method(SampleBean.class, "nameAndCounter", String.class, ObjectProvider.class);
|
||||
assertThat(generateInjection(method, true)).isEqualTo("""
|
||||
instanceContext.method("nameAndCounter", String.class, ObjectProvider.class)
|
||||
.invoke(beanFactory, (attributes) -> bean.nameAndCounter(attributes.get(0), attributes.get(1)))""");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateInjectionForNonRequiredMethodWithGenericParameter() {
|
||||
Method method = method(SampleBean.class, "nameAndCounter", String.class, ObjectProvider.class);
|
||||
assertThat(generateInjection(method, false)).isEqualTo("""
|
||||
instanceContext.method("nameAndCounter", String.class, ObjectProvider.class)
|
||||
.resolve(beanFactory, false).ifResolved((attributes) -> bean.nameAndCounter(attributes.get(0), attributes.get(1)))""");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateInjectionForRequiredField() {
|
||||
Field field = field(SampleBean.class, "counter");
|
||||
assertThat(generateInjection(field, true)).isEqualTo("""
|
||||
instanceContext.field("counter")
|
||||
.invoke(beanFactory, (attributes) -> bean.counter = attributes.get(0))""");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateInjectionForNonRequiredField() {
|
||||
Field field = field(SampleBean.class, "counter");
|
||||
assertThat(generateInjection(field, false)).isEqualTo("""
|
||||
instanceContext.field("counter")
|
||||
.resolve(beanFactory, false).ifResolved((attributes) -> bean.counter = attributes.get(0))""");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateInjectionForRequiredPrivateField() {
|
||||
Field field = field(SampleBean.class, "source");
|
||||
assertThat(generateInjection(field, true)).isEqualTo("""
|
||||
instanceContext.field("source")
|
||||
.invoke(beanFactory, (attributes) -> {
|
||||
Field sourceField = ReflectionUtils.findField(InjectionGeneratorTests.SampleBean.class, "source");
|
||||
ReflectionUtils.makeAccessible(sourceField);
|
||||
ReflectionUtils.setField(sourceField, bean, attributes.get(0));
|
||||
})""");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getProtectedAccessInjectionOptionsForUnsupportedMember() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
getProtectedAccessInjectionOptions(mock(Member.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getProtectedAccessInjectionOptionsForPackagePublicField() {
|
||||
analyzeProtectedAccess(field(SampleBean.class, "enabled"));
|
||||
assertThat(this.protectedAccess.isAccessible("com.example")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getProtectedAccessInjectionOptionsForPackageProtectedField() {
|
||||
analyzeProtectedAccess(field(SampleBean.class, "counter"));
|
||||
assertPrivilegedAccess(SampleBean.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getProtectedAccessInjectionOptionsForPrivateField() {
|
||||
analyzeProtectedAccess(field(SampleBean.class, "source"));
|
||||
assertThat(this.protectedAccess.isAccessible("com.example")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getProtectedAccessInjectionOptionsForPublicMethod() {
|
||||
analyzeProtectedAccess(method(SampleBean.class, "setEnabled", Boolean.class));
|
||||
assertThat(this.protectedAccess.isAccessible("com.example")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getProtectedAccessInjectionOptionsForPackageProtectedMethod() {
|
||||
analyzeProtectedAccess(method(SampleBean.class, "sourceAndCounter", String.class, Integer.class));
|
||||
assertPrivilegedAccess(SampleBean.class);
|
||||
}
|
||||
|
||||
|
||||
private Method method(Class<?> type, String name, Class<?>... parameterTypes) {
|
||||
Method method = ReflectionUtils.findMethod(type, name, parameterTypes);
|
||||
assertThat(method).isNotNull();
|
||||
return method;
|
||||
}
|
||||
|
||||
private Field field(Class<?> type, String name) {
|
||||
Field field = ReflectionUtils.findField(type, name);
|
||||
assertThat(field).isNotNull();
|
||||
return field;
|
||||
}
|
||||
|
||||
private String generateInstantiation(Executable creator) {
|
||||
return CodeSnippet.process(code -> code.add(new InjectionGenerator().generateInstantiation(creator)));
|
||||
}
|
||||
|
||||
private String generateInjection(Member member, boolean required) {
|
||||
return CodeSnippet.process(code -> code.add(new InjectionGenerator().generateInjection(member, required)));
|
||||
}
|
||||
|
||||
private void analyzeProtectedAccess(Member member) {
|
||||
this.protectedAccess.analyze(member, getProtectedAccessInjectionOptions(member));
|
||||
}
|
||||
|
||||
private Options getProtectedAccessInjectionOptions(Member member) {
|
||||
return new InjectionGenerator().getProtectedAccessInjectionOptions(member);
|
||||
}
|
||||
|
||||
private void assertPrivilegedAccess(Class<?> target) {
|
||||
assertThat(this.protectedAccess.isAccessible("com.example")).isFalse();
|
||||
assertThat(this.protectedAccess.getPrivilegedPackageName("com.example")).isEqualTo(target.getPackageName());
|
||||
assertThat(this.protectedAccess.isAccessible(target.getPackageName())).isTrue();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static class SampleBean {
|
||||
|
||||
public Boolean enabled;
|
||||
|
||||
private String source;
|
||||
|
||||
Integer counter;
|
||||
|
||||
|
||||
public void setEnabled(Boolean enabled) {
|
||||
|
||||
}
|
||||
|
||||
void sourceAndCounter(String source, Integer counter) {
|
||||
|
||||
}
|
||||
|
||||
void nameAndCounter(String name, ObjectProvider<Integer> counter) {
|
||||
|
||||
}
|
||||
|
||||
String source(Integer counter) {
|
||||
return "source" + counter;
|
||||
}
|
||||
|
||||
String sourceWithProvider(ObjectProvider<Integer> counter) {
|
||||
return "source" + counter.getIfAvailable(() -> 0);
|
||||
}
|
||||
|
||||
static String staticSource(Integer counter) {
|
||||
return counter + "source";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
static class SimpleBean {
|
||||
|
||||
String name() {
|
||||
return "test";
|
||||
}
|
||||
|
||||
static Integer number() {
|
||||
return 42;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
static class SimpleConstructorBean {
|
||||
|
||||
private final String source;
|
||||
|
||||
private final Integer counter;
|
||||
|
||||
public SimpleConstructorBean(String source, Integer counter) {
|
||||
this.source = source;
|
||||
this.counter = counter;
|
||||
}
|
||||
|
||||
class InnerClass {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
static class GenericConstructorBean {
|
||||
|
||||
private final ObjectProvider<Integer> counter;
|
||||
|
||||
GenericConstructorBean(ObjectProvider<Integer> counter) {
|
||||
this.counter = counter;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class AmbiguousConstructorBean {
|
||||
|
||||
AmbiguousConstructorBean(String first, String second) {
|
||||
|
||||
}
|
||||
|
||||
AmbiguousConstructorBean(String first, Number second) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,463 +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.generator.config;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InOrder;
|
||||
|
||||
import org.springframework.beans.FatalBeanException;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.beans.factory.generator.config.BeanDefinitionRegistrar.BeanInstanceContext;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
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.assertThatThrownBy;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link BeanDefinitionRegistrar}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class BeanDefinitionRegistrarTests {
|
||||
|
||||
@Test
|
||||
void beanDefinitionWithBeanClassDoesNotSetTargetType() {
|
||||
RootBeanDefinition beanDefinition = BeanDefinitionRegistrar.of("test", String.class).toBeanDefinition();
|
||||
assertThat(beanDefinition.getBeanClass()).isEqualTo(String.class);
|
||||
assertThat(beanDefinition.getTargetType()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void beanDefinitionWithResolvableTypeSetsTargetType() {
|
||||
ResolvableType targetType = ResolvableType.forClassWithGenerics(NumberHolder.class, Integer.class);
|
||||
RootBeanDefinition beanDefinition = BeanDefinitionRegistrar.of("test", targetType).toBeanDefinition();
|
||||
assertThat(beanDefinition.getTargetType()).isNotNull().isEqualTo(NumberHolder.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerWithSimpleInstanceSupplier() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
BeanDefinitionRegistrar.of("test", InjectionSample.class)
|
||||
.instanceSupplier(InjectionSample::new).register(beanFactory);
|
||||
assertBeanFactory(beanFactory, () -> {
|
||||
assertThat(beanFactory.containsBean("test")).isTrue();
|
||||
assertThat(beanFactory.getBean(InjectionSample.class)).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerWithSimpleInstanceSupplierThatThrowsRuntimeException() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
Exception exception = new IllegalArgumentException("test exception");
|
||||
BeanDefinitionRegistrar.of("testBean", InjectionSample.class)
|
||||
.instanceSupplier(() -> {
|
||||
throw exception;
|
||||
}).register(beanFactory);
|
||||
assertThatThrownBy(() -> beanFactory.getBean("testBean")).isInstanceOf(BeanCreationException.class)
|
||||
.getRootCause().isEqualTo(exception);
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerWithSimpleInstanceSupplierThatThrowsCheckedException() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
Exception exception = new IOException("test exception");
|
||||
BeanDefinitionRegistrar.of("testBean", InjectionSample.class)
|
||||
.instanceSupplier(() -> {
|
||||
throw exception;
|
||||
}).register(beanFactory);
|
||||
assertThatThrownBy(() -> beanFactory.getBean("testBean")).isInstanceOf(BeanCreationException.class)
|
||||
.getRootCause().isEqualTo(exception);
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerWithoutBeanNameFails() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
BeanDefinitionRegistrar registrar = BeanDefinitionRegistrar.inner(InjectionSample.class)
|
||||
.instanceSupplier(InjectionSample::new);
|
||||
assertThatIllegalStateException().isThrownBy(() -> registrar.register(beanFactory))
|
||||
.withMessageContaining("Bean name not set.");
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void registerWithCustomizer() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
BeanDefinitionRegistrar.ThrowableConsumer<RootBeanDefinition> first = mock(BeanDefinitionRegistrar.ThrowableConsumer.class);
|
||||
BeanDefinitionRegistrar.ThrowableConsumer<RootBeanDefinition> second = mock(BeanDefinitionRegistrar.ThrowableConsumer.class);
|
||||
BeanDefinitionRegistrar.of("test", InjectionSample.class)
|
||||
.instanceSupplier(InjectionSample::new).customize(first).customize(second).register(beanFactory);
|
||||
assertBeanFactory(beanFactory, () -> {
|
||||
assertThat(beanFactory.containsBean("test")).isTrue();
|
||||
InOrder ordered = inOrder(first, second);
|
||||
ordered.verify(first).accept(any(RootBeanDefinition.class));
|
||||
ordered.verify(second).accept(any(RootBeanDefinition.class));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerWithCustomizerThatThrowsRuntimeException() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
Exception exception = new RuntimeException("test exception");
|
||||
BeanDefinitionRegistrar registrar = BeanDefinitionRegistrar.of("test", InjectionSample.class)
|
||||
.instanceSupplier(InjectionSample::new).customize(bd -> {
|
||||
throw exception;
|
||||
});
|
||||
assertThatThrownBy(() -> registrar.register(beanFactory)).isInstanceOf(FatalBeanException.class)
|
||||
.hasMessageContaining("Failed to create bean definition for bean with name 'test'")
|
||||
.hasMessageContaining("test exception")
|
||||
.hasCause(exception);
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerWithCustomizerThatThrowsCheckedException() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
Exception exception = new IOException("test exception");
|
||||
BeanDefinitionRegistrar registrar = BeanDefinitionRegistrar.of("test", InjectionSample.class)
|
||||
.instanceSupplier(InjectionSample::new).customize(bd -> {
|
||||
throw exception;
|
||||
});
|
||||
assertThatThrownBy(() -> registrar.register(beanFactory)).isInstanceOf(FatalBeanException.class)
|
||||
.hasMessageContaining("Failed to create bean definition for bean with name 'test'")
|
||||
.hasMessageContaining("test exception");
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerWithConstructorInstantiation() {
|
||||
ResourceLoader resourceLoader = new DefaultResourceLoader();
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerResolvableDependency(ResourceLoader.class, resourceLoader);
|
||||
BeanDefinitionRegistrar.of("test", ConstructorSample.class).withConstructor(ResourceLoader.class)
|
||||
.instanceSupplier(instanceContext -> instanceContext.create(beanFactory, attributes ->
|
||||
new ConstructorSample(attributes.get(0)))).register(beanFactory);
|
||||
assertBeanFactory(beanFactory, () -> {
|
||||
assertThat(beanFactory.containsBean("test")).isTrue();
|
||||
assertThat(beanFactory.getBean(ConstructorSample.class).resourceLoader).isEqualTo(resourceLoader);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerWithConstructorInstantiationThatThrowsRuntimeException() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
Exception exception = new RuntimeException("test exception");
|
||||
BeanDefinitionRegistrar.of("test", ConstructorSample.class).withConstructor(ResourceLoader.class)
|
||||
.instanceSupplier(instanceContext -> {
|
||||
throw exception;
|
||||
}).register(beanFactory);
|
||||
assertThatThrownBy(() -> beanFactory.getBean("test")).isInstanceOf(BeanCreationException.class)
|
||||
.getRootCause().isEqualTo(exception);
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerWithConstructorInstantiationThatThrowsCheckedException() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
Exception exception = new IOException("test exception");
|
||||
BeanDefinitionRegistrar.of("test", ConstructorSample.class).withConstructor(ResourceLoader.class)
|
||||
.instanceSupplier(instanceContext -> {
|
||||
throw exception;
|
||||
}).register(beanFactory);
|
||||
assertThatThrownBy(() -> beanFactory.getBean("test")).isInstanceOf(BeanCreationException.class)
|
||||
.getRootCause().isEqualTo(exception);
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerWithConstructorOnInnerClass() {
|
||||
Environment environment = mock(Environment.class);
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerSingleton("environment", environment);
|
||||
beanFactory.registerBeanDefinition("sample", BeanDefinitionBuilder.rootBeanDefinition(InnerClassSample.class).getBeanDefinition());
|
||||
BeanDefinitionRegistrar.of("test", InnerClassSample.Inner.class).withConstructor(InnerClassSample.class, Environment.class)
|
||||
.instanceSupplier(instanceContext -> instanceContext.create(beanFactory, attributes ->
|
||||
beanFactory.getBean(InnerClassSample.class).new Inner(attributes.get(1))))
|
||||
.register(beanFactory);
|
||||
assertBeanFactory(beanFactory, () -> {
|
||||
assertThat(beanFactory.containsBean("test")).isTrue();
|
||||
InnerClassSample.Inner bean = beanFactory.getBean(InnerClassSample.Inner.class);
|
||||
assertThat(bean.environment).isEqualTo(environment);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerWithInvalidConstructor() {
|
||||
assertThatThrownBy(() -> BeanDefinitionRegistrar.of("test", ConstructorSample.class).withConstructor(Object.class))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessageContaining("No constructor with type(s) [java.lang.Object] found on")
|
||||
.hasMessageContaining(ConstructorSample.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerWithFactoryMethod() {
|
||||
ResourceLoader resourceLoader = new DefaultResourceLoader();
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerResolvableDependency(ResourceLoader.class, resourceLoader);
|
||||
BeanDefinitionRegistrar.of("configuration", ConfigurationSample.class).instanceSupplier(ConfigurationSample::new)
|
||||
.register(beanFactory);
|
||||
BeanDefinitionRegistrar.of("test", ConstructorSample.class)
|
||||
.withFactoryMethod(ConfigurationSample.class, "sampleBean", ResourceLoader.class)
|
||||
.instanceSupplier(instanceContext -> instanceContext.create(beanFactory, attributes ->
|
||||
beanFactory.getBean(ConfigurationSample.class).sampleBean(attributes.get(0))))
|
||||
.register(beanFactory);
|
||||
assertBeanFactory(beanFactory, () -> {
|
||||
assertThat(beanFactory.containsBean("configuration")).isTrue();
|
||||
assertThat(beanFactory.containsBean("test")).isTrue();
|
||||
assertThat(beanFactory.getBean(ConstructorSample.class).resourceLoader).isEqualTo(resourceLoader);
|
||||
RootBeanDefinition bd = (RootBeanDefinition) beanFactory.getBeanDefinition("test");
|
||||
assertThat(bd.getResolvedFactoryMethod()).isNotNull().isEqualTo(
|
||||
ReflectionUtils.findMethod(ConfigurationSample.class, "sampleBean", ResourceLoader.class));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerWithCreateShortcutWithoutFactoryMethod() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
BeanDefinitionRegistrar.of("configuration", ConfigurationSample.class).instanceSupplier(ConfigurationSample::new)
|
||||
.register(beanFactory);
|
||||
BeanDefinitionRegistrar.of("test", ConstructorSample.class)
|
||||
.instanceSupplier(instanceContext -> instanceContext.create(beanFactory, attributes ->
|
||||
beanFactory.getBean(ConfigurationSample.class).sampleBean(attributes.get(0))))
|
||||
.register(beanFactory);
|
||||
assertThatThrownBy(() -> beanFactory.getBean("test")).isInstanceOf(BeanCreationException.class)
|
||||
.hasMessageContaining("No factory method or constructor is set");
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerWithInjectedField() {
|
||||
Environment environment = mock(Environment.class);
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerSingleton("environment", environment);
|
||||
BeanDefinitionRegistrar.of("test", InjectionSample.class).instanceSupplier(instanceContext -> {
|
||||
InjectionSample bean = new InjectionSample();
|
||||
instanceContext.field("environment").invoke(beanFactory, attributes ->
|
||||
bean.environment = (attributes.get(0)));
|
||||
return bean;
|
||||
}).register(beanFactory);
|
||||
assertBeanFactory(beanFactory, () -> {
|
||||
assertThat(beanFactory.containsBean("test")).isTrue();
|
||||
assertThat(beanFactory.getBean(InjectionSample.class).environment).isEqualTo(environment);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerWithInvalidField() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
BeanDefinitionRegistrar.of("test", InjectionSample.class).instanceSupplier(instanceContext ->
|
||||
instanceContext.field("doesNotExist").resolve(beanFactory)).register(beanFactory);
|
||||
assertThatThrownBy(() -> beanFactory.getBean(InjectionSample.class)
|
||||
).isInstanceOf(BeanCreationException.class).hasMessageContaining(
|
||||
"No field 'doesNotExist' found on " + InjectionSample.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerWithInjectedMethod() {
|
||||
Environment environment = mock(Environment.class);
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerSingleton("environment", environment);
|
||||
BeanDefinitionRegistrar.of("test", InjectionSample.class).instanceSupplier(instanceContext -> {
|
||||
InjectionSample bean = new InjectionSample();
|
||||
instanceContext.method("setEnvironment", Environment.class).invoke(beanFactory,
|
||||
attributes -> bean.setEnvironment(attributes.get(0)));
|
||||
return bean;
|
||||
}).register(beanFactory);
|
||||
assertBeanFactory(beanFactory, () -> {
|
||||
assertThat(beanFactory.containsBean("test")).isTrue();
|
||||
assertThat(beanFactory.getBean(InjectionSample.class).environment).isEqualTo(environment);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerWithInvalidMethod() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
assertThatThrownBy(() -> {
|
||||
BeanDefinitionRegistrar.of("test", InjectionSample.class).instanceSupplier(instanceContext ->
|
||||
instanceContext.method("setEnvironment", Object.class).resolve(beanFactory)).register(beanFactory);
|
||||
beanFactory.getBean(InjectionSample.class);
|
||||
}
|
||||
).isInstanceOf(BeanCreationException.class)
|
||||
.hasMessageContaining("No method '%s' with type(s) [%s] found", "setEnvironment", Object.class.getName())
|
||||
.hasMessageContaining(InjectionSample.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void innerBeanDefinitionWithClass() {
|
||||
RootBeanDefinition beanDefinition = BeanDefinitionRegistrar.inner(ConfigurationSample.class)
|
||||
.customize(bd -> bd.setSynthetic(true)).toBeanDefinition();
|
||||
assertThat(beanDefinition).isNotNull();
|
||||
assertThat(beanDefinition.getResolvableType().resolve()).isEqualTo(ConfigurationSample.class);
|
||||
assertThat(beanDefinition.isSynthetic()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void innerBeanDefinitionWithResolvableType() {
|
||||
RootBeanDefinition beanDefinition = BeanDefinitionRegistrar.inner(ResolvableType.forClass(ConfigurationSample.class))
|
||||
.customize(bd -> bd.setDescription("test")).toBeanDefinition();
|
||||
assertThat(beanDefinition).isNotNull();
|
||||
assertThat(beanDefinition.getResolvableType().resolve()).isEqualTo(ConfigurationSample.class);
|
||||
assertThat(beanDefinition.getDescription()).isEqualTo("test");
|
||||
}
|
||||
|
||||
@Test
|
||||
void innerBeanDefinitionHasInnerBeanNameInInstanceSupplier() {
|
||||
RootBeanDefinition beanDefinition = BeanDefinitionRegistrar.inner(String.class)
|
||||
.instanceSupplier(instanceContext -> {
|
||||
Field field = ReflectionUtils.findField(BeanInstanceContext.class, "beanName", String.class);
|
||||
ReflectionUtils.makeAccessible(field);
|
||||
return ReflectionUtils.getField(field, instanceContext);
|
||||
}).toBeanDefinition();
|
||||
assertThat(beanDefinition).isNotNull();
|
||||
String beanName = (String) beanDefinition.getInstanceSupplier().get();
|
||||
assertThat(beanName).isNotNull().startsWith("(inner bean)#");
|
||||
}
|
||||
|
||||
|
||||
private void assertBeanFactory(DefaultListableBeanFactory beanFactory, Runnable assertions) {
|
||||
assertions.run();
|
||||
}
|
||||
|
||||
|
||||
static class ConfigurationSample {
|
||||
|
||||
ConstructorSample sampleBean(ResourceLoader resourceLoader) {
|
||||
return new ConstructorSample(resourceLoader);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class ConstructorSample {
|
||||
private final ResourceLoader resourceLoader;
|
||||
|
||||
ConstructorSample(ResourceLoader resourceLoader) {
|
||||
this.resourceLoader = resourceLoader;
|
||||
}
|
||||
}
|
||||
|
||||
static class MultiArgConstructorSample {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final String name;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final Integer counter;
|
||||
|
||||
public MultiArgConstructorSample(String name, Integer counter) {
|
||||
this.name = name;
|
||||
this.counter = counter;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class InjectionSample {
|
||||
|
||||
private Environment environment;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private String name;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private Integer counter;
|
||||
|
||||
void setEnvironment(Environment environment) {
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
void setNameAndCounter(@Value("${test.name:test}") String name, @Value("${test.counter:42}") Integer counter) {
|
||||
this.name = name;
|
||||
this.counter = counter;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class InnerClassSample {
|
||||
|
||||
class Inner {
|
||||
|
||||
private Environment environment;
|
||||
|
||||
Inner(Environment environment) {
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class GenericFactoryBeanConfiguration {
|
||||
|
||||
FactoryBean<NumberHolder<?>> integerHolderFactory() {
|
||||
return new GenericFactoryBean<>(integerHolder());
|
||||
}
|
||||
|
||||
NumberHolder<?> integerHolder() {
|
||||
return new NumberHolder<>(42);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class GenericFactoryBean<T> implements FactoryBean<T> {
|
||||
|
||||
private final T value;
|
||||
|
||||
public GenericFactoryBean(T value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getObject() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return this.value.getClass();
|
||||
}
|
||||
}
|
||||
|
||||
static class NumberHolder<N extends Number> {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final N number;
|
||||
|
||||
public NumberHolder(N number) {
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class NumberHolderSample {
|
||||
|
||||
@Autowired
|
||||
@SuppressWarnings("unused")
|
||||
private NumberHolder<Integer> numberHolder;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,476 +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.generator.config;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.assertj.core.util.Arrays;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.UnsatisfiedDependencyException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.assertj.core.api.Assertions.entry;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link InjectedConstructionResolver}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class InjectedConstructionResolverTests {
|
||||
|
||||
@Test
|
||||
void resolveNoArgConstructor() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
InjectedElementAttributes attributes = createResolverForConstructor(
|
||||
InjectedConstructionResolverTests.class).resolve(beanFactory);
|
||||
assertThat(attributes.isResolved()).isTrue();
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("singleArgConstruction")
|
||||
void resolveSingleArgConstructor(InjectedConstructionResolver resolver) {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerSingleton("one", "1");
|
||||
InjectedElementAttributes attributes = resolver.resolve(beanFactory);
|
||||
assertThat(attributes.isResolved()).isTrue();
|
||||
assertThat((String) attributes.get(0)).isEqualTo("1");
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("singleArgConstruction")
|
||||
void resolveRequiredDependencyNotPresentThrowsUnsatisfiedDependencyException(InjectedConstructionResolver resolver) {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
assertThatThrownBy(() -> resolver.resolve(beanFactory))
|
||||
.isInstanceOfSatisfying(UnsatisfiedDependencyException.class, ex -> {
|
||||
assertThat(ex.getBeanName()).isEqualTo("test");
|
||||
assertThat(ex.getInjectionPoint()).isNotNull();
|
||||
assertThat(ex.getInjectionPoint().getMember()).isEqualTo(resolver.getExecutable());
|
||||
});
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("arrayOfBeansConstruction")
|
||||
void resolveArrayOfBeans(InjectedConstructionResolver resolver) {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerSingleton("one", "1");
|
||||
beanFactory.registerSingleton("two", "2");
|
||||
InjectedElementAttributes attributes = resolver.resolve(beanFactory);
|
||||
assertThat(attributes.isResolved()).isTrue();
|
||||
Object attribute = attributes.get(0);
|
||||
assertThat(Arrays.isArray(attribute)).isTrue();
|
||||
assertThat((Object[]) attribute).containsExactly("1", "2");
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("arrayOfBeansConstruction")
|
||||
void resolveRequiredArrayOfBeansInjectEmptyArray(InjectedConstructionResolver resolver) {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
InjectedElementAttributes attributes = resolver.resolve(beanFactory);
|
||||
assertThat(attributes.isResolved()).isTrue();
|
||||
Object attribute = attributes.get(0);
|
||||
assertThat(Arrays.isArray(attribute)).isTrue();
|
||||
assertThat((Object[]) attribute).isEmpty();
|
||||
|
||||
}
|
||||
|
||||
static Stream<Arguments> arrayOfBeansConstruction() {
|
||||
return Stream.of(Arguments.of(createResolverForConstructor(BeansCollectionConstructor.class, String[].class)),
|
||||
Arguments.of(createResolverForFactoryMethod(BeansCollectionFactory.class, "array", String[].class)));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("listOfBeansConstruction")
|
||||
void resolveListOfBeans(InjectedConstructionResolver resolver) {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerSingleton("one", "1");
|
||||
beanFactory.registerSingleton("two", "2");
|
||||
InjectedElementAttributes attributes = resolver.resolve(beanFactory);
|
||||
assertThat(attributes.isResolved()).isTrue();
|
||||
Object attribute = attributes.get(0);
|
||||
assertThat(attribute).isInstanceOf(List.class).asList().containsExactly("1", "2");
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("listOfBeansConstruction")
|
||||
void resolveRequiredListOfBeansInjectEmptyList(InjectedConstructionResolver resolver) {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
InjectedElementAttributes attributes = resolver.resolve(beanFactory);
|
||||
assertThat(attributes.isResolved()).isTrue();
|
||||
Object attribute = attributes.get(0);
|
||||
assertThat(attribute).isInstanceOf(List.class);
|
||||
assertThat((List<?>) attribute).isEmpty();
|
||||
}
|
||||
|
||||
static Stream<Arguments> listOfBeansConstruction() {
|
||||
return Stream.of(Arguments.of(createResolverForConstructor(BeansCollectionConstructor.class, List.class)),
|
||||
Arguments.of(createResolverForFactoryMethod(BeansCollectionFactory.class, "list", List.class)));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("setOfBeansConstruction")
|
||||
@SuppressWarnings("unchecked")
|
||||
void resolveSetOfBeans(InjectedConstructionResolver resolver) {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerSingleton("one", "1");
|
||||
beanFactory.registerSingleton("two", "2");
|
||||
InjectedElementAttributes attributes = resolver.resolve(beanFactory);
|
||||
assertThat(attributes.isResolved()).isTrue();
|
||||
Object attribute = attributes.get(0);
|
||||
assertThat(attribute).isInstanceOf(Set.class);
|
||||
assertThat((Set<String>) attribute).containsExactly("1", "2");
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("setOfBeansConstruction")
|
||||
void resolveRequiredSetOfBeansInjectEmptySet(InjectedConstructionResolver resolver) {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
InjectedElementAttributes attributes = resolver.resolve(beanFactory);
|
||||
assertThat(attributes.isResolved()).isTrue();
|
||||
Object attribute = attributes.get(0);
|
||||
assertThat(attribute).isInstanceOf(Set.class);
|
||||
assertThat((Set<?>) attribute).isEmpty();
|
||||
}
|
||||
|
||||
static Stream<Arguments> setOfBeansConstruction() {
|
||||
return Stream.of(Arguments.of(createResolverForConstructor(BeansCollectionConstructor.class, Set.class)),
|
||||
Arguments.of(createResolverForFactoryMethod(BeansCollectionFactory.class, "set", Set.class)));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("mapOfBeansConstruction")
|
||||
@SuppressWarnings("unchecked")
|
||||
void resolveMapOfBeans(InjectedConstructionResolver resolver) {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerSingleton("one", "1");
|
||||
beanFactory.registerSingleton("two", "2");
|
||||
InjectedElementAttributes attributes = resolver.resolve(beanFactory);
|
||||
assertThat(attributes.isResolved()).isTrue();
|
||||
Object attribute = attributes.get(0);
|
||||
assertThat(attribute).isInstanceOf(Map.class);
|
||||
assertThat((Map<String, String>) attribute).containsExactly(entry("one", "1"), entry("two", "2"));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("mapOfBeansConstruction")
|
||||
void resolveRequiredMapOfBeansInjectEmptySet(InjectedConstructionResolver resolver) {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
InjectedElementAttributes attributes = resolver.resolve(beanFactory);
|
||||
assertThat(attributes.isResolved()).isTrue();
|
||||
Object attribute = attributes.get(0);
|
||||
assertThat(attribute).isInstanceOf(Map.class);
|
||||
assertThat((Map<?, ?>) attribute).isEmpty();
|
||||
}
|
||||
|
||||
static Stream<Arguments> mapOfBeansConstruction() {
|
||||
return Stream.of(Arguments.of(createResolverForConstructor(BeansCollectionConstructor.class, Map.class)),
|
||||
Arguments.of(createResolverForFactoryMethod(BeansCollectionFactory.class, "map", Map.class)));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("multiArgsConstruction")
|
||||
void resolveMultiArgsConstructor(InjectedConstructionResolver resolver) {
|
||||
ResourceLoader resourceLoader = new DefaultResourceLoader();
|
||||
Environment environment = mock(Environment.class);
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerResolvableDependency(ResourceLoader.class, resourceLoader);
|
||||
beanFactory.registerSingleton("environment", environment);
|
||||
beanFactory.registerSingleton("one", "1");
|
||||
InjectedElementAttributes attributes = resolver.resolve(beanFactory);
|
||||
assertThat(attributes.isResolved()).isTrue();
|
||||
assertThat((ResourceLoader) attributes.get(0)).isEqualTo(resourceLoader);
|
||||
assertThat((Environment) attributes.get(1)).isEqualTo(environment);
|
||||
ObjectProvider<String> provider = attributes.get(2);
|
||||
assertThat(provider.getIfAvailable()).isEqualTo("1");
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("mixedArgsConstruction")
|
||||
void resolveMixedArgsConstructorWithUserValue(InjectedConstructionResolver resolver) {
|
||||
ResourceLoader resourceLoader = new DefaultResourceLoader();
|
||||
Environment environment = mock(Environment.class);
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerResolvableDependency(ResourceLoader.class, resourceLoader);
|
||||
beanFactory.registerSingleton("environment", environment);
|
||||
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(MixedArgsConstructor.class)
|
||||
.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR).getBeanDefinition();
|
||||
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(1, "user-value");
|
||||
beanFactory.registerBeanDefinition("test", beanDefinition);
|
||||
InjectedElementAttributes attributes = resolver.resolve(beanFactory);
|
||||
assertThat(attributes.isResolved()).isTrue();
|
||||
assertThat((ResourceLoader) attributes.get(0)).isEqualTo(resourceLoader);
|
||||
assertThat((String) attributes.get(1)).isEqualTo("user-value");
|
||||
assertThat((Environment) attributes.get(2)).isEqualTo(environment);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("mixedArgsConstruction")
|
||||
void resolveMixedArgsConstructorWithUserBeanReference(InjectedConstructionResolver resolver) {
|
||||
ResourceLoader resourceLoader = new DefaultResourceLoader();
|
||||
Environment environment = mock(Environment.class);
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerResolvableDependency(ResourceLoader.class, resourceLoader);
|
||||
beanFactory.registerSingleton("environment", environment);
|
||||
beanFactory.registerSingleton("one", "1");
|
||||
beanFactory.registerSingleton("two", "2");
|
||||
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(MixedArgsConstructor.class)
|
||||
.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR).getBeanDefinition();
|
||||
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(1, new RuntimeBeanReference("two"));
|
||||
beanFactory.registerBeanDefinition("test", beanDefinition);
|
||||
InjectedElementAttributes attributes = resolver.resolve(beanFactory);
|
||||
assertThat(attributes.isResolved()).isTrue();
|
||||
assertThat((ResourceLoader) attributes.get(0)).isEqualTo(resourceLoader);
|
||||
assertThat((String) attributes.get(1)).isEqualTo("2");
|
||||
assertThat((Environment) attributes.get(2)).isEqualTo(environment);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveUserValueWithTypeConversionRequired() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(CharDependency.class)
|
||||
.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR).getBeanDefinition();
|
||||
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, "\\");
|
||||
beanFactory.registerBeanDefinition("test", beanDefinition);
|
||||
InjectedElementAttributes attributes = createResolverForConstructor(CharDependency.class, char.class).resolve(beanFactory);
|
||||
assertThat(attributes.isResolved()).isTrue();
|
||||
Object attribute = attributes.get(0);
|
||||
assertThat(attribute).isInstanceOf(Character.class);
|
||||
assertThat((Character) attribute).isEqualTo('\\');
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("singleArgConstruction")
|
||||
void resolveUserValueWithBeanReference(InjectedConstructionResolver resolver) {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerSingleton("stringBean", "string");
|
||||
beanFactory.registerBeanDefinition("test", BeanDefinitionBuilder.rootBeanDefinition(SingleArgConstructor.class)
|
||||
.addConstructorArgReference("stringBean").getBeanDefinition());
|
||||
InjectedElementAttributes attributes = resolver.resolve(beanFactory);
|
||||
assertThat(attributes.isResolved()).isTrue();
|
||||
Object attribute = attributes.get(0);
|
||||
assertThat(attribute).isEqualTo("string");
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("singleArgConstruction")
|
||||
void resolveUserValueWithBeanDefinition(InjectedConstructionResolver resolver) {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
AbstractBeanDefinition userValue = BeanDefinitionBuilder.rootBeanDefinition(String.class, () -> "string").getBeanDefinition();
|
||||
beanFactory.registerBeanDefinition("test", BeanDefinitionBuilder.rootBeanDefinition(SingleArgConstructor.class)
|
||||
.addConstructorArgValue(userValue).getBeanDefinition());
|
||||
InjectedElementAttributes attributes = resolver.resolve(beanFactory);
|
||||
assertThat(attributes.isResolved()).isTrue();
|
||||
Object attribute = attributes.get(0);
|
||||
assertThat(attribute).isEqualTo("string");
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("singleArgConstruction")
|
||||
void resolveUserValueThatIsAlreadyResolved(InjectedConstructionResolver resolver) {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(SingleArgConstructor.class).getBeanDefinition();
|
||||
ValueHolder valueHolder = new ValueHolder('a');
|
||||
valueHolder.setConvertedValue("this is an a");
|
||||
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, valueHolder);
|
||||
beanFactory.registerBeanDefinition("test", beanDefinition);
|
||||
InjectedElementAttributes attributes = resolver.resolve(beanFactory);
|
||||
assertThat(attributes.isResolved()).isTrue();
|
||||
Object attribute = attributes.get(0);
|
||||
assertThat(attribute).isEqualTo("this is an a");
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("singleArgConstruction")
|
||||
void createInvokeFactory(InjectedConstructionResolver resolver) {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerSingleton("one", "1");
|
||||
String instance = resolver.create(beanFactory, attributes -> attributes.get(0));
|
||||
assertThat(instance).isEqualTo("1");
|
||||
}
|
||||
|
||||
private static InjectedConstructionResolver createResolverForConstructor(Class<?> beanType, Class<?>... parameterTypes) {
|
||||
try {
|
||||
Constructor<?> executable = beanType.getDeclaredConstructor(parameterTypes);
|
||||
return new InjectedConstructionResolver(executable, beanType, "test",
|
||||
InjectedConstructionResolverTests::safeGetBeanDefinition);
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static InjectedConstructionResolver createResolverForFactoryMethod(Class<?> targetType,
|
||||
String methodName, Class<?>... parameterTypes) {
|
||||
Method executable = ReflectionUtils.findMethod(targetType, methodName, parameterTypes);
|
||||
return new InjectedConstructionResolver(executable, targetType, "test",
|
||||
InjectedConstructionResolverTests::safeGetBeanDefinition);
|
||||
}
|
||||
|
||||
private static BeanDefinition safeGetBeanDefinition(DefaultListableBeanFactory beanFactory) {
|
||||
try {
|
||||
return beanFactory.getBeanDefinition("test");
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static Stream<Arguments> singleArgConstruction() {
|
||||
return Stream.of(Arguments.of(createResolverForConstructor(SingleArgConstructor.class, String.class)),
|
||||
Arguments.of(createResolverForFactoryMethod(SingleArgFactory.class, "single", String.class)));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
static class SingleArgConstructor {
|
||||
|
||||
public SingleArgConstructor(String s) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
static class SingleArgFactory {
|
||||
|
||||
String single(String s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
static class BeansCollectionConstructor {
|
||||
|
||||
public BeansCollectionConstructor(String[] beans) {
|
||||
|
||||
}
|
||||
|
||||
public BeansCollectionConstructor(List<String> beans) {
|
||||
|
||||
}
|
||||
|
||||
public BeansCollectionConstructor(Set<String> beans) {
|
||||
|
||||
}
|
||||
|
||||
public BeansCollectionConstructor(Map<String, String> beans) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
static class BeansCollectionFactory {
|
||||
|
||||
public String array(String[] beans) {
|
||||
return "test";
|
||||
}
|
||||
|
||||
public String list(List<String> beans) {
|
||||
return "test";
|
||||
}
|
||||
|
||||
public String set(Set<String> beans) {
|
||||
return "test";
|
||||
}
|
||||
|
||||
public String map(Map<String, String> beans) {
|
||||
return "test";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static Stream<Arguments> multiArgsConstruction() {
|
||||
return Stream.of(
|
||||
Arguments.of(createResolverForConstructor(MultiArgsConstructor.class, ResourceLoader.class,
|
||||
Environment.class, ObjectProvider.class)),
|
||||
Arguments.of(createResolverForFactoryMethod(MultiArgsFactory.class, "multiArgs", ResourceLoader.class,
|
||||
Environment.class, ObjectProvider.class)));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
static class MultiArgsConstructor {
|
||||
|
||||
public MultiArgsConstructor(ResourceLoader resourceLoader, Environment environment, ObjectProvider<String> provider) {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
static class MultiArgsFactory {
|
||||
|
||||
String multiArgs(ResourceLoader resourceLoader, Environment environment, ObjectProvider<String> provider) {
|
||||
return "test";
|
||||
}
|
||||
}
|
||||
|
||||
static Stream<Arguments> mixedArgsConstruction() {
|
||||
return Stream.of(
|
||||
Arguments.of(createResolverForConstructor(MixedArgsConstructor.class, ResourceLoader.class,
|
||||
String.class, Environment.class)),
|
||||
Arguments.of(createResolverForFactoryMethod(MixedArgsFactory.class, "mixedArgs", ResourceLoader.class,
|
||||
String.class, Environment.class)));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
static class MixedArgsConstructor {
|
||||
|
||||
public MixedArgsConstructor(ResourceLoader resourceLoader, String test, Environment environment) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
static class MixedArgsFactory {
|
||||
|
||||
String mixedArgs(ResourceLoader resourceLoader, String test, Environment environment) {
|
||||
return "test";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
static class CharDependency {
|
||||
|
||||
CharDependency(char escapeChar) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,91 +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.generator.config;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
|
||||
/**
|
||||
* Tests for {@link InjectedElementAttributes}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class InjectedElementAttributesTests {
|
||||
|
||||
private static final InjectedElementAttributes unresolved = new InjectedElementAttributes(null);
|
||||
|
||||
private static final InjectedElementAttributes resolved = new InjectedElementAttributes(Collections.singletonList("test"));
|
||||
|
||||
@Test
|
||||
void isResolvedWithUnresolvedAttributes() {
|
||||
assertThat(unresolved.isResolved()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isResolvedWithResoledAttributes() {
|
||||
assertThat(resolved.isResolved()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void ifResolvedWithUnresolvedAttributesDoesNotInvokeRunnable() {
|
||||
Runnable runnable = mock(Runnable.class);
|
||||
unresolved.ifResolved(runnable);
|
||||
verifyNoInteractions(runnable);
|
||||
}
|
||||
|
||||
@Test
|
||||
void ifResolvedWithResolvedAttributesInvokesRunnable() {
|
||||
Runnable runnable = mock(Runnable.class);
|
||||
resolved.ifResolved(runnable);
|
||||
verify(runnable).run();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void ifResolvedWithUnresolvedAttributesDoesNotInvokeConsumer() {
|
||||
BeanDefinitionRegistrar.ThrowableConsumer<InjectedElementAttributes> consumer = mock(BeanDefinitionRegistrar.ThrowableConsumer.class);
|
||||
unresolved.ifResolved(consumer);
|
||||
verifyNoInteractions(consumer);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void ifResolvedWithResolvedAttributesInvokesConsumer() {
|
||||
BeanDefinitionRegistrar.ThrowableConsumer<InjectedElementAttributes> consumer = mock(BeanDefinitionRegistrar.ThrowableConsumer.class);
|
||||
resolved.ifResolved(consumer);
|
||||
verify(consumer).accept(resolved);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getWithAvailableAttribute() {
|
||||
InjectedElementAttributes attributes = new InjectedElementAttributes(Collections.singletonList("test"));
|
||||
assertThat((String) attributes.get(0)).isEqualTo("test");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getWithTypeAndAvailableAttribute() {
|
||||
InjectedElementAttributes attributes = new InjectedElementAttributes(Collections.singletonList("test"));
|
||||
assertThat(attributes.get(0, String.class)).isEqualTo("test");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,79 +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.generator.config;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.UnsatisfiedDependencyException;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
/**
|
||||
* Tests for {@link InjectedFieldResolver}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class InjectedFieldResolverTests {
|
||||
|
||||
@Test
|
||||
void resolveDependency() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerSingleton("one", "1");
|
||||
InjectedElementAttributes attributes = createResolver(TestBean.class, "string",
|
||||
String.class).resolve(beanFactory, true);
|
||||
assertThat(attributes.isResolved()).isTrue();
|
||||
assertThat((String) attributes.get(0)).isEqualTo("1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveRequiredDependencyNotPresentThrowsUnsatisfiedDependencyException() {
|
||||
Field field = ReflectionUtils.findField(TestBean.class, "string", String.class);
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
assertThatThrownBy(() -> createResolver(TestBean.class, "string", String.class).resolve(beanFactory))
|
||||
.isInstanceOfSatisfying(UnsatisfiedDependencyException.class, ex -> {
|
||||
assertThat(ex.getBeanName()).isEqualTo("test");
|
||||
assertThat(ex.getInjectionPoint()).isNotNull();
|
||||
assertThat(ex.getInjectionPoint().getField()).isEqualTo(field);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveNonRequiredDependency() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
InjectedElementAttributes attributes = createResolver(TestBean.class, "string", String.class).resolve(beanFactory, false);
|
||||
assertThat(attributes.isResolved()).isFalse();
|
||||
}
|
||||
|
||||
private InjectedFieldResolver createResolver(Class<?> beanType, String fieldName, Class<?> fieldType) {
|
||||
Field field = ReflectionUtils.findField(beanType, fieldName, fieldType);
|
||||
assertThat(field).isNotNull();
|
||||
return new InjectedFieldResolver(field, "test");
|
||||
}
|
||||
|
||||
static class TestBean {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private String string;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,124 +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.generator.config;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.UnsatisfiedDependencyException;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
|
||||
/**
|
||||
* Tests for {@link InjectedMethodResolver}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class InjectedMethodResolverTests {
|
||||
|
||||
@Test
|
||||
void resolveSingleDependency() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerSingleton("test", "testValue");
|
||||
InjectedElementAttributes attributes = createResolver(TestBean.class, "injectString", String.class)
|
||||
.resolve(beanFactory, true);
|
||||
assertThat(attributes.isResolved()).isTrue();
|
||||
assertThat((String) attributes.get(0)).isEqualTo("testValue");
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveRequiredDependencyNotPresentThrowsUnsatisfiedDependencyException() {
|
||||
Method method = ReflectionUtils.findMethod(TestBean.class, "injectString", String.class);
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
assertThatThrownBy(() -> createResolver(TestBean.class, "injectString", String.class)
|
||||
.resolve(beanFactory)).isInstanceOfSatisfying(UnsatisfiedDependencyException.class, ex -> {
|
||||
assertThat(ex.getBeanName()).isEqualTo("test");
|
||||
assertThat(ex.getInjectionPoint()).isNotNull();
|
||||
assertThat(ex.getInjectionPoint().getMember()).isEqualTo(method);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveNonRequiredDependency() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
InjectedElementAttributes attributes = createResolver(TestBean.class, "injectString", String.class)
|
||||
.resolve(beanFactory, false);
|
||||
assertThat(attributes.isResolved()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveDependencyAndEnvironment() {
|
||||
Environment environment = mock(Environment.class);
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerSingleton("environment", environment);
|
||||
beanFactory.registerSingleton("test", "testValue");
|
||||
InjectedElementAttributes attributes = createResolver(TestBean.class, "injectStringAndEnvironment",
|
||||
String.class, Environment.class).resolve(beanFactory, true);
|
||||
assertThat(attributes.isResolved()).isTrue();
|
||||
String string = attributes.get(0);
|
||||
assertThat(string).isEqualTo("testValue");
|
||||
assertThat((Environment) attributes.get(1)).isEqualTo(environment);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void createWithUnresolvedAttributesDoesNotInvokeCallback() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
BeanDefinitionRegistrar.ThrowableFunction<InjectedElementAttributes, ?> callback = mock(BeanDefinitionRegistrar.ThrowableFunction.class);
|
||||
assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy(() ->
|
||||
createResolver(TestBean.class, "injectString", String.class).create(beanFactory, callback));
|
||||
verifyNoInteractions(callback);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void invokeWithUnresolvedAttributesDoesNotInvokeCallback() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
BeanDefinitionRegistrar.ThrowableConsumer<InjectedElementAttributes> callback = mock(BeanDefinitionRegistrar.ThrowableConsumer.class);
|
||||
assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy(() ->
|
||||
createResolver(TestBean.class, "injectString", String.class).invoke(beanFactory, callback));
|
||||
verifyNoInteractions(callback);
|
||||
}
|
||||
|
||||
private InjectedMethodResolver createResolver(Class<?> beanType, String methodName, Class<?>... parameterTypes) {
|
||||
Method method = ReflectionUtils.findMethod(beanType, methodName, parameterTypes);
|
||||
assertThat(method).isNotNull();
|
||||
return new InjectedMethodResolver(method, beanType, "test");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
static class TestBean {
|
||||
|
||||
public void injectString(String string) {
|
||||
|
||||
}
|
||||
|
||||
public void injectStringAndEnvironment(String string, Environment environment) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user