首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

详解 Spring 3.0 基于 Annotation 的依赖注入兑现(转)

2012-09-01 
详解 Spring 3.0 基于 Annotation 的依赖注入实现(转)使用 @Repository、@Service、@Controller 和 @Compone

详解 Spring 3.0 基于 Annotation 的依赖注入实现(转)

使用 @Repository、@Service、@Controller 和 @Component 将类标识为 Bean

Spring 自 2.0 版本开始,陆续引入了一些注解用于简化 Spring 的开发。@Repository 注解便属于最先引入的一批,它用于将数据访问层 (DAO 层 ) 的类标识为 Spring Bean。具体只需将该注解标注在 DAO 类上即可。同时,为了让 Spring 能够扫描类路径中的类并识别出 @Repository 注解,需要在 XML 配置文件中启用 Bean 的自动扫描功能,这可以通过 <context:component-scan/> 实现。如下所示:


与通过 XML 配置的 Spring Bean 一样,通过上述注解标识的 Bean,其默认作用域是"singleton",为了配合这四个注解,在标注 Bean 的同时能够指定 Bean 的作用域,Spring 2.5 引入了 @Scope 注解。使用该注解时只需提供作用域的名称就行了,如下所示:


回页首

使用 @PostConstruct 和 @PreDestroy 指定生命周期回调方法

Spring Bean 是受 Spring IoC 容器管理,由容器进行初始化和销毁的(prototype 类型由容器初始化之后便不受容器管理),通常我们不需要关注容器对 Bean 的初始化和销毁操作,由 Spring 经过构造函数或者工厂方法创建的 Bean 就是已经初始化完成并立即可用的。然而在某些情况下,可能需要我们手工做一些额外的初始化或者销毁操作,这通常是针对一些资源的获取和释放操作。Spring 1.x 为此提供了两种方式供用户指定执行生命周期回调的方法。

第一种方式是实现 Spring 提供的两个接口:InitializingBean 和 DisposableBean。如果希望在 Bean 初始化完成之后执行一些自定义操作,则可以让 Bean 实现 InitializingBean 接口,该接口包含一个 afterPropertiesSet() 方法,容器在为该 Bean 设置了属性之后,将自动调用该方法;如果 Bean 实现了 DisposableBean 接口,则容器在销毁该 Bean 之前,将调用该接口的 destroy() 方法。这种方式的缺点是,让 Bean 类实现 Spring 提供的接口,增加了代码与 Spring 框架的耦合度,因此不推荐使用。

第二种方式是在 XML 文件中使用 <bean> 的 init-method 和 destroy-method 属性指定初始化之后和销毁之前的回调方法,代码无需实现任何接口。这两个属性的取值是相应 Bean 类中的初始化和销毁方法,方法名任意,但是方法不能有参数。示例如下:

回页首

使用 @Required 进行 Bean 的依赖检查

依赖检查的作用是,判断给定 Bean 的相应 Setter 方法是否都在实例化的时候被调用了。而不是判断字段是否已经存在值了。Spring 进行依赖检查时,只会判断属性是否使用了 Setter 注入。如果某个属性没有使用 Setter 注入,即使是通过构造函数已经为该属性注入了值,Spring 仍然认为它没有执行注入,从而抛出异常。另外,Spring 只管是否通过 Setter 执行了注入,而对注入的值却没有任何要求,即使注入的 <null/>,Spring 也认为是执行了依赖注入。

