12 Java-based Container Configuration
本节的内容:如何使用注解配置Spring容器。包含下面的主题:
- 基本概念:@Bean 和 @Configuration
12.1 基本概念:@Bean 和 @Configuration
Spring新的java配置支持中的核心构件是@Configuration-annotated类和@Bean-annotated方法。
@Bean注释用于指示方法实例化、配置和初始化要由Spring IoC 容器管理的新对象。对于熟悉Spring的XML配置beans元素的人来说,@Bean注释的作用与bean元素相同。您可以对任何Spring @Component使用@bean注释的方法。但是,它们最常与@Configuration 注解的beans一起使用。
使用@Configuration注释类表明其主要目的是作为bean定义的源。
此外,@Configuration类允许通过调用同一类中的其他@Bean方法来定义bean之间的依赖关系。最简单的@Configuration类如下:
@Configuration
public class AppConfig {
@Bean
public MyService myService() {
return new MyServiceImpl();
}
}
前面的AppConfig类等价于下面的Spring beans的 XML:
<beans>
<bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>
对比:Full @Configuration vs “lite” @Bean mode?
当@Bean方法在没有使用@Configuration注释的类中声明时,它们以“lite”模式处理。在@Component或POJO类中声明的Bean方法被认为是“精简的”,包含类的主要用途不同,而@Bean方法在这里是一种附加功能。例如,服务组件可以通过每个适用组件类上的附加@Bean方法向容器暴露管理视图。在这些场景中,@Bean方法是一种通用的工厂方法机制。
与完整的@Configuration不同,lite @Bean方法不能声明内部bean之间的依赖关系。相反,它们对包含它们的组件的内部状态以及可能声明的参数(可选)进行操作。因此,这样的@Bean方法不应该调用其他@Bean方法。每个这样的方法实际上只是一个特定bean引用的工厂方法,没有任何特殊的运行时语义。这样做的好处是不需要在运行时应用CGLIB子类,所以在类设计方面没有限制(也就是说,包含的类可能是final等等)。在常见的使用场景中,@Bean方法是在@Configuration类中声明的,以确保总是使用“full”模式,从而将交叉方法引用重定向到容器的生命周期管理。这可以防止通过常规Java调用意外调用相同的@Bean方法,这有助于减少在“lite”模式下操作时难以跟踪的细微bug。
下面几节将深入讨论@Bean和@Configuration注解。但是,我们首先介绍了通过基于java的配置创建spring容器的各种方法。
12.2 使用AnnotationConfigApplicationContext实例化Spring容器
下面几节将介绍Spring 3.0中引入AnnotationConfigApplicationContext。这个通用的ApplicationContext实现不仅可以接受@Configuration类作为输入,还可以接受普通的@Component类和使用JSR-330元数据注解的类。
当@Configuration类作为输入提供时,@Configuration类本身注册为bean定义,类中所有声明的@Bean方法也注册为bean定义
当提供@Component和JSR-330类时,它们被注册为bean定义,并假设依赖注入元数据(如@Autowired或@Inject)在这些类中使用。
简单构造
与在实例化ClassPathXmlApplicationContext时将Spring XML文件用作输入非常相似,您可以在实例化注释configapplicationcontext时将@Configuration类用作输入。这使得Spring容器可以完全不使用xml,如下面的例子所示:
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
MyService myService = ctx.getBean(MyService.class);
myService.doStuff();
}
如前所述,AnnotationConfigApplicationContext不仅限于使用@Configuration类。任何@Component或JSR-330注释类都可以作为输入提供给构造函数,如下例所示:
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(MyServiceImpl.class, Dependency1.class, Dependency2.class);
MyService myService = ctx.getBean(MyService.class);
myService.doStuff();
}
通过使用register(Class<?>…)程序化构建容器
您可以使用一个无参数的构造函数来实例化一个带注释的configapplicationcontext,然后使用register()方法来配置它。当以编程方式构建AnnotationConfigApplicationContext时,这种方法特别有用。下面的例子演示了如何做到这一点:
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(AppConfig.class, OtherConfig.class);
ctx.register(AdditionalConfig.class);
ctx.refresh();
MyService myService = ctx.getBean(MyService.class);
myService.doStuff();
}
使用sacn(String…)启用组件扫描
要启用组件扫描,可以按如下方式注解@Configuration类
@Configuration
@ComponentScan(basePackages = "com.acme") //启用组件扫描
public class AppConfig {
...
}
有经验的Spring用户可能熟悉与Spring上下文等价的XML声明:namespace,如下例所示:
<beans>
<context:component-scan base-package="com.acme"/>
</beans>
前面的例子中,com.acme 包被扫描以查找所有基于@Componont注解的类,这些类在容器中注册为Spring bean定义。AnnotationConfigApplicationContext提供scan(String…)方法,允许使用相同的组件扫描功能,如下面的示例所示:
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.scan("com.acme");
ctx.refresh();
MyService myService = ctx.getBean(MyService.class);
}
请记住@Configuration类是用@Component进行元注释的,因此它们是组件扫描的候选对象。在前面的例子中,假设AppConfig是在com.acme中声明的。acme包(或下面的任何包),它在调用scan()时被获取。在refresh()中,它的所有@Bean方法都将作为bean定义在容器中进行处理和注册。
支持AnnotationConfigWebapplicationContext的Web应用
AnnotationConfigApplicationContext的WebApplicationContext变体AnnotationConfigWebApplicationContext,您可以在配置Spring ContextLoaderListener servlet监听器、Spring MVC DispatcherServlet等时使用这个实现。下面的web.xml片段配置了一个典型的Spring MVC web应用程序(注意contex-param和init-param的使用):
<web-app>
<!-- Configure ContextLoaderListener to use AnnotationConfigWebApplicationContext
instead of the default XmlWebApplicationContext -->
<context-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</context-param>
<!-- Configuration locations must consist of one or more comma- or space-delimited
fully-qualified @Configuration classes. Fully-qualified packages may also be
specified for component-scanning -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.acme.AppConfig</param-value>
</context-param>
<!-- Bootstrap the root application context as usual using ContextLoaderListener -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Declare a Spring MVC DispatcherServlet as usual -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- Configure DispatcherServlet to use AnnotationConfigWebApplicationContext
instead of the default XmlWebApplicationContext -->
<init-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</init-param>
<!-- Again, config locations must consist of one or more comma- or space-delimited
and fully-qualified @Configuration classes -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.acme.web.MvcConfig</param-value>
</init-param>
</servlet>
<!-- map all requests for /app/* to the dispatcher servlet -->
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/app/*</url-pattern>
</servlet-mapping>
</web-app>