Spring Bean的作用域
创始人
2024-02-22 21:22:59
0

1.写在前面

前面的博客我们已经介绍完Spring的依赖的查找来源,依赖注入的来源等等相关知识,今天我们继续来介绍Spring的Bean的作用域。

2.Spring Bean作用域

作用域

在这里插入图片描述

3.“singleton” Bean作用域

配置

在这里插入图片描述

4.“prototype” Bean作用域

配置

在这里插入图片描述

注意事项

  • Spring 容器没有办法管理 prototype Bean 的完整生命周期, 也没有办法记录实例的存在。 销毁回调方法将不会执行, 可以利用 BeanPostProcessor 进行清扫工作。

示例代码如下:

package org.learn.spring.bean.scope;import org.learn.spring.ioc.overview.domain.User;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;import java.util.Map;// Bean的作用域示例
// 结论一:
// singleton Bean 无论是依赖注入和依赖查找都是同一个对象
// prototype Bean 无论是依赖注入和依赖查找每次都是新的对象// 结论二:
// 如果注入的是集合类型对象,singleton Bean 和 prototype Bean 均只存在一个
// prototype Bean 有别于其他地方的依赖注入 prototype Bean// 结论三:
// 无论是 singleton Bean 还是 prototype Bean 都会执行初始化方法回调
// 不过仅 singleton Bean 会执行销毁方法回调
public class BeanScopeDemo implements DisposableBean {@Bean// 默认singletonpublic static User singletonUser() {return createUser();}@Bean@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)public static User prototypeUser() {return createUser();}@Autowired@Qualifier("singletonUser")private User singletonUser;@Autowired@Qualifier("singletonUser")private User singletonUser1;@Autowired@Qualifier("prototypeUser")private User prototypeUser;@Autowired@Qualifier("prototypeUser")private User prototypeUser1;@Autowired@Qualifier("prototypeUser")private User prototypeUser2;@Autowiredprivate Map users;@Autowiredprivate ConfigurableListableBeanFactory beanFactory; // Resolvable Dependencyprivate static User createUser() {User user = new User();user.setId(System.nanoTime());return user;}public static void main(String[] args) {// 创建BeanFactory的容器AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();// 注册Configuration Class 配置类 -> Spring BeanapplicationContext.register(BeanScopeDemo.class);applicationContext.addBeanFactoryPostProcessor(beanFactory -> {beanFactory.addBeanPostProcessor(new BeanPostProcessor() {@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.printf("%s Bean 名称:%s 在初始化回调...%n", bean.getClass().getName(), beanName);return bean;}});});// 启动应用上下文applicationContext.refresh();scopedBeansByLookup(applicationContext);scopedBeansByInjection(applicationContext);// 显示的关闭spring应用上下文applicationContext.close();}private static void scopedBeansByLookup(AnnotationConfigApplicationContext applicationContext) {for (int i = 0; i < 3; i++) {// singletonUser 每次查找的都是共享的BeanUser singletonUser = applicationContext.getBean("singletonUser", User.class);System.out.println("singletonUser = " + singletonUser);// prototypeUser 每次查找的都是生成新的BeanUser prototypeUser = applicationContext.getBean("prototypeUser", User.class);System.out.println("prototypeUser = " + prototypeUser);}}private static void scopedBeansByInjection(AnnotationConfigApplicationContext applicationContext) {BeanScopeDemo beanScopeDemo = applicationContext.getBean(BeanScopeDemo.class);System.out.println("beanScopeDemo.singletonUser = " + beanScopeDemo.singletonUser);System.out.println("beanScopeDemo.singletonUser1 = " + beanScopeDemo.singletonUser1);System.out.println("beanScopeDemo.prototypeUser = " + beanScopeDemo.prototypeUser);System.out.println("beanScopeDemo.prototypeUser1 = " + beanScopeDemo.prototypeUser1);System.out.println("beanScopeDemo.prototypeUser2 = " + beanScopeDemo.prototypeUser2);System.out.println("beanScopeDemo.users = " + beanScopeDemo.users);}@Overridepublic void destroy() throws Exception {System.out.println("当前BeanScopeDemo Bean 正在销毁中....");this.prototypeUser.destroy();this.prototypeUser1.destroy();this.prototypeUser2.destroy();for (Map.Entry entry : this.users.entrySet()) {String beanName = entry.getKey();BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);if (beanDefinition.isPrototype()) { // 如果当前Bean 是 prototype scopeUser user = entry.getValue();user.destroy();}}System.out.println("当前BeanScopeDemo Bean 正在销毁完成....");}
}

5.“request” Bean作用域

配置

  • XML -
  • Java 注解 - @RequestScope 或 @Scope(WebApplicationContext.SCOPE_REQUEST)

实现

  • API - RequestScope

每次返回给前端的对象都是不一样的,但是spring中生成的对象是同一个。

6.“session” Bean作用域

配置

  • XML -
  • Java 注解 - @SessionScope 或 @Scope(WebApplicationContext.SCOPE_SESSION)

实现

  • API - SessionScope

每次返回给前端的对象是一样的(sessionId是一样的时候)但是spring中生成的对象是同一个。

