From 243e21a3eac95626a14a98bf63e0ae870bd6924f Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 27 Dec 2016 18:32:03 +0100 Subject: [PATCH] Backport streamlined ClassPathBeanDefinitionScanner setup Issue: SPR-15061 --- .../AnnotationConfigApplicationContext.java | 2 +- .../ClassPathBeanDefinitionScanner.java | 41 ++++- ...athScanningCandidateComponentProvider.java | 154 +++++++++--------- .../ComponentScanAnnotationParser.java | 6 +- .../ComponentScanBeanDefinitionParser.java | 9 +- .../CachingMetadataReaderFactory.java | 6 +- 6 files changed, 123 insertions(+), 95 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/annotation/AnnotationConfigApplicationContext.java b/spring-context/src/main/java/org/springframework/context/annotation/AnnotationConfigApplicationContext.java index 4d3b74f551..c4a0657884 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/AnnotationConfigApplicationContext.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/AnnotationConfigApplicationContext.java @@ -35,7 +35,7 @@ import org.springframework.util.Assert; * deliberately override certain bean definitions via an extra {@code @Configuration} * class. * - *

See @{@link Configuration} Javadoc for usage examples. + *

See @{@link Configuration}'s javadoc for usage examples. * * @author Chris Beams * @author Juergen Hoeller diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java b/spring-context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java index 8e90baa5c0..dbf27a924d 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-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. @@ -132,16 +132,39 @@ public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateCo * @since 3.1 * @see #setResourceLoader */ - public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters, Environment environment) { - super(useDefaultFilters, environment); + public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters, + Environment environment) { + + this(registry, useDefaultFilters, environment, + (registry instanceof ResourceLoader ? (ResourceLoader) registry : null)); + } + + /** + * Create a new {@code ClassPathBeanDefinitionScanner} for the given bean factory and + * using the given {@link Environment} when evaluating bean definition profile metadata. + * @param registry the {@code BeanFactory} to load bean definitions into, in the form + * of a {@code BeanDefinitionRegistry} + * @param useDefaultFilters whether to include the default filters for the + * {@link org.springframework.stereotype.Component @Component}, + * {@link org.springframework.stereotype.Repository @Repository}, + * {@link org.springframework.stereotype.Service @Service}, and + * {@link org.springframework.stereotype.Controller @Controller} stereotype annotations + * @param environment the Spring {@link Environment} to use when evaluating bean + * definition profile metadata + * @param resourceLoader the {@link ResourceLoader} to use + * @since 4.3.6 + */ + public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters, + Environment environment, ResourceLoader resourceLoader) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); this.registry = registry; - // Determine ResourceLoader to use. - if (this.registry instanceof ResourceLoader) { - setResourceLoader((ResourceLoader) this.registry); + if (useDefaultFilters) { + registerDefaultFilters(); } + setEnvironment(environment); + setResourceLoader(resourceLoader); } @@ -192,7 +215,8 @@ public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateCo * @see #setScopedProxyMode */ public void setScopeMetadataResolver(ScopeMetadataResolver scopeMetadataResolver) { - this.scopeMetadataResolver = (scopeMetadataResolver != null ? scopeMetadataResolver : new AnnotationScopeMetadataResolver()); + this.scopeMetadataResolver = + (scopeMetadataResolver != null ? scopeMetadataResolver : new AnnotationScopeMetadataResolver()); } /** @@ -258,7 +282,8 @@ public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateCo } if (checkCandidate(beanName, candidate)) { BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); - definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); + definitionHolder = + AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); beanDefinitions.add(definitionHolder); registerBeanDefinition(definitionHolder, this.registry); } 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 d1f5b49ac0..9ca2791dfb 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-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. @@ -36,7 +36,6 @@ import org.springframework.core.env.EnvironmentCapable; import org.springframework.core.env.StandardEnvironment; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; -import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternUtils; import org.springframework.core.type.classreading.CachingMetadataReaderFactory; @@ -72,23 +71,30 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC static final String DEFAULT_RESOURCE_PATTERN = "**/*.class"; + protected final Log logger = LogFactory.getLog(getClass()); - private Environment environment; - - private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); - - private MetadataReaderFactory metadataReaderFactory = - new CachingMetadataReaderFactory(this.resourcePatternResolver); - private String resourcePattern = DEFAULT_RESOURCE_PATTERN; private final List includeFilters = new LinkedList(); private final List excludeFilters = new LinkedList(); + private Environment environment; + private ConditionEvaluator conditionEvaluator; + private ResourcePatternResolver resourcePatternResolver; + + private MetadataReaderFactory metadataReaderFactory; + + + /** + * Protected constructor for flexible subclass initialization. + * @since 4.3.6 + */ + protected ClassPathScanningCandidateComponentProvider() { + } /** * Create a ClassPathScanningCandidateComponentProvider with a {@link StandardEnvironment}. @@ -115,74 +121,11 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC if (useDefaultFilters) { registerDefaultFilters(); } - Assert.notNull(environment, "Environment must not be null"); - this.environment = environment; + setEnvironment(environment); + setResourceLoader(null); } - /** - * Set the ResourceLoader to use for resource locations. - * This will typically be a ResourcePatternResolver implementation. - *

