diff --git a/gradle.properties b/gradle.properties index d70bd556d7e..4e1cad6fed6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,7 +14,7 @@ junitJupiterVersion=5.10.2 kotlinVersion=1.9.24 mavenVersion=3.9.4 nativeBuildToolsVersion=0.10.2 -springFrameworkVersion=6.1.10 +springFrameworkVersion=6.2.0-M4 springFramework60xVersion=6.0.21 tomcatVersion=10.1.25 snakeYamlVersion=2.2 diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/scheduling/ScheduledTasksEndpoint.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/scheduling/ScheduledTasksEndpoint.java index 7b666c8b05c..98eff14a5d4 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/scheduling/ScheduledTasksEndpoint.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/scheduling/ScheduledTasksEndpoint.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-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. @@ -16,7 +16,6 @@ package org.springframework.boot.actuate.scheduling; -import java.lang.reflect.Method; import java.time.Duration; import java.util.Collection; import java.util.Collections; @@ -46,7 +45,6 @@ import org.springframework.scheduling.config.Task; import org.springframework.scheduling.config.TriggerTask; import org.springframework.scheduling.support.CronTrigger; import org.springframework.scheduling.support.PeriodicTrigger; -import org.springframework.scheduling.support.ScheduledMethodRunnable; /** * {@link Endpoint @Endpoint} to expose information about an application's scheduled @@ -284,13 +282,7 @@ public class ScheduledTasksEndpoint { private final String target; private RunnableDescriptor(Runnable runnable) { - if (runnable instanceof ScheduledMethodRunnable scheduledMethodRunnable) { - Method method = scheduledMethodRunnable.getMethod(); - this.target = method.getDeclaringClass().getName() + "." + method.getName(); - } - else { - this.target = runnable.getClass().getName(); - } + this.target = runnable.toString(); } public String getTarget() { diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/scheduling/ScheduledTasksEndpointTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/scheduling/ScheduledTasksEndpointTests.java index 8befd959fcd..6456d266da6 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/scheduling/ScheduledTasksEndpointTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/scheduling/ScheduledTasksEndpointTests.java @@ -80,7 +80,7 @@ class ScheduledTasksEndpointTests { assertThat(tasks.getCron()).hasSize(1); CronTaskDescriptor description = (CronTaskDescriptor) tasks.getCron().get(0); assertThat(description.getExpression()).isEqualTo("0 0 0/6 1/1 * ?"); - assertThat(description.getRunnable().getTarget()).isEqualTo(CronTriggerRunnable.class.getName()); + assertThat(description.getRunnable().getTarget()).contains(CronTriggerRunnable.class.getName()); }); } @@ -109,7 +109,7 @@ class ScheduledTasksEndpointTests { FixedDelayTaskDescriptor description = (FixedDelayTaskDescriptor) tasks.getFixedDelay().get(0); assertThat(description.getInitialDelay()).isEqualTo(2000); assertThat(description.getInterval()).isEqualTo(1000); - assertThat(description.getRunnable().getTarget()).isEqualTo(FixedDelayTriggerRunnable.class.getName()); + assertThat(description.getRunnable().getTarget()).contains(FixedDelayTriggerRunnable.class.getName()); }); } @@ -123,7 +123,7 @@ class ScheduledTasksEndpointTests { FixedDelayTaskDescriptor description = (FixedDelayTaskDescriptor) tasks.getFixedDelay().get(0); assertThat(description.getInitialDelay()).isEqualTo(0); assertThat(description.getInterval()).isEqualTo(1000); - assertThat(description.getRunnable().getTarget()).isEqualTo(FixedDelayTriggerRunnable.class.getName()); + assertThat(description.getRunnable().getTarget()).contains(FixedDelayTriggerRunnable.class.getName()); }); } @@ -152,7 +152,7 @@ class ScheduledTasksEndpointTests { FixedRateTaskDescriptor description = (FixedRateTaskDescriptor) tasks.getFixedRate().get(0); assertThat(description.getInitialDelay()).isEqualTo(3000); assertThat(description.getInterval()).isEqualTo(2000); - assertThat(description.getRunnable().getTarget()).isEqualTo(FixedRateTriggerRunnable.class.getName()); + assertThat(description.getRunnable().getTarget()).contains(FixedRateTriggerRunnable.class.getName()); }); } @@ -166,7 +166,7 @@ class ScheduledTasksEndpointTests { FixedRateTaskDescriptor description = (FixedRateTaskDescriptor) tasks.getFixedRate().get(0); assertThat(description.getInitialDelay()).isEqualTo(0); assertThat(description.getInterval()).isEqualTo(2000); - assertThat(description.getRunnable().getTarget()).isEqualTo(FixedRateTriggerRunnable.class.getName()); + assertThat(description.getRunnable().getTarget()).contains(FixedRateTriggerRunnable.class.getName()); }); } @@ -178,7 +178,7 @@ class ScheduledTasksEndpointTests { assertThat(tasks.getFixedRate()).isEmpty(); assertThat(tasks.getCustom()).hasSize(1); CustomTriggerTaskDescriptor description = (CustomTriggerTaskDescriptor) tasks.getCustom().get(0); - assertThat(description.getRunnable().getTarget()).isEqualTo(CustomTriggerRunnable.class.getName()); + assertThat(description.getRunnable().getTarget()).contains(CustomTriggerRunnable.class.getName()); assertThat(description.getTrigger()).isEqualTo(CustomTriggerTask.trigger.toString()); }); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfiguration.java index 5ad8ff75ae8..43e9620a462 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfiguration.java @@ -157,6 +157,7 @@ public class DataSourceAutoConfiguration { return StringUtils.hasText(environment.getProperty(DATASOURCE_URL_PROPERTY)); } catch (IllegalArgumentException ex) { + // NOTE: This should be PlaceholderResolutionException // Ignore unresolvable placeholder errors } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/http/HttpMessageConvertersTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/http/HttpMessageConvertersTests.java index 4a93124f2d5..17dcc631d12 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/http/HttpMessageConvertersTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/http/HttpMessageConvertersTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-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. @@ -146,7 +146,7 @@ class HttpMessageConvertersTests { } assertThat(converterClasses).containsExactly(ByteArrayHttpMessageConverter.class, StringHttpMessageConverter.class, ResourceHttpMessageConverter.class, - MappingJackson2HttpMessageConverter.class); + MappingJackson2HttpMessageConverter.class, MappingJackson2CborHttpMessageConverter.class); } private List> extractFormPartConverters(List> converters) { diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoTestExecutionListener.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoTestExecutionListener.java index be4561d45a5..0da250af7e7 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoTestExecutionListener.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoTestExecutionListener.java @@ -16,27 +16,18 @@ package org.springframework.boot.test.mock.mockito; -import java.lang.annotation.Annotation; import java.lang.reflect.Field; -import java.util.LinkedHashSet; -import java.util.Set; import java.util.function.BiConsumer; -import org.mockito.Captor; -import org.mockito.MockitoAnnotations; - import org.springframework.test.context.TestContext; import org.springframework.test.context.TestExecutionListener; import org.springframework.test.context.support.AbstractTestExecutionListener; import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; import org.springframework.util.ReflectionUtils; -import org.springframework.util.ReflectionUtils.FieldCallback; /** * {@link TestExecutionListener} to enable {@link MockBean @MockBean} and - * {@link SpyBean @SpyBean} support. Also triggers - * {@link MockitoAnnotations#openMocks(Object)} when any Mockito annotations used, - * primarily to allow {@link Captor @Captor} annotations. + * {@link SpyBean @SpyBean} support. *

* To use the automatic reset support of {@code @MockBean} and {@code @SpyBean}, configure * {@link ResetMocksTestExecutionListener} as well. @@ -49,8 +40,6 @@ import org.springframework.util.ReflectionUtils.FieldCallback; */ public class MockitoTestExecutionListener extends AbstractTestExecutionListener { - private static final String MOCKS_ATTRIBUTE_NAME = MockitoTestExecutionListener.class.getName() + ".mocks"; - @Override public final int getOrder() { return 1950; @@ -58,8 +47,6 @@ public class MockitoTestExecutionListener extends AbstractTestExecutionListener @Override public void prepareTestInstance(TestContext testContext) throws Exception { - closeMocks(testContext); - initMocks(testContext); injectFields(testContext); } @@ -67,41 +54,10 @@ public class MockitoTestExecutionListener extends AbstractTestExecutionListener public void beforeTestMethod(TestContext testContext) throws Exception { if (Boolean.TRUE.equals( testContext.getAttribute(DependencyInjectionTestExecutionListener.REINJECT_DEPENDENCIES_ATTRIBUTE))) { - closeMocks(testContext); - initMocks(testContext); reinjectFields(testContext); } } - @Override - public void afterTestMethod(TestContext testContext) throws Exception { - closeMocks(testContext); - } - - @Override - public void afterTestClass(TestContext testContext) throws Exception { - closeMocks(testContext); - } - - private void initMocks(TestContext testContext) { - if (hasMockitoAnnotations(testContext)) { - testContext.setAttribute(MOCKS_ATTRIBUTE_NAME, MockitoAnnotations.openMocks(testContext.getTestInstance())); - } - } - - private void closeMocks(TestContext testContext) throws Exception { - Object mocks = testContext.getAttribute(MOCKS_ATTRIBUTE_NAME); - if (mocks instanceof AutoCloseable closeable) { - closeable.close(); - } - } - - private boolean hasMockitoAnnotations(TestContext testContext) { - MockitoAnnotationCollection collector = new MockitoAnnotationCollection(); - ReflectionUtils.doWithFields(testContext.getTestClass(), collector); - return collector.hasAnnotations(); - } - private void injectFields(TestContext testContext) { postProcessFields(testContext, (mockitoField, postProcessor) -> postProcessor.inject(mockitoField.field, mockitoField.target, mockitoField.definition)); @@ -130,28 +86,6 @@ public class MockitoTestExecutionListener extends AbstractTestExecutionListener } } - /** - * {@link FieldCallback} to collect Mockito annotations. - */ - private static final class MockitoAnnotationCollection implements FieldCallback { - - private final Set annotations = new LinkedHashSet<>(); - - @Override - public void doWith(Field field) throws IllegalArgumentException { - for (Annotation annotation : field.getDeclaredAnnotations()) { - if (annotation.annotationType().getName().startsWith("org.mockito")) { - this.annotations.add(annotation); - } - } - } - - boolean hasAnnotations() { - return !this.annotations.isEmpty(); - } - - } - private static final class MockitoField { private final Field field; diff --git a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockitoTestExecutionListenerTests.java b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockitoTestExecutionListenerTests.java index 3ce7d272a2e..74dacbdcf1b 100644 --- a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockitoTestExecutionListenerTests.java +++ b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockitoTestExecutionListenerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-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. @@ -53,14 +53,6 @@ class MockitoTestExecutionListenerTests { @Mock private MockitoPostProcessor postProcessor; - @Test - void prepareTestInstanceShouldInitMockitoAnnotations() throws Exception { - WithMockitoAnnotations instance = new WithMockitoAnnotations(); - this.listener.prepareTestInstance(mockTestContext(instance)); - assertThat(instance.mock).isNotNull(); - assertThat(instance.captor).isNotNull(); - } - @Test void prepareTestInstanceShouldInjectMockBean() throws Exception { given(this.applicationContext.getBean(MockitoPostProcessor.class)).willReturn(this.postProcessor); diff --git a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/web/SpringBootTestRandomPortEnvironmentPostProcessorTests.java b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/web/SpringBootTestRandomPortEnvironmentPostProcessorTests.java index e3ee27b5e36..3147d19e520 100644 --- a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/web/SpringBootTestRandomPortEnvironmentPostProcessorTests.java +++ b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/web/SpringBootTestRandomPortEnvironmentPostProcessorTests.java @@ -27,9 +27,10 @@ import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.MutablePropertySources; import org.springframework.mock.env.MockEnvironment; import org.springframework.test.context.support.TestPropertySourceUtils; +import org.springframework.util.PlaceholderResolutionException; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; /** * Tests for {@link SpringBootTestRandomPortEnvironmentPostProcessor}. @@ -169,7 +170,7 @@ class SpringBootTestRandomPortEnvironmentPostProcessorTests { addTestPropertySource("0", null); this.propertySources .addLast(new MapPropertySource("other", Collections.singletonMap("management.server.port", "${port}"))); - assertThatIllegalArgumentException() + assertThatExceptionOfType(PlaceholderResolutionException.class) .isThrownBy(() -> this.postProcessor.postProcessEnvironment(this.environment, null)) .withMessage("Could not resolve placeholder 'port' in value \"${port}\""); } @@ -196,7 +197,7 @@ class SpringBootTestRandomPortEnvironmentPostProcessorTests { source.put("server.port", "${port}"); source.put("management.server.port", "9090"); this.propertySources.addLast(new MapPropertySource("other", source)); - assertThatIllegalArgumentException() + assertThatExceptionOfType(PlaceholderResolutionException.class) .isThrownBy(() -> this.postProcessor.postProcessEnvironment(this.environment, null)) .withMessage("Could not resolve placeholder 'port' in value \"${port}\""); } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorPlaceholdersResolver.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorPlaceholdersResolver.java index 56add53e59d..cce91638af8 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorPlaceholdersResolver.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorPlaceholdersResolver.java @@ -56,7 +56,8 @@ class ConfigDataEnvironmentContributorPlaceholdersResolver implements Placeholde this.failOnResolveFromInactiveContributor = failOnResolveFromInactiveContributor; this.conversionService = conversionService; this.helper = new PropertyPlaceholderHelper(SystemPropertyUtils.PLACEHOLDER_PREFIX, - SystemPropertyUtils.PLACEHOLDER_SUFFIX, SystemPropertyUtils.VALUE_SEPARATOR, true); + SystemPropertyUtils.PLACEHOLDER_SUFFIX, SystemPropertyUtils.VALUE_SEPARATOR, + SystemPropertyUtils.ESCAPE_CHARACTER, true); } @Override diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/PropertySourcesPlaceholdersResolver.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/PropertySourcesPlaceholdersResolver.java index b6b6b9e10cd..67e432fab52 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/PropertySourcesPlaceholdersResolver.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/PropertySourcesPlaceholdersResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-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. @@ -47,8 +47,10 @@ public class PropertySourcesPlaceholdersResolver implements PlaceholdersResolver public PropertySourcesPlaceholdersResolver(Iterable> sources, PropertyPlaceholderHelper helper) { this.sources = sources; - this.helper = (helper != null) ? helper : new PropertyPlaceholderHelper(SystemPropertyUtils.PLACEHOLDER_PREFIX, - SystemPropertyUtils.PLACEHOLDER_SUFFIX, SystemPropertyUtils.VALUE_SEPARATOR, true); + this.helper = (helper != null) ? helper + : new PropertyPlaceholderHelper(SystemPropertyUtils.PLACEHOLDER_PREFIX, + SystemPropertyUtils.PLACEHOLDER_SUFFIX, SystemPropertyUtils.VALUE_SEPARATOR, + SystemPropertyUtils.ESCAPE_CHARACTER, true); } @Override