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

叁歲伎倆 2021-12-12 00:04 378阅读 0赞

摘要

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属性定义

  1. <bean id="userPreferences" class="com.something.UserPreferences" scope="session"/>

注解方式
放在@Component注解上,如@RequestScope

  1. @RequestScope
  2. @Component
  3. public class LoginAction {
  4. // ...
  5. }

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,示例代码:

  1. // 示例类CommandManager使用命令风格的代码处理流程
  2. package fiona.apple;
  3. // 引入Spring-API
  4. import org.springframework.beans.BeansException;
  5. import org.springframework.context.ApplicationContext;
  6. import org.springframework.context.ApplicationContextAware;
  7. public class CommandManager implements ApplicationContextAware {
  8. private ApplicationContext applicationContext;
  9. public Object process(Map commandState) {
  10. // 创建一个合适的新命令实例
  11. Command command = createCommand();
  12. // 新命令实例设置状态
  13. command.setState(commandState);
  14. return command.execute();
  15. }
  16. protected Command createCommand() {
  17. // 使用ApplicationContext 方法获取bean
  18. return this.applicationContext.getBean("command", Command.class);
  19. }
  20. public void setApplicationContext(
  21. ApplicationContext applicationContext) throws BeansException {
  22. this.applicationContext = applicationContext;
  23. }
  24. }

该方案使被实例化的Singlton bean (本例中CommandManager)实现ApplicationContextAware接口,可以感知到ApplicationContext的存在并通过它来实现bean获取。

上述方案可以达到目的,但使业务代码和Spring 框架代码耦合,更优雅的解决方案是使用查找方法注入(Lookup Method Injection)。首先定义Singleton bean

  1. // 定义抽象类
  2. package fiona.apple;
  3. public abstract class CommandManager {
  4. public Object process(Object commandState) {
  5. Command command = createCommand();
  6. command.setState(commandState);
  7. return command.execute();
  8. }
  9. // 谁来实现呢?
  10. protected abstract Command createCommand();
  11. }

Spring容器有一个高级特性,可以利用CGLIB 动态生成子类实现被注入的方法。

  1. <!-- 被依赖的Prototype beand的声明 -->
  2. <bean id="myCommand" class="fiona.apple.AsyncCommand" scope="prototype">
  3. </bean>
  4. <!-- Singleton bean的声明 -->
  5. <bean id="commandManager" class="fiona.apple.CommandManager">
  6. <lookup-method name="createCommand" bean="myCommand"/>
  7. </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容器实现方式的不同,有可能需要定义一个空参数的构造函数。示例定义

  1. <bean id="exampleBean" class="examples.ExampleBean"/>
  2. <bean name="anotherExample" class="examples.ExampleBeanTwo"/>

3.2 通过静态工厂方法实例化

使用这种方法实例化bean, 需要指定class属性为包含静态工厂方法的类,factory-bean属性为指定的静态工厂方法。示例定义

  1. <bean id="clientService"
  2. class="examples.ClientService"
  3. factory-method="createInstance"/>

对应java代码

  1. public class ClientService {
  2. private static ClientService clientService = new ClientService();
  3. private ClientService() {}
  4. public static ClientService createInstance() {
  5. return clientService;
  6. }
  7. }

通过上面定义,就可以使用ClientService类的静态工厂方法createInstance()来实例化ClientService。

3.3 通过工厂bean实例的非静态工厂方法实例化

通过某个bean的非静态方法来实例化bean,配置bean时需要把class属性留空,把factory-bean属性指定为拥有工厂方法的bean,factory-mehtod属性指定为用于实例化的非静态工厂方法,示例如下

  1. <!-- 工厂bean 实例定义,包含创建实例的方法-->
  2. <bean id="serviceLocator" class="examples.DefaultServiceLocator">
  3. </bean>
  4. <!-- 被工厂实例化的bean定义 -->
  5. <bean id="clientService"
  6. factory-bean="serviceLocator"
  7. factory-method="createClientServiceInstance"/>

工厂bean的java代码

  1. public class DefaultServiceLocator {
  2. private static ClientService clientService = new ClientServiceImpl();
  3. public ClientService createClientServiceInstance() {
  4. return clientService;
  5. }
  6. }

参考资料

1, Spring Core Technologies

发表评论

表情:
评论列表 (有 0 条评论,378人围观)

还没有评论,来说两句吧...

相关阅读

    相关 详解Spring IoC容器

    一、Spring IoC容器概述   1.依赖反转(依赖注入):依赖对象的获得被反转了。   如果合作对象的引用或依赖关系的管理由具体对象来完成,会导致代码的高度耦合和可测