(3)Spring study 3 ------- Bean装配 annotation (待续....)

来源:互联网 发布:三级域名需要备案吗 编辑:程序博客网 时间:2024/06/05 07:28
classpath扫描与组件管理
类的自动检测与注册bean
<context:annotation-config/>
@Component  @Reposity   @Service   @Controller
@Required
@Autowired
@Qualifier

@Resource

----------------------------------------------------------------------------------------------------------------------------------------------------------------

1、classpath扫描与组件管理
~ Spring3.0开始 , Spring JavaConfig项目提供了很多特性,包括使用java而不是使用XML定义bean
比如@Configuration   @Bean  @Import @DependsOn

~ @Component 是一个通用的注解,可以用于任何bean  
~ @Reposity @Service @Controller 是更有针对性的注解
   @Reposity    DAO类        (持久层)
   @Service     Service类    (服务层)
   @Controller  Controller类 (控制层) MVC

2、类的自动检测与注册bean
Spring 可以自动检测类并注册Bean到ApplicationContext中

~xml配置(xml方式)
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
  
 </beans>

~xml配置(annotation)
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd" >
       
 </beans>

~ <context:component-scan base-package="com.imooc.beanannotation" />
     
~ <context:annotation-config />仅会查找同一个ApplicationContext中的bean注解

~ 两者的区别
首先说<context:annotation-config> 标签吧,该标签的作用是向Spring 容器注册
AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor、
PersistenceAnnotationBeanPostProcessor 以及 RequiredAnnotationBeanPostProcessor 
这4个BeanPostProcessor。注册这4个BeanPostProcessor的作用,就是为了你的系统能够识别相应的注解。

具体说来:
如果你想使用@Autowired注解,那么就必须事先在 Spring 容器中声明 AutowiredAnnotationBeanPostProcessor Bean。传统声明方式如下:
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor "/> 
如果想使用@Resource 、@PostConstruct、@PreDestroy等注解就必须声明CommonAnnotationBeanPostProcessor
如果想使用@PersistenceContext注解,就必须声明PersistenceAnnotationBeanPostProcessor的Bean。
如果想使用@Required的注解,就必须声明RequiredAnnotationBeanPostProcessor的Bean。同样,传统的声明方式如下:
<bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor"/> 
一般来说,这些注解我们还是比较常用,尤其是Antowired的注解,在自动注入的时候更是经常使用,
所以如果总是需要按照传统的方式一条一条配置显得有些繁琐和没有必要,于是spring给我们提供
<context:annotation-config/>的简化配置方式,自动帮你完成声明。

不过,在如上实现了对bean的定义和注入方式之后,还需要指定那些bean会被注入,这时我们使用注解一般都会配置扫描包路径选项<context:component-scan base-package=”XX.XX”/> 该配置项其实也包含了自动注入上述processor的功能,
 因此当使用 <context:component-scan/> 后,就可以将<context:annotation-config/> 移除了。

~Spring 使用过滤器 自定义扫描
 默认情况下类被自动发现并被注册的条件是使用@Component @Reposity @Service @Controller 或者 @Component自定义注解
 <context:component-scan> 有一个use-default-filters属性,该属性默认为true,这就意味着会扫描指定包下的全部的标有@Component的类, 并注册成bean.也就是@Component的子注解 @Service, @Reposity等。
 并提供两个子标签:<context:include-filter>和<context:exclude-filter>各代表引入和排除的过滤。
如:
<context:component-scan base-package="com.xhlx.finance.budget" >
     <context:include-filter type="regex" expression=".service.*"/>
</context:component-scan>
filter标签在Spring3有五个type,如下:
Filter Type Examples ExpressionDescription
annotation org.example.SomeAnnotation符合SomeAnnoation的target class
assignable org.example.SomeClass指定class或interface的全名
aspectj org.example..*Service+AspectJ语法
regex org\.example\.Default.*Regelar Expression
custom org.example.MyTypeFilterSpring3新增自訂Type,实作org.springframework.core.type.TypeFilter


(转:)context:component-scan扫描使用上的容易忽略的use-default-filters

问题

如下方式可以成功扫描到@Controller注解的Bean,不会扫描@Service/@Repository的Bean。正确

  1.  <context:component-scan base-package="org.bdp.system.test.controller">   
  2.      <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>   
  3. </context:component-scan>  