Default is PathMatchingResourcePatternResolver, also capable of - * resource pattern resolving through the ResourcePatternResolver interface. - * @see org.springframework.core.io.support.ResourcePatternResolver - * @see org.springframework.core.io.support.PathMatchingResourcePatternResolver - */ - @Override - public void setResourceLoader(ResourceLoader resourceLoader) { - this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader); - this.metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader); - } - - /** - * Return the ResourceLoader that this component provider uses. - */ - public final ResourceLoader getResourceLoader() { - return this.resourcePatternResolver; - } - - /** - * Set the {@link MetadataReaderFactory} to use. - *

Default is a {@link CachingMetadataReaderFactory} for the specified - * {@linkplain #setResourceLoader resource loader}. - *

Call this setter method after {@link #setResourceLoader} in order - * for the given MetadataReaderFactory to override the default factory. - */ - public void setMetadataReaderFactory(MetadataReaderFactory metadataReaderFactory) { - this.metadataReaderFactory = metadataReaderFactory; - } - - /** - * Return the MetadataReaderFactory used by this component provider. - */ - public final MetadataReaderFactory getMetadataReaderFactory() { - return this.metadataReaderFactory; - } - - /** - * Set the Environment to use when resolving placeholders and evaluating - * {@link Conditional @Conditional}-annotated component classes. - *

The default is a {@link StandardEnvironment}. - * @param environment the Environment to use - */ - public void setEnvironment(Environment environment) { - Assert.notNull(environment, "Environment must not be null"); - this.environment = environment; - this.conditionEvaluator = null; - } - - @Override - public final Environment getEnvironment() { - return this.environment; - } - - /** - * Returns the {@link BeanDefinitionRegistry} used by this scanner, if any. - */ - protected BeanDefinitionRegistry getRegistry() { - return null; - } - /** * Set the resource pattern to use when scanning the classpath. * This value will be appended to each base package name. @@ -256,6 +199,69 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC } } + /** + * Set the Environment to use when resolving placeholders and evaluating + * {@link Conditional @Conditional}-annotated component classes. + *

The default is a {@link StandardEnvironment}. + * @param environment the Environment to use + */ + public void setEnvironment(Environment environment) { + Assert.notNull(environment, "Environment must not be null"); + this.environment = environment; + this.conditionEvaluator = null; + } + + @Override + public final Environment getEnvironment() { + return this.environment; + } + + /** + * Return the {@link BeanDefinitionRegistry} used by this scanner, if any. + */ + protected BeanDefinitionRegistry getRegistry() { + return null; + } + + /** + * Set the {@link ResourceLoader} to use for resource locations. + * This will typically be a {@link ResourcePatternResolver} implementation. + *

Default is a {@code PathMatchingResourcePatternResolver}, also capable of + * resource pattern resolving through the {@code ResourcePatternResolver} interface. + * @see org.springframework.core.io.support.ResourcePatternResolver + * @see org.springframework.core.io.support.PathMatchingResourcePatternResolver + */ + @Override + public void setResourceLoader(ResourceLoader resourceLoader) { + this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader); + this.metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader); + } + + /** + * Return the ResourceLoader that this component provider uses. + */ + public final ResourceLoader getResourceLoader() { + return this.resourcePatternResolver; + } + + /** + * Set the {@link MetadataReaderFactory} to use. + *

Default is a {@link CachingMetadataReaderFactory} for the specified + * {@linkplain #setResourceLoader resource loader}. + *

Call this setter method after {@link #setResourceLoader} in order + * for the given MetadataReaderFactory to override the default factory. + */ + public void setMetadataReaderFactory(MetadataReaderFactory metadataReaderFactory) { + this.metadataReaderFactory = metadataReaderFactory; + } + + /** + * Return the MetadataReaderFactory used by this component provider. + */ + public final MetadataReaderFactory getMetadataReaderFactory() { + return this.metadataReaderFactory; + } + /** * Scan the class path for candidate components. diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ComponentScanAnnotationParser.java b/spring-context/src/main/java/org/springframework/context/annotation/ComponentScanAnnotationParser.java index 6a8fba7b36..2e7047389f 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ComponentScanAnnotationParser.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ComponentScanAnnotationParser.java @@ -77,10 +77,8 @@ class ComponentScanAnnotationParser { Assert.state(this.environment != null, "Environment must not be null"); Assert.state(this.resourceLoader != null, "ResourceLoader must not be null"); - ClassPathBeanDefinitionScanner scanner = - new ClassPathBeanDefinitionScanner(this.registry, componentScan.getBoolean("useDefaultFilters")); - scanner.setEnvironment(this.environment); - scanner.setResourceLoader(this.resourceLoader); + ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry, + componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader); Class generatorClass = componentScan.getClass("nameGenerator"); boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass); diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ComponentScanBeanDefinitionParser.java b/spring-context/src/main/java/org/springframework/context/annotation/ComponentScanBeanDefinitionParser.java index 26bf6849e9..adc42223b2 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ComponentScanBeanDefinitionParser.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ComponentScanBeanDefinitionParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-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. @@ -98,8 +98,6 @@ public class ComponentScanBeanDefinitionParser implements BeanDefinitionParser { // Delegate bean definition registration to scanner class. ClassPathBeanDefinitionScanner scanner = createScanner(parserContext.getReaderContext(), useDefaultFilters); - scanner.setResourceLoader(parserContext.getReaderContext().getResourceLoader()); - scanner.setEnvironment(parserContext.getReaderContext().getEnvironment()); scanner.setBeanDefinitionDefaults(parserContext.getDelegate().getBeanDefinitionDefaults()); scanner.setAutowireCandidatePatterns(parserContext.getDelegate().getAutowireCandidatePatterns()); @@ -127,7 +125,8 @@ public class ComponentScanBeanDefinitionParser implements BeanDefinitionParser { } protected ClassPathBeanDefinitionScanner createScanner(XmlReaderContext readerContext, boolean useDefaultFilters) { - return new ClassPathBeanDefinitionScanner(readerContext.getRegistry(), useDefaultFilters); + return new ClassPathBeanDefinitionScanner(readerContext.getRegistry(), useDefaultFilters, + readerContext.getEnvironment(), readerContext.getResourceLoader()); } protected void registerComponents( @@ -266,7 +265,7 @@ public class ComponentScanBeanDefinitionParser implements BeanDefinitionParser { throw new IllegalArgumentException("Class [" + className + "] for strategy [" + strategyType.getName() + "] not found", ex); } - catch (Exception ex) { + catch (Throwable ex) { throw new IllegalArgumentException("Unable to instantiate class [" + className + "] for strategy [" + strategyType.getName() + "]: a zero-argument constructor is required", ex); } diff --git a/spring-core/src/main/java/org/springframework/core/type/classreading/CachingMetadataReaderFactory.java b/spring-core/src/main/java/org/springframework/core/type/classreading/CachingMetadataReaderFactory.java index ef8201d6b5..72bf3c037c 100644 --- a/spring-core/src/main/java/org/springframework/core/type/classreading/CachingMetadataReaderFactory.java +++ b/spring-core/src/main/java/org/springframework/core/type/classreading/CachingMetadataReaderFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-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. @@ -25,7 +25,7 @@ import org.springframework.core.io.ResourceLoader; /** * Caching implementation of the {@link MetadataReaderFactory} interface, - * caching {@link MetadataReader} per Spring {@link Resource} handle + * caching a {@link MetadataReader} instance per Spring {@link Resource} handle * (i.e. per ".class" file). * * @author Juergen Hoeller @@ -77,7 +77,7 @@ public class CachingMetadataReaderFactory extends SimpleMetadataReaderFactory { /** * Specify the maximum number of entries for the MetadataReader cache. - * Default is 256. + *

Default is 256. */ public void setCacheLimit(int cacheLimit) { this.cacheLimit = cacheLimit;