Add AOT interfaces and classes to support bean factories
Add AOT processor and contribution interfaces and classes to support the generation of code that can re-hydrate a bean factory. See gh-28414
This commit is contained in:
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.beans.factory.annotation;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link AutowiredArgumentsCodeGenerator}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class AutowiredArgumentsCodeGeneratorTests {
|
||||
|
||||
@Test
|
||||
void generateCodeWhenNoArguments() {
|
||||
Method method = ReflectionUtils.findMethod(UnambiguousMethods.class, "zero");
|
||||
AutowiredArgumentsCodeGenerator generator = new AutowiredArgumentsCodeGenerator(
|
||||
UnambiguousMethods.class, method);
|
||||
assertThat(generator.generateCode(method.getParameterTypes())).hasToString("");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generatedCodeWhenSingleArgument() {
|
||||
Method method = ReflectionUtils.findMethod(UnambiguousMethods.class, "one",
|
||||
String.class);
|
||||
AutowiredArgumentsCodeGenerator generator = new AutowiredArgumentsCodeGenerator(
|
||||
UnambiguousMethods.class, method);
|
||||
assertThat(generator.generateCode(method.getParameterTypes()))
|
||||
.hasToString("args.get(0)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateCodeWhenMulitpleArguments() {
|
||||
Method method = ReflectionUtils.findMethod(UnambiguousMethods.class, "three",
|
||||
String.class, Integer.class, Boolean.class);
|
||||
AutowiredArgumentsCodeGenerator generator = new AutowiredArgumentsCodeGenerator(
|
||||
UnambiguousMethods.class, method);
|
||||
assertThat(generator.generateCode(method.getParameterTypes()))
|
||||
.hasToString("args.get(0), args.get(1), args.get(2)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateCodeWhenMulitpleArgumentsWithOffset() {
|
||||
Constructor<?> constructor = Outer.Nested.class.getDeclaredConstructors()[0];
|
||||
AutowiredArgumentsCodeGenerator generator = new AutowiredArgumentsCodeGenerator(
|
||||
Outer.Nested.class, constructor);
|
||||
assertThat(generator.generateCode(constructor.getParameterTypes(), 1))
|
||||
.hasToString("args.get(0), args.get(1)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateCodeWhenAmbiguousConstructor() throws Exception {
|
||||
Constructor<?> constructor = AmbiguousConstructors.class
|
||||
.getDeclaredConstructor(String.class, Integer.class);
|
||||
AutowiredArgumentsCodeGenerator generator = new AutowiredArgumentsCodeGenerator(
|
||||
AmbiguousConstructors.class, constructor);
|
||||
assertThat(generator.generateCode(constructor.getParameterTypes())).hasToString(
|
||||
"args.get(0, java.lang.String.class), args.get(1, java.lang.Integer.class)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateCodeWhenUnambiguousConstructor() throws Exception {
|
||||
Constructor<?> constructor = UnambiguousConstructors.class
|
||||
.getDeclaredConstructor(String.class, Integer.class);
|
||||
AutowiredArgumentsCodeGenerator generator = new AutowiredArgumentsCodeGenerator(
|
||||
UnambiguousConstructors.class, constructor);
|
||||
assertThat(generator.generateCode(constructor.getParameterTypes()))
|
||||
.hasToString("args.get(0), args.get(1)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateCodeWhenAmbiguousMethod() {
|
||||
Method method = ReflectionUtils.findMethod(AmbiguousMethods.class, "two",
|
||||
String.class, Integer.class);
|
||||
AutowiredArgumentsCodeGenerator generator = new AutowiredArgumentsCodeGenerator(
|
||||
AmbiguousMethods.class, method);
|
||||
assertThat(generator.generateCode(method.getParameterTypes())).hasToString(
|
||||
"args.get(0, java.lang.String.class), args.get(1, java.lang.Integer.class)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateCodeWhenAmbiguousSubclassMethod() {
|
||||
Method method = ReflectionUtils.findMethod(UnambiguousMethods.class, "two",
|
||||
String.class, Integer.class);
|
||||
AutowiredArgumentsCodeGenerator generator = new AutowiredArgumentsCodeGenerator(
|
||||
AmbiguousSubclassMethods.class, method);
|
||||
assertThat(generator.generateCode(method.getParameterTypes())).hasToString(
|
||||
"args.get(0, java.lang.String.class), args.get(1, java.lang.Integer.class)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateCodeWhenUnambiguousMethod() {
|
||||
Method method = ReflectionUtils.findMethod(UnambiguousMethods.class, "two",
|
||||
String.class, Integer.class);
|
||||
AutowiredArgumentsCodeGenerator generator = new AutowiredArgumentsCodeGenerator(
|
||||
UnambiguousMethods.class, method);
|
||||
assertThat(generator.generateCode(method.getParameterTypes()))
|
||||
.hasToString("args.get(0), args.get(1)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateCodeWithCustomArgVariable() {
|
||||
Method method = ReflectionUtils.findMethod(UnambiguousMethods.class, "one",
|
||||
String.class);
|
||||
AutowiredArgumentsCodeGenerator generator = new AutowiredArgumentsCodeGenerator(
|
||||
UnambiguousMethods.class, method);
|
||||
assertThat(generator.generateCode(method.getParameterTypes(), 0, "objs"))
|
||||
.hasToString("objs.get(0)");
|
||||
}
|
||||
|
||||
static class Outer {
|
||||
|
||||
class Nested {
|
||||
|
||||
Nested(String a, Integer b) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class UnambiguousMethods {
|
||||
|
||||
void zero() {
|
||||
}
|
||||
|
||||
void one(String a) {
|
||||
}
|
||||
|
||||
void two(String a, Integer b) {
|
||||
}
|
||||
|
||||
void three(String a, Integer b, Boolean c) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class AmbiguousMethods {
|
||||
|
||||
void two(String a, Integer b) {
|
||||
}
|
||||
|
||||
void two(Integer b, String a) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class AmbiguousSubclassMethods extends UnambiguousMethods {
|
||||
|
||||
void two(Integer a, String b) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class UnambiguousConstructors {
|
||||
|
||||
UnambiguousConstructors() {
|
||||
}
|
||||
|
||||
UnambiguousConstructors(String a) {
|
||||
}
|
||||
|
||||
UnambiguousConstructors(String a, Integer b) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class AmbiguousConstructors {
|
||||
|
||||
AmbiguousConstructors(String a, Integer b) {
|
||||
}
|
||||
|
||||
AmbiguousConstructors(Integer b, String a) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* 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.aot;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.mock.MockSpringFactoriesLoader;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Tests for {@link AotFactoriesLoader}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class AotFactoriesLoaderTests {
|
||||
|
||||
@Test
|
||||
void createWhenBeanFactoryIsNullThrowsException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new AotFactoriesLoader(null))
|
||||
.withMessage("BeanFactory must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWhenSpringFactoriesLoaderIsNullThrowsException() {
|
||||
ListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new AotFactoriesLoader(beanFactory, null))
|
||||
.withMessage("FactoriesLoader must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void loadLoadsFromBeanFactoryAndSpringFactoriesLoaderInOrder() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerSingleton("b1", new TestFactoryImpl(0, "b1"));
|
||||
beanFactory.registerSingleton("b2", new TestFactoryImpl(2, "b2"));
|
||||
MockSpringFactoriesLoader springFactoriesLoader = new MockSpringFactoriesLoader();
|
||||
springFactoriesLoader.addInstance(TestFactory.class,
|
||||
new TestFactoryImpl(1, "l1"));
|
||||
springFactoriesLoader.addInstance(TestFactory.class,
|
||||
new TestFactoryImpl(3, "l2"));
|
||||
AotFactoriesLoader loader = new AotFactoriesLoader(beanFactory,
|
||||
springFactoriesLoader);
|
||||
List<TestFactory> loaded = loader.load(TestFactory.class);
|
||||
assertThat(loaded).hasSize(4);
|
||||
assertThat(loaded.get(0)).hasToString("b1");
|
||||
assertThat(loaded.get(1)).hasToString("l1");
|
||||
assertThat(loaded.get(2)).hasToString("b2");
|
||||
assertThat(loaded.get(3)).hasToString("l2");
|
||||
}
|
||||
|
||||
static interface TestFactory {
|
||||
|
||||
}
|
||||
|
||||
static class TestFactoryImpl implements TestFactory, Ordered {
|
||||
|
||||
private final int order;
|
||||
|
||||
private final String name;
|
||||
|
||||
TestFactoryImpl(int order, String name) {
|
||||
this.order = order;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* 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.aot;
|
||||
|
||||
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.RegisteredBean;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.mock.MockSpringFactoriesLoader;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link BeanDefinitionMethodGeneratorFactory}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class BeanDefinitionMethodGeneratorFactoryTests {
|
||||
|
||||
@Test
|
||||
void getBeanDefinitionMethodGeneratorWhenExcludedByBeanRegistrationExcludeFilterReturnsNull() {
|
||||
MockSpringFactoriesLoader springFactoriesLoader = new MockSpringFactoriesLoader();
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
springFactoriesLoader.addInstance(BeanRegistrationExcludeFilter.class,
|
||||
new MockBeanRegistrationExcludeFilter(true, 0));
|
||||
RegisteredBean registeredBean = registerTestBean(beanFactory);
|
||||
BeanDefinitionMethodGeneratorFactory methodGeneratorFactory = new BeanDefinitionMethodGeneratorFactory(
|
||||
new AotFactoriesLoader(beanFactory, springFactoriesLoader));
|
||||
assertThat(methodGeneratorFactory.getBeanDefinitionMethodGenerator(registeredBean,
|
||||
null)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getBeanDefinitionMethodGeneratorWhenExcludedByBeanRegistrationExcludeFilterBeanReturnsNull() {
|
||||
MockSpringFactoriesLoader springFactoriesLoader = new MockSpringFactoriesLoader();
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
RegisteredBean registeredBean = registerTestBean(beanFactory);
|
||||
beanFactory.registerSingleton("filter",
|
||||
new MockBeanRegistrationExcludeFilter(true, 0));
|
||||
BeanDefinitionMethodGeneratorFactory methodGeneratorFactory = new BeanDefinitionMethodGeneratorFactory(
|
||||
new AotFactoriesLoader(beanFactory, springFactoriesLoader));
|
||||
assertThat(methodGeneratorFactory.getBeanDefinitionMethodGenerator(registeredBean,
|
||||
null)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getBeanDefinitionMethodGeneratorConsidersFactoryLoadedExcludeFiltersAndBeansInOrderedOrder() {
|
||||
MockBeanRegistrationExcludeFilter filter1 = new MockBeanRegistrationExcludeFilter(
|
||||
false, 1);
|
||||
MockBeanRegistrationExcludeFilter filter2 = new MockBeanRegistrationExcludeFilter(
|
||||
false, 2);
|
||||
MockBeanRegistrationExcludeFilter filter3 = new MockBeanRegistrationExcludeFilter(
|
||||
false, 3);
|
||||
MockBeanRegistrationExcludeFilter filter4 = new MockBeanRegistrationExcludeFilter(
|
||||
true, 4);
|
||||
MockBeanRegistrationExcludeFilter filter5 = new MockBeanRegistrationExcludeFilter(
|
||||
true, 5);
|
||||
MockBeanRegistrationExcludeFilter filter6 = new MockBeanRegistrationExcludeFilter(
|
||||
true, 6);
|
||||
MockSpringFactoriesLoader springFactoriesLoader = new MockSpringFactoriesLoader();
|
||||
springFactoriesLoader.addInstance(BeanRegistrationExcludeFilter.class, filter3,
|
||||
filter1, filter5);
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerSingleton("filter4", filter4);
|
||||
beanFactory.registerSingleton("filter2", filter2);
|
||||
beanFactory.registerSingleton("filter6", filter6);
|
||||
RegisteredBean registeredBean = registerTestBean(beanFactory);
|
||||
BeanDefinitionMethodGeneratorFactory methodGeneratorFactory = new BeanDefinitionMethodGeneratorFactory(
|
||||
new AotFactoriesLoader(beanFactory, springFactoriesLoader));
|
||||
assertThat(methodGeneratorFactory.getBeanDefinitionMethodGenerator(registeredBean,
|
||||
null)).isNull();
|
||||
assertThat(filter1.wasCalled()).isTrue();
|
||||
assertThat(filter2.wasCalled()).isTrue();
|
||||
assertThat(filter3.wasCalled()).isTrue();
|
||||
assertThat(filter4.wasCalled()).isTrue();
|
||||
assertThat(filter5.wasCalled()).isFalse();
|
||||
assertThat(filter6.wasCalled()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getBeanDefinitionMethodGeneratorAddsContributionsFromProcessors() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
BeanRegistrationAotContribution beanContribution = mock(
|
||||
BeanRegistrationAotContribution.class);
|
||||
BeanRegistrationAotProcessor processorBean = registeredBean -> beanContribution;
|
||||
beanFactory.registerSingleton("processorBean", processorBean);
|
||||
MockSpringFactoriesLoader springFactoriesLoader = new MockSpringFactoriesLoader();
|
||||
BeanRegistrationAotContribution loaderContribution = mock(
|
||||
BeanRegistrationAotContribution.class);
|
||||
BeanRegistrationAotProcessor loaderProcessor = registeredBean -> loaderContribution;
|
||||
springFactoriesLoader.addInstance(BeanRegistrationAotProcessor.class,
|
||||
loaderProcessor);
|
||||
RegisteredBean registeredBean = registerTestBean(beanFactory);
|
||||
BeanDefinitionMethodGeneratorFactory methodGeneratorFactory = new BeanDefinitionMethodGeneratorFactory(
|
||||
new AotFactoriesLoader(beanFactory, springFactoriesLoader));
|
||||
BeanDefinitionMethodGenerator methodGenerator = methodGeneratorFactory
|
||||
.getBeanDefinitionMethodGenerator(registeredBean, null);
|
||||
assertThat(methodGenerator).extracting("aotContributions").asList()
|
||||
.containsExactly(beanContribution, loaderContribution);
|
||||
}
|
||||
|
||||
private RegisteredBean registerTestBean(DefaultListableBeanFactory beanFactory) {
|
||||
beanFactory.registerBeanDefinition("test", BeanDefinitionBuilder
|
||||
.rootBeanDefinition(TestBean.class).getBeanDefinition());
|
||||
return RegisteredBean.of(beanFactory, "test");
|
||||
}
|
||||
|
||||
static class MockBeanRegistrationExcludeFilter
|
||||
implements BeanRegistrationExcludeFilter, Ordered {
|
||||
|
||||
private final boolean excluded;
|
||||
|
||||
private final int order;
|
||||
|
||||
private RegisteredBean registeredBean;
|
||||
|
||||
MockBeanRegistrationExcludeFilter(boolean excluded, int order) {
|
||||
this.excluded = excluded;
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExcluded(RegisteredBean registeredBean) {
|
||||
this.registeredBean = registeredBean;
|
||||
return this.excluded;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
|
||||
boolean wasCalled() {
|
||||
return this.registeredBean != null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class TestBean {
|
||||
|
||||
}
|
||||
|
||||
static class InnerTestBean {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,402 @@
|
||||
/*
|
||||
* 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.aot;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import javax.lang.model.element.Modifier;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aot.generate.DefaultGenerationContext;
|
||||
import org.springframework.aot.generate.GeneratedMethod;
|
||||
import org.springframework.aot.generate.GenerationContext;
|
||||
import org.springframework.aot.generate.InMemoryGeneratedFiles;
|
||||
import org.springframework.aot.generate.MethodReference;
|
||||
import org.springframework.aot.test.generator.compile.CompileWithTargetClassAccess;
|
||||
import org.springframework.aot.test.generator.compile.Compiled;
|
||||
import org.springframework.aot.test.generator.compile.TestCompiler;
|
||||
import org.springframework.aot.test.generator.file.SourceFile;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.InstanceSupplier;
|
||||
import org.springframework.beans.factory.support.RegisteredBean;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.testfixture.beans.AnnotatedBean;
|
||||
import org.springframework.beans.testfixture.beans.GenericBean;
|
||||
import org.springframework.beans.testfixture.beans.TestBean;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.mock.MockSpringFactoriesLoader;
|
||||
import org.springframework.javapoet.ClassName;
|
||||
import org.springframework.javapoet.CodeBlock;
|
||||
import org.springframework.javapoet.JavaFile;
|
||||
import org.springframework.javapoet.MethodSpec;
|
||||
import org.springframework.javapoet.ParameterizedTypeName;
|
||||
import org.springframework.javapoet.TypeSpec;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link BeanDefinitionMethodGenerator} and
|
||||
* {@link DefaultBeanRegistrationCodeFragments}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class BeanDefinitionMethodGeneratorTests {
|
||||
|
||||
private InMemoryGeneratedFiles generatedFiles;
|
||||
|
||||
private DefaultGenerationContext generationContext;
|
||||
|
||||
private DefaultListableBeanFactory beanFactory;
|
||||
|
||||
private MockSpringFactoriesLoader springFactoriesLoader;
|
||||
|
||||
private MockBeanRegistrationsCode beanRegistrationsCode;
|
||||
|
||||
private BeanDefinitionMethodGeneratorFactory methodGeneratorFactory;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
this.generatedFiles = new InMemoryGeneratedFiles();
|
||||
this.generationContext = new DefaultGenerationContext(this.generatedFiles);
|
||||
this.beanFactory = new DefaultListableBeanFactory();
|
||||
this.springFactoriesLoader = new MockSpringFactoriesLoader();
|
||||
this.methodGeneratorFactory = new BeanDefinitionMethodGeneratorFactory(
|
||||
new AotFactoriesLoader(this.beanFactory, this.springFactoriesLoader));
|
||||
this.beanRegistrationsCode = new MockBeanRegistrationsCode(
|
||||
ClassName.get("__", "Registration"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateBeanDefinitionMethodGeneratesMethod() {
|
||||
RegisteredBean registeredBean = registerBean(
|
||||
new RootBeanDefinition(TestBean.class));
|
||||
BeanDefinitionMethodGenerator generator = new BeanDefinitionMethodGenerator(
|
||||
this.methodGeneratorFactory, registeredBean, null,
|
||||
Collections.emptyList(), Collections.emptyList());
|
||||
MethodReference method = generator.generateBeanDefinitionMethod(
|
||||
this.generationContext, this.beanRegistrationsCode);
|
||||
testCompiledResult(method, (actual, compiled) -> {
|
||||
SourceFile sourceFile = compiled.getSourceFile(".*BeanDefinitions");
|
||||
assertThat(sourceFile).contains("Get the bean definition for 'testBean'");
|
||||
assertThat(sourceFile).contains("beanType = TestBean.class");
|
||||
assertThat(sourceFile).contains("setInstanceSupplier(TestBean::new)");
|
||||
assertThat(actual).isInstanceOf(RootBeanDefinition.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateBeanDefinitionMethodWhenHasGenericsGeneratesMethod() {
|
||||
RegisteredBean registeredBean = registerBean(new RootBeanDefinition(
|
||||
ResolvableType.forClassWithGenerics(GenericBean.class, Integer.class)));
|
||||
BeanDefinitionMethodGenerator generator = new BeanDefinitionMethodGenerator(
|
||||
this.methodGeneratorFactory, registeredBean, null,
|
||||
Collections.emptyList(), Collections.emptyList());
|
||||
MethodReference method = generator.generateBeanDefinitionMethod(
|
||||
this.generationContext, this.beanRegistrationsCode);
|
||||
testCompiledResult(method, (actual, compiled) -> {
|
||||
assertThat(actual.getResolvableType().resolve()).isEqualTo(GenericBean.class);
|
||||
SourceFile sourceFile = compiled.getSourceFile(".*BeanDefinitions");
|
||||
assertThat(sourceFile).contains("Get the bean definition for 'testBean'");
|
||||
assertThat(sourceFile).contains(
|
||||
"beanType = ResolvableType.forClassWithGenerics(GenericBean.class, Integer.class)");
|
||||
assertThat(sourceFile).contains("setInstanceSupplier(GenericBean::new)");
|
||||
assertThat(actual).isInstanceOf(RootBeanDefinition.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateBeanDefinitionMethodWhenHasInstancePostProcessorGeneratesMethod() {
|
||||
RegisteredBean registeredBean = registerBean(
|
||||
new RootBeanDefinition(TestBean.class));
|
||||
BeanRegistrationAotContribution aotContribution = (generationContext,
|
||||
beanRegistrationCode) -> {
|
||||
GeneratedMethod method = beanRegistrationCode.getMethodGenerator()
|
||||
.generateMethod("postProcess")
|
||||
.using(builder -> builder.addModifiers(Modifier.STATIC)
|
||||
.addParameter(RegisteredBean.class, "registeredBean")
|
||||
.addParameter(TestBean.class, "testBean")
|
||||
.returns(TestBean.class).addCode("return new $T($S);",
|
||||
TestBean.class, "postprocessed"));
|
||||
beanRegistrationCode.addInstancePostProcessor(MethodReference.ofStatic(
|
||||
beanRegistrationCode.getClassName(), method.getName().toString()));
|
||||
};
|
||||
List<BeanRegistrationAotContribution> aotContributions = Collections
|
||||
.singletonList(aotContribution);
|
||||
BeanDefinitionMethodGenerator generator = new BeanDefinitionMethodGenerator(
|
||||
this.methodGeneratorFactory, registeredBean, null, aotContributions,
|
||||
Collections.emptyList());
|
||||
MethodReference method = generator.generateBeanDefinitionMethod(
|
||||
this.generationContext, this.beanRegistrationsCode);
|
||||
testCompiledResult(method, (actual, compiled) -> {
|
||||
assertThat(actual.getBeanClass()).isEqualTo(TestBean.class);
|
||||
InstanceSupplier<?> supplier = (InstanceSupplier<?>) actual
|
||||
.getInstanceSupplier();
|
||||
try {
|
||||
TestBean instance = (TestBean) supplier.get(registeredBean);
|
||||
assertThat(instance.getName()).isEqualTo("postprocessed");
|
||||
}
|
||||
catch (Exception ex) {
|
||||
}
|
||||
SourceFile sourceFile = compiled.getSourceFile(".*BeanDefinitions");
|
||||
assertThat(sourceFile).contains("instanceSupplier.andThen(");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateBeanDefinitionMethodWhenHasAttributeFilterGeneratesMethod() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(TestBean.class);
|
||||
beanDefinition.setAttribute("a", "A");
|
||||
beanDefinition.setAttribute("b", "B");
|
||||
RegisteredBean registeredBean = registerBean(beanDefinition);
|
||||
List<BeanRegistrationCodeFragmentsCustomizer> fragmentCustomizers = Collections
|
||||
.singletonList(this::customizeWithAttributeFilter);
|
||||
BeanDefinitionMethodGenerator generator = new BeanDefinitionMethodGenerator(
|
||||
this.methodGeneratorFactory, registeredBean, null,
|
||||
Collections.emptyList(), fragmentCustomizers);
|
||||
MethodReference method = generator.generateBeanDefinitionMethod(
|
||||
this.generationContext, this.beanRegistrationsCode);
|
||||
testCompiledResult(method, (actual, compiled) -> {
|
||||
assertThat(actual.getAttribute("a")).isEqualTo("A");
|
||||
assertThat(actual.getAttribute("b")).isNull();
|
||||
});
|
||||
}
|
||||
|
||||
private BeanRegistrationCodeFragments customizeWithAttributeFilter(
|
||||
RegisteredBean registeredBean, BeanRegistrationCodeFragments codeFragments) {
|
||||
return new BeanRegistrationCodeFragments(codeFragments) {
|
||||
|
||||
@Override
|
||||
public CodeBlock generateSetBeanDefinitionPropertiesCode(
|
||||
GenerationContext generationContext,
|
||||
BeanRegistrationCode beanRegistrationCode,
|
||||
RootBeanDefinition beanDefinition,
|
||||
Predicate<String> attributeFilter) {
|
||||
return super.generateSetBeanDefinitionPropertiesCode(generationContext,
|
||||
beanRegistrationCode, beanDefinition, name -> "a".equals(name));
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateBeanDefinitionMethodWhenInnerBeanGeneratesMethod() {
|
||||
RegisteredBean parent = registerBean(new RootBeanDefinition(TestBean.class));
|
||||
RegisteredBean innerBean = RegisteredBean.ofInnerBean(parent,
|
||||
new RootBeanDefinition(AnnotatedBean.class));
|
||||
BeanDefinitionMethodGenerator generator = new BeanDefinitionMethodGenerator(
|
||||
this.methodGeneratorFactory, innerBean, "testInnerBean",
|
||||
Collections.emptyList(), Collections.emptyList());
|
||||
MethodReference method = generator.generateBeanDefinitionMethod(
|
||||
this.generationContext, this.beanRegistrationsCode);
|
||||
testCompiledResult(method, (actual, compiled) -> {
|
||||
assertThat(compiled.getSourceFile(".*BeanDefinitions"))
|
||||
.contains("Get the inner-bean definition for 'testInnerBean'");
|
||||
assertThat(actual).isInstanceOf(RootBeanDefinition.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateBeanDefinitionMethodWhenHasInnerBeanPropertyValueGeneratesMethod() {
|
||||
RootBeanDefinition innerBeanDefinition = (RootBeanDefinition) BeanDefinitionBuilder
|
||||
.rootBeanDefinition(AnnotatedBean.class)
|
||||
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE).setPrimary(true)
|
||||
.getBeanDefinition();
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(TestBean.class);
|
||||
beanDefinition.getPropertyValues().add("name", innerBeanDefinition);
|
||||
RegisteredBean registeredBean = registerBean(beanDefinition);
|
||||
BeanDefinitionMethodGenerator generator = new BeanDefinitionMethodGenerator(
|
||||
this.methodGeneratorFactory, registeredBean, null,
|
||||
Collections.emptyList(), Collections.emptyList());
|
||||
MethodReference method = generator.generateBeanDefinitionMethod(
|
||||
this.generationContext, this.beanRegistrationsCode);
|
||||
testCompiledResult(method, (actual, compiled) -> {
|
||||
RootBeanDefinition actualInnerBeanDefinition = (RootBeanDefinition) actual
|
||||
.getPropertyValues().get("name");
|
||||
assertThat(actualInnerBeanDefinition.isPrimary()).isTrue();
|
||||
assertThat(actualInnerBeanDefinition.getRole())
|
||||
.isEqualTo(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
Supplier<?> innerInstanceSupplier = actualInnerBeanDefinition
|
||||
.getInstanceSupplier();
|
||||
try {
|
||||
assertThat(innerInstanceSupplier.get()).isInstanceOf(AnnotatedBean.class);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateBeanDefinitionMethodWhenHasInnerBeanConstructorValueGeneratesMethod() {
|
||||
RootBeanDefinition innerBeanDefinition = (RootBeanDefinition) BeanDefinitionBuilder
|
||||
.rootBeanDefinition(String.class)
|
||||
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE).setPrimary(true)
|
||||
.getBeanDefinition();
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(TestBean.class);
|
||||
ValueHolder valueHolder = new ValueHolder(innerBeanDefinition);
|
||||
valueHolder.setName("second");
|
||||
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0,
|
||||
valueHolder);
|
||||
RegisteredBean registeredBean = registerBean(beanDefinition);
|
||||
BeanDefinitionMethodGenerator generator = new BeanDefinitionMethodGenerator(
|
||||
this.methodGeneratorFactory, registeredBean, null,
|
||||
Collections.emptyList(), Collections.emptyList());
|
||||
MethodReference method = generator.generateBeanDefinitionMethod(
|
||||
this.generationContext, this.beanRegistrationsCode);
|
||||
testCompiledResult(method, (actual, compiled) -> {
|
||||
RootBeanDefinition actualInnerBeanDefinition = (RootBeanDefinition) actual
|
||||
.getConstructorArgumentValues()
|
||||
.getIndexedArgumentValue(0, RootBeanDefinition.class).getValue();
|
||||
assertThat(actualInnerBeanDefinition.isPrimary()).isTrue();
|
||||
assertThat(actualInnerBeanDefinition.getRole())
|
||||
.isEqualTo(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
Supplier<?> innerInstanceSupplier = actualInnerBeanDefinition
|
||||
.getInstanceSupplier();
|
||||
try {
|
||||
assertThat(innerInstanceSupplier.get()).isInstanceOf(String.class);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
assertThat(compiled.getSourceFile(".*BeanDefinitions"))
|
||||
.contains("getSecondBeanDefinition()");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateBeanDefinitionMethodWhenHasAotContributionsAppliesContributions() {
|
||||
RegisteredBean registeredBean = registerBean(
|
||||
new RootBeanDefinition(TestBean.class));
|
||||
List<BeanRegistrationAotContribution> aotContributions = new ArrayList<>();
|
||||
aotContributions
|
||||
.add((generationContext, beanRegistrationCode) -> beanRegistrationCode
|
||||
.getMethodGenerator().generateMethod("aotContributedMethod")
|
||||
.using(builder -> builder.addComment("Example Contribution")));
|
||||
BeanDefinitionMethodGenerator generator = new BeanDefinitionMethodGenerator(
|
||||
this.methodGeneratorFactory, registeredBean, null, aotContributions,
|
||||
Collections.emptyList());
|
||||
MethodReference method = generator.generateBeanDefinitionMethod(
|
||||
this.generationContext, this.beanRegistrationsCode);
|
||||
testCompiledResult(method, (actual, compiled) -> {
|
||||
SourceFile sourceFile = compiled.getSourceFile(".*BeanDefinitions");
|
||||
assertThat(sourceFile).contains("AotContributedMethod()");
|
||||
assertThat(sourceFile).contains("Example Contribution");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateBeanDefinitionMethodWhenHasBeanRegistrationCodeFragmentsCustomizerReturnsCodeGeneratesMethod() {
|
||||
RegisteredBean registeredBean = registerBean(
|
||||
new RootBeanDefinition(TestBean.class));
|
||||
List<BeanRegistrationCodeFragmentsCustomizer> codeFragmentsCustomizers = new ArrayList<>();
|
||||
codeFragmentsCustomizers.add(this::customizeBeanRegistrationCodeFragments);
|
||||
BeanDefinitionMethodGenerator generator = new BeanDefinitionMethodGenerator(
|
||||
this.methodGeneratorFactory, registeredBean, null,
|
||||
Collections.emptyList(), codeFragmentsCustomizers);
|
||||
MethodReference method = generator.generateBeanDefinitionMethod(
|
||||
this.generationContext, this.beanRegistrationsCode);
|
||||
testCompiledResult(method,
|
||||
(actual, compiled) -> assertThat(
|
||||
compiled.getSourceFile(".*BeanDefinitions"))
|
||||
.contains("// Custom Code"));
|
||||
}
|
||||
|
||||
private BeanRegistrationCodeFragments customizeBeanRegistrationCodeFragments(
|
||||
RegisteredBean registeredBean, BeanRegistrationCodeFragments codeFragments) {
|
||||
return new BeanRegistrationCodeFragments(codeFragments) {
|
||||
|
||||
@Override
|
||||
public CodeBlock generateReturnCode(GenerationContext generationContext,
|
||||
BeanRegistrationCode beanRegistrationCode) {
|
||||
CodeBlock.Builder builder = CodeBlock.builder();
|
||||
builder.addStatement("// Custom Code");
|
||||
builder.add(super.generateReturnCode(generationContext,
|
||||
beanRegistrationCode));
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
@CompileWithTargetClassAccess(classes = PackagePrivateTestBean.class)
|
||||
void generateBeanDefinitionMethodWhenPackagePrivateBean() {
|
||||
RegisteredBean registeredBean = registerBean(
|
||||
new RootBeanDefinition(PackagePrivateTestBean.class));
|
||||
BeanDefinitionMethodGenerator generator = new BeanDefinitionMethodGenerator(
|
||||
this.methodGeneratorFactory, registeredBean, null,
|
||||
Collections.emptyList(), Collections.emptyList());
|
||||
MethodReference method = generator.generateBeanDefinitionMethod(
|
||||
this.generationContext, this.beanRegistrationsCode);
|
||||
testCompiledResult(method, false, (actual, compiled) -> {
|
||||
DefaultListableBeanFactory freshBeanFactory = new DefaultListableBeanFactory();
|
||||
freshBeanFactory.registerBeanDefinition("test", actual);
|
||||
Object bean = freshBeanFactory.getBean("test");
|
||||
assertThat(bean).isInstanceOf(PackagePrivateTestBean.class);
|
||||
assertThat(compiled.getSourceFileFromPackage(
|
||||
PackagePrivateTestBean.class.getPackageName())).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
private RegisteredBean registerBean(RootBeanDefinition beanDefinition) {
|
||||
String beanName = "testBean";
|
||||
this.beanFactory.registerBeanDefinition(beanName, beanDefinition);
|
||||
RegisteredBean registeredBean = RegisteredBean.of(this.beanFactory, beanName);
|
||||
return registeredBean;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void testCompiledResult(MethodReference method,
|
||||
BiConsumer<RootBeanDefinition, Compiled> result) {
|
||||
testCompiledResult(method, false, result);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void testCompiledResult(MethodReference method, boolean targetClassAccess,
|
||||
BiConsumer<RootBeanDefinition, Compiled> result) {
|
||||
this.generationContext.writeGeneratedContent();
|
||||
JavaFile javaFile = generateJavaFile(method);
|
||||
TestCompiler.forSystem().withFiles(this.generatedFiles).printFiles(System.out)
|
||||
.compile(javaFile::writeTo, compiled -> result.accept(
|
||||
(RootBeanDefinition) compiled.getInstance(Supplier.class).get(),
|
||||
compiled));
|
||||
}
|
||||
|
||||
private JavaFile generateJavaFile(MethodReference method) {
|
||||
TypeSpec.Builder builder = TypeSpec.classBuilder("Registration");
|
||||
builder.addModifiers(Modifier.PUBLIC);
|
||||
builder.addSuperinterface(
|
||||
ParameterizedTypeName.get(Supplier.class, BeanDefinition.class));
|
||||
builder.addMethod(MethodSpec.methodBuilder("get").addModifiers(Modifier.PUBLIC)
|
||||
.returns(BeanDefinition.class)
|
||||
.addCode("return $L;", method.toInvokeCodeBlock()).build());
|
||||
this.beanRegistrationsCode.getGeneratedMethods()
|
||||
.doWithMethodSpecs(builder::addMethod);
|
||||
return JavaFile.builder("__", builder.build()).build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,441 @@
|
||||
/*
|
||||
* 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.aot;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import javax.lang.model.element.Modifier;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aot.generate.GeneratedMethods;
|
||||
import org.springframework.aot.hint.ExecutableMode;
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.test.generator.compile.Compiled;
|
||||
import org.springframework.aot.test.generator.compile.TestCompiler;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanReference;
|
||||
import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanNameReference;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.ManagedList;
|
||||
import org.springframework.beans.factory.support.ManagedMap;
|
||||
import org.springframework.beans.factory.support.ManagedSet;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.javapoet.CodeBlock;
|
||||
import org.springframework.javapoet.JavaFile;
|
||||
import org.springframework.javapoet.MethodSpec;
|
||||
import org.springframework.javapoet.ParameterizedTypeName;
|
||||
import org.springframework.javapoet.TypeSpec;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link BeanDefinitionPropertiesCodeGenerator}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class BeanDefinitionPropertiesCodeGeneratorTests {
|
||||
|
||||
private final RootBeanDefinition beanDefinition = new RootBeanDefinition();
|
||||
|
||||
private final GeneratedMethods generatedMethods = new GeneratedMethods();
|
||||
|
||||
private final RuntimeHints hints = new RuntimeHints();
|
||||
|
||||
private BeanDefinitionPropertiesCodeGenerator generator = new BeanDefinitionPropertiesCodeGenerator(
|
||||
this.hints, attribute -> true, this.generatedMethods, (name, value) -> null);
|
||||
|
||||
|
||||
@Test
|
||||
void setPrimaryWhenFalse() {
|
||||
this.beanDefinition.setPrimary(false);
|
||||
testCompiledResult((actual, compiled) -> {
|
||||
assertThat(compiled.getSourceFile()).doesNotContain("setPrimary");
|
||||
assertThat(actual.isPrimary()).isFalse();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void setPrimaryWhenTrue() {
|
||||
this.beanDefinition.setPrimary(true);
|
||||
testCompiledResult((actual, compiled) -> assertThat(actual.isPrimary()).isTrue());
|
||||
}
|
||||
|
||||
@Test
|
||||
void setScopeWhenEmptyString() {
|
||||
this.beanDefinition.setScope("");
|
||||
testCompiledResult((actual, compiled) -> {
|
||||
assertThat(compiled.getSourceFile()).doesNotContain("setScope");
|
||||
assertThat(actual.getScope()).isEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void setScopeWhenSingleton() {
|
||||
this.beanDefinition.setScope("singleton");
|
||||
testCompiledResult((actual, compiled) -> {
|
||||
assertThat(compiled.getSourceFile()).doesNotContain("setScope");
|
||||
assertThat(actual.getScope()).isEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void setScopeWhenOther() {
|
||||
this.beanDefinition.setScope("prototype");
|
||||
testCompiledResult((actual, compiled) -> assertThat(actual.getScope())
|
||||
.isEqualTo("prototype"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void setDependsOnWhenEmpty() {
|
||||
this.beanDefinition.setDependsOn();
|
||||
testCompiledResult((actual, compiled) -> {
|
||||
assertThat(compiled.getSourceFile()).doesNotContain("setDependsOn");
|
||||
assertThat(actual.getDependsOn()).isNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void setDependsOnWhenNotEmpty() {
|
||||
this.beanDefinition.setDependsOn("a", "b", "c");
|
||||
testCompiledResult((actual, compiled) -> assertThat(actual.getDependsOn())
|
||||
.containsExactly("a", "b", "c"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void setLazyInitWhenNoSet() {
|
||||
testCompiledResult((actual, compiled) -> {
|
||||
assertThat(compiled.getSourceFile()).doesNotContain("setLazyInit");
|
||||
assertThat(actual.isLazyInit()).isFalse();
|
||||
assertThat(actual.getLazyInit()).isNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void setLazyInitWhenFalse() {
|
||||
this.beanDefinition.setLazyInit(false);
|
||||
testCompiledResult((actual, compiled) -> {
|
||||
assertThat(actual.isLazyInit()).isFalse();
|
||||
assertThat(actual.getLazyInit()).isFalse();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void setLazyInitWhenTrue() {
|
||||
this.beanDefinition.setLazyInit(true);
|
||||
testCompiledResult((actual, compiled) -> {
|
||||
assertThat(actual.isLazyInit()).isTrue();
|
||||
assertThat(actual.getLazyInit()).isTrue();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void setAutowireCandidateWhenFalse() {
|
||||
this.beanDefinition.setAutowireCandidate(false);
|
||||
testCompiledResult(
|
||||
(actual, compiled) -> assertThat(actual.isAutowireCandidate()).isFalse());
|
||||
}
|
||||
|
||||
@Test
|
||||
void setAutowireCandidateWhenTrue() {
|
||||
this.beanDefinition.setAutowireCandidate(true);
|
||||
testCompiledResult((actual, compiled) -> {
|
||||
assertThat(compiled.getSourceFile()).doesNotContain("setAutowireCandidate");
|
||||
assertThat(actual.isAutowireCandidate()).isTrue();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void setSyntheticWhenFalse() {
|
||||
this.beanDefinition.setSynthetic(false);
|
||||
testCompiledResult((actual, compiled) -> {
|
||||
assertThat(compiled.getSourceFile()).doesNotContain("setSynthetic");
|
||||
assertThat(actual.isSynthetic()).isFalse();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void setSyntheticWhenTrue() {
|
||||
this.beanDefinition.setSynthetic(true);
|
||||
testCompiledResult(
|
||||
(actual, compiled) -> assertThat(actual.isSynthetic()).isTrue());
|
||||
}
|
||||
|
||||
@Test
|
||||
void setRoleWhenApplication() {
|
||||
this.beanDefinition.setRole(BeanDefinition.ROLE_APPLICATION);
|
||||
testCompiledResult((actual, compiled) -> {
|
||||
assertThat(compiled.getSourceFile()).doesNotContain("setRole");
|
||||
assertThat(actual.getRole()).isEqualTo(BeanDefinition.ROLE_APPLICATION);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void setRoleWhenInfrastructure() {
|
||||
this.beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
testCompiledResult((actual, compiled) -> {
|
||||
assertThat(compiled.getSourceFile())
|
||||
.contains("setRole(BeanDefinition.ROLE_INFRASTRUCTURE);");
|
||||
assertThat(actual.getRole()).isEqualTo(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void setRoleWhenSupport() {
|
||||
this.beanDefinition.setRole(BeanDefinition.ROLE_SUPPORT);
|
||||
testCompiledResult((actual, compiled) -> {
|
||||
assertThat(compiled.getSourceFile())
|
||||
.contains("setRole(BeanDefinition.ROLE_SUPPORT);");
|
||||
assertThat(actual.getRole()).isEqualTo(BeanDefinition.ROLE_SUPPORT);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void setRoleWhenOther() {
|
||||
this.beanDefinition.setRole(999);
|
||||
testCompiledResult(
|
||||
(actual, compiled) -> assertThat(actual.getRole()).isEqualTo(999));
|
||||
}
|
||||
|
||||
@Test
|
||||
void setInitMethodWhenSingleInitMethod() {
|
||||
this.beanDefinition.setTargetType(InitDestroyBean.class);
|
||||
this.beanDefinition.setInitMethodName("i1");
|
||||
testCompiledResult((actual, compiled) -> assertThat(actual.getInitMethodNames())
|
||||
.containsExactly("i1"));
|
||||
assertHasMethodInvokeHints("i1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void setInitMethodWhenMultipleInitMethods() {
|
||||
this.beanDefinition.setTargetType(InitDestroyBean.class);
|
||||
this.beanDefinition.setInitMethodNames("i1", "i2");
|
||||
testCompiledResult((actual, compiled) -> assertThat(actual.getInitMethodNames())
|
||||
.containsExactly("i1", "i2"));
|
||||
assertHasMethodInvokeHints("i1", "i2");
|
||||
}
|
||||
|
||||
@Test
|
||||
void setDestroyMethodWhenDestroyInitMethod() {
|
||||
this.beanDefinition.setTargetType(InitDestroyBean.class);
|
||||
this.beanDefinition.setDestroyMethodName("d1");
|
||||
testCompiledResult(
|
||||
(actual, compiled) -> assertThat(actual.getDestroyMethodNames())
|
||||
.containsExactly("d1"));
|
||||
assertHasMethodInvokeHints("d1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void setDestroyMethodWhenMultipleDestroyMethods() {
|
||||
this.beanDefinition.setTargetType(InitDestroyBean.class);
|
||||
this.beanDefinition.setDestroyMethodNames("d1", "d2");
|
||||
testCompiledResult(
|
||||
(actual, compiled) -> assertThat(actual.getDestroyMethodNames())
|
||||
.containsExactly("d1", "d2"));
|
||||
assertHasMethodInvokeHints("d1", "d2");
|
||||
}
|
||||
|
||||
private void assertHasMethodInvokeHints(String... methodNames) {
|
||||
assertThat(hints.reflection().getTypeHint(InitDestroyBean.class))
|
||||
.satisfies(typeHint -> {
|
||||
for (String methodName : methodNames) {
|
||||
assertThat(typeHint.methods()).anySatisfy(methodHint -> {
|
||||
assertThat(methodHint.getName()).isEqualTo(methodName);
|
||||
assertThat(methodHint.getModes())
|
||||
.containsExactly(ExecutableMode.INVOKE);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void constructorArgumentValuesWhenValues() {
|
||||
this.beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0,
|
||||
String.class);
|
||||
this.beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(1,
|
||||
"test");
|
||||
this.beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(2,
|
||||
123);
|
||||
testCompiledResult((actual, compiled) -> {
|
||||
Map<Integer, ValueHolder> values = actual.getConstructorArgumentValues()
|
||||
.getIndexedArgumentValues();
|
||||
assertThat(values.get(0).getValue()).isEqualTo(String.class);
|
||||
assertThat(values.get(1).getValue()).isEqualTo("test");
|
||||
assertThat(values.get(2).getValue()).isEqualTo(123);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void propertyValuesWhenValues() {
|
||||
this.beanDefinition.getPropertyValues().add("test", String.class);
|
||||
this.beanDefinition.getPropertyValues().add("spring", "framework");
|
||||
testCompiledResult((actual, compiled) -> {
|
||||
assertThat(actual.getPropertyValues().get("test")).isEqualTo(String.class);
|
||||
assertThat(actual.getPropertyValues().get("spring")).isEqualTo("framework");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void propertyValuesWhenContainsBeanReference() {
|
||||
this.beanDefinition.getPropertyValues().add("myService",
|
||||
new RuntimeBeanNameReference("test"));
|
||||
testCompiledResult((actual, compiled) -> {
|
||||
assertThat(actual.getPropertyValues().contains("myService")).isTrue();
|
||||
assertThat(actual.getPropertyValues().get("myService"))
|
||||
.isInstanceOfSatisfying(RuntimeBeanReference.class,
|
||||
beanReference -> assertThat(beanReference.getBeanName())
|
||||
.isEqualTo("test"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void propertyValuesWhenContainsManagedList() {
|
||||
ManagedList<Object> managedList = new ManagedList<>();
|
||||
managedList.add(new RuntimeBeanNameReference("test"));
|
||||
this.beanDefinition.getPropertyValues().add("value", managedList);
|
||||
testCompiledResult((actual, compiled) -> {
|
||||
Object value = actual.getPropertyValues().get("value");
|
||||
assertThat(value).isInstanceOf(ManagedList.class);
|
||||
assertThat(((List<?>) value).get(0)).isInstanceOf(BeanReference.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void propertyValuesWhenContainsManagedSet() {
|
||||
ManagedSet<Object> managedSet = new ManagedSet<>();
|
||||
managedSet.add(new RuntimeBeanNameReference("test"));
|
||||
this.beanDefinition.getPropertyValues().add("value", managedSet);
|
||||
testCompiledResult((actual, compiled) -> {
|
||||
Object value = actual.getPropertyValues().get("value");
|
||||
assertThat(value).isInstanceOf(ManagedSet.class);
|
||||
assertThat(((Set<?>) value).iterator().next())
|
||||
.isInstanceOf(BeanReference.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void propertyValuesWhenContainsManagedMap() {
|
||||
ManagedMap<String, Object> managedMap = new ManagedMap<>();
|
||||
managedMap.put("test", new RuntimeBeanNameReference("test"));
|
||||
this.beanDefinition.getPropertyValues().add("value", managedMap);
|
||||
testCompiledResult((actual, compiled) -> {
|
||||
Object value = actual.getPropertyValues().get("value");
|
||||
assertThat(value).isInstanceOf(ManagedMap.class);
|
||||
assertThat(((Map<?, ?>) value).get("test")).isInstanceOf(BeanReference.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void attributesWhenAllFiltered() {
|
||||
this.beanDefinition.setAttribute("a", "A");
|
||||
this.beanDefinition.setAttribute("b", "B");
|
||||
Predicate<String> attributeFilter = attribute -> false;
|
||||
this.generator = new BeanDefinitionPropertiesCodeGenerator(this.hints,
|
||||
attributeFilter, this.generatedMethods, (name, value) -> null);
|
||||
testCompiledResult((actual, compiled) -> {
|
||||
assertThat(compiled.getSourceFile()).doesNotContain("setAttribute");
|
||||
assertThat(actual.getAttribute("a")).isNull();
|
||||
assertThat(actual.getAttribute("b")).isNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void attributesWhenSomeFiltered() {
|
||||
this.beanDefinition.setAttribute("a", "A");
|
||||
this.beanDefinition.setAttribute("b", "B");
|
||||
Predicate<String> attributeFilter = attribute -> "a".equals(attribute);
|
||||
this.generator = new BeanDefinitionPropertiesCodeGenerator(this.hints,
|
||||
attributeFilter, this.generatedMethods, (name, value) -> null);
|
||||
testCompiledResult(this.beanDefinition, (actual, compiled) -> {
|
||||
assertThat(actual.getAttribute("a")).isEqualTo("A");
|
||||
assertThat(actual.getAttribute("b")).isNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void multipleItems() {
|
||||
this.beanDefinition.setPrimary(true);
|
||||
this.beanDefinition.setScope("test");
|
||||
this.beanDefinition.setRole(BeanDefinition.ROLE_SUPPORT);
|
||||
testCompiledResult((actual, compiled) -> {
|
||||
assertThat(actual.isPrimary()).isTrue();
|
||||
assertThat(actual.getScope()).isEqualTo("test");
|
||||
assertThat(actual.getRole()).isEqualTo(BeanDefinition.ROLE_SUPPORT);
|
||||
});
|
||||
}
|
||||
|
||||
private void testCompiledResult(BiConsumer<RootBeanDefinition, Compiled> result) {
|
||||
testCompiledResult(this.beanDefinition, result);
|
||||
}
|
||||
|
||||
private void testCompiledResult(RootBeanDefinition beanDefinition,
|
||||
BiConsumer<RootBeanDefinition, Compiled> result) {
|
||||
testCompiledResult(() -> this.generator.generateCode(beanDefinition), result);
|
||||
}
|
||||
|
||||
private void testCompiledResult(Supplier<CodeBlock> codeBlock,
|
||||
BiConsumer<RootBeanDefinition, Compiled> result) {
|
||||
JavaFile javaFile = createJavaFile(codeBlock);
|
||||
TestCompiler.forSystem().compile(javaFile::writeTo, compiled -> {
|
||||
RootBeanDefinition beanDefinition = (RootBeanDefinition) compiled
|
||||
.getInstance(Supplier.class).get();
|
||||
result.accept(beanDefinition, compiled);
|
||||
});
|
||||
}
|
||||
|
||||
private JavaFile createJavaFile(Supplier<CodeBlock> codeBlock) {
|
||||
TypeSpec.Builder builder = TypeSpec.classBuilder("BeanSupplier");
|
||||
builder.addModifiers(Modifier.PUBLIC);
|
||||
builder.addSuperinterface(
|
||||
ParameterizedTypeName.get(Supplier.class, RootBeanDefinition.class));
|
||||
builder.addMethod(MethodSpec.methodBuilder("get").addModifiers(Modifier.PUBLIC)
|
||||
.returns(RootBeanDefinition.class)
|
||||
.addStatement("$T beanDefinition = new $T()", RootBeanDefinition.class,
|
||||
RootBeanDefinition.class)
|
||||
.addStatement("$T beanFactory = new $T()",
|
||||
DefaultListableBeanFactory.class,
|
||||
DefaultListableBeanFactory.class)
|
||||
.addCode(codeBlock.get()).addStatement("return beanDefinition").build());
|
||||
this.generatedMethods.doWithMethodSpecs(builder::addMethod);
|
||||
return JavaFile.builder("com.example", builder.build()).build();
|
||||
}
|
||||
|
||||
static class InitDestroyBean {
|
||||
|
||||
void i1() {
|
||||
}
|
||||
|
||||
void i2() {
|
||||
}
|
||||
|
||||
void d1() {
|
||||
}
|
||||
|
||||
void d2() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,483 @@
|
||||
/*
|
||||
* 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.aot;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import javax.lang.model.element.Modifier;
|
||||
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aot.generate.GeneratedMethods;
|
||||
import org.springframework.aot.test.generator.compile.Compiled;
|
||||
import org.springframework.aot.test.generator.compile.TestCompiler;
|
||||
import org.springframework.aot.test.generator.file.SourceFile;
|
||||
import org.springframework.beans.factory.config.BeanReference;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanNameReference;
|
||||
import org.springframework.beans.factory.support.ManagedList;
|
||||
import org.springframework.beans.factory.support.ManagedMap;
|
||||
import org.springframework.beans.factory.support.ManagedSet;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.javapoet.CodeBlock;
|
||||
import org.springframework.javapoet.JavaFile;
|
||||
import org.springframework.javapoet.MethodSpec;
|
||||
import org.springframework.javapoet.ParameterizedTypeName;
|
||||
import org.springframework.javapoet.TypeSpec;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link BeanDefinitionPropertyValueCodeGenerator}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
* @since 6.0
|
||||
* @see BeanDefinitionPropertyValueCodeGeneratorTests
|
||||
*/
|
||||
class BeanDefinitionPropertyValueCodeGeneratorTests {
|
||||
|
||||
private GeneratedMethods generatedMethods = new GeneratedMethods();
|
||||
|
||||
private BeanDefinitionPropertyValueCodeGenerator instance = new BeanDefinitionPropertyValueCodeGenerator(
|
||||
generatedMethods);
|
||||
|
||||
private void compile(Object value, BiConsumer<Object, Compiled> result) {
|
||||
CodeBlock code = instance.generateCode(value);
|
||||
JavaFile javaFile = createJavaFile(code);
|
||||
TestCompiler.forSystem().compile(SourceFile.of(javaFile::writeTo),
|
||||
compiled -> result.accept(compiled.getInstance(Supplier.class).get(),
|
||||
compiled));
|
||||
}
|
||||
|
||||
private JavaFile createJavaFile(CodeBlock code) {
|
||||
TypeSpec.Builder builder = TypeSpec.classBuilder("InstanceSupplier");
|
||||
builder.addModifiers(Modifier.PUBLIC);
|
||||
builder.addSuperinterface(
|
||||
ParameterizedTypeName.get(Supplier.class, Object.class));
|
||||
builder.addMethod(MethodSpec.methodBuilder("get").addModifiers(Modifier.PUBLIC)
|
||||
.returns(Object.class).addStatement("return $L", code).build());
|
||||
generatedMethods.doWithMethodSpecs(builder::addMethod);
|
||||
return JavaFile.builder("com.example", builder.build()).build();
|
||||
}
|
||||
|
||||
@Nested
|
||||
class NullTests {
|
||||
|
||||
@Test
|
||||
void generateWhenNull() {
|
||||
compile(null, (instance, compiled) -> assertThat(instance).isNull());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
class PrimitiveTests {
|
||||
|
||||
@Test
|
||||
void generateWhenBoolean() {
|
||||
compile(true, (instance, compiled) -> {
|
||||
assertThat(instance).isEqualTo(Boolean.TRUE);
|
||||
assertThat(compiled.getSourceFile()).contains("true");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenByte() {
|
||||
compile((byte) 2, (instance, compiled) -> {
|
||||
assertThat(instance).isEqualTo((byte) 2);
|
||||
assertThat(compiled.getSourceFile()).contains("(byte) 2");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenShort() {
|
||||
compile((short) 3, (instance, compiled) -> {
|
||||
assertThat(instance).isEqualTo((short) 3);
|
||||
assertThat(compiled.getSourceFile()).contains("(short) 3");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenInt() {
|
||||
compile(4, (instance, compiled) -> {
|
||||
assertThat(instance).isEqualTo(4);
|
||||
assertThat(compiled.getSourceFile()).contains("return 4;");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenLong() {
|
||||
compile(5L, (instance, compiled) -> {
|
||||
assertThat(instance).isEqualTo(5L);
|
||||
assertThat(compiled.getSourceFile()).contains("5L");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenFloat() {
|
||||
compile(0.1F, (instance, compiled) -> {
|
||||
assertThat(instance).isEqualTo(0.1F);
|
||||
assertThat(compiled.getSourceFile()).contains("0.1F");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenDouble() {
|
||||
compile(0.2, (instance, compiled) -> {
|
||||
assertThat(instance).isEqualTo(0.2);
|
||||
assertThat(compiled.getSourceFile()).contains("(double) 0.2");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenChar() {
|
||||
compile('a', (instance, compiled) -> {
|
||||
assertThat(instance).isEqualTo('a');
|
||||
assertThat(compiled.getSourceFile()).contains("'a'");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenSimpleEscapedCharReturnsEscaped() {
|
||||
testEscaped('\b', "'\\b'");
|
||||
testEscaped('\t', "'\\t'");
|
||||
testEscaped('\n', "'\\n'");
|
||||
testEscaped('\f', "'\\f'");
|
||||
testEscaped('\r', "'\\r'");
|
||||
testEscaped('\"', "'\"'");
|
||||
testEscaped('\'', "'\\''");
|
||||
testEscaped('\\', "'\\\\'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generatedWhenUnicodeEscapedCharReturnsEscaped() {
|
||||
testEscaped('\u007f', "'\\u007f'");
|
||||
}
|
||||
|
||||
private void testEscaped(char value, String expectedSourceContent) {
|
||||
compile(value, (instance, compiled) -> {
|
||||
assertThat(instance).isEqualTo(value);
|
||||
assertThat(compiled.getSourceFile()).contains(expectedSourceContent);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
class StringTests {
|
||||
|
||||
@Test
|
||||
void generateWhenString() {
|
||||
compile("test\n", (instance, compiled) -> {
|
||||
assertThat(instance).isEqualTo("test\n");
|
||||
assertThat(compiled.getSourceFile()).contains("\n");
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
class EnumTests {
|
||||
|
||||
@Test
|
||||
void generateWhenEnum() {
|
||||
compile(ChronoUnit.DAYS, (instance, compiled) -> {
|
||||
assertThat(instance).isEqualTo(ChronoUnit.DAYS);
|
||||
assertThat(compiled.getSourceFile()).contains("ChronoUnit.DAYS");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenEnumWithClassBody() {
|
||||
compile(EnumWithClassBody.TWO, (instance, compiled) -> {
|
||||
assertThat(instance).isEqualTo(EnumWithClassBody.TWO);
|
||||
assertThat(compiled.getSourceFile()).contains("EnumWithClassBody.TWO");
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
class ClassTests {
|
||||
|
||||
@Test
|
||||
void generateWhenClass() {
|
||||
compile(InputStream.class, (instance, compiled) -> assertThat(instance)
|
||||
.isEqualTo(InputStream.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenCglibClass() {
|
||||
compile(ExampleClass$$GeneratedBy.class, (instance,
|
||||
compiled) -> assertThat(instance).isEqualTo(ExampleClass.class));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
class ResolvableTypeTests {
|
||||
|
||||
@Test
|
||||
void generateWhenSimpleResolvableType() {
|
||||
ResolvableType resolvableType = ResolvableType.forClass(String.class);
|
||||
compile(resolvableType, (instance, compiled) -> assertThat(instance)
|
||||
.isEqualTo(resolvableType));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenNoneResolvableType() {
|
||||
ResolvableType resolvableType = ResolvableType.NONE;
|
||||
compile(resolvableType, (instance, compiled) -> {
|
||||
assertThat(instance).isEqualTo(resolvableType);
|
||||
assertThat(compiled.getSourceFile()).contains("ResolvableType.NONE");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenGenericResolvableType() {
|
||||
ResolvableType resolvableType = ResolvableType
|
||||
.forClassWithGenerics(List.class, String.class);
|
||||
compile(resolvableType, (instance, compiled) -> assertThat(instance)
|
||||
.isEqualTo(resolvableType));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenNestedGenericResolvableType() {
|
||||
ResolvableType stringList = ResolvableType.forClassWithGenerics(List.class,
|
||||
String.class);
|
||||
ResolvableType resolvableType = ResolvableType.forClassWithGenerics(Map.class,
|
||||
ResolvableType.forClass(Integer.class), stringList);
|
||||
compile(resolvableType, (instance, compiled) -> assertThat(instance)
|
||||
.isEqualTo(resolvableType));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
class ArrayTests {
|
||||
|
||||
@Test
|
||||
void generateWhenPrimitiveArray() {
|
||||
byte[] bytes = { 0, 1, 2 };
|
||||
compile(bytes, (instance, compiler) -> {
|
||||
assertThat(instance).isEqualTo(bytes);
|
||||
assertThat(compiler.getSourceFile()).contains("new byte[]");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenWrapperArray() {
|
||||
Byte[] bytes = { 0, 1, 2 };
|
||||
compile(bytes, (instance, compiler) -> {
|
||||
assertThat(instance).isEqualTo(bytes);
|
||||
assertThat(compiler.getSourceFile()).contains("new Byte[]");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenClassArray() {
|
||||
Class<?>[] classes = new Class<?>[] { InputStream.class, OutputStream.class };
|
||||
compile(classes, (instance, compiler) -> {
|
||||
assertThat(instance).isEqualTo(classes);
|
||||
assertThat(compiler.getSourceFile()).contains("new Class[]");
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
class ManagedListTests {
|
||||
|
||||
@Test
|
||||
void generateWhenStringManagedList() {
|
||||
ManagedList<String> list = new ManagedList<>();
|
||||
list.add("a");
|
||||
list.add("b");
|
||||
list.add("c");
|
||||
compile(list, (instance, compiler) -> assertThat(instance).isEqualTo(list)
|
||||
.isInstanceOf(ManagedList.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenEmptyManagedList() {
|
||||
ManagedList<String> list = new ManagedList<>();
|
||||
compile(list, (instance, compiler) -> assertThat(instance).isEqualTo(list)
|
||||
.isInstanceOf(ManagedList.class));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
class ManagedSetTests {
|
||||
|
||||
@Test
|
||||
void generateWhenStringManagedSet() {
|
||||
ManagedSet<String> set = new ManagedSet<>();
|
||||
set.add("a");
|
||||
set.add("b");
|
||||
set.add("c");
|
||||
compile(set, (instance, compiler) -> assertThat(instance).isEqualTo(set)
|
||||
.isInstanceOf(ManagedSet.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenEmptyManagedSet() {
|
||||
ManagedSet<String> set = new ManagedSet<>();
|
||||
compile(set, (instance, compiler) -> assertThat(instance).isEqualTo(set)
|
||||
.isInstanceOf(ManagedSet.class));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
class ManagedMapTests {
|
||||
|
||||
@Test
|
||||
void generateWhenManagedMap() {
|
||||
ManagedMap<String, String> map = new ManagedMap<>();
|
||||
map.put("k1", "v1");
|
||||
map.put("k2", "v2");
|
||||
compile(map, (instance, compiler) -> assertThat(instance).isEqualTo(map)
|
||||
.isInstanceOf(ManagedMap.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenEmptyManagedMap() {
|
||||
ManagedMap<String, String> map = new ManagedMap<>();
|
||||
compile(map, (instance, compiler) -> assertThat(instance).isEqualTo(map)
|
||||
.isInstanceOf(ManagedMap.class));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
class ListTests {
|
||||
|
||||
@Test
|
||||
void generateWhenStringList() {
|
||||
List<String> list = List.of("a", "b", "c");
|
||||
compile(list, (instance, compiler) -> assertThat(instance).isEqualTo(list)
|
||||
.isNotInstanceOf(ManagedList.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenEmptyList() {
|
||||
List<String> list = List.of();
|
||||
compile(list, (instance, compiler) -> {
|
||||
assertThat(instance).isEqualTo(list);
|
||||
assertThat(compiler.getSourceFile()).contains("Collections.emptyList();");
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
class SetTests {
|
||||
|
||||
@Test
|
||||
void generateWhenStringSet() {
|
||||
Set<String> set = Set.of("a", "b", "c");
|
||||
compile(set, (instance, compiler) -> assertThat(instance).isEqualTo(set)
|
||||
.isNotInstanceOf(ManagedSet.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenEmptySet() {
|
||||
Set<String> set = Set.of();
|
||||
compile(set, (instance, compiler) -> {
|
||||
assertThat(instance).isEqualTo(set);
|
||||
assertThat(compiler.getSourceFile()).contains("Collections.emptySet();");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenLinkedHashSet() {
|
||||
Set<String> set = new LinkedHashSet<>(List.of("a", "b", "c"));
|
||||
compile(set, (instance, compiler) -> {
|
||||
assertThat(instance).isEqualTo(set).isInstanceOf(LinkedHashSet.class);
|
||||
assertThat(compiler.getSourceFile())
|
||||
.contains("new LinkedHashSet(List.of(");
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
class MapTests {
|
||||
|
||||
@Test
|
||||
void generateWhenSmallMap() {
|
||||
Map<String, String> map = Map.of("k1", "v1", "k2", "v2");
|
||||
compile(map, (instance, compiler) -> {
|
||||
assertThat(instance).isEqualTo(map);
|
||||
assertThat(compiler.getSourceFile()).contains("Map.of(");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenMapWithOverTenElements() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
for (int i = 1; i <= 11; i++) {
|
||||
map.put("k" + i, "v" + i);
|
||||
}
|
||||
compile(map, (instance, compiler) -> {
|
||||
assertThat(instance).isEqualTo(map);
|
||||
assertThat(compiler.getSourceFile()).contains("Map.ofEntries(");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenLinkedHashMap() {
|
||||
Map<String, String> map = new LinkedHashMap<>();
|
||||
map.put("a", "A");
|
||||
map.put("b", "B");
|
||||
map.put("c", "C");
|
||||
compile(map, (instance, compiler) -> {
|
||||
assertThat(instance).isEqualTo(map).isInstanceOf(LinkedHashMap.class);
|
||||
assertThat(compiler.getSourceFile()).contains("getMap()");
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
class BeanReferenceTests {
|
||||
|
||||
@Test
|
||||
void generatedWhenBeanReference() {
|
||||
BeanReference beanReference = new RuntimeBeanNameReference("test");
|
||||
compile(beanReference,
|
||||
(instance,
|
||||
compiler) -> assertThat(
|
||||
((BeanReference) instance).getBeanName())
|
||||
.isEqualTo(beanReference.getBeanName()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* 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.aot;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.lang.model.element.Modifier;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aot.generate.DefaultGenerationContext;
|
||||
import org.springframework.aot.generate.GeneratedMethods;
|
||||
import org.springframework.aot.generate.GenerationContext;
|
||||
import org.springframework.aot.generate.InMemoryGeneratedFiles;
|
||||
import org.springframework.aot.generate.MethodGenerator;
|
||||
import org.springframework.aot.generate.MethodReference;
|
||||
import org.springframework.aot.test.generator.compile.Compiled;
|
||||
import org.springframework.aot.test.generator.compile.TestCompiler;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.RegisteredBean;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.testfixture.beans.TestBean;
|
||||
import org.springframework.core.mock.MockSpringFactoriesLoader;
|
||||
import org.springframework.javapoet.CodeBlock;
|
||||
import org.springframework.javapoet.JavaFile;
|
||||
import org.springframework.javapoet.MethodSpec;
|
||||
import org.springframework.javapoet.ParameterizedTypeName;
|
||||
import org.springframework.javapoet.TypeSpec;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link BeanRegistrationsAotContribution}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class BeanRegistrationsAotContributionTests {
|
||||
|
||||
private InMemoryGeneratedFiles generatedFiles;
|
||||
|
||||
private DefaultGenerationContext generationContext;
|
||||
|
||||
private DefaultListableBeanFactory beanFactory;
|
||||
|
||||
private MockSpringFactoriesLoader springFactoriesLoader;
|
||||
|
||||
private BeanDefinitionMethodGeneratorFactory methodGeneratorFactory;
|
||||
|
||||
private final MockBeanFactoryInitializationCode beanFactoryInitializationCode = new MockBeanFactoryInitializationCode();
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
this.generatedFiles = new InMemoryGeneratedFiles();
|
||||
this.generationContext = new DefaultGenerationContext(this.generatedFiles);
|
||||
this.beanFactory = new DefaultListableBeanFactory();
|
||||
this.springFactoriesLoader = new MockSpringFactoriesLoader();
|
||||
this.methodGeneratorFactory = new BeanDefinitionMethodGeneratorFactory(
|
||||
new AotFactoriesLoader(this.beanFactory, this.springFactoriesLoader));
|
||||
}
|
||||
|
||||
@Test
|
||||
void applyToAppliesContribution() {
|
||||
Map<String, BeanDefinitionMethodGenerator> registrations = new LinkedHashMap<>();
|
||||
RegisteredBean registeredBean = registerBean(
|
||||
new RootBeanDefinition(TestBean.class));
|
||||
BeanDefinitionMethodGenerator generator = new BeanDefinitionMethodGenerator(
|
||||
this.methodGeneratorFactory, registeredBean, null,
|
||||
Collections.emptyList(), Collections.emptyList());
|
||||
registrations.put("testBean", generator);
|
||||
BeanRegistrationsAotContribution contribution = new BeanRegistrationsAotContribution(
|
||||
registrations);
|
||||
contribution.applyTo(this.generationContext, this.beanFactoryInitializationCode);
|
||||
testCompiledResult((consumer, compiled) -> {
|
||||
DefaultListableBeanFactory freshBeanFactory = new DefaultListableBeanFactory();
|
||||
consumer.accept(freshBeanFactory);
|
||||
assertThat(freshBeanFactory.getBean(TestBean.class)).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void applyToCallsRegistrationsWithBeanRegistrationsCode() {
|
||||
List<BeanRegistrationsCode> beanRegistrationsCodes = new ArrayList<>();
|
||||
Map<String, BeanDefinitionMethodGenerator> registrations = new LinkedHashMap<>();
|
||||
RegisteredBean registeredBean = registerBean(
|
||||
new RootBeanDefinition(TestBean.class));
|
||||
BeanDefinitionMethodGenerator generator = new BeanDefinitionMethodGenerator(
|
||||
this.methodGeneratorFactory, registeredBean, null,
|
||||
Collections.emptyList(), Collections.emptyList()) {
|
||||
|
||||
@Override
|
||||
MethodReference generateBeanDefinitionMethod(
|
||||
GenerationContext generationContext,
|
||||
BeanRegistrationsCode beanRegistrationsCode) {
|
||||
beanRegistrationsCodes.add(beanRegistrationsCode);
|
||||
return super.generateBeanDefinitionMethod(generationContext,
|
||||
beanRegistrationsCode);
|
||||
}
|
||||
|
||||
};
|
||||
registrations.put("testBean", generator);
|
||||
BeanRegistrationsAotContribution contribution = new BeanRegistrationsAotContribution(
|
||||
registrations);
|
||||
contribution.applyTo(this.generationContext, this.beanFactoryInitializationCode);
|
||||
assertThat(beanRegistrationsCodes).hasSize(1);
|
||||
BeanRegistrationsCode actual = beanRegistrationsCodes.get(0);
|
||||
assertThat(actual.getMethodGenerator()).isNotNull();
|
||||
}
|
||||
|
||||
private RegisteredBean registerBean(RootBeanDefinition rootBeanDefinition) {
|
||||
String beanName = "testBean";
|
||||
this.beanFactory.registerBeanDefinition(beanName, rootBeanDefinition);
|
||||
return RegisteredBean.of(this.beanFactory, beanName);
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "cast" })
|
||||
private void testCompiledResult(
|
||||
BiConsumer<Consumer<DefaultListableBeanFactory>, Compiled> result) {
|
||||
this.generationContext.writeGeneratedContent();
|
||||
JavaFile javaFile = createJavaFile();
|
||||
TestCompiler.forSystem().withFiles(this.generatedFiles).compile(javaFile::writeTo,
|
||||
compiled -> result.accept(compiled.getInstance(Consumer.class),
|
||||
compiled));
|
||||
}
|
||||
|
||||
private JavaFile createJavaFile() {
|
||||
MethodReference initializer = this.beanFactoryInitializationCode.initializers
|
||||
.get(0);
|
||||
TypeSpec.Builder builder = TypeSpec.classBuilder("BeanFactoryConsumer");
|
||||
builder.addModifiers(Modifier.PUBLIC);
|
||||
builder.addSuperinterface(ParameterizedTypeName.get(Consumer.class,
|
||||
DefaultListableBeanFactory.class));
|
||||
builder.addMethod(MethodSpec.methodBuilder("accept").addModifiers(Modifier.PUBLIC)
|
||||
.addParameter(DefaultListableBeanFactory.class, "beanFactory")
|
||||
.addStatement(initializer.toInvokeCodeBlock(CodeBlock.of("beanFactory")))
|
||||
.build());
|
||||
return JavaFile.builder("__", builder.build()).build();
|
||||
}
|
||||
|
||||
class MockBeanFactoryInitializationCode implements BeanFactoryInitializationCode {
|
||||
|
||||
private final GeneratedMethods generatedMethods = new GeneratedMethods();
|
||||
|
||||
private final List<MethodReference> initializers = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public MethodGenerator getMethodGenerator() {
|
||||
return this.generatedMethods;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInitializer(MethodReference methodReference) {
|
||||
this.initializers.add(methodReference);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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.aot;
|
||||
|
||||
import org.assertj.core.api.InstanceOfAssertFactories;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.testfixture.beans.AnnotatedBean;
|
||||
import org.springframework.beans.testfixture.beans.TestBean;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link BeanRegistrationsAotProcessor}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class BeanRegistrationsAotProcessorTests {
|
||||
|
||||
@Test
|
||||
void processAheadOfTimeReturnsBeanRegistrationsAotContributionWithRegistrations() {
|
||||
BeanRegistrationsAotProcessor processor = new BeanRegistrationsAotProcessor();
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerBeanDefinition("b1", new RootBeanDefinition(TestBean.class));
|
||||
beanFactory.registerBeanDefinition("b2",
|
||||
new RootBeanDefinition(AnnotatedBean.class));
|
||||
BeanRegistrationsAotContribution contribution = processor
|
||||
.processAheadOfTime(beanFactory);
|
||||
assertThat(contribution).extracting("registrations")
|
||||
.asInstanceOf(InstanceOfAssertFactories.MAP).containsKeys("b1", "b2");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,499 @@
|
||||
/*
|
||||
* 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.aot;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Executable;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
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.factory.NumberHolder;
|
||||
import org.springframework.beans.testfixture.beans.factory.generator.factory.NumberHolderFactoryBean;
|
||||
import org.springframework.beans.testfixture.beans.factory.generator.factory.SampleFactory;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.annotation.MergedAnnotations.SearchStrategy;
|
||||
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.assertThatIllegalStateException;
|
||||
|
||||
/**
|
||||
* Tests for {@link ConstructorOrFactoryMethodResolver}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class ConstructorOrFactoryMethodResolverTests {
|
||||
|
||||
@Test
|
||||
void detectBeanInstanceExecutableWithBeanClassAndFactoryMethodName() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerSingleton("testBean", "test");
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(SampleFactory.class).setFactoryMethod("create")
|
||||
.addConstructorArgReference("testBean").getBeanDefinition();
|
||||
Executable executable = resolve(beanFactory, beanDefinition);
|
||||
assertThat(executable).isNotNull().isEqualTo(
|
||||
ReflectionUtils.findMethod(SampleFactory.class, "create", String.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void detectBeanInstanceExecutableWithBeanClassNameAndFactoryMethodName() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerSingleton("testBean", "test");
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(SampleFactory.class.getName())
|
||||
.setFactoryMethod("create").addConstructorArgReference("testBean")
|
||||
.getBeanDefinition();
|
||||
Executable executable = resolve(beanFactory, beanDefinition);
|
||||
assertThat(executable).isNotNull().isEqualTo(
|
||||
ReflectionUtils.findMethod(SampleFactory.class, "create", String.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void beanDefinitionWithFactoryMethodNameAndAssignableConstructorArg() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerSingleton("testNumber", 1L);
|
||||
beanFactory.registerSingleton("testBean", "test");
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(SampleFactory.class).setFactoryMethod("create")
|
||||
.addConstructorArgReference("testNumber")
|
||||
.addConstructorArgReference("testBean").getBeanDefinition();
|
||||
Executable executable = resolve(beanFactory, beanDefinition);
|
||||
assertThat(executable).isNotNull().isEqualTo(ReflectionUtils
|
||||
.findMethod(SampleFactory.class, "create", Number.class, String.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void beanDefinitionWithFactoryMethodNameAndMatchingMethodNamesThatShouldBeIgnored() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(DummySampleFactory.class).setFactoryMethod("of")
|
||||
.addConstructorArgValue(42).getBeanDefinition();
|
||||
Executable executable = resolve(beanFactory, beanDefinition);
|
||||
assertThat(executable).isNotNull().isEqualTo(ReflectionUtils
|
||||
.findMethod(DummySampleFactory.class, "of", Integer.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void detectBeanInstanceExecutableWithBeanClassAndFactoryMethodNameIgnoreTargetType() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerSingleton("testBean", "test");
|
||||
RootBeanDefinition beanDefinition = (RootBeanDefinition) BeanDefinitionBuilder
|
||||
.rootBeanDefinition(SampleFactory.class).setFactoryMethod("create")
|
||||
.addConstructorArgReference("testBean").getBeanDefinition();
|
||||
beanDefinition.setTargetType(String.class);
|
||||
Executable executable = resolve(beanFactory, beanDefinition);
|
||||
assertThat(executable).isNotNull().isEqualTo(
|
||||
ReflectionUtils.findMethod(SampleFactory.class, "create", String.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void beanDefinitionWithConstructorArgsForMultipleConstructors() throws Exception {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerSingleton("testNumber", 1L);
|
||||
beanFactory.registerSingleton("testBean", "test");
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(SampleBeanWithConstructors.class)
|
||||
.addConstructorArgReference("testNumber")
|
||||
.addConstructorArgReference("testBean").getBeanDefinition();
|
||||
Executable executable = resolve(beanFactory, beanDefinition);
|
||||
assertThat(executable).isNotNull().isEqualTo(SampleBeanWithConstructors.class
|
||||
.getDeclaredConstructor(Number.class, String.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void genericBeanDefinitionWithConstructorArgsForMultipleConstructors()
|
||||
throws Exception {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerSingleton("testNumber", 1L);
|
||||
beanFactory.registerSingleton("testBean", "test");
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.genericBeanDefinition(SampleBeanWithConstructors.class)
|
||||
.addConstructorArgReference("testNumber")
|
||||
.addConstructorArgReference("testBean").getBeanDefinition();
|
||||
Executable executable = resolve(beanFactory, beanDefinition);
|
||||
assertThat(executable).isNotNull().isEqualTo(SampleBeanWithConstructors.class
|
||||
.getDeclaredConstructor(Number.class, String.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void beanDefinitionWithMultiArgConstructorAndMatchingValue()
|
||||
throws NoSuchMethodException {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(MultiConstructorSample.class)
|
||||
.addConstructorArgValue(42).getBeanDefinition();
|
||||
Executable executable = resolve(new DefaultListableBeanFactory(), beanDefinition);
|
||||
assertThat(executable).isNotNull().isEqualTo(
|
||||
MultiConstructorSample.class.getDeclaredConstructor(Integer.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void beanDefinitionWithMultiArgConstructorAndMatchingArrayValue()
|
||||
throws NoSuchMethodException {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(MultiConstructorArraySample.class)
|
||||
.addConstructorArgValue(42).getBeanDefinition();
|
||||
Executable executable = resolve(new DefaultListableBeanFactory(), beanDefinition);
|
||||
assertThat(executable).isNotNull().isEqualTo(MultiConstructorArraySample.class
|
||||
.getDeclaredConstructor(Integer[].class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void beanDefinitionWithMultiArgConstructorAndMatchingListValue()
|
||||
throws NoSuchMethodException {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(MultiConstructorListSample.class)
|
||||
.addConstructorArgValue(42).getBeanDefinition();
|
||||
Executable executable = resolve(new DefaultListableBeanFactory(), beanDefinition);
|
||||
assertThat(executable).isNotNull().isEqualTo(
|
||||
MultiConstructorListSample.class.getDeclaredConstructor(List.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void beanDefinitionWithMultiArgConstructorAndMatchingValueAsInnerBean()
|
||||
throws NoSuchMethodException {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(MultiConstructorSample.class)
|
||||
.addConstructorArgValue(
|
||||
BeanDefinitionBuilder.rootBeanDefinition(Integer.class, "valueOf")
|
||||
.addConstructorArgValue("42").getBeanDefinition())
|
||||
.getBeanDefinition();
|
||||
Executable executable = resolve(new DefaultListableBeanFactory(), beanDefinition);
|
||||
assertThat(executable).isNotNull().isEqualTo(
|
||||
MultiConstructorSample.class.getDeclaredConstructor(Integer.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void beanDefinitionWithMultiArgConstructorAndMatchingValueAsInnerBeanFactory()
|
||||
throws NoSuchMethodException {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(MultiConstructorSample.class)
|
||||
.addConstructorArgValue(BeanDefinitionBuilder
|
||||
.rootBeanDefinition(IntegerFactoryBean.class).getBeanDefinition())
|
||||
.getBeanDefinition();
|
||||
Executable executable = resolve(new DefaultListableBeanFactory(), beanDefinition);
|
||||
assertThat(executable).isNotNull().isEqualTo(
|
||||
MultiConstructorSample.class.getDeclaredConstructor(Integer.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void beanDefinitionWithMultiArgConstructorAndNonMatchingValue() {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(MultiConstructorSample.class)
|
||||
.addConstructorArgValue(Locale.ENGLISH).getBeanDefinition();
|
||||
Executable executable = resolve(new DefaultListableBeanFactory(), beanDefinition);
|
||||
assertThat(executable).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void beanDefinitionWithMultiArgConstructorAndNonMatchingValueAsInnerBean() {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(MultiConstructorSample.class)
|
||||
.addConstructorArgValue(BeanDefinitionBuilder
|
||||
.rootBeanDefinition(Locale.class, "getDefault")
|
||||
.getBeanDefinition())
|
||||
.getBeanDefinition();
|
||||
Executable executable = resolve(new DefaultListableBeanFactory(), beanDefinition);
|
||||
assertThat(executable).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void detectBeanInstanceExecutableWithFactoryBeanSetInBeanClass() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition();
|
||||
beanDefinition.setTargetType(
|
||||
ResolvableType.forClassWithGenerics(NumberHolder.class, Integer.class));
|
||||
beanDefinition.setBeanClass(NumberHolderFactoryBean.class);
|
||||
Executable executable = resolve(beanFactory, beanDefinition);
|
||||
assertThat(executable).isNotNull()
|
||||
.isEqualTo(NumberHolderFactoryBean.class.getDeclaredConstructors()[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
void detectBeanInstanceExecutableWithFactoryBeanSetInBeanClassAndNoResolvableType() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition();
|
||||
beanDefinition.setBeanClass(NumberHolderFactoryBean.class);
|
||||
Executable executable = resolve(beanFactory, beanDefinition);
|
||||
assertThat(executable).isNotNull()
|
||||
.isEqualTo(NumberHolderFactoryBean.class.getDeclaredConstructors()[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
void detectBeanInstanceExecutableWithFactoryBeanSetInBeanClassThatDoesNotMatchTargetType() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition();
|
||||
beanDefinition.setTargetType(
|
||||
ResolvableType.forClassWithGenerics(NumberHolder.class, String.class));
|
||||
beanDefinition.setBeanClass(NumberHolderFactoryBean.class);
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> resolve(beanFactory, beanDefinition))
|
||||
.withMessageContaining("Incompatible target type")
|
||||
.withMessageContaining(NumberHolder.class.getName())
|
||||
.withMessageContaining(NumberHolderFactoryBean.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void beanDefinitionWithClassArrayConstructorArgAndStringArrayValueType()
|
||||
throws NoSuchMethodException {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(ConstructorClassArraySample.class.getName())
|
||||
.addConstructorArgValue(new String[] { "test1, test2" })
|
||||
.getBeanDefinition();
|
||||
Executable executable = resolve(beanFactory, beanDefinition);
|
||||
assertThat(executable).isNotNull().isEqualTo(
|
||||
ConstructorClassArraySample.class.getDeclaredConstructor(Class[].class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void beanDefinitionWithClassArrayConstructorArgAndStringValueType() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(ConstructorClassArraySample.class.getName())
|
||||
.addConstructorArgValue("test1").getBeanDefinition();
|
||||
Executable executable = resolve(beanFactory, beanDefinition);
|
||||
assertThat(executable).isNotNull().isEqualTo(
|
||||
ConstructorClassArraySample.class.getDeclaredConstructors()[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
void beanDefinitionWithClassArrayConstructorArgAndAnotherMatchingConstructor()
|
||||
throws NoSuchMethodException {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(MultiConstructorClassArraySample.class.getName())
|
||||
.addConstructorArgValue(new String[] { "test1, test2" })
|
||||
.getBeanDefinition();
|
||||
Executable executable = resolve(beanFactory, beanDefinition);
|
||||
assertThat(executable).isNotNull()
|
||||
.isEqualTo(MultiConstructorClassArraySample.class
|
||||
.getDeclaredConstructor(String[].class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void beanDefinitionWithClassArrayFactoryMethodArgAndStringArrayValueType() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(ClassArrayFactoryMethodSample.class.getName())
|
||||
.setFactoryMethod("of")
|
||||
.addConstructorArgValue(new String[] { "test1, test2" })
|
||||
.getBeanDefinition();
|
||||
Executable executable = resolve(beanFactory, beanDefinition);
|
||||
assertThat(executable).isNotNull().isEqualTo(ReflectionUtils
|
||||
.findMethod(ClassArrayFactoryMethodSample.class, "of", Class[].class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void beanDefinitionWithClassArrayFactoryMethodArgAndAnotherMatchingConstructor() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(
|
||||
ClassArrayFactoryMethodSampleWithAnotherFactoryMethod.class.getName())
|
||||
.setFactoryMethod("of").addConstructorArgValue("test1")
|
||||
.getBeanDefinition();
|
||||
Executable executable = resolve(beanFactory, beanDefinition);
|
||||
assertThat(executable).isNotNull()
|
||||
.isEqualTo(ReflectionUtils.findMethod(
|
||||
ClassArrayFactoryMethodSampleWithAnotherFactoryMethod.class, "of",
|
||||
String[].class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void beanDefinitionWithMultiArgConstructorAndPrimitiveConversion()
|
||||
throws NoSuchMethodException {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(ConstructorPrimitiveFallback.class)
|
||||
.addConstructorArgValue("true").getBeanDefinition();
|
||||
Executable executable = resolve(new DefaultListableBeanFactory(), beanDefinition);
|
||||
assertThat(executable).isEqualTo(
|
||||
ConstructorPrimitiveFallback.class.getDeclaredConstructor(boolean.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void beanDefinitionWithFactoryWithOverloadedClassMethodsOnInterface() {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(FactoryWithOverloadedClassMethodsOnInterface.class)
|
||||
.setFactoryMethod("byAnnotation").addConstructorArgValue(Nullable.class)
|
||||
.getBeanDefinition();
|
||||
Executable executable = resolve(new DefaultListableBeanFactory(), beanDefinition);
|
||||
assertThat(executable).isEqualTo(ReflectionUtils.findMethod(
|
||||
FactoryWithOverloadedClassMethodsOnInterface.class, "byAnnotation",
|
||||
Class.class));
|
||||
}
|
||||
|
||||
private Executable resolve(DefaultListableBeanFactory beanFactory,
|
||||
BeanDefinition beanDefinition) {
|
||||
return new ConstructorOrFactoryMethodResolver(beanFactory)
|
||||
.resolve(beanDefinition);
|
||||
}
|
||||
|
||||
static class IntegerFactoryBean implements FactoryBean<Integer> {
|
||||
|
||||
@Override
|
||||
public Integer getObject() {
|
||||
return 42;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return Integer.class;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
static class MultiConstructorSample {
|
||||
|
||||
MultiConstructorSample(String name) {
|
||||
}
|
||||
|
||||
MultiConstructorSample(Integer value) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
static class MultiConstructorArraySample {
|
||||
|
||||
public MultiConstructorArraySample(String... names) {
|
||||
}
|
||||
|
||||
public MultiConstructorArraySample(Integer... values) {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
static class MultiConstructorListSample {
|
||||
|
||||
public MultiConstructorListSample(String name) {
|
||||
}
|
||||
|
||||
public MultiConstructorListSample(List<Integer> values) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface DummyInterface {
|
||||
|
||||
static String of(Object o) {
|
||||
return o.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
static class DummySampleFactory implements DummyInterface {
|
||||
|
||||
static String of(Integer value) {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
private String of(String ignored) {
|
||||
return ignored;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
static class ConstructorClassArraySample {
|
||||
|
||||
ConstructorClassArraySample(Class<?>... classArrayArg) {
|
||||
}
|
||||
|
||||
ConstructorClassArraySample(Executor somethingElse) {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
static class MultiConstructorClassArraySample {
|
||||
|
||||
MultiConstructorClassArraySample(Class<?>... classArrayArg) {
|
||||
}
|
||||
|
||||
MultiConstructorClassArraySample(String... stringArrayArg) {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
static class ClassArrayFactoryMethodSample {
|
||||
|
||||
static String of(Class<?>[] classArrayArg) {
|
||||
return "test";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
static class ClassArrayFactoryMethodSampleWithAnotherFactoryMethod {
|
||||
|
||||
static String of(Class<?>[] classArrayArg) {
|
||||
return "test";
|
||||
}
|
||||
|
||||
static String of(String[] classArrayArg) {
|
||||
return "test";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unnused")
|
||||
static class ConstructorPrimitiveFallback {
|
||||
|
||||
public ConstructorPrimitiveFallback(boolean useDefaultExecutor) {
|
||||
}
|
||||
|
||||
public ConstructorPrimitiveFallback(Executor executor) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class SampleBeanWithConstructors {
|
||||
|
||||
public SampleBeanWithConstructors() {
|
||||
}
|
||||
|
||||
public SampleBeanWithConstructors(String name) {
|
||||
}
|
||||
|
||||
public SampleBeanWithConstructors(Number number, String name) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface FactoryWithOverloadedClassMethodsOnInterface {
|
||||
|
||||
static FactoryWithOverloadedClassMethodsOnInterface byAnnotation(
|
||||
Class<? extends Annotation> annotationType) {
|
||||
return byAnnotation(annotationType, SearchStrategy.INHERITED_ANNOTATIONS);
|
||||
}
|
||||
|
||||
static FactoryWithOverloadedClassMethodsOnInterface byAnnotation(
|
||||
Class<? extends Annotation> annotationType,
|
||||
SearchStrategy searchStrategy) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.aot;
|
||||
|
||||
/**
|
||||
* Test enum that include a class body.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public enum EnumWithClassBody {
|
||||
|
||||
/**
|
||||
* No class body.
|
||||
*/
|
||||
ONE,
|
||||
|
||||
/**
|
||||
* With class body.
|
||||
*/
|
||||
TWO {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "2";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.beans.factory.aot;
|
||||
|
||||
/**
|
||||
* Fake CGLIB generated class.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class ExampleClass$$GeneratedBy extends ExampleClass {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.beans.factory.aot;
|
||||
|
||||
/**
|
||||
* Public example class used for test.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class ExampleClass {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,347 @@
|
||||
/*
|
||||
* 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.aot;
|
||||
|
||||
import java.lang.reflect.Executable;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import javax.lang.model.element.Modifier;
|
||||
|
||||
import org.assertj.core.api.ThrowingConsumer;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aot.generate.DefaultGenerationContext;
|
||||
import org.springframework.aot.generate.GeneratedMethods;
|
||||
import org.springframework.aot.generate.InMemoryGeneratedFiles;
|
||||
import org.springframework.aot.hint.ExecutableHint;
|
||||
import org.springframework.aot.hint.ExecutableMode;
|
||||
import org.springframework.aot.hint.ReflectionHints;
|
||||
import org.springframework.aot.hint.TypeHint;
|
||||
import org.springframework.aot.test.generator.compile.Compiled;
|
||||
import org.springframework.aot.test.generator.compile.TestCompiler;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.InstanceSupplier;
|
||||
import org.springframework.beans.factory.support.RegisteredBean;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.testfixture.beans.TestBean;
|
||||
import org.springframework.beans.testfixture.beans.TestBeanWithPrivateConstructor;
|
||||
import org.springframework.beans.testfixture.beans.factory.generator.InnerComponentConfiguration;
|
||||
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.NumberHolder;
|
||||
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.core.env.StandardEnvironment;
|
||||
import org.springframework.javapoet.ClassName;
|
||||
import org.springframework.javapoet.CodeBlock;
|
||||
import org.springframework.javapoet.JavaFile;
|
||||
import org.springframework.javapoet.MethodSpec;
|
||||
import org.springframework.javapoet.ParameterizedTypeName;
|
||||
import org.springframework.javapoet.TypeSpec;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link InstanceSupplierCodeGenerator}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class InstanceSupplierCodeGeneratorTests {
|
||||
|
||||
private InMemoryGeneratedFiles generatedFiles;
|
||||
|
||||
private DefaultGenerationContext generationContext;
|
||||
|
||||
private boolean allowDirectSupplierShortcut = false;
|
||||
|
||||
private ClassName className = ClassName.get("__", "InstanceSupplierSupplier");
|
||||
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
this.generatedFiles = new InMemoryGeneratedFiles();
|
||||
this.generationContext = new DefaultGenerationContext(this.generatedFiles);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void generateWhenHasDefaultConstructor() {
|
||||
BeanDefinition beanDefinition = new RootBeanDefinition(TestBean.class);
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
testCompiledResult(beanFactory, beanDefinition, (instanceSupplier, compiled) -> {
|
||||
TestBean bean = getBean(beanFactory, beanDefinition, instanceSupplier);
|
||||
assertThat(bean).isInstanceOf(TestBean.class);
|
||||
assertThat(compiled.getSourceFile())
|
||||
.contains("InstanceSupplier.using(TestBean::new)");
|
||||
});
|
||||
assertThat(getReflectionHints().getTypeHint(TestBean.class))
|
||||
.satisfies(hasConstructorWithMode(ExecutableMode.INTROSPECT));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenHasConstructorWithParameter() {
|
||||
BeanDefinition beanDefinition = new RootBeanDefinition(InjectionComponent.class);
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerSingleton("injected", "injected");
|
||||
testCompiledResult(beanFactory, beanDefinition, (instanceSupplier, compiled) -> {
|
||||
InjectionComponent bean = getBean(beanFactory, beanDefinition,
|
||||
instanceSupplier);
|
||||
assertThat(bean).isInstanceOf(InjectionComponent.class).extracting("bean")
|
||||
.isEqualTo("injected");
|
||||
});
|
||||
assertThat(getReflectionHints().getTypeHint(InjectionComponent.class))
|
||||
.satisfies(hasConstructorWithMode(ExecutableMode.INTROSPECT));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenHasConstructorWithInnerClassAndDefaultConstructor() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(
|
||||
NoDependencyComponent.class);
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerSingleton("configuration", new InnerComponentConfiguration());
|
||||
testCompiledResult(beanFactory, beanDefinition, (instanceSupplier, compiled) -> {
|
||||
NoDependencyComponent bean = getBean(beanFactory, beanDefinition,
|
||||
instanceSupplier);
|
||||
assertThat(bean).isInstanceOf(NoDependencyComponent.class);
|
||||
assertThat(compiled.getSourceFile()).contains(
|
||||
"getBeanFactory().getBean(InnerComponentConfiguration.class).new NoDependencyComponent()");
|
||||
});
|
||||
assertThat(getReflectionHints().getTypeHint(NoDependencyComponent.class))
|
||||
.satisfies(hasConstructorWithMode(ExecutableMode.INTROSPECT));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenHasConstructorWithInnerClassAndParameter() {
|
||||
BeanDefinition beanDefinition = new RootBeanDefinition(
|
||||
EnvironmentAwareComponent.class);
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerSingleton("configuration", new InnerComponentConfiguration());
|
||||
beanFactory.registerSingleton("environment", new StandardEnvironment());
|
||||
testCompiledResult(beanFactory, beanDefinition, (instanceSupplier, compiled) -> {
|
||||
EnvironmentAwareComponent bean = getBean(beanFactory, beanDefinition,
|
||||
instanceSupplier);
|
||||
assertThat(bean).isInstanceOf(EnvironmentAwareComponent.class);
|
||||
assertThat(compiled.getSourceFile()).contains(
|
||||
"getBeanFactory().getBean(InnerComponentConfiguration.class).new EnvironmentAwareComponent(");
|
||||
});
|
||||
assertThat(getReflectionHints().getTypeHint(EnvironmentAwareComponent.class))
|
||||
.satisfies(hasConstructorWithMode(ExecutableMode.INTROSPECT));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenHasConstructorWithGeneric() {
|
||||
BeanDefinition beanDefinition = new RootBeanDefinition(
|
||||
NumberHolderFactoryBean.class);
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerSingleton("number", 123);
|
||||
testCompiledResult(beanFactory, beanDefinition, (instanceSupplier, compiled) -> {
|
||||
NumberHolder<?> bean = getBean(beanFactory, beanDefinition, instanceSupplier);
|
||||
assertThat(bean).isInstanceOf(NumberHolder.class);
|
||||
assertThat(bean).extracting("number").isNull(); // No property
|
||||
// actually set
|
||||
assertThat(compiled.getSourceFile()).contains("NumberHolderFactoryBean::new");
|
||||
});
|
||||
assertThat(getReflectionHints().getTypeHint(NumberHolderFactoryBean.class))
|
||||
.satisfies(hasConstructorWithMode(ExecutableMode.INTROSPECT));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenHasPrivateConstructor() {
|
||||
BeanDefinition beanDefinition = new RootBeanDefinition(
|
||||
TestBeanWithPrivateConstructor.class);
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
testCompiledResult(beanFactory, beanDefinition, (instanceSupplier, compiled) -> {
|
||||
TestBeanWithPrivateConstructor bean = getBean(beanFactory, beanDefinition,
|
||||
instanceSupplier);
|
||||
assertThat(bean).isInstanceOf(TestBeanWithPrivateConstructor.class);
|
||||
assertThat(compiled.getSourceFile())
|
||||
.contains("resolveAndInstantiate(registeredBean)");
|
||||
});
|
||||
assertThat(getReflectionHints().getTypeHint(TestBeanWithPrivateConstructor.class))
|
||||
.satisfies(hasConstructorWithMode(ExecutableMode.INVOKE));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenHasFactoryMethodWithNoArg() {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(String.class)
|
||||
.setFactoryMethodOnBean("stringBean", "config").getBeanDefinition();
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerBeanDefinition("config", BeanDefinitionBuilder
|
||||
.genericBeanDefinition(SimpleConfiguration.class).getBeanDefinition());
|
||||
testCompiledResult(beanFactory, beanDefinition, (instanceSupplier, compiled) -> {
|
||||
String bean = getBean(beanFactory, beanDefinition, instanceSupplier);
|
||||
assertThat(bean).isInstanceOf(String.class);
|
||||
assertThat(bean).isEqualTo("Hello");
|
||||
assertThat(compiled.getSourceFile()).contains(
|
||||
"getBeanFactory().getBean(SimpleConfiguration.class).stringBean()");
|
||||
});
|
||||
assertThat(getReflectionHints().getTypeHint(SimpleConfiguration.class))
|
||||
.satisfies(hasMethodWithMode(ExecutableMode.INTROSPECT));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenHasPrivateStaticFactoryMethodWithNoArg() {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(String.class)
|
||||
.setFactoryMethodOnBean("privateStaticStringBean", "config")
|
||||
.getBeanDefinition();
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerBeanDefinition("config", BeanDefinitionBuilder
|
||||
.genericBeanDefinition(SimpleConfiguration.class).getBeanDefinition());
|
||||
testCompiledResult(beanFactory, beanDefinition, (instanceSupplier, compiled) -> {
|
||||
String bean = getBean(beanFactory, beanDefinition, instanceSupplier);
|
||||
assertThat(bean).isInstanceOf(String.class);
|
||||
assertThat(bean).isEqualTo("Hello");
|
||||
assertThat(compiled.getSourceFile())
|
||||
.contains("resolveAndInstantiate(registeredBean)");
|
||||
});
|
||||
assertThat(getReflectionHints().getTypeHint(SimpleConfiguration.class))
|
||||
.satisfies(hasMethodWithMode(ExecutableMode.INVOKE));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenHasStaticFactoryMethodWithNoArg() {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(Integer.class)
|
||||
.setFactoryMethodOnBean("integerBean", "config").getBeanDefinition();
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerBeanDefinition("config", BeanDefinitionBuilder
|
||||
.genericBeanDefinition(SimpleConfiguration.class).getBeanDefinition());
|
||||
testCompiledResult(beanFactory, beanDefinition, (instanceSupplier, compiled) -> {
|
||||
Integer bean = getBean(beanFactory, beanDefinition, instanceSupplier);
|
||||
assertThat(bean).isInstanceOf(Integer.class);
|
||||
assertThat(bean).isEqualTo(42);
|
||||
assertThat(compiled.getSourceFile())
|
||||
.contains("SimpleConfiguration::integerBean");
|
||||
});
|
||||
assertThat(getReflectionHints().getTypeHint(SimpleConfiguration.class))
|
||||
.satisfies(hasMethodWithMode(ExecutableMode.INTROSPECT));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenHasStaticFactoryMethodWithArg() {
|
||||
RootBeanDefinition beanDefinition = (RootBeanDefinition) BeanDefinitionBuilder
|
||||
.rootBeanDefinition(String.class)
|
||||
.setFactoryMethodOnBean("create", "config").getBeanDefinition();
|
||||
beanDefinition.setResolvedFactoryMethod(ReflectionUtils
|
||||
.findMethod(SampleFactory.class, "create", Number.class, String.class));
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerBeanDefinition("config", BeanDefinitionBuilder
|
||||
.genericBeanDefinition(SampleFactory.class).getBeanDefinition());
|
||||
beanFactory.registerSingleton("number", 42);
|
||||
beanFactory.registerSingleton("string", "test");
|
||||
testCompiledResult(beanFactory, beanDefinition, (instanceSupplier, compiled) -> {
|
||||
String bean = getBean(beanFactory, beanDefinition, instanceSupplier);
|
||||
assertThat(bean).isInstanceOf(String.class);
|
||||
assertThat(bean).isEqualTo("42test");
|
||||
assertThat(compiled.getSourceFile()).contains("SampleFactory.create(");
|
||||
});
|
||||
assertThat(getReflectionHints().getTypeHint(SampleFactory.class))
|
||||
.satisfies(hasMethodWithMode(ExecutableMode.INTROSPECT));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenHasStaticFactoryMethodCheckedException() {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(Integer.class)
|
||||
.setFactoryMethodOnBean("throwingIntegerBean", "config")
|
||||
.getBeanDefinition();
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerBeanDefinition("config", BeanDefinitionBuilder
|
||||
.genericBeanDefinition(SimpleConfiguration.class).getBeanDefinition());
|
||||
testCompiledResult(beanFactory, beanDefinition, (instanceSupplier, compiled) -> {
|
||||
Integer bean = getBean(beanFactory, beanDefinition, instanceSupplier);
|
||||
assertThat(bean).isInstanceOf(Integer.class);
|
||||
assertThat(bean).isEqualTo(42);
|
||||
assertThat(compiled.getSourceFile()).contains(") throws Exception {");
|
||||
});
|
||||
assertThat(getReflectionHints().getTypeHint(SimpleConfiguration.class))
|
||||
.satisfies(hasMethodWithMode(ExecutableMode.INTROSPECT));
|
||||
}
|
||||
|
||||
private ReflectionHints getReflectionHints() {
|
||||
return this.generationContext.getRuntimeHints().reflection();
|
||||
}
|
||||
|
||||
private ThrowingConsumer<TypeHint> hasConstructorWithMode(ExecutableMode mode) {
|
||||
return hint -> assertThat(hint.constructors()).anySatisfy(hasMode(mode));
|
||||
}
|
||||
|
||||
private ThrowingConsumer<TypeHint> hasMethodWithMode(ExecutableMode mode) {
|
||||
return hint -> assertThat(hint.methods()).anySatisfy(hasMode(mode));
|
||||
}
|
||||
|
||||
private ThrowingConsumer<ExecutableHint> hasMode(ExecutableMode mode) {
|
||||
return hint -> assertThat(hint.getModes()).containsExactly(mode);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T getBean(DefaultListableBeanFactory beanFactory,
|
||||
BeanDefinition beanDefinition, InstanceSupplier<?> instanceSupplier) {
|
||||
((RootBeanDefinition) beanDefinition).setInstanceSupplier(instanceSupplier);
|
||||
beanFactory.registerBeanDefinition("testBean", beanDefinition);
|
||||
return (T) beanFactory.getBean("testBean");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void testCompiledResult(DefaultListableBeanFactory beanFactory,
|
||||
BeanDefinition beanDefinition,
|
||||
BiConsumer<InstanceSupplier<?>, Compiled> result) {
|
||||
this.generationContext.writeGeneratedContent();
|
||||
DefaultListableBeanFactory registrationBeanFactory = new DefaultListableBeanFactory(
|
||||
beanFactory);
|
||||
registrationBeanFactory.registerBeanDefinition("testBean", beanDefinition);
|
||||
RegisteredBean registeredBean = RegisteredBean.of(registrationBeanFactory,
|
||||
"testBean");
|
||||
GeneratedMethods generatedMethods = new GeneratedMethods();
|
||||
InstanceSupplierCodeGenerator generator = new InstanceSupplierCodeGenerator(
|
||||
this.generationContext, this.className, generatedMethods,
|
||||
this.allowDirectSupplierShortcut);
|
||||
Executable constructorOrFactoryMethod = ConstructorOrFactoryMethodResolver
|
||||
.resolve(registeredBean);
|
||||
CodeBlock generatedCode = generator.generateCode(registeredBean,
|
||||
constructorOrFactoryMethod);
|
||||
JavaFile javaFile = createJavaFile(generatedCode, generatedMethods);
|
||||
TestCompiler.forSystem().withFiles(this.generatedFiles).compile(javaFile::writeTo,
|
||||
compiled -> result.accept(
|
||||
(InstanceSupplier<?>) compiled.getInstance(Supplier.class).get(),
|
||||
compiled));
|
||||
}
|
||||
|
||||
private JavaFile createJavaFile(CodeBlock generatedCode,
|
||||
GeneratedMethods generatedMethods) {
|
||||
TypeSpec.Builder builder = TypeSpec.classBuilder("InstanceSupplierSupplier");
|
||||
builder.addModifiers(Modifier.PUBLIC);
|
||||
builder.addSuperinterface(
|
||||
ParameterizedTypeName.get(Supplier.class, InstanceSupplier.class));
|
||||
builder.addMethod(MethodSpec.methodBuilder("get").addModifiers(Modifier.PUBLIC)
|
||||
.returns(InstanceSupplier.class).addStatement("return $L", generatedCode)
|
||||
.build());
|
||||
generatedMethods.doWithMethodSpecs(builder::addMethod);
|
||||
return JavaFile.builder("__", builder.build()).build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.aot;
|
||||
|
||||
import org.springframework.aot.generate.GeneratedMethods;
|
||||
import org.springframework.aot.generate.MethodGenerator;
|
||||
import org.springframework.javapoet.ClassName;
|
||||
|
||||
/**
|
||||
* Mock {@link BeanRegistrationsCode} implementation.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class MockBeanRegistrationsCode implements BeanRegistrationsCode {
|
||||
|
||||
private final ClassName className;
|
||||
|
||||
private final GeneratedMethods generatedMethods = new GeneratedMethods();
|
||||
|
||||
|
||||
MockBeanRegistrationsCode(ClassName className) {
|
||||
this.className = className;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ClassName getClassName() {
|
||||
return this.className;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodGenerator getMethodGenerator() {
|
||||
return this.generatedMethods;
|
||||
}
|
||||
|
||||
GeneratedMethods getGeneratedMethods() {
|
||||
return this.generatedMethods;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.beans.factory.aot;
|
||||
|
||||
/**
|
||||
* Package-private test bean.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class PackagePrivateTestBean {
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user