在进行 SSM 整合时,常常使用<component-scan>
标签进行注解扫描,而该标签中有个常常被忽略的属性 use-default-filters。该属性是个特别重要的属性,本文将会对该属性进行介绍。
原理分析
在进行 SSM 整合时,一般都会将 Spring 和 SpringMVC 的配置分别写在两个配置文件中。Spring 配置文件一般配置的是非 Web 组件的 bean,比如 DataSource、Service;而 SpringMVC 配置文件一般配置的是 Web 组件的 bean,比如控制器、视图解析器。所以,Spring 和 SpringMVC 配置文件的注解扫描的包路径是不同的。
☕️ Spring 配置文件中的注解扫描
<!-- 配置 IoC 容器注解扫描的包路径 -->
<context:component-scan base-package="com.example">
<!-- 制定扫包规则,不扫描 @Controller 注解修饰的 Java 类,其它还是要扫描 -->
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
☕️ SpringMVC 配置文件中的注解扫描
<!-- 配置 IoC 容器的注解扫描的包路径 -->
<context:component-scan base-package="com.example" use-default-filters="false">
<!-- 制定扫包规则,只扫描使用 @Controller 注解修饰的 Java 类 -->
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
从上面可以发现,使用<exclude-filter>
标签时,没有将 use-default-filters 属性设置为 false;而在使用<include-filter>
标签时,将 use-default-filters 属性设置为 false。这是为什么?
这一切都要归结为 use-default-filters 属性的作用,该属性的默认值为 true,意为使用默认的 Filter 进行注解扫描,而该 Filter 会扫描所有 @Component 注解及其子注解。我们查看源码:
protected void registerDefaultFilters() {
// 将 @Component 注解添加进 Filter 扫描中
this.includeFilters.add(new AnnotationTypeFilter(Component.class));
ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
try {
this.includeFilters.add(new AnnotationTypeFilter(ClassUtils.forName("javax.annotation.ManagedBean", cl), false));
this.logger.trace("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
} catch (ClassNotFoundException var4) {
}
try {
this.includeFilters.add(new AnnotationTypeFilter(ClassUtils.forName("javax.inject.Named", cl), false));
this.logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
} catch (ClassNotFoundException var3) {
}
}
因此默认的 filter 会扫描所有 @Component 注解修饰的 Java 类,而 @Controller、@Service、@Repository 甚至是 @Configuration 注解都是 @Componet 的衍生注解,所以也会被扫描到。因此,最简单的注解扫描配置就是只配置包路径,而 use-default-filters 属性不需要配置,其值默认为 true:
<!-- 配置 IoC 容器的注解扫描的包路径 -->
<context:component-scan base-package="com.example"/>
⭐️ 现在回过头查看 Spring 配置文件中的配置:
<!-- 配置 IoC 容器的注解扫描的包路径 -->
<context:component-scan base-package="com.example">
<!-- 制定扫包规则,不扫描 @Controller 注解修饰的 Java 类,其它还是要扫描 -->
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
use-default-filters 默认值为 true,默认会扫描 @Component、@Controller、@Service、@Repository 甚至是 @Configuration 注解修饰的 Java 类;而<exclude-filter>
标签指定将 @Controller 注解排除,所以最后只会扫描 @Component、@Service、@Repository 和 @Configuration 注解修饰的 Java 类。
⭐️ 查看 SpringMVC 配置文件中的配置
<!-- 配置 IoC 容器的注解扫描的包路径 -->
<context:component-scan base-package="com.example" use-default-filters="false">
<!-- 制定扫包规则,只扫描使用 @Controller 注解修饰的 Java 类 -->
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
use-default-filters 属性值设置为 false,默认的 filters 关闭,意味着不会进行任何扫描;而<include-filter>
标签指定扫描 @Controller 注解,所以最终只会扫描 @Controller 注解修饰的 Java 类。
注解方式使用
✏️ Spring 配置类中的注解扫描配置
@Configuration
@ComponentScan(value = "com.example", // 配置 IoC 容器注解扫描的包路径
// 制定扫包规则,不扫描 @Controller 注解修饰的 Java 类,其它还是要扫描
excludeFilters = @Filter(type = FilterType.ANNOTATION,
value = Controller.class))
public class SpringConfig {
//...
}
✏️ SpringMVC 配置类中的注解扫描配置
@Configuration
@ComponentScan(value = "com.example", // 配置 IoC 容器注解扫描的包路径
useDefaultFilters = false, // 关闭默认的注解扫描的 Filter
// 制定扫包规则,只扫描使用 @Controller 注解修饰的 Java 类
includeFilters = @Filter(type = FilterType.ANNOTATION,
value = Controller.class)
)
public class SpringMvcConfig {
//...
}