diff --git a/spring-aop/src/main/java/org/springframework/aop/scope/ScopedProxyBeanRegistrationContributionProvider.java b/spring-aop/src/main/java/org/springframework/aop/scope/ScopedProxyBeanRegistrationContributionProvider.java deleted file mode 100644 index 4855106c57..0000000000 --- a/spring-aop/src/main/java/org/springframework/aop/scope/ScopedProxyBeanRegistrationContributionProvider.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.aop.scope; - -import java.lang.reflect.Executable; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.aot.generator.CodeContribution; -import org.springframework.aot.generator.DefaultCodeContribution; -import org.springframework.aot.generator.ProtectedAccess.Options; -import org.springframework.aot.hint.RuntimeHints; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.ConfigurableBeanFactory; -import org.springframework.beans.factory.generator.BeanFactoryContribution; -import org.springframework.beans.factory.generator.BeanInstantiationGenerator; -import org.springframework.beans.factory.generator.BeanRegistrationBeanFactoryContribution; -import org.springframework.beans.factory.generator.BeanRegistrationContributionProvider; -import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.javapoet.support.MultiStatement; -import org.springframework.lang.Nullable; - -/** - * {@link BeanRegistrationContributionProvider} for {@link ScopedProxyFactoryBean}. - * - * @author Stephane Nicoll - */ -class ScopedProxyBeanRegistrationContributionProvider implements BeanRegistrationContributionProvider { - - private static final Log logger = LogFactory.getLog(ScopedProxyBeanRegistrationContributionProvider.class); - - - private final ConfigurableBeanFactory beanFactory; - - ScopedProxyBeanRegistrationContributionProvider(ConfigurableBeanFactory beanFactory) { - this.beanFactory = beanFactory; - } - - @Nullable - @Override - public BeanFactoryContribution getContributionFor(String beanName, RootBeanDefinition beanDefinition) { - Class beanType = beanDefinition.getResolvableType().toClass(); - return (beanType.equals(ScopedProxyFactoryBean.class)) - ? createScopedProxyBeanFactoryContribution(beanName, beanDefinition) : null; - } - - @Nullable - private BeanFactoryContribution createScopedProxyBeanFactoryContribution(String beanName, RootBeanDefinition beanDefinition) { - String targetBeanName = getTargetBeanName(beanDefinition); - BeanDefinition targetBeanDefinition = getTargetBeanDefinition(targetBeanName); - if (targetBeanDefinition == null) { - logger.warn("Could not handle " + ScopedProxyFactoryBean.class.getSimpleName() + - ": no target bean definition found with name " + targetBeanName); - return null; - } - RootBeanDefinition processedBeanDefinition = new RootBeanDefinition(beanDefinition); - processedBeanDefinition.setTargetType(targetBeanDefinition.getResolvableType()); - processedBeanDefinition.getPropertyValues().removePropertyValue("targetBeanName"); - return new BeanRegistrationBeanFactoryContribution(beanName, processedBeanDefinition, - getBeanInstantiationGenerator(targetBeanName)); - } - - private BeanInstantiationGenerator getBeanInstantiationGenerator(String targetBeanName) { - return new BeanInstantiationGenerator() { - - @Override - public Executable getInstanceCreator() { - return ScopedProxyFactoryBean.class.getDeclaredConstructors()[0]; - } - - @Override - public CodeContribution generateBeanInstantiation(RuntimeHints runtimeHints) { - CodeContribution codeContribution = new DefaultCodeContribution(runtimeHints); - codeContribution.protectedAccess().analyze(getInstanceCreator(), Options.defaults().build()); - MultiStatement statements = new MultiStatement(); - statements.addStatement("$T factory = new $T()", ScopedProxyFactoryBean.class, ScopedProxyFactoryBean.class); - statements.addStatement("factory.setTargetBeanName($S)", targetBeanName); - statements.addStatement("factory.setBeanFactory(beanFactory)"); - statements.addStatement("return factory.getObject()"); - codeContribution.statements().add(statements.toLambda("() ->")); - return codeContribution; - } - }; - } - - @Nullable - private String getTargetBeanName(BeanDefinition beanDefinition) { - Object value = beanDefinition.getPropertyValues().get("targetBeanName"); - return (value instanceof String targetBeanName) ? targetBeanName : null; - } - - @Nullable - private BeanDefinition getTargetBeanDefinition(@Nullable String targetBeanName) { - if (targetBeanName != null && this.beanFactory.containsBean(targetBeanName)) { - return this.beanFactory.getMergedBeanDefinition(targetBeanName); - } - return null; - } - -} diff --git a/spring-aop/src/test/java/org/springframework/aop/scope/ScopedProxyBeanRegistrationContributionProviderTests.java b/spring-aop/src/test/java/org/springframework/aop/scope/ScopedProxyBeanRegistrationContributionProviderTests.java deleted file mode 100644 index e303e513ff..0000000000 --- a/spring-aop/src/test/java/org/springframework/aop/scope/ScopedProxyBeanRegistrationContributionProviderTests.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.aop.scope; - -import org.junit.jupiter.api.Test; - -import org.springframework.aop.testfixture.scope.SimpleTarget; -import org.springframework.aot.generator.DefaultGeneratedTypeContext; -import org.springframework.aot.generator.GeneratedType; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.PropertiesFactoryBean; -import org.springframework.beans.factory.generator.BeanFactoryContribution; -import org.springframework.beans.factory.generator.BeanFactoryInitialization; -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.core.ResolvableType; -import org.springframework.javapoet.ClassName; -import org.springframework.javapoet.support.CodeSnippet; -import org.springframework.lang.Nullable; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link ScopedProxyBeanRegistrationContributionProvider}. - * - * @author Stephane Nicoll - */ -class ScopedProxyBeanRegistrationContributionProviderTests { - - private final DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - - @Test - void getWithNonScopedProxy() { - BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(PropertiesFactoryBean.class) - .getBeanDefinition(); - assertThat(getBeanFactoryContribution("test", beanDefinition)).isNull(); - } - - @Test - void getWithScopedProxyWithoutTargetBeanName() { - BeanDefinition scopeBean = BeanDefinitionBuilder.rootBeanDefinition(ScopedProxyFactoryBean.class) - .getBeanDefinition(); - assertThat(getBeanFactoryContribution("test", scopeBean)).isNull(); - } - - @Test - void getWithScopedProxyWithInvalidTargetBeanName() { - BeanDefinition scopeBean = BeanDefinitionBuilder.rootBeanDefinition(ScopedProxyFactoryBean.class) - .addPropertyValue("targetBeanName", "testDoesNotExist").getBeanDefinition(); - assertThat(getBeanFactoryContribution("test", scopeBean)).isNull(); - } - - @Test - void getWithScopedProxyWithTargetBeanName() { - BeanDefinition targetBean = BeanDefinitionBuilder.rootBeanDefinition(SimpleTarget.class) - .getBeanDefinition(); - beanFactory.registerBeanDefinition("simpleTarget", targetBean); - BeanDefinition scopeBean = BeanDefinitionBuilder.rootBeanDefinition(ScopedProxyFactoryBean.class) - .addPropertyValue("targetBeanName", "simpleTarget").getBeanDefinition(); - assertThat(getBeanFactoryContribution("test", scopeBean)).isNotNull(); - } - - @Test - void writeBeanRegistrationForScopedProxy() { - RootBeanDefinition targetBean = new RootBeanDefinition(); - targetBean.setTargetType(ResolvableType.forClassWithGenerics(NumberHolder.class, Integer.class)); - targetBean.setScope("custom"); - this.beanFactory.registerBeanDefinition("numberHolder", targetBean); - BeanDefinition scopeBean = BeanDefinitionBuilder.rootBeanDefinition(ScopedProxyFactoryBean.class) - .addPropertyValue("targetBeanName", "numberHolder").getBeanDefinition(); - assertThat(writeBeanRegistration("test", scopeBean).getSnippet()).isEqualTo(""" - BeanDefinitionRegistrar.of("test", ResolvableType.forClassWithGenerics(NumberHolder.class, Integer.class)) - .instanceSupplier(() -> { - ScopedProxyFactoryBean factory = new ScopedProxyFactoryBean(); - factory.setTargetBeanName("numberHolder"); - factory.setBeanFactory(beanFactory); - return factory.getObject(); - }).register(beanFactory); - """); - } - - private CodeSnippet writeBeanRegistration(String beanName, BeanDefinition beanDefinition) { - BeanFactoryContribution contribution = getBeanFactoryContribution(beanName, beanDefinition); - assertThat(contribution).isNotNull(); - BeanFactoryInitialization initialization = new BeanFactoryInitialization(new DefaultGeneratedTypeContext("comp.example", packageName -> GeneratedType.of(ClassName.get(packageName, "Test")))); - contribution.applyTo(initialization); - return CodeSnippet.of(initialization.toCodeBlock()); - } - - @Nullable - BeanFactoryContribution getBeanFactoryContribution(String beanName, BeanDefinition beanDefinition) { - ScopedProxyBeanRegistrationContributionProvider provider = new ScopedProxyBeanRegistrationContributionProvider(this.beanFactory); - return provider.getContributionFor(beanName, (RootBeanDefinition) beanDefinition); - } - -} - diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java index e6fb72123f..770a447aef 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java @@ -43,7 +43,6 @@ import org.apache.commons.logging.LogFactory; import org.springframework.aot.generate.AccessVisibility; import org.springframework.aot.generate.GenerationContext; import org.springframework.aot.generate.MethodReference; -import org.springframework.aot.generator.CodeContribution; import org.springframework.aot.hint.ExecutableHint; import org.springframework.aot.hint.ExecutableMode; import org.springframework.aot.hint.FieldHint; @@ -59,16 +58,12 @@ import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.beans.factory.InjectionPoint; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.UnsatisfiedDependencyException; -import org.springframework.beans.factory.annotation.InjectionMetadata.InjectedElement; import org.springframework.beans.factory.aot.BeanRegistrationAotContribution; import org.springframework.beans.factory.aot.BeanRegistrationAotProcessor; import org.springframework.beans.factory.aot.BeanRegistrationCode; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.DependencyDescriptor; import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor; -import org.springframework.beans.factory.generator.AotContributingBeanPostProcessor; -import org.springframework.beans.factory.generator.BeanInstantiationContribution; -import org.springframework.beans.factory.generator.InjectionGenerator; import org.springframework.beans.factory.support.LookupOverride; import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor; import org.springframework.beans.factory.support.RegisteredBean; @@ -157,8 +152,7 @@ import org.springframework.util.StringUtils; * @see Value */ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor, - MergedBeanDefinitionPostProcessor, AotContributingBeanPostProcessor, BeanRegistrationAotProcessor, - PriorityOrdered, BeanFactoryAware { + MergedBeanDefinitionPostProcessor, BeanRegistrationAotProcessor, PriorityOrdered, BeanFactoryAware { protected final Log logger = LogFactory.getLog(getClass()); @@ -285,15 +279,6 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA findInjectionMetadata(beanName, beanType, beanDefinition); } - @Override - public BeanInstantiationContribution contribute(RootBeanDefinition beanDefinition, Class beanType, String beanName) { - InjectionMetadata metadata = findInjectionMetadata(beanName, beanType, beanDefinition); - Collection injectedElements = metadata.getInjectedElements(); - return (!ObjectUtils.isEmpty(injectedElements) - ? new AutowiredAnnotationBeanInstantiationContribution(injectedElements) - : null); - } - @Override public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) { Class beanClass = registeredBean.getBeanClass(); @@ -866,52 +851,6 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA } } - private static final class AutowiredAnnotationBeanInstantiationContribution implements BeanInstantiationContribution { - - private final Collection injectedElements; - - private final InjectionGenerator generator; - - AutowiredAnnotationBeanInstantiationContribution(Collection injectedElements) { - this.injectedElements = injectedElements; - this.generator = new InjectionGenerator(); - } - - @Override - public void applyTo(CodeContribution contribution) { - this.injectedElements.forEach(element -> { - boolean isRequired = isRequired(element); - Member member = element.getMember(); - analyzeMember(contribution, member); - contribution.statements().addStatement(this.generator.generateInjection(member, isRequired)); - }); - } - - private boolean isRequired(InjectedElement element) { - if (element instanceof AutowiredMethodElement injectedMethod) { - return injectedMethod.required; - } - else if (element instanceof AutowiredFieldElement injectedField) { - return injectedField.required; - } - return true; - } - - private void analyzeMember(CodeContribution contribution, Member member) { - if (member instanceof Method method) { - contribution.runtimeHints().reflection().registerMethod(method, - hint -> hint.setModes(ExecutableMode.INTROSPECT)); - contribution.protectedAccess().analyze(member, - this.generator.getProtectedAccessInjectionOptions(member)); - } - else if (member instanceof Field field) { - contribution.runtimeHints().reflection().registerField(field); - contribution.protectedAccess().analyze(member, - this.generator.getProtectedAccessInjectionOptions(member)); - } - } - - } /** * DependencyDescriptor variant with a pre-resolved target bean name. diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/InitDestroyAnnotationBeanPostProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/InitDestroyAnnotationBeanPostProcessor.java index 5e2db74e7c..1ac3014af3 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/InitDestroyAnnotationBeanPostProcessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/InitDestroyAnnotationBeanPostProcessor.java @@ -42,8 +42,6 @@ import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.aot.BeanRegistrationAotContribution; import org.springframework.beans.factory.aot.BeanRegistrationAotProcessor; import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor; -import org.springframework.beans.factory.generator.AotContributingBeanPostProcessor; -import org.springframework.beans.factory.generator.BeanInstantiationContribution; import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor; import org.springframework.beans.factory.support.RegisteredBean; import org.springframework.beans.factory.support.RootBeanDefinition; @@ -87,8 +85,7 @@ import org.springframework.util.ReflectionUtils; */ @SuppressWarnings("serial") public class InitDestroyAnnotationBeanPostProcessor implements DestructionAwareBeanPostProcessor, - MergedBeanDefinitionPostProcessor, AotContributingBeanPostProcessor, BeanRegistrationAotProcessor, - PriorityOrdered, Serializable { + MergedBeanDefinitionPostProcessor, BeanRegistrationAotProcessor, PriorityOrdered, Serializable { private final transient LifecycleMetadata emptyLifecycleMetadata = new LifecycleMetadata(Object.class, Collections.emptyList(), Collections.emptyList()) { @@ -159,22 +156,6 @@ public class InitDestroyAnnotationBeanPostProcessor implements DestructionAwareB findInjectionMetadata(beanDefinition, beanType); } - @Override - public BeanInstantiationContribution contribute(RootBeanDefinition beanDefinition, Class beanType, String beanName) { - LifecycleMetadata metadata = findInjectionMetadata(beanDefinition, beanType); - if (!CollectionUtils.isEmpty(metadata.initMethods)) { - String[] initMethodNames = safeMerge( - beanDefinition.getInitMethodNames(), metadata.initMethods); - beanDefinition.setInitMethodNames(initMethodNames); - } - if (!CollectionUtils.isEmpty(metadata.destroyMethods)) { - String[] destroyMethodNames = safeMerge( - beanDefinition.getDestroyMethodNames(), metadata.destroyMethods); - beanDefinition.setDestroyMethodNames(destroyMethodNames); - } - return null; - } - @Override public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) { RootBeanDefinition beanDefinition = registeredBean.getMergedBeanDefinition(); diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/generator/AotContributingBeanFactoryPostProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/generator/AotContributingBeanFactoryPostProcessor.java deleted file mode 100644 index ff2d5a0a53..0000000000 --- a/spring-beans/src/main/java/org/springframework/beans/factory/generator/AotContributingBeanFactoryPostProcessor.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.beans.factory.generator; - -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.config.BeanFactoryPostProcessor; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.lang.Nullable; - -/** - * Specialization of {@link BeanFactoryPostProcessor} that contributes bean - * factory optimizations ahead of time, using generated code that replaces - * runtime behavior. - * - * @author Stephane Nicoll - * @since 6.0 - */ -public interface AotContributingBeanFactoryPostProcessor extends BeanFactoryPostProcessor { - - /** - * Contribute a {@link BeanFactoryContribution} for the given bean factory, - * if applicable. - * @param beanFactory the bean factory to optimize - * @return the contribution to use or {@code null} - */ - @Nullable - BeanFactoryContribution contribute(ConfigurableListableBeanFactory beanFactory); - - @Override - default void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { - - } - -} diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/generator/AotContributingBeanPostProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/generator/AotContributingBeanPostProcessor.java deleted file mode 100644 index 1d1f59323e..0000000000 --- a/spring-beans/src/main/java/org/springframework/beans/factory/generator/AotContributingBeanPostProcessor.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.beans.factory.generator; - -import org.springframework.beans.factory.config.BeanPostProcessor; -import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.core.PriorityOrdered; -import org.springframework.lang.Nullable; - -/** - * Specialization of a priority ordered {@link BeanPostProcessor} that - * contributes to bean instantiation ahead of time, providing generated code - * that is equivalent to its runtime behavior. - * - *

Contrary to other bean post processors, implementations of this interface - * are instantiated at build-time and should not rely on other beans in the - * context. - * - * @author Stephane Nicoll - * @since 6.0 - */ -public interface AotContributingBeanPostProcessor extends BeanPostProcessor, PriorityOrdered { - - /** - * Contribute a {@link BeanInstantiationContribution} for the given bean definition, - * if applicable. - * @param beanDefinition the merged bean definition for the bean - * @param beanType the inferred type of the bean - * @param beanName the name of the bean - * @return the contribution to use or {@code null} if the bean should not be processed - */ - @Nullable - BeanInstantiationContribution contribute(RootBeanDefinition beanDefinition, Class beanType, String beanName); - -} diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/generator/BeanDefinitionGenerationException.java b/spring-beans/src/main/java/org/springframework/beans/factory/generator/BeanDefinitionGenerationException.java deleted file mode 100644 index 9ccc8e721d..0000000000 --- a/spring-beans/src/main/java/org/springframework/beans/factory/generator/BeanDefinitionGenerationException.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.beans.factory.generator; - -import org.springframework.beans.factory.config.BeanDefinition; - -/** - * Thrown when a bean definition could not be generated. - * - * @author Stephane Nicoll - * @since 6.0 - */ -@SuppressWarnings("serial") -public class BeanDefinitionGenerationException extends RuntimeException { - - private final String beanName; - - private final BeanDefinition beanDefinition; - - public BeanDefinitionGenerationException(String beanName, BeanDefinition beanDefinition, String message, Throwable cause) { - super(message, cause); - this.beanName = beanName; - this.beanDefinition = beanDefinition; - } - - public BeanDefinitionGenerationException(String beanName, BeanDefinition beanDefinition, String message) { - super(message); - this.beanName = beanName; - this.beanDefinition = beanDefinition; - } - - /** - * Return the bean name that could not be generated. - * @return the bean name - */ - public String getBeanName() { - return this.beanName; - } - - /** - * Return the bean definition that could not be generated. - * @return the bean definition - */ - public BeanDefinition getBeanDefinition() { - return this.beanDefinition; - } - -} diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/generator/BeanDefinitionsContribution.java b/spring-beans/src/main/java/org/springframework/beans/factory/generator/BeanDefinitionsContribution.java deleted file mode 100644 index 3c3632b08a..0000000000 --- a/spring-beans/src/main/java/org/springframework/beans/factory/generator/BeanDefinitionsContribution.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.beans.factory.generator; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.function.BiPredicate; -import java.util.function.Consumer; - -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.core.io.support.SpringFactoriesLoader; -import org.springframework.core.io.support.SpringFactoriesLoader.ArgumentResolver; - -/** - * A {@link BeanFactoryContribution} that generates the bean definitions of a - * bean factory, using {@link BeanRegistrationContributionProvider} to use - * appropriate customizations if necessary. - * - *

{@link BeanRegistrationContributionProvider} can be ordered, with the default - * implementation always coming last. - * - * @author Stephane Nicoll - * @since 6.0 - * @see DefaultBeanRegistrationContributionProvider - */ -public class BeanDefinitionsContribution implements BeanFactoryContribution { - - private final DefaultListableBeanFactory beanFactory; - - private final List contributionProviders; - - private final Map contributions; - - BeanDefinitionsContribution(DefaultListableBeanFactory beanFactory, - List contributionProviders) { - this.beanFactory = beanFactory; - this.contributionProviders = contributionProviders; - this.contributions = new HashMap<>(); - } - - public BeanDefinitionsContribution(DefaultListableBeanFactory beanFactory) { - this(beanFactory, initializeProviders(beanFactory)); - } - - private static List initializeProviders(DefaultListableBeanFactory beanFactory) { - List providers = new ArrayList<>( - SpringFactoriesLoader.forDefaultResourceLocation(beanFactory.getBeanClassLoader()).load( - BeanRegistrationContributionProvider.class, - ArgumentResolver.from(type -> type.isInstance(beanFactory) ? beanFactory : null))); - providers.add(new DefaultBeanRegistrationContributionProvider(beanFactory)); - return providers; - } - - @Override - public void applyTo(BeanFactoryInitialization initialization) { - writeBeanDefinitions(initialization); - } - - @Override - public BiPredicate getBeanDefinitionExcludeFilter() { - List> predicates = new ArrayList<>(); - for (String beanName : this.beanFactory.getBeanDefinitionNames()) { - handleMergedBeanDefinition(beanName, beanDefinition -> predicates.add( - getBeanRegistrationContribution(beanName, beanDefinition).getBeanDefinitionExcludeFilter())); - } - return predicates.stream().filter(Objects::nonNull).reduce((n, d) -> false, BiPredicate::or); - } - - private void writeBeanDefinitions(BeanFactoryInitialization initialization) { - for (String beanName : this.beanFactory.getBeanDefinitionNames()) { - handleMergedBeanDefinition(beanName, beanDefinition -> { - BeanFactoryContribution registrationContribution = getBeanRegistrationContribution( - beanName, beanDefinition); - registrationContribution.applyTo(initialization); - }); - } - } - - private BeanFactoryContribution getBeanRegistrationContribution( - String beanName, RootBeanDefinition beanDefinition) { - return this.contributions.computeIfAbsent(beanName, name -> { - for (BeanRegistrationContributionProvider provider : this.contributionProviders) { - BeanFactoryContribution contribution = provider.getContributionFor( - beanName, beanDefinition); - if (contribution != null) { - return contribution; - } - } - throw new BeanRegistrationContributionNotFoundException(beanName, beanDefinition); - }); - } - - private void handleMergedBeanDefinition(String beanName, Consumer consumer) { - RootBeanDefinition beanDefinition = (RootBeanDefinition) this.beanFactory.getMergedBeanDefinition(beanName); - try { - consumer.accept(beanDefinition); - } - catch (BeanDefinitionGenerationException ex) { - throw ex; - } - catch (Exception ex) { - String msg = String.format("Failed to handle bean with name '%s' and type '%s'", - beanName, beanDefinition.getResolvableType()); - throw new BeanDefinitionGenerationException(beanName, beanDefinition, msg, ex); - } - } - -} diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/generator/BeanFactoryContribution.java b/spring-beans/src/main/java/org/springframework/beans/factory/generator/BeanFactoryContribution.java deleted file mode 100644 index 87402bc468..0000000000 --- a/spring-beans/src/main/java/org/springframework/beans/factory/generator/BeanFactoryContribution.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.beans.factory.generator; - -import java.util.function.BiPredicate; - -import org.springframework.beans.factory.config.BeanDefinition; - -/** - * Contribute optimizations ahead of time to initialize a bean factory. - * - * @author Stephane Nicoll - * @since 6.0 - */ -public interface BeanFactoryContribution { - - /** - * Contribute ahead of time optimizations to the specific - * {@link BeanFactoryInitialization}. - * @param initialization {@link BeanFactoryInitialization} to contribute to - */ - void applyTo(BeanFactoryInitialization initialization); - - /** - * Return a predicate that determines if a particular bean definition - * should be excluded from processing. Can be used to exclude infrastructure - * that has been optimized using generated code. - * @return the predicate to use - */ - default BiPredicate getBeanDefinitionExcludeFilter() { - return (beanName, beanDefinition) -> false; - } - -} diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/generator/BeanFactoryInitialization.java b/spring-beans/src/main/java/org/springframework/beans/factory/generator/BeanFactoryInitialization.java deleted file mode 100644 index afe7326c83..0000000000 --- a/spring-beans/src/main/java/org/springframework/beans/factory/generator/BeanFactoryInitialization.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.beans.factory.generator; - -import java.util.function.Consumer; -import java.util.function.Supplier; - -import javax.lang.model.element.Modifier; - -import org.springframework.aot.generator.GeneratedType; -import org.springframework.aot.generator.GeneratedTypeContext; -import org.springframework.aot.generator.ProtectedAccess; -import org.springframework.beans.factory.BeanFactory; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.javapoet.CodeBlock; -import org.springframework.javapoet.CodeBlock.Builder; -import org.springframework.javapoet.MethodSpec; - -/** - * The initialization of a {@link BeanFactory}. - * - * @author Andy Wilkinson - * @author Stephane Nicoll - * @since 6.0 - */ -public class BeanFactoryInitialization { - - private final GeneratedTypeContext generatedTypeContext; - - private final CodeBlock.Builder codeContributions; - - public BeanFactoryInitialization(GeneratedTypeContext generatedTypeContext) { - this.generatedTypeContext = generatedTypeContext; - this.codeContributions = CodeBlock.builder(); - } - - /** - * Return the {@link GeneratedTypeContext} to use to contribute - * additional methods or hints. - * @return the generation context - */ - public GeneratedTypeContext generatedTypeContext() { - return this.generatedTypeContext; - } - - /** - * Contribute code that initializes the bean factory and that does not - * require any privileged access. - * @param code the code to contribute - */ - public void contribute(Consumer code) { - CodeBlock.Builder builder = CodeBlock.builder(); - code.accept(builder); - CodeBlock codeBlock = builder.build(); - this.codeContributions.add(codeBlock); - if (!codeBlock.toString().endsWith("\n")) { - this.codeContributions.add("\n"); - } - } - - /** - * Contribute code that initializes the bean factory. If privileged access - * is required, a public method in the target package is created and - * invoked, rather than contributing the code directly. - * @param protectedAccess the {@link ProtectedAccess} instance to use - * @param methodName a method name to use if privileged access is required - * @param methodBody the contribution - */ - public void contribute(ProtectedAccess protectedAccess, Supplier methodName, - Consumer methodBody) { - String targetPackageName = this.generatedTypeContext.getMainGeneratedType().getClassName().packageName(); - String protectedPackageName = protectedAccess.getPrivilegedPackageName(targetPackageName); - if (protectedPackageName != null) { - GeneratedType type = this.generatedTypeContext.getGeneratedType(protectedPackageName); - MethodSpec.Builder method = MethodSpec.methodBuilder(methodName.get()) - .addModifiers(Modifier.PUBLIC, Modifier.STATIC) - .addParameter(DefaultListableBeanFactory.class, "beanFactory"); - CodeBlock.Builder code = CodeBlock.builder(); - methodBody.accept(code); - method.addCode(code.build()); - contribute(main -> main.addStatement("$T.$N(beanFactory)", type.getClassName(), type.addMethod(method))); - } - else { - contribute(methodBody); - } - } - - /** - * Return the code that has been contributed to this instance. - * @return the code - */ - public CodeBlock toCodeBlock() { - return this.codeContributions.build(); - } - -} diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/generator/BeanFieldGenerator.java b/spring-beans/src/main/java/org/springframework/beans/factory/generator/BeanFieldGenerator.java deleted file mode 100644 index 224a7d7a93..0000000000 --- a/spring-beans/src/main/java/org/springframework/beans/factory/generator/BeanFieldGenerator.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.beans.factory.generator; - -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; - -import org.springframework.aot.generator.ProtectedAccess.Options; -import org.springframework.javapoet.CodeBlock; -import org.springframework.javapoet.support.MultiStatement; -import org.springframework.util.ReflectionUtils; - -/** - * Support for generating {@link Field} access. - * - * @author Stephane Nicoll - * @since 6.0 - */ -public class BeanFieldGenerator { - - /** - * The {@link Options} to use to access a field. - */ - public static final Options FIELD_OPTIONS = Options.defaults() - .useReflection(member -> Modifier.isPrivate(member.getModifiers())).build(); - - - /** - * Generate the necessary code to set the specified field. Use reflection - * using {@link ReflectionUtils} if necessary. - * @param field the field to set - * @param value a code representation of the field value - * @return the code to set the specified field - */ - public MultiStatement generateSetValue(String target, Field field, CodeBlock value) { - MultiStatement statement = new MultiStatement(); - boolean useReflection = Modifier.isPrivate(field.getModifiers()); - if (useReflection) { - String fieldName = String.format("%sField", field.getName()); - statement.addStatement("$T $L = $T.findField($T.class, $S)", Field.class, fieldName, ReflectionUtils.class, - field.getDeclaringClass(), field.getName()); - statement.addStatement("$T.makeAccessible($L)", ReflectionUtils.class, fieldName); - statement.addStatement("$T.setField($L, $L, $L)", ReflectionUtils.class, fieldName, target, value); - } - else { - statement.addStatement("$L.$L = $L", target, field.getName(), value); - } - return statement; - } - -} diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/generator/BeanInstantiationContribution.java b/spring-beans/src/main/java/org/springframework/beans/factory/generator/BeanInstantiationContribution.java deleted file mode 100644 index b4ba65715c..0000000000 --- a/spring-beans/src/main/java/org/springframework/beans/factory/generator/BeanInstantiationContribution.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.beans.factory.generator; - -import org.springframework.aot.generator.CodeContribution; - -/** - * A contribution to the instantiation of a bean following ahead of time - * processing. - * - * @author Stephane Nicoll - * @since 6.0 - */ -@FunctionalInterface -public interface BeanInstantiationContribution { - - /** - * Contribute bean instantiation to the specified {@link CodeContribution}. - *

Implementations of this interface can assume the following variables - * to be accessible: - *

    - *
  • {@code beanFactory}: the general {@code DefaultListableBeanFactory}
  • - *
  • {@code instanceContext}: the {@code BeanInstanceContext} callback
  • - *
  • {@code bean}: the variable that refers to the bean instance
  • - *
- * @param contribution the {@link CodeContribution} to use - */ - void applyTo(CodeContribution contribution); - -} diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/generator/BeanInstantiationGenerator.java b/spring-beans/src/main/java/org/springframework/beans/factory/generator/BeanInstantiationGenerator.java deleted file mode 100644 index 5a776428c6..0000000000 --- a/spring-beans/src/main/java/org/springframework/beans/factory/generator/BeanInstantiationGenerator.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.beans.factory.generator; - -import java.lang.reflect.Executable; - -import org.springframework.aot.generator.CodeContribution; -import org.springframework.aot.hint.RuntimeHints; - -/** - * Generate code that instantiate a particular bean. - * - * @author Stephane Nicoll - * @since 6.0 - */ -public interface BeanInstantiationGenerator { - - /** - * Return the {@link Executable} that is used to create the bean instance - * for further metadata processing. - * @return the executable that is used to create the bean instance - */ - Executable getInstanceCreator(); - - /** - * Return the necessary code to instantiate a bean. - * @param runtimeHints the runtime hints instance to use - * @return a code contribution that provides an initialized bean instance - */ - CodeContribution generateBeanInstantiation(RuntimeHints runtimeHints); - -} - diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/generator/BeanParameterGenerator.java b/spring-beans/src/main/java/org/springframework/beans/factory/generator/BeanParameterGenerator.java deleted file mode 100644 index c372db9e6e..0000000000 --- a/spring-beans/src/main/java/org/springframework/beans/factory/generator/BeanParameterGenerator.java +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.beans.factory.generator; - -import java.lang.reflect.Executable; -import java.lang.reflect.Parameter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -import org.springframework.aot.generator.ResolvableTypeGenerator; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.BeanReference; -import org.springframework.beans.factory.config.RuntimeBeanReference; -import org.springframework.beans.factory.support.ManagedList; -import org.springframework.beans.factory.support.ManagedSet; -import org.springframework.core.ResolvableType; -import org.springframework.javapoet.CodeBlock; -import org.springframework.javapoet.CodeBlock.Builder; -import org.springframework.javapoet.support.MultiCodeBlock; -import org.springframework.lang.Nullable; -import org.springframework.util.ObjectUtils; - -/** - * Support for generating parameters. - * - * @author Stephane Nicoll - * @since 6.0 - */ -public final class BeanParameterGenerator { - - /** - * A default instance that does not handle inner bean definitions. - */ - public static final BeanParameterGenerator INSTANCE = new BeanParameterGenerator(); - - private final ResolvableTypeGenerator typeGenerator = new ResolvableTypeGenerator(); - - private final Function innerBeanDefinitionGenerator; - - - /** - * Create an instance with the callback to use to generate an inner bean - * definition. - * @param innerBeanDefinitionGenerator the inner bean definition generator - */ - public BeanParameterGenerator(Function innerBeanDefinitionGenerator) { - this.innerBeanDefinitionGenerator = innerBeanDefinitionGenerator; - } - - /** - * Create an instance with no support for inner bean definitions. - */ - public BeanParameterGenerator() { - this(beanDefinition -> { - throw new IllegalStateException("Inner bean definition is not supported by this instance"); - }); - } - - - /** - * Generate the specified parameter {@code value}. - * @param value the value of the parameter - * @return the value of the parameter - */ - public CodeBlock generateParameterValue(@Nullable Object value) { - return generateParameterValue(value, () -> ResolvableType.forInstance(value)); - } - - /** - * Generate the specified parameter {@code value}. - * @param value the value of the parameter - * @param parameterType the type of the parameter - * @return the value of the parameter - */ - public CodeBlock generateParameterValue(@Nullable Object value, Supplier parameterType) { - Builder code = CodeBlock.builder(); - generateParameterValue(code, value, parameterType); - return code.build(); - } - - /** - * Generate the parameter types of the specified {@link Executable}. - * @param executable the executable - * @return the parameter types of the executable as a comma separated list - */ - public CodeBlock generateExecutableParameterTypes(Executable executable) { - Class[] parameterTypes = Arrays.stream(executable.getParameters()) - .map(Parameter::getType).toArray(Class[]::new); - return CodeBlock.of(Arrays.stream(parameterTypes).map(d -> "$T.class") - .collect(Collectors.joining(", ")), (Object[]) parameterTypes); - } - - private void generateParameterValue(Builder code, @Nullable Object value, Supplier parameterTypeSupplier) { - if (value == null) { - code.add("null"); - return; - } - ResolvableType parameterType = parameterTypeSupplier.get(); - if (parameterType.isArray()) { - code.add("new $T { ", parameterType.toClass()); - code.add(generateAll(Arrays.asList(ObjectUtils.toObjectArray(value)), - item -> parameterType.getComponentType())); - code.add(" }"); - } - else if (value instanceof List list) { - if (list.isEmpty()) { - code.add("$T.emptyList()", Collections.class); - } - else { - Class listType = (value instanceof ManagedList ? ManagedList.class : List.class); - code.add("$T.of(", listType); - ResolvableType collectionType = parameterType.as(List.class).getGenerics()[0]; - code.add(generateAll(list, item -> collectionType)); - code.add(")"); - } - } - else if (value instanceof Set set) { - if (set.isEmpty()) { - code.add("$T.emptySet()", Collections.class); - } - else { - Class setType = (value instanceof ManagedSet ? ManagedSet.class : Set.class); - code.add("$T.of(", setType); - ResolvableType collectionType = parameterType.as(Set.class).getGenerics()[0]; - code.add(generateAll(set, item -> collectionType)); - code.add(")"); - } - } - else if (value instanceof Map map) { - if (map.size() <= 10) { - code.add("$T.of(", Map.class); - List parameters = new ArrayList<>(); - map.forEach((mapKey, mapValue) -> { - parameters.add(mapKey); - parameters.add(mapValue); - }); - code.add(generateAll(parameters, ResolvableType::forInstance)); - code.add(")"); - } - } - else if (value instanceof Character character) { - String result = '\'' + characterLiteralWithoutSingleQuotes(character) + '\''; - code.add(result); - } - else if (isPrimitiveOrWrapper(value)) { - code.add("$L", value); - } - else if (value instanceof String) { - code.add("$S", value); - } - else if (value instanceof Enum enumValue) { - code.add("$T.$N", enumValue.getClass(), enumValue.name()); - } - else if (value instanceof Class) { - code.add("$T.class", value); - } - else if (value instanceof ResolvableType) { - code.add(this.typeGenerator.generateTypeFor((ResolvableType) value)); - } - else if (value instanceof BeanDefinition bd) { - code.add(this.innerBeanDefinitionGenerator.apply(bd)); - } - else if (value instanceof BeanReference) { - code.add("new $T($S)", RuntimeBeanReference.class, ((BeanReference) value).getBeanName()); - } - else { - throw new IllegalArgumentException("Parameter of type " + parameterType + " is not supported"); - } - } - - private CodeBlock generateAll(Iterable items, Function elementType) { - MultiCodeBlock multi = new MultiCodeBlock(); - items.forEach(item -> multi.add(code -> - generateParameterValue(code, item, () -> elementType.apply(item)))); - return multi.join(", "); - } - - private boolean isPrimitiveOrWrapper(Object value) { - Class valueType = value.getClass(); - return (valueType.isPrimitive() || valueType == Double.class || valueType == Float.class - || valueType == Long.class || valueType == Integer.class || valueType == Short.class - || valueType == Character.class || valueType == Byte.class || valueType == Boolean.class); - } - - // Copied from com.squareup.javapoet.Util - private static String characterLiteralWithoutSingleQuotes(char c) { - // see https://docs.oracle.com/javase/specs/jls/se7/html/jls-3.html#jls-3.10.6 - return switch (c) { - case '\b' -> "\\b"; /* \u0008: backspace (BS) */ - case '\t' -> "\\t"; /* \u0009: horizontal tab (HT) */ - case '\n' -> "\\n"; /* \u000a: linefeed (LF) */ - case '\f' -> "\\f"; /* \u000c: form feed (FF) */ - case '\r' -> "\\r"; /* \u000d: carriage return (CR) */ - case '\"' -> "\""; /* \u0022: double quote (") */ - case '\'' -> "\\'"; /* \u0027: single quote (') */ - case '\\' -> "\\\\"; /* \u005c: backslash (\) */ - default -> Character.isISOControl(c) ? String.format("\\u%04x", (int) c) : Character.toString(c); - }; - } - -} diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/generator/BeanRegistrationBeanFactoryContribution.java b/spring-beans/src/main/java/org/springframework/beans/factory/generator/BeanRegistrationBeanFactoryContribution.java deleted file mode 100644 index 104fa61648..0000000000 --- a/spring-beans/src/main/java/org/springframework/beans/factory/generator/BeanRegistrationBeanFactoryContribution.java +++ /dev/null @@ -1,514 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.beans.factory.generator; - -import java.beans.BeanInfo; -import java.beans.IntrospectionException; -import java.beans.Introspector; -import java.lang.reflect.Constructor; -import java.lang.reflect.Executable; -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Objects; -import java.util.function.Predicate; - -import javax.lang.model.SourceVersion; - -import org.springframework.aot.generator.CodeContribution; -import org.springframework.aot.generator.ProtectedAccess; -import org.springframework.aot.generator.ResolvableTypeGenerator; -import org.springframework.aot.hint.ExecutableMode; -import org.springframework.aot.hint.ReflectionHints; -import org.springframework.aot.hint.RuntimeHints; -import org.springframework.beans.BeanInfoFactory; -import org.springframework.beans.ExtendedBeanInfoFactory; -import org.springframework.beans.MutablePropertyValues; -import org.springframework.beans.PropertyValue; -import org.springframework.beans.PropertyValues; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.ConfigurableBeanFactory; -import org.springframework.beans.factory.config.ConstructorArgumentValues; -import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder; -import org.springframework.beans.factory.generator.config.BeanDefinitionRegistrar; -import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.core.AttributeAccessor; -import org.springframework.core.ResolvableType; -import org.springframework.javapoet.CodeBlock; -import org.springframework.javapoet.CodeBlock.Builder; -import org.springframework.javapoet.support.MultiStatement; -import org.springframework.lang.Nullable; -import org.springframework.util.ClassUtils; -import org.springframework.util.ObjectUtils; -import org.springframework.util.ReflectionUtils; -import org.springframework.util.StringUtils; - -/** - * A {@link BeanFactoryContribution} that registers a bean with the bean - * factory. - * - * @author Stephane Nicoll - * @since 6.0 - */ -public class BeanRegistrationBeanFactoryContribution implements BeanFactoryContribution { - - private static final BeanInfoFactory beanInfoFactory = new ExtendedBeanInfoFactory(); - - private static final ResolvableTypeGenerator typeGenerator = new ResolvableTypeGenerator(); - - private final String beanName; - - private final RootBeanDefinition beanDefinition; - - private final BeanInstantiationGenerator beanInstantiationGenerator; - - @Nullable - private final DefaultBeanRegistrationContributionProvider innerBeanRegistrationContributionProvider; - - private int nesting = 0; - - BeanRegistrationBeanFactoryContribution(String beanName, RootBeanDefinition beanDefinition, - BeanInstantiationGenerator beanInstantiationGenerator, - @Nullable DefaultBeanRegistrationContributionProvider innerBeanRegistrationContributionProvider) { - this.beanName = beanName; - this.beanDefinition = beanDefinition; - this.beanInstantiationGenerator = beanInstantiationGenerator; - this.innerBeanRegistrationContributionProvider = innerBeanRegistrationContributionProvider; - } - - public BeanRegistrationBeanFactoryContribution(String beanName, RootBeanDefinition beanDefinition, - BeanInstantiationGenerator beanInstantiationGenerator) { - this(beanName, beanDefinition, beanInstantiationGenerator, null); - } - - String getBeanName() { - return this.beanName; - } - - RootBeanDefinition getBeanDefinition() { - return this.beanDefinition; - } - - @Override - public void applyTo(BeanFactoryInitialization initialization) { - RuntimeHints runtimeHints = initialization.generatedTypeContext().runtimeHints(); - registerRuntimeHints(runtimeHints); - CodeContribution beanInstanceContribution = generateBeanInstance(runtimeHints); - // Write everything in one place - ProtectedAccess protectedAccess = beanInstanceContribution.protectedAccess(); - protectedAccess.analyze(this.beanDefinition.getResolvableType()); - initialization.contribute(protectedAccess, this::registerBeanMethodName, code -> - code.add(generateBeanRegistration(runtimeHints, beanInstanceContribution.statements()))); - } - - /** - * Register the necessary hints that are required to process the bean - * registration generated by this instance. - * @param runtimeHints the runtime hints to use - */ - void registerRuntimeHints(RuntimeHints runtimeHints) { - String[] initMethodNames = this.beanDefinition.getInitMethodNames(); - if (!ObjectUtils.isEmpty(initMethodNames)) { - registerInitDestroyMethodsRuntimeHints(initMethodNames, runtimeHints); - } - String[] destroyMethodNames = this.beanDefinition.getDestroyMethodNames(); - if (!ObjectUtils.isEmpty(destroyMethodNames)) { - registerInitDestroyMethodsRuntimeHints(destroyMethodNames, runtimeHints); - } - registerPropertyValuesRuntimeHints(runtimeHints); - } - - /** - * Generate the necessary code to register a {@link BeanDefinition} in the - * bean registry. - * @param runtimeHints the hints to use - * @param beanInstanceStatements the {@linkplain MultiStatement statements} - * to create and initialize the bean instance - * @return bean registration code - */ - CodeBlock generateBeanRegistration(RuntimeHints runtimeHints, MultiStatement beanInstanceStatements) { - BeanParameterGenerator parameterGenerator = createBeanParameterGenerator(runtimeHints); - Generator generator = new Generator(parameterGenerator); - return generator.generateBeanRegistration(beanInstanceStatements); - } - - /** - * Generate the necessary code to create a {@link BeanDefinition}. - * @param runtimeHints the hints to use - * @return bean definition code - */ - CodeBlock generateBeanDefinition(RuntimeHints runtimeHints) { - CodeContribution beanInstanceContribution = generateBeanInstance(runtimeHints); - BeanParameterGenerator parameterGenerator = createBeanParameterGenerator(runtimeHints); - Generator generator = new Generator(parameterGenerator); - return generator.generateBeanDefinition(beanInstanceContribution.statements()); - } - - private BeanParameterGenerator createBeanParameterGenerator(RuntimeHints runtimeHints) { - return new BeanParameterGenerator(beanDefinition -> - generateInnerBeanDefinition(beanDefinition, runtimeHints)); - } - - /** - * Return the predicate to use to include Bean Definition - * {@link AttributeAccessor attributes}. - * @return the bean definition's attributes include filter - */ - protected Predicate getAttributeFilter() { - return candidate -> false; - } - - /** - * Specify if the creator {@link Executable} should be defined. By default, - * a creator is specified if the {@code instanceSupplier} callback is used - * with an {@code instanceContext} callback. - * @param instanceCreator the executable to use to instantiate the bean - * @return {@code true} to declare the creator - */ - protected boolean shouldDeclareCreator(Executable instanceCreator) { - if (instanceCreator instanceof Method) { - return true; - } - if (instanceCreator instanceof Constructor constructor) { - int minArgs = isInnerClass(constructor.getDeclaringClass()) ? 2 : 1; - return instanceCreator.getParameterCount() >= minArgs; - } - return false; - } - - /** - * Return the necessary code to instantiate and post-process a bean. - * @param runtimeHints the {@link RuntimeHints} to use - * @return a code contribution that provides an initialized bean instance - */ - protected CodeContribution generateBeanInstance(RuntimeHints runtimeHints) { - return this.beanInstantiationGenerator.generateBeanInstantiation(runtimeHints); - } - - private void registerInitDestroyMethodsRuntimeHints(String[] methodNames, RuntimeHints runtimeHints) { - for (String methodName : methodNames) { - Method method = ReflectionUtils.findMethod(getUserBeanClass(), methodName); - if (method != null) { - runtimeHints.reflection().registerMethod(method, hint -> hint.withMode(ExecutableMode.INVOKE)); - } - } - } - - private void registerPropertyValuesRuntimeHints(RuntimeHints runtimeHints) { - if (!this.beanDefinition.hasPropertyValues()) { - return; - } - BeanInfo beanInfo = getBeanInfo(this.beanDefinition.getResolvableType().toClass()); - if (beanInfo != null) { - ReflectionHints reflectionHints = runtimeHints.reflection(); - this.beanDefinition.getPropertyValues().getPropertyValueList().forEach(propertyValue -> { - Method writeMethod = findWriteMethod(beanInfo, propertyValue.getName()); - if (writeMethod != null) { - reflectionHints.registerMethod(writeMethod, hint -> hint.withMode(ExecutableMode.INVOKE)); - } - }); - } - } - - @Nullable - private BeanInfo getBeanInfo(Class beanType) { - try { - BeanInfo beanInfo = beanInfoFactory.getBeanInfo(beanType); - if (beanInfo != null) { - return beanInfo; - } - return Introspector.getBeanInfo(beanType, Introspector.IGNORE_ALL_BEANINFO); - } - catch (IntrospectionException ex) { - return null; - } - } - - @Nullable - private Method findWriteMethod(BeanInfo beanInfo, String propertyName) { - return Arrays.stream(beanInfo.getPropertyDescriptors()) - .filter(pd -> propertyName.equals(pd.getName())) - .map(java.beans.PropertyDescriptor::getWriteMethod) - .filter(Objects::nonNull).findFirst().orElse(null); - } - - protected CodeBlock initializeBeanDefinitionRegistrar() { - return CodeBlock.of("$T.of($S, ", BeanDefinitionRegistrar.class, this.beanName); - } - - private Class getUserBeanClass() { - return ClassUtils.getUserClass(this.beanDefinition.getResolvableType().toClass()); - } - - private void handleCreatorReference(Builder code, Executable creator) { - if (creator instanceof Method) { - code.add(".withFactoryMethod($T.class, $S", creator.getDeclaringClass(), creator.getName()); - if (creator.getParameterCount() > 0) { - code.add(", "); - } - } - else { - code.add(".withConstructor("); - } - code.add(BeanParameterGenerator.INSTANCE.generateExecutableParameterTypes(creator)); - code.add(")"); - } - - private CodeBlock generateInnerBeanDefinition(BeanDefinition beanDefinition, RuntimeHints runtimeHints) { - if (this.innerBeanRegistrationContributionProvider == null) { - throw new IllegalStateException("This generator does not handle inner bean definition " + beanDefinition); - } - BeanRegistrationBeanFactoryContribution innerBeanRegistrationContribution = this.innerBeanRegistrationContributionProvider - .getInnerBeanRegistrationContribution(this, beanDefinition); - innerBeanRegistrationContribution.nesting = this.nesting + 1; - innerBeanRegistrationContribution.registerRuntimeHints(runtimeHints); - return innerBeanRegistrationContribution.generateBeanDefinition(runtimeHints); - } - - private String registerBeanMethodName() { - Executable instanceCreator = this.beanInstantiationGenerator.getInstanceCreator(); - if (instanceCreator instanceof Method method) { - String target = (isValidName(this.beanName)) ? this.beanName : method.getName(); - return String.format("register%s_%s", method.getDeclaringClass().getSimpleName(), target); - } - else if (instanceCreator.getDeclaringClass().getEnclosingClass() != null) { - String target = (isValidName(this.beanName)) ? this.beanName : getUserBeanClass().getSimpleName(); - Class enclosingClass = instanceCreator.getDeclaringClass().getEnclosingClass(); - return String.format("register%s_%s", enclosingClass.getSimpleName(), target); - } - else { - String target = (isValidName(this.beanName)) ? this.beanName : getUserBeanClass().getSimpleName(); - return "register" + StringUtils.capitalize(target); - } - } - - private boolean isValidName(@Nullable String name) { - return name != null && SourceVersion.isIdentifier(name) && !SourceVersion.isKeyword(name); - } - - private String determineVariableName(String name) { - return name + "_".repeat(this.nesting); - } - - private static boolean isInnerClass(Class type) { - return type.isMemberClass() && !java.lang.reflect.Modifier.isStatic(type.getModifiers()); - } - - class Generator { - - private final BeanParameterGenerator parameterGenerator; - - private final RootBeanDefinition beanDefinition; - - Generator(BeanParameterGenerator parameterGenerator) { - this.parameterGenerator = parameterGenerator; - this.beanDefinition = BeanRegistrationBeanFactoryContribution.this.beanDefinition; - } - - CodeBlock generateBeanRegistration(MultiStatement instanceStatements) { - CodeBlock.Builder code = CodeBlock.builder(); - initializeBeanDefinitionRegistrar(instanceStatements, code); - code.addStatement(".register(beanFactory)"); - return code.build(); - } - - CodeBlock generateBeanDefinition(MultiStatement instanceStatements) { - CodeBlock.Builder code = CodeBlock.builder(); - initializeBeanDefinitionRegistrar(instanceStatements, code); - code.add(".toBeanDefinition()"); - return code.build(); - } - - private void initializeBeanDefinitionRegistrar(MultiStatement instanceStatements, Builder code) { - Executable instanceCreator = BeanRegistrationBeanFactoryContribution.this.beanInstantiationGenerator.getInstanceCreator(); - code.add(BeanRegistrationBeanFactoryContribution.this.initializeBeanDefinitionRegistrar()); - generateBeanType(code); - code.add(")"); - boolean shouldDeclareCreator = shouldDeclareCreator(instanceCreator); - if (shouldDeclareCreator) { - handleCreatorReference(code, instanceCreator); - } - code.add("\n").indent().indent(); - code.add(".instanceSupplier("); - code.add(instanceStatements.toLambdaBody()); - code.add(")").unindent().unindent(); - handleBeanDefinitionMetadata(code); - } - - private void generateBeanType(Builder code) { - ResolvableType resolvableType = this.beanDefinition.getResolvableType(); - if (resolvableType.hasGenerics() && !hasUnresolvedGenerics(resolvableType)) { - code.add(typeGenerator.generateTypeFor(resolvableType)); - } - else { - code.add("$T.class", getUserBeanClass()); - } - } - - private boolean hasUnresolvedGenerics(ResolvableType resolvableType) { - if (resolvableType.hasUnresolvableGenerics()) { - return true; - } - for (ResolvableType generic : resolvableType.getGenerics()) { - if (hasUnresolvedGenerics(generic)) { - return true; - } - } - return false; - } - - private void handleBeanDefinitionMetadata(Builder code) { - String bdVariable = determineVariableName("bd"); - MultiStatement statements = new MultiStatement(); - String[] initMethodNames = this.beanDefinition.getInitMethodNames(); - if (!ObjectUtils.isEmpty(initMethodNames)) { - handleInitMethodNames(statements, bdVariable, initMethodNames); - } - String[] destroyMethodNames = this.beanDefinition.getDestroyMethodNames(); - if (!ObjectUtils.isEmpty(destroyMethodNames)) { - handleDestroyMethodNames(statements, bdVariable, destroyMethodNames); - } - if (this.beanDefinition.isPrimary()) { - statements.addStatement("$L.setPrimary(true)", bdVariable); - } - String scope = this.beanDefinition.getScope(); - if (StringUtils.hasText(scope) && !ConfigurableBeanFactory.SCOPE_SINGLETON.equals(scope)) { - statements.addStatement("$L.setScope($S)", bdVariable, scope); - } - String[] dependsOn = this.beanDefinition.getDependsOn(); - if (!ObjectUtils.isEmpty(dependsOn)) { - statements.addStatement("$L.setDependsOn($L)", bdVariable, - this.parameterGenerator.generateParameterValue(dependsOn)); - } - if (this.beanDefinition.isLazyInit()) { - statements.addStatement("$L.setLazyInit(true)", bdVariable); - } - if (!this.beanDefinition.isAutowireCandidate()) { - statements.addStatement("$L.setAutowireCandidate(false)", bdVariable); - } - if (this.beanDefinition.isSynthetic()) { - statements.addStatement("$L.setSynthetic(true)", bdVariable); - } - if (this.beanDefinition.getRole() != BeanDefinition.ROLE_APPLICATION) { - statements.addStatement("$L.setRole($L)", bdVariable, this.beanDefinition.getRole()); - } - Map indexedArgumentValues = this.beanDefinition.getConstructorArgumentValues() - .getIndexedArgumentValues(); - if (!indexedArgumentValues.isEmpty()) { - handleArgumentValues(statements, bdVariable, indexedArgumentValues); - } - if (this.beanDefinition.hasPropertyValues()) { - handlePropertyValues(statements, bdVariable, this.beanDefinition.getPropertyValues()); - } - if (this.beanDefinition.attributeNames().length > 0) { - handleAttributes(statements, bdVariable); - } - if (statements.isEmpty()) { - return; - } - code.add(statements.toLambda(".customize((" + bdVariable + ") ->")); - code.add(")"); - } - - private void handleInitMethodNames(MultiStatement statements, String bdVariable, String[] initMethodNames) { - if (initMethodNames.length == 1) { - statements.addStatement("$L.setInitMethodName($S)", bdVariable, initMethodNames[0]); - } - else { - statements.addStatement("$L.setInitMethodNames($L)", bdVariable, - this.parameterGenerator.generateParameterValue(initMethodNames)); - } - } - - private void handleDestroyMethodNames(MultiStatement statements, String bdVariable, String[] destroyMethodNames) { - if (destroyMethodNames.length == 1) { - statements.addStatement("$L.setDestroyMethodName($S)", bdVariable, destroyMethodNames[0]); - } - else { - statements.addStatement("$L.setDestroyMethodNames($L)", bdVariable, - this.parameterGenerator.generateParameterValue(destroyMethodNames)); - } - } - - private void handleArgumentValues(MultiStatement statements, String bdVariable, - Map indexedArgumentValues) { - if (indexedArgumentValues.size() == 1) { - Entry entry = indexedArgumentValues.entrySet().iterator().next(); - statements.addStatement(generateArgumentValue(bdVariable + ".getConstructorArgumentValues().", - entry.getKey(), entry.getValue())); - } - else { - String avVariable = determineVariableName("argumentValues"); - statements.addStatement("$T $L = $L.getConstructorArgumentValues()", ConstructorArgumentValues.class, avVariable, bdVariable); - statements.addAll(indexedArgumentValues.entrySet(), entry -> generateArgumentValue(avVariable + ".", - entry.getKey(), entry.getValue())); - } - } - - private CodeBlock generateArgumentValue(String prefix, Integer index, ValueHolder valueHolder) { - Builder code = CodeBlock.builder(); - code.add(prefix); - code.add("addIndexedArgumentValue($L, ", index); - Object value = valueHolder.getValue(); - code.add(this.parameterGenerator.generateParameterValue(value)); - code.add(")"); - return code.build(); - } - - private void handlePropertyValues(MultiStatement statements, String bdVariable, - PropertyValues propertyValues) { - PropertyValue[] properties = propertyValues.getPropertyValues(); - if (properties.length == 1) { - statements.addStatement(generatePropertyValue(bdVariable + ".getPropertyValues().", properties[0])); - } - else { - String pvVariable = determineVariableName("propertyValues"); - statements.addStatement("$T $L = $L.getPropertyValues()", MutablePropertyValues.class, pvVariable, bdVariable); - for (PropertyValue property : properties) { - statements.addStatement(generatePropertyValue(pvVariable + ".", property)); - } - } - } - - private CodeBlock generatePropertyValue(String prefix, PropertyValue property) { - Builder code = CodeBlock.builder(); - code.add(prefix); - code.add("addPropertyValue($S, ", property.getName()); - Object value = property.getValue(); - code.add(this.parameterGenerator.generateParameterValue(value)); - code.add(")"); - return code.build(); - } - - private void handleAttributes(MultiStatement statements, String bdVariable) { - String[] attributeNames = this.beanDefinition.attributeNames(); - Predicate filter = getAttributeFilter(); - for (String attributeName : attributeNames) { - if (filter.test(attributeName)) { - Object value = this.beanDefinition.getAttribute(attributeName); - Builder code = CodeBlock.builder(); - code.add("$L.setAttribute($S, ", bdVariable, attributeName); - code.add((this.parameterGenerator.generateParameterValue(value))); - code.add(")"); - statements.addStatement(code.build()); - } - } - } - } - -} diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/generator/BeanRegistrationContributionNotFoundException.java b/spring-beans/src/main/java/org/springframework/beans/factory/generator/BeanRegistrationContributionNotFoundException.java deleted file mode 100644 index 786ca801fc..0000000000 --- a/spring-beans/src/main/java/org/springframework/beans/factory/generator/BeanRegistrationContributionNotFoundException.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.beans.factory.generator; - -import org.springframework.beans.factory.config.BeanDefinition; - -/** - * Thrown when no suitable {@link BeanFactoryContribution} can be provided - * for the registration of a given bean definition. - * - * @author Stephane Nicoll - * @since 6.0 - */ -@SuppressWarnings("serial") -public class BeanRegistrationContributionNotFoundException extends BeanDefinitionGenerationException { - - public BeanRegistrationContributionNotFoundException(String beanName, BeanDefinition beanDefinition) { - super(beanName, beanDefinition, String.format( - "No suitable contribution found for bean with name '%s' and type '%s'", - beanName, beanDefinition.getResolvableType())); - } - -} diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/generator/BeanRegistrationContributionProvider.java b/spring-beans/src/main/java/org/springframework/beans/factory/generator/BeanRegistrationContributionProvider.java deleted file mode 100644 index 77098fb3ec..0000000000 --- a/spring-beans/src/main/java/org/springframework/beans/factory/generator/BeanRegistrationContributionProvider.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.beans.factory.generator; - -import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.lang.Nullable; - -/** - * Strategy interface to be implemented by components that require custom - * contribution for a bean definition. - * - * @author Stephane Nicoll - * @since 6.0 - */ -@FunctionalInterface -public interface BeanRegistrationContributionProvider { - - /** - * Return the {@link BeanFactoryContribution} that is capable of contributing - * the registration of a bean for the given {@link RootBeanDefinition} or - * {@code null} if the specified bean definition is not supported. - * @param beanName the bean name to handle - * @param beanDefinition the merged bean definition - * @return a contribution for the specified bean definition or {@code null} - */ - @Nullable - BeanFactoryContribution getContributionFor(String beanName, RootBeanDefinition beanDefinition); - -} diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/generator/DefaultBeanInstantiationGenerator.java b/spring-beans/src/main/java/org/springframework/beans/factory/generator/DefaultBeanInstantiationGenerator.java deleted file mode 100644 index de781d9a5b..0000000000 --- a/spring-beans/src/main/java/org/springframework/beans/factory/generator/DefaultBeanInstantiationGenerator.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.beans.factory.generator; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Executable; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.springframework.aot.generator.CodeContribution; -import org.springframework.aot.generator.DefaultCodeContribution; -import org.springframework.aot.generator.ProtectedAccess.Options; -import org.springframework.aot.hint.ExecutableMode; -import org.springframework.aot.hint.RuntimeHints; -import org.springframework.javapoet.CodeBlock; -import org.springframework.util.ClassUtils; - -/** - * Default {@link BeanInstantiationGenerator} implementation. - * - * @author Stephane Nicoll - * @see BeanInstantiationContribution - */ -class DefaultBeanInstantiationGenerator implements BeanInstantiationGenerator { - - private final Executable instanceCreator; - - private final List contributions; - - private final InjectionGenerator injectionGenerator; - - private final Options beanInstanceOptions; - - - DefaultBeanInstantiationGenerator(Executable instanceCreator, List contributions) { - this.instanceCreator = instanceCreator; - this.contributions = List.copyOf(contributions); - this.injectionGenerator = new InjectionGenerator(); - this.beanInstanceOptions = Options.defaults().useReflection(member -> false) - .assignReturnType(member -> !this.contributions.isEmpty()).build(); - } - - @Override - public Executable getInstanceCreator() { - return this.instanceCreator; - } - - @Override - public CodeContribution generateBeanInstantiation(RuntimeHints runtimeHints) { - DefaultCodeContribution codeContribution = new DefaultCodeContribution(runtimeHints); - codeContribution.protectedAccess().analyze(this.instanceCreator, this.beanInstanceOptions); - if (this.instanceCreator instanceof Constructor constructor) { - generateBeanInstantiation(codeContribution, constructor); - } - else if (this.instanceCreator instanceof Method method) { - generateBeanInstantiation(codeContribution, method); - } - return codeContribution; - } - - private void generateBeanInstantiation(CodeContribution codeContribution, Constructor constructor) { - Class declaringType = ClassUtils.getUserClass(constructor.getDeclaringClass()); - boolean innerClass = isInnerClass(declaringType); - boolean multiStatements = !this.contributions.isEmpty(); - int minArgs = isInnerClass(declaringType) ? 2 : 1; - CodeBlock.Builder code = CodeBlock.builder(); - // Shortcut for common case - if (!multiStatements && constructor.getParameterTypes().length < minArgs) { - if (innerClass) { - code.add("() -> beanFactory.getBean($T.class).new $L()", - declaringType.getEnclosingClass(), declaringType.getSimpleName()); - } - else { - // Only apply the shortcut if there's one candidate - if (declaringType.getDeclaredConstructors().length > 1) { - code.add("() -> new $T()", declaringType); - } - else { - code.add("$T::new", declaringType); - } - } - codeContribution.statements().addStatement(code.build()); - return; - } - codeContribution.runtimeHints().reflection().registerConstructor(constructor, - hint -> hint.withMode(ExecutableMode.INTROSPECT)); - code.add("(instanceContext) ->"); - branch(multiStatements, () -> code.beginControlFlow(""), () -> code.add(" ")); - if (multiStatements) { - code.add("$T bean = ", declaringType); - } - code.add(this.injectionGenerator.generateInstantiation(constructor)); - codeContribution.statements().addStatement(code.build()); - - if (multiStatements) { - for (BeanInstantiationContribution contribution : this.contributions) { - contribution.applyTo(codeContribution); - } - codeContribution.statements().addStatement("return bean") - .add(codeBlock -> codeBlock.unindent().add("}")); - } - } - - private static boolean isInnerClass(Class type) { - return type.isMemberClass() && !Modifier.isStatic(type.getModifiers()); - } - - private void generateBeanInstantiation(CodeContribution codeContribution, Method method) { - // Factory method can be introspected - codeContribution.runtimeHints().reflection().registerMethod(method, - hint -> hint.withMode(ExecutableMode.INTROSPECT)); - List> parameterTypes = new ArrayList<>(Arrays.asList(method.getParameterTypes())); - boolean multiStatements = !this.contributions.isEmpty(); - Class declaringType = method.getDeclaringClass(); - CodeBlock.Builder code = CodeBlock.builder(); - // Shortcut for common case - if (!multiStatements && parameterTypes.isEmpty()) { - code.add("() -> "); - branch(Modifier.isStatic(method.getModifiers()), - () -> code.add("$T", declaringType), - () -> code.add("beanFactory.getBean($T.class)", declaringType)); - code.add(".$L()", method.getName()); - codeContribution.statements().addStatement(code.build()); - return; - } - code.add("(instanceContext) ->"); - branch(multiStatements, () -> code.beginControlFlow(""), () -> code.add(" ")); - if (multiStatements) { - code.add("$T bean = ", method.getReturnType()); - } - code.add(this.injectionGenerator.generateInstantiation(method)); - codeContribution.statements().addStatement(code.build()); - if (multiStatements) { - for (BeanInstantiationContribution contribution : this.contributions) { - contribution.applyTo(codeContribution); - } - codeContribution.statements().addStatement("return bean") - .add(codeBlock -> codeBlock.unindent().add("}")); - } - } - - private static void branch(boolean condition, Runnable ifTrue, Runnable ifFalse) { - if (condition) { - ifTrue.run(); - } - else { - ifFalse.run(); - } - } - -} diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/generator/DefaultBeanRegistrationContributionProvider.java b/spring-beans/src/main/java/org/springframework/beans/factory/generator/DefaultBeanRegistrationContributionProvider.java deleted file mode 100644 index a993e0dc7c..0000000000 --- a/spring-beans/src/main/java/org/springframework/beans/factory/generator/DefaultBeanRegistrationContributionProvider.java +++ /dev/null @@ -1,494 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.beans.factory.generator; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Executable; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.function.Supplier; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.beans.factory.FactoryBean; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.BeanReference; -import org.springframework.beans.factory.config.ConfigurableBeanFactory; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.config.ConstructorArgumentValues; -import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder; -import org.springframework.beans.factory.support.AbstractBeanDefinition; -import org.springframework.beans.factory.support.BeanDefinitionValueResolver; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.core.OrderComparator; -import org.springframework.core.ResolvableType; -import org.springframework.core.annotation.MergedAnnotations; -import org.springframework.lang.Nullable; -import org.springframework.util.ClassUtils; -import org.springframework.util.ReflectionUtils; -import org.springframework.util.function.SingletonSupplier; - -/** - * Default {@link BeanRegistrationContributionProvider} implementation. - * - * @author Stephane Nicoll - * @since 6.0 - */ -public final class DefaultBeanRegistrationContributionProvider implements BeanRegistrationContributionProvider { - - private final DefaultListableBeanFactory beanFactory; - - private final ExecutableProvider executableProvider; - - private final Supplier> beanPostProcessors; - - public DefaultBeanRegistrationContributionProvider(DefaultListableBeanFactory beanFactory) { - this.beanFactory = beanFactory; - this.executableProvider = new ExecutableProvider(beanFactory); - this.beanPostProcessors = new SingletonSupplier<>(null, - () -> loadAotContributingBeanPostProcessors(beanFactory)); - } - - private static List loadAotContributingBeanPostProcessors( - DefaultListableBeanFactory beanFactory) { - String[] postProcessorNames = beanFactory.getBeanNamesForType(AotContributingBeanPostProcessor.class, true, false); - List postProcessors = new ArrayList<>(); - for (String ppName : postProcessorNames) { - postProcessors.add(beanFactory.getBean(ppName, AotContributingBeanPostProcessor.class)); - } - sortPostProcessors(postProcessors, beanFactory); - return postProcessors; - } - - @Override - public BeanRegistrationBeanFactoryContribution getContributionFor( - String beanName, RootBeanDefinition beanDefinition) { - BeanInstantiationGenerator beanInstantiationGenerator = getBeanInstantiationGenerator( - beanName, beanDefinition); - return new BeanRegistrationBeanFactoryContribution(beanName, beanDefinition, beanInstantiationGenerator, this); - } - - public BeanInstantiationGenerator getBeanInstantiationGenerator( - String beanName, RootBeanDefinition beanDefinition) { - return new DefaultBeanInstantiationGenerator(determineExecutable(beanDefinition), - determineBeanInstanceContributions(beanName, beanDefinition)); - } - - /** - * Return a {@link BeanRegistrationBeanFactoryContribution} that is capable of - * contributing the specified inner {@link BeanDefinition}. - * @param parent the contribution of the parent bean definition - * @param innerBeanDefinition the inner bean definition - * @return a contribution for the specified inner bean definition - */ - BeanRegistrationBeanFactoryContribution getInnerBeanRegistrationContribution( - BeanRegistrationBeanFactoryContribution parent, BeanDefinition innerBeanDefinition) { - BeanDefinitionValueResolver bdvr = new BeanDefinitionValueResolver(this.beanFactory, - parent.getBeanName(), parent.getBeanDefinition()); - return bdvr.resolveInnerBean(null, innerBeanDefinition, (beanName, bd) -> - new InnerBeanRegistrationBeanFactoryContribution(beanName, bd, - getBeanInstantiationGenerator(beanName, bd), this)); - } - - private Executable determineExecutable(RootBeanDefinition beanDefinition) { - Executable executable = this.executableProvider.detectBeanInstanceExecutable(beanDefinition); - if (executable == null) { - throw new IllegalStateException("No suitable executor found for " + beanDefinition); - } - return executable; - } - - private List determineBeanInstanceContributions( - String beanName, RootBeanDefinition beanDefinition) { - List contributions = new ArrayList<>(); - for (AotContributingBeanPostProcessor pp : this.beanPostProcessors.get()) { - BeanInstantiationContribution contribution = pp.contribute(beanDefinition, - beanDefinition.getResolvableType().toClass(), beanName); - if (contribution != null) { - contributions.add(contribution); - } - } - return contributions; - } - - private static void sortPostProcessors(List postProcessors, ConfigurableListableBeanFactory beanFactory) { - // Nothing to sort? - if (postProcessors.size() <= 1) { - return; - } - Comparator comparatorToUse = null; - if (beanFactory instanceof DefaultListableBeanFactory) { - comparatorToUse = ((DefaultListableBeanFactory) beanFactory).getDependencyComparator(); - } - if (comparatorToUse == null) { - comparatorToUse = OrderComparator.INSTANCE; - } - postProcessors.sort(comparatorToUse); - } - - // FIXME: copy-paste from Spring Native that should go away in favor of ConstructorResolver - private static class ExecutableProvider { - - private static final Log logger = LogFactory.getLog(ExecutableProvider.class); - - private final ConfigurableBeanFactory beanFactory; - - private final ClassLoader classLoader; - - ExecutableProvider(ConfigurableBeanFactory beanFactory) { - this.beanFactory = beanFactory; - this.classLoader = (beanFactory.getBeanClassLoader() != null - ? beanFactory.getBeanClassLoader() : getClass().getClassLoader()); - } - - @Nullable - Executable detectBeanInstanceExecutable(BeanDefinition beanDefinition) { - Supplier beanType = () -> getBeanType(beanDefinition); - List valueTypes = beanDefinition.hasConstructorArgumentValues() - ? determineParameterValueTypes(beanDefinition.getConstructorArgumentValues()) : Collections.emptyList(); - Method resolvedFactoryMethod = resolveFactoryMethod(beanDefinition, valueTypes); - if (resolvedFactoryMethod != null) { - return resolvedFactoryMethod; - } - Class factoryBeanClass = getFactoryBeanClass(beanDefinition); - if (factoryBeanClass != null && !factoryBeanClass.equals(beanDefinition.getResolvableType().toClass())) { - ResolvableType resolvableType = beanDefinition.getResolvableType(); - boolean isCompatible = ResolvableType.forClass(factoryBeanClass).as(FactoryBean.class) - .getGeneric(0).isAssignableFrom(resolvableType); - if (isCompatible) { - return resolveConstructor(() -> ResolvableType.forClass(factoryBeanClass), valueTypes); - } - else { - throw new IllegalStateException(String.format("Incompatible target type '%s' for factory bean '%s'", - resolvableType.toClass().getName(), factoryBeanClass.getName())); - } - } - Executable resolvedConstructor = resolveConstructor(beanType, valueTypes); - if (resolvedConstructor != null) { - return resolvedConstructor; - } - Executable resolvedConstructorOrFactoryMethod = getField(beanDefinition, - "resolvedConstructorOrFactoryMethod", Executable.class); - if (resolvedConstructorOrFactoryMethod != null) { - logger.error("resolvedConstructorOrFactoryMethod required for " + beanDefinition); - return resolvedConstructorOrFactoryMethod; - } - return null; - } - - private List determineParameterValueTypes(ConstructorArgumentValues constructorArgumentValues) { - List parameterTypes = new ArrayList<>(); - for (ValueHolder valueHolder : constructorArgumentValues.getIndexedArgumentValues().values()) { - if (valueHolder.getType() != null) { - parameterTypes.add(ResolvableType.forClass(loadClass(valueHolder.getType()))); - } - else { - Object value = valueHolder.getValue(); - if (value instanceof BeanReference) { - parameterTypes.add(ResolvableType.forClass( - this.beanFactory.getType(((BeanReference) value).getBeanName(), false))); - } - else if (value instanceof BeanDefinition) { - parameterTypes.add(extractTypeFromBeanDefinition(getBeanType((BeanDefinition) value))); - } - else { - parameterTypes.add(ResolvableType.forInstance(value)); - } - } - } - return parameterTypes; - } - - private ResolvableType extractTypeFromBeanDefinition(ResolvableType type) { - if (FactoryBean.class.isAssignableFrom(type.toClass())) { - return type.as(FactoryBean.class).getGeneric(0); - } - return type; - } - - @Nullable - private Method resolveFactoryMethod(BeanDefinition beanDefinition, List valueTypes) { - if (beanDefinition instanceof RootBeanDefinition rbd) { - Method resolvedFactoryMethod = rbd.getResolvedFactoryMethod(); - if (resolvedFactoryMethod != null) { - return resolvedFactoryMethod; - } - } - String factoryMethodName = beanDefinition.getFactoryMethodName(); - if (factoryMethodName != null) { - List methods = new ArrayList<>(); - Class beanClass = getBeanClass(beanDefinition); - if (beanClass == null) { - throw new IllegalStateException("Failed to determine bean class of " + beanDefinition); - } - ReflectionUtils.doWithMethods(beanClass, methods::add, - method -> isFactoryMethodCandidate(beanClass, method, factoryMethodName)); - if (methods.size() >= 1) { - Function> parameterTypesFactory = method -> { - List types = new ArrayList<>(); - for (int i = 0; i < method.getParameterCount(); i++) { - types.add(ResolvableType.forMethodParameter(method, i)); - } - return types; - }; - return (Method) resolveFactoryMethod(methods, parameterTypesFactory, valueTypes); - } - } - return null; - } - - private boolean isFactoryMethodCandidate(Class beanClass, Method method, String factoryMethodName) { - if (method.getName().equals(factoryMethodName)) { - if (Modifier.isStatic(method.getModifiers())) { - return method.getDeclaringClass().equals(beanClass); - } - return !Modifier.isPrivate(method.getModifiers()); - } - return false; - } - - @Nullable - private Executable resolveConstructor(Supplier beanType, List valueTypes) { - Class type = ClassUtils.getUserClass(beanType.get().toClass()); - Constructor[] constructors = type.getDeclaredConstructors(); - if (constructors.length == 1) { - return constructors[0]; - } - for (Constructor constructor : constructors) { - if (MergedAnnotations.from(constructor).isPresent(Autowired.class)) { - return constructor; - } - } - Function, List> parameterTypesFactory = executable -> { - List types = new ArrayList<>(); - for (int i = 0; i < executable.getParameterCount(); i++) { - types.add(ResolvableType.forConstructorParameter(executable, i)); - } - return types; - }; - List matches = Arrays.stream(constructors) - .filter(executable -> match(parameterTypesFactory.apply(executable), - valueTypes, FallbackMode.NONE)).toList(); - if (matches.size() == 1) { - return matches.get(0); - } - List assignableElementFallbackMatches = Arrays.stream(constructors) - .filter(executable -> match(parameterTypesFactory.apply(executable), - valueTypes, FallbackMode.ASSIGNABLE_ELEMENT)).toList(); - if (assignableElementFallbackMatches.size() == 1) { - return assignableElementFallbackMatches.get(0); - } - List typeConversionFallbackMatches = Arrays.stream(constructors) - .filter(executable -> match(parameterTypesFactory.apply(executable), - valueTypes, ExecutableProvider.FallbackMode.TYPE_CONVERSION)).toList(); - return (typeConversionFallbackMatches.size() == 1) ? typeConversionFallbackMatches.get(0) : null; - } - - private Executable resolveFactoryMethod(List executables, - Function> parameterTypesFactory, List valueTypes) { - List matches = executables.stream() - .filter(executable -> match(parameterTypesFactory.apply(executable), - valueTypes, ExecutableProvider.FallbackMode.NONE)).toList(); - if (matches.size() == 1) { - return matches.get(0); - } - List assignableElementFallbackMatches = executables.stream() - .filter(executable -> match(parameterTypesFactory.apply(executable), - valueTypes, ExecutableProvider.FallbackMode.ASSIGNABLE_ELEMENT)).toList(); - if (assignableElementFallbackMatches.size() == 1) { - return assignableElementFallbackMatches.get(0); - } - List typeConversionFallbackMatches = executables.stream() - .filter(executable -> match(parameterTypesFactory.apply(executable), - valueTypes, ExecutableProvider.FallbackMode.TYPE_CONVERSION)).toList(); - if (typeConversionFallbackMatches.size() > 1) { - throw new IllegalStateException("Multiple matches with parameters '" - + valueTypes + "': " + typeConversionFallbackMatches); - } - return (typeConversionFallbackMatches.size() == 1) ? typeConversionFallbackMatches.get(0) : null; - } - - private boolean match(List parameterTypes, List valueTypes, - ExecutableProvider.FallbackMode fallbackMode) { - if (parameterTypes.size() != valueTypes.size()) { - return false; - } - for (int i = 0; i < parameterTypes.size(); i++) { - if (!isMatch(parameterTypes.get(i), valueTypes.get(i), fallbackMode)) { - return false; - } - } - return true; - } - - private boolean isMatch(ResolvableType parameterType, ResolvableType valueType, - ExecutableProvider.FallbackMode fallbackMode) { - if (isAssignable(valueType).test(parameterType)) { - return true; - } - return switch (fallbackMode) { - case ASSIGNABLE_ELEMENT -> isAssignable(valueType).test(extractElementType(parameterType)); - case TYPE_CONVERSION -> typeConversionFallback(valueType).test(parameterType); - default -> false; - }; - } - - private Predicate isAssignable(ResolvableType valueType) { - return parameterType -> { - if (valueType.hasUnresolvableGenerics()) { - return parameterType.toClass().isAssignableFrom(valueType.toClass()); - } - else { - return parameterType.isAssignableFrom(valueType); - } - }; - } - - private ResolvableType extractElementType(ResolvableType parameterType) { - if (parameterType.isArray()) { - return parameterType.getComponentType(); - } - if (Collection.class.isAssignableFrom(parameterType.toClass())) { - return parameterType.as(Collection.class).getGeneric(0); - } - return ResolvableType.NONE; - } - - private Predicate typeConversionFallback(ResolvableType valueType) { - return parameterType -> { - if (valueOrCollection(valueType, this::isStringForClassFallback).test(parameterType)) { - return true; - } - return valueOrCollection(valueType, this::isSimpleConvertibleType).test(parameterType); - }; - } - - private Predicate valueOrCollection(ResolvableType valueType, - Function> predicateProvider) { - return parameterType -> { - if (predicateProvider.apply(valueType).test(parameterType)) { - return true; - } - if (predicateProvider.apply(extractElementType(valueType)).test(extractElementType(parameterType))) { - return true; - } - return (predicateProvider.apply(valueType).test(extractElementType(parameterType))); - }; - } - - /** - * Return a {@link Predicate} for a parameter type that checks if its target value - * is a {@link Class} and the value type is a {@link String}. This is a regular use - * cases where a {@link Class} is defined in the bean definition as an FQN. - * @param valueType the type of the value - * @return a predicate to indicate a fallback match for a String to Class parameter - */ - private Predicate isStringForClassFallback(ResolvableType valueType) { - return parameterType -> (valueType.isAssignableFrom(String.class) - && parameterType.isAssignableFrom(Class.class)); - } - - private Predicate isSimpleConvertibleType(ResolvableType valueType) { - return parameterType -> isSimpleConvertibleType(parameterType.toClass()) - && isSimpleConvertibleType(valueType.toClass()); - } - - @Nullable - private Class getFactoryBeanClass(BeanDefinition beanDefinition) { - if (beanDefinition instanceof RootBeanDefinition rbd) { - if (rbd.hasBeanClass()) { - Class beanClass = rbd.getBeanClass(); - return FactoryBean.class.isAssignableFrom(beanClass) ? beanClass : null; - } - } - return null; - } - - @Nullable - private Class getBeanClass(BeanDefinition beanDefinition) { - if (beanDefinition instanceof AbstractBeanDefinition abd) { - return abd.hasBeanClass() ? abd.getBeanClass() : loadClass(abd.getBeanClassName()); - } - return (beanDefinition.getBeanClassName() != null) ? loadClass(beanDefinition.getBeanClassName()) : null; - } - - private ResolvableType getBeanType(BeanDefinition beanDefinition) { - ResolvableType resolvableType = beanDefinition.getResolvableType(); - if (resolvableType != ResolvableType.NONE) { - return resolvableType; - } - if (beanDefinition instanceof RootBeanDefinition rbd) { - if (rbd.hasBeanClass()) { - return ResolvableType.forClass(rbd.getBeanClass()); - } - } - String beanClassName = beanDefinition.getBeanClassName(); - if (beanClassName != null) { - return ResolvableType.forClass(loadClass(beanClassName)); - } - throw new IllegalStateException("Failed to determine bean class of " + beanDefinition); - } - - private Class loadClass(String beanClassName) { - try { - return ClassUtils.forName(beanClassName, this.classLoader); - } - catch (ClassNotFoundException ex) { - throw new IllegalStateException("Failed to load class " + beanClassName); - } - } - - @Nullable - private T getField(BeanDefinition beanDefinition, String fieldName, Class targetType) { - Field field = ReflectionUtils.findField(RootBeanDefinition.class, fieldName); - ReflectionUtils.makeAccessible(field); - return targetType.cast(ReflectionUtils.getField(field, beanDefinition)); - } - - public static boolean isSimpleConvertibleType(Class type) { - return (type.isPrimitive() && type != void.class) || - type == Double.class || type == Float.class || type == Long.class || - type == Integer.class || type == Short.class || type == Character.class || - type == Byte.class || type == Boolean.class || type == String.class; - } - - - enum FallbackMode { - - NONE, - - ASSIGNABLE_ELEMENT, - - TYPE_CONVERSION - - } - - } - -} diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/generator/InjectionGenerator.java b/spring-beans/src/main/java/org/springframework/beans/factory/generator/InjectionGenerator.java deleted file mode 100644 index e0c0ded80b..0000000000 --- a/spring-beans/src/main/java/org/springframework/beans/factory/generator/InjectionGenerator.java +++ /dev/null @@ -1,255 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.beans.factory.generator; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Executable; -import java.lang.reflect.Field; -import java.lang.reflect.Member; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.lang.reflect.Parameter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.function.Consumer; - -import org.springframework.aot.generator.ProtectedAccess; -import org.springframework.aot.generator.ProtectedAccess.Options; -import org.springframework.beans.factory.generator.config.BeanDefinitionRegistrar.BeanInstanceContext; -import org.springframework.javapoet.CodeBlock; -import org.springframework.javapoet.CodeBlock.Builder; -import org.springframework.util.ClassUtils; - -/** - * Generate the necessary code to {@link #generateInstantiation(Executable) - * create a bean instance} or {@link #generateInjection(Member, boolean) - * inject dependencies}. - *

- * The generator assumes a number of variables to be accessible: - *

    - *
  • {@code beanFactory}: the general {@code DefaultListableBeanFactory}
  • - *
  • {@code instanceContext}: the {@link BeanInstanceContext} callback
  • - *
  • {@code bean}: the variable that refers to the bean instance
  • - *
- * - * @author Stephane Nicoll - * @author Brian Clozel - */ -public class InjectionGenerator { - - private static final Options METHOD_INJECTION_OPTIONS = Options.defaults() - .useReflection(member -> false).build(); - - private final BeanParameterGenerator parameterGenerator = new BeanParameterGenerator(); - - private final BeanFieldGenerator fieldGenerator = new BeanFieldGenerator(); - - - /** - * Generate the necessary code to instantiate an object using the specified - * {@link Executable}. The code is suitable to be assigned to a variable - * or used as a {@literal return} statement. - * @param creator the executable to invoke to create an instance of the - * requested object - * @return the code to instantiate an object using the specified executable - */ - public CodeBlock generateInstantiation(Executable creator) { - if (creator instanceof Constructor constructor) { - return generateConstructorInstantiation(constructor); - } - if (creator instanceof Method method) { - return generateMethodInstantiation(method); - } - throw new IllegalArgumentException("Could not handle creator " + creator); - } - - /** - * Generate the code to inject a value resolved by {@link BeanInstanceContext} - * in the specified {@link Member}. - * @param member the field or method to inject - * @param required whether the value is required - * @return a statement that injects a value to the specified member - * @see #getProtectedAccessInjectionOptions(Member) - */ - public CodeBlock generateInjection(Member member, boolean required) { - if (member instanceof Method method) { - return generateMethodInjection(method, required); - } - if (member instanceof Field field) { - return generateFieldInjection(field, required); - } - throw new IllegalArgumentException("Could not handle member " + member); - } - - /** - * Return the {@link Options} to use if protected access analysis is - * required for the specified {@link Member}. - * @param member the field or method to handle - * @return the options to use to analyse protected access - * @see ProtectedAccess - */ - public Options getProtectedAccessInjectionOptions(Member member) { - if (member instanceof Method) { - return METHOD_INJECTION_OPTIONS; - } - if (member instanceof Field) { - return BeanFieldGenerator.FIELD_OPTIONS; - } - throw new IllegalArgumentException("Could not handle member " + member); - } - - private CodeBlock generateConstructorInstantiation(Constructor creator) { - Builder code = CodeBlock.builder(); - Class declaringType = ClassUtils.getUserClass(creator.getDeclaringClass()); - boolean innerClass = isInnerClass(declaringType); - Class[] parameterTypes = Arrays.stream(creator.getParameters()).map(Parameter::getType) - .toArray(Class[]::new); - // Shortcut for common case - if (innerClass && parameterTypes.length == 1) { - code.add("beanFactory.getBean($T.class).new $L()", declaringType.getEnclosingClass(), - declaringType.getSimpleName()); - return code.build(); - } - if (parameterTypes.length == 0) { - code.add("new $T()", declaringType); - return code.build(); - } - boolean isAmbiguous = Arrays.stream(creator.getDeclaringClass().getDeclaredConstructors()) - .filter(constructor -> constructor.getParameterCount() == parameterTypes.length).count() > 1; - code.add("instanceContext.create(beanFactory, (attributes) ->"); - List parameters = resolveParameters(creator.getParameters(), isAmbiguous); - if (innerClass) { // Remove the implicit argument - parameters.remove(0); - } - - code.add(" "); - if (innerClass) { - code.add("beanFactory.getBean($T.class).new $L(", declaringType.getEnclosingClass(), - declaringType.getSimpleName()); - } - else { - code.add("new $T(", declaringType); - } - for (int i = 0; i < parameters.size(); i++) { - code.add(parameters.get(i)); - if (i < parameters.size() - 1) { - code.add(", "); - } - } - code.add(")"); - code.add(")"); - return code.build(); - } - - private static boolean isInnerClass(Class type) { - return type.isMemberClass() && !Modifier.isStatic(type.getModifiers()); - } - - private CodeBlock generateMethodInstantiation(Method injectionPoint) { - if (injectionPoint.getParameterCount() == 0) { - Builder code = CodeBlock.builder(); - Class declaringType = injectionPoint.getDeclaringClass(); - if (Modifier.isStatic(injectionPoint.getModifiers())) { - code.add("$T", declaringType); - } - else { - code.add("beanFactory.getBean($T.class)", declaringType); - } - code.add(".$L()", injectionPoint.getName()); - return code.build(); - } - return generateMethodInvocation(injectionPoint, code -> code.add(".create(beanFactory, (attributes) ->"), true); - } - - private CodeBlock generateMethodInjection(Method injectionPoint, boolean required) { - Consumer attributesResolver = code -> { - if (required) { - code.add(".invoke(beanFactory, (attributes) ->"); - } - else { - code.add(".resolve(beanFactory, false).ifResolved((attributes) ->"); - } - }; - return generateMethodInvocation(injectionPoint, attributesResolver, false); - } - - private CodeBlock generateMethodInvocation(Method injectionPoint, Consumer attributesResolver, boolean instantiation) { - Builder code = CodeBlock.builder(); - code.add("instanceContext"); - if (!instantiation) { - code.add(".method($S, ", injectionPoint.getName()); - code.add(this.parameterGenerator.generateExecutableParameterTypes(injectionPoint)); - code.add(")\n").indent().indent(); - } - attributesResolver.accept(code); - Parameter[] methodParameters = injectionPoint.getParameters(); - boolean isAmbiguous = Arrays.stream(injectionPoint.getDeclaringClass().getDeclaredMethods()) - .filter(method -> method.getName().equals(injectionPoint.getName()) && method.getParameterCount() == methodParameters.length).count() > 1; - List parameters = resolveParameters(methodParameters, isAmbiguous); - code.add(" "); - if (instantiation) { - if (Modifier.isStatic(injectionPoint.getModifiers())) { - code.add("$T", injectionPoint.getDeclaringClass()); - } - else { - code.add("beanFactory.getBean($T.class)", injectionPoint.getDeclaringClass()); - } - } - else { - code.add("bean"); - } - code.add(".$L(", injectionPoint.getName()); - code.add(CodeBlock.join(parameters, ", ")); - code.add(")"); - code.add(")"); - if (!instantiation) { - code.unindent().unindent(); - } - return code.build(); - } - - CodeBlock generateFieldInjection(Field injectionPoint, boolean required) { - Builder code = CodeBlock.builder(); - code.add("instanceContext.field($S", injectionPoint.getName()); - code.add(")\n").indent().indent(); - if (required) { - code.add(".invoke(beanFactory, "); - } - else { - code.add(".resolve(beanFactory, false).ifResolved("); - } - code.add(this.fieldGenerator.generateSetValue("bean", injectionPoint, - CodeBlock.of("attributes.get(0)")).toLambda("(attributes) ->")); - code.add(")").unindent().unindent(); - return code.build(); - } - - private List resolveParameters(Parameter[] parameters, boolean shouldCast) { - List parameterValues = new ArrayList<>(); - for (int i = 0; i < parameters.length; i++) { - if (shouldCast) { - parameterValues.add(CodeBlock.of("attributes.get($L, $T.class)", i, parameters[i].getType())); - } - else { - parameterValues.add(CodeBlock.of("attributes.get($L)", i)); - } - } - return parameterValues; - } - -} diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/generator/InnerBeanRegistrationBeanFactoryContribution.java b/spring-beans/src/main/java/org/springframework/beans/factory/generator/InnerBeanRegistrationBeanFactoryContribution.java deleted file mode 100644 index e4ecb7380e..0000000000 --- a/spring-beans/src/main/java/org/springframework/beans/factory/generator/InnerBeanRegistrationBeanFactoryContribution.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.beans.factory.generator; - -import org.springframework.beans.factory.generator.config.BeanDefinitionRegistrar; -import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.javapoet.CodeBlock; - -/** - * A specialization of {@link BeanRegistrationContributionProvider} that handles - * inner bean definitions. - * - * @author Stephane Nicoll - */ -class InnerBeanRegistrationBeanFactoryContribution extends BeanRegistrationBeanFactoryContribution { - - InnerBeanRegistrationBeanFactoryContribution(String beanName, RootBeanDefinition beanDefinition, - BeanInstantiationGenerator beanInstantiationGenerator, - DefaultBeanRegistrationContributionProvider innerBeanRegistrationContributionProvider) { - super(beanName, beanDefinition, beanInstantiationGenerator, innerBeanRegistrationContributionProvider); - } - - @Override - protected CodeBlock initializeBeanDefinitionRegistrar() { - return CodeBlock.of("$T.inner(", BeanDefinitionRegistrar.class); - } - -} diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/generator/config/BeanDefinitionRegistrar.java b/spring-beans/src/main/java/org/springframework/beans/factory/generator/config/BeanDefinitionRegistrar.java deleted file mode 100644 index 111d8e43e8..0000000000 --- a/spring-beans/src/main/java/org/springframework/beans/factory/generator/config/BeanDefinitionRegistrar.java +++ /dev/null @@ -1,401 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.beans.factory.generator.config; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Executable; -import java.lang.reflect.Field; -import java.lang.reflect.Member; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.beans.FatalBeanException; -import org.springframework.beans.factory.BeanFactoryUtils; -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.core.MethodIntrospector; -import org.springframework.core.ResolvableType; -import org.springframework.core.log.LogMessage; -import org.springframework.lang.Nullable; -import org.springframework.util.Assert; -import org.springframework.util.ObjectUtils; -import org.springframework.util.ReflectionUtils; - -/** - * {@link BeanDefinition} registration mechanism offering transparent - * dependency resolution, as well as exception management. - * - *

Used by code generators and for internal use within the framework - * only. - * - * @author Stephane Nicoll - * @since 6.0 - */ -public final class BeanDefinitionRegistrar { - - private static final Log logger = LogFactory.getLog(BeanDefinitionRegistrar.class); - - @Nullable - private final String beanName; - - private final Class beanClass; - - @Nullable - private final ResolvableType beanType; - - private final List> customizers; - - @Nullable - private Executable instanceCreator; - - @Nullable - private RootBeanDefinition beanDefinition; - - - private BeanDefinitionRegistrar(@Nullable String beanName, Class beanClass, @Nullable ResolvableType beanType) { - this.beanName = beanName; - this.beanClass = beanClass; - this.beanType = beanType; - this.customizers = new ArrayList<>(); - } - - /** - * Initialize the registration of a bean with the specified name and type. - * @param beanName the name of the bean - * @param beanType the type of the bean - * @return a registrar for the specified bean - */ - public static BeanDefinitionRegistrar of(String beanName, ResolvableType beanType) { - return new BeanDefinitionRegistrar(beanName, beanType.toClass(), beanType); - } - - /** - * Initialize the registration of a bean with the specified name and type. - * @param beanName the name of the bean - * @param beanType the type of the bean - * @return a registrar for the specified bean - */ - public static BeanDefinitionRegistrar of(String beanName, Class beanType) { - return new BeanDefinitionRegistrar(beanName, beanType, null); - } - - /** - * Initialize the registration of an inner bean with the specified type. - * @param beanType the type of the inner bean - * @return a registrar for the specified inner bean - */ - public static BeanDefinitionRegistrar inner(ResolvableType beanType) { - return new BeanDefinitionRegistrar(null, beanType.toClass(), beanType); - } - - /** - * Initialize the registration of an inner bean with the specified type. - * @param beanType the type of the inner bean - * @return a registrar for the specified inner bean - */ - public static BeanDefinitionRegistrar inner(Class beanType) { - return new BeanDefinitionRegistrar(null, beanType, null); - } - - /** - * Specify the factory method to use to instantiate the bean. - * @param declaredType the {@link Method#getDeclaringClass() declared type} - * of the factory method. - * @param name the name of the method - * @param parameterTypes the parameter types of the method - * @return {@code this}, to facilitate method chaining - * @see RootBeanDefinition#getResolvedFactoryMethod() - */ - public BeanDefinitionRegistrar withFactoryMethod(Class declaredType, String name, Class... parameterTypes) { - this.instanceCreator = getMethod(declaredType, name, parameterTypes); - return this; - } - - /** - * Specify the constructor to use to instantiate the bean. - * @param parameterTypes the parameter types of the constructor - * @return {@code this}, to facilitate method chaining - */ - public BeanDefinitionRegistrar withConstructor(Class... parameterTypes) { - this.instanceCreator = getConstructor(this.beanClass, parameterTypes); - return this; - } - - /** - * Specify how the bean instance should be created and initialized, using - * the {@link BeanInstanceContext} to resolve dependencies if necessary. - * @param instanceContext the {@link BeanInstanceContext} to use - * @return {@code this}, to facilitate method chaining - */ - public BeanDefinitionRegistrar instanceSupplier(ThrowableFunction instanceContext) { - return customize(beanDefinition -> beanDefinition.setInstanceSupplier(() -> - instanceContext.apply(createBeanInstanceContext()))); - } - - /** - * Specify how the bean instance should be created and initialized. - * @return {@code this}, to facilitate method chaining - */ - public BeanDefinitionRegistrar instanceSupplier(ThrowableSupplier instanceSupplier) { - return customize(beanDefinition -> beanDefinition.setInstanceSupplier(instanceSupplier)); - } - - /** - * Customize the {@link RootBeanDefinition} using the specified consumer. - * @param bd a consumer for the bean definition - * @return {@code this}, to facilitate method chaining - */ - public BeanDefinitionRegistrar customize(ThrowableConsumer bd) { - this.customizers.add(bd); - return this; - } - - /** - * Register the {@link RootBeanDefinition} defined by this instance to - * the specified bean factory. - * @param beanFactory the bean factory to use - */ - public void register(DefaultListableBeanFactory beanFactory) { - BeanDefinition beanDefinition = toBeanDefinition(); - Assert.state(this.beanName != null, () -> "Bean name not set. Could not register " + beanDefinition); - logger.debug(LogMessage.format("Register bean definition with name '%s'", this.beanName)); - beanFactory.registerBeanDefinition(this.beanName, beanDefinition); - } - - /** - * Return the {@link RootBeanDefinition} defined by this instance. - * @return the bean definition - */ - public RootBeanDefinition toBeanDefinition() { - try { - this.beanDefinition = createBeanDefinition(); - return this.beanDefinition; - } - catch (Exception ex) { - throw new FatalBeanException("Failed to create bean definition for bean with name '" + this.beanName + "'", ex); - } - } - - private RootBeanDefinition createBeanDefinition() { - RootBeanDefinition bd = (RootBeanDefinition) BeanDefinitionBuilder - .rootBeanDefinition(this.beanClass).getBeanDefinition(); - if (this.beanType != null) { - bd.setTargetType(this.beanType); - } - if (this.instanceCreator instanceof Method) { - bd.setResolvedFactoryMethod((Method) this.instanceCreator); - } - this.customizers.forEach(customizer -> customizer.accept(bd)); - return bd; - } - - private BeanInstanceContext createBeanInstanceContext() { - String resolvedBeanName = this.beanName != null ? this.beanName : createInnerBeanName(); - return new BeanInstanceContext(resolvedBeanName, this.beanClass); - } - - private String createInnerBeanName() { - return "(inner bean)" + BeanFactoryUtils.GENERATED_BEAN_NAME_SEPARATOR + - (this.beanDefinition != null ? ObjectUtils.getIdentityHexString(this.beanDefinition) : 0); - } - - @Nullable - private BeanDefinition resolveBeanDefinition(DefaultListableBeanFactory beanFactory) { - return this.beanDefinition; - } - - private static Constructor getConstructor(Class beanType, Class... parameterTypes) { - try { - return beanType.getDeclaredConstructor(parameterTypes); - } - catch (NoSuchMethodException ex) { - String message = String.format("No constructor with type(s) [%s] found on %s", - toCommaSeparatedNames(parameterTypes), beanType.getName()); - throw new IllegalArgumentException(message, ex); - } - } - - private static Method getMethod(Class declaredType, String methodName, Class... parameterTypes) { - Method method = ReflectionUtils.findMethod(declaredType, methodName, parameterTypes); - if (method == null) { - String message = String.format("No method '%s' with type(s) [%s] found on %s", methodName, - toCommaSeparatedNames(parameterTypes), declaredType.getName()); - throw new IllegalArgumentException(message); - } - return MethodIntrospector.selectInvocableMethod(method, declaredType); - } - - private static String toCommaSeparatedNames(Class... parameterTypes) { - return Arrays.stream(parameterTypes).map(Class::getName).collect(Collectors.joining(", ")); - } - - - /** - * Callback interface used by instance suppliers that need to resolve - * dependencies for the {@link Executable} used to create the instance - * as well as any {@link Member} that should be handled by the context. - */ - public final class BeanInstanceContext { - - private final String beanName; - - private final Class beanType; - - private BeanInstanceContext(String beanName, Class beanType) { - this.beanName = beanName; - this.beanType = beanType; - } - - /** - * Return the bean instance using the {@code factory}. - * @param beanFactory the bean factory to use - * @param factory a function that returns the bean instance based on - * the resolved attributes required by its instance creator - * @param the type of the bean - * @return the bean instance - */ - public T create(DefaultListableBeanFactory beanFactory, ThrowableFunction factory) { - return resolveInstanceCreator(BeanDefinitionRegistrar.this.instanceCreator).create(beanFactory, factory); - } - - private InjectedElementResolver resolveInstanceCreator(@Nullable Executable instanceCreator) { - if (instanceCreator instanceof Method) { - return new InjectedConstructionResolver(instanceCreator, instanceCreator.getDeclaringClass(), this.beanName, - BeanDefinitionRegistrar.this::resolveBeanDefinition); - } - if (instanceCreator instanceof Constructor) { - return new InjectedConstructionResolver(instanceCreator, this.beanType, this.beanName, - BeanDefinitionRegistrar.this::resolveBeanDefinition); - } - throw new IllegalStateException("No factory method or constructor is set"); - } - - /** - * Create an {@link InjectedElementResolver} for the specified field. - * @param name the name of the field - * @return a resolved for the specified field - */ - public InjectedElementResolver field(String name) { - return new InjectedFieldResolver(getField(name), this.beanName); - } - - /** - * Create an {@link InjectedElementResolver} for the specified bean method. - * @param name the name of the method on the target bean - * @param parameterTypes the method parameter types - * @return a resolved for the specified bean method - */ - public InjectedElementResolver method(String name, Class... parameterTypes) { - return new InjectedMethodResolver(getMethod(this.beanType, name, parameterTypes), this.beanType, this.beanName); - } - - private Field getField(String fieldName) { - Field field = ReflectionUtils.findField(this.beanType, fieldName); - Assert.notNull(field, () -> "No field '" + fieldName + "' found on " + this.beanType.getName()); - return field; - } - - } - - /** - * A {@link Consumer} that allows to invoke code that throws a checked exception. - * - * @author Stephane Nicoll - * @param the type of the input to the operation - */ - @FunctionalInterface - public interface ThrowableConsumer extends Consumer { - - void acceptWithException(T t) throws Exception; - - @Override - default void accept(T t) { - try { - acceptWithException(t); - } - catch (RuntimeException ex) { - throw ex; - } - catch (Exception ex) { - throw new RuntimeException(ex.getMessage(), ex); - } - } - - } - - /** - * A {@link Function} that allows to invoke code that throws a checked exception. - * - * @author Stephane Nicoll - * @param the type of the input to the function - * @param the type of the result of the function - */ - @FunctionalInterface - public interface ThrowableFunction extends Function { - - R applyWithException(T t) throws Exception; - - @Override - default R apply(T t) { - try { - return applyWithException(t); - } - catch (RuntimeException ex) { - throw ex; - } - catch (Exception ex) { - throw new RuntimeException(ex.getMessage(), ex); - } - } - } - - /** - * A {@link Supplier} that allows to invoke code that throws a checked exception. - * - * @author Stephane Nicoll - * @param the type of results supplied by this supplier - */ - public interface ThrowableSupplier extends Supplier { - - T getWithException() throws Exception; - - @Override - default T get() { - try { - return getWithException(); - } - catch (RuntimeException ex) { - throw ex; - } - catch (Exception ex) { - throw new RuntimeException(ex.getMessage(), ex); - } - } - } - -} diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/generator/config/InjectedConstructionResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/generator/config/InjectedConstructionResolver.java deleted file mode 100644 index d1257a7eac..0000000000 --- a/spring-beans/src/main/java/org/springframework/beans/factory/generator/config/InjectedConstructionResolver.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.beans.factory.generator.config; - -import java.lang.reflect.Array; -import java.lang.reflect.Constructor; -import java.lang.reflect.Executable; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.StringJoiner; -import java.util.function.Function; -import java.util.function.Supplier; - -import org.springframework.beans.BeansException; -import org.springframework.beans.TypeConverter; -import org.springframework.beans.factory.InjectionPoint; -import org.springframework.beans.factory.NoSuchBeanDefinitionException; -import org.springframework.beans.factory.UnsatisfiedDependencyException; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.ConstructorArgumentValues; -import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder; -import org.springframework.beans.factory.config.DependencyDescriptor; -import org.springframework.beans.factory.support.BeanDefinitionValueResolver; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.core.CollectionFactory; -import org.springframework.core.MethodParameter; - -/** - * An {@link InjectedElementResolver} for an {@link Executable} that creates - * a bean instance. - * - * @author Stephane Nicoll - */ - -class InjectedConstructionResolver implements InjectedElementResolver { - - private final Executable executable; - - private final Class targetType; - - private final String beanName; - - private final Function beanDefinitionResolver; - - InjectedConstructionResolver(Executable executable, Class targetType, String beanName, - Function beanDefinitionResolver) { - this.executable = executable; - this.targetType = targetType; - this.beanName = beanName; - this.beanDefinitionResolver = beanDefinitionResolver; - } - - - Executable getExecutable() { - return this.executable; - } - - @Override - public InjectedElementAttributes resolve(DefaultListableBeanFactory beanFactory, boolean required) { - int argumentCount = this.executable.getParameterCount(); - List arguments = new ArrayList<>(); - Set autowiredBeans = new LinkedHashSet<>(argumentCount); - TypeConverter typeConverter = beanFactory.getTypeConverter(); - ConstructorArgumentValues argumentValues = resolveArgumentValues(beanFactory); - for (int i = 0; i < argumentCount; i++) { - MethodParameter methodParam = createMethodParameter(i); - ValueHolder valueHolder = argumentValues.getIndexedArgumentValue(i, null); - if (valueHolder != null) { - if (valueHolder.isConverted()) { - arguments.add(valueHolder.getConvertedValue()); - } - else { - Object userValue = beanFactory.getTypeConverter() - .convertIfNecessary(valueHolder.getValue(), methodParam.getParameterType()); - arguments.add(userValue); - } - } - else { - DependencyDescriptor depDescriptor = new DependencyDescriptor(methodParam, true); - depDescriptor.setContainingClass(this.targetType); - try { - Object arg = resolveDependency(() -> beanFactory.resolveDependency( - depDescriptor, this.beanName, autowiredBeans, typeConverter), methodParam.getParameterType()); - arguments.add(arg); - } - catch (BeansException ex) { - throw new UnsatisfiedDependencyException(null, this.beanName, new InjectionPoint(methodParam), ex); - } - } - } - return new InjectedElementAttributes(arguments); - } - - private Object resolveDependency(Supplier resolvedDependency, Class dependencyType) { - try { - return resolvedDependency.get(); - } - catch (NoSuchBeanDefinitionException ex) { - // Single constructor or factory method -> let's return an empty array/collection - // for e.g. a vararg or a non-null List/Set/Map parameter. - if (dependencyType.isArray()) { - return Array.newInstance(dependencyType.getComponentType(), 0); - } - else if (CollectionFactory.isApproximableCollectionType(dependencyType)) { - return CollectionFactory.createCollection(dependencyType, 0); - } - else if (CollectionFactory.isApproximableMapType(dependencyType)) { - return CollectionFactory.createMap(dependencyType, 0); - } - throw ex; - } - } - - private ConstructorArgumentValues resolveArgumentValues(DefaultListableBeanFactory beanFactory) { - ConstructorArgumentValues resolvedValues = new ConstructorArgumentValues(); - BeanDefinition beanDefinition = this.beanDefinitionResolver.apply(beanFactory); - if (beanDefinition == null || !beanDefinition.hasConstructorArgumentValues()) { - return resolvedValues; - } - ConstructorArgumentValues argumentValues = beanDefinition.getConstructorArgumentValues(); - BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(beanFactory, - this.beanName, beanDefinition); - for (Map.Entry entry : argumentValues.getIndexedArgumentValues().entrySet()) { - int index = entry.getKey(); - ValueHolder valueHolder = entry.getValue(); - if (valueHolder.isConverted()) { - resolvedValues.addIndexedArgumentValue(index, valueHolder); - } - else { - Object resolvedValue = - valueResolver.resolveValueIfNecessary("constructor argument", valueHolder.getValue()); - ValueHolder resolvedValueHolder = - new ValueHolder(resolvedValue, valueHolder.getType(), valueHolder.getName()); - resolvedValueHolder.setSource(valueHolder); - resolvedValues.addIndexedArgumentValue(index, resolvedValueHolder); - } - } - return resolvedValues; - } - - private MethodParameter createMethodParameter(int index) { - if (this.executable instanceof Constructor) { - return new MethodParameter((Constructor) this.executable, index); - } - else { - return new MethodParameter((Method) this.executable, index); - } - } - - @Override - public String toString() { - return new StringJoiner(", ", InjectedConstructionResolver.class.getSimpleName() + "[", "]") - .add("executable=" + this.executable) - .toString(); - } - -} diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/generator/config/InjectedElementAttributes.java b/spring-beans/src/main/java/org/springframework/beans/factory/generator/config/InjectedElementAttributes.java deleted file mode 100644 index a8677f2ee1..0000000000 --- a/spring-beans/src/main/java/org/springframework/beans/factory/generator/config/InjectedElementAttributes.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.beans.factory.generator.config; - -import java.util.List; -import java.util.function.Consumer; - -import org.springframework.lang.Nullable; -import org.springframework.util.Assert; - -/** - * Resolved attributes of an injected element. - * - * @author Stephane Nicoll - * @since 6.0 - */ -public class InjectedElementAttributes { - - @Nullable - private final List attributes; - - - InjectedElementAttributes(@Nullable List attributes) { - this.attributes = attributes; - } - - - /** - * Specify if the attributes have been resolved. - * @return the resolution of the injection - */ - public boolean isResolved() { - return (this.attributes != null); - } - - /** - * Run the specified {@linkplain Runnable task} only if this instance is - * {@link #isResolved() resolved}. - * @param task the task to invoke if attributes are available - */ - public void ifResolved(Runnable task) { - if (isResolved()) { - task.run(); - } - } - - /** - * Invoke the specified {@link Consumer} with the resolved attributes. - * @param attributes the consumer to invoke if this instance is resolved - */ - public void ifResolved(BeanDefinitionRegistrar.ThrowableConsumer attributes) { - ifResolved(() -> attributes.accept(this)); - } - - /** - * Return the resolved attribute at the specified index. - * @param index the attribute index - * @param the type of the attribute - * @return the attribute - */ - @SuppressWarnings("unchecked") - public T get(int index) { - Assert.notNull(this.attributes, "Attributes must not be null"); - return (T) this.attributes.get(index); - } - - /** - * Return the resolved attribute at the specified index. - * @param index the attribute index - * @param type the attribute type - * @param the type of the attribute - * @return the attribute - */ - @SuppressWarnings("unchecked") - public T get(int index, Class type) { - Assert.notNull(this.attributes, "Attributes must not be null"); - return (T) this.attributes.get(index); - } -} diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/generator/config/InjectedElementResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/generator/config/InjectedElementResolver.java deleted file mode 100644 index 5e87e10a28..0000000000 --- a/spring-beans/src/main/java/org/springframework/beans/factory/generator/config/InjectedElementResolver.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.beans.factory.generator.config; - -import org.springframework.beans.factory.support.DefaultListableBeanFactory; - -/** - * Resolve the attributes of an injected element such as a {@code Constructor} - * or a factory {@code Method}. - * - * @author Stephane Nicoll - * @since 6.0 - */ -public interface InjectedElementResolver { - - /** - * Resolve the attributes using the specified bean factory. - * @param beanFactory the bean factory to use - * @return the resolved attributes - */ - default InjectedElementAttributes resolve(DefaultListableBeanFactory beanFactory) { - return resolve(beanFactory, true); - } - - /** - * Resolve the attributes using the specified bean factory. - * @param beanFactory the bean factory to use - * @param required whether the injection point is mandatory - * @return the resolved attributes - */ - InjectedElementAttributes resolve(DefaultListableBeanFactory beanFactory, boolean required); - - /** - * Invoke the specified consumer with the resolved - * {@link InjectedElementAttributes attributes}. - * @param beanFactory the bean factory to use to resolve the attributes - * @param attributes a consumer of the resolved attributes - */ - default void invoke(DefaultListableBeanFactory beanFactory, - BeanDefinitionRegistrar.ThrowableConsumer attributes) { - - InjectedElementAttributes elements = resolve(beanFactory); - attributes.accept(elements); - } - - /** - * Create an instance based on the resolved - * {@link InjectedElementAttributes attributes}. - * @param beanFactory the bean factory to use to resolve the attributes - * @param factory a factory to create the instance based on the resolved attributes - * @param the type of the instance - * @return a new instance - */ - default T create(DefaultListableBeanFactory beanFactory, - BeanDefinitionRegistrar.ThrowableFunction factory) { - - InjectedElementAttributes attributes = resolve(beanFactory); - return factory.apply(attributes); - } - -} diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/generator/config/InjectedFieldResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/generator/config/InjectedFieldResolver.java deleted file mode 100644 index a7d35c2ff0..0000000000 --- a/spring-beans/src/main/java/org/springframework/beans/factory/generator/config/InjectedFieldResolver.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.beans.factory.generator.config; - -import java.lang.reflect.Field; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.Set; - -import org.springframework.beans.BeansException; -import org.springframework.beans.TypeConverter; -import org.springframework.beans.factory.InjectionPoint; -import org.springframework.beans.factory.UnsatisfiedDependencyException; -import org.springframework.beans.factory.config.DependencyDescriptor; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; - -/** - * An {@link InjectedElementResolver} for a {@link Field}. - * - * @author Stephane Nicoll - */ -class InjectedFieldResolver implements InjectedElementResolver { - - private final Field field; - - private final String beanName; - - - /** - * Create a new instance. - * @param field the field to handle - * @param beanName the name of the bean, or {@code null} - */ - InjectedFieldResolver(Field field, String beanName) { - this.field = field; - this.beanName = beanName; - } - - @Override - public InjectedElementAttributes resolve(DefaultListableBeanFactory beanFactory, boolean required) { - DependencyDescriptor desc = new DependencyDescriptor(this.field, required); - desc.setContainingClass(this.field.getType()); - Set autowiredBeanNames = new LinkedHashSet<>(1); - TypeConverter typeConverter = beanFactory.getTypeConverter(); - try { - Object value = beanFactory.resolveDependency(desc, this.beanName, autowiredBeanNames, typeConverter); - if (value == null && !required) { - return new InjectedElementAttributes(null); - } - return new InjectedElementAttributes(Collections.singletonList(value)); - } - catch (BeansException ex) { - throw new UnsatisfiedDependencyException(null, this.beanName, new InjectionPoint(this.field), ex); - } - } - -} diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/generator/config/InjectedMethodResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/generator/config/InjectedMethodResolver.java deleted file mode 100644 index c58d616734..0000000000 --- a/spring-beans/src/main/java/org/springframework/beans/factory/generator/config/InjectedMethodResolver.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.beans.factory.generator.config; - -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; - -import org.springframework.beans.BeansException; -import org.springframework.beans.TypeConverter; -import org.springframework.beans.factory.InjectionPoint; -import org.springframework.beans.factory.UnsatisfiedDependencyException; -import org.springframework.beans.factory.config.DependencyDescriptor; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.core.MethodParameter; - -/** - * An {@link InjectedElementResolver} for a {@link Method}. - * - * @author Stephane Nicoll - */ -class InjectedMethodResolver implements InjectedElementResolver { - - private final Method method; - - private final Class target; - - private final String beanName; - - - /** - * Create a new instance. - * @param method the method to handle - * @param target the type on which the method is declared - * @param beanName the name of the bean, or {@code null} - */ - InjectedMethodResolver(Method method, Class target, String beanName) { - this.method = method; - this.target = target; - this.beanName = beanName; - } - - - @Override - public InjectedElementAttributes resolve(DefaultListableBeanFactory beanFactory, boolean required) { - int argumentCount = this.method.getParameterCount(); - List arguments = new ArrayList<>(); - Set autowiredBeans = new LinkedHashSet<>(argumentCount); - TypeConverter typeConverter = beanFactory.getTypeConverter(); - for (int i = 0; i < argumentCount; i++) { - MethodParameter methodParam = new MethodParameter(this.method, i); - DependencyDescriptor depDescriptor = new DependencyDescriptor(methodParam, required); - depDescriptor.setContainingClass(this.target); - try { - Object arg = beanFactory.resolveDependency(depDescriptor, this.beanName, autowiredBeans, typeConverter); - if (arg == null && !required) { - arguments = null; - break; - } - arguments.add(arg); - } - catch (BeansException ex) { - throw new UnsatisfiedDependencyException(null, this.beanName, new InjectionPoint(methodParam), ex); - } - } - return new InjectedElementAttributes(arguments); - } - -} - diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/generator/config/package-info.java b/spring-beans/src/main/java/org/springframework/beans/factory/generator/config/package-info.java deleted file mode 100644 index 821c57180f..0000000000 --- a/spring-beans/src/main/java/org/springframework/beans/factory/generator/config/package-info.java +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Classes used in generated code to ease bean registration. - */ -@NonNullApi -@NonNullFields -package org.springframework.beans.factory.generator.config; - -import org.springframework.lang.NonNullApi; -import org.springframework.lang.NonNullFields; diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/generator/package-info.java b/spring-beans/src/main/java/org/springframework/beans/factory/generator/package-info.java deleted file mode 100644 index 15b08b8fe5..0000000000 --- a/spring-beans/src/main/java/org/springframework/beans/factory/generator/package-info.java +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Support for generating code that represents the state of a bean factory. - */ -@NonNullApi -@NonNullFields -package org.springframework.beans.factory.generator; - -import org.springframework.lang.NonNullApi; -import org.springframework.lang.NonNullFields; diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanInstantiationContributorTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanInstantiationContributorTests.java deleted file mode 100644 index d48d5a71c6..0000000000 --- a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanInstantiationContributorTests.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.beans.factory.annotation; - -import org.junit.jupiter.api.Test; - -import org.springframework.aot.generator.CodeContribution; -import org.springframework.aot.generator.DefaultCodeContribution; -import org.springframework.aot.hint.ExecutableMode; -import org.springframework.aot.hint.RuntimeHints; -import org.springframework.aot.hint.TypeReference; -import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessorTests.ResourceInjectionBean; -import org.springframework.beans.factory.generator.BeanInstantiationContribution; -import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.beans.testfixture.beans.TestBean; -import org.springframework.core.env.Environment; -import org.springframework.javapoet.support.CodeSnippet; -import org.springframework.lang.Nullable; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for code contribution of {@link AutowiredAnnotationBeanPostProcessor}. - * - * @author Stephane Nicoll - */ -class AutowiredAnnotationBeanInstantiationContributionTests { - - @Test - void contributeWithPackageProtectedFieldInjection() { - CodeContribution contribution = contribute(PackageProtectedFieldInjectionSample.class); - assertThat(CodeSnippet.process(contribution.statements().toLambdaBody())).isEqualTo(""" - instanceContext.field("environment") - .invoke(beanFactory, (attributes) -> bean.environment = attributes.get(0))"""); - assertThat(contribution.runtimeHints().reflection().typeHints()).singleElement().satisfies(typeHint -> { - assertThat(typeHint.getType()).isEqualTo(TypeReference.of(PackageProtectedFieldInjectionSample.class)); - assertThat(typeHint.fields()).singleElement().satisfies(fieldHint -> { - assertThat(fieldHint.getName()).isEqualTo("environment"); - assertThat(fieldHint.isAllowWrite()).isTrue(); - assertThat(fieldHint.isAllowUnsafeAccess()).isFalse(); - }); - }); - assertThat(contribution.protectedAccess().getPrivilegedPackageName("com.example")) - .isEqualTo(PackageProtectedFieldInjectionSample.class.getPackageName()); - } - - @Test - void contributeWithPrivateFieldInjection() { - CodeContribution contribution = contribute(PrivateFieldInjectionSample.class); - assertThat(CodeSnippet.process(contribution.statements().toLambdaBody())).isEqualTo(""" - instanceContext.field("environment") - .invoke(beanFactory, (attributes) -> { - Field environmentField = ReflectionUtils.findField(AutowiredAnnotationBeanInstantiationContributionTests.PrivateFieldInjectionSample.class, "environment"); - ReflectionUtils.makeAccessible(environmentField); - ReflectionUtils.setField(environmentField, bean, attributes.get(0)); - })"""); - assertThat(contribution.runtimeHints().reflection().typeHints()).singleElement().satisfies(typeHint -> { - assertThat(typeHint.getType()).isEqualTo(TypeReference.of(PrivateFieldInjectionSample.class)); - assertThat(typeHint.fields()).singleElement().satisfies(fieldHint -> { - assertThat(fieldHint.getName()).isEqualTo("environment"); - assertThat(fieldHint.isAllowWrite()).isTrue(); - assertThat(fieldHint.isAllowUnsafeAccess()).isFalse(); - }); - }); - assertThat(contribution.protectedAccess().isAccessible("com.example")).isTrue(); - } - - @Test - void contributeWithPublicMethodInjection() { - CodeContribution contribution = contribute(PublicMethodInjectionSample.class); - assertThat(CodeSnippet.process(contribution.statements().toLambdaBody())).isEqualTo(""" - instanceContext.method("setTestBean", TestBean.class) - .invoke(beanFactory, (attributes) -> bean.setTestBean(attributes.get(0)))"""); - assertThat(contribution.runtimeHints().reflection().typeHints()).singleElement().satisfies(typeHint -> { - assertThat(typeHint.getType()).isEqualTo(TypeReference.of(PublicMethodInjectionSample.class)); - assertThat(typeHint.methods()).singleElement().satisfies(methodHint -> { - assertThat(methodHint.getName()).isEqualTo("setTestBean"); - assertThat(methodHint.getModes()).contains(ExecutableMode.INTROSPECT); - }); - }); - assertThat(contribution.protectedAccess().isAccessible("com.example")).isTrue(); - } - - @Test - void contributeWithInjectionPoints() { - CodeContribution contribution = contribute(ResourceInjectionBean.class); - assertThat(CodeSnippet.process(contribution.statements().toLambdaBody())).isEqualTo(""" - instanceContext.field("testBean") - .resolve(beanFactory, false).ifResolved((attributes) -> { - Field testBeanField = ReflectionUtils.findField(AutowiredAnnotationBeanPostProcessorTests.ResourceInjectionBean.class, "testBean"); - ReflectionUtils.makeAccessible(testBeanField); - ReflectionUtils.setField(testBeanField, bean, attributes.get(0)); - }); - instanceContext.method("setTestBean2", TestBean.class) - .invoke(beanFactory, (attributes) -> bean.setTestBean2(attributes.get(0)));"""); - assertThat(contribution.runtimeHints().reflection().typeHints()).singleElement().satisfies(typeHint -> { - assertThat(typeHint.fields()).singleElement().satisfies(fieldHint -> - assertThat(fieldHint.getName()).isEqualTo("testBean")); - assertThat(typeHint.methods()).singleElement().satisfies(methodHint -> - assertThat(methodHint.getName()).isEqualTo("setTestBean2")); - }); - assertThat(contribution.protectedAccess().isAccessible("com.example")).isTrue(); - } - - @Test - void contributeWithoutInjectionPoints() { - BeanInstantiationContribution contributor = createContribution(String.class); - assertThat(contributor).isNull(); - } - - private DefaultCodeContribution contribute(Class type) { - BeanInstantiationContribution contributor = createContribution(type); - assertThat(contributor).isNotNull(); - DefaultCodeContribution contribution = new DefaultCodeContribution(new RuntimeHints()); - contributor.applyTo(contribution); - return contribution; - } - - @Nullable - private BeanInstantiationContribution createContribution(Class type) { - AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); - RootBeanDefinition beanDefinition = new RootBeanDefinition(type); - return bpp.contribute(beanDefinition, type, "test"); - } - - - public static class PackageProtectedFieldInjectionSample { - - @Autowired - Environment environment; - - } - - public static class PrivateFieldInjectionSample { - - @Autowired - @SuppressWarnings("unused") - private Environment environment; - - } - - public static class PublicMethodInjectionSample { - - @Autowired - public void setTestBean(TestBean testBean) { - - } - - public void setUnrelated(String unrelated) { - - } - - } - - -} diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/InitDestroyAnnotationBeanPostProcessorTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/InitDestroyAnnotationBeanPostProcessorTests.java index 46d189703e..0ce1ec542f 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/InitDestroyAnnotationBeanPostProcessorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/InitDestroyAnnotationBeanPostProcessorTests.java @@ -18,7 +18,6 @@ package org.springframework.beans.factory.annotation; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.generator.BeanInstantiationContribution; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.RegisteredBean; import org.springframework.beans.factory.support.RootBeanDefinition; @@ -26,11 +25,8 @@ import org.springframework.beans.testfixture.beans.factory.generator.lifecycle.D import org.springframework.beans.testfixture.beans.factory.generator.lifecycle.Init; import org.springframework.beans.testfixture.beans.factory.generator.lifecycle.InitDestroyBean; import org.springframework.beans.testfixture.beans.factory.generator.lifecycle.MultiInitDestroyBean; -import org.springframework.lang.Nullable; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verifyNoInteractions; /** * Tests for {@link InitDestroyAnnotationBeanPostProcessor}. @@ -42,60 +38,6 @@ class InitDestroyAnnotationBeanPostProcessorTests { private DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - @Test - void contributeWithNoCallbackDoesNotMutateRootBeanDefinition() { - RootBeanDefinition beanDefinition = mock(RootBeanDefinition.class); - assertThat(createAotBeanPostProcessor().contribute( - beanDefinition, String.class, "test")).isNull(); - verifyNoInteractions(beanDefinition); - } - - @Test - void contributeWithInitDestroyCallback() { - RootBeanDefinition beanDefinition = new RootBeanDefinition(InitDestroyBean.class); - assertThat(createContribution(beanDefinition)).isNull(); - assertThat(beanDefinition.getInitMethodNames()).containsExactly("initMethod"); - assertThat(beanDefinition.getDestroyMethodNames()).containsExactly("destroyMethod"); - } - - @Test - void contributeWithInitDestroyCallbackRetainCustomMethods() { - RootBeanDefinition beanDefinition = new RootBeanDefinition(InitDestroyBean.class); - beanDefinition.setInitMethodName("customInitMethod"); - beanDefinition.setDestroyMethodNames("customDestroyMethod"); - assertThat(createContribution(beanDefinition)).isNull(); - assertThat(beanDefinition.getInitMethodNames()) - .containsExactly("customInitMethod", "initMethod"); - assertThat(beanDefinition.getDestroyMethodNames()) - .containsExactly("customDestroyMethod", "destroyMethod"); - } - - @Test - void contributeWithInitDestroyCallbackFilterDuplicates() { - RootBeanDefinition beanDefinition = new RootBeanDefinition(InitDestroyBean.class); - beanDefinition.setInitMethodName("initMethod"); - beanDefinition.setDestroyMethodNames("destroyMethod"); - assertThat(createContribution(beanDefinition)).isNull(); - assertThat(beanDefinition.getInitMethodNames()).containsExactly("initMethod"); - assertThat(beanDefinition.getDestroyMethodNames()).containsExactly("destroyMethod"); - } - - @Test - void contributeWithMultipleInitDestroyCallbacks() { - RootBeanDefinition beanDefinition = new RootBeanDefinition(MultiInitDestroyBean.class); - assertThat(createContribution(beanDefinition)).isNull(); - assertThat(beanDefinition.getInitMethodNames()) - .containsExactly("initMethod", "anotherInitMethod"); - assertThat(beanDefinition.getDestroyMethodNames()) - .containsExactly("anotherDestroyMethod", "destroyMethod"); - } - - @Nullable - private BeanInstantiationContribution createContribution(RootBeanDefinition beanDefinition) { - InitDestroyAnnotationBeanPostProcessor bpp = createAotBeanPostProcessor(); - return bpp.contribute(beanDefinition, beanDefinition.getResolvableType().toClass(), "test"); - } - @Test void processAheadOfTimeWhenNoCallbackDoesNotMutateRootBeanDefinition() { RootBeanDefinition beanDefinition = new RootBeanDefinition(String.class); diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/generator/BeanDefinitionsContributionTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/generator/BeanDefinitionsContributionTests.java deleted file mode 100644 index 68e605f62c..0000000000 --- a/spring-beans/src/test/java/org/springframework/beans/factory/generator/BeanDefinitionsContributionTests.java +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.beans.factory.generator; - -import java.io.IOException; -import java.net.URL; -import java.util.Enumeration; -import java.util.List; -import java.util.function.BiPredicate; - -import org.junit.jupiter.api.Test; -import org.mockito.ArgumentMatchers; -import org.mockito.BDDMockito; -import org.mockito.Mockito; - -import org.springframework.aot.generator.DefaultGeneratedTypeContext; -import org.springframework.aot.generator.GeneratedType; -import org.springframework.aot.generator.GeneratedTypeContext; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.javapoet.ClassName; -import org.springframework.javapoet.support.CodeSnippet; -import org.springframework.lang.Nullable; -import org.springframework.util.Assert; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -/** - * Tests for {@link BeanDefinitionsContribution}. - * - * @author Stephane Nicoll - */ -class BeanDefinitionsContributionTests { - - @Test - void loadContributorWithConstructorArgumentOnBeanFactory() { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - beanFactory.setBeanClassLoader(new TestSpringFactoriesClassLoader( - "bean-registration-contribution-provider-constructor.factories")); - BeanDefinitionsContribution contribution = new BeanDefinitionsContribution(beanFactory); - assertThat(contribution).extracting("contributionProviders").asList() - .anySatisfy(provider -> assertThat(provider).isInstanceOfSatisfying(TestConstructorBeanRegistrationContributionProvider.class, - testProvider -> assertThat(testProvider.beanFactory).isSameAs(beanFactory))) - .anySatisfy(provider -> assertThat(provider).isInstanceOf(DefaultBeanRegistrationContributionProvider.class)) - .hasSize(2); - } - - @Test - void contributeThrowsContributionNotFoundIfNoContributionIsAvailable() { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - beanFactory.registerBeanDefinition("test", new RootBeanDefinition()); - BeanDefinitionsContribution contribution = new BeanDefinitionsContribution(beanFactory, - List.of(Mockito.mock(BeanRegistrationContributionProvider.class))); - BeanFactoryInitialization initialization = new BeanFactoryInitialization(createGenerationContext()); - assertThatThrownBy(() -> contribution.applyTo(initialization)) - .isInstanceOfSatisfying(BeanRegistrationContributionNotFoundException.class, ex -> { - assertThat(ex.getBeanName()).isEqualTo("test"); - assertThat(ex.getBeanDefinition()).isSameAs(beanFactory.getMergedBeanDefinition("test")); - }); - } - - @Test - void contributeThrowsBeanRegistrationExceptionIfContributionThrowsException() { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - beanFactory.registerBeanDefinition("test", new RootBeanDefinition()); - BeanFactoryContribution testContribution = Mockito.mock(BeanFactoryContribution.class); - IllegalStateException testException = new IllegalStateException(); - BDDMockito.willThrow(testException).given(testContribution).applyTo(ArgumentMatchers.any(BeanFactoryInitialization.class)); - BeanDefinitionsContribution contribution = new BeanDefinitionsContribution(beanFactory, - List.of(new TestBeanRegistrationContributionProvider("test", testContribution))); - BeanFactoryInitialization initialization = new BeanFactoryInitialization(createGenerationContext()); - assertThatThrownBy(() -> contribution.applyTo(initialization)) - .isInstanceOfSatisfying(BeanDefinitionGenerationException.class, ex -> { - assertThat(ex.getBeanName()).isEqualTo("test"); - assertThat(ex.getBeanDefinition()).isSameAs(beanFactory.getMergedBeanDefinition("test")); - assertThat(ex.getCause()).isEqualTo(testException); - }); - } - - @Test - void contributeGeneratesBeanDefinitionsInOrder() { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - beanFactory.registerBeanDefinition("counter", BeanDefinitionBuilder - .rootBeanDefinition(Integer.class, "valueOf").addConstructorArgValue(42).getBeanDefinition()); - beanFactory.registerBeanDefinition("name", BeanDefinitionBuilder - .rootBeanDefinition(String.class).addConstructorArgValue("Hello").getBeanDefinition()); - CodeSnippet code = contribute(beanFactory, createGenerationContext()); - assertThat(code.getSnippet()).isEqualTo(""" - BeanDefinitionRegistrar.of("counter", Integer.class).withFactoryMethod(Integer.class, "valueOf", int.class) - .instanceSupplier((instanceContext) -> instanceContext.create(beanFactory, (attributes) -> Integer.valueOf(attributes.get(0, int.class)))).customize((bd) -> bd.getConstructorArgumentValues().addIndexedArgumentValue(0, 42)).register(beanFactory); - BeanDefinitionRegistrar.of("name", String.class).withConstructor(String.class) - .instanceSupplier((instanceContext) -> instanceContext.create(beanFactory, (attributes) -> new String(attributes.get(0, String.class)))).customize((bd) -> bd.getConstructorArgumentValues().addIndexedArgumentValue(0, "Hello")).register(beanFactory); - """); - } - - @Test - void getBeanDefinitionWithNoUnderlyingContributorReturnFalseByDefault() { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - BiPredicate excludeFilter = new BeanDefinitionsContribution(beanFactory) - .getBeanDefinitionExcludeFilter(); - assertThat(excludeFilter.test("foo", new RootBeanDefinition())).isFalse(); - } - - @Test - @SuppressWarnings("unchecked") - void getBeanDefinitionExcludeFilterWrapsUnderlyingFilter() { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - beanFactory.registerBeanDefinition("bean1", new RootBeanDefinition()); - beanFactory.registerBeanDefinition("bean2", new RootBeanDefinition()); - BiPredicate excludeFilter1 = Mockito.mock(BiPredicate.class); - BDDMockito.given(excludeFilter1.test(ArgumentMatchers.eq("bean1"), ArgumentMatchers.any(BeanDefinition.class))).willReturn(Boolean.TRUE); - BDDMockito.given(excludeFilter1.test(ArgumentMatchers.eq("bean2"), ArgumentMatchers.any(BeanDefinition.class))).willReturn(Boolean.FALSE); - BiPredicate excludeFilter2 = Mockito.mock(BiPredicate.class); - BDDMockito.given(excludeFilter2.test(ArgumentMatchers.eq("bean2"), ArgumentMatchers.any(BeanDefinition.class))).willReturn(Boolean.TRUE); - BiPredicate excludeFilter = new BeanDefinitionsContribution(beanFactory, List.of( - new TestBeanRegistrationContributionProvider("bean1", mockExcludeFilter(excludeFilter1)), - new TestBeanRegistrationContributionProvider("bean2", mockExcludeFilter(excludeFilter2))) - ).getBeanDefinitionExcludeFilter(); - assertThat(excludeFilter.test("bean2", new RootBeanDefinition())).isTrue(); - Mockito.verify(excludeFilter1).test(ArgumentMatchers.eq("bean2"), ArgumentMatchers.any(BeanDefinition.class)); - Mockito.verify(excludeFilter2).test(ArgumentMatchers.eq("bean2"), ArgumentMatchers.any(BeanDefinition.class)); - assertThat(excludeFilter.test("bean1", new RootBeanDefinition())).isTrue(); - Mockito.verify(excludeFilter1).test(ArgumentMatchers.eq("bean1"), ArgumentMatchers.any(BeanDefinition.class)); - Mockito.verifyNoMoreInteractions(excludeFilter2); - } - - private CodeSnippet contribute(DefaultListableBeanFactory beanFactory, GeneratedTypeContext generationContext) { - BeanDefinitionsContribution contribution = new BeanDefinitionsContribution(beanFactory); - BeanFactoryInitialization initialization = new BeanFactoryInitialization(generationContext); - contribution.applyTo(initialization); - return CodeSnippet.of(initialization.toCodeBlock()); - } - - private GeneratedTypeContext createGenerationContext() { - return new DefaultGeneratedTypeContext("com.example", packageName -> - GeneratedType.of(ClassName.get(packageName, "Test"))); - } - - private BeanFactoryContribution mockExcludeFilter(BiPredicate excludeFilter) { - BeanFactoryContribution contribution = Mockito.mock(BeanFactoryContribution.class); - BDDMockito.given(contribution.getBeanDefinitionExcludeFilter()).willReturn(excludeFilter); - return contribution; - } - - static class TestBeanRegistrationContributionProvider implements BeanRegistrationContributionProvider { - - private final String beanName; - - private final BeanFactoryContribution contribution; - - public TestBeanRegistrationContributionProvider(String beanName, BeanFactoryContribution contribution) { - this.beanName = beanName; - this.contribution = contribution; - } - - @Override - public BeanFactoryContribution getContributionFor(String beanName, RootBeanDefinition beanDefinition) { - return (beanName.equals(this.beanName) ? this.contribution : null); - } - } - - static class TestConstructorBeanRegistrationContributionProvider implements BeanRegistrationContributionProvider { - - private final ConfigurableListableBeanFactory beanFactory; - - TestConstructorBeanRegistrationContributionProvider(ConfigurableListableBeanFactory beanFactory) { - Assert.notNull(beanFactory, "BeanFactory must not be null"); - this.beanFactory = beanFactory; - } - - @Nullable - @Override - public BeanFactoryContribution getContributionFor(String beanName, RootBeanDefinition beanDefinition) { - return null; - } - - } - - static class TestSpringFactoriesClassLoader extends ClassLoader { - - private final String factoriesName; - - TestSpringFactoriesClassLoader(String factoriesName) { - super(BeanDefinitionsContributionTests.class.getClassLoader()); - this.factoriesName = factoriesName; - } - - @Override - public Enumeration getResources(String name) throws IOException { - if ("META-INF/spring.factories".equals(name)) { - return super.getResources("org/springframework/beans/factory/generator/" + this.factoriesName); - } - return super.getResources(name); - } - - } - -} diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/generator/BeanFieldGeneratorTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/generator/BeanFieldGeneratorTests.java deleted file mode 100644 index 331a8f3f5a..0000000000 --- a/spring-beans/src/test/java/org/springframework/beans/factory/generator/BeanFieldGeneratorTests.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.beans.factory.generator; - -import java.lang.reflect.Field; - -import org.junit.jupiter.api.Test; - -import org.springframework.javapoet.CodeBlock; -import org.springframework.javapoet.support.CodeSnippet; -import org.springframework.javapoet.support.MultiStatement; -import org.springframework.util.ReflectionUtils; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link BeanFieldGenerator}. - * - * @author Stephane Nicoll - */ -class BeanFieldGeneratorTests { - - private final BeanFieldGenerator generator = new BeanFieldGenerator(); - - @Test - void generateSetFieldWithPublicField() { - MultiStatement statement = this.generator.generateSetValue("bean", - field(SampleBean.class, "one"), CodeBlock.of("$S", "test")); - assertThat(CodeSnippet.process(statement.toCodeBlock())).isEqualTo(""" - bean.one = "test"; - """); - } - - @Test - void generateSetFieldWithPrivateField() { - MultiStatement statement = this.generator.generateSetValue("example", - field(SampleBean.class, "two"), CodeBlock.of("42")); - CodeSnippet code = CodeSnippet.of(statement.toCodeBlock()); - assertThat(code.getSnippet()).isEqualTo(""" - Field twoField = ReflectionUtils.findField(BeanFieldGeneratorTests.SampleBean.class, "two"); - ReflectionUtils.makeAccessible(twoField); - ReflectionUtils.setField(twoField, example, 42); - """); - assertThat(code.hasImport(ReflectionUtils.class)).isTrue(); - assertThat(code.hasImport(BeanFieldGeneratorTests.class)).isTrue(); - } - - - private Field field(Class type, String name) { - Field field = ReflectionUtils.findField(type, name); - assertThat(field).isNotNull(); - return field; - } - - - public static class SampleBean { - - public String one; - - @SuppressWarnings("unused") - private int two; - - } - -} diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/generator/BeanParameterGeneratorTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/generator/BeanParameterGeneratorTests.java deleted file mode 100644 index c0fbb38405..0000000000 --- a/spring-beans/src/test/java/org/springframework/beans/factory/generator/BeanParameterGeneratorTests.java +++ /dev/null @@ -1,267 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.beans.factory.generator; - -import java.io.StringWriter; -import java.lang.reflect.Constructor; -import java.lang.reflect.Method; -import java.time.temporal.ChronoUnit; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.function.Consumer; -import java.util.stream.Stream; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; - -import org.springframework.beans.factory.config.BeanReference; -import org.springframework.beans.factory.support.ManagedList; -import org.springframework.beans.factory.support.ManagedSet; -import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.core.ResolvableType; -import org.springframework.core.io.ResourceLoader; -import org.springframework.javapoet.CodeBlock; -import org.springframework.javapoet.support.CodeSnippet; -import org.springframework.lang.Nullable; -import org.springframework.util.ReflectionUtils; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; -import static org.assertj.core.api.Assertions.assertThatIllegalStateException; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; - -/** - * Tests for {@link BeanParameterGenerator}. - * - * @author Stephane Nicoll - */ -class BeanParameterGeneratorTests { - - private final BeanParameterGenerator generator = new BeanParameterGenerator(); - - @Test - void generateCharArray() { - char[] value = new char[] { 'v', 'a', 'l', 'u', 'e' }; - assertThat(generate(value, ResolvableType.forArrayComponent(ResolvableType.forClass(char.class)))) - .isEqualTo("new char[] { 'v', 'a', 'l', 'u', 'e' }"); - } - - @Test - void generateStringArray() { - String[] value = new String[] { "a", "test" }; - assertThat(generate(value, ResolvableType.forArrayComponent(ResolvableType.forClass(String.class)))) - .isEqualTo("new String[] { \"a\", \"test\" }"); - } - - @Test - void generateStringList() { - List value = List.of("a", "test"); - CodeSnippet code = codeSnippet(value, ResolvableType.forClassWithGenerics(List.class, String.class)); - assertThat(code.getSnippet()).isEqualTo( - "List.of(\"a\", \"test\")"); - assertThat(code.hasImport(List.class)).isTrue(); - } - - @Test - void generateStringManagedList() { - ManagedList value = ManagedList.of("a", "test"); - CodeSnippet code = codeSnippet(value, ResolvableType.forClassWithGenerics(List.class, String.class)); - assertThat(code.getSnippet()).isEqualTo( - "ManagedList.of(\"a\", \"test\")"); - assertThat(code.hasImport(ManagedList.class)).isTrue(); - } - - @Test - void generateEmptyList() { - List value = List.of(); - CodeSnippet code = codeSnippet(value, ResolvableType.forClassWithGenerics(List.class, String.class)); - assertThat(code.getSnippet()).isEqualTo("Collections.emptyList()"); - assertThat(code.hasImport(Collections.class)).isTrue(); - } - - @Test - void generateStringSet() { - Set value = Set.of("a", "test"); - CodeSnippet code = codeSnippet(value, ResolvableType.forClassWithGenerics(Set.class, String.class)); - assertThat(code.getSnippet()).startsWith("Set.of(").contains("a").contains("test"); - assertThat(code.hasImport(Set.class)).isTrue(); - } - - @Test - void generateStringManagedSet() { - Set value = ManagedSet.of("a", "test"); - CodeSnippet code = codeSnippet(value, ResolvableType.forClassWithGenerics(Set.class, String.class)); - assertThat(code.getSnippet()).isEqualTo( - "ManagedSet.of(\"a\", \"test\")"); - assertThat(code.hasImport(ManagedSet.class)).isTrue(); - } - - @Test - void generateEmptySet() { - Set value = Set.of(); - CodeSnippet code = codeSnippet(value, ResolvableType.forClassWithGenerics(Set.class, String.class)); - assertThat(code.getSnippet()).isEqualTo("Collections.emptySet()"); - assertThat(code.hasImport(Collections.class)).isTrue(); - } - - @Test - void generateMap() { - Map value = new LinkedHashMap<>(); - value.put("name", "Hello"); - value.put("counter", 42); - assertThat(generate(value)).isEqualTo("Map.of(\"name\", \"Hello\", \"counter\", 42)"); - } - - @Test - void generateMapWithEnum() { - Map value = new HashMap<>(); - value.put("unit", ChronoUnit.DAYS); - assertThat(generate(value)).isEqualTo("Map.of(\"unit\", ChronoUnit.DAYS)"); - } - - @Test - void generateEmptyMap() { - assertThat(generate(Map.of())).isEqualTo("Map.of()"); - } - - @Test - void generateString() { - assertThat(generate("test", ResolvableType.forClass(String.class))).isEqualTo("\"test\""); - } - - @Test - void generateCharEscapeBackslash() { - assertThat(generate('\\', ResolvableType.forType(char.class))).isEqualTo("'\\\\'"); - } - - @ParameterizedTest - @MethodSource("primitiveValues") - void generatePrimitiveValue(Object value, String parameter) { - assertThat(generate(value, ResolvableType.forClass(value.getClass()))).isEqualTo(parameter); - } - - private static Stream primitiveValues() { - return Stream.of(Arguments.of((short) 0, "0"), Arguments.of((1), "1"), Arguments.of(2L, "2"), - Arguments.of(2.5d, "2.5"), Arguments.of(2.7f, "2.7"), Arguments.of('c', "'c'"), - Arguments.of((byte) 1, "1"), Arguments.of(true, "true")); - } - - @Test - void generateEnum() { - assertThat(generate(ChronoUnit.DAYS, ResolvableType.forClass(ChronoUnit.class))) - .isEqualTo("ChronoUnit.DAYS"); - } - - @Test - void generateClass() { - assertThat(generate(Integer.class, ResolvableType.forClass(Class.class))) - .isEqualTo("Integer.class"); - } - - @Test - void generateResolvableType() { - ResolvableType type = ResolvableType.forClassWithGenerics(Consumer.class, Integer.class); - assertThat(generate(type, type)) - .isEqualTo("ResolvableType.forClassWithGenerics(Consumer.class, Integer.class)"); - } - - @Test - void generateExecutableParameterTypesWithConstructor() { - Constructor constructor = TestSample.class.getDeclaredConstructors()[0]; - assertThat(CodeSnippet.process(this.generator.generateExecutableParameterTypes(constructor))) - .isEqualTo("String.class, ResourceLoader.class"); - } - - @Test - void generateExecutableParameterTypesWithNoArgConstructor() { - Constructor constructor = BeanParameterGeneratorTests.class.getDeclaredConstructors()[0]; - assertThat(CodeSnippet.process(this.generator.generateExecutableParameterTypes(constructor))) - .isEmpty(); - } - - @Test - void generateExecutableParameterTypesWithMethod() { - Method method = ReflectionUtils.findMethod(TestSample.class, "createBean", String.class, Integer.class); - assertThat(CodeSnippet.process(this.generator.generateExecutableParameterTypes(method))) - .isEqualTo("String.class, Integer.class"); - } - - @Test - void generateNull() { - assertThat(generate(null)).isEqualTo("null"); - } - - @Test - void generateBeanReference() { - BeanReference beanReference = mock(BeanReference.class); - given(beanReference.getBeanName()).willReturn("testBean"); - assertThat(generate(beanReference)).isEqualTo("new RuntimeBeanReference(\"testBean\")"); - } - - @Test - void generateBeanDefinitionCallsConsumer() { - BeanParameterGenerator customGenerator = new BeanParameterGenerator( - beanDefinition -> CodeBlock.of("test")); - assertThat(CodeSnippet.process(customGenerator.generateParameterValue( - new RootBeanDefinition()))).isEqualTo("test"); - } - - @Test - void generateBeanDefinitionWithoutConsumerFails() { - BeanParameterGenerator customGenerator = new BeanParameterGenerator(); - assertThatIllegalStateException().isThrownBy(() -> customGenerator - .generateParameterValue(new RootBeanDefinition())); - } - - @Test - void generateUnsupportedParameter() { - assertThatIllegalArgumentException().isThrownBy(() -> generate(new StringWriter())) - .withMessageContaining(StringWriter.class.getName()); - } - - private String generate(@Nullable Object value) { - return CodeSnippet.process(this.generator.generateParameterValue(value)); - } - - private String generate(Object value, ResolvableType resolvableType) { - return codeSnippet(value, resolvableType).getSnippet(); - } - - private CodeSnippet codeSnippet(Object value, ResolvableType resolvableType) { - return CodeSnippet.of(this.generator.generateParameterValue(value, () -> resolvableType)); - } - - - @SuppressWarnings("unused") - static class TestSample { - - public TestSample(String test, ResourceLoader resourceLoader) { - } - - String createBean(String name, Integer counter) { - return "test"; - } - } - -} diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/generator/BeanRegistrationBeanFactoryContributionTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/generator/BeanRegistrationBeanFactoryContributionTests.java deleted file mode 100644 index 9cc4204937..0000000000 --- a/spring-beans/src/test/java/org/springframework/beans/factory/generator/BeanRegistrationBeanFactoryContributionTests.java +++ /dev/null @@ -1,736 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.beans.factory.generator; - -import java.io.IOException; -import java.io.StringWriter; -import java.lang.reflect.Constructor; -import java.lang.reflect.Executable; -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.function.Consumer; -import java.util.function.Predicate; -import java.util.stream.Collectors; - -import javax.lang.model.element.Modifier; - -import org.junit.jupiter.api.Test; - -import org.springframework.aot.generator.DefaultGeneratedTypeContext; -import org.springframework.aot.generator.GeneratedType; -import org.springframework.aot.hint.ExecutableHint; -import org.springframework.aot.hint.ExecutableMode; -import org.springframework.aot.hint.ReflectionHints; -import org.springframework.aot.hint.RuntimeHints; -import org.springframework.aot.hint.TypeReference; -import org.springframework.aot.test.generator.compile.TestCompiler; -import org.springframework.aot.test.generator.file.SourceFile; -import org.springframework.aot.test.generator.file.SourceFiles; -import org.springframework.beans.factory.FactoryBean; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.ConfigurableBeanFactory; -import org.springframework.beans.factory.config.RuntimeBeanReference; -import org.springframework.beans.factory.support.AbstractBeanDefinition; -import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.beans.testfixture.beans.factory.generator.BeanFactoryInitializer; -import org.springframework.beans.testfixture.beans.factory.generator.InnerComponentConfiguration.EnvironmentAwareComponent; -import org.springframework.beans.testfixture.beans.factory.generator.InnerComponentConfiguration.NoDependencyComponent; -import org.springframework.beans.testfixture.beans.factory.generator.SimpleConfiguration; -import org.springframework.beans.testfixture.beans.factory.generator.factory.SampleFactory; -import org.springframework.beans.testfixture.beans.factory.generator.injection.InjectionComponent; -import org.springframework.beans.testfixture.beans.factory.generator.lifecycle.InitDestroyBean; -import org.springframework.beans.testfixture.beans.factory.generator.property.ConfigurableBean; -import org.springframework.beans.testfixture.beans.factory.generator.visibility.ProtectedConstructorComponent; -import org.springframework.beans.testfixture.beans.factory.generator.visibility.ProtectedFactoryMethod; -import org.springframework.core.env.Environment; -import org.springframework.core.testfixture.aot.generator.visibility.PublicFactoryBean; -import org.springframework.javapoet.ClassName; -import org.springframework.javapoet.CodeBlock; -import org.springframework.javapoet.CodeBlock.Builder; -import org.springframework.javapoet.JavaFile; -import org.springframework.javapoet.MethodSpec; -import org.springframework.javapoet.support.CodeSnippet; -import org.springframework.javapoet.support.MultiStatement; -import org.springframework.util.ReflectionUtils; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verifyNoInteractions; - -/** - * Tests for {@link BeanRegistrationBeanFactoryContribution}. - * - * @author Stephane Nicoll - */ -class BeanRegistrationBeanFactoryContributionTests { - - private final DefaultGeneratedTypeContext generatedTypeContext = new DefaultGeneratedTypeContext("com.example", packageName -> GeneratedType.of(ClassName.get(packageName, "Test"))); - - private final BeanFactoryInitialization initialization = new BeanFactoryInitialization(this.generatedTypeContext); - - @Test - void generateUsingConstructor() { - BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(InjectionComponent.class).getBeanDefinition(); - CodeSnippet registration = beanRegistration(beanDefinition, singleConstructor(InjectionComponent.class), code -> code.add("() -> test")); - assertThat(registration.getSnippet()).isEqualTo(""" - BeanDefinitionRegistrar.of("test", InjectionComponent.class).withConstructor(String.class) - .instanceSupplier(() -> test).register(beanFactory); - """); - } - - @Test - void generateUsingConstructorWithNoArgument() { - BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(SimpleConfiguration.class).getBeanDefinition(); - CodeSnippet registration = beanRegistration(beanDefinition, singleConstructor(SimpleConfiguration.class), code -> code.add("() -> test")); - assertThat(registration.getSnippet()).isEqualTo(""" - BeanDefinitionRegistrar.of("test", SimpleConfiguration.class) - .instanceSupplier(() -> test).register(beanFactory); - """); - } - - @Test - void generateUsingConstructorOnInnerClass() { - BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(EnvironmentAwareComponent.class).getBeanDefinition(); - CodeSnippet registration = beanRegistration(beanDefinition, singleConstructor(EnvironmentAwareComponent.class), code -> code.add("() -> test")); - assertThat(registration.getSnippet()).isEqualTo(""" - BeanDefinitionRegistrar.of("test", InnerComponentConfiguration.EnvironmentAwareComponent.class).withConstructor(InnerComponentConfiguration.class, Environment.class) - .instanceSupplier(() -> test).register(beanFactory); - """); - } - - @Test - void generateUsingConstructorOnInnerClassWithNoExtraArg() { - BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(NoDependencyComponent.class).getBeanDefinition(); - CodeSnippet registration = beanRegistration(beanDefinition, singleConstructor(NoDependencyComponent.class), code -> code.add("() -> test")); - assertThat(registration.getSnippet()).isEqualTo(""" - BeanDefinitionRegistrar.of("test", InnerComponentConfiguration.NoDependencyComponent.class) - .instanceSupplier(() -> test).register(beanFactory); - """); - } - - @Test - void generateUsingFactoryMethod() { - BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(String.class).getBeanDefinition(); - CodeSnippet registration = beanRegistration(beanDefinition, method(SampleFactory.class, "create", String.class), code -> code.add("() -> test")); - assertThat(registration.hasImport(SampleFactory.class)).isTrue(); - assertThat(registration.getSnippet()).isEqualTo(""" - BeanDefinitionRegistrar.of("test", String.class).withFactoryMethod(SampleFactory.class, "create", String.class) - .instanceSupplier(() -> test).register(beanFactory); - """); - } - - @Test - void generateUsingFactoryMethodWithNoArgument() { - BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Integer.class).getBeanDefinition(); - CodeSnippet registration = beanRegistration(beanDefinition, method(SampleFactory.class, "integerBean"), code -> code.add("() -> test")); - assertThat(registration.hasImport(SampleFactory.class)).isTrue(); - assertThat(registration.getSnippet()).isEqualTo(""" - BeanDefinitionRegistrar.of("test", Integer.class).withFactoryMethod(SampleFactory.class, "integerBean") - .instanceSupplier(() -> test).register(beanFactory); - """); - } - - @Test - void generateUsingPublicAccessDoesNotAccessAnotherPackage() { - BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(SimpleConfiguration.class).getBeanDefinition(); - getContributionFor(beanDefinition, singleConstructor(SimpleConfiguration.class)).applyTo(this.initialization); - assertThat(this.generatedTypeContext.toJavaFiles()).hasSize(1); - assertThat(CodeSnippet.of(this.initialization.toCodeBlock()).getSnippet()).isEqualTo(""" - BeanDefinitionRegistrar.of("test", SimpleConfiguration.class) - .instanceSupplier(SimpleConfiguration::new).register(beanFactory); - """); - } - - @Test - void generateUsingProtectedConstructorWritesToBlessedPackage() { - BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(ProtectedConstructorComponent.class).getBeanDefinition(); - getContributionFor(beanDefinition, singleConstructor(ProtectedConstructorComponent.class)).applyTo(this.initialization); - assertThat(this.generatedTypeContext.hasGeneratedType(ProtectedConstructorComponent.class.getPackageName())).isTrue(); - GeneratedType generatedType = this.generatedTypeContext.getGeneratedType(ProtectedConstructorComponent.class.getPackageName()); - assertThat(removeIndent(codeOf(generatedType), 1)).containsSequence(""" - public static void registerTest(DefaultListableBeanFactory beanFactory) { - BeanDefinitionRegistrar.of("test", ProtectedConstructorComponent.class) - .instanceSupplier(ProtectedConstructorComponent::new).register(beanFactory); - }"""); - assertThat(CodeSnippet.of(this.initialization.toCodeBlock()).getSnippet()).isEqualTo( - ProtectedConstructorComponent.class.getPackageName() + ".Test.registerTest(beanFactory);\n"); - } - - @Test - void generateUsingProtectedFactoryMethodWritesToBlessedPackage() { - BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(String.class).getBeanDefinition(); - getContributionFor(beanDefinition, method(ProtectedFactoryMethod.class, "testBean", Integer.class)) - .applyTo(this.initialization); - assertThat(this.generatedTypeContext.hasGeneratedType(ProtectedFactoryMethod.class.getPackageName())).isTrue(); - GeneratedType generatedType = this.generatedTypeContext.getGeneratedType(ProtectedConstructorComponent.class.getPackageName()); - assertThat(removeIndent(codeOf(generatedType), 1)).containsSequence(""" - public static void registerProtectedFactoryMethod_test(DefaultListableBeanFactory beanFactory) { - BeanDefinitionRegistrar.of("test", String.class).withFactoryMethod(ProtectedFactoryMethod.class, "testBean", Integer.class) - .instanceSupplier((instanceContext) -> instanceContext.create(beanFactory, (attributes) -> beanFactory.getBean(ProtectedFactoryMethod.class).testBean(attributes.get(0)))).register(beanFactory); - }"""); - assertThat(CodeSnippet.of(this.initialization.toCodeBlock()).getSnippet()).isEqualTo( - ProtectedConstructorComponent.class.getPackageName() + ".Test.registerProtectedFactoryMethod_test(beanFactory);\n"); - } - - @Test - void generateUsingProtectedGenericTypeWritesToBlessedPackage() { - RootBeanDefinition beanDefinition = (RootBeanDefinition) BeanDefinitionBuilder.rootBeanDefinition( - PublicFactoryBean.class).getBeanDefinition(); - beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, String.class); - // This resolve the generic parameter to a protected type - beanDefinition.setTargetType(PublicFactoryBean.resolveToProtectedGenericParameter()); - getContributionFor(beanDefinition, singleConstructor(PublicFactoryBean.class)).applyTo(this.initialization); - assertThat(this.generatedTypeContext.hasGeneratedType(PublicFactoryBean.class.getPackageName())).isTrue(); - GeneratedType generatedType = this.generatedTypeContext.getGeneratedType(PublicFactoryBean.class.getPackageName()); - assertThat(removeIndent(codeOf(generatedType), 1)).containsSequence(""" - public static void registerTest(DefaultListableBeanFactory beanFactory) { - BeanDefinitionRegistrar.of("test", ResolvableType.forClassWithGenerics(PublicFactoryBean.class, ProtectedType.class)).withConstructor(Class.class) - .instanceSupplier((instanceContext) -> instanceContext.create(beanFactory, (attributes) -> new PublicFactoryBean(attributes.get(0)))).customize((bd) -> bd.getConstructorArgumentValues().addIndexedArgumentValue(0, String.class)).register(beanFactory); - }"""); - assertThat(CodeSnippet.of(this.initialization.toCodeBlock()).getSnippet()).isEqualTo( - PublicFactoryBean.class.getPackageName() + ".Test.registerTest(beanFactory);\n"); - } - - @Test - void generateWithBeanDefinitionHavingInitMethodName() { - compile(simpleConfigurationRegistration(bd -> bd.setInitMethodName("someMethod")), - hasBeanDefinition(generatedBd -> assertThat(generatedBd.getInitMethodNames()).containsExactly("someMethod"))); - } - - @Test - void generateWithBeanDefinitionHavingInitMethodNames() { - compile(simpleConfigurationRegistration(bd -> bd.setInitMethodNames("i1", "i2")), - hasBeanDefinition(generatedBd -> assertThat(generatedBd.getInitMethodNames()).containsExactly("i1", "i2"))); - } - - @Test - void generateWithBeanDefinitionHavingDestroyMethodName() { - compile(simpleConfigurationRegistration(bd -> bd.setDestroyMethodName("someMethod")), - hasBeanDefinition(generatedBd -> assertThat(generatedBd.getDestroyMethodNames()).containsExactly("someMethod"))); - } - - @Test - void generateWithBeanDefinitionHavingDestroyMethodNames() { - compile(simpleConfigurationRegistration(bd -> bd.setDestroyMethodNames("d1", "d2")), - hasBeanDefinition(generatedBd -> assertThat(generatedBd.getDestroyMethodNames()).containsExactly("d1", "d2"))); - } - - @Test - void generateWithBeanDefinitionHavingSyntheticFlag() { - compile(simpleConfigurationRegistration(bd -> bd.setSynthetic(true)), - hasBeanDefinition(generatedBd -> assertThat(generatedBd.isSynthetic()).isTrue())); - } - - @Test - void generateWithBeanDefinitionHavingDependsOn() { - compile(simpleConfigurationRegistration(bd -> bd.setDependsOn("test")), - hasBeanDefinition(generatedBd -> assertThat(generatedBd.getDependsOn()).containsExactly("test"))); - } - - @Test - void generateWithBeanDefinitionHavingLazyInit() { - compile(simpleConfigurationRegistration(bd -> bd.setLazyInit(true)), - hasBeanDefinition(generatedBd -> assertThat(generatedBd.isLazyInit()).isTrue())); - } - - @Test - void generateWithBeanDefinitionHavingRole() { - compile(simpleConfigurationRegistration(bd -> bd.setRole(BeanDefinition.ROLE_INFRASTRUCTURE)), - hasBeanDefinition(generatedBd -> assertThat(generatedBd.getRole()) - .isEqualTo(BeanDefinition.ROLE_INFRASTRUCTURE))); - } - - @Test - void generateWithBeanDefinitionHavingScope() { - compile(simpleConfigurationRegistration(bd -> bd.setScope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)), - hasBeanDefinition(generatedBd -> assertThat(generatedBd.getScope()) - .isEqualTo(ConfigurableBeanFactory.SCOPE_PROTOTYPE))); - } - - @Test - void generateWithBeanDefinitionHavingAutowiredCandidate() { - compile(simpleConfigurationRegistration(bd -> bd.setAutowireCandidate(false)), - hasBeanDefinition(generatedBd -> assertThat(generatedBd.isAutowireCandidate()).isFalse())); - } - - @Test - void generateWithBeanDefinitionHavingDefaultKeepsThem() { - compile(simpleConfigurationRegistration(bd -> {}), hasBeanDefinition(generatedBd -> { - assertThat(generatedBd.isSynthetic()).isFalse(); - assertThat(generatedBd.getDependsOn()).isNull(); - assertThat(generatedBd.isLazyInit()).isFalse(); - assertThat(generatedBd.getRole()).isEqualTo(BeanDefinition.ROLE_APPLICATION); - assertThat(generatedBd.getScope()).isEqualTo(ConfigurableBeanFactory.SCOPE_SINGLETON); - assertThat(generatedBd.isAutowireCandidate()).isTrue(); - })); - } - - @Test - void generateWithBeanDefinitionHavingMultipleAttributes() { - compile(simpleConfigurationRegistration(bd -> { - bd.setSynthetic(true); - bd.setPrimary(true); - }), hasBeanDefinition(generatedBd -> { - assertThat(generatedBd.isSynthetic()).isTrue(); - assertThat(generatedBd.isPrimary()).isTrue(); - })); - } - - @Test - void generateWithBeanDefinitionHavingProperty() { - compile(simpleConfigurationRegistration(bd -> bd.getPropertyValues().addPropertyValue("test", "Hello")), - hasBeanDefinition(generatedBd -> { - assertThat(generatedBd.getPropertyValues().contains("test")).isTrue(); - assertThat(generatedBd.getPropertyValues().get("test")).isEqualTo("Hello"); - })); - } - - @Test - void generateWithBeanDefinitionHavingSeveralProperties() { - compile(simpleConfigurationRegistration(bd -> { - bd.getPropertyValues().addPropertyValue("test", "Hello"); - bd.getPropertyValues().addPropertyValue("counter", 42); - }), hasBeanDefinition(generatedBd -> { - assertThat(generatedBd.getPropertyValues().contains("test")).isTrue(); - assertThat(generatedBd.getPropertyValues().get("test")).isEqualTo("Hello"); - assertThat(generatedBd.getPropertyValues().contains("counter")).isTrue(); - assertThat(generatedBd.getPropertyValues().get("counter")).isEqualTo(42); - })); - } - - @Test - void generateWithBeanDefinitionHavingPropertyReference() { - compile(simpleConfigurationRegistration(bd -> bd.getPropertyValues().addPropertyValue( - "myService", new RuntimeBeanReference("test"))), hasBeanDefinition(generatedBd -> { - assertThat(generatedBd.getPropertyValues().contains("myService")).isTrue(); - assertThat(generatedBd.getPropertyValues().get("myService")) - .isInstanceOfSatisfying(RuntimeBeanReference.class, ref -> - assertThat(ref.getBeanName()).isEqualTo("test")); - })); - } - - @Test - void generateWithBeanDefinitionHavingPropertyAsBeanDefinition() { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - BeanDefinition innerBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(SimpleConfiguration.class, "stringBean") - .getBeanDefinition(); - BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(ConfigurableBean.class) - .addPropertyValue("name", innerBeanDefinition).getBeanDefinition(); - compile(getDefaultContribution(beanFactory, beanDefinition), hasBeanDefinition(generatedBd -> { - assertThat(generatedBd.getPropertyValues().contains("name")).isTrue(); - assertThat(generatedBd.getPropertyValues().get("name")).isInstanceOfSatisfying(RootBeanDefinition.class, innerGeneratedBd -> - assertThat(innerGeneratedBd.getResolvedFactoryMethod()).isEqualTo(method(SimpleConfiguration.class, "stringBean"))); - })); - } - - @Test - void generateWithBeanDefinitionHavingPropertyAsListOfBeanDefinitions() { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - BeanDefinition innerBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(SimpleConfiguration.class, "stringBean") - .getBeanDefinition(); - BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(ConfigurableBean.class) - .addPropertyValue("names", List.of(innerBeanDefinition, innerBeanDefinition)).getBeanDefinition(); - compile(getDefaultContribution(beanFactory, beanDefinition), hasBeanDefinition(generatedBd -> { - assertThat(generatedBd.getPropertyValues().contains("names")).isTrue(); - assertThat(generatedBd.getPropertyValues().get("names")).asList().hasSize(2); - })); - } - - @Test - void generateWithBeanDefinitionHavingPropertyAsBeanDefinitionUseDedicatedVariableNames() { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - BeanDefinition innerBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(SimpleConfiguration.class, "stringBean") - .setRole(2).getBeanDefinition(); - BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(ConfigurableBean.class) - .addPropertyValue("name", innerBeanDefinition).getBeanDefinition(); - getDefaultContribution(beanFactory, beanDefinition).applyTo(this.initialization); - CodeSnippet registration = CodeSnippet.of(this.initialization.toCodeBlock()); - assertThat(registration.getSnippet()).isEqualTo(""" - BeanDefinitionRegistrar.of("test", ConfigurableBean.class) - .instanceSupplier(ConfigurableBean::new).customize((bd) -> bd.getPropertyValues().addPropertyValue("name", BeanDefinitionRegistrar.inner(SimpleConfiguration.class).withFactoryMethod(SimpleConfiguration.class, "stringBean") - .instanceSupplier(() -> beanFactory.getBean(SimpleConfiguration.class).stringBean()).customize((bd_) -> bd_.setRole(2)).toBeanDefinition())).register(beanFactory); - """); - assertThat(registration.hasImport(SimpleConfiguration.class)).isTrue(); - } - - @Test - void generateUsingSingleConstructorArgument() { - BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(String.class).getBeanDefinition(); - beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, "hello"); - compile(getContributionFor(beanDefinition, method(SampleFactory.class, "create", String.class)), beanFactory -> - assertThat(beanFactory.getBean(String.class)).isEqualTo("hello")); - } - - @Test - void generateUsingSeveralConstructorArguments() { - BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(String.class) - .addConstructorArgValue(42).addConstructorArgValue("testBean") - .getBeanDefinition(); - compile(getContributionFor(beanDefinition, method(SampleFactory.class, "create", Number.class, String.class)), beanFactory -> - assertThat(beanFactory.getBean(String.class)).isEqualTo("42testBean")); - } - - @Test - void generateWithBeanDefinitionHavingAttributesDoesNotWriteThemByDefault() { - compile(simpleConfigurationRegistration(bd -> { - bd.setAttribute("test", "value"); - bd.setAttribute("counter", 42); - }), hasBeanDefinition(generatedBd -> { - assertThat(generatedBd.getAttribute("test")).isNull(); - assertThat(generatedBd.getAttribute("counter")).isNull(); - })); - } - - @Test - void generateWithBeanDefinitionHavingAttributesUseCustomFilter() { - RootBeanDefinition bd = new RootBeanDefinition(SimpleConfiguration.class); - bd.setAttribute("test", "value"); - bd.setAttribute("counter", 42); - DefaultBeanInstantiationGenerator beanInstantiationGenerator = new DefaultBeanInstantiationGenerator( - singleConstructor(SimpleConfiguration.class), Collections.emptyList()); - compile(new BeanRegistrationBeanFactoryContribution("test", bd, beanInstantiationGenerator) { - @Override - protected Predicate getAttributeFilter() { - return candidate -> candidate.equals("counter"); - } - }, hasBeanDefinition(generatedBd -> { - assertThat(generatedBd.getAttribute("test")).isNull(); - assertThat(generatedBd.getAttribute("counter")).isNotNull().isEqualTo(42); - })); - } - - @Test - void registerRuntimeHintsWithInitMethodNames() { - RootBeanDefinition bd = new RootBeanDefinition(InitDestroyBean.class); - bd.setInitMethodNames("customInitMethod", "initMethod"); - RuntimeHints runtimeHints = new RuntimeHints(); - getDefaultContribution(new DefaultListableBeanFactory(), bd).registerRuntimeHints(runtimeHints); - assertThat(runtimeHints.reflection().getTypeHint(InitDestroyBean.class)).satisfies(hint -> - assertThat(hint.methods()).anySatisfy(invokeMethodHint("customInitMethod")) - .anySatisfy(invokeMethodHint("initMethod")).hasSize(2)); - } - - @Test - void registerRuntimeHintsWithDestroyMethodNames() { - RootBeanDefinition bd = new RootBeanDefinition(InitDestroyBean.class); - bd.setDestroyMethodNames("customDestroyMethod", "destroyMethod"); - RuntimeHints runtimeHints = new RuntimeHints(); - getDefaultContribution(new DefaultListableBeanFactory(), bd).registerRuntimeHints(runtimeHints); - assertThat(runtimeHints.reflection().getTypeHint(InitDestroyBean.class)).satisfies(hint -> - assertThat(hint.methods()).anySatisfy(invokeMethodHint("customDestroyMethod")) - .anySatisfy(invokeMethodHint("destroyMethod")).hasSize(2)); - } - - @Test - void registerRuntimeHintsWithNoPropertyValuesDoesNotAccessRuntimeHints() { - RootBeanDefinition bd = new RootBeanDefinition(String.class); - RuntimeHints runtimeHints = mock(RuntimeHints.class); - getDefaultContribution(new DefaultListableBeanFactory(), bd).registerRuntimeHints(runtimeHints); - verifyNoInteractions(runtimeHints); - } - - @Test - void registerRuntimeHintsWithInvalidProperty() { - BeanDefinition bd = BeanDefinitionBuilder.rootBeanDefinition(ConfigurableBean.class) - .addPropertyValue("notAProperty", "invalid").addPropertyValue("name", "hello") - .getBeanDefinition(); - RuntimeHints runtimeHints = new RuntimeHints(); - getDefaultContribution(new DefaultListableBeanFactory(), bd).registerRuntimeHints(runtimeHints); - assertThat(runtimeHints.reflection().getTypeHint(ConfigurableBean.class)).satisfies(hint -> { - assertThat(hint.fields()).isEmpty(); - assertThat(hint.constructors()).isEmpty(); - assertThat(hint.methods()).singleElement().satisfies(methodHint -> { - assertThat(methodHint.getName()).isEqualTo("setName"); - assertThat(methodHint.getParameterTypes()).containsExactly(TypeReference.of(String.class)); - assertThat(methodHint.getModes()).containsOnly(ExecutableMode.INVOKE); - }); - assertThat(hint.getMemberCategories()).isEmpty(); - }); - } - - @Test - void registerRuntimeHintsForPropertiesUseDeclaringClass() { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - beanFactory.registerSingleton("environment", mock(Environment.class)); - BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(IntegerFactoryBean.class) - .addConstructorArgReference("environment") - .addPropertyValue("name", "Hello").getBeanDefinition(); - getDefaultContribution(beanFactory, beanDefinition).applyTo(this.initialization); - ReflectionHints reflectionHints = this.initialization.generatedTypeContext().runtimeHints().reflection(); - assertThat(reflectionHints.typeHints()).anySatisfy(typeHint -> { - assertThat(typeHint.getType()).isEqualTo(TypeReference.of(BaseFactoryBean.class)); - assertThat(typeHint.constructors()).isEmpty(); - assertThat(typeHint.methods()).singleElement() - .satisfies(invokeMethodHint("setName", String.class)); - assertThat(typeHint.fields()).isEmpty(); - }).anySatisfy(typeHint -> { - assertThat(typeHint.getType()).isEqualTo(TypeReference.of(IntegerFactoryBean.class)); - assertThat(typeHint.constructors()).singleElement() - .satisfies(introspectConstructorHint(Environment.class)); - assertThat(typeHint.methods()).isEmpty(); - assertThat(typeHint.fields()).isEmpty(); - }).hasSize(2); - } - - - @Test - void registerRuntimeHintsForProperties() { - BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(NameAndCountersComponent.class) - .addPropertyValue("name", "Hello").addPropertyValue("counter", 42).getBeanDefinition(); - getDefaultContribution(new DefaultListableBeanFactory(), beanDefinition).applyTo(this.initialization); - ReflectionHints reflectionHints = this.initialization.generatedTypeContext().runtimeHints().reflection(); - assertThat(reflectionHints.typeHints()).singleElement().satisfies(typeHint -> { - assertThat(typeHint.getType()).isEqualTo(TypeReference.of(NameAndCountersComponent.class)); - assertThat(typeHint.constructors()).isEmpty(); - assertThat(typeHint.methods()).anySatisfy(invokeMethodHint("setName", String.class)) - .anySatisfy(invokeMethodHint("setCounter", Integer.class)).hasSize(2); - assertThat(typeHint.fields()).isEmpty(); - }); - } - - - @Test - void registerReflectionEntriesForInnerBeanDefinition() { - AbstractBeanDefinition innerBd = BeanDefinitionBuilder.rootBeanDefinition(IntegerFactoryBean.class) - .addPropertyValue("name", "test").getBeanDefinition(); - BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(NameAndCountersComponent.class) - .addPropertyValue("counter", innerBd).getBeanDefinition(); - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - beanFactory.registerSingleton("environment", Environment.class); - getDefaultContribution(beanFactory, beanDefinition).applyTo(this.initialization); - ReflectionHints reflectionHints = this.initialization.generatedTypeContext().runtimeHints().reflection(); - assertThat(reflectionHints.typeHints()).anySatisfy(typeHint -> { - assertThat(typeHint.getType()).isEqualTo(TypeReference.of(NameAndCountersComponent.class)); - assertThat(typeHint.constructors()).isEmpty(); - assertThat(typeHint.methods()).singleElement().satisfies(invokeMethodHint("setCounter", Integer.class)); - assertThat(typeHint.fields()).isEmpty(); - }).anySatisfy(typeHint -> { - assertThat(typeHint.getType()).isEqualTo(TypeReference.of(BaseFactoryBean.class)); - assertThat(typeHint.methods()).singleElement().satisfies(invokeMethodHint("setName", String.class)); - }).anySatisfy(typeHint -> { - assertThat(typeHint.getType()).isEqualTo(TypeReference.of(IntegerFactoryBean.class)); - assertThat(typeHint.constructors()).singleElement().satisfies(introspectConstructorHint(Environment.class)); - }).hasSize(3); - } - - @Test - void registerReflectionEntriesForListOfInnerBeanDefinition() { - AbstractBeanDefinition innerBd1 = BeanDefinitionBuilder.rootBeanDefinition(IntegerFactoryBean.class) - .addPropertyValue("name", "test").getBeanDefinition(); - AbstractBeanDefinition innerBd2 = BeanDefinitionBuilder.rootBeanDefinition(AnotherIntegerFactoryBean.class) - .addPropertyValue("name", "test").getBeanDefinition(); - BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(NameAndCountersComponent.class) - .addPropertyValue("counters", List.of(innerBd1, innerBd2)).getBeanDefinition(); - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - beanFactory.registerSingleton("environment", Environment.class); - getDefaultContribution(beanFactory, beanDefinition).applyTo(this.initialization); - ReflectionHints reflectionHints = this.initialization.generatedTypeContext().runtimeHints().reflection(); - assertThat(reflectionHints.typeHints()).anySatisfy(typeHint -> { - assertThat(typeHint.getType()).isEqualTo(TypeReference.of(NameAndCountersComponent.class)); - assertThat(typeHint.constructors()).isEmpty(); - assertThat(typeHint.methods()).singleElement().satisfies(invokeMethodHint("setCounters", List.class)); - assertThat(typeHint.fields()).isEmpty(); - }).anySatisfy(typeHint -> { - assertThat(typeHint.getType()).isEqualTo(TypeReference.of(BaseFactoryBean.class)); - assertThat(typeHint.methods()).singleElement().satisfies(invokeMethodHint("setName", String.class)); - }).anySatisfy(typeHint -> { - assertThat(typeHint.getType()).isEqualTo(TypeReference.of(IntegerFactoryBean.class)); - assertThat(typeHint.constructors()).singleElement().satisfies(introspectConstructorHint(Environment.class)); - }).anySatisfy(typeHint -> { - assertThat(typeHint.getType()).isEqualTo(TypeReference.of(AnotherIntegerFactoryBean.class)); - assertThat(typeHint.constructors()).singleElement().satisfies(introspectConstructorHint(Environment.class)); - }).hasSize(4); - } - - private Consumer invokeMethodHint(String name, Class... parameterTypes) { - return executableHint(ExecutableMode.INVOKE, name, parameterTypes); - } - - private Consumer introspectConstructorHint(Class... parameterTypes) { - return executableHint(ExecutableMode.INTROSPECT, "", parameterTypes); - } - - private Consumer executableHint(ExecutableMode mode, String name, Class... parameterTypes) { - return executableHint -> { - assertThat(executableHint.getName()).isEqualTo(name); - assertThat(executableHint.getParameterTypes()).containsExactly(Arrays.stream(parameterTypes) - .map(TypeReference::of).toArray(TypeReference[]::new)); - assertThat(executableHint.getModes()).containsExactly(mode); - }; - } - - private Consumer hasBeanDefinition(Consumer bd) { - return beanFactory -> { - assertThat(beanFactory.getBeanDefinitionNames()).contains("test"); - RootBeanDefinition beanDefinition = (RootBeanDefinition) beanFactory.getMergedBeanDefinition("test"); - bd.accept(beanDefinition); - }; - } - - private BeanFactoryContribution simpleConfigurationRegistration(Consumer bd) { - RootBeanDefinition beanDefinition = (RootBeanDefinition) BeanDefinitionBuilder - .rootBeanDefinition(SimpleConfiguration.class).getBeanDefinition(); - bd.accept(beanDefinition); - return getDefaultContribution(new DefaultListableBeanFactory(), beanDefinition); - } - - private BeanRegistrationBeanFactoryContribution getDefaultContribution(DefaultListableBeanFactory beanFactory, BeanDefinition beanDefinition) { - BeanRegistrationBeanFactoryContribution contribution = new DefaultBeanRegistrationContributionProvider(beanFactory) - .getContributionFor("test", (RootBeanDefinition) beanDefinition); - assertThat(contribution).isNotNull(); - return contribution; - } - - private BeanRegistrationBeanFactoryContribution getContributionFor(BeanDefinition beanDefinition, Executable instanceCreator) { - return new BeanRegistrationBeanFactoryContribution("test", (RootBeanDefinition) beanDefinition, - new DefaultBeanInstantiationGenerator(instanceCreator, Collections.emptyList())); - } - - private CodeSnippet beanRegistration(BeanDefinition beanDefinition, Executable instanceCreator, Consumer instanceSupplier) { - BeanRegistrationBeanFactoryContribution generator = new BeanRegistrationBeanFactoryContribution( - "test", (RootBeanDefinition) beanDefinition, - new DefaultBeanInstantiationGenerator(instanceCreator, Collections.emptyList())); - return CodeSnippet.of(generator.generateBeanRegistration(new RuntimeHints(), - toMultiStatements(instanceSupplier))); - } - - private Constructor singleConstructor(Class type) { - return type.getDeclaredConstructors()[0]; - } - - private Method method(Class type, String name, Class... parameterTypes) { - Method method = ReflectionUtils.findMethod(type, name, parameterTypes); - assertThat(method).isNotNull(); - return method; - } - - private MultiStatement toMultiStatements(Consumer instanceSupplier) { - Builder code = CodeBlock.builder(); - instanceSupplier.accept(code); - MultiStatement statements = new MultiStatement(); - statements.add(code.build()); - return statements; - } - - private String codeOf(GeneratedType type) { - try { - StringWriter out = new StringWriter(); - type.toJavaFile().writeTo(out); - return out.toString(); - } - catch (IOException ex) { - throw new IllegalStateException(ex); - } - } - - private String removeIndent(String content, int indent) { - return content.lines().map(line -> { - for (int i = 0; i < indent; i++) { - if (line.startsWith("\t")) { - line = line.substring(1); - } - } - return line; - }).collect(Collectors.joining("\n")); - } - - private void compile(BeanFactoryContribution contribution, Consumer beanFactory) { - contribution.applyTo(this.initialization); - GeneratedType generatedType = this.generatedTypeContext.getMainGeneratedType(); - generatedType.customizeType(type -> { - type.addModifiers(Modifier.PUBLIC); - type.addSuperinterface(BeanFactoryInitializer.class); - }); - generatedType.addMethod(MethodSpec.methodBuilder("initializeBeanFactory") - .addModifiers(Modifier.PUBLIC).addAnnotation(Override.class) - .addParameter(DefaultListableBeanFactory.class, "beanFactory") - .addCode(this.initialization.toCodeBlock())); - SourceFiles sourceFiles = SourceFiles.none(); - for (JavaFile javaFile : this.generatedTypeContext.toJavaFiles()) { - sourceFiles = sourceFiles.and(SourceFile.of((javaFile::writeTo))); - } - TestCompiler.forSystem().withSources(sourceFiles).compile(compiled -> { - BeanFactoryInitializer initializer = compiled.getInstance(BeanFactoryInitializer.class, - generatedType.getClassName().canonicalName()); - DefaultListableBeanFactory freshBeanFactory = new DefaultListableBeanFactory(); - initializer.initializeBeanFactory(freshBeanFactory); - beanFactory.accept(freshBeanFactory); - }); - } - - static abstract class BaseFactoryBean { - - public void setName(String name) { - - } - - } - - @SuppressWarnings("unused") - static class IntegerFactoryBean extends BaseFactoryBean implements FactoryBean { - - public IntegerFactoryBean(Environment environment) { - - } - - @Override - public Class getObjectType() { - return Integer.class; - } - - @Override - public Integer getObject() { - return 42; - } - - } - - @SuppressWarnings("unused") - static class AnotherIntegerFactoryBean extends IntegerFactoryBean { - - public AnotherIntegerFactoryBean(Environment environment) { - super(environment); - } - - } - - static class NameAndCountersComponent { - - @SuppressWarnings("unused") - private String name; - - @SuppressWarnings("unused") - private List counters; - - public void setName(String name) { - this.name = name; - } - - public void setCounter(Integer counter) { - setCounters(List.of(counter)); - } - - public void setCounters(List counters) { - this.counters = counters; - } - } - -} diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/generator/DefaultBeanInstantiationGeneratorTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/generator/DefaultBeanInstantiationGeneratorTests.java deleted file mode 100644 index 2b2a28f094..0000000000 --- a/spring-beans/src/test/java/org/springframework/beans/factory/generator/DefaultBeanInstantiationGeneratorTests.java +++ /dev/null @@ -1,255 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.beans.factory.generator; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Executable; -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.function.Consumer; - -import org.junit.jupiter.api.Test; - -import org.springframework.aot.generator.CodeContribution; -import org.springframework.aot.hint.ExecutableHint; -import org.springframework.aot.hint.ExecutableMode; -import org.springframework.aot.hint.MemberCategory; -import org.springframework.aot.hint.RuntimeHints; -import org.springframework.aot.hint.TypeHint; -import org.springframework.aot.hint.TypeReference; -import org.springframework.beans.testfixture.beans.TestBean; -import org.springframework.beans.testfixture.beans.factory.generator.InnerComponentConfiguration.EnvironmentAwareComponent; -import org.springframework.beans.testfixture.beans.factory.generator.InnerComponentConfiguration.NoDependencyComponent; -import org.springframework.beans.testfixture.beans.factory.generator.SimpleConfiguration; -import org.springframework.beans.testfixture.beans.factory.generator.factory.NumberHolderFactoryBean; -import org.springframework.beans.testfixture.beans.factory.generator.factory.SampleFactory; -import org.springframework.beans.testfixture.beans.factory.generator.injection.InjectionComponent; -import org.springframework.beans.testfixture.beans.factory.generator.visibility.ProtectedConstructorComponent; -import org.springframework.beans.testfixture.beans.factory.generator.visibility.ProtectedFactoryMethod; -import org.springframework.javapoet.CodeBlock; -import org.springframework.javapoet.support.CodeSnippet; -import org.springframework.lang.Nullable; -import org.springframework.util.ReflectionUtils; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link DefaultBeanInstantiationGenerator}. - * - * @author Stephane Nicoll - */ -class DefaultBeanInstantiationGeneratorTests { - - @Test - void generateUsingDefaultConstructorUsesMethodReference() { - CodeContribution contribution = generate(SimpleConfiguration.class.getDeclaredConstructors()[0]); - assertThat(code(contribution)).isEqualTo("SimpleConfiguration::new"); - assertThat(reflectionHints(contribution, SimpleConfiguration.class)).isNull(); - } - - @Test - void generateUsingConstructorWithoutParameterAndMultipleCandidatesDoesNotUseMethodReference() throws NoSuchMethodException { - CodeContribution contribution = generate(TestBean.class.getConstructor()); - assertThat(code(contribution)).isEqualTo("() -> new TestBean()"); - assertThat(reflectionHints(contribution, TestBean.class)).isNull(); - } - - @Test - void generateUsingConstructorWithParameter() { - Constructor constructor = InjectionComponent.class.getDeclaredConstructors()[0]; - CodeContribution contribution = generate(constructor); - assertThat(code(contribution).lines()).containsOnly( - "(instanceContext) -> instanceContext.create(beanFactory, (attributes) -> " - + "new InjectionComponent(attributes.get(0)))"); - assertThat(reflectionHints(contribution, InjectionComponent.class)) - .satisfies(hasSingleQueryConstructor(constructor)); - } - - @Test - void generateUsingConstructorWithInnerClassAndNoExtraArg() { - CodeContribution contribution = generate(NoDependencyComponent.class.getDeclaredConstructors()[0]); - assertThat(code(contribution).lines()).containsOnly( - "() -> beanFactory.getBean(InnerComponentConfiguration.class).new NoDependencyComponent()"); - assertThat(reflectionHints(contribution, NoDependencyComponent.class)).isNull(); - } - - @Test - void generateUsingConstructorWithInnerClassAndExtraArg() { - Constructor constructor = EnvironmentAwareComponent.class.getDeclaredConstructors()[0]; - CodeContribution contribution = generate(constructor); - assertThat(code(contribution).lines()).containsOnly( - "(instanceContext) -> instanceContext.create(beanFactory, (attributes) -> " - + "beanFactory.getBean(InnerComponentConfiguration.class).new EnvironmentAwareComponent(attributes.get(1)))"); - assertThat(reflectionHints(contribution, EnvironmentAwareComponent.class)) - .satisfies(hasSingleQueryConstructor(constructor)); - } - - @Test - void generateUsingConstructorOfTypeWithGeneric() { - CodeContribution contribution = generate(NumberHolderFactoryBean.class.getDeclaredConstructors()[0]); - assertThat(code(contribution)).isEqualTo("NumberHolderFactoryBean::new"); - assertThat(reflectionHints(contribution, NumberHolderFactoryBean.class)).isNull(); - } - - @Test - void generateUsingNoArgConstructorAndContributionsDoesNotUseMethodReference() { - CodeContribution contribution = generate(SimpleConfiguration.class.getDeclaredConstructors()[0], - contrib -> contrib.statements().add(CodeBlock.of("// hello\n")), - contrib -> {}); - assertThat(code(contribution)).isEqualTo(""" - (instanceContext) -> { - SimpleConfiguration bean = new SimpleConfiguration(); - // hello - return bean; - }"""); - } - - @Test - void generateUsingContributionsRegisterHints() { - CodeContribution contribution = generate(SimpleConfiguration.class.getDeclaredConstructors()[0], - contrib -> { - contrib.statements().add(CodeBlock.of("// hello\n")); - contrib.runtimeHints().resources().registerPattern("com/example/*.properties"); - }, - contrib -> contrib.runtimeHints().reflection().registerType(TypeReference.of(String.class), - hint -> hint.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS))); - assertThat(code(contribution)).isEqualTo(""" - (instanceContext) -> { - SimpleConfiguration bean = new SimpleConfiguration(); - // hello - return bean; - }"""); - assertThat(contribution.runtimeHints().resources().resourcePatterns()).singleElement().satisfies(hint -> - assertThat(hint.getIncludes()).containsOnly("com/example/*.properties")); - assertThat(contribution.runtimeHints().reflection().getTypeHint(String.class)).satisfies(hint -> { - assertThat(hint.getType()).isEqualTo(TypeReference.of(String.class)); - assertThat(hint.getMemberCategories()).containsOnly(MemberCategory.INVOKE_PUBLIC_METHODS); - }); - } - - @Test - void generateUsingMethodWithNoArg() { - Method method = method(SimpleConfiguration.class, "stringBean"); - CodeContribution contribution = generate(method); - assertThat(code(contribution)).isEqualTo("() -> beanFactory.getBean(SimpleConfiguration.class).stringBean()"); - assertThat(reflectionHints(contribution, SimpleConfiguration.class)) - .satisfies(hasSingleQueryMethod(method)); - } - - @Test - void generateUsingStaticMethodWithNoArg() { - Method method = method(SampleFactory.class, "integerBean"); - CodeContribution contribution = generate(method); - assertThat(code(contribution)).isEqualTo("() -> SampleFactory.integerBean()"); - assertThat(reflectionHints(contribution, SampleFactory.class)) - .satisfies(hasSingleQueryMethod(method)); - } - - @Test - void generateUsingMethodWithArg() { - Method method = method(SampleFactory.class, "create", Number.class, String.class); - CodeContribution contribution = generate(method); - assertThat(code(contribution)).isEqualTo("(instanceContext) -> instanceContext.create(beanFactory, (attributes) -> " - + "SampleFactory.create(attributes.get(0), attributes.get(1)))"); - assertThat(reflectionHints(contribution, SampleFactory.class)) - .satisfies(hasSingleQueryMethod(method)); - } - - @Test - void generateUsingMethodAndContributions() { - CodeContribution contribution = generate(method(SimpleConfiguration.class, "stringBean"), - contrib -> { - contrib.statements().add(CodeBlock.of("// hello\n")); - contrib.runtimeHints().resources().registerPattern("com/example/*.properties"); - }, - contrib -> contrib.runtimeHints().reflection().registerType(TypeReference.of(String.class), - hint -> hint.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS))); - assertThat(code(contribution)).isEqualTo(""" - (instanceContext) -> { - String bean = beanFactory.getBean(SimpleConfiguration.class).stringBean(); - // hello - return bean; - }"""); - assertThat(contribution.runtimeHints().resources().resourcePatterns()).singleElement().satisfies(hint -> - assertThat(hint.getIncludes()).containsOnly("com/example/*.properties")); - assertThat(contribution.runtimeHints().reflection().getTypeHint(String.class)).satisfies(hint -> { - assertThat(hint.getType()).isEqualTo(TypeReference.of(String.class)); - assertThat(hint.getMemberCategories()).containsOnly(MemberCategory.INVOKE_PUBLIC_METHODS); - }); - } - - @Test - void generateUsingProtectedConstructorRegistersProtectedAccess() { - CodeContribution contribution = generate(ProtectedConstructorComponent.class.getDeclaredConstructors()[0]); - assertThat(contribution.protectedAccess().isAccessible("com.example")).isFalse(); - assertThat(contribution.protectedAccess().getPrivilegedPackageName("com.example")) - .isEqualTo(ProtectedConstructorComponent.class.getPackageName()); - } - - @Test - void generateUsingProtectedMethodRegistersProtectedAccess() { - CodeContribution contribution = generate(method(ProtectedFactoryMethod.class, "testBean", Integer.class)); - assertThat(contribution.protectedAccess().isAccessible("com.example")).isFalse(); - assertThat(contribution.protectedAccess().getPrivilegedPackageName("com.example")) - .isEqualTo(ProtectedFactoryMethod.class.getPackageName()); - } - - private String code(CodeContribution contribution) { - return CodeSnippet.process(contribution.statements().toLambdaBody()); - } - - @Nullable - private TypeHint reflectionHints(CodeContribution contribution, Class type) { - return contribution.runtimeHints().reflection().getTypeHint(type); - } - - private Consumer hasSingleQueryConstructor(Constructor constructor) { - return typeHint -> assertThat(typeHint.constructors()).singleElement() - .satisfies(match(constructor, "", ExecutableMode.INTROSPECT)); - } - - private Consumer hasSingleQueryMethod(Method method) { - return typeHint -> assertThat(typeHint.methods()).singleElement() - .satisfies(match(method, method.getName(), ExecutableMode.INTROSPECT)); - } - - private Consumer match(Executable executable, String name, ExecutableMode... modes) { - return hint -> { - assertThat(hint.getName()).isEqualTo(name); - assertThat(hint.getParameterTypes()).hasSameSizeAs(executable.getParameterTypes()); - for (int i = 0; i < hint.getParameterTypes().size(); i++) { - assertThat(hint.getParameterTypes().get(i)) - .isEqualTo(TypeReference.of(executable.getParameterTypes()[i])); - } - assertThat(hint.getModes()).containsOnly(modes); - }; - } - - private CodeContribution generate(Executable executable, - BeanInstantiationContribution... beanInstantiationContributions) { - DefaultBeanInstantiationGenerator generator = new DefaultBeanInstantiationGenerator(executable, - Arrays.asList(beanInstantiationContributions)); - return generator.generateBeanInstantiation(new RuntimeHints()); - } - - private static Method method(Class type, String methodName, Class... parameterTypes) { - Method method = ReflectionUtils.findMethod(type, methodName, parameterTypes); - assertThat(method).isNotNull(); - return method; - } - -} diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/generator/DefaultBeanRegistrationContributionProviderTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/generator/DefaultBeanRegistrationContributionProviderTests.java deleted file mode 100644 index 412deeb47b..0000000000 --- a/spring-beans/src/test/java/org/springframework/beans/factory/generator/DefaultBeanRegistrationContributionProviderTests.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.beans.factory.generator; - -import org.junit.jupiter.api.Test; - -import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.beans.testfixture.beans.factory.generator.SimpleConfiguration; -import org.springframework.core.Ordered; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; - -/** - * Tests for {@link DefaultBeanRegistrationContributionProvider}. - * - * @author Stephane Nicoll - */ -class DefaultBeanRegistrationContributionProviderTests { - - @Test - void aotContributingBeanPostProcessorsAreIncluded() { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - AotContributingBeanPostProcessor first = mockNoOpPostProcessor(-1); - AotContributingBeanPostProcessor second = mockNoOpPostProcessor(5); - beanFactory.registerBeanDefinition("second", BeanDefinitionBuilder.rootBeanDefinition( - AotContributingBeanPostProcessor.class, () -> second).getBeanDefinition()); - beanFactory.registerBeanDefinition("first", BeanDefinitionBuilder.rootBeanDefinition( - AotContributingBeanPostProcessor.class, () -> first).getBeanDefinition()); - RootBeanDefinition beanDefinition = new RootBeanDefinition(SimpleConfiguration.class); - new DefaultBeanRegistrationContributionProvider(beanFactory).getContributionFor( - "test", beanDefinition); - verify((Ordered) second).getOrder(); - verify((Ordered) first).getOrder(); - verify(first).contribute(beanDefinition, SimpleConfiguration.class, "test"); - verify(second).contribute(beanDefinition, SimpleConfiguration.class, "test"); - verifyNoMoreInteractions(first, second); - } - - - private AotContributingBeanPostProcessor mockNoOpPostProcessor(int order) { - AotContributingBeanPostProcessor postProcessor = mock(AotContributingBeanPostProcessor.class); - given(postProcessor.contribute(any(), any(), any())).willReturn(null); - given(postProcessor.getOrder()).willReturn(order); - return postProcessor; - } - -} diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/generator/InjectionGeneratorTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/generator/InjectionGeneratorTests.java deleted file mode 100644 index 2a8f667f7a..0000000000 --- a/spring-beans/src/test/java/org/springframework/beans/factory/generator/InjectionGeneratorTests.java +++ /dev/null @@ -1,336 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.beans.factory.generator; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Executable; -import java.lang.reflect.Field; -import java.lang.reflect.Member; -import java.lang.reflect.Method; - -import org.junit.jupiter.api.Test; - -import org.springframework.aot.generator.ProtectedAccess; -import org.springframework.aot.generator.ProtectedAccess.Options; -import org.springframework.beans.factory.ObjectProvider; -import org.springframework.beans.factory.generator.InjectionGeneratorTests.SimpleConstructorBean.InnerClass; -import org.springframework.beans.testfixture.beans.factory.generator.factory.SampleFactory; -import org.springframework.javapoet.support.CodeSnippet; -import org.springframework.util.ReflectionUtils; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; -import static org.mockito.Mockito.mock; - -/** - * Tests for {@link InjectionGenerator}. - * - * @author Stephane Nicoll - */ -class InjectionGeneratorTests { - - private final ProtectedAccess protectedAccess = new ProtectedAccess(); - - @Test - void generateInstantiationForConstructorWithNoArgUseShortcut() { - Constructor constructor = SimpleBean.class.getDeclaredConstructors()[0]; - assertThat(generateInstantiation(constructor).lines()) - .containsExactly("new InjectionGeneratorTests.SimpleBean()"); - } - - @Test - void generateInstantiationForConstructorWithNonGenericParameter() { - Constructor constructor = SimpleConstructorBean.class.getDeclaredConstructors()[0]; - assertThat(generateInstantiation(constructor).lines()).containsExactly( - "instanceContext.create(beanFactory, (attributes) -> new InjectionGeneratorTests.SimpleConstructorBean(attributes.get(0), attributes.get(1)))"); - } - - @Test - void generateInstantiationForConstructorWithGenericParameter() { - Constructor constructor = GenericConstructorBean.class.getDeclaredConstructors()[0]; - assertThat(generateInstantiation(constructor).lines()).containsExactly( - "instanceContext.create(beanFactory, (attributes) -> new InjectionGeneratorTests.GenericConstructorBean(attributes.get(0)))"); - } - - @Test - void generateInstantiationForAmbiguousConstructor() throws Exception { - Constructor constructor = AmbiguousConstructorBean.class.getDeclaredConstructor(String.class, Number.class); - assertThat(generateInstantiation(constructor).lines()).containsExactly( - "instanceContext.create(beanFactory, (attributes) -> new InjectionGeneratorTests.AmbiguousConstructorBean(attributes.get(0, String.class), attributes.get(1, Number.class)))"); - } - - @Test - void generateInstantiationForConstructorInInnerClass() { - Constructor constructor = InnerClass.class.getDeclaredConstructors()[0]; - assertThat(generateInstantiation(constructor).lines()).containsExactly( - "beanFactory.getBean(InjectionGeneratorTests.SimpleConstructorBean.class).new InnerClass()"); - } - - @Test - void generateInstantiationForMethodWithNoArgUseShortcut() { - assertThat(generateInstantiation(method(SimpleBean.class, "name")).lines()).containsExactly( - "beanFactory.getBean(InjectionGeneratorTests.SimpleBean.class).name()"); - } - - @Test - void generateInstantiationForStaticMethodWithNoArgUseShortcut() { - assertThat(generateInstantiation(method(SimpleBean.class, "number")).lines()).containsExactly( - "InjectionGeneratorTests.SimpleBean.number()"); - } - - @Test - void generateInstantiationForMethodWithNonGenericParameter() { - assertThat(generateInstantiation(method(SampleBean.class, "source", Integer.class)).lines()).containsExactly( - "instanceContext.create(beanFactory, (attributes) -> beanFactory.getBean(InjectionGeneratorTests.SampleBean.class).source(attributes.get(0)))"); - } - - @Test - void generateInstantiationForStaticMethodWithNonGenericParameter() { - assertThat(generateInstantiation(method(SampleBean.class, "staticSource", Integer.class)).lines()).containsExactly( - "instanceContext.create(beanFactory, (attributes) -> InjectionGeneratorTests.SampleBean.staticSource(attributes.get(0)))"); - } - - @Test - void generateInstantiationForMethodWithGenericParameters() { - assertThat(generateInstantiation(method(SampleBean.class, "sourceWithProvider", ObjectProvider.class)).lines()).containsExactly( - "instanceContext.create(beanFactory, (attributes) -> beanFactory.getBean(InjectionGeneratorTests.SampleBean.class).sourceWithProvider(attributes.get(0)))"); - } - - @Test - void generateInstantiationForAmbiguousMethod() { - assertThat(generateInstantiation(method(SampleFactory.class, "create", String.class)).lines()).containsExactly( - "instanceContext.create(beanFactory, (attributes) -> SampleFactory.create(attributes.get(0, String.class)))"); - } - - @Test - void generateInjectionForUnsupportedMember() { - assertThatIllegalArgumentException().isThrownBy(() -> generateInjection(mock(Member.class), false)); - } - - @Test - void generateInjectionForNonRequiredMethodWithNonGenericParameters() { - Method method = method(SampleBean.class, "sourceAndCounter", String.class, Integer.class); - assertThat(generateInjection(method, false)).isEqualTo(""" - instanceContext.method("sourceAndCounter", String.class, Integer.class) - .resolve(beanFactory, false).ifResolved((attributes) -> bean.sourceAndCounter(attributes.get(0), attributes.get(1)))"""); - } - - @Test - void generateInjectionForRequiredMethodWithGenericParameter() { - Method method = method(SampleBean.class, "nameAndCounter", String.class, ObjectProvider.class); - assertThat(generateInjection(method, true)).isEqualTo(""" - instanceContext.method("nameAndCounter", String.class, ObjectProvider.class) - .invoke(beanFactory, (attributes) -> bean.nameAndCounter(attributes.get(0), attributes.get(1)))"""); - } - - @Test - void generateInjectionForNonRequiredMethodWithGenericParameter() { - Method method = method(SampleBean.class, "nameAndCounter", String.class, ObjectProvider.class); - assertThat(generateInjection(method, false)).isEqualTo(""" - instanceContext.method("nameAndCounter", String.class, ObjectProvider.class) - .resolve(beanFactory, false).ifResolved((attributes) -> bean.nameAndCounter(attributes.get(0), attributes.get(1)))"""); - } - - @Test - void generateInjectionForRequiredField() { - Field field = field(SampleBean.class, "counter"); - assertThat(generateInjection(field, true)).isEqualTo(""" - instanceContext.field("counter") - .invoke(beanFactory, (attributes) -> bean.counter = attributes.get(0))"""); - } - - @Test - void generateInjectionForNonRequiredField() { - Field field = field(SampleBean.class, "counter"); - assertThat(generateInjection(field, false)).isEqualTo(""" - instanceContext.field("counter") - .resolve(beanFactory, false).ifResolved((attributes) -> bean.counter = attributes.get(0))"""); - } - - @Test - void generateInjectionForRequiredPrivateField() { - Field field = field(SampleBean.class, "source"); - assertThat(generateInjection(field, true)).isEqualTo(""" - instanceContext.field("source") - .invoke(beanFactory, (attributes) -> { - Field sourceField = ReflectionUtils.findField(InjectionGeneratorTests.SampleBean.class, "source"); - ReflectionUtils.makeAccessible(sourceField); - ReflectionUtils.setField(sourceField, bean, attributes.get(0)); - })"""); - } - - @Test - void getProtectedAccessInjectionOptionsForUnsupportedMember() { - assertThatIllegalArgumentException().isThrownBy(() -> - getProtectedAccessInjectionOptions(mock(Member.class))); - } - - @Test - void getProtectedAccessInjectionOptionsForPackagePublicField() { - analyzeProtectedAccess(field(SampleBean.class, "enabled")); - assertThat(this.protectedAccess.isAccessible("com.example")).isTrue(); - } - - @Test - void getProtectedAccessInjectionOptionsForPackageProtectedField() { - analyzeProtectedAccess(field(SampleBean.class, "counter")); - assertPrivilegedAccess(SampleBean.class); - } - - @Test - void getProtectedAccessInjectionOptionsForPrivateField() { - analyzeProtectedAccess(field(SampleBean.class, "source")); - assertThat(this.protectedAccess.isAccessible("com.example")).isTrue(); - } - - @Test - void getProtectedAccessInjectionOptionsForPublicMethod() { - analyzeProtectedAccess(method(SampleBean.class, "setEnabled", Boolean.class)); - assertThat(this.protectedAccess.isAccessible("com.example")).isTrue(); - } - - @Test - void getProtectedAccessInjectionOptionsForPackageProtectedMethod() { - analyzeProtectedAccess(method(SampleBean.class, "sourceAndCounter", String.class, Integer.class)); - assertPrivilegedAccess(SampleBean.class); - } - - - private Method method(Class type, String name, Class... parameterTypes) { - Method method = ReflectionUtils.findMethod(type, name, parameterTypes); - assertThat(method).isNotNull(); - return method; - } - - private Field field(Class type, String name) { - Field field = ReflectionUtils.findField(type, name); - assertThat(field).isNotNull(); - return field; - } - - private String generateInstantiation(Executable creator) { - return CodeSnippet.process(code -> code.add(new InjectionGenerator().generateInstantiation(creator))); - } - - private String generateInjection(Member member, boolean required) { - return CodeSnippet.process(code -> code.add(new InjectionGenerator().generateInjection(member, required))); - } - - private void analyzeProtectedAccess(Member member) { - this.protectedAccess.analyze(member, getProtectedAccessInjectionOptions(member)); - } - - private Options getProtectedAccessInjectionOptions(Member member) { - return new InjectionGenerator().getProtectedAccessInjectionOptions(member); - } - - private void assertPrivilegedAccess(Class target) { - assertThat(this.protectedAccess.isAccessible("com.example")).isFalse(); - assertThat(this.protectedAccess.getPrivilegedPackageName("com.example")).isEqualTo(target.getPackageName()); - assertThat(this.protectedAccess.isAccessible(target.getPackageName())).isTrue(); - } - - @SuppressWarnings("unused") - public static class SampleBean { - - public Boolean enabled; - - private String source; - - Integer counter; - - - public void setEnabled(Boolean enabled) { - - } - - void sourceAndCounter(String source, Integer counter) { - - } - - void nameAndCounter(String name, ObjectProvider counter) { - - } - - String source(Integer counter) { - return "source" + counter; - } - - String sourceWithProvider(ObjectProvider counter) { - return "source" + counter.getIfAvailable(() -> 0); - } - - static String staticSource(Integer counter) { - return counter + "source"; - } - - } - - @SuppressWarnings("unused") - static class SimpleBean { - - String name() { - return "test"; - } - - static Integer number() { - return 42; - } - - } - - @SuppressWarnings("unused") - static class SimpleConstructorBean { - - private final String source; - - private final Integer counter; - - public SimpleConstructorBean(String source, Integer counter) { - this.source = source; - this.counter = counter; - } - - class InnerClass { - - } - - } - - @SuppressWarnings("unused") - static class GenericConstructorBean { - - private final ObjectProvider counter; - - GenericConstructorBean(ObjectProvider counter) { - this.counter = counter; - } - - } - - static class AmbiguousConstructorBean { - - AmbiguousConstructorBean(String first, String second) { - - } - - AmbiguousConstructorBean(String first, Number second) { - - } - - } -} diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/generator/config/BeanDefinitionRegistrarTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/generator/config/BeanDefinitionRegistrarTests.java deleted file mode 100644 index 0f6a097056..0000000000 --- a/spring-beans/src/test/java/org/springframework/beans/factory/generator/config/BeanDefinitionRegistrarTests.java +++ /dev/null @@ -1,463 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.beans.factory.generator.config; - -import java.io.IOException; -import java.lang.reflect.Field; - -import org.junit.jupiter.api.Test; -import org.mockito.InOrder; - -import org.springframework.beans.FatalBeanException; -import org.springframework.beans.factory.BeanCreationException; -import org.springframework.beans.factory.FactoryBean; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.beans.factory.generator.config.BeanDefinitionRegistrar.BeanInstanceContext; -import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.core.ResolvableType; -import org.springframework.core.env.Environment; -import org.springframework.core.io.DefaultResourceLoader; -import org.springframework.core.io.ResourceLoader; -import org.springframework.util.ReflectionUtils; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatIllegalStateException; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.mock; - -/** - * Tests for {@link BeanDefinitionRegistrar}. - * - * @author Stephane Nicoll - */ -class BeanDefinitionRegistrarTests { - - @Test - void beanDefinitionWithBeanClassDoesNotSetTargetType() { - RootBeanDefinition beanDefinition = BeanDefinitionRegistrar.of("test", String.class).toBeanDefinition(); - assertThat(beanDefinition.getBeanClass()).isEqualTo(String.class); - assertThat(beanDefinition.getTargetType()).isNull(); - } - - @Test - void beanDefinitionWithResolvableTypeSetsTargetType() { - ResolvableType targetType = ResolvableType.forClassWithGenerics(NumberHolder.class, Integer.class); - RootBeanDefinition beanDefinition = BeanDefinitionRegistrar.of("test", targetType).toBeanDefinition(); - assertThat(beanDefinition.getTargetType()).isNotNull().isEqualTo(NumberHolder.class); - } - - @Test - void registerWithSimpleInstanceSupplier() { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - BeanDefinitionRegistrar.of("test", InjectionSample.class) - .instanceSupplier(InjectionSample::new).register(beanFactory); - assertBeanFactory(beanFactory, () -> { - assertThat(beanFactory.containsBean("test")).isTrue(); - assertThat(beanFactory.getBean(InjectionSample.class)).isNotNull(); - }); - } - - @Test - void registerWithSimpleInstanceSupplierThatThrowsRuntimeException() { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - Exception exception = new IllegalArgumentException("test exception"); - BeanDefinitionRegistrar.of("testBean", InjectionSample.class) - .instanceSupplier(() -> { - throw exception; - }).register(beanFactory); - assertThatThrownBy(() -> beanFactory.getBean("testBean")).isInstanceOf(BeanCreationException.class) - .getRootCause().isEqualTo(exception); - } - - @Test - void registerWithSimpleInstanceSupplierThatThrowsCheckedException() { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - Exception exception = new IOException("test exception"); - BeanDefinitionRegistrar.of("testBean", InjectionSample.class) - .instanceSupplier(() -> { - throw exception; - }).register(beanFactory); - assertThatThrownBy(() -> beanFactory.getBean("testBean")).isInstanceOf(BeanCreationException.class) - .getRootCause().isEqualTo(exception); - } - - @Test - void registerWithoutBeanNameFails() { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - BeanDefinitionRegistrar registrar = BeanDefinitionRegistrar.inner(InjectionSample.class) - .instanceSupplier(InjectionSample::new); - assertThatIllegalStateException().isThrownBy(() -> registrar.register(beanFactory)) - .withMessageContaining("Bean name not set."); - } - - @Test - @SuppressWarnings("unchecked") - void registerWithCustomizer() { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - BeanDefinitionRegistrar.ThrowableConsumer first = mock(BeanDefinitionRegistrar.ThrowableConsumer.class); - BeanDefinitionRegistrar.ThrowableConsumer second = mock(BeanDefinitionRegistrar.ThrowableConsumer.class); - BeanDefinitionRegistrar.of("test", InjectionSample.class) - .instanceSupplier(InjectionSample::new).customize(first).customize(second).register(beanFactory); - assertBeanFactory(beanFactory, () -> { - assertThat(beanFactory.containsBean("test")).isTrue(); - InOrder ordered = inOrder(first, second); - ordered.verify(first).accept(any(RootBeanDefinition.class)); - ordered.verify(second).accept(any(RootBeanDefinition.class)); - }); - } - - @Test - void registerWithCustomizerThatThrowsRuntimeException() { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - Exception exception = new RuntimeException("test exception"); - BeanDefinitionRegistrar registrar = BeanDefinitionRegistrar.of("test", InjectionSample.class) - .instanceSupplier(InjectionSample::new).customize(bd -> { - throw exception; - }); - assertThatThrownBy(() -> registrar.register(beanFactory)).isInstanceOf(FatalBeanException.class) - .hasMessageContaining("Failed to create bean definition for bean with name 'test'") - .hasMessageContaining("test exception") - .hasCause(exception); - } - - @Test - void registerWithCustomizerThatThrowsCheckedException() { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - Exception exception = new IOException("test exception"); - BeanDefinitionRegistrar registrar = BeanDefinitionRegistrar.of("test", InjectionSample.class) - .instanceSupplier(InjectionSample::new).customize(bd -> { - throw exception; - }); - assertThatThrownBy(() -> registrar.register(beanFactory)).isInstanceOf(FatalBeanException.class) - .hasMessageContaining("Failed to create bean definition for bean with name 'test'") - .hasMessageContaining("test exception"); - } - - @Test - void registerWithConstructorInstantiation() { - ResourceLoader resourceLoader = new DefaultResourceLoader(); - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - beanFactory.registerResolvableDependency(ResourceLoader.class, resourceLoader); - BeanDefinitionRegistrar.of("test", ConstructorSample.class).withConstructor(ResourceLoader.class) - .instanceSupplier(instanceContext -> instanceContext.create(beanFactory, attributes -> - new ConstructorSample(attributes.get(0)))).register(beanFactory); - assertBeanFactory(beanFactory, () -> { - assertThat(beanFactory.containsBean("test")).isTrue(); - assertThat(beanFactory.getBean(ConstructorSample.class).resourceLoader).isEqualTo(resourceLoader); - }); - } - - @Test - void registerWithConstructorInstantiationThatThrowsRuntimeException() { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - Exception exception = new RuntimeException("test exception"); - BeanDefinitionRegistrar.of("test", ConstructorSample.class).withConstructor(ResourceLoader.class) - .instanceSupplier(instanceContext -> { - throw exception; - }).register(beanFactory); - assertThatThrownBy(() -> beanFactory.getBean("test")).isInstanceOf(BeanCreationException.class) - .getRootCause().isEqualTo(exception); - } - - @Test - void registerWithConstructorInstantiationThatThrowsCheckedException() { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - Exception exception = new IOException("test exception"); - BeanDefinitionRegistrar.of("test", ConstructorSample.class).withConstructor(ResourceLoader.class) - .instanceSupplier(instanceContext -> { - throw exception; - }).register(beanFactory); - assertThatThrownBy(() -> beanFactory.getBean("test")).isInstanceOf(BeanCreationException.class) - .getRootCause().isEqualTo(exception); - } - - @Test - void registerWithConstructorOnInnerClass() { - Environment environment = mock(Environment.class); - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - beanFactory.registerSingleton("environment", environment); - beanFactory.registerBeanDefinition("sample", BeanDefinitionBuilder.rootBeanDefinition(InnerClassSample.class).getBeanDefinition()); - BeanDefinitionRegistrar.of("test", InnerClassSample.Inner.class).withConstructor(InnerClassSample.class, Environment.class) - .instanceSupplier(instanceContext -> instanceContext.create(beanFactory, attributes -> - beanFactory.getBean(InnerClassSample.class).new Inner(attributes.get(1)))) - .register(beanFactory); - assertBeanFactory(beanFactory, () -> { - assertThat(beanFactory.containsBean("test")).isTrue(); - InnerClassSample.Inner bean = beanFactory.getBean(InnerClassSample.Inner.class); - assertThat(bean.environment).isEqualTo(environment); - }); - } - - @Test - void registerWithInvalidConstructor() { - assertThatThrownBy(() -> BeanDefinitionRegistrar.of("test", ConstructorSample.class).withConstructor(Object.class)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("No constructor with type(s) [java.lang.Object] found on") - .hasMessageContaining(ConstructorSample.class.getName()); - } - - @Test - void registerWithFactoryMethod() { - ResourceLoader resourceLoader = new DefaultResourceLoader(); - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - beanFactory.registerResolvableDependency(ResourceLoader.class, resourceLoader); - BeanDefinitionRegistrar.of("configuration", ConfigurationSample.class).instanceSupplier(ConfigurationSample::new) - .register(beanFactory); - BeanDefinitionRegistrar.of("test", ConstructorSample.class) - .withFactoryMethod(ConfigurationSample.class, "sampleBean", ResourceLoader.class) - .instanceSupplier(instanceContext -> instanceContext.create(beanFactory, attributes -> - beanFactory.getBean(ConfigurationSample.class).sampleBean(attributes.get(0)))) - .register(beanFactory); - assertBeanFactory(beanFactory, () -> { - assertThat(beanFactory.containsBean("configuration")).isTrue(); - assertThat(beanFactory.containsBean("test")).isTrue(); - assertThat(beanFactory.getBean(ConstructorSample.class).resourceLoader).isEqualTo(resourceLoader); - RootBeanDefinition bd = (RootBeanDefinition) beanFactory.getBeanDefinition("test"); - assertThat(bd.getResolvedFactoryMethod()).isNotNull().isEqualTo( - ReflectionUtils.findMethod(ConfigurationSample.class, "sampleBean", ResourceLoader.class)); - }); - } - - @Test - void registerWithCreateShortcutWithoutFactoryMethod() { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - BeanDefinitionRegistrar.of("configuration", ConfigurationSample.class).instanceSupplier(ConfigurationSample::new) - .register(beanFactory); - BeanDefinitionRegistrar.of("test", ConstructorSample.class) - .instanceSupplier(instanceContext -> instanceContext.create(beanFactory, attributes -> - beanFactory.getBean(ConfigurationSample.class).sampleBean(attributes.get(0)))) - .register(beanFactory); - assertThatThrownBy(() -> beanFactory.getBean("test")).isInstanceOf(BeanCreationException.class) - .hasMessageContaining("No factory method or constructor is set"); - } - - @Test - void registerWithInjectedField() { - Environment environment = mock(Environment.class); - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - beanFactory.registerSingleton("environment", environment); - BeanDefinitionRegistrar.of("test", InjectionSample.class).instanceSupplier(instanceContext -> { - InjectionSample bean = new InjectionSample(); - instanceContext.field("environment").invoke(beanFactory, attributes -> - bean.environment = (attributes.get(0))); - return bean; - }).register(beanFactory); - assertBeanFactory(beanFactory, () -> { - assertThat(beanFactory.containsBean("test")).isTrue(); - assertThat(beanFactory.getBean(InjectionSample.class).environment).isEqualTo(environment); - }); - } - - @Test - void registerWithInvalidField() { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - BeanDefinitionRegistrar.of("test", InjectionSample.class).instanceSupplier(instanceContext -> - instanceContext.field("doesNotExist").resolve(beanFactory)).register(beanFactory); - assertThatThrownBy(() -> beanFactory.getBean(InjectionSample.class) - ).isInstanceOf(BeanCreationException.class).hasMessageContaining( - "No field 'doesNotExist' found on " + InjectionSample.class.getName()); - } - - @Test - void registerWithInjectedMethod() { - Environment environment = mock(Environment.class); - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - beanFactory.registerSingleton("environment", environment); - BeanDefinitionRegistrar.of("test", InjectionSample.class).instanceSupplier(instanceContext -> { - InjectionSample bean = new InjectionSample(); - instanceContext.method("setEnvironment", Environment.class).invoke(beanFactory, - attributes -> bean.setEnvironment(attributes.get(0))); - return bean; - }).register(beanFactory); - assertBeanFactory(beanFactory, () -> { - assertThat(beanFactory.containsBean("test")).isTrue(); - assertThat(beanFactory.getBean(InjectionSample.class).environment).isEqualTo(environment); - }); - } - - @Test - void registerWithInvalidMethod() { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - assertThatThrownBy(() -> { - BeanDefinitionRegistrar.of("test", InjectionSample.class).instanceSupplier(instanceContext -> - instanceContext.method("setEnvironment", Object.class).resolve(beanFactory)).register(beanFactory); - beanFactory.getBean(InjectionSample.class); - } - ).isInstanceOf(BeanCreationException.class) - .hasMessageContaining("No method '%s' with type(s) [%s] found", "setEnvironment", Object.class.getName()) - .hasMessageContaining(InjectionSample.class.getName()); - } - - @Test - void innerBeanDefinitionWithClass() { - RootBeanDefinition beanDefinition = BeanDefinitionRegistrar.inner(ConfigurationSample.class) - .customize(bd -> bd.setSynthetic(true)).toBeanDefinition(); - assertThat(beanDefinition).isNotNull(); - assertThat(beanDefinition.getResolvableType().resolve()).isEqualTo(ConfigurationSample.class); - assertThat(beanDefinition.isSynthetic()).isTrue(); - } - - @Test - void innerBeanDefinitionWithResolvableType() { - RootBeanDefinition beanDefinition = BeanDefinitionRegistrar.inner(ResolvableType.forClass(ConfigurationSample.class)) - .customize(bd -> bd.setDescription("test")).toBeanDefinition(); - assertThat(beanDefinition).isNotNull(); - assertThat(beanDefinition.getResolvableType().resolve()).isEqualTo(ConfigurationSample.class); - assertThat(beanDefinition.getDescription()).isEqualTo("test"); - } - - @Test - void innerBeanDefinitionHasInnerBeanNameInInstanceSupplier() { - RootBeanDefinition beanDefinition = BeanDefinitionRegistrar.inner(String.class) - .instanceSupplier(instanceContext -> { - Field field = ReflectionUtils.findField(BeanInstanceContext.class, "beanName", String.class); - ReflectionUtils.makeAccessible(field); - return ReflectionUtils.getField(field, instanceContext); - }).toBeanDefinition(); - assertThat(beanDefinition).isNotNull(); - String beanName = (String) beanDefinition.getInstanceSupplier().get(); - assertThat(beanName).isNotNull().startsWith("(inner bean)#"); - } - - - private void assertBeanFactory(DefaultListableBeanFactory beanFactory, Runnable assertions) { - assertions.run(); - } - - - static class ConfigurationSample { - - ConstructorSample sampleBean(ResourceLoader resourceLoader) { - return new ConstructorSample(resourceLoader); - } - - } - - static class ConstructorSample { - private final ResourceLoader resourceLoader; - - ConstructorSample(ResourceLoader resourceLoader) { - this.resourceLoader = resourceLoader; - } - } - - static class MultiArgConstructorSample { - - @SuppressWarnings("unused") - private final String name; - - @SuppressWarnings("unused") - private final Integer counter; - - public MultiArgConstructorSample(String name, Integer counter) { - this.name = name; - this.counter = counter; - } - - } - - static class InjectionSample { - - private Environment environment; - - @SuppressWarnings("unused") - private String name; - - @SuppressWarnings("unused") - private Integer counter; - - void setEnvironment(Environment environment) { - this.environment = environment; - } - - void setNameAndCounter(@Value("${test.name:test}") String name, @Value("${test.counter:42}") Integer counter) { - this.name = name; - this.counter = counter; - } - - } - - static class InnerClassSample { - - class Inner { - - private Environment environment; - - Inner(Environment environment) { - this.environment = environment; - } - - } - - } - - static class GenericFactoryBeanConfiguration { - - FactoryBean> integerHolderFactory() { - return new GenericFactoryBean<>(integerHolder()); - } - - NumberHolder integerHolder() { - return new NumberHolder<>(42); - } - - } - - static class GenericFactoryBean implements FactoryBean { - - private final T value; - - public GenericFactoryBean(T value) { - this.value = value; - } - - @Override - public T getObject() { - return this.value; - } - - @Override - public Class getObjectType() { - return this.value.getClass(); - } - } - - static class NumberHolder { - - @SuppressWarnings("unused") - private final N number; - - public NumberHolder(N number) { - this.number = number; - } - - } - - static class NumberHolderSample { - - @Autowired - @SuppressWarnings("unused") - private NumberHolder numberHolder; - - } - -} diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/generator/config/InjectedConstructionResolverTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/generator/config/InjectedConstructionResolverTests.java deleted file mode 100644 index ff599eb4ff..0000000000 --- a/spring-beans/src/test/java/org/springframework/beans/factory/generator/config/InjectedConstructionResolverTests.java +++ /dev/null @@ -1,476 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.beans.factory.generator.config; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Method; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Stream; - -import org.assertj.core.util.Arrays; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; - -import org.springframework.beans.factory.NoSuchBeanDefinitionException; -import org.springframework.beans.factory.ObjectProvider; -import org.springframework.beans.factory.UnsatisfiedDependencyException; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder; -import org.springframework.beans.factory.config.RuntimeBeanReference; -import org.springframework.beans.factory.support.AbstractBeanDefinition; -import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.core.env.Environment; -import org.springframework.core.io.DefaultResourceLoader; -import org.springframework.core.io.ResourceLoader; -import org.springframework.util.ReflectionUtils; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.assertj.core.api.Assertions.entry; -import static org.mockito.Mockito.mock; - -/** - * Tests for {@link InjectedConstructionResolver}. - * - * @author Stephane Nicoll - */ -class InjectedConstructionResolverTests { - - @Test - void resolveNoArgConstructor() { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - InjectedElementAttributes attributes = createResolverForConstructor( - InjectedConstructionResolverTests.class).resolve(beanFactory); - assertThat(attributes.isResolved()).isTrue(); - } - - @ParameterizedTest - @MethodSource("singleArgConstruction") - void resolveSingleArgConstructor(InjectedConstructionResolver resolver) { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - beanFactory.registerSingleton("one", "1"); - InjectedElementAttributes attributes = resolver.resolve(beanFactory); - assertThat(attributes.isResolved()).isTrue(); - assertThat((String) attributes.get(0)).isEqualTo("1"); - } - - @ParameterizedTest - @MethodSource("singleArgConstruction") - void resolveRequiredDependencyNotPresentThrowsUnsatisfiedDependencyException(InjectedConstructionResolver resolver) { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - assertThatThrownBy(() -> resolver.resolve(beanFactory)) - .isInstanceOfSatisfying(UnsatisfiedDependencyException.class, ex -> { - assertThat(ex.getBeanName()).isEqualTo("test"); - assertThat(ex.getInjectionPoint()).isNotNull(); - assertThat(ex.getInjectionPoint().getMember()).isEqualTo(resolver.getExecutable()); - }); - } - - @ParameterizedTest - @MethodSource("arrayOfBeansConstruction") - void resolveArrayOfBeans(InjectedConstructionResolver resolver) { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - beanFactory.registerSingleton("one", "1"); - beanFactory.registerSingleton("two", "2"); - InjectedElementAttributes attributes = resolver.resolve(beanFactory); - assertThat(attributes.isResolved()).isTrue(); - Object attribute = attributes.get(0); - assertThat(Arrays.isArray(attribute)).isTrue(); - assertThat((Object[]) attribute).containsExactly("1", "2"); - } - - @ParameterizedTest - @MethodSource("arrayOfBeansConstruction") - void resolveRequiredArrayOfBeansInjectEmptyArray(InjectedConstructionResolver resolver) { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - InjectedElementAttributes attributes = resolver.resolve(beanFactory); - assertThat(attributes.isResolved()).isTrue(); - Object attribute = attributes.get(0); - assertThat(Arrays.isArray(attribute)).isTrue(); - assertThat((Object[]) attribute).isEmpty(); - - } - - static Stream arrayOfBeansConstruction() { - return Stream.of(Arguments.of(createResolverForConstructor(BeansCollectionConstructor.class, String[].class)), - Arguments.of(createResolverForFactoryMethod(BeansCollectionFactory.class, "array", String[].class))); - } - - @ParameterizedTest - @MethodSource("listOfBeansConstruction") - void resolveListOfBeans(InjectedConstructionResolver resolver) { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - beanFactory.registerSingleton("one", "1"); - beanFactory.registerSingleton("two", "2"); - InjectedElementAttributes attributes = resolver.resolve(beanFactory); - assertThat(attributes.isResolved()).isTrue(); - Object attribute = attributes.get(0); - assertThat(attribute).isInstanceOf(List.class).asList().containsExactly("1", "2"); - } - - @ParameterizedTest - @MethodSource("listOfBeansConstruction") - void resolveRequiredListOfBeansInjectEmptyList(InjectedConstructionResolver resolver) { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - InjectedElementAttributes attributes = resolver.resolve(beanFactory); - assertThat(attributes.isResolved()).isTrue(); - Object attribute = attributes.get(0); - assertThat(attribute).isInstanceOf(List.class); - assertThat((List) attribute).isEmpty(); - } - - static Stream listOfBeansConstruction() { - return Stream.of(Arguments.of(createResolverForConstructor(BeansCollectionConstructor.class, List.class)), - Arguments.of(createResolverForFactoryMethod(BeansCollectionFactory.class, "list", List.class))); - } - - @ParameterizedTest - @MethodSource("setOfBeansConstruction") - @SuppressWarnings("unchecked") - void resolveSetOfBeans(InjectedConstructionResolver resolver) { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - beanFactory.registerSingleton("one", "1"); - beanFactory.registerSingleton("two", "2"); - InjectedElementAttributes attributes = resolver.resolve(beanFactory); - assertThat(attributes.isResolved()).isTrue(); - Object attribute = attributes.get(0); - assertThat(attribute).isInstanceOf(Set.class); - assertThat((Set) attribute).containsExactly("1", "2"); - } - - @ParameterizedTest - @MethodSource("setOfBeansConstruction") - void resolveRequiredSetOfBeansInjectEmptySet(InjectedConstructionResolver resolver) { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - InjectedElementAttributes attributes = resolver.resolve(beanFactory); - assertThat(attributes.isResolved()).isTrue(); - Object attribute = attributes.get(0); - assertThat(attribute).isInstanceOf(Set.class); - assertThat((Set) attribute).isEmpty(); - } - - static Stream setOfBeansConstruction() { - return Stream.of(Arguments.of(createResolverForConstructor(BeansCollectionConstructor.class, Set.class)), - Arguments.of(createResolverForFactoryMethod(BeansCollectionFactory.class, "set", Set.class))); - } - - @ParameterizedTest - @MethodSource("mapOfBeansConstruction") - @SuppressWarnings("unchecked") - void resolveMapOfBeans(InjectedConstructionResolver resolver) { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - beanFactory.registerSingleton("one", "1"); - beanFactory.registerSingleton("two", "2"); - InjectedElementAttributes attributes = resolver.resolve(beanFactory); - assertThat(attributes.isResolved()).isTrue(); - Object attribute = attributes.get(0); - assertThat(attribute).isInstanceOf(Map.class); - assertThat((Map) attribute).containsExactly(entry("one", "1"), entry("two", "2")); - } - - @ParameterizedTest - @MethodSource("mapOfBeansConstruction") - void resolveRequiredMapOfBeansInjectEmptySet(InjectedConstructionResolver resolver) { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - InjectedElementAttributes attributes = resolver.resolve(beanFactory); - assertThat(attributes.isResolved()).isTrue(); - Object attribute = attributes.get(0); - assertThat(attribute).isInstanceOf(Map.class); - assertThat((Map) attribute).isEmpty(); - } - - static Stream mapOfBeansConstruction() { - return Stream.of(Arguments.of(createResolverForConstructor(BeansCollectionConstructor.class, Map.class)), - Arguments.of(createResolverForFactoryMethod(BeansCollectionFactory.class, "map", Map.class))); - } - - @ParameterizedTest - @MethodSource("multiArgsConstruction") - void resolveMultiArgsConstructor(InjectedConstructionResolver resolver) { - ResourceLoader resourceLoader = new DefaultResourceLoader(); - Environment environment = mock(Environment.class); - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - beanFactory.registerResolvableDependency(ResourceLoader.class, resourceLoader); - beanFactory.registerSingleton("environment", environment); - beanFactory.registerSingleton("one", "1"); - InjectedElementAttributes attributes = resolver.resolve(beanFactory); - assertThat(attributes.isResolved()).isTrue(); - assertThat((ResourceLoader) attributes.get(0)).isEqualTo(resourceLoader); - assertThat((Environment) attributes.get(1)).isEqualTo(environment); - ObjectProvider provider = attributes.get(2); - assertThat(provider.getIfAvailable()).isEqualTo("1"); - } - - @ParameterizedTest - @MethodSource("mixedArgsConstruction") - void resolveMixedArgsConstructorWithUserValue(InjectedConstructionResolver resolver) { - ResourceLoader resourceLoader = new DefaultResourceLoader(); - Environment environment = mock(Environment.class); - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - beanFactory.registerResolvableDependency(ResourceLoader.class, resourceLoader); - beanFactory.registerSingleton("environment", environment); - AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(MixedArgsConstructor.class) - .setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR).getBeanDefinition(); - beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(1, "user-value"); - beanFactory.registerBeanDefinition("test", beanDefinition); - InjectedElementAttributes attributes = resolver.resolve(beanFactory); - assertThat(attributes.isResolved()).isTrue(); - assertThat((ResourceLoader) attributes.get(0)).isEqualTo(resourceLoader); - assertThat((String) attributes.get(1)).isEqualTo("user-value"); - assertThat((Environment) attributes.get(2)).isEqualTo(environment); - } - - @ParameterizedTest - @MethodSource("mixedArgsConstruction") - void resolveMixedArgsConstructorWithUserBeanReference(InjectedConstructionResolver resolver) { - ResourceLoader resourceLoader = new DefaultResourceLoader(); - Environment environment = mock(Environment.class); - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - beanFactory.registerResolvableDependency(ResourceLoader.class, resourceLoader); - beanFactory.registerSingleton("environment", environment); - beanFactory.registerSingleton("one", "1"); - beanFactory.registerSingleton("two", "2"); - AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(MixedArgsConstructor.class) - .setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR).getBeanDefinition(); - beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(1, new RuntimeBeanReference("two")); - beanFactory.registerBeanDefinition("test", beanDefinition); - InjectedElementAttributes attributes = resolver.resolve(beanFactory); - assertThat(attributes.isResolved()).isTrue(); - assertThat((ResourceLoader) attributes.get(0)).isEqualTo(resourceLoader); - assertThat((String) attributes.get(1)).isEqualTo("2"); - assertThat((Environment) attributes.get(2)).isEqualTo(environment); - } - - @Test - void resolveUserValueWithTypeConversionRequired() { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(CharDependency.class) - .setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR).getBeanDefinition(); - beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, "\\"); - beanFactory.registerBeanDefinition("test", beanDefinition); - InjectedElementAttributes attributes = createResolverForConstructor(CharDependency.class, char.class).resolve(beanFactory); - assertThat(attributes.isResolved()).isTrue(); - Object attribute = attributes.get(0); - assertThat(attribute).isInstanceOf(Character.class); - assertThat((Character) attribute).isEqualTo('\\'); - } - - @ParameterizedTest - @MethodSource("singleArgConstruction") - void resolveUserValueWithBeanReference(InjectedConstructionResolver resolver) { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - beanFactory.registerSingleton("stringBean", "string"); - beanFactory.registerBeanDefinition("test", BeanDefinitionBuilder.rootBeanDefinition(SingleArgConstructor.class) - .addConstructorArgReference("stringBean").getBeanDefinition()); - InjectedElementAttributes attributes = resolver.resolve(beanFactory); - assertThat(attributes.isResolved()).isTrue(); - Object attribute = attributes.get(0); - assertThat(attribute).isEqualTo("string"); - } - - @ParameterizedTest - @MethodSource("singleArgConstruction") - void resolveUserValueWithBeanDefinition(InjectedConstructionResolver resolver) { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - AbstractBeanDefinition userValue = BeanDefinitionBuilder.rootBeanDefinition(String.class, () -> "string").getBeanDefinition(); - beanFactory.registerBeanDefinition("test", BeanDefinitionBuilder.rootBeanDefinition(SingleArgConstructor.class) - .addConstructorArgValue(userValue).getBeanDefinition()); - InjectedElementAttributes attributes = resolver.resolve(beanFactory); - assertThat(attributes.isResolved()).isTrue(); - Object attribute = attributes.get(0); - assertThat(attribute).isEqualTo("string"); - } - - @ParameterizedTest - @MethodSource("singleArgConstruction") - void resolveUserValueThatIsAlreadyResolved(InjectedConstructionResolver resolver) { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(SingleArgConstructor.class).getBeanDefinition(); - ValueHolder valueHolder = new ValueHolder('a'); - valueHolder.setConvertedValue("this is an a"); - beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, valueHolder); - beanFactory.registerBeanDefinition("test", beanDefinition); - InjectedElementAttributes attributes = resolver.resolve(beanFactory); - assertThat(attributes.isResolved()).isTrue(); - Object attribute = attributes.get(0); - assertThat(attribute).isEqualTo("this is an a"); - } - - @ParameterizedTest - @MethodSource("singleArgConstruction") - void createInvokeFactory(InjectedConstructionResolver resolver) { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - beanFactory.registerSingleton("one", "1"); - String instance = resolver.create(beanFactory, attributes -> attributes.get(0)); - assertThat(instance).isEqualTo("1"); - } - - private static InjectedConstructionResolver createResolverForConstructor(Class beanType, Class... parameterTypes) { - try { - Constructor executable = beanType.getDeclaredConstructor(parameterTypes); - return new InjectedConstructionResolver(executable, beanType, "test", - InjectedConstructionResolverTests::safeGetBeanDefinition); - } - catch (NoSuchMethodException ex) { - throw new IllegalStateException(ex); - } - } - - private static InjectedConstructionResolver createResolverForFactoryMethod(Class targetType, - String methodName, Class... parameterTypes) { - Method executable = ReflectionUtils.findMethod(targetType, methodName, parameterTypes); - return new InjectedConstructionResolver(executable, targetType, "test", - InjectedConstructionResolverTests::safeGetBeanDefinition); - } - - private static BeanDefinition safeGetBeanDefinition(DefaultListableBeanFactory beanFactory) { - try { - return beanFactory.getBeanDefinition("test"); - } - catch (NoSuchBeanDefinitionException ex) { - return null; - } - } - - static Stream singleArgConstruction() { - return Stream.of(Arguments.of(createResolverForConstructor(SingleArgConstructor.class, String.class)), - Arguments.of(createResolverForFactoryMethod(SingleArgFactory.class, "single", String.class))); - } - - @SuppressWarnings("unused") - static class SingleArgConstructor { - - public SingleArgConstructor(String s) { - } - - } - - @SuppressWarnings("unused") - static class SingleArgFactory { - - String single(String s) { - return s; - } - - } - - @SuppressWarnings("unused") - static class BeansCollectionConstructor { - - public BeansCollectionConstructor(String[] beans) { - - } - - public BeansCollectionConstructor(List beans) { - - } - - public BeansCollectionConstructor(Set beans) { - - } - - public BeansCollectionConstructor(Map beans) { - - } - - } - - @SuppressWarnings("unused") - static class BeansCollectionFactory { - - public String array(String[] beans) { - return "test"; - } - - public String list(List beans) { - return "test"; - } - - public String set(Set beans) { - return "test"; - } - - public String map(Map beans) { - return "test"; - } - - } - - static Stream multiArgsConstruction() { - return Stream.of( - Arguments.of(createResolverForConstructor(MultiArgsConstructor.class, ResourceLoader.class, - Environment.class, ObjectProvider.class)), - Arguments.of(createResolverForFactoryMethod(MultiArgsFactory.class, "multiArgs", ResourceLoader.class, - Environment.class, ObjectProvider.class))); - } - - @SuppressWarnings("unused") - static class MultiArgsConstructor { - - public MultiArgsConstructor(ResourceLoader resourceLoader, Environment environment, ObjectProvider provider) { - } - } - - @SuppressWarnings("unused") - static class MultiArgsFactory { - - String multiArgs(ResourceLoader resourceLoader, Environment environment, ObjectProvider provider) { - return "test"; - } - } - - static Stream mixedArgsConstruction() { - return Stream.of( - Arguments.of(createResolverForConstructor(MixedArgsConstructor.class, ResourceLoader.class, - String.class, Environment.class)), - Arguments.of(createResolverForFactoryMethod(MixedArgsFactory.class, "mixedArgs", ResourceLoader.class, - String.class, Environment.class))); - } - - @SuppressWarnings("unused") - static class MixedArgsConstructor { - - public MixedArgsConstructor(ResourceLoader resourceLoader, String test, Environment environment) { - - } - - } - - @SuppressWarnings("unused") - static class MixedArgsFactory { - - String mixedArgs(ResourceLoader resourceLoader, String test, Environment environment) { - return "test"; - } - - } - - @SuppressWarnings("unused") - static class CharDependency { - - CharDependency(char escapeChar) { - } - - } - -} diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/generator/config/InjectedElementAttributesTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/generator/config/InjectedElementAttributesTests.java deleted file mode 100644 index f2f66811b9..0000000000 --- a/spring-beans/src/test/java/org/springframework/beans/factory/generator/config/InjectedElementAttributesTests.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.beans.factory.generator.config; - -import java.util.Collections; - -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; - -/** - * Tests for {@link InjectedElementAttributes}. - * - * @author Stephane Nicoll - */ -class InjectedElementAttributesTests { - - private static final InjectedElementAttributes unresolved = new InjectedElementAttributes(null); - - private static final InjectedElementAttributes resolved = new InjectedElementAttributes(Collections.singletonList("test")); - - @Test - void isResolvedWithUnresolvedAttributes() { - assertThat(unresolved.isResolved()).isFalse(); - } - - @Test - void isResolvedWithResoledAttributes() { - assertThat(resolved.isResolved()).isTrue(); - } - - @Test - void ifResolvedWithUnresolvedAttributesDoesNotInvokeRunnable() { - Runnable runnable = mock(Runnable.class); - unresolved.ifResolved(runnable); - verifyNoInteractions(runnable); - } - - @Test - void ifResolvedWithResolvedAttributesInvokesRunnable() { - Runnable runnable = mock(Runnable.class); - resolved.ifResolved(runnable); - verify(runnable).run(); - } - - @Test - @SuppressWarnings("unchecked") - void ifResolvedWithUnresolvedAttributesDoesNotInvokeConsumer() { - BeanDefinitionRegistrar.ThrowableConsumer consumer = mock(BeanDefinitionRegistrar.ThrowableConsumer.class); - unresolved.ifResolved(consumer); - verifyNoInteractions(consumer); - } - - @Test - @SuppressWarnings("unchecked") - void ifResolvedWithResolvedAttributesInvokesConsumer() { - BeanDefinitionRegistrar.ThrowableConsumer consumer = mock(BeanDefinitionRegistrar.ThrowableConsumer.class); - resolved.ifResolved(consumer); - verify(consumer).accept(resolved); - } - - @Test - void getWithAvailableAttribute() { - InjectedElementAttributes attributes = new InjectedElementAttributes(Collections.singletonList("test")); - assertThat((String) attributes.get(0)).isEqualTo("test"); - } - - @Test - void getWithTypeAndAvailableAttribute() { - InjectedElementAttributes attributes = new InjectedElementAttributes(Collections.singletonList("test")); - assertThat(attributes.get(0, String.class)).isEqualTo("test"); - } - -} diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/generator/config/InjectedFieldResolverTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/generator/config/InjectedFieldResolverTests.java deleted file mode 100644 index 490729af94..0000000000 --- a/spring-beans/src/test/java/org/springframework/beans/factory/generator/config/InjectedFieldResolverTests.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.beans.factory.generator.config; - -import java.lang.reflect.Field; - -import org.junit.jupiter.api.Test; - -import org.springframework.beans.factory.UnsatisfiedDependencyException; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.util.ReflectionUtils; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -/** - * Tests for {@link InjectedFieldResolver}. - * - * @author Stephane Nicoll - */ -class InjectedFieldResolverTests { - - @Test - void resolveDependency() { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - beanFactory.registerSingleton("one", "1"); - InjectedElementAttributes attributes = createResolver(TestBean.class, "string", - String.class).resolve(beanFactory, true); - assertThat(attributes.isResolved()).isTrue(); - assertThat((String) attributes.get(0)).isEqualTo("1"); - } - - @Test - void resolveRequiredDependencyNotPresentThrowsUnsatisfiedDependencyException() { - Field field = ReflectionUtils.findField(TestBean.class, "string", String.class); - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - assertThatThrownBy(() -> createResolver(TestBean.class, "string", String.class).resolve(beanFactory)) - .isInstanceOfSatisfying(UnsatisfiedDependencyException.class, ex -> { - assertThat(ex.getBeanName()).isEqualTo("test"); - assertThat(ex.getInjectionPoint()).isNotNull(); - assertThat(ex.getInjectionPoint().getField()).isEqualTo(field); - }); - } - - @Test - void resolveNonRequiredDependency() { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - InjectedElementAttributes attributes = createResolver(TestBean.class, "string", String.class).resolve(beanFactory, false); - assertThat(attributes.isResolved()).isFalse(); - } - - private InjectedFieldResolver createResolver(Class beanType, String fieldName, Class fieldType) { - Field field = ReflectionUtils.findField(beanType, fieldName, fieldType); - assertThat(field).isNotNull(); - return new InjectedFieldResolver(field, "test"); - } - - static class TestBean { - - @SuppressWarnings("unused") - private String string; - - } - -} diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/generator/config/InjectedMethodResolverTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/generator/config/InjectedMethodResolverTests.java deleted file mode 100644 index d9b9e2aee2..0000000000 --- a/spring-beans/src/test/java/org/springframework/beans/factory/generator/config/InjectedMethodResolverTests.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.beans.factory.generator.config; - -import java.lang.reflect.Method; - -import org.junit.jupiter.api.Test; - -import org.springframework.beans.factory.UnsatisfiedDependencyException; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.core.env.Environment; -import org.springframework.util.ReflectionUtils; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verifyNoInteractions; - -/** - * Tests for {@link InjectedMethodResolver}. - * - * @author Stephane Nicoll - */ -class InjectedMethodResolverTests { - - @Test - void resolveSingleDependency() { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - beanFactory.registerSingleton("test", "testValue"); - InjectedElementAttributes attributes = createResolver(TestBean.class, "injectString", String.class) - .resolve(beanFactory, true); - assertThat(attributes.isResolved()).isTrue(); - assertThat((String) attributes.get(0)).isEqualTo("testValue"); - } - - @Test - void resolveRequiredDependencyNotPresentThrowsUnsatisfiedDependencyException() { - Method method = ReflectionUtils.findMethod(TestBean.class, "injectString", String.class); - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - assertThatThrownBy(() -> createResolver(TestBean.class, "injectString", String.class) - .resolve(beanFactory)).isInstanceOfSatisfying(UnsatisfiedDependencyException.class, ex -> { - assertThat(ex.getBeanName()).isEqualTo("test"); - assertThat(ex.getInjectionPoint()).isNotNull(); - assertThat(ex.getInjectionPoint().getMember()).isEqualTo(method); - }); - } - - @Test - void resolveNonRequiredDependency() { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - InjectedElementAttributes attributes = createResolver(TestBean.class, "injectString", String.class) - .resolve(beanFactory, false); - assertThat(attributes.isResolved()).isFalse(); - } - - @Test - void resolveDependencyAndEnvironment() { - Environment environment = mock(Environment.class); - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - beanFactory.registerSingleton("environment", environment); - beanFactory.registerSingleton("test", "testValue"); - InjectedElementAttributes attributes = createResolver(TestBean.class, "injectStringAndEnvironment", - String.class, Environment.class).resolve(beanFactory, true); - assertThat(attributes.isResolved()).isTrue(); - String string = attributes.get(0); - assertThat(string).isEqualTo("testValue"); - assertThat((Environment) attributes.get(1)).isEqualTo(environment); - } - - @Test - @SuppressWarnings("unchecked") - void createWithUnresolvedAttributesDoesNotInvokeCallback() { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - BeanDefinitionRegistrar.ThrowableFunction callback = mock(BeanDefinitionRegistrar.ThrowableFunction.class); - assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy(() -> - createResolver(TestBean.class, "injectString", String.class).create(beanFactory, callback)); - verifyNoInteractions(callback); - } - - @Test - @SuppressWarnings("unchecked") - void invokeWithUnresolvedAttributesDoesNotInvokeCallback() { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - BeanDefinitionRegistrar.ThrowableConsumer callback = mock(BeanDefinitionRegistrar.ThrowableConsumer.class); - assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy(() -> - createResolver(TestBean.class, "injectString", String.class).invoke(beanFactory, callback)); - verifyNoInteractions(callback); - } - - private InjectedMethodResolver createResolver(Class beanType, String methodName, Class... parameterTypes) { - Method method = ReflectionUtils.findMethod(beanType, methodName, parameterTypes); - assertThat(method).isNotNull(); - return new InjectedMethodResolver(method, beanType, "test"); - } - - @SuppressWarnings("unused") - static class TestBean { - - public void injectString(String string) { - - } - - public void injectStringAndEnvironment(String string, Environment environment) { - - } - - } - -} diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java index 35086d756f..ba42d98f5a 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java @@ -51,9 +51,6 @@ import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor; import org.springframework.beans.factory.config.SingletonBeanRegistry; -import org.springframework.beans.factory.generator.AotContributingBeanFactoryPostProcessor; -import org.springframework.beans.factory.generator.BeanFactoryContribution; -import org.springframework.beans.factory.generator.BeanFactoryInitialization; import org.springframework.beans.factory.parsing.FailFastProblemReporter; import org.springframework.beans.factory.parsing.PassThroughSourceExtractor; import org.springframework.beans.factory.parsing.ProblemReporter; @@ -81,7 +78,6 @@ import org.springframework.core.type.MethodMetadata; import org.springframework.core.type.classreading.CachingMetadataReaderFactory; import org.springframework.core.type.classreading.MetadataReaderFactory; import org.springframework.javapoet.CodeBlock; -import org.springframework.javapoet.CodeBlock.Builder; import org.springframework.javapoet.MethodSpec; import org.springframework.javapoet.ParameterizedTypeName; import org.springframework.lang.Nullable; @@ -108,8 +104,8 @@ import org.springframework.util.ClassUtils; * @since 3.0 */ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor, - AotContributingBeanFactoryPostProcessor, BeanFactoryInitializationAotProcessor, PriorityOrdered, - ResourceLoaderAware, ApplicationStartupAware, BeanClassLoaderAware, EnvironmentAware { + BeanFactoryInitializationAotProcessor, PriorityOrdered, ResourceLoaderAware, ApplicationStartupAware, + BeanClassLoaderAware, EnvironmentAware { /** * A {@code BeanNameGenerator} using fully qualified class names as default bean names. @@ -289,15 +285,10 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory)); } - @Override - public BeanFactoryContribution contribute(ConfigurableListableBeanFactory beanFactory) { - return (beanFactory.containsBean(IMPORT_REGISTRY_BEAN_NAME) - ? new ImportAwareBeanFactoryConfiguration(beanFactory) : null); - } - @Override public BeanFactoryInitializationAotContribution processAheadOfTime( ConfigurableListableBeanFactory beanFactory) { + return (beanFactory.containsBean(IMPORT_REGISTRY_BEAN_NAME) ? new AotContribution(beanFactory) : null); } @@ -518,56 +509,6 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo } } - private static final class ImportAwareBeanFactoryConfiguration implements BeanFactoryContribution { - - private final ConfigurableListableBeanFactory beanFactory; - - private ImportAwareBeanFactoryConfiguration(ConfigurableListableBeanFactory beanFactory) { - this.beanFactory = beanFactory; - } - - - @Override - public void applyTo(BeanFactoryInitialization initialization) { - Map mappings = buildImportAwareMappings(); - if (!mappings.isEmpty()) { - MethodSpec method = initialization.generatedTypeContext().getMainGeneratedType() - .addMethod(beanPostProcessorMethod(mappings)); - initialization.contribute(code -> code.addStatement("beanFactory.addBeanPostProcessor($N())", method)); - ResourceHints resourceHints = initialization.generatedTypeContext().runtimeHints().resources(); - mappings.forEach((target, importedFrom) -> resourceHints.registerType( - TypeReference.of(importedFrom))); - } - } - - private MethodSpec.Builder beanPostProcessorMethod(Map mappings) { - Builder code = CodeBlock.builder(); - code.addStatement("$T mappings = new $T<>()", ParameterizedTypeName.get( - Map.class, String.class, String.class), HashMap.class); - mappings.forEach((key, value) -> code.addStatement("mappings.put($S, $S)", key, value)); - code.addStatement("return new $T($L)", ImportAwareAotBeanPostProcessor.class, "mappings"); - return MethodSpec.methodBuilder("createImportAwareBeanPostProcessor") - .returns(ImportAwareAotBeanPostProcessor.class) - .addModifiers(Modifier.PRIVATE).addCode(code.build()); - } - - private Map buildImportAwareMappings() { - ImportRegistry ir = this.beanFactory.getBean(IMPORT_REGISTRY_BEAN_NAME, ImportRegistry.class); - Map mappings = new LinkedHashMap<>(); - for (String name : this.beanFactory.getBeanDefinitionNames()) { - Class beanType = this.beanFactory.getType(name); - if (beanType != null && ImportAware.class.isAssignableFrom(beanType)) { - String type = ClassUtils.getUserClass(beanType).getName(); - AnnotationMetadata importingClassMetadata = ir.getImportingClassFor(type); - if (importingClassMetadata != null) { - mappings.put(type, importingClassMetadata.getClassName()); - } - } - } - return mappings; - } - - } private class AotContribution implements BeanFactoryInitializationAotContribution { diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ImportRuntimeHints.java b/spring-context/src/main/java/org/springframework/context/annotation/ImportRuntimeHints.java index 5950c7db5a..6ca327651b 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ImportRuntimeHints.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ImportRuntimeHints.java @@ -26,7 +26,7 @@ import org.springframework.aot.hint.RuntimeHintsRegistrar; /** * Indicates that one or more {@link RuntimeHintsRegistrar} implementations should be processed. - *

Unlike declaring {@link RuntimeHintsRegistrar} as {@code spring.factories}, + *

Unlike declaring {@link RuntimeHintsRegistrar} as {@code spring/aot.factories}, * {@code @ImportRuntimeHints} allows for more flexible use cases where registrations are only * processed if the annotated configuration class or bean method is considered by the * application context. diff --git a/spring-context/src/main/java/org/springframework/context/generator/ApplicationContextAotGenerator.java b/spring-context/src/main/java/org/springframework/context/generator/ApplicationContextAotGenerator.java deleted file mode 100644 index 3a96e0b23a..0000000000 --- a/spring-context/src/main/java/org/springframework/context/generator/ApplicationContextAotGenerator.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.context.generator; - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.Objects; -import java.util.function.BiPredicate; -import java.util.stream.Stream; - -import javax.lang.model.element.Modifier; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.aot.generator.GeneratedType; -import org.springframework.aot.generator.GeneratedTypeContext; -import org.springframework.beans.factory.BeanFactory; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.generator.AotContributingBeanFactoryPostProcessor; -import org.springframework.beans.factory.generator.AotContributingBeanPostProcessor; -import org.springframework.beans.factory.generator.BeanDefinitionsContribution; -import org.springframework.beans.factory.generator.BeanFactoryContribution; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextInitializer; -import org.springframework.context.support.GenericApplicationContext; -import org.springframework.core.OrderComparator; -import org.springframework.core.io.support.SpringFactoriesLoader; -import org.springframework.javapoet.CodeBlock; -import org.springframework.javapoet.MethodSpec; -import org.springframework.javapoet.ParameterizedTypeName; - -/** - * Process an {@link ApplicationContext} and its {@link BeanFactory} to generate - * code that represents the state of the bean factory, as well as the necessary - * hints that can be used at runtime in a constrained environment. - * - * @author Stephane Nicoll - * @since 6.0 - */ -public class ApplicationContextAotGenerator { - - private static final Log logger = LogFactory.getLog(ApplicationContextAotGenerator.class); - - /** - * Refresh the specified {@link GenericApplicationContext} and generate the - * necessary code to restore the state of its {@link BeanFactory}, using the - * specified {@link GeneratedTypeContext}. - * @param applicationContext the application context to handle - * @param generationContext the generation context to use - */ - public void generateApplicationContext(GenericApplicationContext applicationContext, - GeneratedTypeContext generationContext) { - applicationContext.refreshForAotProcessing(); - - DefaultListableBeanFactory beanFactory = applicationContext.getDefaultListableBeanFactory(); - List contributions = resolveBeanFactoryContributions(beanFactory); - - filterBeanFactory(contributions, beanFactory); - ApplicationContextInitialization applicationContextInitialization = new ApplicationContextInitialization(generationContext); - applyContributions(contributions, applicationContextInitialization); - - GeneratedType mainGeneratedType = generationContext.getMainGeneratedType(); - mainGeneratedType.customizeType(type -> type.addSuperinterface(ParameterizedTypeName.get( - ApplicationContextInitializer.class, GenericApplicationContext.class))); - mainGeneratedType.addMethod(initializeMethod(applicationContextInitialization.toCodeBlock())); - } - - private MethodSpec.Builder initializeMethod(CodeBlock methodBody) { - MethodSpec.Builder method = MethodSpec.methodBuilder("initialize").addModifiers(Modifier.PUBLIC) - .addParameter(GenericApplicationContext.class, "context").addAnnotation(Override.class); - method.addCode(methodBody); - return method; - } - - private void filterBeanFactory(List contributions, DefaultListableBeanFactory beanFactory) { - BiPredicate filter = Stream.concat(Stream.of(aotContributingExcludeFilter()), - contributions.stream().map(BeanFactoryContribution::getBeanDefinitionExcludeFilter)) - .filter(Objects::nonNull).reduce((n, d) -> false, BiPredicate::or); - for (String beanName : beanFactory.getBeanDefinitionNames()) { - BeanDefinition bd = beanFactory.getMergedBeanDefinition(beanName); - if (filter.test(beanName, bd)) { - if (logger.isDebugEnabled()) { - logger.debug("Filtering out bean with name" + beanName + ": " + bd); - } - beanFactory.removeBeanDefinition(beanName); - } - } - } - - // TODO: is this right? - private BiPredicate aotContributingExcludeFilter() { - return (beanName, beanDefinition) -> { - Class type = beanDefinition.getResolvableType().toClass(); - return AotContributingBeanFactoryPostProcessor.class.isAssignableFrom(type) || - AotContributingBeanPostProcessor.class.isAssignableFrom(type); - }; - } - - - private void applyContributions(List contributions, - ApplicationContextInitialization initialization) { - for (BeanFactoryContribution contribution : contributions) { - contribution.applyTo(initialization); - } - } - - /** - * Resolve the {@link BeanFactoryContribution} available in the specified - * bean factory. Infrastructure is contributed first, and bean definitions - * registration last. - * @param beanFactory the bean factory to process - * @return the contribution to apply - * @see InfrastructureContribution - * @see BeanDefinitionsContribution - */ - private List resolveBeanFactoryContributions(DefaultListableBeanFactory beanFactory) { - List contributions = new ArrayList<>(); - contributions.add(new InfrastructureContribution()); - List postProcessors = getAotContributingBeanFactoryPostProcessors(beanFactory); - for (AotContributingBeanFactoryPostProcessor postProcessor : postProcessors) { - BeanFactoryContribution contribution = postProcessor.contribute(beanFactory); - if (contribution != null) { - contributions.add(contribution); - } - } - contributions.add(new BeanDefinitionsContribution(beanFactory)); - return contributions; - } - - private static List getAotContributingBeanFactoryPostProcessors(DefaultListableBeanFactory beanFactory) { - String[] postProcessorNames = beanFactory.getBeanNamesForType(AotContributingBeanFactoryPostProcessor.class, true, false); - List postProcessors = new ArrayList<>(); - for (String ppName : postProcessorNames) { - postProcessors.add(beanFactory.getBean(ppName, AotContributingBeanFactoryPostProcessor.class)); - } - postProcessors.addAll(SpringFactoriesLoader.loadFactories(AotContributingBeanFactoryPostProcessor.class, - beanFactory.getBeanClassLoader())); - sortPostProcessors(postProcessors, beanFactory); - return postProcessors; - } - - private static void sortPostProcessors(List postProcessors, ConfigurableListableBeanFactory beanFactory) { - // Nothing to sort? - if (postProcessors.size() <= 1) { - return; - } - Comparator comparatorToUse = null; - if (beanFactory instanceof DefaultListableBeanFactory) { - comparatorToUse = ((DefaultListableBeanFactory) beanFactory).getDependencyComparator(); - } - if (comparatorToUse == null) { - comparatorToUse = OrderComparator.INSTANCE; - } - postProcessors.sort(comparatorToUse); - } - -} diff --git a/spring-context/src/main/java/org/springframework/context/generator/ApplicationContextInitialization.java b/spring-context/src/main/java/org/springframework/context/generator/ApplicationContextInitialization.java deleted file mode 100644 index 9f2704f2c0..0000000000 --- a/spring-context/src/main/java/org/springframework/context/generator/ApplicationContextInitialization.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.context.generator; - -import org.springframework.aot.generator.GeneratedTypeContext; -import org.springframework.beans.factory.generator.BeanFactoryInitialization; -import org.springframework.context.ApplicationContext; - -/** - * The initialization of an {@link ApplicationContext}. - * - * @author Andy Wilkinson - * @since 6.0 - */ -public class ApplicationContextInitialization extends BeanFactoryInitialization { - - public ApplicationContextInitialization(GeneratedTypeContext generatedTypeContext) { - super(generatedTypeContext); - } - -} diff --git a/spring-context/src/main/java/org/springframework/context/generator/InfrastructureContribution.java b/spring-context/src/main/java/org/springframework/context/generator/InfrastructureContribution.java deleted file mode 100644 index 6a0ce89e3a..0000000000 --- a/spring-context/src/main/java/org/springframework/context/generator/InfrastructureContribution.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.context.generator; - -import org.springframework.beans.factory.generator.BeanFactoryContribution; -import org.springframework.beans.factory.generator.BeanFactoryInitialization; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver; - -/** - * A {@link BeanFactoryContribution} that configures the low-level - * infrastructure necessary to process an AOT context. - * - * @author Stephane Nicoll - */ -class InfrastructureContribution implements BeanFactoryContribution { - - @Override - public void applyTo(BeanFactoryInitialization initialization) { - initialization.contribute(code -> { - code.add("// infrastructure\n"); - code.addStatement("$T beanFactory = context.getDefaultListableBeanFactory()", - DefaultListableBeanFactory.class); - code.addStatement("beanFactory.setAutowireCandidateResolver(new $T())", - ContextAnnotationAutowireCandidateResolver.class); - }); - } - -} diff --git a/spring-context/src/main/java/org/springframework/context/generator/RuntimeHintsPostProcessor.java b/spring-context/src/main/java/org/springframework/context/generator/RuntimeHintsPostProcessor.java deleted file mode 100644 index c28dea2b0f..0000000000 --- a/spring-context/src/main/java/org/springframework/context/generator/RuntimeHintsPostProcessor.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.context.generator; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.aot.hint.RuntimeHintsRegistrar; -import org.springframework.beans.BeanUtils; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.generator.AotContributingBeanFactoryPostProcessor; -import org.springframework.beans.factory.generator.BeanFactoryContribution; -import org.springframework.beans.factory.generator.BeanFactoryInitialization; -import org.springframework.context.annotation.ImportRuntimeHints; -import org.springframework.core.io.support.SpringFactoriesLoader; -import org.springframework.core.log.LogMessage; -import org.springframework.lang.Nullable; - -/** - * AOT {@code BeanFactoryPostProcessor} that processes {@link RuntimeHintsRegistrar} implementations - * declared as {@code spring.factories} or using {@link ImportRuntimeHints @ImportRuntimeHints} annotated - * configuration classes or bean methods. - *

This processor is registered by default in the {@link ApplicationContextAotGenerator} as it is - * only useful in an AOT context. - * - * @author Brian Clozel - * @see ApplicationContextAotGenerator - */ -class RuntimeHintsPostProcessor implements AotContributingBeanFactoryPostProcessor { - - private static final Log logger = LogFactory.getLog(RuntimeHintsPostProcessor.class); - - @Override - public BeanFactoryContribution contribute(ConfigurableListableBeanFactory beanFactory) { - ClassLoader beanClassLoader = beanFactory.getBeanClassLoader(); - List registrars = - new ArrayList<>(SpringFactoriesLoader.loadFactories(RuntimeHintsRegistrar.class, beanClassLoader)); - Arrays.stream(beanFactory.getBeanNamesForAnnotation(ImportRuntimeHints.class)).forEach(beanDefinitionName -> { - ImportRuntimeHints importRuntimeHints = beanFactory.findAnnotationOnBean(beanDefinitionName, ImportRuntimeHints.class); - if (importRuntimeHints != null) { - Class[] registrarClasses = importRuntimeHints.value(); - for (Class registrarClass : registrarClasses) { - logger.trace(LogMessage.format("Loaded [%s] registrar from annotated bean [%s]", registrarClass.getCanonicalName(), beanDefinitionName)); - RuntimeHintsRegistrar registrar = BeanUtils.instantiateClass(registrarClass); - registrars.add(registrar); - } - } - }); - return new RuntimeHintsRegistrarContribution(registrars, beanClassLoader); - } - - - static class RuntimeHintsRegistrarContribution implements BeanFactoryContribution { - - private final List registrars; - - @Nullable - private final ClassLoader beanClassLoader; - - RuntimeHintsRegistrarContribution(List registrars, @Nullable ClassLoader beanClassLoader) { - this.registrars = registrars; - this.beanClassLoader = beanClassLoader; - } - - @Override - public void applyTo(BeanFactoryInitialization initialization) { - this.registrars.forEach(registrar -> { - logger.trace(LogMessage.format("Processing RuntimeHints contribution from [%s]", registrar.getClass().getCanonicalName())); - registrar.registerHints(initialization.generatedTypeContext().runtimeHints(), this.beanClassLoader); - }); - } - } - -} diff --git a/spring-context/src/main/java/org/springframework/context/generator/package-info.java b/spring-context/src/main/java/org/springframework/context/generator/package-info.java deleted file mode 100644 index af0aaf7294..0000000000 --- a/spring-context/src/main/java/org/springframework/context/generator/package-info.java +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Support for generating code that represents the state of an application - * context. - */ -@NonNullApi -@NonNullFields -package org.springframework.context.generator; - -import org.springframework.lang.NonNullApi; -import org.springframework.lang.NonNullFields; diff --git a/spring-context/src/test/java/org/springframework/context/annotation/ImportAwareBeanFactoryContributionTests.java b/spring-context/src/test/java/org/springframework/context/annotation/ImportAwareBeanFactoryContributionTests.java deleted file mode 100644 index 2e8d39e1e7..0000000000 --- a/spring-context/src/test/java/org/springframework/context/annotation/ImportAwareBeanFactoryContributionTests.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.context.annotation; - -import java.io.IOException; -import java.io.StringWriter; - -import org.junit.jupiter.api.Test; - -import org.springframework.aot.generator.DefaultGeneratedTypeContext; -import org.springframework.aot.generator.GeneratedType; -import org.springframework.aot.generator.GeneratedTypeContext; -import org.springframework.beans.factory.generator.BeanFactoryContribution; -import org.springframework.beans.factory.generator.BeanFactoryInitialization; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.beans.testfixture.beans.factory.generator.SimpleConfiguration; -import org.springframework.context.testfixture.context.generator.annotation.ImportConfiguration; -import org.springframework.javapoet.ClassName; -import org.springframework.javapoet.support.CodeSnippet; -import org.springframework.lang.Nullable; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@code ImportAwareBeanFactoryConfiguration}. - * - * @author Stephane Nicoll - */ -public class ImportAwareBeanFactoryContributionTests { - - @Test - void contributeWithImportAwareConfigurationRegistersBeanPostProcessor() { - BeanFactoryContribution contribution = createContribution(ImportConfiguration.class); - assertThat(contribution).isNotNull(); - BeanFactoryInitialization initialization = new BeanFactoryInitialization(createGenerationContext()); - contribution.applyTo(initialization); - assertThat(CodeSnippet.of(initialization.toCodeBlock()).getSnippet()).isEqualTo(""" - beanFactory.addBeanPostProcessor(createImportAwareBeanPostProcessor()); - """); - } - - @Test - void contributeWithImportAwareConfigurationCreateMappingsMethod() { - BeanFactoryContribution contribution = createContribution(ImportConfiguration.class); - assertThat(contribution).isNotNull(); - GeneratedTypeContext generationContext = createGenerationContext(); - contribution.applyTo(new BeanFactoryInitialization(generationContext)); - assertThat(codeOf(generationContext.getMainGeneratedType())).contains(""" - private ImportAwareAotBeanPostProcessor createImportAwareBeanPostProcessor() { - Map mappings = new HashMap<>(); - mappings.put("org.springframework.context.testfixture.context.generator.annotation.ImportAwareConfiguration", "org.springframework.context.testfixture.context.generator.annotation.ImportConfiguration"); - return new ImportAwareAotBeanPostProcessor(mappings); - } - """); - - } - - @Test - void contributeWithImportAwareConfigurationRegisterBytecodeResourceHint() { - BeanFactoryContribution contribution = createContribution(ImportConfiguration.class); - assertThat(contribution).isNotNull(); - GeneratedTypeContext generationContext = createGenerationContext(); - contribution.applyTo(new BeanFactoryInitialization(generationContext)); - assertThat(generationContext.runtimeHints().resources().resourcePatterns()) - .singleElement().satisfies(resourceHint -> assertThat(resourceHint.getIncludes()).containsOnly( - "org/springframework/context/testfixture/context/generator/annotation/ImportConfiguration.class")); - } - - @Test - void contributeWithNoImportAwareConfigurationReturnsNull() { - assertThat(createContribution(SimpleConfiguration.class)).isNull(); - } - - - @Nullable - private BeanFactoryContribution createContribution(Class type) { - DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - beanFactory.registerBeanDefinition("configuration", new RootBeanDefinition(type)); - ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor(); - pp.postProcessBeanFactory(beanFactory); - return pp.contribute(beanFactory); - } - - private GeneratedTypeContext createGenerationContext() { - return new DefaultGeneratedTypeContext("com.example", packageName -> - GeneratedType.of(ClassName.get(packageName, "Test"))); - } - - private String codeOf(GeneratedType type) { - try { - StringWriter out = new StringWriter(); - type.toJavaFile().writeTo(out); - return out.toString(); - } - catch (IOException ex) { - throw new IllegalStateException(ex); - } - } - -} diff --git a/spring-context/src/test/java/org/springframework/context/generator/ApplicationContextAotGeneratorTests.java b/spring-context/src/test/java/org/springframework/context/generator/ApplicationContextAotGeneratorTests.java deleted file mode 100644 index b87804d737..0000000000 --- a/spring-context/src/test/java/org/springframework/context/generator/ApplicationContextAotGeneratorTests.java +++ /dev/null @@ -1,410 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.context.generator; - -import java.io.IOException; -import java.io.StringWriter; -import java.net.URL; -import java.util.Enumeration; -import java.util.function.BiPredicate; -import java.util.function.Consumer; -import java.util.function.Supplier; - -import org.junit.jupiter.api.Test; - -import org.springframework.aot.generator.DefaultGeneratedTypeContext; -import org.springframework.aot.generator.GeneratedType; -import org.springframework.aot.generator.GeneratedTypeContext; -import org.springframework.aot.test.generator.compile.TestCompiler; -import org.springframework.aot.test.generator.file.SourceFile; -import org.springframework.aot.test.generator.file.SourceFiles; -import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.generator.AotContributingBeanFactoryPostProcessor; -import org.springframework.beans.factory.generator.AotContributingBeanPostProcessor; -import org.springframework.beans.factory.generator.BeanFactoryContribution; -import org.springframework.beans.factory.generator.BeanFactoryInitialization; -import org.springframework.beans.factory.generator.BeanInstantiationContribution; -import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.context.ApplicationContextInitializer; -import org.springframework.context.annotation.AnnotationConfigUtils; -import org.springframework.context.annotation.CommonAnnotationBeanPostProcessor; -import org.springframework.context.support.GenericApplicationContext; -import org.springframework.context.testfixture.context.generator.SimpleComponent; -import org.springframework.context.testfixture.context.generator.annotation.AutowiredComponent; -import org.springframework.context.testfixture.context.generator.annotation.InitDestroyComponent; -import org.springframework.javapoet.ClassName; -import org.springframework.javapoet.CodeBlock; -import org.springframework.javapoet.JavaFile; -import org.springframework.lang.Nullable; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -/** - * Tests for {@link ApplicationContextAotGenerator}. - * - * @author Stephane Nicoll - */ -class ApplicationContextAotGeneratorTests { - - private static final ClassName MAIN_GENERATED_TYPE = ClassName.get("com.example", "Test"); - - @Test - void generateApplicationContextWithSimpleBean() { - GenericApplicationContext context = new GenericApplicationContext(); - context.registerBeanDefinition("test", new RootBeanDefinition(SimpleComponent.class)); - compile(context, toFreshApplicationContext(GenericApplicationContext::new, aotContext -> { - assertThat(aotContext.getBeanDefinitionNames()).containsOnly("test"); - assertThat(aotContext.getBean("test")).isInstanceOf(SimpleComponent.class); - })); - } - - @Test - void generateApplicationContextWithAutowiring() { - GenericApplicationContext context = new GenericApplicationContext(); - context.registerBeanDefinition(AnnotationConfigUtils.AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME, - BeanDefinitionBuilder.rootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class) - .setRole(BeanDefinition.ROLE_INFRASTRUCTURE).getBeanDefinition()); - context.registerBeanDefinition("autowiredComponent", new RootBeanDefinition(AutowiredComponent.class)); - context.registerBeanDefinition("number", BeanDefinitionBuilder.rootBeanDefinition(Integer.class, "valueOf") - .addConstructorArgValue("42").getBeanDefinition()); - compile(context, toFreshApplicationContext(GenericApplicationContext::new, aotContext -> { - assertThat(aotContext.getBeanDefinitionNames()).containsOnly("autowiredComponent", "number"); - AutowiredComponent bean = aotContext.getBean(AutowiredComponent.class); - assertThat(bean.getEnvironment()).isSameAs(aotContext.getEnvironment()); - assertThat(bean.getCounter()).isEqualTo(42); - })); - } - - @Test - void generateApplicationContextWithInitDestroyMethods() { - GenericApplicationContext context = new GenericApplicationContext(); - context.registerBeanDefinition(AnnotationConfigUtils.COMMON_ANNOTATION_PROCESSOR_BEAN_NAME, - BeanDefinitionBuilder.rootBeanDefinition(CommonAnnotationBeanPostProcessor.class) - .setRole(BeanDefinition.ROLE_INFRASTRUCTURE).getBeanDefinition()); - context.registerBeanDefinition("initDestroyComponent", new RootBeanDefinition(InitDestroyComponent.class)); - compile(context, toFreshApplicationContext(GenericApplicationContext::new, aotContext -> { - assertThat(aotContext.getBeanDefinitionNames()).containsOnly("initDestroyComponent"); - InitDestroyComponent bean = aotContext.getBean(InitDestroyComponent.class); - assertThat(bean.events).containsExactly("init"); - aotContext.close(); - assertThat(bean.events).containsExactly("init", "destroy"); - })); - } - - @Test - void generateApplicationContextWithMultipleInitDestroyMethods() { - GenericApplicationContext context = new GenericApplicationContext(); - context.registerBeanDefinition(AnnotationConfigUtils.COMMON_ANNOTATION_PROCESSOR_BEAN_NAME, - BeanDefinitionBuilder.rootBeanDefinition(CommonAnnotationBeanPostProcessor.class) - .setRole(BeanDefinition.ROLE_INFRASTRUCTURE).getBeanDefinition()); - RootBeanDefinition beanDefinition = new RootBeanDefinition(InitDestroyComponent.class); - beanDefinition.setInitMethodName("customInit"); - beanDefinition.setDestroyMethodName("customDestroy"); - context.registerBeanDefinition("initDestroyComponent", beanDefinition); - compile(context, toFreshApplicationContext(GenericApplicationContext::new, aotContext -> { - assertThat(aotContext.getBeanDefinitionNames()).containsOnly("initDestroyComponent"); - InitDestroyComponent bean = aotContext.getBean(InitDestroyComponent.class); - assertThat(bean.events).containsExactly("customInit", "init"); - aotContext.close(); - assertThat(bean.events).containsExactly("customInit", "init", "customDestroy", "destroy"); - })); - } - - @Test - void generateApplicationContextWitNoContributors() { - GeneratedTypeContext generationContext = createGenerationContext(); - ApplicationContextAotGenerator generator = new ApplicationContextAotGenerator(); - generator.generateApplicationContext(new GenericApplicationContext(), generationContext); - assertThat(write(generationContext.getMainGeneratedType())).contains(""" - public class Test implements ApplicationContextInitializer { - @Override - public void initialize(GenericApplicationContext context) { - // infrastructure - DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory(); - beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); - } - } - """); - } - - @Test - void generateApplicationContextLoadsBeanFactoryContributors() { - GeneratedTypeContext generationContext = createGenerationContext(); - ApplicationContextAotGenerator generator = new ApplicationContextAotGenerator(); - GenericApplicationContext applicationContext = new GenericApplicationContext(); - applicationContext.setClassLoader( - new TestSpringFactoriesClassLoader("bean-factory-contributors.factories")); - generator.generateApplicationContext(applicationContext, generationContext); - assertThat(write(generationContext.getMainGeneratedType())).contains(""" - public class Test implements ApplicationContextInitializer { - @Override - public void initialize(GenericApplicationContext context) { - // infrastructure - DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory(); - beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); - // Test - } - } - """); - } - - @Test - void generateApplicationContextApplyContributionAsIsWithNewLineAtTheEnd() { - GenericApplicationContext applicationContext = new GenericApplicationContext(); - registerAotContributingBeanDefinition(applicationContext, "bpp", code -> code.add("// Hello")); - GeneratedTypeContext generationContext = createGenerationContext(); - ApplicationContextAotGenerator generator = new ApplicationContextAotGenerator(); - generator.generateApplicationContext(applicationContext, generationContext); - assertThat(write(generationContext.getMainGeneratedType())).contains(""" - public class Test implements ApplicationContextInitializer { - @Override - public void initialize(GenericApplicationContext context) { - // infrastructure - DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory(); - beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); - // Hello - } - } - """); - } - - @Test - void generateApplicationContextApplyMultipleContributionAsIsWithNewLineAtTheEnd() { - GeneratedTypeContext generationContext = createGenerationContext(); - GenericApplicationContext applicationContext = new GenericApplicationContext(); - registerAotContributingBeanDefinition(applicationContext, "bpp", code -> code.add("// Hello")); - registerAotContributingBeanDefinition(applicationContext, "bpp2", code -> code.add("// World")); - ApplicationContextAotGenerator generator = new ApplicationContextAotGenerator(); - generator.generateApplicationContext(applicationContext, generationContext); - assertThat(write(generationContext.getMainGeneratedType())).contains(""" - public class Test implements ApplicationContextInitializer { - @Override - public void initialize(GenericApplicationContext context) { - // infrastructure - DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory(); - beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); - // Hello - // World - } - } - """); - } - - @Test - void generateApplicationContextExcludeAotContributingBeanFactoryPostProcessorByDefault() { - GenericApplicationContext context = new GenericApplicationContext(); - context.registerBeanDefinition("test", new RootBeanDefinition(NoOpAotContributingBeanFactoryPostProcessor.class)); - compile(context, toFreshApplicationContext(GenericApplicationContext::new, aotContext -> - assertThat(aotContext.getBeanDefinitionNames()).isEmpty())); - } - - @Test - void generateApplicationContextExcludeAotContributingBeanPostProcessorByDefault() { - GenericApplicationContext context = new GenericApplicationContext(); - context.registerBeanDefinition("test", new RootBeanDefinition(NoOpAotContributingBeanPostProcessor.class)); - compile(context, toFreshApplicationContext(GenericApplicationContext::new, aotContext -> - assertThat(aotContext.getBeanDefinitionNames()).isEmpty())); - } - - @Test - void generateApplicationContextInvokeExcludePredicateInOrder() { - GeneratedTypeContext generationContext = createGenerationContext(); - GenericApplicationContext applicationContext = new GenericApplicationContext(); - DefaultListableBeanFactory beanFactory = applicationContext.getDefaultListableBeanFactory(); - BiPredicate excludeFilter = mockExcludeFilter(); - given(excludeFilter.test(eq("bean1"), any(BeanDefinition.class))).willReturn(Boolean.FALSE); - given(excludeFilter.test(eq("bean2"), any(BeanDefinition.class))).willReturn(Boolean.TRUE); - applicationContext.registerBeanDefinition("bean2", new RootBeanDefinition(SimpleComponent.class)); - applicationContext.registerBeanDefinition("bean1", new RootBeanDefinition(SimpleComponent.class)); - registerAotContributingBeanDefinition(applicationContext, "bpp", code -> {}, excludeFilter); - ApplicationContextAotGenerator generator = new ApplicationContextAotGenerator(); - generator.generateApplicationContext(applicationContext, generationContext); - assertThat(write(generationContext.getMainGeneratedType())) - .doesNotContain("bean2").doesNotContain("bpp") - .contains("BeanDefinitionRegistrar.of(\"bean1\", SimpleComponent.class)"); - verify(excludeFilter).test(eq("bean2"), any(BeanDefinition.class)); - verify(excludeFilter).test("bean1", beanFactory.getMergedBeanDefinition("bean1")); - } - - - @SuppressWarnings("unchecked") - private BiPredicate mockExcludeFilter() { - return mock(BiPredicate.class); - } - - @SuppressWarnings("rawtypes") - private void compile(GenericApplicationContext applicationContext, Consumer initializer) { - DefaultGeneratedTypeContext generationContext = createGenerationContext(); - ApplicationContextAotGenerator generator = new ApplicationContextAotGenerator(); - generator.generateApplicationContext(applicationContext, generationContext); - SourceFiles sourceFiles = SourceFiles.none(); - for (JavaFile javaFile : generationContext.toJavaFiles()) { - sourceFiles = sourceFiles.and(SourceFile.of((javaFile::writeTo))); - } - TestCompiler.forSystem().withSources(sourceFiles).compile(compiled -> { - ApplicationContextInitializer instance = compiled.getInstance(ApplicationContextInitializer.class, MAIN_GENERATED_TYPE.canonicalName()); - initializer.accept(instance); - }); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - private Consumer toFreshApplicationContext( - Supplier applicationContextFactory, Consumer context) { - return applicationContextInitializer -> { - T applicationContext = applicationContextFactory.get(); - applicationContextInitializer.initialize(applicationContext); - applicationContext.refresh(); - context.accept(applicationContext); - }; - } - - private DefaultGeneratedTypeContext createGenerationContext() { - return new DefaultGeneratedTypeContext(MAIN_GENERATED_TYPE.packageName(), packageName -> - GeneratedType.of(ClassName.get(packageName, MAIN_GENERATED_TYPE.simpleName()))); - } - - private String write(GeneratedType generatedType) { - try { - StringWriter out = new StringWriter(); - generatedType.toJavaFile().writeTo(out); - return out.toString(); - } - catch (IOException ex) { - throw new IllegalStateException("Failed to write " + generatedType, ex); - } - } - - private void registerAotContributingBeanDefinition(GenericApplicationContext context, String name, - Consumer code) { - registerAotContributingBeanDefinition(context, name, code, - (beanName, beanDefinition) -> name.equals(beanName)); - } - - private void registerAotContributingBeanDefinition(GenericApplicationContext context, String name, - Consumer code, BiPredicate excludeFilter) { - BeanFactoryContribution contribution = new TestBeanFactoryContribution( - initialization -> initialization.contribute(code), excludeFilter); - context.registerBeanDefinition(name, BeanDefinitionBuilder.rootBeanDefinition( - TestAotContributingBeanFactoryPostProcessor.class, () -> - new TestAotContributingBeanFactoryPostProcessor(contribution)).getBeanDefinition()); - } - - - static class TestAotContributingBeanFactoryPostProcessor implements AotContributingBeanFactoryPostProcessor { - - @Nullable - private final BeanFactoryContribution beanFactoryContribution; - - TestAotContributingBeanFactoryPostProcessor(@Nullable BeanFactoryContribution beanFactoryContribution) { - this.beanFactoryContribution = beanFactoryContribution; - } - - TestAotContributingBeanFactoryPostProcessor() { - this(null); - } - - @Override - public BeanFactoryContribution contribute(ConfigurableListableBeanFactory beanFactory) { - return this.beanFactoryContribution; - } - - } - - static class TextAotContributingBeanFactoryPostProcessor implements AotContributingBeanFactoryPostProcessor { - - @Override - public BeanFactoryContribution contribute(ConfigurableListableBeanFactory beanFactory) { - return initialization -> initialization.contribute(code -> code.add("// Test\n")); - } - - } - - static class NoOpAotContributingBeanFactoryPostProcessor implements AotContributingBeanFactoryPostProcessor { - - @Override - public BeanFactoryContribution contribute(ConfigurableListableBeanFactory beanFactory) { - return null; - } - } - - static class NoOpAotContributingBeanPostProcessor implements AotContributingBeanPostProcessor { - - @Override - public BeanInstantiationContribution contribute(RootBeanDefinition beanDefinition, Class beanType, String beanName) { - return null; - } - - @Override - public int getOrder() { - return 0; - } - - } - - static class TestBeanFactoryContribution implements BeanFactoryContribution { - - private final Consumer contribution; - - private final BiPredicate excludeFilter; - - public TestBeanFactoryContribution(Consumer contribution, - BiPredicate excludeFilter) { - this.contribution = contribution; - this.excludeFilter = excludeFilter; - } - - @Override - public void applyTo(BeanFactoryInitialization initialization) { - this.contribution.accept(initialization); - } - - @Override - public BiPredicate getBeanDefinitionExcludeFilter() { - return this.excludeFilter; - } - - } - - static class TestSpringFactoriesClassLoader extends ClassLoader { - - private final String factoriesName; - - TestSpringFactoriesClassLoader(String factoriesName) { - super(RuntimeHintsPostProcessorTests.class.getClassLoader()); - this.factoriesName = factoriesName; - } - - @Override - public Enumeration getResources(String name) throws IOException { - if ("META-INF/spring.factories".equals(name)) { - return super.getResources("org/springframework/context/generator/aot/" + this.factoriesName); - } - return super.getResources(name); - } - - } - -} diff --git a/spring-context/src/test/java/org/springframework/context/generator/InfrastructureContributionTests.java b/spring-context/src/test/java/org/springframework/context/generator/InfrastructureContributionTests.java deleted file mode 100644 index 0616826eb1..0000000000 --- a/spring-context/src/test/java/org/springframework/context/generator/InfrastructureContributionTests.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.context.generator; - -import org.junit.jupiter.api.Test; - -import org.springframework.aot.generator.DefaultGeneratedTypeContext; -import org.springframework.aot.generator.GeneratedType; -import org.springframework.aot.generator.GeneratedTypeContext; -import org.springframework.beans.factory.generator.BeanFactoryInitialization; -import org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver; -import org.springframework.javapoet.ClassName; -import org.springframework.javapoet.support.CodeSnippet; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link InfrastructureContribution}. - * - * @author Stephane Nicoll - */ -class InfrastructureContributionTests { - - @Test - void contributeInfrastructure() { - CodeSnippet codeSnippet = contribute(createGenerationContext()); - assertThat(codeSnippet.getSnippet()).isEqualTo(""" - // infrastructure - DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory(); - beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); - """); - assertThat(codeSnippet.hasImport(ContextAnnotationAutowireCandidateResolver.class)).isTrue(); - } - - private CodeSnippet contribute(GeneratedTypeContext generationContext) { - InfrastructureContribution contribution = new InfrastructureContribution(); - BeanFactoryInitialization initialization = new BeanFactoryInitialization(generationContext); - contribution.applyTo(initialization); - return CodeSnippet.of(initialization.toCodeBlock()); - } - - private GeneratedTypeContext createGenerationContext() { - return new DefaultGeneratedTypeContext("com.example", packageName -> - GeneratedType.of(ClassName.get(packageName, "Test"))); - } - -} diff --git a/spring-context/src/test/java/org/springframework/context/generator/RuntimeHintsPostProcessorTests.java b/spring-context/src/test/java/org/springframework/context/generator/RuntimeHintsPostProcessorTests.java deleted file mode 100644 index 43a6eb0569..0000000000 --- a/spring-context/src/test/java/org/springframework/context/generator/RuntimeHintsPostProcessorTests.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.context.generator; - -import java.io.IOException; -import java.net.URL; -import java.util.Enumeration; -import java.util.stream.Stream; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import org.springframework.aot.generator.DefaultGeneratedTypeContext; -import org.springframework.aot.generator.GeneratedType; -import org.springframework.aot.hint.ResourceBundleHint; -import org.springframework.aot.hint.RuntimeHints; -import org.springframework.aot.hint.RuntimeHintsRegistrar; -import org.springframework.beans.BeanInstantiationException; -import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.context.annotation.AnnotationConfigUtils; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.ImportRuntimeHints; -import org.springframework.context.support.GenericApplicationContext; -import org.springframework.javapoet.ClassName; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -/** - * Tests for {@link RuntimeHintsPostProcessor}. - * - * @author Brian Clozel - */ -class RuntimeHintsPostProcessorTests { - - private DefaultGeneratedTypeContext generationContext; - - private ApplicationContextAotGenerator generator; - - @BeforeEach - void setup() { - this.generationContext = createGenerationContext(); - this.generator = new ApplicationContextAotGenerator(); - } - - @Test - void shouldProcessRegistrarOnConfiguration() { - GenericApplicationContext applicationContext = createContext(ConfigurationWithHints.class); - this.generator.generateApplicationContext(applicationContext, this.generationContext); - assertThatSampleRegistrarContributed(); - } - - @Test - void shouldProcessRegistrarOnBeanMethod() { - GenericApplicationContext applicationContext = createContext(ConfigurationWithBeanDeclaringHints.class); - this.generator.generateApplicationContext(applicationContext, this.generationContext); - assertThatSampleRegistrarContributed(); - } - - @Test - void shouldProcessRegistrarInSpringFactory() { - GenericApplicationContext applicationContext = createContext(); - applicationContext.setClassLoader(new TestSpringFactoriesClassLoader("test.factories")); - this.generator.generateApplicationContext(applicationContext, this.generationContext); - assertThatSampleRegistrarContributed(); - } - - @Test - void shouldRejectRuntimeHintsRegistrarWithoutDefaultConstructor() { - GenericApplicationContext applicationContext = createContext(ConfigurationWithIllegalRegistrar.class); - assertThatThrownBy(() -> this.generator.generateApplicationContext(applicationContext, this.generationContext)) - .isInstanceOf(BeanInstantiationException.class); - } - - private void assertThatSampleRegistrarContributed() { - Stream bundleHints = this.generationContext.runtimeHints().resources().resourceBundles(); - assertThat(bundleHints).anyMatch(bundleHint -> "sample".equals(bundleHint.getBaseName())); - } - - private GenericApplicationContext createContext(Class... configClasses) { - GenericApplicationContext applicationContext = new GenericApplicationContext(); - AnnotationConfigUtils.registerAnnotationConfigProcessors(applicationContext); - for (Class configClass : configClasses) { - applicationContext.registerBeanDefinition(configClass.getSimpleName(), new RootBeanDefinition(configClass)); - } - applicationContext.registerBeanDefinition("runtimeHintsPostProcessor", - BeanDefinitionBuilder.rootBeanDefinition(RuntimeHintsPostProcessor.class, RuntimeHintsPostProcessor::new).getBeanDefinition()); - return applicationContext; - } - - private DefaultGeneratedTypeContext createGenerationContext() { - ClassName mainGeneratedType = ClassName.get("com.example", "Test"); - return new DefaultGeneratedTypeContext(mainGeneratedType.packageName(), packageName -> - GeneratedType.of(ClassName.get(packageName, mainGeneratedType.simpleName()))); - } - - - @ImportRuntimeHints(SampleRuntimeHintsRegistrar.class) - @Configuration(proxyBeanMethods = false) - static class ConfigurationWithHints { - - } - - @Configuration(proxyBeanMethods = false) - static class ConfigurationWithBeanDeclaringHints { - - @Bean - @ImportRuntimeHints(SampleRuntimeHintsRegistrar.class) - SampleBean sampleBean() { - return new SampleBean(); - } - - } - - public static class SampleRuntimeHintsRegistrar implements RuntimeHintsRegistrar { - - @Override - public void registerHints(RuntimeHints hints, ClassLoader classLoader) { - hints.resources().registerResourceBundle("sample"); - } - - } - - static class SampleBean { - - } - - @ImportRuntimeHints(IllegalRuntimeHintsRegistrar.class) - @Configuration(proxyBeanMethods = false) - static class ConfigurationWithIllegalRegistrar { - - } - - public static class IllegalRuntimeHintsRegistrar implements RuntimeHintsRegistrar { - - public IllegalRuntimeHintsRegistrar(String arg) { - - } - - @Override - public void registerHints(RuntimeHints hints, ClassLoader classLoader) { - hints.resources().registerResourceBundle("sample"); - } - - } - - - static class TestSpringFactoriesClassLoader extends ClassLoader { - - private final String factoriesName; - - TestSpringFactoriesClassLoader(String factoriesName) { - super(RuntimeHintsPostProcessorTests.class.getClassLoader()); - this.factoriesName = factoriesName; - } - - @Override - public Enumeration getResources(String name) throws IOException { - if ("META-INF/spring.factories".equals(name)) { - return super.getResources("org/springframework/context/generator/runtimehints/" + this.factoriesName); - } - return super.getResources(name); - } - - } - -} diff --git a/spring-context/src/test/resources/org/springframework/context/generator/aot/bean-factory-contributors.factories b/spring-context/src/test/resources/org/springframework/context/generator/aot/bean-factory-contributors.factories deleted file mode 100644 index 36475ad5ba..0000000000 --- a/spring-context/src/test/resources/org/springframework/context/generator/aot/bean-factory-contributors.factories +++ /dev/null @@ -1,3 +0,0 @@ -org.springframework.beans.factory.generator.AotContributingBeanFactoryPostProcessor= \ -org.springframework.context.generator.ApplicationContextAotGeneratorTests.NoOpAotContributingBeanFactoryPostProcessor, \ -org.springframework.context.generator.ApplicationContextAotGeneratorTests.TextAotContributingBeanFactoryPostProcessor \ No newline at end of file diff --git a/spring-context/src/test/resources/org/springframework/context/generator/runtimehints/test.factories b/spring-context/src/test/resources/org/springframework/context/generator/runtimehints/test.factories deleted file mode 100644 index b39e551213..0000000000 --- a/spring-context/src/test/resources/org/springframework/context/generator/runtimehints/test.factories +++ /dev/null @@ -1,2 +0,0 @@ -org.springframework.aot.hint.RuntimeHintsRegistrar= \ -org.springframework.context.generator.RuntimeHintsPostProcessorTests.SampleRuntimeHintsRegistrar \ No newline at end of file diff --git a/spring-core/src/main/java/org/springframework/aot/generator/CodeContribution.java b/spring-core/src/main/java/org/springframework/aot/generator/CodeContribution.java deleted file mode 100644 index 964c7090a4..0000000000 --- a/spring-core/src/main/java/org/springframework/aot/generator/CodeContribution.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.aot.generator; - -import org.springframework.aot.hint.RuntimeHints; -import org.springframework.javapoet.support.MultiStatement; - -/** - * A code contribution that gathers the code, the {@linkplain RuntimeHints - * runtime hints}, and the {@linkplain ProtectedElement protected elements} - * that are necessary to execute it. - * - * @author Stephane Nicoll - * @since 6.0 - */ -public interface CodeContribution { - - /** - * Return the {@linkplain MultiStatement statements} that can be used to - * append code. - * @return the statements instance to use to contribute code - */ - MultiStatement statements(); - - /** - * Return the {@linkplain RuntimeHints hints} to use to register - * potential optimizations for contributed code. - * @return the runtime hints - */ - RuntimeHints runtimeHints(); - - /** - * Return the {@linkplain ProtectedAccess protected access} to use to - * analyze any privileged access, if necessary. - * @return the protected access - */ - ProtectedAccess protectedAccess(); - -} diff --git a/spring-core/src/main/java/org/springframework/aot/generator/DefaultCodeContribution.java b/spring-core/src/main/java/org/springframework/aot/generator/DefaultCodeContribution.java deleted file mode 100644 index 3fd34922fe..0000000000 --- a/spring-core/src/main/java/org/springframework/aot/generator/DefaultCodeContribution.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.aot.generator; - -import org.springframework.aot.hint.RuntimeHints; -import org.springframework.javapoet.support.MultiStatement; - -/** - * A default {@link CodeContribution} implementation. - * - * @author Stephane Nicoll - * @since 6.0 - */ -public class DefaultCodeContribution implements CodeContribution { - - private final MultiStatement statements; - - private final RuntimeHints runtimeHints; - - private final ProtectedAccess protectedAccess; - - - protected DefaultCodeContribution(MultiStatement statements, RuntimeHints runtimeHints, - ProtectedAccess protectedAccess) { - - this.statements = statements; - this.runtimeHints = runtimeHints; - this.protectedAccess = protectedAccess; - } - - /** - * Create an instance with the {@link RuntimeHints} instance to use. - * @param runtimeHints the runtime hints instance to use - */ - public DefaultCodeContribution(RuntimeHints runtimeHints) { - this(new MultiStatement(), runtimeHints, new ProtectedAccess()); - } - - @Override - public MultiStatement statements() { - return this.statements; - } - - @Override - public RuntimeHints runtimeHints() { - return this.runtimeHints; - } - - @Override - public ProtectedAccess protectedAccess() { - return this.protectedAccess; - } - -} diff --git a/spring-core/src/main/java/org/springframework/aot/generator/DefaultGeneratedTypeContext.java b/spring-core/src/main/java/org/springframework/aot/generator/DefaultGeneratedTypeContext.java deleted file mode 100644 index 88c4a3e4d4..0000000000 --- a/spring-core/src/main/java/org/springframework/aot/generator/DefaultGeneratedTypeContext.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.aot.generator; - -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; - -import org.springframework.aot.hint.RuntimeHints; -import org.springframework.javapoet.JavaFile; - -/** - * Default {@link GeneratedTypeContext} implementation. - * - * @author Stephane Nicoll - * @since 6.0 - */ -public class DefaultGeneratedTypeContext implements GeneratedTypeContext { - - private final String packageName; - - private final RuntimeHints runtimeHints; - - private final Function generatedTypeFactory; - - private final Map generatedTypes; - - /** - * Create a context targeting the specified package name and using the specified - * factory to create a {@link GeneratedType} per requested package name. - * @param packageName the main package name - * @param generatedTypeFactory the factory to use to create a {@link GeneratedType} - * based on a package name. - */ - public DefaultGeneratedTypeContext(String packageName, Function generatedTypeFactory) { - this.packageName = packageName; - this.runtimeHints = new RuntimeHints(); - this.generatedTypeFactory = generatedTypeFactory; - this.generatedTypes = new LinkedHashMap<>(); - } - - @Override - public RuntimeHints runtimeHints() { - return this.runtimeHints; - } - - @Override - public GeneratedType getGeneratedType(String packageName) { - return this.generatedTypes.computeIfAbsent(packageName, this.generatedTypeFactory); - } - - @Override - public GeneratedType getMainGeneratedType() { - return getGeneratedType(this.packageName); - } - - /** - * Specify if a {@link GeneratedType} for the specified package name is registered. - * @param packageName the package name to use - * @return {@code true} if a type is registered for that package - */ - public boolean hasGeneratedType(String packageName) { - return this.generatedTypes.containsKey(packageName); - } - - /** - * Return the list of {@link JavaFile} of known generated type. - * @return the java files of bootstrap classes in this instance - */ - public List toJavaFiles() { - return this.generatedTypes.values().stream() - .map(GeneratedType::toJavaFile) - .collect(Collectors.toList()); - } - -} diff --git a/spring-core/src/main/java/org/springframework/aot/generator/GeneratedType.java b/spring-core/src/main/java/org/springframework/aot/generator/GeneratedType.java deleted file mode 100644 index d50edf1b0f..0000000000 --- a/spring-core/src/main/java/org/springframework/aot/generator/GeneratedType.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.aot.generator; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.Consumer; -import java.util.function.Predicate; - -import javax.lang.model.element.Modifier; - -import org.springframework.javapoet.ClassName; -import org.springframework.javapoet.JavaFile; -import org.springframework.javapoet.MethodSpec; -import org.springframework.javapoet.TypeSpec; - -/** - * Wrapper for a generated {@linkplain TypeSpec type}. - * - * @author Stephane Nicoll - * @since 6.0 - */ -public class GeneratedType { - - private final ClassName className; - - private final TypeSpec.Builder type; - - private final List methods; - - GeneratedType(ClassName className, Consumer type) { - this.className = className; - this.type = TypeSpec.classBuilder(className); - type.accept(this.type); - this.methods = new ArrayList<>(); - } - - /** - * Create an instance for the specified {@link ClassName}, customizing the type with - * the specified {@link Consumer consumer callback}. - * @param className the class name - * @param type a callback to customize the type, i.e. to change default modifiers - * @return a new {@link GeneratedType} - */ - public static GeneratedType of(ClassName className, Consumer type) { - return new GeneratedType(className, type); - } - - /** - * Create an instance for the specified {@link ClassName}, as a {@code public} type. - * @param className the class name - * @return a new {@link GeneratedType} - */ - public static GeneratedType of(ClassName className) { - return of(className, type -> type.addModifiers(Modifier.PUBLIC)); - } - - /** - * Return the {@link ClassName} of this instance. - * @return the class name - */ - public ClassName getClassName() { - return this.className; - } - - /** - * Customize the type of this instance. - * @param type the consumer of the type builder - * @return this for method chaining - */ - public GeneratedType customizeType(Consumer type) { - type.accept(this.type); - return this; - } - - /** - * Add a method using the state of the specified {@link MethodSpec.Builder}, - * updating the name of the method if a similar method already exists. - * @param method a method builder representing the method to add - * @return the added method - */ - public MethodSpec addMethod(MethodSpec.Builder method) { - MethodSpec methodToAdd = createUniqueNameIfNecessary(method.build()); - this.methods.add(methodToAdd); - return methodToAdd; - } - - /** - * Return a {@link JavaFile} with the state of this instance. - * @return a java file - */ - public JavaFile toJavaFile() { - return JavaFile.builder(this.className.packageName(), - this.type.addMethods(this.methods).build()).indent("\t").build(); - } - - private MethodSpec createUniqueNameIfNecessary(MethodSpec method) { - List candidates = this.methods.stream().filter(isSimilar(method)).toList(); - if (candidates.isEmpty()) { - return method; - } - MethodSpec updatedMethod = method.toBuilder().setName(method.name + "_").build(); - return createUniqueNameIfNecessary(updatedMethod); - } - - private Predicate isSimilar(MethodSpec method) { - return candidate -> method.name.equals(candidate.name) - && method.parameters.size() == candidate.parameters.size(); - } - -} diff --git a/spring-core/src/main/java/org/springframework/aot/generator/GeneratedTypeContext.java b/spring-core/src/main/java/org/springframework/aot/generator/GeneratedTypeContext.java deleted file mode 100644 index 679423b4fe..0000000000 --- a/spring-core/src/main/java/org/springframework/aot/generator/GeneratedTypeContext.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.aot.generator; - -import org.springframework.aot.hint.RuntimeHints; - -/** - * Context passed to object that can generate code, giving access to a main - * {@link GeneratedType} as well as to a {@link GeneratedType} in a given - * package if privileged access is required. - * - * @author Stephane Nicoll - * @since 6.0 - */ -public interface GeneratedTypeContext { - - /** - * Return the {@link RuntimeHints} instance to use to contribute hints for - * generated types. - * @return the runtime hints - */ - RuntimeHints runtimeHints(); - - /** - * Return a {@link GeneratedType} for the specified package. If it does not - * exist, it is created. - * @param packageName the package name to use - * @return a generated type - */ - GeneratedType getGeneratedType(String packageName); - - /** - * Return the main {@link GeneratedType}. - * @return the generated type for the target package - */ - GeneratedType getMainGeneratedType(); - -} diff --git a/spring-core/src/main/java/org/springframework/aot/generator/GeneratedTypeReference.java b/spring-core/src/main/java/org/springframework/aot/generator/GeneratedTypeReference.java deleted file mode 100644 index 54ae72a1f4..0000000000 --- a/spring-core/src/main/java/org/springframework/aot/generator/GeneratedTypeReference.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.aot.generator; - -import org.springframework.aot.hint.AbstractTypeReference; -import org.springframework.aot.hint.TypeReference; -import org.springframework.javapoet.ClassName; -import org.springframework.lang.Nullable; -import org.springframework.util.Assert; - -/** - * A {@link TypeReference} for a generated {@linkplain ClassName type}. - * - * @author Stephane Nicoll - * @since 6.0 - */ -public final class GeneratedTypeReference extends AbstractTypeReference { - - private final ClassName className; - - private GeneratedTypeReference(ClassName className) { - super(className.packageName(), className.simpleName(), safeCreate(className.enclosingClassName())); - this.className = className; - } - - @Nullable - private static GeneratedTypeReference safeCreate(@Nullable ClassName className) { - return (className != null ? new GeneratedTypeReference(className) : null); - } - - public static GeneratedTypeReference of(ClassName className) { - Assert.notNull(className, "ClassName must not be null"); - return new GeneratedTypeReference(className); - } - - @Override - public String getCanonicalName() { - return this.className.canonicalName(); - } - - @Override - protected boolean isPrimitive() { - return this.className.isPrimitive(); - } - -} diff --git a/spring-core/src/main/java/org/springframework/aot/generator/ProtectedAccess.java b/spring-core/src/main/java/org/springframework/aot/generator/ProtectedAccess.java deleted file mode 100644 index 884f0a1a27..0000000000 --- a/spring-core/src/main/java/org/springframework/aot/generator/ProtectedAccess.java +++ /dev/null @@ -1,281 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.aot.generator; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Executable; -import java.lang.reflect.Field; -import java.lang.reflect.Member; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.function.Function; - -import org.springframework.core.ResolvableType; -import org.springframework.lang.Nullable; -import org.springframework.util.ClassUtils; - -/** - * Gather the need of non-public access and determine the privileged package - * to use, if necessary. - * - * @author Stephane Nicoll - * @since 6.0 - */ -public class ProtectedAccess { - - private final List elements; - - public ProtectedAccess() { - this.elements = new ArrayList<>(); - } - - /** - * Specify whether the protected elements registered in this instance are - * accessible from the specified package name. - * @param packageName the target package name - * @return {@code true} if the registered access can be safely used from - * the specified package name - */ - public boolean isAccessible(String packageName) { - return getProtectedElements(packageName).isEmpty(); - } - - /** - * Return the privileged package name to use for the specified package - * name, or {@code null} if none is required. - * @param packageName the target package name to use - * @return the privileged package name to use, or {@code null} - * @throws ProtectedAccessException if a single privileged package cannot - * be identified - * @see #isAccessible(String) - */ - @Nullable - public String getPrivilegedPackageName(String packageName) throws ProtectedAccessException { - List protectedElements = getProtectedElements(packageName); - if (protectedElements.isEmpty()) { - return null; - } - List packageNames = protectedElements.stream() - .map(element -> element.getType().getPackageName()) - .distinct().toList(); - if (packageNames.size() == 1) { - return packageNames.get(0); - } - throw new ProtectedAccessException("Multiple packages require a privileged access: " - + packageNames, protectedElements); - } - - private List getProtectedElements(String packageName) { - List matches = new ArrayList<>(); - for (ProtectedElement element : this.elements) { - if (!element.getType().getPackage().getName().equals(packageName)) { - matches.add(element); - } - } - return matches; - } - - /** - * Analyze the specified {@linkplain ResolvableType type}, including its - * full type signature. - * @param type the type to analyze - */ - public void analyze(ResolvableType type) { - Class protectedType = isProtected(type); - if (protectedType != null) { - registerProtectedType(protectedType, null); - } - } - - /** - * Analyze accessing the specified {@link Member} using the specified - * {@link Options options}. - * @param member the member to analyze - * @param options the options to use - */ - public void analyze(Member member, Options options) { - if (isProtected(member.getDeclaringClass())) { - registerProtectedType(member.getDeclaringClass(), member); - } - if (isProtected(member.getModifiers()) && !options.useReflection.apply(member)) { - registerProtectedType(member.getDeclaringClass(), member); - } - if (member instanceof Field field) { - ResolvableType fieldType = ResolvableType.forField(field); - Class protectedType = isProtected(fieldType); - if (protectedType != null && options.assignReturnType.apply(field)) { - registerProtectedType(protectedType, field); - } - } - else if (member instanceof Constructor constructor) { - analyzeParameterTypes(constructor, i -> - ResolvableType.forConstructorParameter(constructor, i)); - } - else if (member instanceof Method method) { - ResolvableType returnType = ResolvableType.forMethodReturnType(method); - Class protectedType = isProtected(returnType); - if (protectedType != null && options.assignReturnType.apply(method)) { - registerProtectedType(protectedType, method); - } - analyzeParameterTypes(method, i -> ResolvableType.forMethodParameter(method, i)); - } - } - - private void analyzeParameterTypes(Executable executable, Function parameterTypeFactory) { - - for (int i = 0; i < executable.getParameters().length; i++) { - ResolvableType parameterType = parameterTypeFactory.apply(i); - Class protectedType = isProtected(parameterType); - if (protectedType != null) { - registerProtectedType(protectedType, executable); - } - } - } - - @Nullable - Class isProtected(ResolvableType resolvableType) { - return isProtected(new HashSet<>(), resolvableType); - } - - @Nullable - private Class isProtected(Set seen, ResolvableType target) { - if (seen.contains(target)) { - return null; - } - seen.add(target); - ResolvableType nonProxyTarget = target.as(ClassUtils.getUserClass(target.toClass())); - Class rawClass = nonProxyTarget.toClass(); - if (isProtected(rawClass)) { - return rawClass; - } - Class declaringClass = rawClass.getDeclaringClass(); - if (declaringClass != null) { - if (isProtected(declaringClass)) { - return declaringClass; - } - } - if (nonProxyTarget.hasGenerics()) { - for (ResolvableType generic : nonProxyTarget.getGenerics()) { - return isProtected(seen, generic); - } - } - return null; - } - - private boolean isProtected(Class type) { - Class candidate = ClassUtils.getUserClass(type); - return isProtected(candidate.getModifiers()); - } - - private boolean isProtected(int modifiers) { - return !Modifier.isPublic(modifiers); - } - - private void registerProtectedType(Class type, @Nullable Member member) { - this.elements.add(ProtectedElement.of(type, member)); - } - - /** - * Options to use to analyze if invoking a {@link Member} requires - * privileged access. - */ - public static final class Options { - - private final Function assignReturnType; - - private final Function useReflection; - - - private Options(Builder builder) { - this.assignReturnType = builder.assignReturnType; - this.useReflection = builder.useReflection; - } - - /** - * Initialize a {@link Builder} with default options, that is use - * reflection if the member is private and does not assign the - * return type. - * @return an options builder - */ - public static Builder defaults() { - return new Builder(member -> false, - member -> Modifier.isPrivate(member.getModifiers())); - } - - public static final class Builder { - - private Function assignReturnType; - - private Function useReflection; - - private Builder(Function assignReturnType, - Function useReflection) { - this.assignReturnType = assignReturnType; - this.useReflection = useReflection; - } - - /** - * Specify if the return type is assigned so that its type can be - * analyzed if necessary. - * @param assignReturnType whether the return type is assigned - * @return {@code this}, to facilitate method chaining - */ - public Builder assignReturnType(boolean assignReturnType) { - return assignReturnType(member -> assignReturnType); - } - - /** - * Specify a function that determines whether the return type is - * assigned so that its type can be analyzed. - * @param assignReturnType whether the return type is assigned - * @return {@code this}, to facilitate method chaining - */ - public Builder assignReturnType(Function assignReturnType) { - this.assignReturnType = assignReturnType; - return this; - } - - /** - * Specify a function that determines whether reflection can be - * used for a given {@link Member}. - * @param useReflection whether reflection can be used - * @return {@code this}, to facilitate method chaining - */ - public Builder useReflection(Function useReflection) { - this.useReflection = useReflection; - return this; - } - - /** - * Build an {@link Options} instance based on the state of this - * builder. - * @return a new options instance - */ - public Options build() { - return new Options(this); - } - - } - - } - -} diff --git a/spring-core/src/main/java/org/springframework/aot/generator/ProtectedAccessException.java b/spring-core/src/main/java/org/springframework/aot/generator/ProtectedAccessException.java deleted file mode 100644 index 0a2c3fb8f5..0000000000 --- a/spring-core/src/main/java/org/springframework/aot/generator/ProtectedAccessException.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.aot.generator; - -import java.util.List; - -/** - * Thrown when a code block requires privileged access on multiple packages. - * - * @author Stephane Nicoll - * @since 6.0 - */ -@SuppressWarnings("serial") -public class ProtectedAccessException extends RuntimeException { - - private final List protectedElements; - - public ProtectedAccessException(String message, List protectedElements) { - super(message); - this.protectedElements = protectedElements; - } - - /** - * Return the {@linkplain ProtectedElement protected elements}. - * @return the protected access - */ - public List getProtectedElements() { - return this.protectedElements; - } - -} diff --git a/spring-core/src/main/java/org/springframework/aot/generator/ProtectedElement.java b/spring-core/src/main/java/org/springframework/aot/generator/ProtectedElement.java deleted file mode 100644 index c7b5d6c2e5..0000000000 --- a/spring-core/src/main/java/org/springframework/aot/generator/ProtectedElement.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.aot.generator; - -import java.lang.reflect.Member; - -import org.springframework.lang.Nullable; - -/** - * A {@link Member} that is non-public, with the related type. - * - * @author Stephane Nicoll - * @since 6.0 - */ -public final class ProtectedElement { - - private final Class type; - - @Nullable - private final Member target; - - - private ProtectedElement(Class type, @Nullable Member member) { - this.type = type; - this.target = member; - } - - /** - * Return the {@link Class type} that is non-public. For a plain - * protected {@link Member member} access, the type of the declaring class - * is used. Otherwise, the type in the member signature, such as a parameter - * type for an executable, or the return type of a field is used. If the - * type is generic, the type that is protected in the generic signature is - * used. - * @return the type that is not public - */ - public Class getType() { - return this.type; - } - - /** - * Return the {@link Member} that is not publicly accessible. - * @return the member - */ - @Nullable - public Member getMember() { - return this.target; - } - - static ProtectedElement of(Class type, @Nullable Member member) { - return new ProtectedElement(type, member); - } - -} diff --git a/spring-core/src/main/java/org/springframework/aot/generator/ResolvableTypeGenerator.java b/spring-core/src/main/java/org/springframework/aot/generator/ResolvableTypeGenerator.java deleted file mode 100644 index 339a993bf9..0000000000 --- a/spring-core/src/main/java/org/springframework/aot/generator/ResolvableTypeGenerator.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.aot.generator; - -import java.util.Arrays; - -import org.springframework.core.ResolvableType; -import org.springframework.javapoet.CodeBlock; -import org.springframework.javapoet.support.MultiCodeBlock; -import org.springframework.util.ClassUtils; - -/** - * Code generator for {@link ResolvableType}. - * - * @author Stephane Nicoll - * @since 6.0 - */ -public final class ResolvableTypeGenerator { - - /** - * Generate a type signature for the specified {@link ResolvableType}. - * @param target the type to generate - * @return the representation of that type - */ - public CodeBlock generateTypeFor(ResolvableType target) { - CodeBlock.Builder code = CodeBlock.builder(); - generate(code, target, false); - return code.build(); - } - - private void generate(CodeBlock.Builder code, ResolvableType target, boolean forceResolvableType) { - Class type = ClassUtils.getUserClass(target.toClass()); - if (!target.hasGenerics()) { - if (forceResolvableType) { - code.add("$T.forClass($T.class)", ResolvableType.class, type); - } - else { - code.add("$T.class", type); - } - } - else { - code.add("$T.forClassWithGenerics($T.class, ", ResolvableType.class, type); - ResolvableType[] generics = target.getGenerics(); - boolean hasGenericParameter = Arrays.stream(generics).anyMatch(ResolvableType::hasGenerics); - MultiCodeBlock multi = new MultiCodeBlock(); - for (int i = 0; i < generics.length; i++) { - ResolvableType parameter = target.getGeneric(i); - multi.add(parameterCode -> generate(parameterCode, parameter, hasGenericParameter)); - } - code.add(multi.join(", ")).add(")"); - } - } - -} diff --git a/spring-core/src/main/java/org/springframework/aot/generator/package-info.java b/spring-core/src/main/java/org/springframework/aot/generator/package-info.java deleted file mode 100644 index beef9af746..0000000000 --- a/spring-core/src/main/java/org/springframework/aot/generator/package-info.java +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Support classes for components that contribute generated code equivalent - * to a runtime behavior. - */ -@NonNullApi -@NonNullFields -package org.springframework.aot.generator; - -import org.springframework.lang.NonNullApi; -import org.springframework.lang.NonNullFields; diff --git a/spring-core/src/main/java/org/springframework/aot/hint/RuntimeHintsRegistrar.java b/spring-core/src/main/java/org/springframework/aot/hint/RuntimeHintsRegistrar.java index edcd06c431..0b764858e1 100644 --- a/spring-core/src/main/java/org/springframework/aot/hint/RuntimeHintsRegistrar.java +++ b/spring-core/src/main/java/org/springframework/aot/hint/RuntimeHintsRegistrar.java @@ -22,7 +22,7 @@ import org.springframework.lang.Nullable; * Contract for registering {@link RuntimeHints} in a static fashion. *

Implementations will contribute hints without any knowledge of the application context * and can only use the given {@link ClassLoader} to conditionally contribute hints. - *

{@code RuntimeHintsRegistrar} can be declared as {@code spring.factories} entries; + *

{@code RuntimeHintsRegistrar} can be declared as {@code spring/aot.factories} entries; * the registrar will be processed as soon as its declaration is found in the classpath. * A standard no-arg constructor is required for implementations. * diff --git a/spring-core/src/main/java/org/springframework/javapoet/support/CodeSnippet.java b/spring-core/src/main/java/org/springframework/javapoet/support/CodeSnippet.java deleted file mode 100644 index ba2f47815c..0000000000 --- a/spring-core/src/main/java/org/springframework/javapoet/support/CodeSnippet.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.javapoet.support; - -import java.io.IOException; -import java.io.StringWriter; -import java.util.function.Consumer; -import java.util.stream.Collectors; - -import javax.lang.model.element.Modifier; - -import org.springframework.javapoet.CodeBlock; -import org.springframework.javapoet.JavaFile; -import org.springframework.javapoet.MethodSpec; -import org.springframework.javapoet.TypeSpec; - -/** - * A code snippet using tabs indentation that is fully processed by JavaPoet so - * that imports are resolved. - * - * @author Stephane Nicoll - * @since 6.0 - */ -public final class CodeSnippet { - - private static final String START_SNIPPET = "// start-snippet\n"; - - private static final String END_SNIPPET = "// end-snippet"; - - private final String fileContent; - - private final String snippet; - - - CodeSnippet(String fileContent, String snippet) { - this.fileContent = fileContent; - this.snippet = snippet; - } - - - String getFileContent() { - return this.fileContent; - } - - /** - * Return the rendered code snippet. - * @return a code snippet where imports have been resolved - */ - public String getSnippet() { - return this.snippet; - } - - /** - * Specify if an import statement for the specified type is present. - * @param type the type to check - * @return true if this type has an import statement, false otherwise - */ - public boolean hasImport(Class type) { - return hasImport(type.getName()); - } - - /** - * Specify if an import statement for the specified class name is present. - * @param className the name of the class to check - * @return true if this type has an import statement, false otherwise - */ - public boolean hasImport(String className) { - return getFileContent().lines().anyMatch(candidate -> - candidate.equals(String.format("import %s;", className))); - } - - /** - * Return a new {@link CodeSnippet} where the specified number of indentations - * have been removed. - * @param indent the number of indent to remove - * @return a CodeSnippet instance with the number of indentations removed - */ - public CodeSnippet removeIndent(int indent) { - return new CodeSnippet(this.fileContent, this.snippet.lines().map(line -> - removeIndent(line, indent)).collect(Collectors.joining("\n"))); - } - - /** - * Create a {@link CodeSnippet} using the specified code. - * @param code the code snippet - * @return a {@link CodeSnippet} instance - */ - public static CodeSnippet of(CodeBlock code) { - return new Builder().build(code); - } - - /** - * Process the specified code and return a fully-processed code snippet - * as a String. - * @param code a consumer to use to generate the code snippet - * @return a resolved code snippet - */ - public static String process(Consumer code) { - CodeBlock.Builder body = CodeBlock.builder(); - code.accept(body); - return process(body.build()); - } - - /** - * Process the specified {@link CodeBlock code} and return a - * fully-processed code snippet as a String. - * @param code the code snippet - * @return a resolved code snippet - */ - public static String process(CodeBlock code) { - return of(code).getSnippet(); - } - - private String removeIndent(String line, int indent) { - for (int i = 0; i < indent; i++) { - if (line.startsWith("\t")) { - line = line.substring(1); - } - } - return line; - } - - private static final class Builder { - - private static final String INDENT = "\t"; - - private static final String SNIPPET_INDENT = INDENT + INDENT; - - public CodeSnippet build(CodeBlock code) { - MethodSpec.Builder method = MethodSpec.methodBuilder("test") - .addModifiers(Modifier.PUBLIC); - CodeBlock.Builder body = CodeBlock.builder(); - body.add(START_SNIPPET); - body.add(code); - body.add(END_SNIPPET); - method.addCode(body.build()); - String fileContent = write(createTestJavaFile(method.build())); - String snippet = isolateGeneratedContent(fileContent); - return new CodeSnippet(fileContent, snippet); - } - - private String isolateGeneratedContent(String javaFile) { - int start = javaFile.indexOf(START_SNIPPET); - String tmp = javaFile.substring(start + START_SNIPPET.length()); - int end = tmp.indexOf(END_SNIPPET); - tmp = tmp.substring(0, end); - // Remove indent - return tmp.lines().map(line -> { - if (!line.startsWith(SNIPPET_INDENT)) { - throw new IllegalStateException("Missing indent for " + line); - } - return line.substring(SNIPPET_INDENT.length()); - }).collect(Collectors.joining("\n")); - } - - private JavaFile createTestJavaFile(MethodSpec method) { - return JavaFile.builder("example", TypeSpec.classBuilder("Test") - .addModifiers(Modifier.PUBLIC) - .addMethod(method).build()).indent(INDENT).build(); - } - - private String write(JavaFile file) { - try { - StringWriter out = new StringWriter(); - file.writeTo(out); - return out.toString(); - } - catch (IOException ex) { - throw new IllegalStateException("Failed to write " + file, ex); - } - } - - } -} diff --git a/spring-core/src/main/java/org/springframework/javapoet/support/MultiCodeBlock.java b/spring-core/src/main/java/org/springframework/javapoet/support/MultiCodeBlock.java deleted file mode 100644 index f1391b62d5..0000000000 --- a/spring-core/src/main/java/org/springframework/javapoet/support/MultiCodeBlock.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.javapoet.support; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.Consumer; - -import org.springframework.javapoet.CodeBlock; -import org.springframework.javapoet.CodeBlock.Builder; - - -/** - * A {@link CodeBlock} wrapper for joining multiple blocks. - * - * @author Stephane Nicoll - * @since 6.0 - */ -public class MultiCodeBlock { - - private final List codeBlocks = new ArrayList<>(); - - - /** - * Add the specified {@link CodeBlock}. - * @param code the code block to add - */ - public void add(CodeBlock code) { - if (code.isEmpty()) { - throw new IllegalArgumentException("Could not add empty CodeBlock"); - } - this.codeBlocks.add(code); - } - - /** - * Add a {@link CodeBlock} using the specified callback. - * @param code the callback to use - */ - public void add(Consumer code) { - Builder builder = CodeBlock.builder(); - code.accept(builder); - add(builder.build()); - } - - /** - * Add a code block using the specified formatted String and the specified - * arguments. - * @param code the code - * @param arguments the arguments - * @see Builder#add(String, Object...) - */ - public void add(String code, Object... arguments) { - add(CodeBlock.of(code, arguments)); - } - - /** - * Return a {@link CodeBlock} that joins the different blocks registered in - * this instance with the specified delimiter. - * @param delimiter the delimiter to use (not {@literal null}) - * @return a {@link CodeBlock} joining the blocks of this instance with the - * specified {@code delimiter} - * @see CodeBlock#join(Iterable, String) - */ - public CodeBlock join(String delimiter) { - return CodeBlock.join(this.codeBlocks, delimiter); - } - -} diff --git a/spring-core/src/main/java/org/springframework/javapoet/support/MultiStatement.java b/spring-core/src/main/java/org/springframework/javapoet/support/MultiStatement.java deleted file mode 100644 index b7730659ca..0000000000 --- a/spring-core/src/main/java/org/springframework/javapoet/support/MultiStatement.java +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.javapoet.support; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.Consumer; -import java.util.function.Function; - -import org.springframework.javapoet.CodeBlock; -import org.springframework.javapoet.CodeBlock.Builder; - - -/** - * A {@link CodeBlock} wrapper for multiple statements. - * - * @author Stephane Nicoll - * @since 6.0 - */ -public final class MultiStatement { - - private final List statements = new ArrayList<>(); - - - /** - * Specify if this instance is empty. - * @return {@code true} if no statement is registered, {@code false} otherwise - */ - public boolean isEmpty() { - return this.statements.isEmpty(); - } - - /** - * Add the statements defined in the specified multi statement to this instance. - * @param multiStatement the statements to add - * @return {@code this}, to facilitate method chaining - */ - public MultiStatement add(MultiStatement multiStatement) { - this.statements.addAll(multiStatement.statements); - return this; - } - - /** - * Add the specified {@link CodeBlock codeblock} rendered as-is. - * @param codeBlock the code block to add - * @return {@code this}, to facilitate method chaining - * @see #addStatement(CodeBlock) to add a code block that represents - * a statement - */ - public MultiStatement add(CodeBlock codeBlock) { - this.statements.add(Statement.of(codeBlock)); - return this; - } - - /** - * Add a {@link CodeBlock} rendered as-is using the specified callback. - * @param code the callback to use - * @return {@code this}, to facilitate method chaining - * @see #addStatement(CodeBlock) to add a code block that represents - * a statement - */ - public MultiStatement add(Consumer code) { - CodeBlock.Builder builder = CodeBlock.builder(); - code.accept(builder); - add(builder.build()); - return this; - } - - /** - * Add a statement. - * @param statement the statement to add - * @return {@code this}, to facilitate method chaining - */ - public MultiStatement addStatement(CodeBlock statement) { - this.statements.add(Statement.ofStatement(statement)); - return this; - } - - /** - * Add a statement using the specified callback. - * @param code the callback to use - * @return {@code this}, to facilitate method chaining - */ - public MultiStatement addStatement(Consumer code) { - CodeBlock.Builder builder = CodeBlock.builder(); - code.accept(builder); - return addStatement(builder.build()); - } - - /** - * Add a statement using the specified formatted String and the specified - * arguments. - * @param code the code of the statement - * @param args the arguments for placeholders - * @return {@code this}, to facilitate method chaining - * @see CodeBlock#of(String, Object...) - */ - public MultiStatement addStatement(String code, Object... args) { - return addStatement(CodeBlock.of(code, args)); - } - - /** - * Add the statements produced from the {@code itemGenerator} applied on the specified - * items. - * @param items the items to handle, each item is represented as a statement - * @param itemGenerator the item generator - * @param the type of the item - * @return {@code this}, to facilitate method chaining - */ - public MultiStatement addAll(Iterable items, Function itemGenerator) { - items.forEach(element -> addStatement(itemGenerator.apply(element))); - return this; - } - - /** - * Return a {@link CodeBlock} that applies all the {@code statements} of - * this instance. - * @return the code block - */ - public CodeBlock toCodeBlock() { - Builder code = CodeBlock.builder(); - this.statements.forEach(statement -> statement.add(code)); - return code.build(); - } - - /** - * Return a {@link CodeBlock} that applies all the {@code statements} of this - * instance. If only one statement is available, it is not completed using the - * {@code ;} termination so that it can be used in the context of a lambda. - * @return the body of the lambda - */ - public CodeBlock toLambdaBody() { - Builder code = CodeBlock.builder(); - for (int i = 0; i < this.statements.size(); i++) { - Statement statement = this.statements.get(i); - statement.contribute(code, this.isMulti(), i == this.statements.size() - 1); - } - return code.build(); - } - - /** - * Return a {@link CodeBlock} that applies all the {@code statements} of this - * instance in the context of a lambda. - * @param lambdaParameter the parameter(s) of the lambda, must end with {@code ->} - * @return a lambda whose body is generated from the statements of this instance - */ - public CodeBlock toLambda(CodeBlock lambdaParameter) { - Builder code = CodeBlock.builder(); - code.add(lambdaParameter); - if (isMulti()) { - code.beginControlFlow(""); - } - else { - code.add(" "); - } - code.add(toLambdaBody()); - if (isMulti()) { - code.add("\n").unindent().add("}"); - } - return code.build(); - } - - /** - * Return a {@link CodeBlock} that applies all the {@code statements} of this - * instance in the context of a lambda. - * @param lambdaParameter the parameter(s) of the lambda, must end with {@code ->} - * @return a lambda whose body is generated from the statements of this instance - */ - public CodeBlock toLambda(String lambdaParameter) { - return toLambda(CodeBlock.of(lambdaParameter)); - } - - private boolean isMulti() { - return this.statements.size() > 1; - } - - - private static class Statement { - - private final CodeBlock codeBlock; - - private final boolean addStatementTermination; - - Statement(CodeBlock codeBlock, boolean addStatementTermination) { - this.codeBlock = codeBlock; - this.addStatementTermination = addStatementTermination; - } - - void add(CodeBlock.Builder code) { - code.add(this.codeBlock); - if (this.addStatementTermination) { - code.add(";\n"); - } - } - - void contribute(CodeBlock.Builder code, boolean multi, boolean isLastStatement) { - code.add(this.codeBlock); - if (this.addStatementTermination) { - if (!isLastStatement) { - code.add(";\n"); - } - else if (multi) { - code.add(";"); - } - } - } - - static Statement ofStatement(CodeBlock codeBlock) { - return new Statement(codeBlock, true); - } - - static Statement of(CodeBlock codeBlock) { - return new Statement(codeBlock, false); - } - - } - -} diff --git a/spring-core/src/main/java/org/springframework/javapoet/support/package-info.java b/spring-core/src/main/java/org/springframework/javapoet/support/package-info.java deleted file mode 100644 index 6f2c37d88b..0000000000 --- a/spring-core/src/main/java/org/springframework/javapoet/support/package-info.java +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Support classes for JavaPoet usage. - */ -@NonNullApi -@NonNullFields -package org.springframework.javapoet.support; - -import org.springframework.lang.NonNullApi; -import org.springframework.lang.NonNullFields; diff --git a/spring-core/src/test/java/org/springframework/aot/generator/DefaultCodeContributionTests.java b/spring-core/src/test/java/org/springframework/aot/generator/DefaultCodeContributionTests.java deleted file mode 100644 index ce69671ddf..0000000000 --- a/spring-core/src/test/java/org/springframework/aot/generator/DefaultCodeContributionTests.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.aot.generator; - -import org.junit.jupiter.api.Test; - -import org.springframework.aot.hint.RuntimeHints; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link DefaultCodeContribution}. - * - * @author Stephane Nicoll - */ -class DefaultCodeContributionTests { - - @Test - void newCodeContributionIsEmpty() { - CodeContribution contribution = new DefaultCodeContribution(new RuntimeHints()); - assertThat(contribution.statements().isEmpty()).isTrue(); - assertThat(contribution.protectedAccess().isAccessible("com.example")).isTrue(); - } - - @Test - void codeContributionReusesRuntimeHints() { - RuntimeHints runtimeHints = new RuntimeHints(); - CodeContribution contribution = new DefaultCodeContribution(runtimeHints); - assertThat(contribution.runtimeHints()).isSameAs(runtimeHints); - } - -} diff --git a/spring-core/src/test/java/org/springframework/aot/generator/DefaultGeneratedTypeContextTests.java b/spring-core/src/test/java/org/springframework/aot/generator/DefaultGeneratedTypeContextTests.java deleted file mode 100644 index f030467ae3..0000000000 --- a/spring-core/src/test/java/org/springframework/aot/generator/DefaultGeneratedTypeContextTests.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.aot.generator; - -import javax.lang.model.element.Modifier; - -import org.junit.jupiter.api.Test; - -import org.springframework.javapoet.ClassName; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link DefaultGeneratedTypeContext}. - * - * @author Stephane Nicoll - */ -class DefaultGeneratedTypeContextTests { - - @Test - void runtimeHints() { - DefaultGeneratedTypeContext context = createComAcmeContext(); - assertThat(context.runtimeHints()).isNotNull(); - } - - @Test - void getGeneratedTypeMatchesGetMainGeneratedTypeForMainPackage() { - DefaultGeneratedTypeContext context = createComAcmeContext(); - assertThat(context.getMainGeneratedType().getClassName()).isEqualTo(ClassName.get("com.acme", "Main")); - assertThat(context.getGeneratedType("com.acme")).isSameAs(context.getMainGeneratedType()); - } - - @Test - void getMainGeneratedTypeIsLazilyCreated() { - DefaultGeneratedTypeContext context = createComAcmeContext(); - assertThat(context.hasGeneratedType("com.acme")).isFalse(); - context.getMainGeneratedType(); - assertThat(context.hasGeneratedType("com.acme")).isTrue(); - } - - @Test - void getGeneratedTypeRegisterInstance() { - DefaultGeneratedTypeContext context = createComAcmeContext(); - assertThat(context.hasGeneratedType("com.example")).isFalse(); - GeneratedType generatedType = context.getGeneratedType("com.example"); - assertThat(generatedType).isNotNull(); - assertThat(generatedType.getClassName().simpleName()).isEqualTo("Main"); - assertThat(context.hasGeneratedType("com.example")).isTrue(); - } - - @Test - void getGeneratedTypeReuseInstance() { - DefaultGeneratedTypeContext context = createComAcmeContext(); - GeneratedType generatedType = context.getGeneratedType("com.example"); - assertThat(generatedType.getClassName().packageName()).isEqualTo("com.example"); - assertThat(context.getGeneratedType("com.example")).isSameAs(generatedType); - } - - @Test - void toJavaFilesWithNoTypeIsEmpty() { - DefaultGeneratedTypeContext writerContext = createComAcmeContext(); - assertThat(writerContext.toJavaFiles()).hasSize(0); - } - - @Test - void toJavaFilesWithDefaultTypeIsAddedLazily() { - DefaultGeneratedTypeContext writerContext = createComAcmeContext(); - writerContext.getMainGeneratedType(); - assertThat(writerContext.toJavaFiles()).hasSize(1); - } - - @Test - void toJavaFilesWithDefaultTypeAndAdditionaTypes() { - DefaultGeneratedTypeContext writerContext = createComAcmeContext(); - writerContext.getGeneratedType("com.example"); - writerContext.getGeneratedType("com.another"); - writerContext.getGeneratedType("com.another.another"); - assertThat(writerContext.toJavaFiles()).hasSize(3); - } - - private DefaultGeneratedTypeContext createComAcmeContext() { - return new DefaultGeneratedTypeContext("com.acme", packageName -> - GeneratedType.of(ClassName.get(packageName, "Main"), type -> type.addModifiers(Modifier.PUBLIC))); - } - -} diff --git a/spring-core/src/test/java/org/springframework/aot/generator/GeneratedTypeReferenceTests.java b/spring-core/src/test/java/org/springframework/aot/generator/GeneratedTypeReferenceTests.java deleted file mode 100644 index 23d7646b84..0000000000 --- a/spring-core/src/test/java/org/springframework/aot/generator/GeneratedTypeReferenceTests.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.aot.generator; - -import java.util.stream.Stream; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; - -import org.springframework.aot.hint.TypeReference; -import org.springframework.javapoet.ClassName; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link GeneratedTypeReference}. - * - * @author Stephane Nicoll - */ -class GeneratedTypeReferenceTests { - - - @ParameterizedTest - @MethodSource("reflectionTargetNames") - void hasSuitableReflectionTargetName(TypeReference typeReference, String binaryName) { - assertThat(typeReference.getName()).isEqualTo(binaryName); - } - - static Stream reflectionTargetNames() { - return Stream.of( - Arguments.of(GeneratedTypeReference.of(ClassName.get("com.example", "Test")), "com.example.Test"), - Arguments.of(GeneratedTypeReference.of(ClassName.get("com.example", "Test", "Inner")), "com.example.Test$Inner")); - } - - @Test - void createWithClassName() { - GeneratedTypeReference typeReference = GeneratedTypeReference.of( - ClassName.get("com.example", "Test")); - assertThat(typeReference.getPackageName()).isEqualTo("com.example"); - assertThat(typeReference.getSimpleName()).isEqualTo("Test"); - assertThat(typeReference.getCanonicalName()).isEqualTo("com.example.Test"); - assertThat(typeReference.getEnclosingType()).isNull(); - } - - @Test - void createWithClassNameAndParent() { - GeneratedTypeReference typeReference = GeneratedTypeReference.of( - ClassName.get("com.example", "Test").nestedClass("Nested")); - assertThat(typeReference.getPackageName()).isEqualTo("com.example"); - assertThat(typeReference.getSimpleName()).isEqualTo("Nested"); - assertThat(typeReference.getCanonicalName()).isEqualTo("com.example.Test.Nested"); - assertThat(typeReference.getEnclosingType()).satisfies(parentTypeReference -> { - assertThat(parentTypeReference.getPackageName()).isEqualTo("com.example"); - assertThat(parentTypeReference.getSimpleName()).isEqualTo("Test"); - assertThat(parentTypeReference.getCanonicalName()).isEqualTo("com.example.Test"); - assertThat(parentTypeReference.getEnclosingType()).isNull(); - }); - } - - @Test - void equalsWithIdenticalCanonicalNameIsTrue() { - assertThat(GeneratedTypeReference.of(ClassName.get("java.lang", "String"))) - .isEqualTo(TypeReference.of(String.class)); - } - -} diff --git a/spring-core/src/test/java/org/springframework/aot/generator/GeneratedTypeTests.java b/spring-core/src/test/java/org/springframework/aot/generator/GeneratedTypeTests.java deleted file mode 100644 index d3044bc04c..0000000000 --- a/spring-core/src/test/java/org/springframework/aot/generator/GeneratedTypeTests.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.aot.generator; - -import java.io.IOException; -import java.io.StringWriter; - -import javax.lang.model.element.Modifier; - -import org.junit.jupiter.api.Test; - -import org.springframework.javapoet.ClassName; -import org.springframework.javapoet.CodeBlock; -import org.springframework.javapoet.FieldSpec; -import org.springframework.javapoet.MethodSpec; -import org.springframework.javapoet.TypeName; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link GeneratedType}. - * - * @author Stephane Nicoll - */ -class GeneratedTypeTests { - - private static final ClassName TEST_CLASS_NAME = ClassName.get("com.acme", "Test"); - - @Test - void className() { - GeneratedType generatedType = new GeneratedType(TEST_CLASS_NAME, - type -> type.addModifiers(Modifier.STATIC)); - assertThat(generatedType.getClassName()).isEqualTo(TEST_CLASS_NAME); - assertThat(generateCode(generatedType)).contains("static class Test {"); - } - - @Test - void createWithCustomField() { - GeneratedType generatedType = new GeneratedType(TEST_CLASS_NAME, - type -> type.addField(FieldSpec.builder(TypeName.BOOLEAN, "enabled").build())); - assertThat(generateCode(generatedType)).contains("boolean enabled;"); - } - - @Test - void customizeType() { - GeneratedType generatedType = createTestGeneratedType(); - generatedType.customizeType(type -> type.addJavadoc("Test javadoc.")) - .customizeType(type -> type.addJavadoc(" Another test javadoc")); - assertThat(generateCode(generatedType)).containsSequence( - "/**\n", - " * Test javadoc. Another test javadoc\n", - " */"); - } - - @Test - void addMethod() { - GeneratedType generatedType = createTestGeneratedType(); - generatedType.addMethod(MethodSpec.methodBuilder("test").returns(Integer.class) - .addCode(CodeBlock.of("return 42;"))); - assertThat(generateCode(generatedType)).containsSequence( - "\tInteger test() {\n", - "\t\treturn 42;\n", - "\t}"); - } - - @Test - void addMultipleMethods() { - GeneratedType generatedType = createTestGeneratedType(); - generatedType.addMethod(MethodSpec.methodBuilder("first")); - generatedType.addMethod(MethodSpec.methodBuilder("second")); - assertThat(generateCode(generatedType)) - .containsSequence("\tvoid first() {\n", "\t}") - .containsSequence("\tvoid second() {\n", "\t}"); - } - - @Test - void addSimilarMethodGenerateUniqueNames() { - GeneratedType generatedType = createTestGeneratedType(); - MethodSpec firstMethod = generatedType.addMethod(MethodSpec.methodBuilder("test")); - MethodSpec secondMethod = generatedType.addMethod(MethodSpec.methodBuilder("test")); - MethodSpec thirdMethod = generatedType.addMethod(MethodSpec.methodBuilder("test")); - assertThat(firstMethod.name).isEqualTo("test"); - assertThat(secondMethod.name).isEqualTo("test_"); - assertThat(thirdMethod.name).isEqualTo("test__"); - assertThat(generateCode(generatedType)) - .containsSequence("\tvoid test() {\n", "\t}") - .containsSequence("\tvoid test_() {\n", "\t}") - .containsSequence("\tvoid test__() {\n", "\t}"); - } - - @Test - void addMethodWithSameNameAndDifferentArgumentsDoesNotChangeName() { - GeneratedType generatedType = createTestGeneratedType(); - generatedType.addMethod(MethodSpec.methodBuilder("test")); - MethodSpec secondMethod = generatedType.addMethod(MethodSpec.methodBuilder("test") - .addParameter(String.class, "param")); - assertThat(secondMethod.name).isEqualTo("test"); - } - - private GeneratedType createTestGeneratedType() { - return GeneratedType.of(TEST_CLASS_NAME); - } - - private String generateCode(GeneratedType generatedType) { - try { - StringWriter out = new StringWriter(); - generatedType.toJavaFile().writeTo(out); - return out.toString(); - } - catch (IOException ex) { - throw new IllegalStateException(ex); - } - } - -} diff --git a/spring-core/src/test/java/org/springframework/aot/generator/ProtectedAccessTests.java b/spring-core/src/test/java/org/springframework/aot/generator/ProtectedAccessTests.java deleted file mode 100644 index 9e32b62030..0000000000 --- a/spring-core/src/test/java/org/springframework/aot/generator/ProtectedAccessTests.java +++ /dev/null @@ -1,273 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.aot.generator; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; - -import org.junit.jupiter.api.Test; - -import org.springframework.aot.generator.ProtectedAccess.Options; -import org.springframework.core.ResolvableType; -import org.springframework.core.testfixture.aot.generator.visibility.ProtectedGenericParameter; -import org.springframework.core.testfixture.aot.generator.visibility.ProtectedParameter; -import org.springframework.core.testfixture.aot.generator.visibility.PublicFactoryBean; -import org.springframework.util.ReflectionUtils; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -/** - * Tests for {@link ProtectedAccess}. - * - * @author Stephane Nicoll - */ -class ProtectedAccessTests { - - public static final Options DEFAULT_OPTIONS = Options.defaults().build(); - - private final ProtectedAccess protectedAccess = new ProtectedAccess(); - - @Test - void analyzeWithPublicConstructor() throws NoSuchMethodException { - this.protectedAccess.analyze(PublicClass.class.getConstructor(), DEFAULT_OPTIONS); - assertThat(this.protectedAccess.isAccessible("com.example")).isTrue(); - } - - @Test - void analyzeWithPackagePrivateConstructor() { - this.protectedAccess.analyze(ProtectedAccessor.class.getDeclaredConstructors()[0], - DEFAULT_OPTIONS); - assertPrivilegedAccess(ProtectedAccessor.class); - } - - @Test - void analyzeWithPackagePrivateConstructorAndReflectionEnabled() { - Constructor constructor = ProtectedAccessor.class.getDeclaredConstructors()[0]; - this.protectedAccess.analyze(constructor, - Options.defaults().useReflection(member -> member.equals(constructor)).build()); - assertThat(this.protectedAccess.isAccessible("com.example")).isTrue(); - } - - @Test - void analyzeWithPackagePrivateClass() { - this.protectedAccess.analyze(ProtectedClass.class.getDeclaredConstructors()[0], DEFAULT_OPTIONS); - assertPrivilegedAccess(ProtectedClass.class); - } - - @Test - void analyzeWithPackagePrivateDeclaringType() { - this.protectedAccess.analyze(method(ProtectedClass.class, "stringBean"), DEFAULT_OPTIONS); - assertPrivilegedAccess(ProtectedClass.class); - } - - @Test - void analyzeWithPackagePrivateConstructorParameter() { - this.protectedAccess.analyze(ProtectedParameter.class.getConstructors()[0], DEFAULT_OPTIONS); - assertPrivilegedAccess(ProtectedParameter.class); - } - - @Test - void analyzeWithPackagePrivateConstructorGenericParameter() { - this.protectedAccess.analyze(ProtectedGenericParameter.class.getConstructors()[0], DEFAULT_OPTIONS); - assertPrivilegedAccess(ProtectedParameter.class); - } - - @Test - void analyzeWithPackagePrivateMethod() { - this.protectedAccess.analyze(method(PublicClass.class, "getProtectedMethod"), DEFAULT_OPTIONS); - assertPrivilegedAccess(PublicClass.class); - } - - @Test - void analyzeWithPackagePrivateMethodAndReflectionEnabled() { - this.protectedAccess.analyze(method(PublicClass.class, "getProtectedMethod"), - Options.defaults().useReflection(member -> !Modifier.isPublic(member.getModifiers())).build()); - assertThat(this.protectedAccess.isAccessible("com.example")).isTrue(); - } - - @Test - void analyzeWithPackagePrivateMethodReturnType() { - this.protectedAccess.analyze(method(ProtectedAccessor.class, "methodWithProtectedReturnType"), DEFAULT_OPTIONS); - assertThat(this.protectedAccess.isAccessible("com.example")).isTrue(); - } - - @Test - void analyzeWithPackagePrivateMethodReturnTypeAndAssignReturnTypeFunction() { - this.protectedAccess.analyze(method(ProtectedAccessor.class, "methodWithProtectedReturnType"), - Options.defaults().assignReturnType(member -> false).build()); - assertThat(this.protectedAccess.isAccessible("com.example")).isTrue(); - } - - @Test - void analyzeWithPackagePrivateMethodReturnTypeAndAssignReturnType() { - this.protectedAccess.analyze(method(ProtectedAccessor.class, "methodWithProtectedReturnType"), - Options.defaults().assignReturnType(true).build()); - assertPrivilegedAccess(ProtectedAccessor.class); - } - - @Test - void analyzeWithPackagePrivateMethodParameter() { - this.protectedAccess.analyze(method(ProtectedAccessor.class, "methodWithProtectedParameter", - ProtectedClass.class), DEFAULT_OPTIONS); - assertPrivilegedAccess(ProtectedAccessor.class); - } - - @Test - void analyzeWithPackagePrivateField() { - this.protectedAccess.analyze(field(PublicClass.class, "protectedField"), DEFAULT_OPTIONS); - assertPrivilegedAccess(PublicClass.class); - } - - @Test - void analyzeWithPackagePrivateFieldAndReflectionEnabled() { - this.protectedAccess.analyze(field(PublicClass.class, "protectedField"), - Options.defaults().useReflection(member -> true).build()); - assertThat(this.protectedAccess.isAccessible("com.example")).isTrue(); - } - - @Test - void analyzeWithPublicFieldAndProtectedType() { - this.protectedAccess.analyze(field(PublicClass.class, "protectedClassField"), DEFAULT_OPTIONS); - assertThat(this.protectedAccess.isAccessible("com.example")).isTrue(); - } - - @Test - void analyzeWithPublicFieldAndProtectedTypeAssigned() { - this.protectedAccess.analyze(field(PublicClass.class, "protectedClassField"), - Options.defaults().assignReturnType(true).build()); - assertPrivilegedAccess(ProtectedClass.class); - } - - @Test - void analyzeWithPackagePrivateGenericArgument() { - this.protectedAccess.analyze(method(PublicFactoryBean.class, "protectedTypeFactoryBean"), - Options.defaults().assignReturnType(true).build()); - assertPrivilegedAccess(PublicFactoryBean.class); - } - - @Test - void analyzeTypeWithProtectedGenericArgument() { - this.protectedAccess.analyze(PublicFactoryBean.resolveToProtectedGenericParameter()); - assertPrivilegedAccess(PublicFactoryBean.class); - } - - @Test - void analyzeWithRecursiveType() { - assertThat(this.protectedAccess.isProtected(ResolvableType.forClassWithGenerics( - SelfReference.class, SelfReference.class))).isEqualTo(SelfReference.class); - } - - @Test - void getProtectedPackageWithPublicAccess() throws NoSuchMethodException { - this.protectedAccess.analyze(PublicClass.class.getConstructor(), DEFAULT_OPTIONS); - assertThat(this.protectedAccess.getPrivilegedPackageName("com.example")).isNull(); - } - - @Test - void getProtectedPackageWithProtectedAccessInOnePackage() { - this.protectedAccess.analyze(method(PublicFactoryBean.class, "protectedTypeFactoryBean"), - Options.defaults().assignReturnType(true).build()); - assertThat(this.protectedAccess.getPrivilegedPackageName("com.example")) - .isEqualTo(PublicFactoryBean.class.getPackageName()); - } - - @Test - void getProtectedPackageWithProtectedAccessInSeveralPackages() { - Method protectedMethodFirstPackage = method(PublicFactoryBean.class, "protectedTypeFactoryBean"); - Method protectedMethodSecondPackage = method(ProtectedAccessor.class, "methodWithProtectedParameter", - ProtectedClass.class); - this.protectedAccess.analyze(protectedMethodFirstPackage, - Options.defaults().assignReturnType(true).build()); - this.protectedAccess.analyze(protectedMethodSecondPackage, DEFAULT_OPTIONS); - assertThatThrownBy(() -> this.protectedAccess.getPrivilegedPackageName("com.example")) - .isInstanceOfSatisfying(ProtectedAccessException.class, ex -> - assertThat(ex.getProtectedElements().stream().map(ProtectedElement::getMember)) - .containsOnly(protectedMethodFirstPackage, protectedMethodSecondPackage)); - } - - private void assertPrivilegedAccess(Class target) { - assertThat(this.protectedAccess.isAccessible("com.example")).isFalse(); - assertThat(this.protectedAccess.getPrivilegedPackageName("com.example")).isEqualTo(target.getPackageName()); - assertThat(this.protectedAccess.isAccessible(target.getPackageName())).isTrue(); - } - - private static Method method(Class type, String name, Class... parameterTypes) { - Method method = ReflectionUtils.findMethod(type, name, parameterTypes); - assertThat(method).isNotNull(); - return method; - } - - private static Field field(Class type, String name) { - Field field = ReflectionUtils.findField(type, name); - assertThat(field).isNotNull(); - return field; - } - - - @SuppressWarnings("unused") - public static class PublicClass { - - String protectedField; - - public ProtectedClass protectedClassField; - - String getProtectedMethod() { - return this.protectedField; - } - - } - - @SuppressWarnings("unused") - public static class ProtectedAccessor { - - ProtectedAccessor() { - } - - public String methodWithProtectedParameter(ProtectedClass type) { - return "test"; - } - - public ProtectedClass methodWithProtectedReturnType() { - return new ProtectedClass(); - } - } - - @SuppressWarnings("unused") - static class ProtectedClass { - - public ProtectedClass() { - } - - public String stringBean() { - return "public"; - } - - } - - static class SelfReference> { - - @SuppressWarnings("unchecked") - T getThis() { - return (T) this; - } - - } - -} diff --git a/spring-core/src/test/java/org/springframework/aot/generator/ResolvableTypeGeneratorTests.java b/spring-core/src/test/java/org/springframework/aot/generator/ResolvableTypeGeneratorTests.java deleted file mode 100644 index 34ab0f8671..0000000000 --- a/spring-core/src/test/java/org/springframework/aot/generator/ResolvableTypeGeneratorTests.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.aot.generator; - -import java.util.function.Function; -import java.util.function.Supplier; - -import org.junit.jupiter.api.Test; - -import org.springframework.core.ResolvableType; -import org.springframework.javapoet.support.CodeSnippet; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link ResolvableTypeGenerator}. - * - * @author Stephane Nicoll - */ -class ResolvableTypeGeneratorTests { - - @Test - void generateTypeForResolvableTypeWithGenericParameter() { - assertThat(generateTypeFor( - ResolvableType.forClassWithGenerics(Function.class, - ResolvableType.forClassWithGenerics(Supplier.class, String.class), - ResolvableType.forClassWithGenerics(Supplier.class, Integer.class)))) - .isEqualTo("ResolvableType.forClassWithGenerics(Function.class, " - + "ResolvableType.forClassWithGenerics(Supplier.class, String.class), " - + "ResolvableType.forClassWithGenerics(Supplier.class, Integer.class))"); - } - - @Test - void generateTypeForResolvableTypeWithMixedParameter() { - assertThat(generateTypeFor( - ResolvableType.forClassWithGenerics(Function.class, - ResolvableType.forClassWithGenerics(Supplier.class, String.class), - ResolvableType.forClass(Integer.class)))) - .isEqualTo("ResolvableType.forClassWithGenerics(Function.class, " - + "ResolvableType.forClassWithGenerics(Supplier.class, String.class), " - + "ResolvableType.forClass(Integer.class))"); - } - - private String generateTypeFor(ResolvableType type) { - return CodeSnippet.process(new ResolvableTypeGenerator().generateTypeFor(type)); - } - -} diff --git a/spring-core/src/test/java/org/springframework/javapoet/support/CodeSnippetTests.java b/spring-core/src/test/java/org/springframework/javapoet/support/CodeSnippetTests.java deleted file mode 100644 index d11383f2db..0000000000 --- a/spring-core/src/test/java/org/springframework/javapoet/support/CodeSnippetTests.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.javapoet.support; - -import java.util.ArrayList; -import java.util.List; - -import org.junit.jupiter.api.Test; - -import org.springframework.javapoet.CodeBlock; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link CodeSnippet}. - * - * @author Stephane Nicoll - */ -class CodeSnippetTests { - - @Test - void snippetUsesTabs() { - CodeBlock.Builder code = CodeBlock.builder(); - code.beginControlFlow("if (condition)"); - code.addStatement("bean.doThis()"); - code.endControlFlow(); - CodeSnippet codeSnippet = CodeSnippet.of(code.build()); - assertThat(codeSnippet.getSnippet()).isEqualTo(""" - if (condition) { - bean.doThis(); - } - """); - } - - @Test - void snippetResolvesImports() { - CodeSnippet codeSnippet = CodeSnippet.of( - CodeBlock.of("$T list = new $T<>()", List.class, ArrayList.class)); - assertThat(codeSnippet.getSnippet()).isEqualTo("List list = new ArrayList<>()"); - assertThat(codeSnippet.hasImport(List.class)).isTrue(); - assertThat(codeSnippet.hasImport(ArrayList.class)).isTrue(); - } - - @Test - void removeIndent() { - CodeBlock.Builder code = CodeBlock.builder(); - code.beginControlFlow("if (condition)"); - code.addStatement("doStuff()"); - code.endControlFlow(); - CodeSnippet snippet = CodeSnippet.of(code.build()); - assertThat(snippet.getSnippet().lines()).contains("\tdoStuff();"); - assertThat(snippet.removeIndent(1).getSnippet().lines()).contains("doStuff();"); - } - - @Test - void processProvidesSnippet() { - assertThat(CodeSnippet.process(code -> code.add("$T list;", List.class))) - .isEqualTo("List list;"); - } - -} diff --git a/spring-core/src/test/java/org/springframework/javapoet/support/MultiCodeBlockTests.java b/spring-core/src/test/java/org/springframework/javapoet/support/MultiCodeBlockTests.java deleted file mode 100644 index 603aa10d3e..0000000000 --- a/spring-core/src/test/java/org/springframework/javapoet/support/MultiCodeBlockTests.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.javapoet.support; - -import org.junit.jupiter.api.Test; - -import org.springframework.javapoet.CodeBlock; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; - -/** - * Tests for {@link MultiCodeBlock}. - * - * @author Stephane Nicoll - */ -class MultiCodeBlockTests { - - @Test - void joinWithNoElement() { - MultiCodeBlock multi = new MultiCodeBlock(); - assertThat(multi.join(", ").isEmpty()).isTrue(); - } - - @Test - void joinWithEmptyElement() { - MultiCodeBlock multi = new MultiCodeBlock(); - assertThatIllegalArgumentException().isThrownBy(() -> multi.add(CodeBlock.builder().build())); - } - - @Test - void joinWithSingleElement() { - MultiCodeBlock multi = new MultiCodeBlock(); - multi.add(CodeBlock.of("$S", "Hello")); - assertThat(multi.join(", ")).hasToString("\"Hello\""); - } - - @Test - void joinWithSeveralElement() { - MultiCodeBlock multi = new MultiCodeBlock(); - multi.add(CodeBlock.of("$S", "Hello")); - multi.add(code -> code.add("42")); - multi.add("null"); - assertThat(multi.join(", ")).hasToString("\"Hello\", 42, null"); - } - -} diff --git a/spring-core/src/test/java/org/springframework/javapoet/support/MultiStatementTests.java b/spring-core/src/test/java/org/springframework/javapoet/support/MultiStatementTests.java deleted file mode 100644 index 3f74ffde16..0000000000 --- a/spring-core/src/test/java/org/springframework/javapoet/support/MultiStatementTests.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.javapoet.support; - -import java.util.List; - -import org.junit.jupiter.api.Test; - -import org.springframework.javapoet.CodeBlock; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link MultiStatement}. - * - * @author Stephane Nicoll - */ -class MultiStatementTests { - - @Test - void isEmptyWithNoStatement() { - assertThat(new MultiStatement().isEmpty()).isTrue(); - } - - @Test - void isEmptyWithStatement() { - MultiStatement statements = new MultiStatement(); - statements.addStatement(CodeBlock.of("int i = 0")); - assertThat(statements.isEmpty()).isFalse(); - } - - @Test - void singleStatementCodeBlock() { - MultiStatement statements = new MultiStatement(); - statements.addStatement("field.method($S)", "hello"); - CodeBlock codeBlock = statements.toCodeBlock(); - assertThat(codeBlock.toString()).isEqualTo(""" - field.method("hello"); - """); - } - - @Test - void multiStatementsCodeBlock() { - MultiStatement statements = new MultiStatement(); - statements.addStatement("field.method($S)", "hello"); - statements.addStatement("field.another($S)", "test"); - CodeBlock codeBlock = statements.toCodeBlock(); - assertThat(codeBlock.toString()).isEqualTo(""" - field.method("hello"); - field.another("test"); - """); - } - - @Test - void singleStatementLambdaBody() { - MultiStatement statements = new MultiStatement(); - statements.addStatement("field.method($S)", "hello"); - CodeBlock codeBlock = statements.toLambdaBody(); - assertThat(codeBlock.toString()).isEqualTo("field.method(\"hello\")"); - } - - @Test - void singleStatementWithCallbackLambdaBody() { - MultiStatement statements = new MultiStatement(); - statements.addStatement(code -> code.add("field.method($S)", "hello")); - CodeBlock codeBlock = statements.toLambdaBody(); - assertThat(codeBlock.toString()).isEqualTo("field.method(\"hello\")"); - } - - @Test - void singleStatementWithCodeBlockLambdaBody() { - MultiStatement statements = new MultiStatement(); - statements.addStatement(CodeBlock.of("field.method($S)", "hello")); - CodeBlock codeBlock = statements.toLambdaBody(); - assertThat(codeBlock.toString()).isEqualTo("field.method(\"hello\")"); - } - - @Test - void multiStatementsLambdaBody() { - MultiStatement statements = new MultiStatement(); - statements.addStatement("field.method($S)", "hello"); - statements.addStatement("field.anotherMethod($S)", "hello"); - CodeBlock codeBlock = statements.toLambdaBody(); - assertThat(codeBlock.toString()).isEqualTo(""" - field.method("hello"); - field.anotherMethod("hello");"""); - } - - @Test - void multiStatementsWithCodeBlockRenderedAsIsLambdaBody() { - MultiStatement statements = new MultiStatement(); - statements.addStatement("field.method($S)", "hello"); - statements.add(CodeBlock.of(("// Hello\n"))); - statements.add(code -> code.add("// World\n")); - statements.addStatement("field.anotherMethod($S)", "hello"); - CodeBlock codeBlock = statements.toLambdaBody(); - assertThat(codeBlock.toString()).isEqualTo(""" - field.method("hello"); - // Hello - // World - field.anotherMethod("hello");"""); - } - - @Test - void singleStatementWithLambda() { - MultiStatement statements = new MultiStatement(); - statements.addStatement("field.method($S)", "hello"); - CodeBlock codeBlock = statements.toLambda(CodeBlock.of("() ->")); - assertThat(codeBlock.toString()).isEqualTo("() -> field.method(\"hello\")"); - } - - @Test - void multiStatementsWithLambda() { - MultiStatement statements = new MultiStatement(); - statements.addStatement("field.method($S)", "hello"); - statements.addStatement("field.anotherMethod($S)", "hello"); - CodeBlock codeBlock = statements.toLambda(CodeBlock.of("() ->")); - assertThat(codeBlock.toString().lines()).containsExactly( - "() -> {", - " field.method(\"hello\");", - " field.anotherMethod(\"hello\");", - "}"); - } - - @Test - void multiStatementsWithAddAllAndLambda() { - MultiStatement statements = new MultiStatement(); - statements.addAll(List.of(0, 1, 2), - index -> CodeBlock.of("field[$L] = $S", index, "hello")); - CodeBlock codeBlock = statements.toLambda("() ->"); - assertThat(codeBlock.toString().lines()).containsExactly( - "() -> {", - " field[0] = \"hello\";", - " field[1] = \"hello\";", - " field[2] = \"hello\";", - "}"); - } - - @Test - void addWithAnotherMultiStatement() { - MultiStatement statements = new MultiStatement(); - statements.addStatement(CodeBlock.of("test.invoke()")); - MultiStatement another = new MultiStatement(); - another.addStatement(CodeBlock.of("test.another()")); - statements.add(another); - assertThat(statements.toCodeBlock().toString()).isEqualTo(""" - test.invoke(); - test.another(); - """); - } - -} diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/support/PersistenceAnnotationBeanPostProcessor.java b/spring-orm/src/main/java/org/springframework/orm/jpa/support/PersistenceAnnotationBeanPostProcessor.java index a3e9cbe2c5..68b4a178ed 100644 --- a/spring-orm/src/main/java/org/springframework/orm/jpa/support/PersistenceAnnotationBeanPostProcessor.java +++ b/spring-orm/src/main/java/org/springframework/orm/jpa/support/PersistenceAnnotationBeanPostProcessor.java @@ -19,7 +19,6 @@ package org.springframework.orm.jpa.support; import java.beans.PropertyDescriptor; import java.io.Serializable; import java.lang.reflect.AnnotatedElement; -import java.lang.reflect.Field; import java.lang.reflect.Member; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -46,8 +45,6 @@ import org.springframework.aot.generate.GenerationContext; import org.springframework.aot.generate.MethodGenerator; import org.springframework.aot.generate.MethodNameGenerator; import org.springframework.aot.generate.MethodReference; -import org.springframework.aot.generator.CodeContribution; -import org.springframework.aot.generator.ProtectedAccess.Options; import org.springframework.aot.hint.RuntimeHints; import org.springframework.beans.BeanUtils; import org.springframework.beans.PropertyValues; @@ -66,9 +63,6 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor; import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor; import org.springframework.beans.factory.config.NamedBeanHolder; -import org.springframework.beans.factory.generator.AotContributingBeanPostProcessor; -import org.springframework.beans.factory.generator.BeanFieldGenerator; -import org.springframework.beans.factory.generator.BeanInstantiationContribution; import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor; import org.springframework.beans.factory.support.RegisteredBean; import org.springframework.beans.factory.support.RootBeanDefinition; @@ -81,7 +75,6 @@ import org.springframework.javapoet.CodeBlock; import org.springframework.javapoet.JavaFile; import org.springframework.javapoet.MethodSpec; import org.springframework.javapoet.TypeSpec; -import org.springframework.javapoet.support.MultiStatement; import org.springframework.jndi.JndiLocatorDelegate; import org.springframework.jndi.JndiTemplate; import org.springframework.lang.Nullable; @@ -198,9 +191,8 @@ import org.springframework.util.StringUtils; * @see jakarta.persistence.PersistenceContext */ @SuppressWarnings("serial") -public class PersistenceAnnotationBeanPostProcessor - implements InstantiationAwareBeanPostProcessor, DestructionAwareBeanPostProcessor, - MergedBeanDefinitionPostProcessor, AotContributingBeanPostProcessor, BeanRegistrationAotProcessor, +public class PersistenceAnnotationBeanPostProcessor implements InstantiationAwareBeanPostProcessor, + DestructionAwareBeanPostProcessor, MergedBeanDefinitionPostProcessor, BeanRegistrationAotProcessor, PriorityOrdered, BeanFactoryAware, Serializable { @Nullable @@ -365,16 +357,6 @@ public class PersistenceAnnotationBeanPostProcessor findInjectionMetadata(beanDefinition, beanType, beanName); } - @Override - public BeanInstantiationContribution contribute(RootBeanDefinition beanDefinition, Class beanType, String beanName) { - InjectionMetadata metadata = findInjectionMetadata(beanDefinition, beanType, beanName); - Collection injectedElements = metadata.getInjectedElements(); - if (!CollectionUtils.isEmpty(injectedElements)) { - return new PersistenceAnnotationBeanInstantiationContribution(injectedElements); - } - return null; - } - @Override public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) { Class beanClass = registeredBean.getBeanClass(); @@ -783,68 +765,6 @@ public class PersistenceAnnotationBeanPostProcessor } } - private static final class PersistenceAnnotationBeanInstantiationContribution implements BeanInstantiationContribution { - - private static final BeanFieldGenerator fieldGenerator = new BeanFieldGenerator(); - - private final Collection injectedElements; - - private PersistenceAnnotationBeanInstantiationContribution(Collection injectedElements) { - this.injectedElements = injectedElements.stream() - .filter(obj -> obj instanceof PersistenceElement) - .map(PersistenceElement.class::cast).toList(); - } - - @Override - public void applyTo(CodeContribution contribution) { - this.injectedElements.forEach(element -> { - Member member = element.getMember(); - analyzeMember(contribution, member); - injectElement(contribution, element); - }); - } - - private void analyzeMember(CodeContribution contribution, Member member) { - if (member instanceof Method) { - contribution.protectedAccess().analyze(member, Options.defaults().build()); - } - else if (member instanceof Field field) { - contribution.protectedAccess().analyze(member, BeanFieldGenerator.FIELD_OPTIONS); - if (Modifier.isPrivate(field.getModifiers())) { - contribution.runtimeHints().reflection().registerField(field); - } - } - } - - private void injectElement(CodeContribution contribution, PersistenceElement element) { - MultiStatement statements = contribution.statements(); - statements.addStatement("$T entityManagerFactory = $T.findEntityManagerFactory(beanFactory, $S)", - EntityManagerFactory.class, EntityManagerFactoryUtils.class, element.unitName); - boolean requireEntityManager = (element.type != null); - if (requireEntityManager) { - Properties persistenceProperties = element.properties; - boolean hasPersistenceProperties = persistenceProperties != null && !persistenceProperties.isEmpty(); - if (hasPersistenceProperties) { - statements.addStatement("$T persistenceProperties = new Properties()", Properties.class); - persistenceProperties.stringPropertyNames().stream().sorted(String::compareTo).forEach(propertyName -> - statements.addStatement("persistenceProperties.put($S, $S)", - propertyName, persistenceProperties.getProperty(propertyName))); - } - statements.addStatement("$T entityManager = $T.createSharedEntityManager(entityManagerFactory, $L, $L)", - EntityManager.class, SharedEntityManagerCreator.class, (hasPersistenceProperties) ? "persistenceProperties" : null, element.synchronizedWithTransaction); - } - Member member = element.getMember(); - CodeBlock value = (requireEntityManager) ? CodeBlock.of("entityManager") : CodeBlock.of("entityManagerFactory"); - if (member instanceof Field field) { - statements.add(fieldGenerator.generateSetValue("bean", field, value)); - } - else { - statements.addStatement("bean.$L($L)", member.getName(), value); - } - } - - } - private static class AotContribution implements BeanRegistrationAotContribution { diff --git a/spring-orm/src/test/java/org/springframework/orm/jpa/support/PersistenceAnnotationBeanPostProcessorTests.java b/spring-orm/src/test/java/org/springframework/orm/jpa/support/PersistenceAnnotationBeanPostProcessorTests.java deleted file mode 100644 index aabb089314..0000000000 --- a/spring-orm/src/test/java/org/springframework/orm/jpa/support/PersistenceAnnotationBeanPostProcessorTests.java +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.orm.jpa.support; - -import java.util.function.Consumer; -import java.util.function.Supplier; - -import jakarta.persistence.EntityManager; -import jakarta.persistence.EntityManagerFactory; -import jakarta.persistence.PersistenceContext; -import jakarta.persistence.PersistenceProperty; -import jakarta.persistence.PersistenceUnit; -import org.junit.jupiter.api.Test; - -import org.springframework.aot.generator.CodeContribution; -import org.springframework.aot.generator.DefaultCodeContribution; -import org.springframework.aot.generator.DefaultGeneratedTypeContext; -import org.springframework.aot.generator.GeneratedType; -import org.springframework.aot.hint.RuntimeHints; -import org.springframework.aot.hint.TypeReference; -import org.springframework.aot.test.generator.compile.CompileWithTargetClassAccess; -import org.springframework.aot.test.generator.compile.TestCompiler; -import org.springframework.aot.test.generator.file.SourceFile; -import org.springframework.aot.test.generator.file.SourceFiles; -import org.springframework.beans.factory.generator.BeanInstantiationContribution; -import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.context.ApplicationContextInitializer; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import org.springframework.context.generator.ApplicationContextAotGenerator; -import org.springframework.context.support.GenericApplicationContext; -import org.springframework.javapoet.ClassName; -import org.springframework.javapoet.JavaFile; -import org.springframework.javapoet.support.CodeSnippet; -import org.springframework.lang.Nullable; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; - -/** - * Tests for {@link PersistenceAnnotationBeanPostProcessor}. - * - * @author Stephane Nicoll - */ -class PersistenceAnnotationBeanPostProcessorTests { - - @Test - void contributeForPersistenceUnitOnPublicField() { - CodeContribution contribution = contribute(DefaultPersistenceUnitField.class); - assertThat(contribution).isNotNull(); - assertThat(contribution.runtimeHints().reflection().typeHints()).isEmpty(); - assertThat(CodeSnippet.process(contribution.statements().toCodeBlock())).isEqualTo(""" - EntityManagerFactory entityManagerFactory = EntityManagerFactoryUtils.findEntityManagerFactory(beanFactory, ""); - bean.emf = entityManagerFactory; - """); - } - - @Test - void contributeForPersistenceUnitOnPublicSetter() { - CodeContribution contribution = contribute(DefaultPersistenceUnitMethod.class); - assertThat(contribution).isNotNull(); - assertThat(contribution.runtimeHints().reflection().typeHints()).isEmpty(); - assertThat(CodeSnippet.process(contribution.statements().toCodeBlock())).isEqualTo(""" - EntityManagerFactory entityManagerFactory = EntityManagerFactoryUtils.findEntityManagerFactory(beanFactory, ""); - bean.setEmf(entityManagerFactory); - """); - } - - @Test - void contributeForPersistenceUnitWithCustomUnitOnPublicSetter() { - CodeContribution contribution = contribute(CustomUnitNamePublicPersistenceUnitMethod.class); - assertThat(contribution).isNotNull(); - assertThat(contribution.runtimeHints().reflection().typeHints()).isEmpty(); - assertThat(CodeSnippet.process(contribution.statements().toCodeBlock())).isEqualTo(""" - EntityManagerFactory entityManagerFactory = EntityManagerFactoryUtils.findEntityManagerFactory(beanFactory, "custom"); - bean.setEmf(entityManagerFactory); - """); - } - - @Test - void contributeForPersistenceContextOnPrivateField() { - CodeContribution contribution = contribute(DefaultPersistenceContextField.class); - assertThat(contribution).isNotNull(); - assertThat(contribution.runtimeHints().reflection().typeHints()).singleElement().satisfies(typeHint -> { - assertThat(typeHint.getType()).isEqualTo(TypeReference.of(DefaultPersistenceContextField.class)); - assertThat(typeHint.fields()).singleElement().satisfies(fieldHint -> { - assertThat(fieldHint.getName()).isEqualTo("entityManager"); - assertThat(fieldHint.isAllowWrite()).isTrue(); - assertThat(fieldHint.isAllowUnsafeAccess()).isFalse(); - }); - }); - assertThat(CodeSnippet.process(contribution.statements().toCodeBlock())).isEqualTo(""" - EntityManagerFactory entityManagerFactory = EntityManagerFactoryUtils.findEntityManagerFactory(beanFactory, ""); - EntityManager entityManager = SharedEntityManagerCreator.createSharedEntityManager(entityManagerFactory, null, true); - Field entityManagerField = ReflectionUtils.findField(PersistenceAnnotationBeanPostProcessorTests.DefaultPersistenceContextField.class, "entityManager"); - ReflectionUtils.makeAccessible(entityManagerField); - ReflectionUtils.setField(entityManagerField, bean, entityManager); - """); - } - - @Test - void contributeForPersistenceContextWithCustomPropertiesOnMethod() { - CodeContribution contribution = contribute(CustomPropertiesPersistenceContextMethod.class); - assertThat(contribution).isNotNull(); - assertThat(contribution.runtimeHints().reflection().typeHints()).isEmpty(); - assertThat(CodeSnippet.process(contribution.statements().toCodeBlock())).isEqualTo(""" - EntityManagerFactory entityManagerFactory = EntityManagerFactoryUtils.findEntityManagerFactory(beanFactory, ""); - Properties persistenceProperties = new Properties(); - persistenceProperties.put("jpa.test", "value"); - persistenceProperties.put("jpa.test2", "value2"); - EntityManager entityManager = SharedEntityManagerCreator.createSharedEntityManager(entityManagerFactory, persistenceProperties, true); - bean.setEntityManager(entityManager); - """); - } - - @Test - @CompileWithTargetClassAccess(classes = DefaultPersistenceUnitField.class) - void generateEntityManagerFactoryInjection() { - GenericApplicationContext context = new AnnotationConfigApplicationContext(); - context.registerBeanDefinition("test", new RootBeanDefinition(DefaultPersistenceUnitField.class)); - - EntityManagerFactory entityManagerFactory = mock(EntityManagerFactory.class); - compile(context, toFreshApplicationContext(() -> { - GenericApplicationContext ctx = new GenericApplicationContext(); - ctx.getDefaultListableBeanFactory().registerSingleton("myEmf", entityManagerFactory); - return ctx; - }, aotContext -> assertThat(aotContext.getBean("test")).hasFieldOrPropertyWithValue("emf", entityManagerFactory))); - } - - private DefaultCodeContribution contribute(Class type) { - BeanInstantiationContribution contributor = createContribution(type); - assertThat(contributor).isNotNull(); - DefaultCodeContribution contribution = new DefaultCodeContribution(new RuntimeHints()); - contributor.applyTo(contribution); - return contribution; - } - - @Nullable - private BeanInstantiationContribution createContribution(Class beanType) { - PersistenceAnnotationBeanPostProcessor bpp = new PersistenceAnnotationBeanPostProcessor(); - RootBeanDefinition beanDefinition = new RootBeanDefinition(beanType); - return bpp.contribute(beanDefinition, beanType, "test"); - } - - @SuppressWarnings("rawtypes") - private void compile(GenericApplicationContext applicationContext, Consumer initializer) { - DefaultGeneratedTypeContext generationContext = new DefaultGeneratedTypeContext("com.example", - packageName -> GeneratedType.of(ClassName.get(packageName, "Test"))); - ApplicationContextAotGenerator generator = new ApplicationContextAotGenerator(); - generator.generateApplicationContext(applicationContext, generationContext); - SourceFiles sourceFiles = SourceFiles.none(); - for (JavaFile javaFile : generationContext.toJavaFiles()) { - sourceFiles = sourceFiles.and(SourceFile.of((javaFile::writeTo))); - } - TestCompiler.forSystem().withSources(sourceFiles).compile(compiled -> { - ApplicationContextInitializer instance = compiled.getInstance(ApplicationContextInitializer.class, "com.example.Test"); - initializer.accept(instance); - }); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - private Consumer toFreshApplicationContext( - Supplier applicationContextFactory, Consumer context) { - return applicationContextInitializer -> { - T applicationContext = applicationContextFactory.get(); - applicationContextInitializer.initialize(applicationContext); - applicationContext.refresh(); - context.accept(applicationContext); - }; - } - - - static class DefaultPersistenceUnitField { - - @PersistenceUnit - public EntityManagerFactory emf; - - } - - static class DefaultPersistenceUnitMethod { - - @PersistenceUnit - public void setEmf(EntityManagerFactory emf) { - } - - } - - static class CustomUnitNamePublicPersistenceUnitMethod { - - @PersistenceUnit(unitName = "custom") - public void setEmf(EntityManagerFactory emf) { - } - - } - - static class DefaultPersistenceContextField { - - @PersistenceContext - private EntityManager entityManager; - - } - - static class CustomPropertiesPersistenceContextMethod { - - @PersistenceContext(properties = { - @PersistenceProperty(name = "jpa.test", value = "value"), - @PersistenceProperty(name = "jpa.test2", value = "value2") }) - public void setEntityManager(EntityManager entityManager) { - - } - - } - -}