Merge branch '3.5.x'

Closes gh-46801
This commit is contained in:
Stéphane Nicoll 2025-08-12 11:27:56 +02:00
commit 8f44c89850
6 changed files with 30 additions and 37 deletions

View File

@ -56,7 +56,7 @@ class ArchitectureCheckTests {
@Test @Test
void whenPackagesAreTangledTaskFailsAndWritesAReport() throws IOException { void whenPackagesAreTangledTaskFailsAndWritesAReport() throws IOException {
runGradleWithCompiledClasses("tangled", runGradleWithCompiledClasses("tangled",
shouldHaveFailureReportWithMessage("slices matching '(**)' should be free of cycles")); shouldHaveFailureReportWithMessages("slices matching '(**)' should be free of cycles"));
} }
@Test @Test
@ -67,7 +67,7 @@ class ArchitectureCheckTests {
@Test @Test
void whenBeanPostProcessorBeanMethodIsNotStaticTaskFailsAndWritesAReport() throws IOException { void whenBeanPostProcessorBeanMethodIsNotStaticTaskFailsAndWritesAReport() throws IOException {
runGradleWithCompiledClasses("bpp/nonstatic", runGradleWithCompiledClasses("bpp/nonstatic",
shouldHaveFailureReportWithMessage( shouldHaveFailureReportWithMessages(
"methods that are annotated with @Bean and have raw return type assignable " "methods that are annotated with @Bean and have raw return type assignable "
+ "to org.springframework.beans.factory.config.BeanPostProcessor")); + "to org.springframework.beans.factory.config.BeanPostProcessor"));
} }
@ -75,7 +75,7 @@ class ArchitectureCheckTests {
@Test @Test
void whenBeanPostProcessorBeanMethodIsStaticAndHasUnsafeParametersTaskFailsAndWritesAReport() throws IOException { void whenBeanPostProcessorBeanMethodIsStaticAndHasUnsafeParametersTaskFailsAndWritesAReport() throws IOException {
runGradleWithCompiledClasses("bpp/unsafeparameters", runGradleWithCompiledClasses("bpp/unsafeparameters",
shouldHaveFailureReportWithMessage( shouldHaveFailureReportWithMessages(
"methods that are annotated with @Bean and have raw return type assignable " "methods that are annotated with @Bean and have raw return type assignable "
+ "to org.springframework.beans.factory.config.BeanPostProcessor")); + "to org.springframework.beans.factory.config.BeanPostProcessor"));
} }
@ -95,14 +95,14 @@ class ArchitectureCheckTests {
@Test @Test
void whenBeanFactoryPostProcessorBeanMethodIsNotStaticTaskFailsAndWritesAReport() throws IOException { void whenBeanFactoryPostProcessorBeanMethodIsNotStaticTaskFailsAndWritesAReport() throws IOException {
runGradleWithCompiledClasses("bfpp/nonstatic", runGradleWithCompiledClasses("bfpp/nonstatic",
shouldHaveFailureReportWithMessage("methods that are annotated with @Bean and have raw return " shouldHaveFailureReportWithMessages("methods that are annotated with @Bean and have raw return "
+ "type assignable to org.springframework.beans.factory.config.BeanFactoryPostProcessor")); + "type assignable to org.springframework.beans.factory.config.BeanFactoryPostProcessor"));
} }
@Test @Test
void whenBeanFactoryPostProcessorBeanMethodIsStaticAndHasParametersTaskFailsAndWritesAReport() throws IOException { void whenBeanFactoryPostProcessorBeanMethodIsStaticAndHasParametersTaskFailsAndWritesAReport() throws IOException {
runGradleWithCompiledClasses("bfpp/parameters", runGradleWithCompiledClasses("bfpp/parameters",
shouldHaveFailureReportWithMessage("methods that are annotated with @Bean and have raw return " shouldHaveFailureReportWithMessages("methods that are annotated with @Bean and have raw return "
+ "type assignable to org.springframework.beans.factory.config.BeanFactoryPostProcessor")); + "type assignable to org.springframework.beans.factory.config.BeanFactoryPostProcessor"));
} }
@ -114,7 +114,7 @@ class ArchitectureCheckTests {
@Test @Test
void whenClassLoadsResourceUsingResourceUtilsTaskFailsAndWritesReport() throws IOException { void whenClassLoadsResourceUsingResourceUtilsTaskFailsAndWritesReport() throws IOException {
runGradleWithCompiledClasses("resources/loads", shouldHaveFailureReportWithMessage( runGradleWithCompiledClasses("resources/loads", shouldHaveFailureReportWithMessages(
"no classes should call method where target owner type org.springframework.util.ResourceUtils and target name 'getURL'")); "no classes should call method where target owner type org.springframework.util.ResourceUtils and target name 'getURL'"));
} }
@ -130,26 +130,26 @@ class ArchitectureCheckTests {
@Test @Test
void whenClassCallsObjectsRequireNonNullWithMessageTaskFailsAndWritesReport() throws IOException { void whenClassCallsObjectsRequireNonNullWithMessageTaskFailsAndWritesReport() throws IOException {
runGradleWithCompiledClasses("objects/requireNonNullWithString", shouldHaveFailureReportWithMessage( runGradleWithCompiledClasses("objects/requireNonNullWithString", shouldHaveFailureReportWithMessages(
"no classes should call method Objects.requireNonNull(Object, String)")); "no classes should call method Objects.requireNonNull(Object, String)"));
} }
@Test @Test
void whenClassCallsObjectsRequireNonNullWithSupplierTaskFailsAndWritesReport() throws IOException { void whenClassCallsObjectsRequireNonNullWithSupplierTaskFailsAndWritesReport() throws IOException {
runGradleWithCompiledClasses("objects/requireNonNullWithSupplier", shouldHaveFailureReportWithMessage( runGradleWithCompiledClasses("objects/requireNonNullWithSupplier", shouldHaveFailureReportWithMessages(
"no classes should call method Objects.requireNonNull(Object, Supplier)")); "no classes should call method Objects.requireNonNull(Object, Supplier)"));
} }
@Test @Test
void whenClassCallsStringToUpperCaseWithoutLocaleFailsAndWritesReport() throws IOException { void whenClassCallsStringToUpperCaseWithoutLocaleFailsAndWritesReport() throws IOException {
runGradleWithCompiledClasses("string/toUpperCase", runGradleWithCompiledClasses("string/toUpperCase",
shouldHaveFailureReportWithMessage("because String.toUpperCase(Locale.ROOT) should be used instead")); shouldHaveFailureReportWithMessages("because String.toUpperCase(Locale.ROOT) should be used instead"));
} }
@Test @Test
void whenClassCallsStringToLowerCaseWithoutLocaleFailsAndWritesReport() throws IOException { void whenClassCallsStringToLowerCaseWithoutLocaleFailsAndWritesReport() throws IOException {
runGradleWithCompiledClasses("string/toLowerCase", runGradleWithCompiledClasses("string/toLowerCase",
shouldHaveFailureReportWithMessage("because String.toLowerCase(Locale.ROOT) should be used instead")); shouldHaveFailureReportWithMessages("because String.toLowerCase(Locale.ROOT) should be used instead"));
} }
@Test @Test
@ -164,7 +164,7 @@ class ArchitectureCheckTests {
@Test @Test
void whenBeanMethodExposePrivateTypeShouldFailAndWriteReport() throws IOException { void whenBeanMethodExposePrivateTypeShouldFailAndWriteReport() throws IOException {
runGradleWithCompiledClasses("beans/privatebean", shouldHaveFailureReportWithMessage( runGradleWithCompiledClasses("beans/privatebean", shouldHaveFailureReportWithMessages(
"methods that are annotated with @Bean should not return types declared with the PRIVATE modifier," "methods that are annotated with @Bean should not return types declared with the PRIVATE modifier,"
+ " as such types are incompatible with Spring AOT processing", + " as such types are incompatible with Spring AOT processing",
"Method <org.springframework.boot.build.architecture.beans.privatebean.PrivateBean.myBean()> " "Method <org.springframework.boot.build.architecture.beans.privatebean.PrivateBean.myBean()> "
@ -173,7 +173,7 @@ class ArchitectureCheckTests {
} }
@Test @Test
void whenBeanMethodExposeNonPrivateTypeeShouldNotFail() throws IOException { void whenBeanMethodExposeNonPrivateTypeShouldNotFail() throws IOException {
runGradleWithCompiledClasses("beans/regular", shouldHaveEmptyFailureReport()); runGradleWithCompiledClasses("beans/regular", shouldHaveEmptyFailureReport());
} }
@ -207,7 +207,7 @@ class ArchitectureCheckTests {
} }
} }
"""); """);
runGradle(shouldHaveFailureReportWithMessage("methods that are annotated with @Bean and have raw return " runGradle(shouldHaveFailureReportWithMessages("methods that are annotated with @Bean and have raw return "
+ "type assignable to org.springframework.beans.factory.config.BeanPostProcessor ")); + "type assignable to org.springframework.beans.factory.config.BeanPostProcessor "));
} }
@ -224,7 +224,7 @@ class ArchitectureCheckTests {
}; };
} }
private Consumer<GradleRunner> shouldHaveFailureReportWithMessage(String... messages) { private Consumer<GradleRunner> shouldHaveFailureReportWithMessages(String... messages) {
return (gradleRunner) -> { return (gradleRunner) -> {
assertThat(gradleRunner.buildAndFail().getOutput()).contains("BUILD FAILED") assertThat(gradleRunner.buildAndFail().getOutput()).contains("BUILD FAILED")
.contains("Task :checkArchitectureMain FAILED"); .contains("Task :checkArchitectureMain FAILED");

View File

@ -30,7 +30,7 @@ class MyApplication {
private val logger: Log = LogFactory.getLog(MyApplication::class.java) private val logger: Log = LogFactory.getLog(MyApplication::class.java)
@RequestMapping("/") @RequestMapping("/")
fun hello(): String { fun home(): String {
logger.info("home() has been called") logger.info("home() has been called")
return "Hello, World!" return "Hello, World!"
} }

View File

@ -21,7 +21,7 @@ import java.nio.charset.StandardCharsets;
import java.util.HexFormat; import java.util.HexFormat;
/** /**
* Utility to decode URL strings. Copied frm Spring Framework's {@code StringUtils} as we * Utility to decode URL strings. Copied from Spring Framework's {@code StringUtils} as we
* cannot depend on it in the loader. * cannot depend on it in the loader.
* *
* @author Phillip Webb * @author Phillip Webb
@ -52,6 +52,7 @@ public final class UrlDecoder {
* @param charset the character encoding to use to decode the "<i>{@code %xy}</i>" * @param charset the character encoding to use to decode the "<i>{@code %xy}</i>"
* sequences * sequences
* @return the decoded value * @return the decoded value
* @since 4.0.0
*/ */
public static String decode(String source, Charset charset) { public static String decode(String source, Charset charset) {
int length = source.length(); int length = source.length();

View File

@ -31,7 +31,6 @@ import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.test.util.ReflectionTestUtils;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
@ -70,8 +69,7 @@ class MetricsAspectsAutoConfigurationTests {
this.contextRunner.withBean(ValueExpressionResolver.class, () -> mock(ValueExpressionResolver.class)) this.contextRunner.withBean(ValueExpressionResolver.class, () -> mock(ValueExpressionResolver.class))
.run((context) -> { .run((context) -> {
assertThat(context).hasSingleBean(TimedAspect.class).hasSingleBean(MeterTagAnnotationHandler.class); assertThat(context).hasSingleBean(TimedAspect.class).hasSingleBean(MeterTagAnnotationHandler.class);
assertThat( assertThat(context.getBean(TimedAspect.class)).extracting("meterTagAnnotationHandler")
ReflectionTestUtils.getField(context.getBean(TimedAspect.class), "meterTagAnnotationHandler"))
.isSameAs(context.getBean(MeterTagAnnotationHandler.class)); .isSameAs(context.getBean(MeterTagAnnotationHandler.class));
}); });
} }
@ -83,8 +81,7 @@ class MetricsAspectsAutoConfigurationTests {
() -> new MeterTagAnnotationHandler(null, null)) () -> new MeterTagAnnotationHandler(null, null))
.run((context) -> { .run((context) -> {
assertThat(context).hasSingleBean(TimedAspect.class).hasSingleBean(MeterTagAnnotationHandler.class); assertThat(context).hasSingleBean(TimedAspect.class).hasSingleBean(MeterTagAnnotationHandler.class);
assertThat( assertThat(context.getBean(TimedAspect.class)).extracting("meterTagAnnotationHandler")
ReflectionTestUtils.getField(context.getBean(TimedAspect.class), "meterTagAnnotationHandler"))
.isSameAs(context.getBean("customMeterTagAnnotationHandler")); .isSameAs(context.getBean("customMeterTagAnnotationHandler"));
}); });
} }
@ -95,8 +92,7 @@ class MetricsAspectsAutoConfigurationTests {
.run((context) -> { .run((context) -> {
assertThat(context).hasSingleBean(CountedAspect.class) assertThat(context).hasSingleBean(CountedAspect.class)
.hasSingleBean(CountedMeterTagAnnotationHandler.class); .hasSingleBean(CountedMeterTagAnnotationHandler.class);
assertThat( assertThat(context.getBean(CountedAspect.class)).extracting("meterTagAnnotationHandler")
ReflectionTestUtils.getField(context.getBean(CountedAspect.class), "meterTagAnnotationHandler"))
.isSameAs(context.getBean(CountedMeterTagAnnotationHandler.class)); .isSameAs(context.getBean(CountedMeterTagAnnotationHandler.class));
}); });
} }
@ -109,8 +105,7 @@ class MetricsAspectsAutoConfigurationTests {
.run((context) -> { .run((context) -> {
assertThat(context).hasSingleBean(CountedAspect.class) assertThat(context).hasSingleBean(CountedAspect.class)
.hasSingleBean(CountedMeterTagAnnotationHandler.class); .hasSingleBean(CountedMeterTagAnnotationHandler.class);
assertThat( assertThat(context.getBean(CountedAspect.class)).extracting("meterTagAnnotationHandler")
ReflectionTestUtils.getField(context.getBean(CountedAspect.class), "meterTagAnnotationHandler"))
.isSameAs(context.getBean(CountedMeterTagAnnotationHandler.class)); .isSameAs(context.getBean(CountedMeterTagAnnotationHandler.class));
}); });
} }

View File

@ -78,7 +78,7 @@ class StackdriverPropertiesConfigAdapterTests
} }
@Test @Test
void whenPropertiesAutoCreateMetricDescriptorsIsSetAdapterAutoCreateMetricDescriptorReturnsIt() { void whenPropertiesAutoCreateMetricDescriptorsIsSetAdapterAutoCreateMetricDescriptorsReturnsIt() {
StackdriverProperties properties = new StackdriverProperties(); StackdriverProperties properties = new StackdriverProperties();
properties.setAutoCreateMetricDescriptors(false); properties.setAutoCreateMetricDescriptors(false);
assertThat(new StackdriverPropertiesConfigAdapter(properties).autoCreateMetricDescriptors()).isFalse(); assertThat(new StackdriverPropertiesConfigAdapter(properties).autoCreateMetricDescriptors()).isFalse();

View File

@ -27,7 +27,7 @@ import io.micrometer.observation.ObservationRegistry.ObservationConfig;
import io.micrometer.tracing.Tracer; import io.micrometer.tracing.Tracer;
import io.micrometer.tracing.handler.TracingAwareMeterObservationHandler; import io.micrometer.tracing.handler.TracingAwareMeterObservationHandler;
import io.micrometer.tracing.handler.TracingObservationHandler; import io.micrometer.tracing.handler.TracingObservationHandler;
import org.assertj.core.extractor.Extractors; import org.assertj.core.api.InstanceOfAssertFactories;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;
@ -63,7 +63,6 @@ class TracingAndMeterObservationHandlerGroupTests {
} }
@Test @Test
@SuppressWarnings("unchecked")
void registerMembersWrapsMeterObservationHandlersAndRegistersDistinctGroups() { void registerMembersWrapsMeterObservationHandlersAndRegistersDistinctGroups() {
Tracer tracer = mock(Tracer.class); Tracer tracer = mock(Tracer.class);
TracingAndMeterObservationHandlerGroup group = new TracingAndMeterObservationHandlerGroup(tracer); TracingAndMeterObservationHandlerGroup group = new TracingAndMeterObservationHandlerGroup(tracer);
@ -79,16 +78,14 @@ class TracingAndMeterObservationHandlerGroupTests {
List<ObservationHandler<?>> actualComposites = handlerCaptor.getAllValues(); List<ObservationHandler<?>> actualComposites = handlerCaptor.getAllValues();
assertThat(actualComposites).hasSize(2); assertThat(actualComposites).hasSize(2);
ObservationHandler<?> tracingComposite = actualComposites.get(0); ObservationHandler<?> tracingComposite = actualComposites.get(0);
assertThat(tracingComposite).isInstanceOf(FirstMatchingCompositeObservationHandler.class); assertThat(tracingComposite).isInstanceOf(FirstMatchingCompositeObservationHandler.class)
List<ObservationHandler<?>> tracingHandlers = (List<ObservationHandler<?>>) Extractors.byName("handlers") .extracting("handlers", InstanceOfAssertFactories.LIST)
.apply(tracingComposite); .containsExactly(tracingHandler1, tracingHandler2);
assertThat(tracingHandlers).containsExactly(tracingHandler1, tracingHandler2);
ObservationHandler<?> metricsComposite = actualComposites.get(1); ObservationHandler<?> metricsComposite = actualComposites.get(1);
assertThat(metricsComposite).isInstanceOf(FirstMatchingCompositeObservationHandler.class); assertThat(metricsComposite).isInstanceOf(FirstMatchingCompositeObservationHandler.class)
List<ObservationHandler<?>> metricsHandlers = (List<ObservationHandler<?>>) Extractors.byName("handlers") .extracting("handlers", InstanceOfAssertFactories.LIST)
.apply(metricsComposite); .extracting("delegate")
assertThat(metricsHandlers).hasSize(2); .containsExactly(meterHandler1, meterHandler2);
assertThat(metricsHandlers).extracting("delegate").containsExactly(meterHandler1, meterHandler2);
} }
@Test @Test