Java开发学习之Bean的作用域和生命周期详解

网友投稿 431 2022-07-22


目录一、Bean 的作用域二、Spring 的执行流程三、Bean 的生命周期

一、Bean 的作用域

在之前学习java基础的时候,有接触到作用域这样的概念。一个变量并不一定在任何区域都是有效的,限定这个变量的可用性的代码范围就是该变量的作用域。

但是在这里 Bean 的作用域的概念和以前所认为的作用域有所不同。

Bean 的作用域是指 Bean 在 Spring 整个框架中的某种行为模式。

接下来,将会举一个案例来讲讲什么是作用域,什么是行为模式

案例概要:

创建一个共有的 Bean ,使用者A和使用者B都对该 Bean 进行了使用

使用者A在进行使用的时候,创建一个新的变量接收注入进来的 Bean,进行修改,将修改后的结果进行返回;

使用者B 直接将注入进来的 Bean 进行返回,不进行任何操作

代码实现:

步骤一:创建出一个公共的 Bean

@Component

public class UserCompnXXsQXhCjonent {

@Bean

public User getUser() {

User user = new User();

user.setId(1);

user.setName("张三");

user.setPassWord("111111");

return user;

}

}

步骤二:使用者A获取到公共 Bean,进行修改

@Controller

public class UserControllerA {

@Autowired

private User user;

public User getUser1() {

System.out.println("使用者A拿到的原始user:" + user);

User myUser = user;

myUser.setName("李四");

return myUser;

}

}

步骤三:使用者B直接将获取到的公共的 Bean 进行返回

@Controller

public class UserControllerB {

@Autowired

private User user;

public User getUser2() {

return user;

}

}

步骤四:main 中获取 UserControllerA 类和 UserControllerB 类使用查看

public class Start {

public static void main(String[] args) {

//获取 Spring 上下文

ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");

//获取到Spring容器中的 UserControllerA 类(Bean 对象)

UserControllerA userControllerA = context.getBean("userControllerA",UserControllerA.class);

//使用 Bean 对象

System.out.println("使用者A->"+userControllerA.getUser1());

//获取到Spring容器中的 UserControllerA 类(Bean 对象)

UserControllerB userControllerB = context.getBean("userControllerB",UserControllerB.class);

//使用 Bean 对象

System.out.println("使用者B->"+userControllerB.getUser2());

}

}

预期结果:

使用者 A修改后,其结果是修改过的;使用者 B 没有修改过,其结果应该和原始user一样

结果显示:

和预期结果有所不同,使用者 A 和使用者 B 所得到的结果都是被修改过的。

这是因为在 Spring 中,Bean 默认情况下是单例状态,大家用的都是同一份对象,是全局共享的,当有其他人修改了该对象,另一个人所获取到的对象就是被修改过的,这便是 Bean 六大作用域之一——单例作用域(singleton)

在写 WEB 项目的时候,我们知道 DataSource 就是单例模式,使用单例,好处多多,可以确保所有对象都访问唯一实例,而且减少了内存的开支和系统性能的开销,因此 Bean 默认情况下是单例状态。

若想要按照预期结果输出,就需要将 Bean 的作用域设置成原型作用域,即无论谁来使用 Bean 对象,获取到的都是原始的 Bean 对象,大家各玩各的。

需要在注入的对象上使用注解修改作用域,有以下两种方法(Scope就是作用域的意思)

直接将作用域以 String 类型写到()中使用全局参数,类似于枚举

@Component

public class UserComponent {

//@Scope("prototype") //方法一

@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) //方法二

@Bean

public User getUser() {

User user = new User();

user.setId(1);

user.setName("张三");

user.setPassWord("111111");

return user;

}

}

Bean 的 6 种作用域

singleton:单例作用域

Bean 在 IoC 容器中只存在一个实例当 Bean 对象属性状态无需更新时使用该作用域Spring 支持

prototype:原型作⽤域/多例作⽤域

每次获取该 Bean 时都会创建新的实例,即获取的都是原始的 Bean当 Bean 对象属性状态会更新时使用该作用域Spring 支持

request:请求作用域

每次 HTTP 请求都会创建新的 Bean 实例一次 HTTP 请求和响应共享的 Bean限定 SpringMVC

session:会话作用域

在一个 HTTP session中,创建一个 Bean 实例用户会话共享一个 Bean限定 SpringMVC

