Spring @Configuration 和@Bean的使用

http://www.tuicool.com/articles/M3MVr2

虽然 2.0 版本发布以来,Spring 陆续提供了十多个注解,但是提供的这些注解只是为了在某些情况下简化 XML 的配置,并非要取代 XML 配置方式。

这一点可以从 Spring IoC 容器的初始化类可以看出:

ApplicationContext 接口的最常用的实现类是它们都是面向 XML 的 

- ClassPathXmlApplicationContext

- FileSystemXmlApplicationContext,

- 面向 Portlet 的 XmlPortletApplicationContext

- 面向 web 的 XmlWebApplicationContext。

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 配置中的 标签。示例如下:  

@Configuration  

public class BookStoreDaoConfig{  

@Bean  

public UserDao userDao(){ return new UserDaoImpl();}  

@Bean  

public BookDao bookDao(){return new BookDaoImpl();}  

}  

Spring 在解析到以上文件时,将识别出标注 @Bean 的所有方法,执行之,并将方法的返回值 ( 这里是 UserDaoImpl 和 BookDaoImpl 对象 ) 注册到 IoC 容器中。默认情况下,Bean 的名字即为方法名。

因此,与以上配置等价的 XML 配置如下: 

@Bean 具有以下四个属性:  

name -- 指定一个或者多个 Bean 的名字。这等价于 XML 配置中 的 name 属性。

initMethod -- 容器在初始化完 Bean 之后,会调用该属性指定的方法。这等价于 XML 配置中 的 init-method 属性。

destroyMethod -- 该属性与 initMethod 功能相似,在容器销毁 Bean 之前,会调用该属性指定的方法。这等价于 XML 配置中 的 destroy-method 属性。

autowire -- 指定 Bean 属性的自动装配策略,取值是 Autowire 类型的三个静态属性。      

                    > Autowire.BY_NAME,

                    > Autowire.BY_TYPE,

                    > Autowire.NO。

与 XML 配置中的 autowire 属性的取值相比,这里少了 constructor,这是因为 constructor 在这里已经没有意义了。

@Bean 没有直接提供指定作用域的属性,可以通过 @Scope 来实现该功能,关于 @Scope 的用法已在上文列举。  

下面讲解基于注解的容器初始化。

AnnotationConfigApplicationContext 提供了三个构造函数用于初始化容器。  

- AnnotationConfigApplicationContext():该构造函数初始化一个空容器,容器不包含任何 Bean 信息,需要在稍后通过调用其 register() 方法注册配置类,并调用 refresh() 方法刷新容器。

- AnnotationConfigApplicationContext(Class...annotatedClasses):这是最常用的构造函数,通过将涉及到的配置类传递给该构造函数, 以实现将相应配置类中的 Bean 自动注册到容器中。

- AnnotationConfigApplicationContext(String... basePackages):该构造函数会自动扫描以给定的包及其子包下的所有类,并自动识别所有的 Spring Bean,将其注册到容器中。它不但识别标注 @Configuration 的配置类并正确解析,而且同样能识别使用 @Repository、@Service、@Controller、@Component 标注的类。

除了使用上面第三种类型的构造函数让容器自动扫描 Bean 的配置信息以外,AnnotationConfigApplicationContext 还提供了 scan() 方法,其功能与上面也类似,该方法主要用在容器初始化之后动态增加 Bean 至容器中。调用了该方法以后,通常需要立即手动调用 refresh() 刷新容器,以让变更立即生效。 

需要注意的是,AnnotationConfigApplicationContext 在解析配置类时,会将配置类自身注册为一个 Bean,因为 @Configuration 注解本身定义时被 @Component 标注了。

因此可以说,一个 @Configuration 同时也是一个 @Component。大多数情况下,开发者用不到该 Bean,并且在理想情况下,该 Bean 应该是对开发者透明的。@Configuration 的定义如下所示:  

@Target({ElementType.TYPE})  

@Retention(RetentionPolicy.RUNTIME)  

@Documented  

@Component  

public @interface Configuration {  

String value() default "";  

}  

在一般的项目中,为了结构清晰,通常会根据软件的模块或者结构定义多个 XML 配置文件,然后再定义一个入口的配置文件,该文件使用 将其他的配置文件组织起来。最后只需将该文件传给 ClassPathXmlApplicationContext 的构造函数即可。

针对基于注解的配置,Spring 也提供了类似的功能,只需定义一个入口配置类,并在该类上使用 @Import 注解引入其他的配置类即可,最后只需要将该入口类传递给 AnnotationConfigApplicationContext。具体示例如下:  

@Configuration  

@Import({BookStoreServiceConfig.class,BookStoreDaoConfig.class})  

public class BookStoreConfig{ … }  

混合使用 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 后处理器即可。示例如下:  

// 假设存在如下的 @Configuration 类:  

package bookstore.config;  

import bookstore.dao.*;  

@Configuration  

public class MyConfig{  

@Bean  

public UserDao userDao(){  

return new UserDaoImpl();  

}  

}  

此时,只需在 XML 中作如下声明即可:  

……  

由于启用了针对注解的 Bean 后处理器,因此在 ApplicationContext 解析到 MyConfig 类时,会发现该类标注了 @Configuration 注解,随后便会处理该类中标注 @Bean 的方法,将这些方法的返回值注册为容器总的 Bean。  

对于以上的方式,如果存在多个标注了 @Configuration 的类,则需要在 XML 文件中逐一列出。另一种方式是使用前面提到的自动扫描功能,配置如下:  

如此,Spring 将扫描所有 demo.config 包及其子包中的类,识别所有标记了 @Component、@Controller、@Service、@Repository 注解的类,由于 @Configuration 注解本身也用 @Component 标注了,Spring 将能够识别出 @Configuration 标注类并正确解析之。  

对于以注解为中心的配置方式,只需使用 @ImportResource 注解引入存在的 XML 即可,如下所示:  

@Configuration  

@ImportResource(“classpath:/bookstore/config/spring-beans.xml”)  

public class MyConfig{  

……  

}  

// 容器的初始化过程和纯粹的以配置为中心的方式一致:  

AnnotationConfigApplicationContext ctx =  new AnnotationConfigApplicationContext(MyConfig.class);  

……  

猜你喜欢

转载自nethub2.iteye.com/blog/2331697