From 76d1995b37d72f0020d8ed5679aa522fc34baabc Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Mon, 5 Dec 2016 10:49:10 +0100 Subject: [PATCH] DATACMNS-952 - Switched from component scanning to SpringFactoriesLoader for internal extension points. Both the configuration code looking up Spring Data specific Jackson modules as well as the code detecting whether we have multiple Spring Data modules on the classpath used component scanning. That can have quite significant impact on startup times. This commit replaces the classpath scanning with the use of SpringFactoriesLoader that now requires modules that want to extend the behavior of Spring Data's web configuration or indicate a repository implementation being present by shipping a file called META-INF/spring.factories. Spring Data relies on the following keys: - org.springframework.data.web.config.SpringDataJacksonModules - list the Spring configuration classes that will expose additional Jackson modules that are supposed to be registered for Spring Data's Jackson web support - org.springframework.data.repository.core.support.RepositoryFactorySupport - list the Spring Data repository factory implementation class that implements repository support for your module. The general detection and configuration mechanism for user repositories is not affected by this. Currently Spring Data only uses the pure number of different entries for that key to switch into strict configuration mode in case we find more than one entry. --- .../RepositoryConfigurationDelegate.java | 16 +++----- .../config/EnableSpringDataWebSupport.java | 15 ++----- .../SpringDataJacksonConfiguration.java | 5 +-- .../web/config/SpringDataJacksonModules.java | 27 ++++++++++++ .../SpringDataWebConfigurationMixin.java | 41 ------------------- src/main/resources/META-INF/spring.factories | 1 + .../data/web/config/SampleMixin.java | 3 +- src/test/resources/META-INF/spring.factories | 1 + 8 files changed, 40 insertions(+), 69 deletions(-) create mode 100644 src/main/java/org/springframework/data/web/config/SpringDataJacksonModules.java delete mode 100644 src/main/java/org/springframework/data/web/config/SpringDataWebConfigurationMixin.java create mode 100644 src/main/resources/META-INF/spring.factories create mode 100644 src/test/resources/META-INF/spring.factories diff --git a/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationDelegate.java b/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationDelegate.java index 950e504cb..6b0718d91 100644 --- a/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationDelegate.java +++ b/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationDelegate.java @@ -25,12 +25,11 @@ import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanNameGenerator; -import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; import org.springframework.core.env.Environment; import org.springframework.core.env.EnvironmentCapable; import org.springframework.core.env.StandardEnvironment; import org.springframework.core.io.ResourceLoader; -import org.springframework.core.type.filter.AssignableTypeFilter; +import org.springframework.core.io.support.SpringFactoriesLoader; import org.springframework.data.repository.core.support.RepositoryFactorySupport; import org.springframework.util.Assert; @@ -48,7 +47,6 @@ public class RepositoryConfigurationDelegate { private static final String REPOSITORY_REGISTRATION = "Spring Data {} - Registering repository: {} - Interface: {} - Factory: {}"; private static final String MULTIPLE_MODULES = "Multiple Spring Data modules found, entering strict repository configuration mode!"; - private static final String MODULE_DETECTION_PACKAGE = "org.springframework.data.**.repository.support"; static final String FACTORY_BEAN_OBJECT_TYPE = "factoryBeanObjectType"; @@ -160,17 +158,13 @@ public class RepositoryConfigurationDelegate { */ private boolean multipleStoresDetected() { - ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false); - scanner.setEnvironment(environment); - scanner.setResourceLoader(resourceLoader); - scanner.addIncludeFilter(new AssignableTypeFilter(RepositoryFactorySupport.class)); - - if (scanner.findCandidateComponents(MODULE_DETECTION_PACKAGE).size() > 1) { + boolean multipleModulesFound = SpringFactoriesLoader + .loadFactoryNames(RepositoryFactorySupport.class, resourceLoader.getClassLoader()).size() > 1; + if (multipleModulesFound) { LOGGER.info(MULTIPLE_MODULES); - return true; } - return false; + return multipleModulesFound; } } diff --git a/src/main/java/org/springframework/data/web/config/EnableSpringDataWebSupport.java b/src/main/java/org/springframework/data/web/config/EnableSpringDataWebSupport.java index 3acf2f565..d895f8d8a 100644 --- a/src/main/java/org/springframework/data/web/config/EnableSpringDataWebSupport.java +++ b/src/main/java/org/springframework/data/web/config/EnableSpringDataWebSupport.java @@ -23,16 +23,14 @@ import java.lang.annotation.Target; import java.util.ArrayList; import java.util.List; -import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.EnvironmentAware; import org.springframework.context.ResourceLoaderAware; -import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.ImportSelector; import org.springframework.core.env.Environment; import org.springframework.core.io.ResourceLoader; +import org.springframework.core.io.support.SpringFactoriesLoader; import org.springframework.core.type.AnnotationMetadata; -import org.springframework.core.type.filter.AnnotationTypeFilter; import org.springframework.data.querydsl.QueryDslUtils; import org.springframework.data.web.PageableHandlerMethodArgumentResolver; import org.springframework.util.ClassUtils; @@ -124,15 +122,8 @@ public @interface EnableSpringDataWebSupport { : SpringDataWebConfiguration.class.getName()); if (JACKSON_PRESENT) { - - ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false); - provider.setEnvironment(environment); - provider.setResourceLoader(resourceLoader); - provider.addIncludeFilter(new AnnotationTypeFilter(SpringDataWebConfigurationMixin.class)); - - for (BeanDefinition definition : provider.findCandidateComponents("org.springframework.data")) { - imports.add(definition.getBeanClassName()); - } + imports.addAll( + SpringFactoriesLoader.loadFactoryNames(SpringDataJacksonModules.class, resourceLoader.getClassLoader())); } return imports.toArray(new String[imports.size()]); diff --git a/src/main/java/org/springframework/data/web/config/SpringDataJacksonConfiguration.java b/src/main/java/org/springframework/data/web/config/SpringDataJacksonConfiguration.java index 3fd412e50..5d9d60334 100644 --- a/src/main/java/org/springframework/data/web/config/SpringDataJacksonConfiguration.java +++ b/src/main/java/org/springframework/data/web/config/SpringDataJacksonConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 the original author or authors. + * Copyright 2014-2016 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. @@ -23,8 +23,7 @@ import org.springframework.data.geo.GeoModule; * * @author Oliver Gierke */ -@SpringDataWebConfigurationMixin -public class SpringDataJacksonConfiguration { +public class SpringDataJacksonConfiguration implements SpringDataJacksonModules { @Bean public GeoModule jacksonGeoModule() { diff --git a/src/main/java/org/springframework/data/web/config/SpringDataJacksonModules.java b/src/main/java/org/springframework/data/web/config/SpringDataJacksonModules.java new file mode 100644 index 000000000..ef030a84b --- /dev/null +++ b/src/main/java/org/springframework/data/web/config/SpringDataJacksonModules.java @@ -0,0 +1,27 @@ +/* + * Copyright 2016 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 + * + * http://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.data.web.config; + +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * Marker interface to describe configuration classes that ship Jackson modules that are supposed to be added to the + * Jackson {@link ObjectMapper} configured for {@link EnableSpringDataWebSupport}. + * + * @author Oliver Gierke + * @since 1.13 + */ +public interface SpringDataJacksonModules {} diff --git a/src/main/java/org/springframework/data/web/config/SpringDataWebConfigurationMixin.java b/src/main/java/org/springframework/data/web/config/SpringDataWebConfigurationMixin.java deleted file mode 100644 index 15b615245..000000000 --- a/src/main/java/org/springframework/data/web/config/SpringDataWebConfigurationMixin.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2015 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 - * - * http://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.data.web.config; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import org.springframework.context.annotation.Configuration; -import org.springframework.data.web.config.EnableSpringDataWebSupport.SpringDataWebConfigurationImportSelector; - -/** - * Annotation to be able to scan for additional Spring Data configuration classes to contribute to the web integration. - * - * @author Oliver Gierke - * @since 1.10 - * @soundtrack Selah Sue - This World (Selah Sue) - * @see SpringDataJacksonConfiguration - * @see SpringDataWebConfigurationImportSelector - */ -@Retention(RetentionPolicy.RUNTIME) -@Documented -@Configuration -@Target({ ElementType.TYPE, ElementType.ANNOTATION_TYPE }) -public @interface SpringDataWebConfigurationMixin { -} diff --git a/src/main/resources/META-INF/spring.factories b/src/main/resources/META-INF/spring.factories new file mode 100644 index 000000000..5b6d502f7 --- /dev/null +++ b/src/main/resources/META-INF/spring.factories @@ -0,0 +1 @@ +org.springframework.data.web.config.SpringDataJacksonModules=org.springframework.data.web.config.SpringDataJacksonConfiguration diff --git a/src/test/java/org/springframework/data/web/config/SampleMixin.java b/src/test/java/org/springframework/data/web/config/SampleMixin.java index 26e8001b3..019e73d74 100644 --- a/src/test/java/org/springframework/data/web/config/SampleMixin.java +++ b/src/test/java/org/springframework/data/web/config/SampleMixin.java @@ -20,8 +20,7 @@ import org.springframework.context.annotation.Bean; /** * @author Oliver Gierke */ -@SpringDataWebConfigurationMixin -public class SampleMixin { +public class SampleMixin implements SpringDataJacksonModules { @Bean String sampleBean() { diff --git a/src/test/resources/META-INF/spring.factories b/src/test/resources/META-INF/spring.factories new file mode 100644 index 000000000..81d1c0f82 --- /dev/null +++ b/src/test/resources/META-INF/spring.factories @@ -0,0 +1 @@ +org.springframework.data.web.config.SpringDataJacksonModules=org.springframework.data.web.config.SampleMixin