XML配置文件中可以通过id特性给Bean分配名称,相同XML中不能重复名称。
Bean定义时可用name特性可以为一个Bean分配多个名称(不同名称间用空格、逗号、分号分离),除了第一个外都被称为别名。
name="accountDao, accountDaoInMemory"
class="com.wiley.beginningspring.ch2.AccountDaoInMemoryImpl"/>
除了在Bean定义中,也可在其他地方用
name="accountDaoInMemory"
class="com.wiley.beginningspring.ch2.AccountDaoInMemoryImpl"/>
<alias name="accountDaoInMemory" alias="accountDao"/>
使用@Bean的name特性赋予Bean多个名称,没有设置该特性值时Bean名称即为工厂方法名称。
@Configuration
public class Ch2BeanConfiguration{
@Bean(name={"accountDao,accountDaoInMemory"})
public AccountDao foo(){
AccountDaoInMemoryImpl bean = new AccountDaoInMemoryImpl();
return bean
}
//Implementation
}
如上Bean被赋予了accountDao,accountDaoInMemory两个名称,如果没有设置name特性则Bean名称为foo。
@Component及其派生接受一个String值作为Bean名称,没提供名称则名称为简单的类名(首字母将变为小写)。
@Service("accountService")
public class AccountServiceImpl implements AccountService{
//Implementation
}
如上设置Bean名称为accountService,若没有设置则为accountServiceImpl。
含参构造函数直接指明参数实例化Bean,无参构造函数通过
对于如下类
public class Foo {
private String name;
public Foo() {
}
public Foo(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
XML文件中以下两种实例化方式都是可行的
<bean id="foo1" class="com.wiley.beginningspring.ch2.Foo">
<property name="name" value="foo1"/>
bean>
<bean id="foo2" class="com.wiley.beginningspring.ch2.Foo">
<constructor-arg value="foo2"/>
bean>
什么是工厂方法?
工厂类仅作为创建产品的接口,具体的实现由子类完成。而属性依赖的注入在工厂发生
详情参见工厂方法模式-百度百科
还是对于上边的Foo类,创建其工厂类
public class FooFactory {
public static Foo createFoo3() {
Foo foo = new Foo();
foo.setName("foo3");
return foo;
}
public Foo createFoo4() {
Foo foo = new Foo();
foo.setName("foo4");
return foo;
}
}
此时在XML文件中可以指定class特性和factory-method特性创建实例
"foo3" class="com.wiley.beginningspring.ch2.FooFactory" factory-method="createFoo3"/>
还可以为工厂类创建工厂Bean,再使用factory-bean和factory-method特性创建实例
id="fooFactory" class="com.wiley.beginningspring.ch2.FooFactory"/>
id="foo4" factory-bean="fooFactory" factory-method="createFoo4"/>
工厂方法还可以在基于注解的配置中使用,此时工厂类要用@Component注解。这与基于Java配置类似,后者用@Configuration代替@Component即可
@Component
public class FooFactory {
@Bean(name="foo3")
public static Foo createFoo3() {
Foo foo = new Foo();
foo.setName("foo3");
return foo;
}
@Bean(name="foo4")
public Foo createFoo4() {
Foo foo = new Foo();
foo.setName("foo4");
return foo;
}
}
此时XML文件中只要设置好扫描包即可
<context:component-scan base-package="com.wiley.beginningspring.ch2"/>
工厂方法可以实现Spring自带的FactoryBean接口,其中除了工厂方法还定义了获得返回类以及返回是否为单例模式的方法
public class FooFactoryBean implements FactoryBean<Foo> {
@Override
public Foo getObject() throws Exception {
Foo foo = new Foo();
foo.setName("foo5");
return foo;
}
@Override
public Class> getObjectType() {
return Foo.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
此时XML文件中只要加入工厂类的Bean即可
id="foo5" class="com.wiley.beginningspring.ch2.FooFactoryBean"/>
即由Spring容器创建的Bean的生存期,默认状态下为Singleton
XML配置中可以在
"command" class="com.wiley.beginningspring.ch2.Command" scope="prototype">
将名为command的bean的作用域指定为prototype
Java配置中可以用@Scope注解通过一个String标识值来定义Bean作用域,如
@Component
@Scope("protoype")
public class Command {
public void execute(CommandContext commandContext) {
}
}
作用域的种类
- singleton: Bean定义中只创建一个实例。
- prototype:每一次对Bean定义的访问(无论是通过其他Bean定义还是通过getBean方法)都会创建一个新的Bean实例,类似于new操作符。
- request:整个Web请求过程中使用相同的Bean实例,每一个Web请求创建新的Bean实例。仅适用支持Web的ApplicationContext。
- session:同一个HTTP会话使用相同的Bean实例,仅适用支持Web的ApplicationContext。
- globalSession:类似session,仅适用于支持portlet的Web应用程序上下文。
注意定义Request和Session作用域的Bean时必须在
默认情况,Spring容器在启动阶段创建Bean。这样能尽早发现配置错误,但是也可能导致内存不必要消耗(一些Bean只在特定场合或者备选方案中使用)和导致初始化时间过长(Hibernate Sessioin Factory 或 JPA EntityManagerFactory中存在大量的Bean)
使用
id="accountService" class="com.wiley.beginningspring.ch2.AccountServiceImpl"
lazy-init="true">
<property name="accountDao" ref="accountDao"/>
如果想将一个XML文件中的所有Bean定义为延迟,可使用default-lazy-init特性
"http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
default-lazy-init="true">
使用@Lazy注解设置特性值为true
@Service
@Lazy(true)
public class AccountServiceImpl implements AccountService {
//Implementation
}
@Configuration
public class Ch2BeanConfiguration {
@Bean
@Lazy(true)
public AccountService accountService() {
AccountServiceImpl bean = new AccountServiceImpl();
bean.setAccountDao(accountDao());
return bean;
}
//Other Beans
}
Bean可以定义回调方法,可以在Bean生命周期任何时间由容器调用。称为初始化方法和销毁方法,前者在Bean创建后立刻被调用,后者在Bean销毁前调用
注意,由于不同bean作用域不同,destroy方法调用时间也不同
作用域为singleton时,在容器关闭时调用
作用域为request时,在web请求结束时调用
作用域为session时在HTTP会话超时或无效时调用
作用域为prototype时实例不能被跟踪,不会被调用
采用
如下Foo类中定义了intit()和destory()两个方法
public class Foo {
public void init() throws Exception {
System.out.println("init method invoked");
}
public void destroy() throws RuntimeException {
System.out.println("destroy method invoked");
}
}
现在XML中用init-method和destory-method特性将他们定义为初始化方法和销毁方法
"foo" class="com.wiley.beginningspring.ch2.Foo" init-method="init" destroy-method="destroy"/>
在@PostConstruct和@PreDestory注解分别放在初始化和销毁方法之上即可
public class Bar {
@PostConstruct
public void init() throws Exception {
System.out.println("init method invoked");
}
@PreDestroy
public void destroy() throws RuntimeException {
System.out.println("destroy method invoked");
}
}
然后在XML文件中添加该Bean以及
<bean class="com.wiley.beginningspring.ch2.Bar"/>
<context:annotation-config/>
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
public class Baz implements InitializingBean, DisposableBean {
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("init method invoked");
}
@Override
public void destroy() throws Exception {
System.out.println("destroy method invoked");
}
}
然后将该类的Bean添加进配置文件即可
class="com.wiley.beginningspring.ch2.Baz"/>
一个很自然的问题是以上几种初始化方法和销毁方法同时出现时调用顺序,在此给出结论
Bean在实例化的过程中:Constructor > @PostConstruct >InitializingBean > init-method
Bean在销毁的过程中:@PreDestroy > DisposableBean > destroy-method
详细测试代码参见Spring容器中的Bean几种初始化方法和销毁方法的先后顺序
通过import org.springframework.core.env.Environment接口来代表程序运行环境。可通过该接口管理配置文件和属性信息
针对如下Foo类
public class Foo {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
创建Java配置类
@Configuration
public class Ch2Configuration {
@Bean
@Profile("dev")
public Foo devFoo(@Value("${name}") String name) {
Foo foo = new Foo();
foo.setName("dev " + name);
return foo;
}
@Bean
@Profile("prod")
public Foo prodFoo(@Value("${name}") String name) {
Foo foo = new Foo();
foo.setName("prod " + name);
return foo;
}
@Bean
public static PropertySourcesPlaceholderConfigurer propertyPlaceHolderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
其中@Profile注释标注方法启用所需要的环境
在Main函数中配置环境获取foo Bean,
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(Ch2Configuration.class);
ConfigurableEnvironment environment = applicationContext.getEnvironment();
environment.setActiveProfiles("dev");
MutablePropertySources propertySources = environment.getPropertySources();
propertySources.addLast(new MapPropertySource("mapSource", Collections.singletonMap("name", (Object)"my foo")));
applicationContext.refresh();
Foo foo = applicationContext.getBean(Foo.class);
System.out.println(foo.getName());
}
}