Browse Source

Merge branch '6.2.x'

# Conflicts:
#	spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java
#	spring-jms/src/main/java/org/springframework/jms/config/AbstractJmsListenerContainerFactory.java
#	spring-jms/src/main/java/org/springframework/jms/listener/AbstractMessageListenerContainer.java
pull/34657/head
Juergen Hoeller 10 months ago
parent
commit
3872c1a762
  1. 15
      spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java
  2. 41
      spring-context/src/test/java/org/springframework/context/annotation/BackgroundBootstrapTests.java
  3. 1
      spring-core/src/main/java/org/springframework/core/SpringProperties.java
  4. 4
      spring-core/src/main/java/org/springframework/objenesis/SpringObjenesis.java
  5. 16
      spring-jms/src/main/java/org/springframework/jms/config/AbstractJmsListenerContainerFactory.java
  6. 51
      spring-jms/src/main/java/org/springframework/jms/listener/AbstractMessageListenerContainer.java

15
spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java

@ -77,6 +77,7 @@ import org.springframework.core.NamedThreadLocal; @@ -77,6 +77,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; @@ -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);
private static @Nullable Class<?> jakartaInjectProviderClass;
static {
@ -1015,7 +1027,8 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto @@ -1015,7 +1027,8 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
@Override
protected @Nullable Boolean isCurrentThreadAllowedToHoldSingletonLock() {
return (this.preInstantiationPhase ? this.preInstantiationThread.get() != PreInstantiation.BACKGROUND : null);
return (lenientLockingAllowed && this.preInstantiationPhase ?
this.preInstantiationThread.get() != PreInstantiation.BACKGROUND : null);
}
@Override

41
spring-context/src/test/java/org/springframework/context/annotation/BackgroundBootstrapTests.java

@ -20,11 +20,15 @@ import org.junit.jupiter.api.Test; @@ -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 { @@ -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 { @@ -148,6 +167,28 @@ class BackgroundBootstrapTests {
}
@Configuration(proxyBeanMethods = false)
static class StrictLockingBeanConfig {
@Bean
public TestBean testBean1(ObjectProvider<TestBean> 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 {

1
spring-core/src/main/java/org/springframework/core/SpringProperties.java

@ -39,6 +39,7 @@ import org.jspecify.annotations.Nullable; @@ -39,6 +39,7 @@ import org.jspecify.annotations.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

4
spring-core/src/main/java/org/springframework/objenesis/SpringObjenesis.java

@ -1,5 +1,5 @@ @@ -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 { @@ -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;
}
}

16
spring-jms/src/main/java/org/springframework/jms/config/AbstractJmsListenerContainerFactory.java

@ -1,5 +1,5 @@ @@ -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.
@ -56,6 +56,8 @@ public abstract class AbstractJmsListenerContainerFactory<C extends AbstractMess @@ -56,6 +56,8 @@ public abstract class AbstractJmsListenerContainerFactory<C extends AbstractMess
private @Nullable Integer sessionAcknowledgeMode;
private @Nullable Boolean acknowledgeAfterListener;
private @Nullable Boolean pubSubDomain;
private @Nullable Boolean replyPubSubDomain;
@ -125,6 +127,14 @@ public abstract class AbstractJmsListenerContainerFactory<C extends AbstractMess @@ -125,6 +127,14 @@ public abstract class AbstractJmsListenerContainerFactory<C extends AbstractMess
this.sessionAcknowledgeMode = sessionAcknowledgeMode;
}
/**
* @since 6.2.6
* @see AbstractMessageListenerContainer#setAcknowledgeAfterListener(boolean)
*/
public void setAcknowledgeAfterListener(Boolean acknowledgeAfterListener) {
this.acknowledgeAfterListener = acknowledgeAfterListener;
}
/**
* @see AbstractMessageListenerContainer#setPubSubDomain(boolean)
*/
@ -193,6 +203,7 @@ public abstract class AbstractJmsListenerContainerFactory<C extends AbstractMess @@ -193,6 +203,7 @@ public abstract class AbstractJmsListenerContainerFactory<C extends AbstractMess
this.observationRegistry = observationRegistry;
}
@Override
public C createListenerContainer(JmsListenerEndpoint endpoint) {
C instance = createContainerInstance();
@ -218,6 +229,9 @@ public abstract class AbstractJmsListenerContainerFactory<C extends AbstractMess @@ -218,6 +229,9 @@ public abstract class AbstractJmsListenerContainerFactory<C extends AbstractMess
if (this.sessionAcknowledgeMode != null) {
instance.setSessionAcknowledgeMode(this.sessionAcknowledgeMode);
}
if (this.acknowledgeAfterListener != null) {
instance.setAcknowledgeAfterListener(this.acknowledgeAfterListener);
}
if (this.pubSubDomain != null) {
instance.setPubSubDomain(this.pubSubDomain);
}

51
spring-jms/src/main/java/org/springframework/jms/listener/AbstractMessageListenerContainer.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 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.
@ -152,7 +152,8 @@ public abstract class AbstractMessageListenerContainer extends AbstractJmsListen @@ -152,7 +152,8 @@ public abstract class AbstractMessageListenerContainer extends AbstractJmsListen
implements MessageListenerContainer {
private static final boolean micrometerJakartaPresent = ClassUtils.isPresent(
"io.micrometer.jakarta9.instrument.jms.JmsInstrumentation", AbstractMessageListenerContainer.class.getClassLoader());
"io.micrometer.jakarta9.instrument.jms.JmsInstrumentation",
AbstractMessageListenerContainer.class.getClassLoader());
private volatile @Nullable Object destination;
@ -166,12 +167,12 @@ public abstract class AbstractMessageListenerContainer extends AbstractJmsListen @@ -166,12 +167,12 @@ public abstract class AbstractMessageListenerContainer extends AbstractJmsListen
private @Nullable String subscriptionName;
private boolean pubSubNoLocal = false;
private @Nullable Boolean replyPubSubDomain;
private @Nullable QosSettings replyQosSettings;
private boolean pubSubNoLocal = false;
private @Nullable MessageConverter messageConverter;
private @Nullable ExceptionListener exceptionListener;
@ -180,6 +181,8 @@ public abstract class AbstractMessageListenerContainer extends AbstractJmsListen @@ -180,6 +181,8 @@ public abstract class AbstractMessageListenerContainer extends AbstractJmsListen
private @Nullable ObservationRegistry observationRegistry;
private boolean acknowledgeAfterListener = true;
private boolean exposeListenerSession = true;
private boolean acceptMessagesWhileStopping = false;
@ -484,12 +487,7 @@ public abstract class AbstractMessageListenerContainer extends AbstractJmsListen @@ -484,12 +487,7 @@ public abstract class AbstractMessageListenerContainer extends AbstractJmsListen
*/
@Override
public boolean isReplyPubSubDomain() {
if (this.replyPubSubDomain != null) {
return this.replyPubSubDomain;
}
else {
return isPubSubDomain();
}
return (this.replyPubSubDomain != null ? this.replyPubSubDomain : isPubSubDomain());
}
/**
@ -575,6 +573,37 @@ public abstract class AbstractMessageListenerContainer extends AbstractJmsListen @@ -575,6 +573,37 @@ public abstract class AbstractMessageListenerContainer extends AbstractJmsListen
return this.observationRegistry;
}
/**
* Specify whether the listener container should automatically acknowledge
* each JMS Message after the message listener returned. This applies in
* case of client acknowledge modes, including vendor-specific modes but
* not in case of auto-acknowledge or a transacted JMS Session.
* <p>As 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.
* <p>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
@ -812,7 +841,7 @@ public abstract class AbstractMessageListenerContainer extends AbstractJmsListen @@ -812,7 +841,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();
}
}

Loading…
Cancel
Save