Merge branch '4.1.x'

Closes gh-51455
This commit is contained in:
Phillip Webb
2026-08-25 14:19:47 -07:00
65 changed files with 145 additions and 77 deletions
@@ -39,7 +39,7 @@ import org.springframework.util.Assert;
@Endpoint(id = "shutdown", defaultAccess = Access.NONE)
public class ShutdownEndpoint implements ApplicationContextAware {
@Nullable private ConfigurableApplicationContext context;
private @Nullable ConfigurableApplicationContext context;
@WriteOperation
public ShutdownDescriptor shutdown() {
@@ -27,6 +27,7 @@ import java.util.regex.PatternSyntaxException;
import java.util.stream.Stream;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import org.jspecify.annotations.Nullable;
import org.springframework.boot.actuate.endpoint.InvalidEndpointRequestException;
@@ -252,7 +253,7 @@ public class EnvironmentEndpoint {
/**
* Description of an entry of the {@link Environment}.
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonInclude(Include.NON_NULL)
public static final class EnvironmentEntryDescriptor {
private final @Nullable PropertySummaryDescriptor property;
@@ -292,7 +293,7 @@ public class EnvironmentEndpoint {
/**
* Description of a particular entry of the {@link Environment}.
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonInclude(Include.NON_NULL)
public static final class PropertySummaryDescriptor {
private final String source;
@@ -341,7 +342,7 @@ public class EnvironmentEndpoint {
/**
* Description of a particular entry of {@link PropertySource}.
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonInclude(Include.NON_NULL)
public static final class PropertySourceEntryDescriptor {
private final String name;
@@ -366,7 +367,7 @@ public class EnvironmentEndpoint {
/**
* Description of a property's value, including its origin if available.
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonInclude(Include.NON_NULL)
public static final class PropertyValueDescriptor {
private final @Nullable Object value;
@@ -60,7 +60,7 @@ public final class RabbitAmqpListenerContainerFactoryConfigurer {
* converter should be used.
* @param messageConverter the {@link MessageConverter}
*/
protected void setMessageConverter(@Nullable MessageConverter messageConverter) {
void setMessageConverter(@Nullable MessageConverter messageConverter) {
this.messageConverter = messageConverter;
}
@@ -68,7 +68,7 @@ public final class RabbitAmqpListenerContainerFactoryConfigurer {
* Set the {@link TaskScheduler} to use or {@code null} if the default should be used.
* @param taskScheduler the {@link TaskScheduler}
*/
protected void setTaskScheduler(@Nullable TaskScheduler taskScheduler) {
void setTaskScheduler(@Nullable TaskScheduler taskScheduler) {
this.taskScheduler = taskScheduler;
}
@@ -101,12 +101,12 @@ abstract class AbstractDevToolsIntegrationTests {
this.classesDirectory = classesDirectory;
}
protected ControllerBuilder withRequestMapping(String mapping) {
ControllerBuilder withRequestMapping(String mapping) {
this.mappings.add(mapping);
return this;
}
protected void build() throws Exception {
void build() throws Exception {
DynamicType.Builder<Object> builder = new ByteBuddy().subclass(Object.class)
.name(this.name)
.annotateType(AnnotationDescription.Builder.ofType(RestController.class).build());
@@ -21,6 +21,7 @@ import io.grpc.servlet.jakarta.GrpcServlet;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
import org.springframework.boot.diagnostics.FailureAnalyzedException;
import org.springframework.boot.grpc.server.GrpcServletRegistration;
import org.springframework.context.annotation.Bean;
@@ -38,7 +39,7 @@ import org.springframework.grpc.server.service.GrpcServiceDiscoverer;
* @author Phillip Webb
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(GrpcServlet.class)
@ConditionalOnMissingNetworkGrpcServer
@ConditionalOnBooleanProperty(name = "spring.grpc.server.servlet.enabled", matchIfMissing = true)
@@ -14,6 +14,9 @@
* limitations under the License.
*/
/**
* Auto-configuration for application health indicators.
*/
@NullMarked
package org.springframework.boot.health.autoconfigure.application;
@@ -34,6 +34,7 @@ import org.springframework.aot.test.generate.TestGenerationContext;
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution;
import org.springframework.beans.factory.aot.BeanFactoryInitializationCode;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.jackson.JacksonComponent.Scope;
import org.springframework.boot.jackson.JacksonComponentModule.JacksonComponentBeanFactoryInitializationAotProcessor;
import org.springframework.boot.jackson.JacksonComponentModuleTests.ComponentWithInnerAbstractClass.AbstractSerializer;
import org.springframework.boot.jackson.JacksonComponentModuleTests.ComponentWithInnerAbstractClass.ConcreteSerializer;
@@ -236,12 +237,12 @@ class JacksonComponentModuleTests {
}
@JacksonComponent(scope = JacksonComponent.Scope.KEYS)
@JacksonComponent(scope = Scope.KEYS)
static class OnlyKeySerializer extends NameAndAgeJacksonKeyComponent.Serializer {
}
@JacksonComponent(scope = JacksonComponent.Scope.KEYS, type = NameAndAge.class)
@JacksonComponent(scope = Scope.KEYS, type = NameAndAge.class)
static class OnlyKeyDeserializer extends NameAndAgeJacksonKeyComponent.Deserializer {
}
@@ -22,6 +22,7 @@ import tools.jackson.databind.KeyDeserializer;
import tools.jackson.databind.SerializationContext;
import tools.jackson.databind.ValueSerializer;
import org.springframework.boot.jackson.JacksonComponent.Scope;
import org.springframework.boot.jackson.types.NameAndAge;
/**
@@ -29,7 +30,7 @@ import org.springframework.boot.jackson.types.NameAndAge;
*
* @author Paul Aly
*/
@JacksonComponent(type = NameAndAge.class, scope = JacksonComponent.Scope.KEYS)
@JacksonComponent(type = NameAndAge.class, scope = Scope.KEYS)
public class NameAndAgeJacksonKeyComponent {
static class Serializer extends ValueSerializer<NameAndAge> {
@@ -23,6 +23,7 @@ import java.util.Map;
import java.util.function.Consumer;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.Test;
import tools.jackson.core.JsonGenerator;
@@ -251,7 +252,7 @@ class SpringBeanHandlerInstantiatorTests {
}
@JsonTypeInfo(use = JsonTypeInfo.Id.CUSTOM, property = "type")
@JsonTypeInfo(use = Id.CUSTOM, property = "type")
@JsonTypeResolver(CustomTypeResolverBuilder.class)
@JsonTypeIdResolver(CustomTypeIdResolver.class)
public static class Group {
@@ -58,6 +58,7 @@ class JsonComponentModuleTests {
private @Nullable AnnotationConfigApplicationContext context;
@Deprecated(since = "4.0.0", forRemoval = true)
@AfterEach
void closeContext() {
if (this.context != null) {
@@ -65,6 +66,7 @@ class JsonComponentModuleTests {
}
}
@Deprecated(since = "4.0.0", forRemoval = true)
@Test
void moduleShouldRegisterSerializers() throws Exception {
load(OnlySerializer.class);
@@ -72,6 +74,7 @@ class JsonComponentModuleTests {
assertSerialize(module);
}
@Deprecated(since = "4.0.0", forRemoval = true)
@Test
void moduleShouldRegisterDeserializers() throws Exception {
load(OnlyDeserializer.class);
@@ -79,6 +82,7 @@ class JsonComponentModuleTests {
assertDeserialize(module);
}
@Deprecated(since = "4.0.0", forRemoval = true)
@Test
void moduleShouldRegisterInnerClasses() throws Exception {
load(NameAndAgeJsonComponent.class);
@@ -87,6 +91,7 @@ class JsonComponentModuleTests {
assertDeserialize(module);
}
@Deprecated(since = "4.0.0", forRemoval = true)
@Test
void moduleShouldAllowInnerAbstractClasses() throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(JsonComponentModule.class,
@@ -96,6 +101,7 @@ class JsonComponentModuleTests {
context.close();
}
@Deprecated(since = "4.0.0", forRemoval = true)
@Test
void moduleShouldRegisterKeySerializers() throws Exception {
load(OnlyKeySerializer.class);
@@ -103,6 +109,7 @@ class JsonComponentModuleTests {
assertKeySerialize(module);
}
@Deprecated(since = "4.0.0", forRemoval = true)
@Test
void moduleShouldRegisterKeyDeserializers() throws Exception {
load(OnlyKeyDeserializer.class);
@@ -110,6 +117,7 @@ class JsonComponentModuleTests {
assertKeyDeserialize(module);
}
@Deprecated(since = "4.0.0", forRemoval = true)
@Test
void moduleShouldRegisterInnerClassesForKeyHandlers() throws Exception {
load(NameAndAgeJsonKeyComponent.class);
@@ -118,6 +126,7 @@ class JsonComponentModuleTests {
assertKeyDeserialize(module);
}
@Deprecated(since = "4.0.0", forRemoval = true)
@Test
void moduleShouldRegisterOnlyForSpecifiedClasses() throws Exception {
load(NameAndCareerJsonComponent.class);
@@ -127,6 +136,7 @@ class JsonComponentModuleTests {
assertDeserializeForSpecifiedClasses(module);
}
@Deprecated(since = "4.0.0", forRemoval = true)
@Test
void aotContributionRegistersReflectionHintsForSuitableInnerClasses() {
load(ComponentWithInnerAbstractClass.class);
@@ -214,39 +224,47 @@ class JsonComponentModuleTests {
return context;
}
@Deprecated(since = "4.0.0", forRemoval = true)
@JsonComponent
static class OnlySerializer extends NameAndAgeJsonComponent.Serializer {
}
@Deprecated(since = "4.0.0", forRemoval = true)
@JsonComponent
static class OnlyDeserializer extends NameAndAgeJsonComponent.Deserializer {
}
@Deprecated(since = "4.0.0", forRemoval = true)
@JsonComponent
static class ComponentWithInnerAbstractClass {
@Deprecated(since = "4.0.0", forRemoval = true)
abstract static class AbstractSerializer extends NameAndAgeJsonComponent.Serializer {
}
@Deprecated(since = "4.0.0", forRemoval = true)
static class ConcreteSerializer extends AbstractSerializer {
}
@Deprecated(since = "4.0.0", forRemoval = true)
static class NotSuitable {
}
}
@JsonComponent(scope = JsonComponent.Scope.KEYS)
@Deprecated(since = "4.0.0", forRemoval = true)
@JsonComponent(scope = org.springframework.boot.jackson2.JsonComponent.Scope.KEYS)
static class OnlyKeySerializer extends NameAndAgeJsonKeyComponent.Serializer {
}
@JsonComponent(scope = JsonComponent.Scope.KEYS, type = NameAndAge.class)
@Deprecated(since = "4.0.0", forRemoval = true)
@JsonComponent(scope = org.springframework.boot.jackson2.JsonComponent.Scope.KEYS, type = NameAndAge.class)
static class OnlyKeyDeserializer extends NameAndAgeJsonKeyComponent.Deserializer {
}
@@ -34,11 +34,13 @@ import org.springframework.boot.jackson2.types.NameAndAge;
*/
@Deprecated(since = "4.0.0", forRemoval = true)
@SuppressWarnings("removal")
@JsonComponent(type = NameAndAge.class, scope = JsonComponent.Scope.KEYS)
@JsonComponent(type = NameAndAge.class, scope = org.springframework.boot.jackson2.JsonComponent.Scope.KEYS)
public class NameAndAgeJsonKeyComponent {
@Deprecated(since = "4.0.0", forRemoval = true)
static class Serializer extends JsonSerializer<NameAndAge> {
@Deprecated(since = "4.0.0", forRemoval = true)
@Override
public void serialize(NameAndAge value, JsonGenerator jgen, SerializerProvider serializers) throws IOException {
jgen.writeFieldName(value.asKey());
@@ -46,8 +48,10 @@ public class NameAndAgeJsonKeyComponent {
}
@Deprecated(since = "4.0.0", forRemoval = true)
static class Deserializer extends KeyDeserializer {
@Deprecated(since = "4.0.0", forRemoval = true)
@Override
public NameAndAge deserializeKey(String key, DeserializationContext ctxt) throws IOException {
String[] keys = key.split("is");
@@ -23,6 +23,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.jdbc.EmbeddedDatabaseConnection;
import org.springframework.boot.jdbc.test.autoconfigure.AutoConfigureTestDatabase.Replace;
import org.springframework.context.annotation.Configuration;
import static org.assertj.core.api.Assertions.assertThat;
@@ -34,8 +35,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Stephane Nicoll
*/
@JdbcTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.AUTO_CONFIGURED,
connection = EmbeddedDatabaseConnection.HSQLDB)
@AutoConfigureTestDatabase(replace = Replace.AUTO_CONFIGURED, connection = EmbeddedDatabaseConnection.HSQLDB)
class JdbcTestWithAutoConfigureTestDatabaseReplaceAutoConfiguredIntegrationTests {
@Autowired
@@ -21,6 +21,7 @@ import javax.sql.DataSource;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.jdbc.test.autoconfigure.AutoConfigureTestDatabase.Replace;
import static org.assertj.core.api.Assertions.assertThat;
@@ -31,7 +32,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Stephane Nicoll
*/
@JdbcTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.AUTO_CONFIGURED)
@AutoConfigureTestDatabase(replace = Replace.AUTO_CONFIGURED)
class JdbcTestWithAutoConfigureTestDatabaseReplaceAutoConfiguredWithoutOverrideIntegrationTests {
@Autowired
@@ -21,6 +21,7 @@ import javax.sql.DataSource;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.jdbc.test.autoconfigure.AutoConfigureTestDatabase.Replace;
import static org.assertj.core.api.Assertions.assertThat;
@@ -31,7 +32,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Stephane Nicoll
*/
@JdbcTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@AutoConfigureTestDatabase(replace = Replace.NONE)
class JdbcTestWithAutoConfigureTestDatabaseReplaceNoneIntegrationTests {
@Autowired
@@ -58,7 +58,7 @@ public interface LiquibaseConnectionDetails extends ConnectionDetails {
* @see DatabaseDriver#fromJdbcUrl(String)
* @see DatabaseDriver#getDriverClassName()
*/
@Nullable default String getDriverClassName() {
default @Nullable String getDriverClassName() {
String jdbcUrl = getJdbcUrl();
return (jdbcUrl != null) ? DatabaseDriver.fromJdbcUrl(jdbcUrl).getDriverClassName() : null;
}
@@ -102,7 +102,6 @@ public abstract class AbstractRabbitListenerContainerFactoryConfigurer<T extends
/**
* Sets the observation convention to use.
* @param observationConvention the observation convention to use
* @since 4.2.0
*/
protected void setObservationConvention(@Nullable RabbitListenerObservationConvention observationConvention) {
this.observationConvention = observationConvention;
@@ -73,7 +73,6 @@ public class RabbitStreamTemplateConfigurer {
/**
* Set the observation convention to use.
* @param observationConvention the observation convention to use
* @since 4.2.0
*/
public void setObservationConvention(@Nullable RabbitStreamTemplateObservationConvention observationConvention) {
this.observationConvention = observationConvention;
@@ -87,7 +87,6 @@ public class RabbitTemplateConfigurer {
/**
* Sets the observation convention to use.
* @param observationConvention the observation convention to use
* @since 4.2.0
*/
public void setObservationConvention(@Nullable RabbitTemplateObservationConvention observationConvention) {
this.observationConvention = observationConvention;
@@ -170,7 +170,7 @@ public class Cookie {
*/
STRICT("Strict");
private @Nullable final String attributeValue;
private final @Nullable String attributeValue;
SameSite(@Nullable String attributeValue) {
this.attributeValue = attributeValue;
@@ -114,6 +114,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.io.TempDir;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import org.junit.jupiter.params.provider.EnumSource.Mode;
import org.mockito.InOrder;
import org.springframework.boot.ssl.DefaultSslBundleRegistry;
@@ -914,7 +915,7 @@ public abstract class AbstractServletWebServerFactoryTests {
}
@ParameterizedTest
@EnumSource(mode = EnumSource.Mode.EXCLUDE, names = "OMITTED")
@EnumSource(mode = Mode.EXCLUDE, names = "OMITTED")
void sessionCookieSameSiteAttributeCanBeConfiguredAndOnlyAffectsSessionCookies(SameSite sameSite) throws Exception {
ConfigurableServletWebServerFactory factory = getFactory();
factory.getSettings().getSession().getCookie().setSameSite(sameSite);
@@ -929,7 +930,7 @@ public abstract class AbstractServletWebServerFactoryTests {
}
@ParameterizedTest
@EnumSource(mode = EnumSource.Mode.EXCLUDE, names = "OMITTED")
@EnumSource(mode = Mode.EXCLUDE, names = "OMITTED")
void sessionCookieSameSiteAttributeCanBeConfiguredAndOnlyAffectsSessionCookiesWhenUsingCustomName(SameSite sameSite)
throws Exception {
ConfigurableServletWebServerFactory factory = getFactory();