Spring Framework 中的 @ComponentScan 注解扫描的配置类分为两种:一种是完全(Full)的配置类,另外一种是简化(Lite)的配置类。
完全(Full)的配置类:@Configuration 注解修饰的类。
1 2 3 public static boolean isFullConfigurationCandidate (AnnotationMetadata metadata) { return metadata.isAnnotated(Configuration.class.getName()); }
简化(Lite)的配置类:@Component、 @ComponentScan、 @Import、 @ImportResource 注解修饰的类以及 @Bean注解修饰的方法。
1 2 3 4 5 6 static { candidateIndicators.add(Component.class.getName()); candidateIndicators.add(ComponentScan.class.getName()); candidateIndicators.add(Import.class.getName()); candidateIndicators.add(ImportResource.class.getName()); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public static boolean isLiteConfigurationCandidate (AnnotationMetadata metadata) { if (metadata.isInterface()) { return false ; } for (String indicator : candidateIndicators) { if (metadata.isAnnotated(indicator)) { return true ; } } try { return metadata.hasAnnotatedMethods(Bean.class.getName()); } catch (Throwable ex) { if (logger.isDebugEnabled()) { logger.debug("Failed to introspect @Bean methods on class [" + metadata.getClassName() + "]: " + ex); } return false ; } }
@ComponentScan 直接使用 创建一个 Spring 配置类,直接添加 @ComponentScan 注解。该注解默认会扫描该类所在包下的所有配置类,相当于之前的 <context:component-scan/>
。
1 2 3 4 5 6 7 8 9 10 package cn.worstone.config;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;@Configuration @ComponentScan public class SpringConfig {}
创建一个 测试类,使用 AnnotationConfigApplicationContext 对象加载配置类,使用 getBeanDefinitionNames() 方法获取已经注册到容器中的 bean 的名称。
1 2 3 4 5 6 7 8 9 10 11 12 13 import cn.worstone.config.SpringConfig;import org.springframework.context.ApplicationContext;import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class AnnotationTest { public static void main (String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext (SpringConfig.class); for (String beanName : context.getBeanDefinitionNames()) { System.out.println("bean name: " + beanName); } } }
运行结果:
1 2 3 4 5 6 bean name: org.springframework.context.annotation.internalConfigurationAnnotationProcessor bean name: org.springframework.context.annotation.internalAutowiredAnnotationProcessor bean name: org.springframework.context.annotation.internalCommonAnnotationProcessor bean name: org.springframework.context.event.internalEventListenerProcessor bean name: org.springframework.context.event.internalEventListenerFactory bean name: springConfig
可以看到,除了 Spring 本身注册的一些 bean 以外,SpringConfig 这个类也注册到容器中了。如果这个配置类使用了 @Import 注解导入了其他的配置类,那么导入的类也会被注册到容器中。
@ComponentScan 指定扫描位置 指定要扫描的位置,需要使用 @ComponentScan 的 valule 属性来配置。创建了一个 dao包,并在包下创建了 UserDao 类。在类上添加 @Repository 注解,说明该类是一个 Component。
1 2 3 4 5 6 7 8 package cn.worstone.dao;import org.springframework.stereotype.Repository;@Repository public class UserDao { }
修改配置类 SpringConfig,在 @ComponentScan 注解中指定扫描的位置:
1 2 3 4 5 6 7 8 9 10 package cn.worstone.config;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;@Configuration @ComponentScan(basePackages = "cn.worstone.dao") public class SpringConfig {}
运行结果:
1 2 3 4 5 6 7 bean name: org.springframework.context.annotation.internalConfigurationAnnotationProcessor bean name: org.springframework.context.annotation.internalAutowiredAnnotationProcessor bean name: org.springframework.context.annotation.internalCommonAnnotationProcessor bean name: org.springframework.context.event.internalEventListenerProcessor bean name: org.springframework.context.event.internalEventListenerFactory bean name: springConfig bean name: userDao
可以看到,UserDao 已经注册到容器中了。
@ComponentScan 设置 excludeFilters 和 includeFilters 使用 使用 excludeFilters 来按照规则排除某些位置的扫描,使用 includeFilters 来按照规则只扫描某些位置。
excludeFilters 的参数是一个 Filter[] 数组,然后指定 FilterType 的类型为 ANNOTATION,也就是通过注解来过滤,最后的 value 则是 Service 注解类。配置之后,在 Spring 扫描的时候,就会跳过 cn.worstone 路径下所有被 @Service 注解修饰的类。includeFilters 的参数与 excludeFilters 参数相同。
1 2 3 4 5 6 7 8 9 10 11 12 package cn.worstone.config;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.FilterType;import org.springframework.stereotype.Service;@Configuration @ComponentScan(basePackages = "cn.worstone", excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = {Service.class})}) public class SpringConfig {}
创建一个 service 的包,并在包下创建一个 UserService 类,在类上添加 @Service 注解。
1 2 3 4 5 6 7 8 package cn.worstone.service;import org.springframework.stereotype.Service;@Service public class UserService { }
修改 SpringConfig 配置类,设置 includeFilters:
1 2 3 4 5 6 7 8 9 10 11 12 package cn.worstone.config;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.FilterType;import org.springframework.stereotype.Service;@Configuration @ComponentScan(basePackages = "cn.worstone", includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = {Service.class})}) public class SpringConfig {}
运行结果:
1 2 3 4 5 6 7 8 bean name: org.springframework.context.annotation.internalConfigurationAnnotationProcessor bean name: org.springframework.context.annotation.internalAutowiredAnnotationProcessor bean name: org.springframework.context.annotation.internalCommonAnnotationProcessor bean name: org.springframework.context.event.internalEventListenerProcessor bean name: org.springframework.context.event.internalEventListenerFactory bean name: springConfig bean name: userDao bean name: userService
根据配置过滤规则,应该只有 @Service 注解修饰的类才会被注册到容器中,那么,为什么 @Repository 注解修饰的类也被注册到容器中了呢?这里涉及 @ComponentScan useDefaultFilters 属性的用法,该属性默认值为 true,也就是说 Spring 默认会自动发现被 @Component、 @Repository、 @Service 以及 @Controller 注解修饰的类,并注册到容器中。想要达到只包含某些位置的扫描结果,就必须将这个默认行为给禁用掉(在 @ComponentScan 中将 useDefaultFilters 属性设置为 false 即可)。
一定要慎重使用这个属性,否则无法扫描到 @Component、 @Repository、 @Service 以及 @Controller 注解修饰的类。
1 2 3 4 5 6 7 <xsd:attribute name ="use-default-filters" type ="xsd:boolean" default ="true" > <xsd:annotation > <xsd:documentation > <![CDATA[Indicates whether automatic detection of classes annotated with @Component, @Repository, @Service, or @Controller should be enabled. Default is "true".]]> </xsd:documentation > </xsd:annotation > </xsd:attribute >
1 2 3 4 5 6 7 8 9 10 11 12 package cn.worstone.config;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.FilterType;import org.springframework.stereotype.Service;@Configuration @ComponentScan(basePackages = "cn.worstone", includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = {Service.class})}, useDefaultFilters = false) public class SpringConfig {}
运行结果:
1 2 3 4 5 6 7 bean name: org.springframework.context.annotation.internalConfigurationAnnotationProcessor bean name: org.springframework.context.annotation.internalAutowiredAnnotationProcessor bean name: org.springframework.context.annotation.internalCommonAnnotationProcessor bean name: org.springframework.context.event.internalEventListenerProcessor bean name: org.springframework.context.event.internalEventListenerFactory bean name: springConfig bean name: userService
@ComponentScan 重复标注使用 以这种方式使用,必须在配置类中添加 @Configuration 注解,否则无效。
如果使用 Java 8 及以上版本,则可以直接添加多个 @ComponentScan 注解来添加多个扫描规则。
1 2 3 4 5 6 7 8 9 10 11 12 package cn.worstone.config;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.FilterType;@Configuration @ComponentScan(basePackages = "cn.worstone.dao") @ComponentScan(basePackages = "cn.worstone.service") public class SpringConfig {}
使用 @ComponentScans 注解来添加多个 @ComponentScan,从而添加多个扫描规则。
1 2 3 4 5 6 7 8 9 package cn.worstone.config;import org.springframework.context.annotation.*;@Configuration @ComponentScans({@ComponentScan(basePackages = "cn.worstone.dao"), @ComponentScan(basePackages = "cn.worstone.service")}) public class SpringConfig {}
@ComponentScan 自定义过滤规则使用 前面使用过的 @Filter 注解,里面的 type 属性是一个 FilterType 的枚举类型。使用 CUSTOM 类型,就可以自定义过滤类,从而达到自定义过滤规则的目的。
1 2 3 4 5 6 7 public enum FilterType { ANNOTATION, ASSIGNABLE_TYPE, ASPECTJ, REGEX, CUSTOM }
首先创建一个实现 TypeFilter 接口的 ServiceFilter 类,并实现其 match 方法。
这里简单对扫描到的类名进行判断,如果类名包含 ”Service“ 的就符合条件,也就会注入到容器中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 package cn.worstone.filter;import org.springframework.core.io.Resource;import org.springframework.core.type.AnnotationMetadata;import org.springframework.core.type.ClassMetadata;import org.springframework.core.type.classreading.MetadataReader;import org.springframework.core.type.classreading.MetadataReaderFactory;import org.springframework.core.type.filter.TypeFilter;import java.io.IOException;public class ServiceFilter implements TypeFilter { @Override public boolean match (MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata(); ClassMetadata classMetadata = metadataReader.getClassMetadata(); Resource resource = metadataReader.getResource(); if (classMetadata.getClassName().contains("Service" )) { return true ; } return false ; } }
修改配置类 SpringConfig,修改过滤类型为 CUSTOM,并设置 value 属性为 ServiceFilter.class。
1 2 3 4 5 6 7 8 9 10 11 12 package cn.worstone.config;import cn.worstone.filter.ServiceFilter;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.FilterType;@Configuration @ComponentScan(basePackages = "cn.worstone", includeFilters = {@ComponentScan.Filter(type = FilterType.CUSTOM, value = {ServiceFilter.class})}, useDefaultFilters = false) public class SpringConfig {}
运行结果:
1 2 3 4 5 6 7 8 bean name: org.springframework.context.annotation.internalConfigurationAnnotationProcessor bean name: org.springframework.context.annotation.internalAutowiredAnnotationProcessor bean name: org.springframework.context.annotation.internalCommonAnnotationProcessor bean name: org.springframework.context.event.internalEventListenerProcessor bean name: org.springframework.context.event.internalEventListenerFactory bean name: springConfig bean name: serviceFilter bean name: userService
因为 cn.worstone.filter.ServiceFilter 以及 cn.worstone.service.UserService 两个类都符合条件,所以都被注入到了容器中。
参考资料:
__END__