From 20736bd06f70825136ff9837f3ef022d4a3f01e7 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 25 Mar 2025 17:06:28 +0100 Subject: [PATCH 1/3] Introduce acknowledgeAfterListener flag for custom acknowledge handling Closes gh-34635 --- .../AbstractJmsListenerContainerFactory.java | 17 ++++++- .../AbstractMessageListenerContainer.java | 51 +++++++++++++++---- 2 files changed, 56 insertions(+), 12 deletions(-) diff --git a/spring-jms/src/main/java/org/springframework/jms/config/AbstractJmsListenerContainerFactory.java b/spring-jms/src/main/java/org/springframework/jms/config/AbstractJmsListenerContainerFactory.java index 32816c00927..68551aba4d8 100644 --- a/spring-jms/src/main/java/org/springframework/jms/config/AbstractJmsListenerContainerFactory.java +++ b/spring-jms/src/main/java/org/springframework/jms/config/AbstractJmsListenerContainerFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2025 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. @@ -63,6 +63,9 @@ public abstract class AbstractJmsListenerContainerFactoryAs of 6.2, the default is {@code true}: The listener container will + * acknowledge each JMS Message even in case of a vendor-specific mode, + * assuming client-acknowledge style processing for custom vendor modes. + *

If the provided listener prefers to manually acknowledge each message in + * the listener itself, in combination with an "individual acknowledge" mode, + * switch this flag to {code false} along with the vendor-specific mode. + * @since 6.2.6 + * @see #setSessionAcknowledgeMode + * @see #setMessageListener + * @see Message#acknowledge() + */ + public void setAcknowledgeAfterListener(boolean acknowledgeAfterListener) { + this.acknowledgeAfterListener = acknowledgeAfterListener; + } + + /** + * Determine whether the listener container should automatically acknowledge + * each JMS Message after the message listener returned. + * @since 6.2.6 + * @see #setAcknowledgeAfterListener + * @see #isClientAcknowledge(Session) + */ + public boolean isAcknowledgeAfterListener() { + return this.acknowledgeAfterListener; + } + /** * Set whether to expose the listener JMS Session to a registered * {@link SessionAwareMessageListener} as well as to @@ -833,7 +862,7 @@ public abstract class AbstractMessageListenerContainer extends AbstractJmsListen JmsUtils.commitIfNecessary(session); } } - else if (message != null && isClientAcknowledge(session)) { + else if (message != null && isAcknowledgeAfterListener() && isClientAcknowledge(session)) { message.acknowledge(); } } From 6905dff660d7dc1b8ad4cca1c3c039ad8405feef Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 25 Mar 2025 17:08:55 +0100 Subject: [PATCH 2/3] Introduce spring.locking.strict=true flag for 6.1.x style bean creation locking Closes gh-34303 --- .../support/DefaultListableBeanFactory.java | 15 ++++++- .../annotation/BackgroundBootstrapTests.java | 41 +++++++++++++++++++ .../core/SpringProperties.java | 1 + 3 files changed, 56 insertions(+), 1 deletion(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java index 355e73d5abb..0e444f567bc 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java @@ -76,6 +76,7 @@ import org.springframework.core.NamedThreadLocal; import org.springframework.core.OrderComparator; import org.springframework.core.Ordered; import org.springframework.core.ResolvableType; +import org.springframework.core.SpringProperties; import org.springframework.core.annotation.MergedAnnotation; import org.springframework.core.annotation.MergedAnnotations; import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; @@ -128,6 +129,17 @@ import org.springframework.util.StringUtils; public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable { + /** + * System property that instructs Spring to enforce string locking during bean creation, + * rather than the mix of strict and lenient locking that 6.2 applies by default. Setting + * this flag to "true" restores 6.1.x style locking in the entire pre-instantiation phase. + * @since 6.2.6 + * @see #preInstantiateSingletons() + */ + public static final String STRICT_LOCKING_PROPERTY_NAME = "spring.locking.strict"; + + private static final boolean lenientLockingAllowed = !SpringProperties.getFlag(STRICT_LOCKING_PROPERTY_NAME); + @Nullable private static Class jakartaInjectProviderClass; @@ -1031,7 +1043,8 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto @Override @Nullable protected Boolean isCurrentThreadAllowedToHoldSingletonLock() { - return (this.preInstantiationPhase ? this.preInstantiationThread.get() != PreInstantiation.BACKGROUND : null); + return (lenientLockingAllowed && this.preInstantiationPhase ? + this.preInstantiationThread.get() != PreInstantiation.BACKGROUND : null); } @Override diff --git a/spring-context/src/test/java/org/springframework/context/annotation/BackgroundBootstrapTests.java b/spring-context/src/test/java/org/springframework/context/annotation/BackgroundBootstrapTests.java index dda782ce897..913cc863d04 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/BackgroundBootstrapTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/BackgroundBootstrapTests.java @@ -20,11 +20,15 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.springframework.beans.factory.ObjectProvider; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.testfixture.beans.TestBean; import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.SpringProperties; import org.springframework.core.testfixture.EnabledForTestGroups; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.context.annotation.Bean.Bootstrap.BACKGROUND; import static org.springframework.core.testfixture.TestGroup.LONG_RUNNING; @@ -56,6 +60,21 @@ class BackgroundBootstrapTests { ctx.close(); } + @Test + @Timeout(5) + @EnabledForTestGroups(LONG_RUNNING) + void bootstrapWithStrictLockingThread() { + SpringProperties.setFlag(DefaultListableBeanFactory.STRICT_LOCKING_PROPERTY_NAME); + try { + ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(StrictLockingBeanConfig.class); + assertThat(ctx.getBean("testBean2", TestBean.class).getSpouse()).isSameAs(ctx.getBean("testBean1")); + ctx.close(); + } + finally { + SpringProperties.setProperty(DefaultListableBeanFactory.STRICT_LOCKING_PROPERTY_NAME, null); + } + } + @Test @Timeout(5) @EnabledForTestGroups(LONG_RUNNING) @@ -148,6 +167,28 @@ class BackgroundBootstrapTests { } + @Configuration(proxyBeanMethods = false) + static class StrictLockingBeanConfig { + + @Bean + public TestBean testBean1(ObjectProvider testBean2) { + new Thread(testBean2::getObject).start(); + try { + Thread.sleep(1000); + } + catch (InterruptedException ex) { + throw new RuntimeException(ex); + } + return new TestBean(); + } + + @Bean + public TestBean testBean2(ConfigurableListableBeanFactory beanFactory) { + return new TestBean((TestBean) beanFactory.getSingleton("testBean1")); + } + } + + @Configuration(proxyBeanMethods = false) static class CircularReferenceBeanConfig { diff --git a/spring-core/src/main/java/org/springframework/core/SpringProperties.java b/spring-core/src/main/java/org/springframework/core/SpringProperties.java index 299ec0d987b..fbb94deba25 100644 --- a/spring-core/src/main/java/org/springframework/core/SpringProperties.java +++ b/spring-core/src/main/java/org/springframework/core/SpringProperties.java @@ -39,6 +39,7 @@ import org.springframework.lang.Nullable; * @author Juergen Hoeller * @since 3.2.7 * @see org.springframework.beans.StandardBeanInfoFactory#IGNORE_BEANINFO_PROPERTY_NAME + * @see org.springframework.beans.factory.support.DefaultListableBeanFactory#STRICT_LOCKING_PROPERTY_NAME * @see org.springframework.core.env.AbstractEnvironment#IGNORE_GETENV_PROPERTY_NAME * @see org.springframework.expression.spel.SpelParserConfiguration#SPRING_EXPRESSION_COMPILER_MODE_PROPERTY_NAME * @see org.springframework.jdbc.core.StatementCreatorUtils#IGNORE_GETPARAMETERTYPE_PROPERTY_NAME From 84430a8db2017f88228e515ceb93c937a0879764 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 25 Mar 2025 17:09:24 +0100 Subject: [PATCH 3/3] Polishing --- .../java/org/springframework/objenesis/SpringObjenesis.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/objenesis/SpringObjenesis.java b/spring-core/src/main/java/org/springframework/objenesis/SpringObjenesis.java index 755ddb7232c..11b3aa004ca 100644 --- a/spring-core/src/main/java/org/springframework/objenesis/SpringObjenesis.java +++ b/spring-core/src/main/java/org/springframework/objenesis/SpringObjenesis.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2025 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. @@ -69,7 +69,7 @@ public class SpringObjenesis implements Objenesis { this.strategy = (strategy != null ? strategy : new StdInstantiatorStrategy()); // Evaluate the "spring.objenesis.ignore" property upfront... - if (SpringProperties.getFlag(SpringObjenesis.IGNORE_OBJENESIS_PROPERTY_NAME)) { + if (SpringProperties.getFlag(IGNORE_OBJENESIS_PROPERTY_NAME)) { this.worthTrying = Boolean.FALSE; } }