1 /* 2 * Copyright 2002-2014 the original author or authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.springframework.context.annotation; 18 19 import java.util.LinkedHashSet; 20 import java.util.Set; 21 22 import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; 23 import org.springframework.beans.factory.config.BeanDefinition; 24 import org.springframework.beans.factory.config.BeanDefinitionHolder; 25 import org.springframework.beans.factory.support.AbstractBeanDefinition; 26 import org.springframework.beans.factory.support.BeanDefinitionDefaults; 27 import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; 28 import org.springframework.beans.factory.support.BeanDefinitionRegistry; 29 import org.springframework.beans.factory.support.BeanNameGenerator; 30 import org.springframework.core.env.Environment; 31 import org.springframework.core.env.EnvironmentCapable; 32 import org.springframework.core.env.StandardEnvironment; 33 import org.springframework.core.io.ResourceLoader; 34 import org.springframework.util.Assert; 35 import org.springframework.util.PatternMatchUtils; 36 37 /** 38 * A bean definition scanner that detects bean candidates on the classpath, 39 * registering corresponding bean definitions with a given registry ({@code BeanFactory} 40 * or {@code ApplicationContext}). 41 * 42 * <p>Candidate classes are detected through configurable type filters. The 43 * default filters include classes that are annotated with Spring's 44 * {@link org.springframework.stereotype.Component @Component}, 45 * {@link org.springframework.stereotype.Repository @Repository}, 46 * {@link org.springframework.stereotype.Service @Service}, or 47 * {@link org.springframework.stereotype.Controller @Controller} stereotype. 48 * 49 * <p>Also supports Java EE 6's {@link javax.annotation.ManagedBean} and 50 * JSR-330's {@link javax.inject.Named} annotations, if available. 51 * 52 * @author Mark Fisher 53 * @author Juergen Hoeller 54 * @author Chris Beams 55 * @since 2.5 56 * @see AnnotationConfigApplicationContext#scan 57 * @see org.springframework.stereotype.Component 58 * @see org.springframework.stereotype.Repository 59 * @see org.springframework.stereotype.Service 60 * @see org.springframework.stereotype.Controller 61 */ 62 public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider { 63 64 private final BeanDefinitionRegistry registry; 65 66 private BeanDefinitionDefaults beanDefinitionDefaults = new BeanDefinitionDefaults(); 67 68 private String[] autowireCandidatePatterns; 69 70 private BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator(); 71 72 private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver(); 73 74 private boolean includeAnnotationConfig = true; 75 76 77 /** 78 * Create a new {@code ClassPathBeanDefinitionScanner} for the given bean factory. 79 * @param registry the {@code BeanFactory} to load bean definitions into, in the form 80 * of a {@code BeanDefinitionRegistry} 81 */ 82 public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) { 83 this(registry, true); 84 } 85 86 /** 87 * Create a new {@code ClassPathBeanDefinitionScanner} for the given bean factory. 88 * <p>If the passed-in bean factory does not only implement the 89 * {@code BeanDefinitionRegistry} interface but also the {@code ResourceLoader} 90 * interface, it will be used as default {@code ResourceLoader} as well. This will 91 * usually be the case for {@link org.springframework.context.ApplicationContext} 92 * implementations. 93 * <p>If given a plain {@code BeanDefinitionRegistry}, the default {@code ResourceLoader} 94 * will be a {@link org.springframework.core.io.support.PathMatchingResourcePatternResolver}. 95 * <p>If the the passed-in bean factory also implements {@link EnvironmentCapable} its 96 * environment will be used by this reader. Otherwise, the reader will initialize and 97 * use a {@link org.springframework.core.env.StandardEnvironment}. All 98 * {@code ApplicationContext} implementations are {@code EnvironmentCapable}, while 99 * normal {@code BeanFactory} implementations are not. 100 * @param registry the {@code BeanFactory} to load bean definitions into, in the form 101 * of a {@code BeanDefinitionRegistry} 102 * @param useDefaultFilters whether to include the default filters for the 103 * {@link org.springframework.stereotype.Component @Component}, 104 * {@link org.springframework.stereotype.Repository @Repository}, 105 * {@link org.springframework.stereotype.Service @Service}, and 106 * {@link org.springframework.stereotype.Controller @Controller} stereotype annotations 107 * @see #setResourceLoader 108 * @see #setEnvironment 109 */ 110 public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) { 111 this(registry, useDefaultFilters, getOrCreateEnvironment(registry)); 112 } 113 114 /** 115 * Create a new {@code ClassPathBeanDefinitionScanner} for the given bean factory and 116 * using the given {@link Environment} when evaluating bean definition profile metadata. 117 * <p>If the passed-in bean factory does not only implement the {@code 118 * BeanDefinitionRegistry} interface but also the {@link ResourceLoader} interface, it 119 * will be used as default {@code ResourceLoader} as well. This will usually be the 120 * case for {@link org.springframework.context.ApplicationContext} implementations. 121 * <p>If given a plain {@code BeanDefinitionRegistry}, the default {@code ResourceLoader} 122 * will be a {@link org.springframework.core.io.support.PathMatchingResourcePatternResolver}. 123 * @param registry the {@code BeanFactory} to load bean definitions into, in the form 124 * of a {@code BeanDefinitionRegistry} 125 * @param useDefaultFilters whether to include the default filters for the 126 * {@link org.springframework.stereotype.Component @Component}, 127 * {@link org.springframework.stereotype.Repository @Repository}, 128 * {@link org.springframework.stereotype.Service @Service}, and 129 * {@link org.springframework.stereotype.Controller @Controller} stereotype annotations 130 * @param environment the Spring {@link Environment} to use when evaluating bean 131 * definition profile metadata 132 * @since 3.1 133 * @see #setResourceLoader 134 */ 135 public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters, Environment environment) { 136 super(useDefaultFilters, environment); 137 138 Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); 139 this.registry = registry; 140 141 // Determine ResourceLoader to use. 142 if (this.registry instanceof ResourceLoader) { 143 setResourceLoader((ResourceLoader) this.registry); 144 } 145 } 146 147 148 /** 149 * Return the BeanDefinitionRegistry that this scanner operates on. 150 */ 151 public final BeanDefinitionRegistry getRegistry() { 152 return this.registry; 153 } 154 155 /** 156 * Set the defaults to use for detected beans. 157 * @see BeanDefinitionDefaults 158 */ 159 public void setBeanDefinitionDefaults(BeanDefinitionDefaults beanDefinitionDefaults) { 160 this.beanDefinitionDefaults = 161 (beanDefinitionDefaults != null ? beanDefinitionDefaults : new BeanDefinitionDefaults()); 162 } 163 164 /** 165 * Return the defaults to use for detected beans (never {@code null}). 166 * @since 4.1 167 */ 168 public BeanDefinitionDefaults getBeanDefinitionDefaults() { 169 return this.beanDefinitionDefaults; 170 } 171 172 /** 173 * Set the name-matching patterns for determining autowire candidates. 174 * @param autowireCandidatePatterns the patterns to match against 175 */ 176 public void setAutowireCandidatePatterns(String... autowireCandidatePatterns) { 177 this.autowireCandidatePatterns = autowireCandidatePatterns; 178 } 179 180 /** 181 * Set the BeanNameGenerator to use for detected bean classes. 182 * <p>Default is a {@link AnnotationBeanNameGenerator}. 183 */ 184 public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) { 185 this.beanNameGenerator = (beanNameGenerator != null ? beanNameGenerator : new AnnotationBeanNameGenerator()); 186 } 187 188 /** 189 * Set the ScopeMetadataResolver to use for detected bean classes. 190 * Note that this will override any custom "scopedProxyMode" setting. 191 * <p>The default is an {@link AnnotationScopeMetadataResolver}. 192 * @see #setScopedProxyMode 193 */ 194 public void setScopeMetadataResolver(ScopeMetadataResolver scopeMetadataResolver) { 195 this.scopeMetadataResolver = (scopeMetadataResolver != null ? scopeMetadataResolver : new AnnotationScopeMetadataResolver()); 196 } 197 198 /** 199 * Specify the proxy behavior for non-singleton scoped beans. 200 * Note that this will override any custom "scopeMetadataResolver" setting. 201 * <p>The default is {@link ScopedProxyMode#NO}. 202 * @see #setScopeMetadataResolver 203 */ 204 public void setScopedProxyMode(ScopedProxyMode scopedProxyMode) { 205 this.scopeMetadataResolver = new AnnotationScopeMetadataResolver(scopedProxyMode); 206 } 207 208 /** 209 * Specify whether to register annotation config post-processors. 210 * <p>The default is to register the post-processors. Turn this off 211 * to be able to ignore the annotations or to process them differently. 212 */ 213 public void setIncludeAnnotationConfig(boolean includeAnnotationConfig) { 214 this.includeAnnotationConfig = includeAnnotationConfig; 215 } 216 217 218 /** 219 * Perform a scan within the specified base packages. 220 * @param basePackages the packages to check for annotated classes 221 * @return number of beans registered 222 */ 223 public int scan(String... basePackages) { 224 int beanCountAtScanStart = this.registry.getBeanDefinitionCount(); 225 226 doScan(basePackages); 227 228 // Register annotation config processors, if necessary. 229 if (this.includeAnnotationConfig) { 230 AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); 231 } 232 233 return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart); 234 } 235 236 /** 237 * Perform a scan within the specified base packages, 238 * returning the registered bean definitions. 239 * <p>This method does <i>not</i> register an annotation config processor 240 * but rather leaves this up to the caller. 241 * @param basePackages the packages to check for annotated classes 242 * @return set of beans registered if any for tooling registration purposes (never {@code null}) 243 */ 244 protected Set<BeanDefinitionHolder> doScan(String... basePackages) { 245 Assert.notEmpty(basePackages, "At least one base package must be specified"); 246 Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>(); 247 for (String basePackage : basePackages) { 248 Set<BeanDefinition> candidates = findCandidateComponents(basePackage); 249 for (BeanDefinition candidate : candidates) { 250 ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate); 251 candidate.setScope(scopeMetadata.getScopeName()); 252 String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); 253 if (candidate instanceof AbstractBeanDefinition) { 254 postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); 255 } 256 if (candidate instanceof AnnotatedBeanDefinition) { 257 AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); 258 } 259 if (checkCandidate(beanName, candidate)) { 260 BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); 261 definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); 262 beanDefinitions.add(definitionHolder); 263 registerBeanDefinition(definitionHolder, this.registry); 264 } 265 } 266 } 267 return beanDefinitions; 268 } 269 270 /** 271 * Apply further settings to the given bean definition, 272 * beyond the contents retrieved from scanning the component class. 273 * @param beanDefinition the scanned bean definition 274 * @param beanName the generated bean name for the given bean 275 */ 276 protected void postProcessBeanDefinition(AbstractBeanDefinition beanDefinition, String beanName) { 277 beanDefinition.applyDefaults(this.beanDefinitionDefaults); 278 if (this.autowireCandidatePatterns != null) { 279 beanDefinition.setAutowireCandidate(PatternMatchUtils.simpleMatch(this.autowireCandidatePatterns, beanName)); 280 } 281 } 282 283 /** 284 * Register the specified bean with the given registry. 285 * <p>Can be overridden in subclasses, e.g. to adapt the registration 286 * process or to register further bean definitions for each scanned bean. 287 * @param definitionHolder the bean definition plus bean name for the bean 288 * @param registry the BeanDefinitionRegistry to register the bean with 289 */ 290 protected void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) { 291 BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry); 292 } 293 294 295 /** 296 * Check the given candidate's bean name, determining whether the corresponding 297 * bean definition needs to be registered or conflicts with an existing definition. 298 * @param beanName the suggested name for the bean 299 * @param beanDefinition the corresponding bean definition 300 * @return {@code true} if the bean can be registered as-is; 301 * {@code false} if it should be skipped because there is an 302 * existing, compatible bean definition for the specified name 303 * @throws ConflictingBeanDefinitionException if an existing, incompatible 304 * bean definition has been found for the specified name 305 */ 306 protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException { 307 if (!this.registry.containsBeanDefinition(beanName)) { 308 return true; 309 } 310 BeanDefinition existingDef = this.registry.getBeanDefinition(beanName); 311 BeanDefinition originatingDef = existingDef.getOriginatingBeanDefinition(); 312 if (originatingDef != null) { 313 existingDef = originatingDef; 314 } 315 if (isCompatible(beanDefinition, existingDef)) { 316 return false; 317 } 318 throw new ConflictingBeanDefinitionException("Annotation-specified bean name '" + beanName + 319 "' for bean class [" + beanDefinition.getBeanClassName() + "] conflicts with existing, " + 320 "non-compatible bean definition of same name and class [" + existingDef.getBeanClassName() + "]"); 321 } 322 323 /** 324 * Determine whether the given new bean definition is compatible with 325 * the given existing bean definition. 326 * <p>The default implementation considers them as compatible when the existing 327 * bean definition comes from the same source or from a non-scanning source. 328 * @param newDefinition the new bean definition, originated from scanning 329 * @param existingDefinition the existing bean definition, potentially an 330 * explicitly defined one or a previously generated one from scanning 331 * @return whether the definitions are considered as compatible, with the 332 * new definition to be skipped in favor of the existing definition 333 */ 334 protected boolean isCompatible(BeanDefinition newDefinition, BeanDefinition existingDefinition) { 335 return (!(existingDefinition instanceof ScannedGenericBeanDefinition) || // explicitly registered overriding bean 336 newDefinition.getSource().equals(existingDefinition.getSource()) || // scanned same file twice 337 newDefinition.equals(existingDefinition)); // scanned equivalent class twice 338 } 339 340 341 /** 342 * Get the Environment from the given registry if possible, otherwise return a new 343 * StandardEnvironment. 344 */ 345 private static Environment getOrCreateEnvironment(BeanDefinitionRegistry registry) { 346 Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); 347 if (registry instanceof EnvironmentCapable) { 348 return ((EnvironmentCapable) registry).getEnvironment(); 349 } 350 return new StandardEnvironment(); 351 } 352 353 }