前面两节学习了IoC容器的XML配置和annotation的配置方式,本节学习 一下IoC container 基于java-code的配置。
@Bean and @Configuration
概述
spring基于javaCode的配置方式的核心是类级别的注解@Configuration
和 方法级别的注解@Bean
@Bean
注解用来表明此方法实例化,配置,初始化了一个新对象,并且交给container管理。有点类似于XML中的<bean/>
配置,一般用于被@Configuration
注解的类中,也可以用于被@Component
注解的类中。
@Configuration
表明这个类是Bean
定义的“源”
@Configuration
public class AppConfig {
@Bean
public MyService myService() {
return new MyServiceImpl();
}
}
换成XML是:
<beans>
<bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>
AnnotationConfigApplicationContext
使用javaCode配置的方式没有了XML的配置文件,那么该如何初始化容器呢?
在spring3.0以后引入了一个新类AnnotationConfigApplicationContext
它能接受被@Configuration
标注的类作为注入源,也能接受 @Component
标注的类作为注入源。
当@Configuration
标注的类作为注入源时,此类本身也会作为一个bean定义被注册,同时,所有的被@Bean
标注的方法也会作为bean定义被注入
当@Component
标注的类作为注入源时, JSR-330 的注解都会被作为bean被注入,为解决依赖的@Autowired
和@Inject
也会作为bean被注入
与XML配置时容器初始化的方式类似,javaCode初始化的方式如下:
//通过构造
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手动注册
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();
}
//使用scan
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.scan("com.acme");
ctx.refresh();
MyService myService = ctx.getBean(MyService.class);
}
对于Web工程,使用的AnnotationConfigWebApplicationContext
在此不多做说明
@Bean 使用
声明一个Bean
@Configuration
public class AppConfig {
@Bean
public TransferService transferService() {
return new TransferServiceImpl();
}
}
//xml写法
<beans>
<bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>
使用上述方法会在ApplicationContext
注册一个bean的定义,bean默认的名字即为方法名。
Bean的依赖
如果Bean中有以来,可以通过在方法中加入参数进行注入
@Configuration
public class AppConfig {
@Bean
public TransferService transferService(AccountRepository accountRepository) {
return new TransferServiceImpl(accountRepository);
}
}
bean初始化和销毁前动作
使用@Bean
一样可以执行Bean的生命周期方法
public class Foo {
public void init() {
// initialization logic
}
}
public class Bar {
public void cleanup() {
// destruction logic
}
}
@Configuration
public class AppConfig {
@Bean(initMethod = "init")
public Foo foo() {
return new Foo();
}
@Bean(destroyMethod = "cleanup")
public Bar bar() {
return new Bar();
}
}
使用 @Scope 确定Bean作用域
@Configuration
public class MyConfiguration {
@Bean
@Scope("prototype")
public Encryptor encryptor() {
// ...
}
}
自定义Bean的使用name
@Configuration
public class AppConfig {
@Bean(name = "myFoo")
public Foo foo() {
return new Foo();
}
}
@Configuration 的使用
注入Bean依赖
@Configuration
public class AppConfig {
@Bean
public Foo foo() {
//注入Bar对象
return new Foo(bar());
}
@Bean
public Bar bar() {
return new Bar();
}
}
@Import
下面的例子中,容器初始化过程中只需要加入ConfigB.class即可
@Configuration
public class ConfigA {
@Bean
public A a() {
return new A();
}
}
@Configuration
@Import(ConfigA.class)
//@Import({ServiceConfig.class, RepositoryConfig.class})
public class ConfigB {
@Bean
public B b() {
return new B();
}
}
上一节介绍的@Autowired
也可以用在@Configuration
注解的类中实现自动装配的效果(当然对应的类需要在容器中注册)。
Java和XML 配置结合
有一些java的类在加载过程中需要给一系列的初始化值,比如DataSource
看下面一个例子:
@Configuration
public class AppConfig {
@Autowired
private DataSource dataSource;
@Bean
public AccountRepository accountRepository() {
return new JdbcAccountRepository(dataSource);
}
@Bean
public TransferService transferService() {
return new TransferService(accountRepository());
}
}
system-test-config.xml:
<beans>
<!-- enable processing of annotations such as @Autowired and @Configuration -->
<!--也可以使用 <context:component-scan base-package="com.acme"/>-->
<context:annotation-config/>
<!--在xml中引入properties文件-->
<context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
<bean class="com.acme.AppConfig"/>
<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<!--取得jdbc.properties文件中的值-->
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
</beans>
jdbc.properties文件:
jdbc.url=jdbc:hsqldb:hsql://localhost/xdb
jdbc.username=sa
jdbc.password=
使用:
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/com/acme/system-test-config.xml");
TransferService transferService = ctx.getBean(TransferService.class);
// ...
}
上述例子中AppConfig
类是在xml中注册的,也可以不在此定义而使用@ImportResource
结合 @Value
@Configuration
@ImportResource("classpath:/com/acme/properties-config.xml")
public class AppConfig {
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean
public DataSource dataSource() {
return new DriverManagerDataSource(url, username, password);
}
}
XML只需要引入properties
properties-config.xml
<beans>
<context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
</beans>
jdbc.properties文件:
jdbc.url=jdbc:hsqldb:hsql://localhost/xdb
jdbc.username=sa
jdbc.password=
使用:
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
TransferService transferService = ctx.getBean(TransferService.class);
// ...
}
获取配置信息
环境Environment
是容器的抽象集合,应用中的环境包括2个方面:配置和属性
配置:注册在容器中的bean的逻辑分组,只有初始化后才会生效,不管是通过XML的方式还是注解的方式,Bean都会成为”配置”,关联配置项的环境对象决定哪个配置文件生效,哪个配置文件默认生效
属性:属性文件在很多应用中扮演了重要角色,关联属性项的环境对象给用户提供了很方便的访问他们的接口
@Profile注解-配置
通过设定Enviroment
的 ActiveProfiles
可以决定当前context需要使用的配置环境,在开发中使用@Profile注解类或方法达到在不同情况下选择实例化不同的Bean
通过设定jvm的spring.profiles.active
参数来设置环境
Web项目设置servlet的context.paramte
@Profile 可以用在类上,方法上,同时他是一个元注解-可以永远新建别的注解
//用在类上:
@Configuration
@Profile("dev")
public class StandaloneDataConfig {
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.addScript("classpath:com/bank/config/sql/test-data.sql")
.build();
}
}
//用在方法上
@Configuration
public class AppConfig {
@Bean
@Profile("dev")
public DataSource devDataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.addScript("classpath:com/bank/config/sql/test-data.sql")
.build();
}
@Bean
@Profile("production")
public DataSource productionDataSource() throws Exception {
Context ctx = new InitialContext();
return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
}
}
激活某一套环境:
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("dev");
ctx.register(SomeConfig.class, StandaloneDataConfig.class, JndiDataConfig.class);
ctx.refresh();
设置profile的默认使用环境:
在@Profile()
设置"default"
即可
@Configuration
@Profile("default")
public class DefaultDataConfig {
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.build();
}
}
如果没有手动设置active的profile,那么上述的dataSource将会生效
可以使用Environmentset对象的DefaultProfiles()设置
@PropertySource
使用@PropertySource
注解可以很方便的向spring的Environment
添加属性信息
@Configuration
@PropertySource("classpath:/com/myco/app.properties")
public class AppConfig {
@Autowired
Environment env;
@Bean
public TestBean testBean() {
TestBean testBean = new TestBean();
testBean.setName(env.getProperty("testbean.name"));
return testBean;
}
}