@ComponentScan向Spring IOC容器中注入组件
在使用Spring框架开发应用的过程中,大家都知道使用Spring开发应用程序,我们应用程序中大多数的Bean都是通过Spring的IOC容器来管理。将Bean注入到Spring IOC容器中的方式多种多样,如通过传统的XML方式注入,通过注解的方式注入等。本文我们就来详细的探索一下在使用注解配置的方式注入Bean时,@ComponentScan注解组件扫描的应用。
1、@ComponentScan介绍
顾名思义,@ComponentScan注解提供给我们的功能就是自动组件扫描,将指定包下的被@Repository、@Service、@Controller、@RestController、@Component、@Configuration注解所标注的Bean自动扫描注入到Spring IOC容器中,由Spring IOC容器统一管理这些Bean。
-
basePackages与value: 用于指定包的路径,进行自动扫描;
-
basePackageClasses: 用于指定某个类的包的路径进行扫描;
-
nameGenerator: bean的名称的生成器;
-
useDefaultFilters: 是否开启对@Repository、@Service、@Controller、@RestController、@Component、@Configuration注解的Bean进行自动扫描注入;
-
includeFilters: 包含的过滤条件,包括:
- FilterType.ANNOTATION:按照注解的方式过滤;
- FilterType.ASSIGNABLE_TYPE:按照给定类型的方式过滤;
- FilterType.ASPECTJ:使用ASPECTJ表达式的方式过滤;
- FilterType.REGEX:使用正则表达式的方式过滤;
- FilterType.CUSTOM:自定义规则过滤;
- excludeFilters: 排除的过滤条件,用法和includeFilters一样;
2、使用@ComponentScan注解的方式注入组件
首先我们新创建一个Maven项目,添加spring-context和junit的依赖,pom.xml如下:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
在com.training.componentscan包下我们分别创建component、controller、repository、service、config、filter包。
在component包中创建SoftWareComponent类,加@Component注解:
@Component
public class SoftWareComponent {
//... ...
}
在controller包中创建SoftWareController类,加@Controller注解:
@Controller
public class SoftWareController {
//... ...
}
在repository包中创建SoftWareRepository类,加@Repository注解:
@Repository
public class SoftWareRepository {
//... ...
}
在service包中创建SoftWareService类,加@Service注解:
@Service
public class SoftWareService {
//... ...
}
在config包中创建ComponentScanConfig类,写入如下内容:
@Configuration //告诉Spring这是一个配置类
@ComponentScan(basePackages = "com.training.componentscan")
//扫描com.training.componentscan包及其子包中被@Repository、@Service、@Controller、@RestController、@Component、@Configuration注解所标注的Bean
public class ComponentScanConfig {
//... ...
}
我们创建一个单元测试类IOCTest,用来测试看有哪些组件被注入到了Spring IOC容器中。
public class IOCTest {
@Test
public void test() {
AnnotationConfigApplicationContext application = new AnnotationConfigApplicationContext(ComponentScanConfig.class);
String[] names = application.getBeanDefinitionNames();
for (String name : names) {
System.out.println(name);
}
}
}
项目结构如下图:
执行单元测试我们得到如下结果:
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
componentScanConfig
softWareComponent
softWareController
softWareRepository
softWareService
从执行结果我们可以看到,容器启动后,我们所有加了@Repository、@Service、@Controller、@Component、@Configuration注解的Bean都被注入到了Spring IOC容器中。
3、excludeFilters应用举例
在上面的例子中,在使用组件扫描时,如果我们想过滤某些Bean,不希望所有加了@Repository、@Service、@Controller、@RestController、@Component、@Configuration这些注解的Bean都被注入到Spring IOC中,我们该怎么处理呢?
@ComponentScan注解有一个excludeFilters属性,通过给该属性设置对应的值可以过滤掉指定条件的Bean自动注入到Spring IOC容器中,可过滤的类型我们在上文中已经说明,这里不再赘述。
3.1 FilterType.ANNOTATION(注解的方式过滤)
使用注解方式过滤指定注解的Bean很简单,我们修改配置类ComponentScanConfig如下,过滤掉@Controller注解的Bean自动注入Spring IOC容器中。
@Configuration //告诉Spring这是一个配置类
@ComponentScan(basePackages = "com.training.componentscan",excludeFilters={
@Filter(type=FilterType.ANNOTATION,classes={Controller.class})
})
//扫描com.training.componentscan包及其子包中被@Repository、@Service、@Controller、@RestController、@Component、@Configuration注解所标注的Bean
public class ComponentScanConfig {
}
执行单元测试我们可以得到如下结果:
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
componentScanConfig
softWareComponent
softWareRepository
softWareService
从执行结果我们可以看到,容器启动后,被@Controller注解的Bean被过滤掉了。
3.2 FilterType.ASSIGNABLE_TYPE(给定类型的方式过滤)
使用给定类型的方式过滤,即过滤指定的Bean,我们修改配置类ComponentScanConfig如下,过滤掉SoftWareRepository这个Bean。
@Configuration //告诉Spring这是一个配置类
@ComponentScan(basePackages = "com.training.componentscan",excludeFilters={
@Filter(type=FilterType.ANNOTATION,classes={Controller.class}),
@Filter(type=FilterType.ASSIGNABLE_TYPE,classes={SoftWareRepository.class})
})
//扫描com.training.componentscan包及其子包中被@Repository、@Service、@Controller、@RestController、@Component、@Configuration注解所标注的Bean
public class ComponentScanConfig {
}
执行单元测试我们可以得到如下结果:
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
componentScanConfig
softWareComponent
softWareService
从执行结果我们可以看到,容器启动后,SoftWareRepository这个Bean被过滤掉了。
3.3 FilterType.ASPECTJ(ASPECTJ表达式的方式过滤)
使用ASPECTJ表达式的方式过滤Bean,首先我们需要加入切面aspectj的依赖。
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.2</version>
</dependency>
我们使用ASPECTJ表达式的方式过滤掉以Component结尾的Bean,修改ComponentScanConfig如下:
@Configuration //告诉Spring这是一个配置类
@ComponentScan(basePackages = "com.training.componentscan",excludeFilters={
@Filter(type=FilterType.ANNOTATION,classes={Controller.class}),
@Filter(type=FilterType.ASSIGNABLE_TYPE,classes={SoftWareRepository.class}),
@Filter(type=FilterType.ASPECTJ,pattern={"com.training.componentscan..*Component"})
})
//扫描com.training.componentscan包及其子包中被@Repository、@Service、@Controller、@RestController、@Component、@Configuration注解所标注的Bean
public class ComponentScanConfig {
}
执行单元测试,我们得到如下结果:
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
componentScanConfig
softWareService
从执行结果我们可以看到,容器启动后,以Component结尾的SoftWareComponent这个Bean被过滤掉了。
3.4 FilterType.REGEX(正则表达式的方式过滤)
我们使用正则表达式的方式过滤掉以Service结尾的Bean,修改ComponentScanConfig如下:
@Configuration //告诉Spring这是一个配置类
@ComponentScan(basePackages = "com.training.componentscan",excludeFilters={
@Filter(type=FilterType.ANNOTATION,classes={Controller.class}),
@Filter(type=FilterType.ASSIGNABLE_TYPE,classes={SoftWareRepository.class}),
@Filter(type=FilterType.ASPECTJ,pattern={"com.training.componentscan..*Component"}),
@Filter(type=FilterType.REGEX,pattern=".*Service")
})
//扫描com.training.componentscan包及其子包中被@Repository、@Service、@Controller、@RestController、@Component、@Configuration注解所标注的Bean
public class ComponentScanConfig {
}
执行单元测试,得到如下结果:
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
componentScanConfig
从执行结果我们可以看到,容器启动后,以Service结尾的SoftWareService这个Bean被过滤掉了。
3.5 FilterType.CUSTOM(自定义规则过滤)
我们使用自定义过滤器过滤类名中不含有"Co"的Bean。
自定义过滤器要实现org.springframework.core.type.filter.TypeFilter接口,定义自己的过滤器ComponentFilter实现TypeFilter接口如下:
public class ComponentFilter implements TypeFilter {
/**
* metadataReader:读取到的当前正在扫描的类的信息 metadataReaderFactory:可以获取到其他任何类信息
*/
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
throws IOException {
// 获取当前类注解的信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
// 获取当前正在扫描的类的类信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
// 获取当前类资源(类的路径)
Resource resource = metadataReader.getResource();
String className = classMetadata.getClassName();
System.out.println("---->>>>>"+className);
if(!className.contains("Co")){
return true;
}
return false;
}
}
修改ComponentScanConfig如下:
@Configuration //告诉Spring这是一个配置类
@ComponentScan(basePackages = "com.training.componentscan",excludeFilters={
@Filter(type=FilterType.CUSTOM,classes={ComponentFilter.class})
})
//扫描com.training.componentscan包及其子包中被@Repository、@Service、@Controller、@RestController、@Component、@Configuration注解所标注的Bean
public class ComponentScanConfig {
}
执行单元测试我们得到如下结果:
---->>>>>com.training.componentscan.IOCTest
---->>>>>com.training.componentscan.IOCTest1
---->>>>>com.training.componentscan.component.SoftWareComponent
---->>>>>com.training.componentscan.controller.SoftWareController
---->>>>>com.training.componentscan.filter.ComponentFilter
---->>>>>com.training.componentscan.repository.SoftWareRepository
---->>>>>com.training.componentscan.service.SoftWareService
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
componentScanConfig
softWareComponent
softWareController
从执行结果可知,所有没有包含“Co”的Bean已经被全部过滤,只有类名包含"Co"的Bean注入到了Spring IOC容器中。
只包含includeFilters属性的使用与excludeFilters属性的使用基本一致,这里不再赘述。
小结:
本文我们通过注解的方式讲述了@ComponentScan的应用,以及通过实例演示的方式向大家展示了自动扫描注入时如何通过不同方式排除、包含满足指定条件的Bean,希望大家共勉。