但是如下方式,不仅仅扫描@Controller,还扫描@Service/@Repository的Bean,可能造成一些问题

  1.  <context:component-scan base-package="org.bdp">   
  2.      <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>   
  3. </context:component-scan>  

这个尤其在springmvc+spring+hibernate等集成时最容易出问题的地,最典型的错误就是:事务不起作用

这是什么问题呢?

分析

1、<context:component-scan>会交给org.springframework.context.config.ContextNamespaceHandler处理;

  1. registerBeanDefinitionParser("component-scan"new ComponentScanBeanDefinitionParser());  

2、ComponentScanBeanDefinitionParser会读取配置文件信息并组装成org.springframework.context.annotation.ClassPathBeanDefinitionScanner进行处理;

3、如果没有配置<context:component-scan>的use-default-filters属性,则默认为true,在创建ClassPathBeanDefinitionScanner时会根据use-default-filters是否为true来调用如下代码:

  1.   protected void registerDefaultFilters() {  
  2. this.includeFilters.add(new AnnotationTypeFilter(Component.class));  
  3. ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();  
  4. try {  
  5.     this.includeFilters.add(new AnnotationTypeFilter(  
  6.             ((Class<? extends Annotation>) cl.loadClass("javax.annotation.ManagedBean")), false));  
  7.     logger.info("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");  
  8. }  
  9. catch (ClassNotFoundException ex) {  
  10.     // JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.  
  11. }  
  12. try {  
  13.     this.includeFilters.add(new AnnotationTypeFilter(  
  14.             ((Class<? extends Annotation>) cl.loadClass("javax.inject.Named")), false));  
  15.     logger.info("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");  
  16. }  
  17. catch (ClassNotFoundException ex) {  
  18.     // JSR-330 API not available - simply skip.  
  19. }  

可以看到默认ClassPathBeanDefinitionScanner会自动注册对@Component、@ManagedBean、@Named注解的Bean进行扫描。

如果细心,到此我们就找到问题根源了。

4、在进行扫描时会通过include-filter/exclude-filter来判断你的Bean类是否是合法的:

  1. protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {  
  2.     for (TypeFilter tf : this.excludeFilters) {  
  3.         if (tf.match(metadataReader, this.metadataReaderFactory)) {  
  4.             return false;  
  5.         }  
  6.     }  
  7.     for (TypeFilter tf : this.includeFilters) {  
  8.         if (tf.match(metadataReader, this.metadataReaderFactory)) {  
  9.             AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();  
  10.             if (!metadata.isAnnotated(Profile.class.getName())) {  
  11.                 return true;  
  12.             }  
  13.             AnnotationAttributes profile = MetadataUtils.attributesFor(metadata, Profile.class);  
  14.             return this.environment.acceptsProfiles(profile.getStringArray("value"));  
  15.         }  
  16.     }  
  17.     return false;  
  18. }  

首先通过exclude-filter 进行黑名单过滤;

然后通过include-filter 进行白名单过滤;

否则默认排除。

结论

Java代码  收藏代码
  1. <context:component-scan base-package="org.bdp">   
  2.      <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>   
  3. </context:component-scan>  

为什么这段代码不仅仅扫描@Controller注解的Bean,而且还扫描了@Component的子注解@Service、@Reposity。因为use-default-filters默认为true。所以如果不需要默认的,则use-default-filters=“false”禁用掉。

如果在springmvc配置文件,不使用cn.javass.demo.web.controller前缀,而是使用cn.javass.demo,则service、dao层的bean可能也重新加载了,但事务的AOP代理没有配置在springmvc配置文件中,从而造成新加载的bean覆盖了老的bean,造成事务失效。只要使用use-default-filters=“false”禁用掉默认的行为就可以了。


~  定义bean name
  扫描过程中组件被自动检测。那么bean的名称是由BeanNameGenerator生成的
  (@Component @Reposity @Service @Controller都有name属性用于显示设置 bean name,通常类名首字母小写)
  自定义bean命名策略,实现BeanNameGenerator接口


~  作用域scope


~ 代理模式


















0 0
原创粉丝点击