• 详述对 Bean 的作用域及生命周期的理解


    1 作用域

    关于 Bean 的作用域用的场景并不是很多, 我们只需要掌握其概念, 了解每个作用域的应用场景即可, 当然也会举一个简单的🌰进行分析;
    注意: 后四中是基于 Spring MVC 生效.

    关于 Bean 的作用域的内容有些头皮发麻, 不用太担心, 只需知道怎么一回事即可.
    在这里插入图片描述

    1.1 singleton: 单例作用域.

    描述: 该作用域下在 IoC 容器中只存在一个实例, 获取 Bean 及装配 Bean 都是同一个对象; 这里需要说明 Bean 默认情况下是单例状态, 也就是所有人的使用都是同一个对象, 使用单例可以很大程度上提高性能, 因此在 Spring 中 Bean 的作用域默认是单例的;
    使用场景: 当 Bean 对象的属性状态不需要更新的情况下使用该作用域, 也就是说无状态的 Bean 使用该作用域.

    1.2 prototype: 原型作用域 / 多例作用域

    描述: 每次对该作用域下的 Bean 的请求都会创建新的实例: 获取 Bean 及装配 Bean 都是新的对象实例;
    使用场景: 当 Bean 对象的属性状态需要更新的情况下使用该作用域, 也就是说有状态的 Bean 使用该作用域.

    1.3 request: 请求作用域

    描述: 每次 Http 请求都会创建新的 Bean 实例, 与 prototype 类似;
    使用场景: 一次 http 的请求和响应的共享 Bean.

    1.4 session: 回话作用域

    描述: 在一个 Http Session 中定义一个 Bean 实例;
    使用场景: 用户回话的共享 Bean, 例如记录一个用户的登录状态.

    1.5 application: 全局作用域(了解)

    描述: 在一个 Http Servlet Context 中定义了一个 Bean 实例;
    使用场景: Web 应用的上下文信息, 例如记录一个应用的共享信息.

    1.6 websocket: Http WebSocket 作用域(了解)

    描述: 在一个 Http WebSocket 的生命周期中, 定义一个 Bean 实例;
    使用场景: WebSocket 的每次会话中, 保存了一个 Map 结构的头信息, 将用来包裹客户端消息头, 第一次初始化后, 直到 WebSocket 结束都是同一个 Bean.

    🌰:
    我们以 prototype 为例简单说一下:
    第一步: 首先定义一个 bean:

    @Component
    public class UserBean {
    	@Bean(name = "user1") 
        public User user1() {
            // 查询数据库并返回对象
            // 这里我们使用伪代码来构建一个对象
            User user = new User();
            user.setId(1);
            user.setName("Java");
            user.setAge(18);
            return user;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    第二步: 在 controller 中写两个方法:
    方法一: 将 User 对象里面的 name 改成 “C++”

    @Component
    public class BeanScope1 {
        @Autowired
        private User user1;
        public User getUser() {
            User user = user1;
            user.setName("C++");
            return user1;
        }
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    方法二: 不做改变

    @Component
    public class BeanScope2 {
        @Autowired
        private User user1;
    
        public User getUser() {
            return user1;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    第三步: 在启动类里面获取到 Bean 的实例:

    public class App {
        public static void main(String[] args) {
            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
            BeanScope1 beanScope1 = context.getBean(BeanScope1.class);
            User user1 = beanScope1.getUser();
            System.out.println("BeanScope1: " + user1);
    
            BeanScope2 beanScope2 = context.getBean(BeanScope2.class);
            User user2 = beanScope2.getUser();
            System.out.println("beanScope2: " + user2);
         }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    因为我们没有修改其作用域, 上文已经说了如果没有修改作用域的话, Spring 默认 Bean 的作用域是单例模式, 也就是 singleton 模式, 因此运行后的结果如下:
    在这里插入图片描述
    第四步: 如果这里我们将单例作用域改成多例作用域, 也就是加上 @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE), 只需要在UserBean 里面加上, 如下:

    @Component
    public class UserBean {
    	@Bean(name = "user1") 
    	@Scope(ConfigurableListableBeanFactory.SCOPE_PROTOTYPE)
        public User user1() {
            // 查询数据库并返回对象
            // 这里我们使用伪代码来构建一个对象
            User user = new User();
            user.setId(1);
            user.setName("Java");
            user.setAge(18);
            return user;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    这时候其他地方不变, 运行结果则如下:
    在这里插入图片描述

    注意

    • @scope 可以适用于方法注解, 也可以适用于类注解, 如果使用的是 @Bean + @Scope, @Scope 一定要放在方法上进行注解, 不能放在类上.
    • 当然 Bean 的作用域有两种写法, 如下:
    • @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    • @Scope(“prototype”)
      推荐使用第一种写法.
      以上就是 Bean 的 作用域有关的内容.

    2 生命周期

    关于 Bean 的生命周期比较繁琐, 总结如下:

    • 实例化, 也就是给 Bean 分配内容空间;
    • 设置属性, 也就是对象的注入;
    • 初始化, 初始化包含的内容比较多, 如下:
    • 执行各种通知, 各种 Aware;
    • 执行初始化的前置方法;
    • 执行构造方法, 这里有两种执行方式, 一种是执行 @PostConstruct, 另一种是 init-method;
    • 执行初始化的后置方法.
    • 使用 Bean;
    • 销毁 Bean, 这里要包含几个部分, 如下:
    • @PreDestroy;
    • 重写 DisposableBean 接口方法;
    • destory-method.

    看完其生命周期是不是更头皮发麻!!!
    在这里插入图片描述
    还是以一个生活中的🌰来描述一下吧!!!
    例如我们要买一套房子:
    在这里插入图片描述
    补充: 也可看如下流程图:
    在这里插入图片描述

  • 相关阅读:
    华为杯数学建模比赛经验分享第二期——编程手篇
    面试官居然问我:删库后,除了跑路还能干什么?
    外包干了一个月,技术明显进步。。。。。
    [论文总结] 深度学习在农业领域应用论文笔记10
    Java终止线程的三种方式
    docker 安装kafka
    【跨境电商】WhatsApp营销保姆级教程!
    Webpack--devServer的常用配置
    有关排序的算法
    java基于springboot+vue基本微信小程序的乒乓球课程管理系统 uniapp小程序
  • 原文地址:https://blog.csdn.net/Onion_521257/article/details/126914384