7.“application” Bean作用域

配置

  • XML -
  • Java 注解 - @ApplicationScope 或 @Scope(WebApplicationContext.SCOPE_APPLICATION)

实现

  • API - ServletContextScope

8.自定义 Bean 作用域

实现 Scope

  • org.springframework.beans.factory.config.Scope

注册 Scope

  • API - org.springframework.beans.factory.config.ConfigurableBeanFactory#registerScope

  • 配置

    
    
    

示例代码如下:

package org.learn.spring.bean.scope;import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;
import org.springframework.core.NamedThreadLocal;import java.util.HashMap;
import java.util.Map;// ThreadLocal 级别的 Scope
public class ThreadLocalScope implements Scope {public static final String SCOPE_NAME = "thread-local";private final NamedThreadLocal> threadLocal = new NamedThreadLocal("thread-local-scope") {public Map initialValue() {return new HashMap<>();}};// 通过容器去取@Overridepublic Object get(String name, ObjectFactory objectFactory) {// 非空Map context = getContext();Object object = context.get(name);if (object == null) {object = objectFactory.getObject();context.put(name, object);}return object;}private Map getContext() {return threadLocal.get();}// 删除的时候调用@Overridepublic Object remove(String name) {Map context = getContext();return context.remove(name);}// 注册一个销毁系统的回调@Overridepublic void registerDestructionCallback(String name, Runnable callback) {// TODO}@Overridepublic Object resolveContextualObject(String key) {Map context = getContext();return context.get(key);}// 返回一个会话的id@Overridepublic String getConversationId() {Thread thread = Thread.currentThread();return String.valueOf(thread.getId());}
}
package org.learn.spring.bean.scope;import org.learn.spring.ioc.overview.domain.User;
import org.springframework.aop.ThrowsAdvice;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;// 自定义 Scope 示例
public class ThreadLocalScopeDemo {@Bean@Scope(ThreadLocalScope.SCOPE_NAME)public User user() {return createUser();}private static User createUser() {User user = new User();user.setId(System.nanoTime());return user;}public static void main(String[] args) {// 创建BeanFactory的容器AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();// 注册Configuration Class 配置类 -> Spring BeanapplicationContext.register(ThreadLocalScopeDemo.class);applicationContext.addBeanFactoryPostProcessor(beanFactory -> {// 注册自定义ScopebeanFactory.registerScope(ThreadLocalScope.SCOPE_NAME, new ThreadLocalScope());});// 启动应用上下文applicationContext.refresh();scopedBeansByLookup(applicationContext);applicationContext.close();}private static void scopedBeansByLookup(AnnotationConfigApplicationContext applicationContext) {for (int i = 0; i < 3; i++) {Thread thread = new Thread(() -> {User user = applicationContext.getBean("user", User.class);System.out.printf("[Thread id : %d] user = %s %n", Thread.currentThread().getId(), user);});// 启动线程thread.start();// 强制线程执行完成try {thread.join();} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

运行的结果如下:

在这里插入图片描述

9.面试题

9.1 Spring 內建的 Bean 作用域有几种?

singleton、 prototype、 request、 session、 application 以及websocket

9.2 singleton Bean 是否在一个应用是唯一的?

否, singleton bean 仅在当前 Spring IoC 容器( BeanFactory) 中是单例对象。

9.3 application” Bean 是否被其他方案替代?

可以的, 实际上, “ application” Bean 与“ singleton” Bean 没有本质区别

相关内容

热门资讯

政策再接力 初心恒久远 王 浩 随着全国统一大市场日益成形,区域和城乡之间要素资源流动更加畅通,乡村不再是“偏僻”的代名词,...
进一步健全反腐败法规制度 良法是善治之前提,法规制度是防腐、治腐的利器。习近平总书记多次强调,要加强反腐倡廉党内法规制度建设,...
特稿丨中国高水平开放新答卷——... 新华社北京12月18日电 题:中国高水平开放新答卷——从海南自贸港封关看中国制度型开放 新华社记者宿...
法院不判离婚,发“感情修复通知... 近日,一份由“武汉某区人民法院”出具的“夫妻感情修复通知书”在网上流传,引发热议。 对此,不少网...
EUDR法规解读机构排名及法规... 在当今竞争激烈的商业环境中,各类法规的解读和应用对于企业的发展至关重要。其中,EUDR法规在行业中扮...
施工殃及池鱼?常德鼎城法院联合... 近日,常德市鼎城区人民法院西洞庭法庭联合辖区人大代表协同发力,成功化解一起个体工商户与企业间的财产损...
石楼召开集体林权制度改革工作推... 12月17日,我县召开深化集体林权制度改革工作推进会,县委常委、政府副县长张帅主持会议并讲话。各乡镇...
政策再接力 初心恒久远(人民时... 王 浩 随着全国统一大市场日益成形,区域和城乡之间要素资源流动更加畅通,乡村不再是“偏僻”的代名词,...
赣州警方深挖招嫖违法犯罪线索 ... 赣州市公安局章贡分局12月10日 分局治安大队 紧盯涉黄“小卡片” 招嫖违法犯罪线索 精准施策、主动...