<bean> 标签提供了 dependency-check 属性用于进行依赖检查。该属性的取值包括以下几种:

  • none -- 默认不执行依赖检查。可以在 <beans> 标签上使用 default-dependency-check 属性改变默认值。
  • simple -- 对原始基本类型和集合类型进行检查。
  • objects -- 对复杂类型进行检查(除了 simple 所检查类型之外的其他类型)。
  • all -- 对所有类型进行检查。

    旧版本使用 dependency-check 在配置文件中设置,缺点是粒度较粗。使用 Spring2.0 提供的 @Required 注解,提供了更细粒度的控制。@Required 注解只能标注在 Setter 方法之上。因为依赖注入的本质是检查 Setter 方法是否被调用了,而不是真的去检查属性是否赋值了以及赋了什么样的值。如果将该注解标注在非 setXxxx() 类型的方法则被忽略。

    为了让 Spring 能够处理该注解,需要激活相应的 Bean 后处理器。要激活该后处理器,只需在 XML 中增加如下一行即可。

    回页首

    使用 @Resource、@Autowired 和 @Qualifier 指定 Bean 的自动装配策略

    自动装配是指,Spring 在装配 Bean 的时候,根据指定的自动装配规则,将某个 Bean 所需要引用类型的 Bean 注入进来。<bean> 元素提供了一个指定自动装配类型的 autowire 属性,该属性有如下选项:

    • no -- 显式指定不使用自动装配。
    • byName -- 如果存在一个和当前属性名字一致的 Bean,则使用该 Bean 进行注入。如果名称匹配但是类型不匹配,则抛出异常。如果没有匹配的类型,则什么也不做。
    • byType -- 如果存在一个和当前属性类型一致的 Bean ( 相同类型或者子类型 ),则使用该 Bean 进行注入。byType 能够识别工厂方法,即能够识别 factory-method 的返回类型。如果存在多个类型一致的 Bean,则抛出异常。如果没有匹配的类型,则什么也不做。
    • constructor -- 与 byType 类似,只不过它是针对构造函数注入而言的。如果当前没有与构造函数的参数类型匹配的 Bean,则抛出异常。使用该种装配模式时,优先匹配参数最多的构造函数。
    • autodetect -- 根据 Bean 的自省机制决定采用 byType 还是 constructor 进行自动装配。如果 Bean 提供了默认的构造函数,则采用 byType;否则采用 constructor 进行自动装配。

      当使用 byType 或者 constructor 类型的自动装配的时候,自动装配也支持引用类型的数组或者使用了泛型的集合,这样,Spring 就会检查容器中所有类型匹配的 Bean,组成集合或者数组后执行注入。对于使用了泛型的 Map 类型,如果键是 String 类型,则 Spring 也会自动执行装配,将所有类型匹配的 Bean 作为值,Bean 的名字作为键。

      我们可以给 <beans> 增加 default-autowire 属性,设置默认的自动封装策略。默认值为"no"。如果使用自动装配的同时,也指定了 property 或者 constructor-arg 标签,则显式指定的值将覆盖自动装配的值。目前的自动封装不支持简单类型,比如基本类型、String、Class,以及它们的数组类型。

      在按类型匹配的时候 ( 可能是 byType、constructor、autodetect),同一个类型可能存在多个 Bean,如果被注入的属性是数组、集合或者 Map,这可能没有问题,但是如果只是简单的引用类型,则会抛出异常。解决方法有如下几种:

      • 取消该 Bean 的自动装配特性,使用显式的注入。我们可能不希望某个 Bean 被当作其他 Bean 执行自动封装时的候选对象,我们可以给该 <bean> 增加 autowire-candidate="false"。(autowire-candidate 属性和 autowire 属性相互独立,互不相干。某个 Bean 可以将 autowire-candidate 设置为 false,同时使用 autowire 特性。) 另外,我们可以设置 <beans> 的 default-autowire-candidates 属性,可以在该属性中指定可以用于自动装配候选 Bean 的匹配模式,比如 default-autowire-candidates="*serv,*dao",这表示所有名字以 serv 或者 dao 结尾的 Bean 被列为候选,其他则忽略,相当于其他 Bean 都指定为 autowire-candidate="false",此时可以显式为 <bean> 指定 autowire-candidate="true"。在 <bean> 上指定的设置要覆盖 <beans> 上指定的设置。
      • 如果在多个类型相同的 Bean 中有首选的 Bean,那么可以将该 <bean> 的 primary 属性设置为 "true" ,这样自动装配时便优先使用该 Bean 进行装配。此时不能将 autowire-candidate 设为 false。
      • 如果使用的是 Java 5 以上版本,可以使用注解进行更细粒度的控制。

        使用 @Autowired 和 @Qualifier 注解执行自动装配

        使用 @Autowired 注解进行装配,只能是根据类型进行匹配。@Autowired 注解可以用于 Setter 方法、构造函数、字段,甚至普通方法,前提是方法必须有至少一个参数。@Autowired 可以用于数组和使用泛型的集合类型。然后 Spring 会将容器中所有类型符合的 Bean 注入进来。@Autowired 标注作用于 Map 类型时,如果 Map 的 key 为 String 类型,则 Spring 会将容器中所有类型符合 Map 的 value 对应的类型的 Bean 增加进来,用 Bean 的 id 或 name 作为 Map 的 key。

        @Autowired 标注作用于普通方法时,会产生一个副作用,就是在容器初始化该 Bean 实例的时候就会调用该方法。当然,前提是执行了自动装配,对于不满足装配条件的情况,该方法也不会被执行。

        当标注了 @Autowired 后,自动注入不能满足,则会抛出异常。我们可以给 @Autowired 标注增加一个 required=false 属性,以改变这个行为。另外,每一个类中只能有一个构造函数的 @Autowired.required() 属性为 true。否则就出问题了。如果用 @Autowired 同时标注了多个构造函数,那么,Spring 将采用贪心算法匹配构造函数 ( 构造函数最长 )。

        @Autowired 还有一个作用就是,如果将其标注在 BeanFactory 类型、ApplicationContext 类型、ResourceLoader 类型、ApplicationEventPublisher 类型、MessageSource 类型上,那么 Spring 会自动注入这些实现类的实例,不需要额外的操作。

        当容器中存在多个 Bean 的类型与需要注入的相同时,注入将不能执行,我们可以给 @Autowired 增加一个候选值,做法是在 @Autowired 后面增加一个 @Qualifier 标注,提供一个 String 类型的值作为候选的 Bean 的名字。举例如下:

        我们还可以将 @Qualifier 标注在集合类型上,那么所有 qualifier 名字与指定值相同的 Bean 都将被注入进来。

        最后,配置文件中需要指定每一个自定义注解的属性值。我们可以使用 <meta> 标签来代替 <qualifier/> 标签,如果 <meta> 标签和 <qualifier/> 标签同时出现,那么优先使用 <qualifier> 标签。如果没有 <qualifier> 标签,那么会用 <meta> 提供的键值对来封装 <qualifier> 标签。示例如下:


        @Autowired 注解对应的后处理注册与前面相似,只需在配置文件中增加如下一行即可:

        使用 JSR-250 中的 @Resource 和 @Qualifier 注解

        如果希望根据 name 执行自动装配,那么应该使用 JSR-250 提供的 @Resource 注解,而不应该使用 @Autowired 与 @Qualifier 的组合。

        @Resource 使用 byName 的方式执行自动封装。@Resource 标注可以作用于带一个参数的 Setter 方法、字段,以及带一个参数的普通方法上。@Resource 注解有一个 name 属性,用于指定 Bean 在配置文件中对应的名字。如果没有指定 name 属性,那么默认值就是字段或者属性的名字。@Resource 和 @Qualifier 的配合虽然仍然成立,但是 @Qualifier 对于 @Resource 而言,几乎与 name 属性等效。

        如果 @Resource 没有指定 name 属性,那么使用 byName 匹配失败后,会退而使用 byType 继续匹配,如果再失败,则抛出异常。在没有为 @Resource 注解显式指定 name 属性的前提下,如果将其标注在 BeanFactory 类型、ApplicationContext 类型、ResourceLoader 类型、ApplicationEventPublisher 类型、MessageSource 类型上,那么 Spring 会自动注入这些实现类的实例,不需要额外的操作。此时 name 属性不需要指定 ( 或者指定为""),否则注入失败;如果使用了 @Qualifier,则该注解将被忽略。而对于用户自定义类型的注入,@Qualifier 和 name 等价,并且不被忽略。

        <bean> 的 primary 和 autowire-candidate 属性对 @Resource、@Autowired 仍然有效。

        回页首

        使用 @Configuration 和 @Bean 进行 Bean 的声明

        虽然 2.0 版本发布以来,Spring 陆续提供了十多个注解,但是提供的这些注解只是为了在某些情况下简化 XML 的配置,并非要取代 XML 配置方式。这一点可以从 Spring IoC 容器的初始化类可以看出:ApplicationContext 接口的最常用的实现类是 ClassPathXmlApplicationContext 和 FileSystemXmlApplicationContext,以及面向 Portlet 的 XmlPortletApplicationContext 和面向 web 的 XmlWebApplicationContext,它们都是面向 XML 的。Spring 3.0 新增了另外两个实现类:AnnotationConfigApplicationContext 和 AnnotationConfigWebApplicationContext。从名字便可以看出,它们是为注解而生,直接依赖于注解作为容器配置信息来源的 IoC 容器初始化类。由于 AnnotationConfigWebApplicationContext 是 AnnotationConfigApplicationContext 的 web 版本,其用法与后者相比几乎没有什么差别,因此本文将以 AnnotationConfigApplicationContext 为例进行讲解。

        AnnotationConfigApplicationContext 搭配上 @Configuration 和 @Bean 注解,自此,XML 配置方式不再是 Spring IoC 容器的唯一配置方式。两者在一定范围内存在着竞争的关系,但是它们在大多数情况下还是相互协作的关系,两者的结合使得 Spring IoC 容器的配置更简单,更强大。

        之前,我们将配置信息集中写在 XML 中,如今使用注解,配置信息的载体由 XML 文件转移到了 Java 类中。我们通常将用于存放配置信息的类的类名以 “Config” 结尾,比如 AppDaoConfig.java、AppServiceConfig.java 等等。我们需要在用于指定配置信息的类上加上 @Configuration 注解,以明确指出该类是 Bean 配置的信息源。并且 Spring 对标注 Configuration 的类有如下要求:

        • 配置类不能是 final 的;
        • 配置类不能是本地化的,亦即不能将配置类定义在其他类的方法内部;
        • 配置类必须有一个无参构造函数。

          AnnotationConfigApplicationContext 将配置类中标注了 @Bean 的方法的返回值识别为 Spring Bean,并注册到容器中,受 IoC 容器管理。@Bean 的作用等价于 XML 配置中的 <bean/> 标签。示例如下:

          回页首

          混合使用 XML 与注解进行 Bean 的配置

          设计 @Configuration 和 @Bean 的初衷,并不是为了完全取代 XML,而是为了在 XML 之外多一种可行的选择。由于 Spring 自发布以来,Spring 开发小组便不断简化 XML 配置,使得 XML 配置方式已经非常成熟,加上 Spring 2.0 以后出现了一系列命名空间的支持,使得 XML 配置方式成为了使用简单、功能强大的 Bean 定义方式。而且,XML 配置的一些高级功能目前还没有相关注解能够直接支持。因此,在目前的多数项目中,要么使用纯粹的 XML 配置方式进行 Bean 的配置,要么使用以注解为主,XML 为辅的配置方式进行 Bean 的配置。

          之所以会出现两者共存的情况,主要归结为三个原因:其一,目前绝大多数采用 Spring 进行开发的项目,几乎都是基于 XML 配置方式的,Spring 在引入注解的同时,必须保证注解能够与 XML 和谐共存,这是前提;其二,由于注解引入较晚,因此功能也没有发展多年的 XML 强大,因此,对于复杂的配置,注解还很难独当一面,在一段时间内仍然需要 XML 的配合才能解决问题。除此之外,Spring 的 Bean 的配置方式与 Spring 核心模块之间是解耦的,因此,改变配置方式对 Spring 的框架自身是透明的。Spring 可以通过使用 Bean 后处理器 (BeanPostProcessor) 非常方便的增加对于注解的支持。这在技术实现上非常容易的事情。

          要使用混合配置方式,首先需要判断以哪一种配置方式为主。对这个问题的不同回答将会直接影响到实现的方式。然而大可不必为此伤脑筋,因为不论是以 XML 为主,还是以注解为主,配置方式都是简单而且容易理解的。这里不存在错误的决定,因为仅仅是表现方式不一样。我们首先假设以 XML 配置为主的情况。

          对于已经存在的大型项目,可能初期是以 XML 进行 Bean 配置的,后续逐渐加入了注解的支持,这时我们只需在 XML 配置文件中将被 @Configuration 标注的类定义为普通的 <bean>,同时注册处理注解的 Bean 后处理器即可。示例如下:

          回页首

          结束语

          从 2.0 版本开始,Spring 的每一次更新都会提供更多新的注解供开发者使用。这满足了注解爱好者的胃口。但是正如前面所说,Spring 提供更多的注解并不是为了有朝一日取代 XML 配置方式,而是为了给开发者多一种选择。两种声明 Bean 的方式各有特色,XML 方式更加灵活,并且发展的相对成熟,这种配置方式为大多数 Spring 开发者熟悉;注解方式使用起来非常简洁,但是尚处于发展阶段。我们很难说两种配置方式孰优孰劣,但是如果能够灵活搭配两种方式,一定能够进一步提升开发效率。


          参考资料

          • Spring 3.0官方参考指南:提供了对 Spring 3.0 的全方位讲解,是学习 Spring 3.0 的最佳资料。

          • 使用Spring 2.5 注释驱动的 IoC 功能:详细讲述了 Spring 2.5 基于注释 IoC 功能的使用。

          • Spring官方论坛:在这里可以和来自世界各地的 Spring 爱好者交流 Spring 技术。

          • 访问 developerWorks?Open source 专区获得丰富的 how-to 信息、工具和项目更新以及?最受欢迎的文章和教程,帮助您用开放源码技术进行开发,并将它们与 IBM 产品结合使用。

          • 转:http://www.ibm.com/developerworks/cn/opensource/os-cn-spring-iocannt/index.html?ca=drs-tp4608

热点排行