diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java index 88547dea04b..a2645e6275d 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java @@ -823,11 +823,11 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac /** * This implementation attempts to query the FactoryBean's generic parameter metadata * if present to determine the object type. If not present, i.e. the FactoryBean is - * declared as a raw type, checks the FactoryBean's {@code getObjectType} method + * declared as a raw type, it checks the FactoryBean's {@code getObjectType} method * on a plain instance of the FactoryBean, without bean properties applied yet. - * If this doesn't return a type yet, and {@code allowInit} is {@code true} a - * full creation of the FactoryBean is used as fallback (through delegation to the - * superclass's implementation). + * If this doesn't return a type yet and {@code allowInit} is {@code true}, full + * creation of the FactoryBean is attempted as fallback (through delegation to the + * superclass implementation). *

The shortcut check for a FactoryBean is only applied in case of a singleton * FactoryBean. If the FactoryBean instance itself is not kept as singleton, * it will be fully created to check the type of its exposed object. diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java index 20b6af7a6f4..660b421ef74 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java @@ -1682,7 +1682,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp * already. The implementation is allowed to instantiate the target factory bean if * {@code allowInit} is {@code true} and the type cannot be determined another way; * otherwise it is restricted to introspecting signatures and related metadata. - *

If no {@link FactoryBean#OBJECT_TYPE_ATTRIBUTE} if set on the bean definition + *

If no {@link FactoryBean#OBJECT_TYPE_ATTRIBUTE} is set on the bean definition * and {@code allowInit} is {@code true}, the default implementation will create * the FactoryBean via {@code getBean} to call its {@code getObjectType} method. * Subclasses are encouraged to optimize this, typically by inspecting the generic diff --git a/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationWithFactoryBeanBeanEarlyDeductionTests.java b/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationWithFactoryBeanEarlyDeductionTests.java similarity index 75% rename from spring-context/src/test/java/org/springframework/context/annotation/ConfigurationWithFactoryBeanBeanEarlyDeductionTests.java rename to spring-context/src/test/java/org/springframework/context/annotation/ConfigurationWithFactoryBeanEarlyDeductionTests.java index 1c5a4bb146f..28a803f60ad 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationWithFactoryBeanBeanEarlyDeductionTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationWithFactoryBeanEarlyDeductionTests.java @@ -29,7 +29,9 @@ import org.springframework.beans.factory.support.AbstractBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.GenericBeanDefinition; +import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.support.GenericApplicationContext; +import org.springframework.core.ResolvableType; import org.springframework.core.type.AnnotationMetadata; import static org.assertj.core.api.Assertions.assertThat; @@ -39,8 +41,9 @@ import static org.assertj.core.api.Assertions.assertThat; * {@link FactoryBean FactoryBeans} defined in the configuration. * * @author Phillip Webb + * @author Juergen Hoeller */ -class ConfigurationWithFactoryBeanBeanEarlyDeductionTests { +class ConfigurationWithFactoryBeanEarlyDeductionTests { @Test void preFreezeDirect() { @@ -82,6 +85,16 @@ class ConfigurationWithFactoryBeanBeanEarlyDeductionTests { assertPostFreeze(AttributeClassConfiguration.class); } + @Test + void preFreezeTargetType() { + assertPreFreeze(TargetTypeConfiguration.class); + } + + @Test + void postFreezeTargetType() { + assertPostFreeze(TargetTypeConfiguration.class); + } + @Test void preFreezeUnresolvedGenericFactoryBean() { // Covers the case where a @Configuration is picked up via component scanning @@ -105,14 +118,13 @@ class ConfigurationWithFactoryBeanBeanEarlyDeductionTests { } } + private void assertPostFreeze(Class configurationClass) { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( - configurationClass); + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(configurationClass); assertContainsMyBeanName(context); } - private void assertPreFreeze(Class configurationClass, - BeanFactoryPostProcessor... postProcessors) { + private void assertPreFreeze(Class configurationClass, BeanFactoryPostProcessor... postProcessors) { NameCollectingBeanFactoryPostProcessor postProcessor = new NameCollectingBeanFactoryPostProcessor(); AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); try (context) { @@ -132,41 +144,38 @@ class ConfigurationWithFactoryBeanBeanEarlyDeductionTests { assertThat(names).containsExactly("myBean"); } - private static class NameCollectingBeanFactoryPostProcessor - implements BeanFactoryPostProcessor { + + private static class NameCollectingBeanFactoryPostProcessor implements BeanFactoryPostProcessor { private String[] names; @Override - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) - throws BeansException { - this.names = beanFactory.getBeanNamesForType(MyBean.class, true, false); + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { + ResolvableType typeToMatch = ResolvableType.forClassWithGenerics(MyBean.class, String.class); + this.names = beanFactory.getBeanNamesForType(typeToMatch, true, false); } public String[] getNames() { return this.names; } - } @Configuration static class DirectConfiguration { @Bean - MyBean myBean() { - return new MyBean(); + MyBean myBean() { + return new MyBean<>(); } - } @Configuration static class GenericMethodConfiguration { @Bean - FactoryBean myBean() { - return new TestFactoryBean<>(new MyBean()); + FactoryBean> myBean() { + return new TestFactoryBean<>(new MyBean<>()); } - } @Configuration @@ -176,13 +185,11 @@ class ConfigurationWithFactoryBeanBeanEarlyDeductionTests { MyFactoryBean myBean() { return new MyFactoryBean(); } - } @Configuration @Import(AttributeClassRegistrar.class) static class AttributeClassConfiguration { - } static class AttributeClassRegistrar implements ImportBeanDefinitionRegistrar { @@ -191,16 +198,32 @@ class ConfigurationWithFactoryBeanBeanEarlyDeductionTests { public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { BeanDefinition definition = BeanDefinitionBuilder.genericBeanDefinition( RawWithAbstractObjectTypeFactoryBean.class).getBeanDefinition(); - definition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, MyBean.class); + definition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, + ResolvableType.forClassWithGenerics(MyBean.class, String.class)); registry.registerBeanDefinition("myBean", definition); } + } + @Configuration + @Import(TargetTypeRegistrar.class) + static class TargetTypeConfiguration { + } + + static class TargetTypeRegistrar implements ImportBeanDefinitionRegistrar { + + @Override + public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { + RootBeanDefinition definition = new RootBeanDefinition(RawWithAbstractObjectTypeFactoryBean.class); + definition.setTargetType(ResolvableType.forClassWithGenerics(FactoryBean.class, + ResolvableType.forClassWithGenerics(MyBean.class, String.class))); + registry.registerBeanDefinition("myBean", definition); + } } abstract static class AbstractMyBean { } - static class MyBean extends AbstractMyBean { + static class MyBean extends AbstractMyBean { } static class TestFactoryBean implements FactoryBean { @@ -220,31 +243,26 @@ class ConfigurationWithFactoryBeanBeanEarlyDeductionTests { public Class getObjectType() { return this.instance.getClass(); } - } - static class MyFactoryBean extends TestFactoryBean { + static class MyFactoryBean extends TestFactoryBean> { public MyFactoryBean() { - super(new MyBean()); + super(new MyBean<>()); } - } static class RawWithAbstractObjectTypeFactoryBean implements FactoryBean { - private final Object object = new MyBean(); - @Override - public Object getObject() { - return object; + public Object getObject() throws Exception { + throw new IllegalStateException(); } @Override public Class getObjectType() { return MyBean.class; } - } } diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/support/rowset/ResultSetWrappingRowSetTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/support/rowset/ResultSetWrappingRowSetTests.java index 44823b0ff32..7ead1f32cc0 100644 --- a/spring-jdbc/src/test/java/org/springframework/jdbc/support/rowset/ResultSetWrappingRowSetTests.java +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/support/rowset/ResultSetWrappingRowSetTests.java @@ -39,9 +39,9 @@ import static org.mockito.Mockito.mock; */ class ResultSetWrappingRowSetTests { - private ResultSet resultSet = mock(); + private final ResultSet resultSet = mock(); - private ResultSetWrappingSqlRowSet rowSet = new ResultSetWrappingSqlRowSet(resultSet); + private final ResultSetWrappingSqlRowSet rowSet = new ResultSetWrappingSqlRowSet(resultSet); @Test @@ -198,6 +198,7 @@ class ResultSetWrappingRowSetTests { doTest(rset, rowset, "test", true); } + private void doTest(Method rsetMethod, Method rowsetMethod, Object arg, Object ret) throws Exception { if (arg instanceof String) { given(resultSet.findColumn((String) arg)).willReturn(1); @@ -207,9 +208,9 @@ class ResultSetWrappingRowSetTests { given(rsetMethod.invoke(resultSet, arg)).willReturn(ret).willThrow(new SQLException("test")); } rowsetMethod.invoke(rowSet, arg); - assertThatExceptionOfType(InvocationTargetException.class).isThrownBy(() -> - rowsetMethod.invoke(rowSet, arg)). - satisfies(ex -> assertThat(ex.getTargetException()).isExactlyInstanceOf(InvalidResultSetAccessException.class)); + assertThatExceptionOfType(InvocationTargetException.class) + .isThrownBy(() -> rowsetMethod.invoke(rowSet, arg)) + .satisfies(ex -> assertThat(ex.getTargetException()).isExactlyInstanceOf(InvalidResultSetAccessException.class)); } } diff --git a/spring-tx/src/main/java/org/springframework/transaction/interceptor/DefaultTransactionAttribute.java b/spring-tx/src/main/java/org/springframework/transaction/interceptor/DefaultTransactionAttribute.java index 148a9b0f550..0eaf43a7308 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/interceptor/DefaultTransactionAttribute.java +++ b/spring-tx/src/main/java/org/springframework/transaction/interceptor/DefaultTransactionAttribute.java @@ -51,7 +51,7 @@ public class DefaultTransactionAttribute extends DefaultTransactionDefinition im /** - * Create a new DefaultTransactionAttribute, with default settings. + * Create a new {@code DefaultTransactionAttribute} with default settings. * Can be modified through bean property setters. * @see #setPropagationBehavior * @see #setIsolationLevel @@ -75,7 +75,7 @@ public class DefaultTransactionAttribute extends DefaultTransactionDefinition im } /** - * Create a new DefaultTransactionAttribute with the given + * Create a new {@code DefaultTransactionAttribute} with the given * propagation behavior. Can be modified through bean property setters. * @param propagationBehavior one of the propagation constants in the * TransactionDefinition interface diff --git a/spring-tx/src/main/java/org/springframework/transaction/reactive/AbstractReactiveTransactionManager.java b/spring-tx/src/main/java/org/springframework/transaction/reactive/AbstractReactiveTransactionManager.java index d29b380b0fb..6efe444555c 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/reactive/AbstractReactiveTransactionManager.java +++ b/spring-tx/src/main/java/org/springframework/transaction/reactive/AbstractReactiveTransactionManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -234,11 +234,13 @@ public abstract class AbstractReactiveTransactionManager prepareSynchronization(synchronizationManager, status, definition)).thenReturn(status); } - // Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED. + // PROPAGATION_REQUIRED, PROPAGATION_SUPPORTS, PROPAGATION_MANDATORY: + // regular participation in existing transaction. if (debugEnabled) { logger.debug("Participating in existing transaction"); } - return Mono.just(prepareReactiveTransaction(synchronizationManager, definition, transaction, false, debugEnabled, null)); + return Mono.just(prepareReactiveTransaction( + synchronizationManager, definition, transaction, false, debugEnabled, null)); } /** diff --git a/spring-tx/src/main/java/org/springframework/transaction/support/AbstractPlatformTransactionManager.java b/spring-tx/src/main/java/org/springframework/transaction/support/AbstractPlatformTransactionManager.java index 0ada2671fb7..8da5bab1185 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/support/AbstractPlatformTransactionManager.java +++ b/spring-tx/src/main/java/org/springframework/transaction/support/AbstractPlatformTransactionManager.java @@ -491,7 +491,8 @@ public abstract class AbstractPlatformTransactionManager } } - // Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED. + // PROPAGATION_REQUIRED, PROPAGATION_SUPPORTS, PROPAGATION_MANDATORY: + // regular participation in existing transaction. if (debugEnabled) { logger.debug("Participating in existing transaction"); } diff --git a/spring-tx/src/main/java/org/springframework/transaction/support/DefaultTransactionDefinition.java b/spring-tx/src/main/java/org/springframework/transaction/support/DefaultTransactionDefinition.java index 8b505392416..4faabde754e 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/support/DefaultTransactionDefinition.java +++ b/spring-tx/src/main/java/org/springframework/transaction/support/DefaultTransactionDefinition.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -90,7 +90,7 @@ public class DefaultTransactionDefinition implements TransactionDefinition, Seri /** - * Create a new DefaultTransactionDefinition, with default settings. + * Create a new {@code DefaultTransactionDefinition} with default settings. * Can be modified through bean property setters. * @see #setPropagationBehavior * @see #setIsolationLevel @@ -118,7 +118,7 @@ public class DefaultTransactionDefinition implements TransactionDefinition, Seri } /** - * Create a new DefaultTransactionDefinition with the given + * Create a new {@code DefaultTransactionDefinition} with the given * propagation behavior. Can be modified through bean property setters. * @param propagationBehavior one of the propagation constants in the * TransactionDefinition interface