在讲述 @Configuration 启动容器+ @Component 注册 Bean 小节中简单介绍了 @ComponentScan 注解的使用。
@ComponentScan 的功能其实就是自动扫描并加载符合条件的组件或 bean 定义,最终将这些 bean 定义加载到容器中。 我们可以通过 backPackages 等属性指定@ComponentScan 自动扫描的范围,如果不指定,则默认 Spring 框架实现从声明@ComponentScan 所在类的 package 进行扫描,默认情况下是不指定的,所以 SpringBoot 的启动类最好在 root package 下。
首先看下 @ComponentScan 的源码:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;
ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
String resourcePattern() default "**/*.class";
boolean useDefaultFilters() default true;
ComponentScan.Filter[] includeFilters() default {};
ComponentScan.Filter[] excludeFilters() default {};
boolean lazyInit() default false;
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface Filter {
FilterType type() default FilterType.ANNOTATION;
@AliasFor("classes")
Class<?>[] value() default {};
@AliasFor("value")
Class<?>[] classes() default {};
String[] pattern() default {};
}
}
复制代码
重点有以下几个属性:
属性
basePackages 与 value
@ComponentScan(basePackages = “”) //单个
@ComponentScan(basePackages = {“com.example.dao”,“aaa”,“…”}) //多个
//value 同理
复制代码
注意:可以省略 “ basePackages = ”。
@Configuration
@ComponentScan("com.example.dao")
public class MyConfig {}
@Configuration
@ComponentScan("com.example.dao","com.example.service")
public class MyConfig {}
@Configuration
@ComponentScan("com.example.*") //通配符匹配所有的包
public class MyConfig {}
复制代码
basePackageClasses
@ComponentScan(basePackageClasses = “”) //单个
@ComponentScan(basePackageClasses = {“HelloController.class”,“bbb”,“…”}) //多个
复制代码
注意:不可以省略“basePackageClasses =”
@Configuration
@ComponentScan(basePackageClasses = HelloController.class)
public class MyConfig {
}
复制代码
includeFilters
接下来我们用以下代码进行测试。
Repository 的注解类:
package com.example.dao;
@Repository
public class BusinessDAO {
public void update(){
System.out.println("调用了 dao 层的 update 方法....");
}
}
复制代码
Service 的注解类:
package com.example.service;
@Service
public class BusinessService {
@Autowired
private BusinessDAO businessDAO;
public void service(){
System.out.println("调用了 service 层的 service() 方法 .....");
businessDAO.update();
}
}
复制代码
Controller 的注解类:
package com.example.controller;
@Controller
public class BusinessController {
@Autowired
private BusinessService service;
public void request() {
System.out.println(" 调用了 Controller 的 request() 方法...");
service.service();
}
}
复制代码
Configuration 配置类:
package com.example.configuration;
@Configuration
//@ComponentScan(basePackages = {"com.example.dao","com.example.service","com.example.controller"})
@ComponentScan(value = {"com.example.dao","com.example.service","com.example.controller"},
includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Repository.class)},
useDefaultFilters = false)
public class ScanConfig {
}
复制代码
测试类代码:
public class ScanConfigTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ScanConfig.class);
String[] definitionNames = context.getBeanDefinitionNames();
for(String name:definitionNames){
System.out.println(name);
}
}
}
复制代码
执行结果为:
19:22:48.157 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'scanConfig' 19:22:48.162 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'businessDAO' org.springframework.context.annotation.internalConfigurationAnnotationProcessor org.springframework.context.annotation.internalAutowiredAnnotationProcessor org.springframework.context.annotation.internalCommonAnnotationProcessor org.springframework.context.event.internalEventListenerProcessor org.springframework.context.event.internalEventListenerFactory scanConfig businessDAO 复制代码
根据结果分析: 除了 spring 本身注册的一些 bean 之外,可以看到最后一行,已经将 ScanConfig 这个类和 BusinessDAO 注册进容器中了。
includeFilters 的参数是一个 Filter[] 数组,然后指定 FilterType 的类型为 ANNOTATION,也就是通过注解来过滤,最后的 value 则是 Repository 注解类。配置之后,在 spring 扫描的时候,就会筛选 com.example 下的三个包中,所有被 @Repository 注解标注的类。
注意:需要设置 useDefaultFilters 为 false,否则@ComponentScan 注解会将 被 @Component、@Repository、@Service 和 @Controller 标注的类都注册进容器中。同时因为我们在 Controller 和 Service 注解的类中关联了其他的类(@Autowired),所以最好在 includeFilters 的 Filter 属性中不要设置为 Controller.class 或 Service.class。
excludeFilters
修改配置类:
@Configuration
//@ComponentScan(basePackages = {"com.example.dao","com.example.service","com.example.controller"})
@ComponentScan(value = {"com.example.dao","com.example.service","com.example.controller"},
excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Controller.class)})
public class ScanConfig {
}
复制代码
同 excludeFilters 属性类似, 配置之后,在 spring 扫描的时候,就会跳过 com.example 下的三个包中,所有被 @Controller 注解标注的类。
执行结果为:
19:44:39.191 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'scanConfig' 19:44:39.195 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'businessDAO' 19:44:39.195 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'businessService' org.springframework.context.annotation.internalConfigurationAnnotationProcessor org.springframework.context.annotation.internalAutowiredAnnotationProcessor org.springframework.context.annotation.internalCommonAnnotationProcessor org.springframework.context.event.internalEventListenerProcessor org.springframework.context.event.internalEventListenerFactory scanConfig businessDAO businessService 复制代码
添加自定义过滤规则
在前面使用过 @Filter 注解,里面的 type 属性是一个 FilterType 的枚举类型:
public enum FilterType {
ANNOTATION,
ASSIGNABLE_TYPE,
ASPECTJ,
REGEX,
CUSTOM
}
复制代码
使用 CUSTOM 类型,就可以实现自定义过滤规则。
package com.example.filter;
public class CustomTypeFilter 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("Business")){
return true;
}
return false;
}
}
复制代码
增加 Service 注解修饰的类:
package com.example.service;
@Service
public class UserService {
}
复制代码
修改配置类:
@Configuration
@ComponentScan(value = {"com.example.dao","com.example.service","com.example.controller"},
excludeFilters = {@ComponentScan.Filter(type = FilterType.CUSTOM,classes = CustomTypeFilter.class)})
public class ScanConfig {
}
复制代码
执行测试类代码,结果为:
19:56:25.419 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'scanConfig' 19:56:25.424 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'userService' org.springframework.context.annotation.internalConfigurationAnnotationProcessor org.springframework.context.annotation.internalAutowiredAnnotationProcessor org.springframework.context.annotation.internalCommonAnnotationProcessor org.springframework.context.event.internalEventListenerProcessor org.springframework.context.event.internalEventListenerFactory scanConfig userService 复制代码
这里简单对扫描到的类名进行判断,如果类名包含”Business“的就符合条件,就会被剔除,不会注入到容器中。
@Component
1、不指定 bean 的名称,默认为类名首字母小写 university
@Component
public class BeanWithComponent {
public void sayHello(){
System.out.println("BeanWithComponent sayHello...");
}
public void start(){
System.out.println("BeanWithComponent 初始化。。。");
}
public void cleanUp(){
System.out.println("BeanWithComponent 销毁。。。");
}
}
复制代码
获取 bean 方式:
@Autowired BeanWithComponent beanWithComponent; 复制代码
或
ApplicationContext context = new AnnotationConfigApplicationContext(ComfigureWithScan.class);
BeanWithComponent bean = (BeanWithComponent) context.getBean("beanWithComponent");
复制代码
2、指定 bean 的名称
@Component("bean2")
public class BeanWithComponent {
}
复制代码
获取 bean 方式:
@Autowired BeanWithComponent bean2; 复制代码
或
ApplicationContext context = new AnnotationConfigApplicationContext(ComfigureWithScan.class);
BeanWithComponent bean = (BeanWithComponent) context.getBean("bean2");
复制代码
@ComponentScan 注解有以下特性:
自定义扫描路径下边带有 @Controller , @Service , @Repository , @Component 注解加入 spring 容器
通过 includeFilters 加入扫描路径下没有以上注解的类加入 spring 容器
通过 excludeFilters 过滤出不用加入 spring 容器的类
自定义增加了@Component 注解的注解方式
接下来讲述一下关于@ComponentScan 注解的两个扩展。
源码如下:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
public @interface ComponentScans {
ComponentScan[] value();
}
复制代码
配置类:
@Configuration
@ComponentScans(value = @ComponentScan("com.example.dao"))
public class ScanConfig {
}
复制代码
执行结果为:
20:07:45.908 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'scanConfig' 20:07:45.914 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'businessDAO' org.springframework.context.annotation.internalConfigurationAnnotationProcessor org.springframework.context.annotation.internalAutowiredAnnotationProcessor org.springframework.context.annotation.internalCommonAnnotationProcessor org.springframework.context.event.internalEventListenerProcessor org.springframework.context.event.internalEventListenerFactory scanConfig businessDAO 复制代码
上述代码我们是通过 @Configuration + @ComponentScan 注解来实现 spring 加载,当然也可以在配置文件中加上扫描的配置。
spring-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!--<context:component-scan base-package="com.example.dao" annotation-config="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
</context:component-scan>-->
<context:component-scan base-package="com.example.dao" />
<context:component-scan base-package="com.example.controller" />
<context:component-scan base-package="com.example.service" />
</beans>
复制代码
修改测试类代码:
public class ScanConfigTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
String[] definitionNames = context.getBeanDefinitionNames();
for(String name:definitionNames){
System.out.println(name);
}
}
}
复制代码
运行得到以下结果:
14:59:12.773 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'businessDAO' 14:59:12.778 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'businessController' 14:59:12.789 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'businessService' 14:59:12.789 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'userService' businessDAO org.springframework.context.annotation.internalConfigurationAnnotationProcessor org.springframework.context.annotation.internalAutowiredAnnotationProcessor org.springframework.context.annotation.internalCommonAnnotationProcessor org.springframework.context.event.internalEventListenerProcessor org.springframework.context.event.internalEventListenerFactory businessController businessService userService 复制代码
关于 的使用,关于属性的详细讲解可以参考: 详解
注意: 中有一个 annotation-config 属性,该属性主要是隐式地向 Spring 容器注册 internalConfigurationAnnotationProcessor 、 internalAutowiredAnnotationProcessor 、 internalCommonAnnotationProcessor 、 internalEventListenerProcessor 以及 internalEventListenerFactory 这五个 bean 类。
当 annotation-config 属性值设为 false 时,即不会注册这五个类。但是 @ComponentScan 注解没有该属性。 这是因为在几乎所有情况下,使用 @ComponentScan 时,都假定使用默认的注释配置处理(例如,处理@Autowired 和 friends )。 此外,在使用 AnnotationConfigApplicationContext 时,注释配置处理器始终会被注册,这意味着在 @ComponentScan 级别禁用它们的任何尝试都将被忽略。
@SpringBootApplication不仅包括上面的三个重要注解,还包含有4个方法:
Class[] exclude() default {};
String[] excludeName() default {};
String[] scanBasePackages() default {};
Class[] scanBasePackageClasses() default {};
这里总结下 @SpringBootApplication 中的三个重要注解的特征:
@Configuration 定义 Spring Ioc 容器的配置类。
@EnableAutoConfiguration 从 classpath 中搜寻所有 META/spring.factories 配置文件,并将其中 org.springframework.boot.autoconfigure.EnableAutoConfiguration 对应的配置项,也就是一个自动配置类列表加载到 Ioc 容器中。 简单说,就是 @EnawebleAutoConfiguration 让 Spring Boot 根据类路径下的 jar 包依赖为当前项目进行自动配置,例如,添加了 spring-boot-starter-web 依赖,会自动添加 Tomcat 和 Spring MVC 的依赖。而对于所有标注 @Configuration 的配置类,统一使用 ConfigurationClassParser 解析的。
@ComponentScan 自动扫描并加载符合条件的组件或者 bean 定义。
spring4.0之二:@Configuration的使用
springboot系列文章之SpringBootApplication注解