基本概念: @Bean 和 @Configuration
在Spring新的Java配置支持中,其核心构件是
@Configuration
注解类和
@Bean
注解方法.
@Bean
注解用来表示方法实例化,配置以及初始化由Spring IoC容器管理的新对象.
对于那些熟悉Spring
<beans/>
XML配置的人来说,
@Bean
注解扮演了与
<bean/>
元素相同的角色.你可以在任何Spring
@Component
上使用
@Bean
注解方法,但通常情况下,它们经常与
@Configuration
beans一起使用.
@Configuration
注解类表示其主要目的作为bean定义的来源(source).
此外,
@Configuration
类允许在同一个类中调用其它的
@Bean
方法来定义它们之间的依赖关系.
可能最简单的
@Configuration
类可以像下面这样读取:
@Configuration
public class AppConfig {
@Bean
public MyService myService() {
return new MyServiceImpl();
}
}
public class AppConfig {
@Bean
public MyService myService() {
return new MyServiceImpl();
}
}
AppConfig
类等价于下面的Spring
<beans/>
XML:
<beans>
<bean id = "myService" class = "com.acme.services.MyServiceImpl" />
</beans>
<bean id = "myService" class = "com.acme.services.MyServiceImpl" />
</beans>
完整(full)@Configuration vs 精简(lite) @Beans 模式?
当
@Bean
方法声明在未用
@Configuration
注解的类中时,他们被称为“精简”模式处理.
例如,bean 方法声明在
@Component
中或普通类(plain old class)中将被认为是精简的('lite').
不像完整
@Configuration
, 精简
@Bean
方法不能容易地声明bean之间的依赖关系.通常情况下,当处于精简模式时,一个
@Bean
方法不能调用另一个
@Bean
方法.
只有在
@Configuration
类中使用
@Bean
方法才是一种可确保总是处于完整模式下的推荐方法.
这可以阻止同一个@Bean方法不小心被调用多次,有助于减少细微的bug(当运行在精简模式下时,这些bug很难追查)。
@Bean
和
@Configuration
注解会在下面的章节中详细讨论. 但首先,我们要讲解基于Java配置来创建Spring 容器的各种方法.
7.12.2 使用AnnotationConfigApplicationContext来实例化Spring 容器
下面的章节记录了Spring的
AnnotationConfigApplicationContext
, 它是Spring 3.0中新引入的.
这个多用途的
ApplicationContext
实现不仅有能力接受
@Configuration
类作为它的输入,也可以接受
@Component
类和使用JSR-330 元数据注解类作为它的输入.
当
@Configuration
类作为输入时,
@Configuration
类自身也将作为bean定义进行注册,并且这个类中所有声明了
@Bean
方法也会以bean的定义进行注册.
当提供
@Component
和JSR-330时,它们也是按bean定义进行注册, 并会假设在那些类的必要地方使用DI元数据,如
@Autowired
或
@Inject
are.
简单构造
与实例化
ClassPathXmlApplicationContext时需要Spring XML文件方式相同
, 当实例化
AnnotationConfigApplicationContext时,也需要@Configuration
类作为它的输入.
这允许在Spring容器中完全自由地使用XML:
public
static
void
main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig. class );
MyService myService = ctx.getBean(MyService. class );
myService.doStuff();
}
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();
}
ApplicationContext ctx = new AnnotationConfigApplicationContext(MyServiceImpl. class , Dependency1. class , Dependency2. class );
MyService myService = ctx.getBean(MyService. class );
myService.doStuff();
}
上面的代码会假设
MyServiceImpl
,
Dependency1
和
Dependency2
使用的是Spring 依赖注入,如
@Autowired
.
通过编程使用register(Class<?>…)来构建容器
AnnotationConfigApplicationContext
可以使用无参构造器来实例化,然后再调用
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();
}
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(AppConfig. class , OtherConfig. class );
ctx.register(AdditionalConfig. class );
ctx.refresh();
MyService myService = ctx.getBean(MyService. class );
myService.doStuff();
}
使用scan(String…)来启用组件扫描
要启用组件扫描,只需要像这样下面注解你的
@Configuration
类:
@Configuration
@ComponentScan(basePackages = "com.acme")
public class AppConfig { ... }
@ComponentScan(basePackages = "com.acme")
public class AppConfig { ... }
有经验的 Spring用户非常熟悉与它等价的来自Spring context:命名空间的XML声明:
<beans>
<context:component-scan base-package= "com.acme" />
</beans>
<context:component-scan base-package= "com.acme" />
</beans>
在上面的例子中,
com.acme
包将扫描, 会查找所有
@Component
注解的类, 那些类也会以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 );
}
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.scan( "com.acme" );
ctx.refresh();
MyService myService = ctx.getBean(MyService. class );
}
记住
@Configuration
类是带有
@Component的meta-annotated
, 因此它们是组件扫描的候选人!
在上面的例子中, 假设
AppConfig
声明在
com.acme
包(或它的子包)中,在调用scan()方法时,它也会挑选出来,并当
refresh()的时候,它所有的
@Bean
方法将被处理,并以bean的定义注册到容器中.
使用 AnnotationConfigWebApplicationContext来支持Web应用程序
AnnotationConfigApplicationContext
的
WebApplicationContext变异是AnnotationConfigWebApplicationContext
. 此实现可用于配置Spring
ContextLoaderListener
servlet 监听器, Spring MVC
DispatcherServlet
等等.
下面在web.xml中配置典型Spring MVC web application的片断.
注意
contextClass
和 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>
<!-- 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>