1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.context.annotation;
18
19 import java.io.IOException;
20 import java.lang.annotation.Annotation;
21 import java.util.LinkedHashSet;
22 import java.util.LinkedList;
23 import java.util.List;
24 import java.util.Set;
25
26 import org.apache.commons.logging.Log;
27 import org.apache.commons.logging.LogFactory;
28
29 import org.springframework.beans.factory.BeanDefinitionStoreException;
30 import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
31 import org.springframework.beans.factory.config.BeanDefinition;
32 import org.springframework.beans.factory.support.BeanDefinitionRegistry;
33 import org.springframework.context.ResourceLoaderAware;
34 import org.springframework.core.env.Environment;
35 import org.springframework.core.env.EnvironmentCapable;
36 import org.springframework.core.env.StandardEnvironment;
37 import org.springframework.core.io.Resource;
38 import org.springframework.core.io.ResourceLoader;
39 import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
40 import org.springframework.core.io.support.ResourcePatternResolver;
41 import org.springframework.core.io.support.ResourcePatternUtils;
42 import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
43 import org.springframework.core.type.classreading.MetadataReader;
44 import org.springframework.core.type.classreading.MetadataReaderFactory;
45 import org.springframework.core.type.filter.AnnotationTypeFilter;
46 import org.springframework.core.type.filter.TypeFilter;
47 import org.springframework.stereotype.Component;
48 import org.springframework.stereotype.Controller;
49 import org.springframework.stereotype.Repository;
50 import org.springframework.stereotype.Service;
51 import org.springframework.util.Assert;
52 import org.springframework.util.ClassUtils;
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71 public class ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware {
72
73 static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
74
75 protected final Log logger = LogFactory.getLog(getClass());
76
77 private Environment environment;
78
79 private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
80
81 private MetadataReaderFactory metadataReaderFactory =
82 new CachingMetadataReaderFactory(this.resourcePatternResolver);
83
84 private String resourcePattern = DEFAULT_RESOURCE_PATTERN;
85
86 private final List<TypeFilter> includeFilters = new LinkedList<TypeFilter>();
87
88 private final List<TypeFilter> excludeFilters = new LinkedList<TypeFilter>();
89
90 private ConditionEvaluator conditionEvaluator;
91
92
93
94
95
96
97
98
99
100
101 public ClassPathScanningCandidateComponentProvider(boolean useDefaultFilters) {
102 this(useDefaultFilters, new StandardEnvironment());
103 }
104
105
106
107
108
109
110
111
112
113
114 public ClassPathScanningCandidateComponentProvider(boolean useDefaultFilters, Environment environment) {
115 if (useDefaultFilters) {
116 registerDefaultFilters();
117 }
118 Assert.notNull(environment, "Environment must not be null");
119 this.environment = environment;
120 }
121
122
123
124
125
126
127
128
129
130
131 @Override
132 public void setResourceLoader(ResourceLoader resourceLoader) {
133 this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
134 this.metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader);
135 }
136
137
138
139
140 public final ResourceLoader getResourceLoader() {
141 return this.resourcePatternResolver;
142 }
143
144
145
146
147
148
149
150
151 public void setMetadataReaderFactory(MetadataReaderFactory metadataReaderFactory) {
152 this.metadataReaderFactory = metadataReaderFactory;
153 }
154
155
156
157
158 public final MetadataReaderFactory getMetadataReaderFactory() {
159 return this.metadataReaderFactory;
160 }
161
162
163
164
165
166
167
168 public void setEnvironment(Environment environment) {
169 Assert.notNull(environment, "Environment must not be null");
170 this.environment = environment;
171 this.conditionEvaluator = null;
172 }
173
174 @Override
175 public final Environment getEnvironment() {
176 return this.environment;
177 }
178
179
180
181
182 protected BeanDefinitionRegistry getRegistry() {
183 return null;
184 }
185
186
187
188
189
190
191
192 public void setResourcePattern(String resourcePattern) {
193 Assert.notNull(resourcePattern, "'resourcePattern' must not be null");
194 this.resourcePattern = resourcePattern;
195 }
196
197
198
199
200 public void addIncludeFilter(TypeFilter includeFilter) {
201 this.includeFilters.add(includeFilter);
202 }
203
204
205
206
207 public void addExcludeFilter(TypeFilter excludeFilter) {
208 this.excludeFilters.add(0, excludeFilter);
209 }
210
211
212
213
214
215
216
217
218
219 public void resetFilters(boolean useDefaultFilters) {
220 this.includeFilters.clear();
221 this.excludeFilters.clear();
222 if (useDefaultFilters) {
223 registerDefaultFilters();
224 }
225 }
226
227
228
229
230
231
232
233
234
235
236
237 @SuppressWarnings("unchecked")
238 protected void registerDefaultFilters() {
239 this.includeFilters.add(new AnnotationTypeFilter(Component.class));
240 ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
241 try {
242 this.includeFilters.add(new AnnotationTypeFilter(
243 ((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
244 logger.debug("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
245 }
246 catch (ClassNotFoundException ex) {
247
248 }
249 try {
250 this.includeFilters.add(new AnnotationTypeFilter(
251 ((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
252 logger.debug("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
253 }
254 catch (ClassNotFoundException ex) {
255
256 }
257 }
258
259
260
261
262
263
264
265 public Set<BeanDefinition> findCandidateComponents(String basePackage) {
266 Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
267 try {
268 String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
269 resolveBasePackage(basePackage) + "/" + this.resourcePattern;
270 Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
271 boolean traceEnabled = logger.isTraceEnabled();
272 boolean debugEnabled = logger.isDebugEnabled();
273 for (Resource resource : resources) {
274 if (traceEnabled) {
275 logger.trace("Scanning " + resource);
276 }
277 if (resource.isReadable()) {
278 try {
279 MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
280 if (isCandidateComponent(metadataReader)) {
281 ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
282 sbd.setResource(resource);
283 sbd.setSource(resource);
284 if (isCandidateComponent(sbd)) {
285 if (debugEnabled) {
286 logger.debug("Identified candidate component class: " + resource);
287 }
288 candidates.add(sbd);
289 }
290 else {
291 if (debugEnabled) {
292 logger.debug("Ignored because not a concrete top-level class: " + resource);
293 }
294 }
295 }
296 else {
297 if (traceEnabled) {
298 logger.trace("Ignored because not matching any filter: " + resource);
299 }
300 }
301 }
302 catch (Throwable ex) {
303 throw new BeanDefinitionStoreException(
304 "Failed to read candidate component class: " + resource, ex);
305 }
306 }
307 else {
308 if (traceEnabled) {
309 logger.trace("Ignored because not readable: " + resource);
310 }
311 }
312 }
313 }
314 catch (IOException ex) {
315 throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
316 }
317 return candidates;
318 }
319
320
321
322
323
324
325
326
327
328
329 protected String resolveBasePackage(String basePackage) {
330 return ClassUtils.convertClassNameToResourcePath(this.environment.resolveRequiredPlaceholders(basePackage));
331 }
332
333
334
335
336
337
338
339 protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
340 for (TypeFilter tf : this.excludeFilters) {
341 if (tf.match(metadataReader, this.metadataReaderFactory)) {
342 return false;
343 }
344 }
345 for (TypeFilter tf : this.includeFilters) {
346 if (tf.match(metadataReader, this.metadataReaderFactory)) {
347 return isConditionMatch(metadataReader);
348 }
349 }
350 return false;
351 }
352
353
354
355
356
357
358
359 private boolean isConditionMatch(MetadataReader metadataReader) {
360 if (this.conditionEvaluator == null) {
361 this.conditionEvaluator = new ConditionEvaluator(getRegistry(), getEnvironment(), getResourceLoader());
362 }
363 return !this.conditionEvaluator.shouldSkip(metadataReader.getAnnotationMetadata());
364 }
365
366
367
368
369
370
371
372
373 protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
374 return (beanDefinition.getMetadata().isConcrete() && beanDefinition.getMetadata().isIndependent());
375 }
376
377
378
379
380
381 public void clearCache() {
382 if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
383 ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
384 }
385 }
386
387 }