该系列文章主要学习 雷丰阳老师的《Spring注解驱动》课程总结。
原课程地址:课程地址包括了自己阅读其他书籍《Spring揭秘》《Spring Boot 实战》等课程。该系列文档会不断的完善,欢迎大家留言及提意见。
以前可以通过xml来控制包扫描的范围,如今如果只是利用注解,如何合理的控制包扫描的范围呢?
注意:凡是在指定的包或其子包中的类上标注了@Repository、@Service、@Controller、@Component注解的类都会被扫描到,并将这个类注入到Spring容器中。其他的比如 @Configuration、@Bean是不受影响的。
定义两个演示的类,分别标注有 @Repository、@Service,其余的注解跟该方法类似
@Repository
public class BookDao {
}
@Service
public class BookService {
}
还是继续利用之前定义的配置类
@Configuration(value = "mainConfig")
public class MainConfig {
// @Bean注解是给IOC容器中注册一个bean,类型自然就是返回值的类型,id默认是用方法名作为id
@Primary
@Bean(value = "child")
public Person personChild() {
return new Person("张三", 20);
}
@Bean(value = "adult")
public Person personAdult() {
return new Person("蜡笔小新", 20);
}
}
在不加包扫描的前提下查看容器中所有的组件信息
public class TestMain {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
printAllBean(applicationContext);
}
private static void printAllBean(ApplicationContext applicationContext) {
System.out.println("=====所有的Bean对象=====");
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String s : beanDefinitionNames) {
System.out.println(s);
}
}
}
查看现有组件列表
=====所有的Bean对象=====
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig
bookDao
bookService
child
adult
在 MainConfig 类上增加包扫描注解信息,只想扫描 com.hone.learn.dao 路径下的组件
@Configuration(value = "mainConfig")
@ComponentScan(value = "com.hone.learn.dao")
public class MainConfig {
// @Bean注解是给IOC容器中注册一个bean,类型自然就是返回值的类型,id默认是用方法名作为id
@Primary
@Bean(value = "child")
public Person personChild() {
return new Person("张三", 20);
}
@Bean(value = "adult")
public Person personAdult() {
return new Person("蜡笔小新", 20);
}
}
查看输出结果发现 bookService 已经不输出了
=====所有的Bean对象=====
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig
bookDao
child
adult
可以指定只包含或者排除某些条件
Filter[] includeFilters() default {};
Filter[] excludeFilters() default {};
两个方法的返回值都是Filter[]数组,在ComponentScan注解类的内部存在Filter注解类,大家可以看下上面的代码。
现在有这样一个需求,除了@Controller和@Service标注的组件之外,IOC容器中剩下的组件我都要,即相当于是我要排除@Controller和@Service这俩注解标注的组件。要想达到这样一个目的,我们可以在MainConfig类上通过@ComponentScan注解的excludeFilters()方法实现。例如,我们在MainConfig类上添加了如下的注解。
@Configuration(value = "mainConfig")
@ComponentScan(value = "com.hone.learn", excludeFilters =
{@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Repository.class})})
public class MainConfig {
最后 扫描容器中所有的组件,发现已经不包括 bookDao 了,已经根据规则合理的排除了。
=====所有的Bean对象=====
//省略自带的bean输出结果
mainConfig
bookService
child
adult
当我们使用includeFilters()方法来指定只包含哪些注解标注的类时,需要禁用掉默认的过滤规则,如果只想扫描备注有 @Repository 的组件。
/**
* 只想获取 Repository 组件
*/
@ComponentScan(value = "com.hone.learn", includeFilters =
{@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Repository.class}) },
useDefaultFilters = false)
@Configuration(value = "mainConfig")
public class MainConfig {
在输出的结果中,只包含了@Repository注解标注的组件名称,并没有输出@Service注解标注的组件名称。
=====所有的Bean对象=====
// 省略Spring自带的组件
mainConfig
bookDao
child
adult
上面只是使用了 FilterType.ANNOTATION ,根据名称知道这个肯定是根据注解名称来判断的。但是Spring本身提供了多种方式
分别有
前四种的使用方式基本上跟 第一种按照注解的方式类似,使用上没有什么区别。值得说明的是第五种自定义的类型。
该匹配规则只是定义了一个抽象的方法,如果用户需要自定义,可以实现该方法。
比如这里我定义一个属于自己的过滤规则器
当我们实现TypeFilter接口时,需要实现该接口中的match()方法,match()方法的返回值为boolean类型。当返回true时,表示符合规则,会包含在Spring容器中;当返回false时,表示不符合规则,那就是一个都不匹配,自然就都不会被包含在Spring容器中。另外,在match()方法中存在两个参数,分别为MetadataReader类型的参数和MetadataReaderFactory类型的参数,含义分别如下。
比如我下面实现一个自定义的 filter,规则是:只有类名包含 “Dao” 才注入到容器中
public class SelfTypeFilter implements TypeFilter {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
ClassMetadata classMetadata = metadataReader.getClassMetadata();
String className = classMetadata.getClassName();
if (className.contains("Dao")) {
return true;
}
return false;
}
}
然后定义配置类
@ComponentScan(value = "com.hone.learn", includeFilters =
{@ComponentScan.Filter(type = FilterType.CUSTOM, classes = {SelfTypeFilter.class}) },
useDefaultFilters = false)
@Configuration(value = "mainConfig")
public class MainConfig {
// @Bean注解是给IOC容器中注册一个bean,类型自然就是返回值的类型,id默认是用方法名作为id
@Primary
@Bean(value = "child")
public Person personChild() {
return new Person("张三", 20);
}
@Bean(value = "adult")
public Person personAdult() {
return new Person("蜡笔小新", 20);
}
}
最后还是利用测试类,输出所有的 组件
public class TestMain {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
printAllBean(applicationContext);
}
private static void printAllBean(ApplicationContext applicationContext) {
System.out.println("=====所有的Bean对象=====");
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String s : beanDefinitionNames) {
System.out.println(s);
}
}
}
通过输出结果可以发现,BookService 已经不包括在内部了
=====所有的Bean对象=====
// 忽略自带组件
mainConfig
bookDao
child
adult
我们可以使用 @ComponentScan 注解来指定 Spring 扫描哪些包,可以使用 excludeFilters() 方法来指定扫描时排除哪些组件;也可以使用 includeFilters() 方法来指定扫描时只包含哪些组件。
当使用includeFilters()方法指定只包含哪些组件时,需要禁用掉默认的过滤规则。