Spring的context命名空间提供两个非常重要的注解 <context:annotation-config>和<context:component-scan>
context:annotation-config
<context:annotation-config>的作用是 显示地向Spring容器中注册以下4个BeanPostProcessor,激活已经通过xml注册过的Bean,之后应用就可以通过注解直接使用Bean。
- AutowiredAnnotationBeanPostProcessor
- CommonAnnotationBeanPostProcessor
- PersistenceAnnotationBeanPostProcessor
- RequiredAnnotationBeanPostProcessor
这4个
XxxxxxBeanPostProcessor的作用就是为了让spring识别相应的注解。
如果想使用@Autowired,那么就必须事先在 Spring 容器中声明AutowiredAnnotationBeanPostProcessorBean。声明方式如下
|
|
- 如果想使用 @Resource 、@PostConstruct、@PreDestroy 等注解就必须声明
CommonAnnotationBeanPostProcessor。- 如果想使用 @PersistenceContext注解,就必须声明PersistenceAnnotationBeanPostProcessor的Bean。
- 如果想使用 @Required的注解,就必须声明RequiredAnnotationBeanPostProcessor的Bean。声明方式如下
1 <bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor "/>
如果想使用以上注解,需要直接在Spring配置文件中定义这些Bean,显然这种方式比较笨拙,也不够优雅。Spring为我们提供了一种极为方便注册这些BeanPostProcessor的方式,如下,它会隐式地向 Spring容器注册上述BeanPostProcessor。
<context:annotation-config/>
context:component-scan
<context:component-scan> 除了具有<context:annotation-config/>的作用外,还具有以下作用:
- 通过base-package属性执行一个需要扫描的基类包,Spring容器将会扫描这个基类包里的所有类并从类的注解(@Component @Controller@Service等)信息中获取Bean的定义信息。
- 如果仅希望扫描特定的类而非基包下的所有类,那么可以使用resource-pattern属性过滤出特定的类,如下所示:
1 <context:component-scan base-package="com.meituan" resource-pattern="anno/*.class" />
这里resource-pattern设置为 “anno/*.class” , 意味着Spring仅会扫描基包里 anno 子包中的类。
- Spring还提供
<context:component-scan />的过滤子元素实现来实现resource-pattern属性不能满足的一些特殊需求。比如:仅过滤基类包中实现了XxxService接口的类 或 标注类某个特定注解的类等。如下:
12345 <context:component-scan base-package ="com.meituan" ><context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" /><context:include-filter type="aspectj" expression="com.meituan..*Controller+" /><context:exlude-filter type="regex" expression="com\.meituan\.anno.*"/></context:component-scan>
- 其中
<context:include-filter>表示 要包含的目标类,而<context:exclude-filter>表示要排除在外的目标类。- 一个
<context:component-scan>可以包含若干个<context:include-filter>和<context:exclude-filter>元素。- 这两个过滤元素均支持多种类型的过滤表达式,表达式类型如下:
| Filter Type | Example Expression | Description |
|---|---|---|
| annotation (default) | org.example.SomeAnnotation | An annotation to be present at the type level in target components.所有标注了 XxxAnnotation的类。该类型针对 目标类是否标注了某个注解来进行过滤。 |
| assignable | org.example.SomeClass | A class (or interface) that the target components are assignable to (extend/implement).所有继承或实现 XxxClass 的类。该类型针对 目标类是否继承或实现某个特定类来进行过滤。 |
| aspectj | org.example..*Service+ | An AspectJ type expression to be matched by the target components.所有类名以 Service结束的类以继承或扩展它们的类。该类型针对AspectJ表达式进行过滤。 |
| regex | org\.example\.Default.* | A regex expression to be matched by the target components class names.所有org.example.Default 类包下的类。该类型 使用正则表达式对目标类的类名进行过滤。 |
| custom | org.example.MyTypeFilter | A custom implementation of the org.springframework.core.type .TypeFilter interface.采用XxxTypeFilter采用代码的方式进行过滤。该类必须实现 org.springframework.core.type.TypeFilter 接口 |
在所有这些过滤类型中,除custom类型外,aspectj 的过滤表达式能力是最强的,它可以轻易的实现其他类型所能表达的过滤规则。
下面的例子忽略所有org.example 包下的 @Repository注解的类,注入org.example.*Stub结尾的包下的所有 Repository结尾的类。
注解方式:
|
|
use-default-filters 属性
<context:component-scan>有一个use-default-filters属性,该属性默认为true,这就意味着会扫描 指定包及其子包 的全部的标有@Component的类及其子注解子注解@Service、@Reposity、@Controller等,并注入bean。
如下配置,说明 :use-default-filter默认为true,此时Spring会对base-package包或者子包下的所有的进行java类进行扫描,并注入bean。
|
|
但是这种扫描方式粒度过大,如果只想扫描 指定包下面的
Controller,该怎么办?此时就可以用<context:component-scan>的 子标签<context:incluce-filter>
如下配置,说明:此时Spring就只会扫描base-package中有@Controller注解 的java类,不会扫描@Service、@Repository注解的java类。
|
|
但是如果使用如下方式,
use-dafault-filter默认为ture,不仅会扫描base-package(注意值发生变化) 中@Controller注解 的java类,还会扫描@Service、@Repository注解的java类,这样可能造成一些问题
|
|
此时
<context:include-filter/>并没有起到作用,只要把use-default-filter设置成false就可以了。这样就可以避免 在base-packeage配置多个包名这种不是很优雅的方法来解决这个问题了。
原因分析:
- 先看 context:component-scan 标签的解析过程
|
|
ComponentScanBeanDefinitionParser会读取配置文件信息并组装成org.springframework.context.annotation.ClassPathBeanDefinitionScanner进行处理;
- 如果没有配置
<context:component-scan>的use-default-filters属性,则默认为true,在创建ClassPathBeanDefinitionScanner时会根据use-default-filters是否为true来调用如下代码:
|
|
可以看到默认ClassPathBeanDefinitionScanner会自动注册对@Component、@ManagedBean、@Named注解的Bean进行扫描。
- 在进行扫描时会通过include-filter 和 exclude-filter来判断你的Bean类是否是合法的:
12345678910111213141516171819 /*** Determine whether the given class does not match any exclude filter* and does match at least one include filter.* @param metadataReader the ASM ClassReader for the class* @return whether the class qualifies as a candidate component*/protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {for (TypeFilter tf : this.excludeFilters) {if (tf.match(metadataReader, this.metadataReaderFactory)) {return false;}}for (TypeFilter tf : this.includeFilters) {if (tf.match(metadataReader, this.metadataReaderFactory)) {return isConditionMatch(metadataReader);}}return false;}可以看到 首先 对 exclude-filter 进行黑名单过滤;然后对 include-filter 进行白名单过滤;否则默认排除。
此时问题的根源就找到了: use-default-filter设置成false 即可解决问题。
- 如果同时配置,必须先配置
<context:include-filter/>,然后载配置<context:exclude-filter/>,否则报错。
原因:在spring-context-3.0.xsd 中 component-scan 元素定义如下, 其中 xsd:sequence 定义的是 元素的次序,so……
