From 1bc82d241a81183ddba1aab081a96a93e90c364d Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 9 Oct 2025 20:26:59 +0200 Subject: [PATCH 1/3] Remove deprecation on CandidateComponentsIndex(Loader) Closes gh-35472 --- .../processor/CandidateComponentsIndexer.java | 5 ++-- .../StandardStereotypesProvider.java | 4 ++-- ...athScanningCandidateComponentProvider.java | 12 +++++----- .../index/CandidateComponentsIndex.java | 6 ++--- .../index/CandidateComponentsIndexLoader.java | 3 --- .../ClassPathBeanDefinitionScannerTests.java | 24 ++++++++++++------- .../CandidateComponentsIndexLoaderTests.java | 2 -- .../index/CandidateComponentsIndexTests.java | 2 -- 8 files changed, 28 insertions(+), 30 deletions(-) diff --git a/spring-context-indexer/src/main/java/org/springframework/context/index/processor/CandidateComponentsIndexer.java b/spring-context-indexer/src/main/java/org/springframework/context/index/processor/CandidateComponentsIndexer.java index 9092e9d2150..43b16ac8486 100644 --- a/spring-context-indexer/src/main/java/org/springframework/context/index/processor/CandidateComponentsIndexer.java +++ b/spring-context-indexer/src/main/java/org/springframework/context/index/processor/CandidateComponentsIndexer.java @@ -37,12 +37,13 @@ import javax.lang.model.element.TypeElement; /** * Annotation {@link Processor} that writes a {@link CandidateComponentsMetadata} - * file for spring components. + * file for Spring components. * * @author Stephane Nicoll * @author Juergen Hoeller * @since 5.0 - * @deprecated as of 6.1, in favor of the AOT engine. + * @deprecated as of 6.1, in favor of the AOT engine and the forthcoming + * support for an AOT-generated Spring components index */ @Deprecated(since = "6.1", forRemoval = true) public class CandidateComponentsIndexer implements Processor { diff --git a/spring-context-indexer/src/main/java/org/springframework/context/index/processor/StandardStereotypesProvider.java b/spring-context-indexer/src/main/java/org/springframework/context/index/processor/StandardStereotypesProvider.java index e8d66623e5d..e93138b797c 100644 --- a/spring-context-indexer/src/main/java/org/springframework/context/index/processor/StandardStereotypesProvider.java +++ b/spring-context-indexer/src/main/java/org/springframework/context/index/processor/StandardStereotypesProvider.java @@ -25,8 +25,8 @@ import javax.lang.model.element.ElementKind; /** * A {@link StereotypesProvider} that extracts a stereotype for each - * {@code jakarta.*} or {@code javax.*} annotation present on a class or - * interface. + * {@code jakarta.*} or {@code javax.*} annotation present + * on a class or interface. * * @author Stephane Nicoll * @since 5.0 diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java b/spring-context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java index 9b13476ae72..832b4a18153 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java @@ -452,9 +452,9 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC private Set scanCandidateComponents(String basePackage) { Set candidates = new LinkedHashSet<>(); try { - String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + + String packageSearchPattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) + '/' + this.resourcePattern; - Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath); + Resource[] resources = getResourcePatternResolver().getResources(packageSearchPattern); boolean traceEnabled = logger.isTraceEnabled(); boolean debugEnabled = logger.isDebugEnabled(); for (Resource resource : resources) { @@ -537,13 +537,13 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC * @return whether the class qualifies as a candidate component */ protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException { - for (TypeFilter tf : this.excludeFilters) { - if (tf.match(metadataReader, getMetadataReaderFactory())) { + for (TypeFilter filter : this.excludeFilters) { + if (filter.match(metadataReader, getMetadataReaderFactory())) { return false; } } - for (TypeFilter tf : this.includeFilters) { - if (tf.match(metadataReader, getMetadataReaderFactory())) { + for (TypeFilter filter : this.includeFilters) { + if (filter.match(metadataReader, getMetadataReaderFactory())) { return isConditionMatch(metadataReader); } } diff --git a/spring-context/src/main/java/org/springframework/context/index/CandidateComponentsIndex.java b/spring-context/src/main/java/org/springframework/context/index/CandidateComponentsIndex.java index 49c20023f82..e13674d6b11 100644 --- a/spring-context/src/main/java/org/springframework/context/index/CandidateComponentsIndex.java +++ b/spring-context/src/main/java/org/springframework/context/index/CandidateComponentsIndex.java @@ -45,9 +45,7 @@ import org.springframework.util.MultiValueMap; * * @author Stephane Nicoll * @since 5.0 - * @deprecated as of 6.1, in favor of the AOT engine. */ -@Deprecated(since = "6.1", forRemoval = true) public class CandidateComponentsIndex { private static final AntPathMatcher pathMatcher = new AntPathMatcher("."); @@ -83,7 +81,7 @@ public class CandidateComponentsIndex { public Set getCandidateTypes(String basePackage, String stereotype) { List candidates = this.index.get(stereotype); if (candidates != null) { - return candidates.parallelStream() + return candidates.stream() .filter(t -> t.match(basePackage)) .map(t -> t.type) .collect(Collectors.toSet()); @@ -94,7 +92,7 @@ public class CandidateComponentsIndex { private static class Entry { - private final String type; + final String type; private final String packageName; diff --git a/spring-context/src/main/java/org/springframework/context/index/CandidateComponentsIndexLoader.java b/spring-context/src/main/java/org/springframework/context/index/CandidateComponentsIndexLoader.java index 10e39df9164..f098164027b 100644 --- a/spring-context/src/main/java/org/springframework/context/index/CandidateComponentsIndexLoader.java +++ b/spring-context/src/main/java/org/springframework/context/index/CandidateComponentsIndexLoader.java @@ -38,10 +38,7 @@ import org.springframework.util.ConcurrentReferenceHashMap; * * @author Stephane Nicoll * @since 5.0 - * @deprecated as of 6.1, in favor of the AOT engine. */ -@Deprecated(since = "6.1", forRemoval = true) -@SuppressWarnings("removal") public final class CandidateComponentsIndexLoader { /** diff --git a/spring-context/src/test/java/org/springframework/context/annotation/ClassPathBeanDefinitionScannerTests.java b/spring-context/src/test/java/org/springframework/context/annotation/ClassPathBeanDefinitionScannerTests.java index 88619e095a5..031639434b3 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/ClassPathBeanDefinitionScannerTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/ClassPathBeanDefinitionScannerTests.java @@ -61,6 +61,7 @@ class ClassPathBeanDefinitionScannerTests { GenericApplicationContext context = new GenericApplicationContext(); ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context); int beanCount = scanner.scan(BASE_PACKAGE); + assertThat(beanCount).isGreaterThanOrEqualTo(12); assertThat(context.containsBean("serviceInvocationCounter")).isTrue(); assertThat(context.containsBean("fooServiceImpl")).isTrue(); @@ -73,8 +74,8 @@ class ClassPathBeanDefinitionScannerTests { assertThat(context.containsBean(AnnotationConfigUtils.COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)).isTrue(); assertThat(context.containsBean(AnnotationConfigUtils.EVENT_LISTENER_PROCESSOR_BEAN_NAME)).isTrue(); assertThat(context.containsBean(AnnotationConfigUtils.EVENT_LISTENER_FACTORY_BEAN_NAME)).isTrue(); - context.refresh(); + context.refresh(); FooServiceImpl fooService = context.getBean("fooServiceImpl", FooServiceImpl.class); assertThat(context.getDefaultListableBeanFactory().containsSingleton("myNamedComponent")).isTrue(); assertThat(fooService.foo(123)).isEqualTo("bar"); @@ -157,6 +158,7 @@ class ClassPathBeanDefinitionScannerTests { ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context); int beanCount = scanner.scan(BASE_PACKAGE); + assertThat(beanCount).isGreaterThanOrEqualTo(12); ClassPathBeanDefinitionScanner scanner2 = new ClassPathBeanDefinitionScanner(context) { @@ -182,8 +184,8 @@ class ClassPathBeanDefinitionScannerTests { ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context); scanner.setIncludeAnnotationConfig(false); int beanCount = scanner.scan(BASE_PACKAGE); - assertThat(beanCount).isGreaterThanOrEqualTo(7); + assertThat(beanCount).isGreaterThanOrEqualTo(7); assertThat(context.containsBean("serviceInvocationCounter")).isTrue(); assertThat(context.containsBean("fooServiceImpl")).isTrue(); assertThat(context.containsBean("stubFooDao")).isTrue(); @@ -482,12 +484,14 @@ class ClassPathBeanDefinitionScannerTests { ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context); scanner.setBeanNameGenerator(new TestBeanNameGenerator()); int beanCount = scanner.scan(BASE_PACKAGE); - assertThat(beanCount).isGreaterThanOrEqualTo(12); - context.refresh(); + assertThat(beanCount).isGreaterThanOrEqualTo(12); + + context.refresh(); FooServiceImpl fooService = context.getBean("fooService", FooServiceImpl.class); StaticListableBeanFactory myBf = (StaticListableBeanFactory) context.getBean("myBf"); MessageSource ms = (MessageSource) context.getBean("messageSource"); + assertThat(fooService.isInitCalled()).isTrue(); assertThat(fooService.foo(123)).isEqualTo("bar"); assertThat(fooService.lookupFoo(123)).isEqualTo("bar"); @@ -509,9 +513,10 @@ class ClassPathBeanDefinitionScannerTests { scanner.setIncludeAnnotationConfig(false); scanner.setBeanNameGenerator(new TestBeanNameGenerator()); int beanCount = scanner.scan(BASE_PACKAGE); - assertThat(beanCount).isGreaterThanOrEqualTo(7); - context.refresh(); + assertThat(beanCount).isGreaterThanOrEqualTo(7); + + context.refresh(); try { context.getBean("fooService"); } @@ -545,9 +550,10 @@ class ClassPathBeanDefinitionScannerTests { scanner.setAutowireCandidatePatterns("*NoSuchDao"); scanner.scan(BASE_PACKAGE); context.refresh(); - assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() -> - context.getBean("fooService")) - .satisfies(ex -> assertThat(ex.getMostSpecificCause()).isInstanceOf(NoSuchBeanDefinitionException.class)); + assertThatExceptionOfType(BeanCreationException.class) + .isThrownBy(() -> context.getBean("fooService")) + .satisfies(ex -> + assertThat(ex.getMostSpecificCause()).isInstanceOf(NoSuchBeanDefinitionException.class)); } diff --git a/spring-context/src/test/java/org/springframework/context/index/CandidateComponentsIndexLoaderTests.java b/spring-context/src/test/java/org/springframework/context/index/CandidateComponentsIndexLoaderTests.java index 8161f4f9359..32d4eb7c2a0 100644 --- a/spring-context/src/test/java/org/springframework/context/index/CandidateComponentsIndexLoaderTests.java +++ b/spring-context/src/test/java/org/springframework/context/index/CandidateComponentsIndexLoaderTests.java @@ -32,8 +32,6 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException; * * @author Stephane Nicoll */ -@Deprecated -@SuppressWarnings("removal") public class CandidateComponentsIndexLoaderTests { @Test diff --git a/spring-context/src/test/java/org/springframework/context/index/CandidateComponentsIndexTests.java b/spring-context/src/test/java/org/springframework/context/index/CandidateComponentsIndexTests.java index 2fbf17ce949..32befc3230e 100644 --- a/spring-context/src/test/java/org/springframework/context/index/CandidateComponentsIndexTests.java +++ b/spring-context/src/test/java/org/springframework/context/index/CandidateComponentsIndexTests.java @@ -30,8 +30,6 @@ import static org.assertj.core.api.Assertions.assertThat; * * @author Stephane Nicoll */ -@Deprecated -@SuppressWarnings("removal") public class CandidateComponentsIndexTests { @Test From 2ee34a5632a15e2e5f9dcb46d1999139cfc31011 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 9 Oct 2025 20:30:25 +0200 Subject: [PATCH 2/3] Add MySQLTableMetaDataProvider for correct generated-keys support Closes gh-35593 --- .../metadata/MySQLTableMetaDataProvider.java | 36 +++++++++++++++++++ .../TableMetaDataProviderFactory.java | 3 ++ 2 files changed, 39 insertions(+) create mode 100644 spring-jdbc/src/main/java/org/springframework/jdbc/core/metadata/MySQLTableMetaDataProvider.java diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/metadata/MySQLTableMetaDataProvider.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/metadata/MySQLTableMetaDataProvider.java new file mode 100644 index 00000000000..39632ba9bed --- /dev/null +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/metadata/MySQLTableMetaDataProvider.java @@ -0,0 +1,36 @@ +/* + * Copyright 2002-present 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.jdbc.core.metadata; + +import java.sql.DatabaseMetaData; +import java.sql.SQLException; + +/** + * The MySQL/MariaDB specific implementation of {@link TableMetaDataProvider}. + * Sets {@link #setGeneratedKeysColumnNameArraySupported} to {@code false}. + * + * @author Juergen Hoeller + * @since 6.2.12 + */ +public class MySQLTableMetaDataProvider extends GenericTableMetaDataProvider { + + public MySQLTableMetaDataProvider(DatabaseMetaData databaseMetaData) throws SQLException { + super(databaseMetaData); + setGeneratedKeysColumnNameArraySupported(false); + } + +} diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/metadata/TableMetaDataProviderFactory.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/metadata/TableMetaDataProviderFactory.java index 640d76ec2e0..f0301f92695 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/metadata/TableMetaDataProviderFactory.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/metadata/TableMetaDataProviderFactory.java @@ -66,6 +66,9 @@ public final class TableMetaDataProviderFactory { else if ("HSQL Database Engine".equals(databaseProductName)) { provider = new HsqlTableMetaDataProvider(databaseMetaData); } + else if ("MySQL".equals(databaseProductName) || "MariaDB".equals(databaseProductName)) { + provider = new MySQLTableMetaDataProvider(databaseMetaData); + } else { provider = new GenericTableMetaDataProvider(databaseMetaData); } From c2a66e723f36497100589cea78b2b2b910582479 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 9 Oct 2025 20:34:03 +0200 Subject: [PATCH 3/3] Polishing --- .../aop/interceptor/AsyncExecutionInterceptorTests.java | 7 ++++--- .../org/springframework/context/annotation/AdviceMode.java | 7 +++---- .../context/aot/AotApplicationContextInitializer.java | 4 ++-- .../jdbc/core/metadata/TableMetaDataContext.java | 3 +-- .../jdbc/core/metadata/TableMetaDataProvider.java | 6 ++---- .../annotation/ResponseEntityReturnValueHandler.java | 2 +- 6 files changed, 13 insertions(+), 16 deletions(-) diff --git a/spring-aop/src/test/java/org/springframework/aop/interceptor/AsyncExecutionInterceptorTests.java b/spring-aop/src/test/java/org/springframework/aop/interceptor/AsyncExecutionInterceptorTests.java index 70b34728277..ec751f46e33 100644 --- a/spring-aop/src/test/java/org/springframework/aop/interceptor/AsyncExecutionInterceptorTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/interceptor/AsyncExecutionInterceptorTests.java @@ -37,7 +37,6 @@ import static org.mockito.Mockito.verify; * Tests for {@link AsyncExecutionInterceptor}. * * @author Bao Ngo - * @since 7.0 */ class AsyncExecutionInterceptorTests { @@ -62,11 +61,13 @@ class AsyncExecutionInterceptorTests { O run(); } + static class FutureRunner implements GenericRunner> { + @Override public Future run() { - return CompletableFuture.runAsync(() -> { - }); + return CompletableFuture.runAsync(() -> {}); } } + } diff --git a/spring-context/src/main/java/org/springframework/context/annotation/AdviceMode.java b/spring-context/src/main/java/org/springframework/context/annotation/AdviceMode.java index 2f33ebe3af2..ac06381da6a 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/AdviceMode.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/AdviceMode.java @@ -17,19 +17,18 @@ package org.springframework.context.annotation; /** - * Enumeration used to determine whether JDK proxy-based or + * Enumeration used to determine whether JDK/CGLIB proxy-based or * AspectJ weaving-based advice should be applied. * * @author Chris Beams * @since 3.1 - * @see org.springframework.scheduling.annotation.EnableAsync#mode() * @see org.springframework.scheduling.annotation.AsyncConfigurationSelector#selectImports - * @see org.springframework.transaction.annotation.EnableTransactionManagement#mode() + * @see org.springframework.scheduling.annotation.EnableAsync#mode() */ public enum AdviceMode { /** - * JDK proxy-based advice. + * JDK/CGLIB proxy-based advice. */ PROXY, diff --git a/spring-context/src/main/java/org/springframework/context/aot/AotApplicationContextInitializer.java b/spring-context/src/main/java/org/springframework/context/aot/AotApplicationContextInitializer.java index 3c98c3d9b95..f4348640af1 100644 --- a/spring-context/src/main/java/org/springframework/context/aot/AotApplicationContextInitializer.java +++ b/spring-context/src/main/java/org/springframework/context/aot/AotApplicationContextInitializer.java @@ -63,13 +63,13 @@ public interface AotApplicationContextInitializer void initialize( C applicationContext, String... initializerClassNames) { + Log logger = LogFactory.getLog(AotApplicationContextInitializer.class); ClassLoader classLoader = applicationContext.getClassLoader(); logger.debug("Initializing ApplicationContext with AOT"); for (String initializerClassName : initializerClassNames) { logger.trace(LogMessage.format("Applying %s", initializerClassName)); - instantiateInitializer(initializerClassName, classLoader) - .initialize(applicationContext); + instantiateInitializer(initializerClassName, classLoader).initialize(applicationContext); } } diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/metadata/TableMetaDataContext.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/metadata/TableMetaDataContext.java index e6ab016f6d9..40ff8fd4764 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/metadata/TableMetaDataContext.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/metadata/TableMetaDataContext.java @@ -417,8 +417,7 @@ public class TableMetaDataContext { } /** - * Does this database support a column name String array for retrieving generated - * keys? + * Does this database support a column name String array for retrieving generated keys? * @see java.sql.Connection#createStruct(String, Object[]) */ public boolean isGeneratedKeysColumnNameArraySupported() { diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/metadata/TableMetaDataProvider.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/metadata/TableMetaDataProvider.java index 12985758325..6b991a1c458 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/metadata/TableMetaDataProvider.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/metadata/TableMetaDataProvider.java @@ -136,16 +136,14 @@ public interface TableMetaDataProvider { String getSimpleQueryForGetGeneratedKey(String tableName, String keyColumnName); /** - * Does this database support a column name String array for retrieving generated - * keys? + * Does this database support a column name String array for retrieving generated keys? * @see java.sql.Connection#createStruct(String, Object[]) */ boolean isGeneratedKeysColumnNameArraySupported(); /** * Get the string used to quote SQL identifiers. - *

This method returns a space ({@code " "}) if identifier quoting is not - * supported. + *

This method returns a space ({@code " "}) if identifier quoting is not supported. * @return database identifier quote string * @since 6.1 * @see DatabaseMetaData#getIdentifierQuoteString() diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityReturnValueHandler.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityReturnValueHandler.java index 1409a79819d..bc6e4b56baf 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityReturnValueHandler.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityReturnValueHandler.java @@ -38,7 +38,7 @@ import org.springframework.web.method.support.ModelAndViewContainer; * provide enough information to decide via {@link #supportsReturnType}. * * @author Rossen Stoyanchev - * @since 7.0 + * @since 6.2.9 */ public class ResponseEntityReturnValueHandler implements HandlerMethodReturnValueHandler {