描述:错误地spring容器配置,导致spring实例service二次。
项目现状
web.xml配置
web容器,首先加载spring-beans.xml配置,再加载spring-mvc.xml配置。
1 | <!-- The definition of the Root Spring Container shared by all Servlets and Filters --> |
root spring配置
期望通过注解实例化除Controller之外的对象。
1 | <!-- 使用annotation 自动注册bean, 并保证@Required、@Autowired的属性被注入 --> |
mvc spring配置
期望通过注解实例化只有Controller的对象。
1 | <!-- 使用annotation 自动注册bean, 并保证@Required、@Autowired的属性被注入 --> |
问题小析
通过如此配置,发现spring在加载spring-beans.xml时,实例化了一次service,再加载spring-mvc.xml时,实例化了一次controller和一次service,导致spring先后总共实例化二次service的情况发生。
解析方案1
spring-mvc.xml扫描时,base-package设置更细粒度的子包。如:
1 | <!-- 使用annotation 自动注册bean, 并保证@Required、@Autowired的属性被注入 --> |
此配置的含义:只扫描com.martinye.demo.controller包路径下子标签指明的类型,以及默认的@Component、@ManagedBean、@Named注解类型。
注:子标签与注解类型冲突,以子标签配置为准。
解析方案2
设置use-default-filters值为false,其值默认为true。如:
1 | <!-- 使用annotation 自动注册bean, 并保证@Required、@Autowired的属性被注入 --> |
此配置的含义:只扫描com.martinye.demo包路径下子标签指明的类型,不自动注册默认的@Component、@ManagedBean、@Named注解类型。
注:子标签与注解类型冲突,以子标签配置为准。
结语
查阅了相关文档,发现:
- spring
<context:component-scan>
标签处理逻辑会交给org.springframework.context.config.ContextNamespaceHandler处理,然后季托ComponentScanBeanDefinitionParser会读取配置文件信息并组装成org.springframework.context.annotation.ClassPathBeanDefinitionScanner进行处理。 - context:component-scan的子标签,作用有先后顺序,越上面的子标签优先越高。
- use-default-filters为true时,实例化ClassPathBeanDefinitionScanner时,会调用registerDefaultFilters,include @Component、@ManagedBean、@Named注解类;否则不include。之后再添加include-filter和exclude-filter里的逻辑。
参考文档
- context:component-scan扫描使用上的容易忽略的use-default-filters
- spring配置中context:component-scan/的use-default-filters的作用