摘要
bean对于Spring就如同object对于OOP,这一节详细整理下关于bean的基础知识:
- bean 作用域
- bean 命名
- bean 实例化
1 Bean的作用域
1.1 作用域的类型
Singleton
在Spring IOC 容器中只有一个实例,所用引用它的bean共享这个实例
与设计模式中单例模式的区别:
单例模式指每个JVM进程中只有一个实例,它的范围比Singleton大,因为一个JVM进程中可以由多个Spring IOC容器
Prototype
在Spring IOC容器中有任意多个实例,每次被引用都会单独实例化一个实例供使用
Request
只用于ApplicationContext实现的容器,每个http请求中共享一个实例,不同请求的实例互不影响
Session
只用于ApplicationContext实现的容器,每个http会话共享一个实例
Application
只用于ApplicationContext实现的容器,作用范围是ServletContext。比Singleton范围广,因为一个应用下可以定义多个ApplictionContext
1.2 定义作用域的方式
XML 文件
在bean标签的scope属性定义
<bean id="userPreferences" class="com.something.UserPreferences" scope="session"/>
注解方式
放在@Component注解上,如@RequestScope
@RequestScope
@Component
public class LoginAction {
// ...
}
1.3 Singleton bean依赖Prototype bean的问题
如果Prototype bean作为属性被Singleton bean依赖,则在实例化Singlton bean时,Spring 容器通过DI的方式注入Prototype bean也只会发生一次。但如果你的需求是在运行时(runtime)让 Singleton bean多次获取Prototype bean且每次都要一个新的实例怎么办?
一种解决方式放弃使用IOC容器的依赖注入方式,让被实例化的Singleton bean通过Sring API 来获取Prototype bean,示例代码:
// 示例类CommandManager使用命令风格的代码处理流程
package fiona.apple;
// 引入Spring-API
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class CommandManager implements ApplicationContextAware {
private ApplicationContext applicationContext;
public Object process(Map commandState) {
// 创建一个合适的新命令实例
Command command = createCommand();
// 新命令实例设置状态
command.setState(commandState);
return command.execute();
}
protected Command createCommand() {
// 使用ApplicationContext 方法获取bean
return this.applicationContext.getBean("command", Command.class);
}
public void setApplicationContext(
ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
该方案使被实例化的Singlton bean (本例中CommandManager)实现ApplicationContextAware
接口,可以感知到ApplicationContext的存在并通过它来实现bean获取。
上述方案可以达到目的,但使业务代码和Spring 框架代码耦合,更优雅的解决方案是使用查找方法注入(Lookup Method Injection)。首先定义Singleton bean
// 定义抽象类
package fiona.apple;
public abstract class CommandManager {
public Object process(Object commandState) {
Command command = createCommand();
command.setState(commandState);
return command.execute();
}
// 谁来实现呢?
protected abstract Command createCommand();
}
Spring容器有一个高级特性,可以利用CGLIB 动态生成子类实现被注入的方法。
<!-- 被依赖的Prototype beand的声明 -->
<bean id="myCommand" class="fiona.apple.AsyncCommand" scope="prototype">
</bean>
<!-- Singleton bean的声明 -->
<bean id="commandManager" class="fiona.apple.CommandManager">
<lookup-method name="createCommand" bean="myCommand"/>
</bean>
只要加上上面配置,每次commandManager需要获取一个新的myCommand实例,Spring容器都会调用被注入的createCommand()方法,返回AsyncCommand的实例。对查找方法注入的底层实现原理感兴趣的可以参考这篇博文。
2 bean的命名
2.1 系统默认命名
定义bean时,如果不指定一个name,系统会自动命名。命名方法是Class的简写名首字母小写,例外情况:Class简写名的头两个字母都是大写时,系统默认name就是简写名
2.2 别名
使用场景包含许多子系统的大型系统。每个子系统都有各自一套命名,如果多个子系统引用了相同的类,则在父系统中就需要使用别名来统一
3 bean的实例化方式
3.1 通过构造函数实例化
使用这种方式实例化bean,只要在定义bean定义时指定class属性即可。 根据Spring IOC容器实现方式的不同,有可能需要定义一个空参数的构造函数。示例定义
<bean id="exampleBean" class="examples.ExampleBean"/>
<bean name="anotherExample" class="examples.ExampleBeanTwo"/>
3.2 通过静态工厂方法实例化
使用这种方法实例化bean, 需要指定class属性为包含静态工厂方法的类,factory-bean属性为指定的静态工厂方法。示例定义
<bean id="clientService"
class="examples.ClientService"
factory-method="createInstance"/>
对应java代码
public class ClientService {
private static ClientService clientService = new ClientService();
private ClientService() {}
public static ClientService createInstance() {
return clientService;
}
}
通过上面定义,就可以使用ClientService类的静态工厂方法createInstance()来实例化ClientService。
3.3 通过工厂bean实例的非静态工厂方法实例化
通过某个bean的非静态方法来实例化bean,配置bean时需要把class属性留空,把factory-bean属性指定为拥有工厂方法的bean,factory-mehtod属性指定为用于实例化的非静态工厂方法,示例如下
<!-- 工厂bean 实例定义,包含创建实例的方法-->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
</bean>
<!-- 被工厂实例化的bean定义 -->
<bean id="clientService"
factory-bean="serviceLocator"
factory-method="createClientServiceInstance"/>
工厂bean的java代码
public class DefaultServiceLocator {
private static ClientService clientService = new ClientServiceImpl();
public ClientService createClientServiceInstance() {
return clientService;
}
}