【Spring IOC容器学习笔记】二——详解Spring bean

摘要

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;
    }
}

参考资料

1, Spring Core Technologies

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值