spring bean scope 用法分析

简介: Spring 容器是应用组件 bean 创建和组装专家, 支持 `InitializingBean`, `AbstractBeanDefinition.setInitMethodName(String)`, `@PostConstruct` 等多种方法初始化(及销毁)bean。 ## 单例(singleton)bean 最常使用的是单例(singleton)bean,Spring 自

Spring 容器是应用组件 bean 创建和组装专家,
支持 InitializingBean, AbstractBeanDefinition.setInitMethodName(String), @PostConstruct 等多种方法初始化(及销毁)bean。

单例(singleton)bean

最常使用的是单例(singleton)bean,Spring 自动帮我们管理其生命周期。

代码示例如下:

class Rick {
    @PostConstruct
    void init() {
        System.out.println("rick init");
    }
    @PreDestroy
    void destroy() {
        System.out.println("rick destroy");
    }
}

@Configuration
class RickConfig {
    @Bean
    Rick rick() {
        return new Rick();
    }
}

@Configuration
@ComponentScan
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
}

运行结果如下:

rick init
rick destroy

原型(prototype)bean

有些场景不适合使用单例 bean,因此 spring 还支持原型(prototype)bean,每次请求 bean 都返回一个新的实例。

一段 错误的 代码示例如下,每个平行宇宙都有一个 rick:

class Rick {
    static int NextId;
    int id = NextId++;
    @PostConstruct
    void init() {
        System.out.println("rick init " + id);
    }
    @PreDestroy
    void destroy() {
        System.out.println("rick destroy " + id);
    }
}

@Configuration
class RickConfig {
    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    Rick rickPrototype() {
        return new Rick();
    }
    @Lookup
    Rick rick() {
        throw new IllegalStateException("this method would be override by spring.");
    }
}

@Component
class WorldC37 {
    @Autowired
    Rick rick;
}

@Component
class WorldZ {
    @Autowired
    Rick rick;
}

@Configuration
@ComponentScan
public class App implements ApplicationRunner {
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
    @Autowired
    RickConfig rickConfig;
    @Override
    public void run(ApplicationArguments args) throws Exception {
        Rick rick = rickConfig.rick();
    }
}

运行结果如下:

rick init 0
rick init 1
rick init 2

问题:

  1. 很显然,原型 bean 被实例化了多次,但没有被销毁。

    原型 bean 很灵活,除了容器初始化时创建外,应用运行时还可以通过 `@Lookup` 方法任意创建。

    因此 spring 采取了放养政策,初始化完就交给应用自己处理,不会自动管理原型 bean 的生命周期。

这段代码还有其他不足:

  1. 每个平行宇宙的 rick 可能很不一样,需要支持个性化定制。
  2. 一个宇宙只有一个 rick,动态创建应该跟其宇宙绑定,而不是总是不停的创建。

先来解第一个问题:Spring 让应用自己管理原型 bean,前面提到 bean 支持多种销毁方法,如何保证处理正确?
靠谱的做法还是交回 Spring 销毁,但应用负责决定什么时候 bean 不要了,应该销毁,回调 Spring。
原型 bean 可动态创建,也可以动态销毁,所以应用可以更灵活的控制其生命周期。
适时调用 ConfigurableBeanFactory.destroyBean(String beanName, Object beanInstance) 方法即可销毁 bean。

订正后的代码示例如下:

abstract class AbstractWorld extends ApplicationObjectSupport {
    @Autowired
    Rick rick;
    @PreDestroy
    void destroy() {
        ((ConfigurableApplicationContext) getApplicationContext()).getBeanFactory().destroyBean("rickPrototype", rick);
    }
}

@Component
class WorldC37 extends AbstractWorld {
}

@Component
class WorldZ extends AbstractWorld {
}

@Configuration
@ComponentScan
public class App extends ApplicationObjectSupport implements ApplicationRunner {
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
    @Autowired
    RickConfig rickConfig;
    @Override
    public void run(ApplicationArguments args) throws Exception {
        Rick rick = rickConfig.rick();
        ((ConfigurableApplicationContext) getApplicationContext()).getBeanFactory().destroyBean("rickPrototype", rick);
        rick = null;
    }
}

运行结果:

rick init 0
rick init 1
rick init 2
rick destroy 2
rick destroy 1
rick destroy 0

可看到原型 bean 被正确销毁。

自定义 scope

Spring 还支持自定义 scope,其用法介于单例和原型之间。

如之前说过的,每个新的平行宇宙会有新的一个 rick,但同一个宇宙总会获取到同一个 rick(而不是总是新建),这里宇宙即可作为一种自定义 scope。
详情参考文档:https://docs.spring.io/spring/docs/4.3.13.RELEASE/spring-framework-reference/htmlsingle/#beans-factory-scopes-custom
此处不再细说。

相关文章
|
30天前
|
安全 Java Spring
Spring框架中的单例Bean是线程安全的吗?
Spring框架中的单例Bean是线程安全的吗?
24 1
|
2天前
|
Java Spring
解决 Spring 中 Prototype Bean 注入后被固定的问题
【6月更文挑战第8天】学习 Spring 框架内不原理的意义就是,当遇到问题时,分析出原因,就可以从多个切入点,利用 Spring 的特性,来解决问题。
14 2
|
3天前
|
JSON Java 数据格式
Spring Boot 中的 @DateTimeFormat 和 @JsonFormat 的用法及作用
【6月更文挑战第11天】在开发 Spring Boot 应用时,处理日期和时间数据是一个常见的需求。Spring Boot 提供了两个注解 @DateTimeFormat 和 @JsonFormat 来帮助我们处理这些问题。
13 4
|
12天前
|
Java Spring 缓存
Spring Bean循环依赖详解
【6月更文挑战第2天】
22 2
|
16天前
|
存储 Java 数据库
Spring的使用-Bean对象的储存和获取/Bea对象的作用域与生命周期
Spring的使用-Bean对象的储存和获取/Bea对象的作用域与生命周期
|
21天前
|
Java Spring 容器
Spring注解开发,bean的作用范围及生命周期、Spring注解开发依赖注入
Spring注解开发,bean的作用范围及生命周期、Spring注解开发依赖注入
33 1
Spring注解开发,bean的作用范围及生命周期、Spring注解开发依赖注入
|
21天前
|
Java 容器 Spring
Spring的加载配置文件、容器和获取bean的方式
Spring的加载配置文件、容器和获取bean的方式
28 3
Spring的加载配置文件、容器和获取bean的方式
|
21天前
|
Java Spring 容器
Spring核心概念、IoC和DI的认识、Spring中bean的配置及实例化、bean的生命周期
Spring核心概念、IoC和DI的认识、Spring中bean的配置及实例化、bean的生命周期
37 0
|
22天前
|
XML Java 数据格式
Spring框架学习 -- Bean的生命周期和作用域
Spring框架学习 -- Bean的生命周期和作用域
21 2
|
22天前
|
存储 XML Java
Spring框架学习 -- 读取和存储Bean对象
Spring框架学习 -- 读取和存储Bean对象
18 0
Spring框架学习 -- 读取和存储Bean对象