001: /*
002: * Copyright 2002-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.context.annotation;
018:
019: import java.util.LinkedHashSet;
020: import java.util.Set;
021:
022: import org.springframework.aop.scope.ScopedProxyUtils;
023: import org.springframework.beans.factory.config.BeanDefinition;
024: import org.springframework.beans.factory.config.BeanDefinitionHolder;
025: import org.springframework.beans.factory.support.AbstractBeanDefinition;
026: import org.springframework.beans.factory.support.BeanDefinitionDefaults;
027: import org.springframework.beans.factory.support.BeanDefinitionRegistry;
028: import org.springframework.beans.factory.support.BeanNameGenerator;
029: import org.springframework.core.io.ResourceLoader;
030: import org.springframework.util.Assert;
031: import org.springframework.util.PatternMatchUtils;
032:
033: /**
034: * A bean definition scanner that detects bean candidates on the classpath,
035: * registering corresponding bean definitions with a given registry (BeanFactory
036: * or ApplicationContext).
037: *
038: * <p>Candidate classes are detected through configurable type filters. The
039: * default filters include classes that are annotated with Spring's
040: * {@link org.springframework.stereotype.Component @Component},
041: * {@link org.springframework.stereotype.Repository @Repository},
042: * {@link org.springframework.stereotype.Service @Service}, or
043: * {@link org.springframework.stereotype.Controller @Controller} stereotype.
044: *
045: * @author Mark Fisher
046: * @author Juergen Hoeller
047: * @since 2.5
048: * @see org.springframework.stereotype.Component
049: * @see org.springframework.stereotype.Repository
050: * @see org.springframework.stereotype.Service
051: * @see org.springframework.stereotype.Controller
052: */
053: public class ClassPathBeanDefinitionScanner extends
054: ClassPathScanningCandidateComponentProvider {
055:
056: private final BeanDefinitionRegistry registry;
057:
058: private BeanDefinitionDefaults beanDefinitionDefaults = new BeanDefinitionDefaults();
059:
060: private BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();
061:
062: private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver();
063:
064: private String[] autowireCandidatePatterns;
065:
066: private boolean includeAnnotationConfig = true;
067:
068: /**
069: * Create a new ClassPathBeanDefinitionScanner for the given bean factory.
070: * @param registry the BeanFactory to load bean definitions into,
071: * in the form of a BeanDefinitionRegistry
072: */
073: public ClassPathBeanDefinitionScanner(
074: BeanDefinitionRegistry registry) {
075: this (registry, true);
076: }
077:
078: /**
079: * Create a new ClassPathBeanDefinitionScanner for the given bean factory.
080: * <p>If the passed-in bean factory does not only implement the BeanDefinitionRegistry
081: * interface but also the ResourceLoader interface, it will be used as default
082: * ResourceLoader as well. This will usually be the case for
083: * {@link org.springframework.context.ApplicationContext} implementations.
084: * <p>If given a plain BeanDefinitionRegistry, the default ResourceLoader will be a
085: * {@link org.springframework.core.io.support.PathMatchingResourcePatternResolver}.
086: * @param registry the BeanFactory to load bean definitions into,
087: * in the form of a BeanDefinitionRegistry
088: * @param useDefaultFilters whether to include the default filters for the
089: * {@link org.springframework.stereotype.Component @Component},
090: * {@link org.springframework.stereotype.Repository @Repository},
091: * {@link org.springframework.stereotype.Service @Service}, and
092: * {@link org.springframework.stereotype.Controller @Controller} stereotype
093: * annotations.
094: * @see #setResourceLoader
095: */
096: public ClassPathBeanDefinitionScanner(
097: BeanDefinitionRegistry registry, boolean useDefaultFilters) {
098: super (useDefaultFilters);
099:
100: Assert.notNull(registry,
101: "BeanDefinitionRegistry must not be null");
102: this .registry = registry;
103:
104: // Determine ResourceLoader to use.
105: if (this .registry instanceof ResourceLoader) {
106: setResourceLoader((ResourceLoader) this .registry);
107: }
108: }
109:
110: /**
111: * Set the defaults to use for detected beans.
112: * @see BeanDefinitionDefaults
113: */
114: public void setBeanDefinitionDefaults(
115: BeanDefinitionDefaults beanDefinitionDefaults) {
116: this .beanDefinitionDefaults = (beanDefinitionDefaults != null ? beanDefinitionDefaults
117: : new BeanDefinitionDefaults());
118: }
119:
120: /**
121: * Set the name-matching patterns for determining autowire candidates.
122: * @param autowireCandidatePatterns the patterns to match against
123: */
124: public void setAutowireCandidatePatterns(
125: String[] autowireCandidatePatterns) {
126: this .autowireCandidatePatterns = autowireCandidatePatterns;
127: }
128:
129: /**
130: * Set the BeanNameGenerator to use for detected bean classes.
131: * <p>Default is a {@link AnnotationBeanNameGenerator}.
132: */
133: public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) {
134: this .beanNameGenerator = (beanNameGenerator != null ? beanNameGenerator
135: : new AnnotationBeanNameGenerator());
136: }
137:
138: /**
139: * Set the ScopeMetadataResolver to use for detected bean classes.
140: * Note that this will override any custom "scopedProxyMode" setting.
141: * <p>The default is an {@link AnnotationScopeMetadataResolver}.
142: * @see #setScopedProxyMode
143: */
144: public void setScopeMetadataResolver(
145: ScopeMetadataResolver scopeMetadataResolver) {
146: this .scopeMetadataResolver = scopeMetadataResolver;
147: }
148:
149: /**
150: * Specify the proxy behavior for non-singleton scoped beans.
151: * Note that this will override any custom "scopeMetadataResolver" setting.
152: * <p>The default is {@link ScopedProxyMode#NO}.
153: * @see #setScopeMetadataResolver
154: */
155: public void setScopedProxyMode(ScopedProxyMode scopedProxyMode) {
156: this .scopeMetadataResolver = new AnnotationScopeMetadataResolver(
157: scopedProxyMode);
158: }
159:
160: /**
161: * Specify whether to register annotation config post-processors.
162: * <p>The default is to register the post-processors. Turn this off
163: * to be able to ignore the annotations or to process them differently.
164: */
165: public void setIncludeAnnotationConfig(
166: boolean includeAnnotationConfig) {
167: this .includeAnnotationConfig = includeAnnotationConfig;
168: }
169:
170: /**
171: * Perform a scan within the specified base packages.
172: * @param basePackages the packages to check for annotated classes
173: * @return number of beans registered
174: */
175: public int scan(String... basePackages) {
176: int beanCountAtScanStart = this .registry
177: .getBeanDefinitionCount();
178:
179: doScan(basePackages);
180:
181: // Register annotation config processors, if necessary.
182: if (this .includeAnnotationConfig) {
183: AnnotationConfigUtils
184: .registerAnnotationConfigProcessors(this .registry);
185: }
186:
187: return this .registry.getBeanDefinitionCount()
188: - beanCountAtScanStart;
189: }
190:
191: /**
192: * Perform a scan within the specified base packages,
193: * returning the registered bean definitions.
194: * <p>This method does <i>not</i> register an annotation config processor
195: * but rather leaves this up to the caller.
196: * @param basePackages the packages to check for annotated classes
197: * @return number of beans registered
198: */
199: protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
200: Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
201: for (int i = 0; i < basePackages.length; i++) {
202: Set<BeanDefinition> candidates = findCandidateComponents(basePackages[i]);
203: for (BeanDefinition candidate : candidates) {
204: String beanName = this .beanNameGenerator
205: .generateBeanName(candidate, this .registry);
206: if (checkBeanName(beanName, candidate)) {
207: if (candidate instanceof AbstractBeanDefinition) {
208: AbstractBeanDefinition abd = (AbstractBeanDefinition) candidate;
209: abd.applyDefaults(this .beanDefinitionDefaults);
210: if (this .autowireCandidatePatterns != null) {
211: abd
212: .setAutowireCandidate(PatternMatchUtils
213: .simpleMatch(
214: this .autowireCandidatePatterns,
215: beanName));
216: }
217: }
218: ScopeMetadata scopeMetadata = this .scopeMetadataResolver
219: .resolveScopeMetadata(candidate);
220: BeanDefinition beanDefinition = applyScope(
221: candidate, beanName, scopeMetadata);
222: beanDefinitions.add(new BeanDefinitionHolder(
223: beanDefinition, beanName));
224: this .registry.registerBeanDefinition(beanName,
225: beanDefinition);
226: }
227: }
228: }
229: return beanDefinitions;
230: }
231:
232: /**
233: * Check the given bean name, determining whether the corresponding
234: * bean definition needs to be registered or conflicts with an
235: * existing definition.
236: * @param beanName the suggested name for the bean
237: * @param beanDefinition the corresponding bean definition
238: * @return <code>true</code> if the bean can be registered as-is;
239: * <code>false</code> if it should be skipped because there is an
240: * existing, compatible bean definition for the specified name
241: * @throws IllegalStateException if an existing, incompatible
242: * bean definition has been found for the specified name
243: */
244: private boolean checkBeanName(String beanName,
245: BeanDefinition beanDefinition) throws IllegalStateException {
246: if (!this .registry.containsBeanDefinition(beanName)) {
247: return true;
248: }
249: BeanDefinition existingDef = this .registry
250: .getBeanDefinition(beanName);
251: if (isCompatible(beanDefinition, existingDef)) {
252: return false;
253: }
254: throw new IllegalStateException(
255: "Annotation-specified bean name '"
256: + beanName
257: + "' for bean class ["
258: + beanDefinition.getBeanClassName()
259: + "] conflicts with existing, "
260: + "non-compatible bean definition of same name and class ["
261: + existingDef.getBeanClassName() + "]");
262: }
263:
264: /**
265: * Determine whether the given new bean definition is compatible with
266: * the given existing bean definition.
267: * <p>The default implementation simply considers them as compatible
268: * when the bean class name matches.
269: * @param newDefinition the new bean definition, originated from scanning
270: * @param existingDefinition the existing bean definition, probably from
271: * an existing bean definition
272: * @return whether the definitions are considered as compatible, with the
273: * new definition to be skipped in favor of the existing definition
274: */
275: private boolean isCompatible(BeanDefinition newDefinition,
276: BeanDefinition existingDefinition) {
277: return newDefinition.getBeanClassName().equals(
278: existingDefinition.getBeanClassName());
279: }
280:
281: /**
282: * Apply the specified scope to the given bean definition.
283: * @param beanDefinition the bean definition to configure
284: * @param beanName the name of the bean
285: * @param scopeMetadata the corresponding scope metadata
286: * @return the final bean definition to use (potentially a proxy)
287: */
288: private BeanDefinition applyScope(BeanDefinition beanDefinition,
289: String beanName, ScopeMetadata scopeMetadata) {
290: String scope = scopeMetadata.getScopeName();
291: ScopedProxyMode scopedProxyMode = scopeMetadata
292: .getScopedProxyMode();
293: beanDefinition.setScope(scope);
294: if (BeanDefinition.SCOPE_SINGLETON.equals(scope)
295: || BeanDefinition.SCOPE_PROTOTYPE.equals(scope)
296: || scopedProxyMode.equals(ScopedProxyMode.NO)) {
297: return beanDefinition;
298: }
299: boolean proxyTargetClass = scopedProxyMode
300: .equals(ScopedProxyMode.TARGET_CLASS);
301: BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(
302: beanDefinition, beanName);
303: return ScopedProxyCreator.createScopedProxy(definitionHolder,
304: this .registry, proxyTargetClass);
305: }
306:
307: /**
308: * Inner factory class used to just introduce an AOP framework dependency
309: * when actually creating a scoped proxy.
310: */
311: private static class ScopedProxyCreator {
312:
313: public static BeanDefinition createScopedProxy(
314: BeanDefinitionHolder definitionHolder,
315: BeanDefinitionRegistry registry,
316: boolean proxyTargetClass) {
317:
318: BeanDefinitionHolder scopedProxyDefinition = ScopedProxyUtils
319: .createScopedProxy(definitionHolder, registry,
320: proxyTargetClass);
321: return scopedProxyDefinition.getBeanDefinition();
322: }
323: }
324:
325: }
|