JavaEE进阶:Bean 作⽤域和⽣命周期
创始人
2024-03-25 22:36:38
0

文章目录

  • 一、Bean 的作用域问题
    • 1、案例
    • 2、分析
  • 二、作用域定义
    • 1、Bean 的 6 种作用域
      • ① singleton
      • ② prototype
      • ③ request
      • ④ session
      • ⑤ application
      • ⑥ websocket
      • ⑦ 单例作用域(singleton)和全局作用域(application)区别
    • 2、设置作用域
    • 3、案例修改
  • 三、Bean 原理分析
    • 1、Bean 执行流程
    • 2、Bean 生命周期
      • ① 实例化和初始化的区别
      • ② 生命流程的“故事”
      • ③ 生命周期演示
      • ④ 思考:为什么要先设置属性在进行初始化呢?

一、Bean 的作用域问题

1、案例

先定义一个 Cat 类:

public class Cat {private int id;private String name;private int age;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "Cat{" +"id=" + id +", name='" + name + '\'' +", age=" + age +'}';}
}

定义一个公共的 Bean:

@Component
public class CatBeans {@Beanpublic Cat cat() {Cat cat = new Cat();cat.setId(1);cat.setName("喵喵喵");cat.setAge(8);return cat;}
}

小明 在使用时,修改了 Bean:

@Controller
public class ScopeController {@Autowiredprivate Cat cat;public void doScope() {System.out.println("Do scope controller.");System.out.println("原数据:" + cat.toString());// 修改Cat cat2 = new Cat();cat2.setName("喵喵");System.out.println("修改之后的数据: " + cat2.toString());}
}

我们之后如果还要用这个 Bean:

@Controller
public class ScopeController2 {@Resourceprivate Cat cat;public void doSCope(){System.out.println("Do scope controller 2.");System.out.println(cat.toString());}
}

测试:

public class App {public static void main(String[] args) {// 1.得到 Spring 上下文对象ApplicationContext context =new ClassPathXmlApplicationContext("spring-config.xml");ScopeController scopeController =context.getBean("scopeController", ScopeController.class);scopeController.doScope();System.out.println();ScopeController2 scopeController2 =context.getBean("scopeController2", ScopeController2.class);scopeController2.doSCope();System.out.println();}
}

运行结果为:
在这里插入图片描述

2、分析

Spring 中 Bean 默认情况下是单例模式,即 cat 和 cat1 都指向了同一块区域,我们无论是修改 cat 还是 cat1 都会对这个 Bean 造成修改。

二、作用域定义

Bean 在 Spring 整个框架中的某种⾏为模式,⽐如 singleton 单例作⽤域,就表示 Bean 在整个 Spring 中只有⼀份。

1、Bean 的 6 种作用域

Spring 容器在初始化⼀个 Bean 的实例时,同时会指定该实例的作⽤域。Spring有 6 种作⽤域,
最后四种是基于 Spring MVC ⽣效的(主要学习前四种 作用域):

  • singleton:单例作⽤域(默认作用域),只有一个全局对象。
  • prototype:原型作⽤域(多例作⽤域)每次访问都会创建一个新的对象。
  • request:请求作⽤域(只能在 Spring MVC 中使用),每次 HTTP 请求都会创建一个对象。
  • session:回话作⽤域(只能在 Spring MVC 中使用),每次(session)会话使用一个 bean 对象。
  • application:全局作⽤域(只能在 Spring MVC 中使用),一个 http servlet context 中共享一个对象。
  • websocket:HTTP WebSocket 作⽤域(只能在 Web Socket 中使用

下面是官方的资料:

① singleton

  • 官⽅说明:(Default) Scopes a single bean definition to a single object instance for each Spring IoC container.
  • 描述:该作⽤域下的Bean在IoC容器中只存在⼀个实例:获取Bean(即通过 applicationContext.getBean等⽅法获取)及装配Bean(即通过@Autowired注⼊)都是同⼀ 个对象。
  • 场景:通常无状态的Bean使⽤该作⽤域。⽆状态表示Bean对象的属性状态不需要更新
  • 备注:Spring默认选择该作⽤域

② prototype

  • 官⽅说明:Scopes a single bean definition to any number of object instances.
  • 描述:每次对该作⽤域下的Bean的请求都会创建新的实例:获取Bean(即通过 applicationContext.getBean等⽅法获取)及装配Bean(即通过@Autowired注⼊)都是新的 对象实例。
  • 场景:通常有状态的Bean使⽤该作⽤域

③ request

  • 官⽅说明:Scopes a single bean definition to the lifecycle of a single HTTP request. That is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext.
  • 描述:每次http请求会创建新的Bean实例,类似于prototype
  • 场景:⼀次http的请求和响应的共享Bean
  • 备注:限定SpringMVC中使⽤

④ session

  • 官⽅说明:Scopes a single bean definition to the lifecycle of an HTTP Session. Only valid in the context of a web-aware Spring ApplicationContext.
  • 描述:在⼀个http session中,定义⼀个Bean实例
  • 场景:⽤户回话的共享Bean, ⽐如:记录⼀个⽤户的登陆信息
  • 备注:限定SpringMVC中使⽤

⑤ application

  • 官⽅说明:Scopes a single bean definition to the lifecycle of a ServletContext. Only valid in the context of a web-aware Spring ApplicationContext.
  • 描述:在⼀个http servlet Context中,定义⼀个Bean实例
  • 场景:Web应⽤的上下⽂信息,⽐如:记录⼀个应⽤的共享信息
  • 备注:限定SpringMVC中使⽤

⑥ websocket

  • 官⽅说明:Scopes a single bean definition to the lifecycle of a WebSocket. Only valid in the context of a web-aware Spring ApplicationContext.
  • 描述:在⼀个HTTP WebSocket的⽣命周期中,定义⼀个Bean实例
  • 场景:WebSocket的每次会话中,保存了⼀个Map结构的头信息,将⽤来包裹客户端消息头。第⼀次初始化后,直到WebSocket结束都是同⼀个Bean。
  • 备注:限定Spring WebSocket中使⽤

⑦ 单例作用域(singleton)和全局作用域(application)区别

  • singleton 是 Spring Core 的作⽤域;application 是 Spring Web 中的作⽤域;
  • singleton 作⽤于 IoC 的容器,⽽ application 作⽤于 Servlet 容器。

2、设置作用域

我们可以使用 @Scope 标签来声明 Bean 的作用域,代码如下:

@Component
public class MyComponent {
//    @Scope("prototype")@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)@Bean(name = "c1")public void doComponent1() {System.out.println("Do user component1.");}
}

使用 @Scope 标签声明 Bean 作用域的两种方式:

  1. 直接输入作用域:@Scope(“prototype”)
  2. 采用枚举的方式:@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

注意

  • 在使用第一种方式的时候,要注意不能拼写错误;
  • 使用第二种方式就不容易写错(idea 自带提示).

3、案例修改

通过刚刚的学习,我们知道了:可以用 @Scope 标签来声明 Bean 作用域。我们之前也分析过,出现问题的原因,即 Spring 中默认为 单例作用域(singleton),那我们只需要将公共的 Bean 的作用域修改为 多例作用域(prototype)即可,修改如下:

@Component
public class CatBeans {@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)@Beanpublic Cat cat() {Cat cat = new Cat();cat.setId(1);cat.setName("喵喵喵");cat.setAge(8);return cat;}
}

此时我们再运行程序,结果为:
在这里插入图片描述

三、Bean 原理分析

1、Bean 执行流程

Bean 执⾏流程(Spring 执⾏流程):

  • 启动 Spring 容器
  • 实例化 Bean(分配内存空间,从⽆到有)
  • Bean 注册到 Spring 中(存操作)
  • 将 Bean 装配到需要的类中(取操作)
    在这里插入图片描述

2、Bean 生命周期

Bean 生命周期:

  1. 实例化(≠初始化):分配内存空间
  2. 设置属性:依赖注入 DI
  3. 初始化:
    1. 执行各种通知
    2. 初始化的前置方法:xml 文件里 定义的 init-method | @PostConstruct
    3. 初始化方法
    4. 初始化的后置方法
  4. 使用 Bean
  5. 销毁 Bean

① 实例化和初始化的区别

  • 实例化:是指创建一个对象的过程。这个过程中会在堆中开辟内存,将一些非静态的方法,变量存放在里面。在程序执行的过程中,可以创建多个对象,既多次实例化。每次实例化都会开辟一块新的内存。
  • 初始化:是完成程序执行前的准备工作。在这个阶段,静态的(变量,方法,代码块)会被执行。同时在会开辟一块存储空间用来存放静态的数据。初始化只在类加载的时候执行一次。

② 生命流程的“故事”

以买房为例:

  1. 买房(实例化)
  2. 装修(设置属性)
  3. 买家具(初始化)
  4. 入住(使用 Bean)
  5. 卖方(销毁 Bean)

③ 生命周期演示

//@Component
public class BeanLifeComponent implements BeanNameAware {public void setBeanName(String s) {System.out.println("执行了 Bean Name 通知:" +s);}/*** 方法名随意定义* xml中 init-method 指定的方法*/public void initMethod(){System.out.println("执行了 init-method 方法");}/*** 方法名随意定义*/@PostConstructpublic void myPostConstruct(){System.out.println("执行了 PostConstruct 方法");}/*** 销毁前执行方法*/@PreDestroypublic void myPreDestroy(){System.out.println("执行了 PreDestroy 方法");}public void run(){System.out.println("执行了 run 方法");}
}

xml配置:




调用类:

public class App {public static void main(String[] args) {ClassPathXmlApplicationContext context =new ClassPathXmlApplicationContext("spring-config.xml");BeanLifeComponent component =context.getBean("beanlife",BeanLifeComponent.class);component.run();context.destroy();}
}

在这里插入图片描述

④ 思考:为什么要先设置属性在进行初始化呢?

这就是因为,我们初始化过程中,有很多的方法,这些方法可能会调用某个属性,如果没有设置属性,就会报错(空指向异常)

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
苏州离哪个飞机场近(苏州离哪个... 本篇文章极速百科小编给大家谈谈苏州离哪个飞机场近,以及苏州离哪个飞机场近点对应的知识点,希望对各位有...
客厅放八骏马摆件可以吗(家里摆... 今天给各位分享客厅放八骏马摆件可以吗的知识,其中也会对家里摆八骏马摆件好吗进行解释,如果能碰巧解决你...