application:全局作用域

在一个 HTTP Servlet Context中,创建一个 Bean 实例一个 WEB 上下文中共享一个 Bean限定 SpringMVC

websocket: HTTP WebSocket 作⽤域(不常用)

在一个 HTTP WebSocket 中,创建一个 Bean 实例限定 Spring WebSocket

单例作用域和全局作用域比较像,但全局作用域范围没有单例作用域大,前者是 Spring 核心的作用域,后者是 Spring Web 中的作用域,前者作用于 IoC 容器,后者作用于 Servlet 容器

二、Spring 的执行流程

Spring 的执行流程也可以说是 Bean的执行流程,主要分成4部分

启动 Spring 容器加载 XML 文件,实例化 Bean(进行内存的分配)Bean 存到 Spring 容器中(五大类注解,方法注解)将存储的 Bean 中的注入的对象属性进行初始化,即进行装配(取出 Bean)

三、Bean 的生命周期

Bean 的生命周期即 Bean 从诞生到销毁的整个过程

实例化 Bean 对象,申请内存空间

设置 Bean 的属性,进行依赖注入和装配

Bean 的初始化

各种 Aware 感知:BeanNameAware、BeanFactoryAware、ApplicationContextAware、…执⾏ BeanPostProcessor 初始化前置⽅法初始化方法:构造器方法 @PostConstructor (对象加载完依赖注入后执行)初始化方法:init-method执⾏ BeanPostProcessor 初始化后置⽅法

使用 Bean

销毁 Bean

销毁方法:@PreDestroy接口方法:DisposableBean销毁方法:destroy-method

补充

实例化和初始化的区别

实例化:这里的实例化和从前的实例化对象是有区别的,这里的实例化就负责内存的分配,一个从无到有的过程。实例化和属性设置时 Java 级别的系统“事件”,操作过程是不可人工干预的初始化:是对 Bean 进行填充的过程,程序员可以进行介入,真正的将 Bean 放到 Spring 容器中

@PostConstructor 和 @PreDestroy 是注解方式进行初始化和注销,init-method 和 destroy-method 是 XML 方法进行初始化和注销,一般只要使用其中的一种进行初始化

设置属性一定要在初始化之前,因为初始化也可能需要使用到注入的对象,如果没有进行属性的设置,初始化就会出现问题

案例:生命周期演示

@Component

public class BeanLife implements BeanNameAware {

@Override

public void setBeanName(String s) {

System.out.println("BeanName 感知:"+ s);

}

@PostConstruct

public void postConstructor() {

System.out.println("执行初始化方法:PostConstructor");

}

@PreDestroy

public void preDestroy() {

System.out.println("执行销毁方法:PreDestroy");

}

public void initMethod() {

System.out.println("执行初始化方法:init-method");

}

public void destroyMethod() {

System.out.println("执行销毁方法:destroy-method");

}

}

XML

xmlns:xsi="http://w3.org/2001/XMLSchema-instance"

xmlns:content="http://springframework.org/schema/context"

xsi:schemaLocation="http://springframework.org/schema/beans http://springframework.org/schema/beans/spring-beans.xsd http://springframework.org/schema/context https://springframework.org/schema/context/spring-context.xsd">

destroy-method="destroyMethod">

xmlns:xsi="http://w3.org/2001/XMLSchema-instance"

xmlns:content="http://springframework.org/schema/context"

xsi:schemaLocation="http://springframework.org/schema/beans http://springframework.org/schema/beans/spring-beans.xsd http://springframework.org/schema/context https://springframework.org/schema/context/spring-context.xsd">

destroy-method="destroyMethod">

destroy-method="destroyMethod">

调用

public class Start {

public static void main(String[] args) {

ClassPathXmlApplicationContext context = new

ClassPathXmlApplicationContext("spring.xml");

BeanLife beanLife = context.getBean("beanLife",BeanLife.class);

System.out.println("---使用 Bean 对象---");

System.out.println("---注销 Bean 对象--- ");

context.destroy();//容器的销毁相当于销毁所有的 Bean

}

}

结果显示:

流程图展示:


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:idea聚合工程搭建过程详解
下一篇:SpringBoot项目配置明文密码泄露问题的处理方式
相关文章

 发表评论

暂时没有评论,来抢沙发吧~