获取component-scan的base-package属性,可能是个list,所以要遍历;其中,循环内部,调用了ClassPathScanningCandidateComponentProvider#findCandidateComponents。
for (String basePackage : basePackages) { /** * 扫描候选的component,注意,这里的名称叫CandidateComponent,所以这里真的就只扫描了 * @component或者基于它的那几个。(service、controller那些) * 这里是没包含下面这些: * 1、propertysource注解的 */ Set<BeanDefinition> candidates = findCandidateComponents(basePackage);如下所示,在获取某个包下面的满足条件的bean时,代码如下:
public Set<BeanDefinition> findCandidateComponents(String basePackage) { Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>(); try { // 1 String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) + "http://www.likecs.com/" + this.resourcePattern; Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath); // 2 for (Resource resource : resources) { if (resource.isReadable()) { try { // 3 MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource); // 4 if (isCandidateComponent(metadataReader)) { ...我们逐个讲解每个代码点:
1处,获取包下面的全部resource,类型为Resource
2处,遍历Resource数组
3处,获取资源的metadataReader,这个metadataReader,可以用来获取资源(一般为class文件)上的注解
4处,调用方法isCandidateComponent,判断是否为候选的bean
接下来,我们看看 isCandidateComponent 怎么实现的:
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException { // 1 for (TypeFilter tf : this.excludeFilters) { if (tf.match(metadataReader, this.metadataReaderFactory)) { return false; } } for (TypeFilter tf : this.includeFilters) { // 2 if (tf.match(metadataReader, this.metadataReaderFactory)) { AnnotationMetadata metadata = metadataReader.getAnnotationMetadata(); if (!metadata.isAnnotated(Profile.class.getName())) { return true; } AnnotationAttributes profile = MetadataUtils.attributesFor(metadata, Profile.class); return this.environment.acceptsProfiles(profile.getStringArray("value")); } } return false; }
1处,遍历excludeFilters,如果参数中的class,匹配excludeFilter,则返回false,表示不合格;
2处,遍历includeFilters,如果参数中的class,匹配includeFilter,则基本可以断定合格了,但是因为@profile注解的存在,又加了一层判断,如果class上不存在profile,则返回true,合格;
否则,判断profile是否和当前激活了的profile匹配,如果匹配,则返回true,否则flase。
敲黑板,这里的excludeFilters和includeFilters,其实就是@component-scan中的如下属性:
public @interface ComponentScan { ... /** * Indicates whether automatic detection of classes annotated with {@code @Component} * {@code @Repository}, {@code @Service}, or {@code @Controller} should be enabled. */ boolean useDefaultFilters() default true; /** * Specifies which types are eligible for component scanning. * <p>Further narrows the set of candidate components from everything in * {@link #basePackages()} to everything in the base packages that matches * the given filter or filters. * @see #resourcePattern() */ Filter[] includeFilters() default {}; /** * Specifies which types are not eligible for component scanning. * @see #resourcePattern() */ Filter[] excludeFilters() default {}; ... } spring 为什么认识@Component注解的类大家看了前面的代码,大概知道了,判断一个类,是否足够荣幸,被扫描为一个bean,是依赖于两个属性,一个includeFilters,一个excludeFilters。
但是,我们好像并不能知道:为什么@Component注解的类、@controller、@service注解的类,就能成为一个bean呢?
我们先直接做个黑盒实验,按照如下配置:
<context:component-scan use-default-filters="true" base-package="org.springframework.test"> </context:component-scan>被扫描的类路径下,一个测试类,注解了Controller:
@Controller public class TestController { }然后我们运行测试代码:
public static void testDefaultFilter() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:component-scan-default-filter.xml"); TestController bean = context.getBean(TestController.class); System.out.println(bean); }在如下地方,debug断点可以看到:
如上的includeFilters,大家看到了,包含了一个TypeFilter,类型为org.springframework.core.type.filter.AnnotationTypeFilter,其类继承结构为: