文章目录
- 前言
- 一.Bean的作用域
- 1.1 作用域例子
- 1.2 Bean的作用域类型
- 二.Bean的生命周期
前言
在前面我们学习了spring简单的读取和存储对象之后,Spring 中 Bean 是最核心的操作资源,我们接下来会介绍Bean对象.
一.Bean的作用域
什么是Bean作用域呢?
限定程序中变量的可用范围叫做作用域,或者说在源代码中定义变量的某个区域就叫做作用域。
而Bean 的作用域是指Bean 在 Spring整个框架中的某种行为模式.
我们具体用一个例子来说Bean的域.例子如下:
1.1 作用域例子
具体的场景如下:
假设现在有一个公共的Bean,提供给A用户和B用户使用,然而在使用的途中A用户却“悄悄"地修改了公共 Bean 的数据,导致B用户在使用时发生了预期之外的逻辑错误。
具体先看代码:
1.准备好具体的User类
public class User {
private int id;
private String name;
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;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
2.把User存入spring容器中
@Component
public class UserBeans {
@Bean
public User user(){
User user=new User();
user.setId(11);
user.setName("关羽");
return user;
}
}
3.这里有两个对象,分别对User对象做操作
对象一.
@Controller
public class UserController {
@Autowired
private User user;
public void printUser(){
System.out.println(user);
//修改User
User myUser=user;
myUser.setName("张飞");
System.out.println("myUser -> "+myUser);
System.out.println("user ->"+user);
}
}
对象二:
@Controller
public class UserController2 {
@Resource
private User user;
public void printUser2(){
System.out.println("user->" +user);
}
}
4.具体的操作
public class App {
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("spring-config.xml");
UserController userController= context.getBean("userController",UserController.class);
userController.printUser();
UserController2 userController2=context.getBean("userController2",UserController2.class);
userController2.printUser2();
}
}
执行结果:
其实这里的预期结果,我们第二次读取的操作的时候,应该打印的还是关羽的信息,但是现在出现了,张飞.按照正常的执行流程,我们应该出现的是关羽的信息.
这就是因为Bean对象是有作用域的.下面介绍Bean的作用域
1.2 Bean的作用域类型
在Spring中,Bean的作用域类型决定了Bean的生命周期和在应用程序中的可见性。Spring框架支持以下几种常见的Bean作用域类型:
-
Singleton(默认): 在整个应用程序中,只创建一个Bean实例。无论多少次请求该Bean,都返回同一个实例。这是Spring默认的作用域。可以通过@Scope(“singleton”)或者不添加@Scope注解来指定Singleton作用域。
-
Prototype: 每次请求Bean时,都会创建一个新的实例。每次获取Bean时,都会返回一个不同的实例。可以通过@Scope(“prototype”)来指定Prototype作用域。
-
Request: 每次HTTP请求都会创建一个新的Bean实例。在同一个请求中,多次获取Bean时都会返回同一个实例。通常用于Web应用程序中的每个请求,保证在同一个请求中的多次调用中都使用同一个Bean实例。可以通过@Scope(“request”)来指定Request作用域。
-
Session: 每个HTTP Session都会创建一个新的Bean实例。在同一个Session中,多次获取Bean时都会返回同一个实例。通常用于Web应用程序中,保证在同一个Session中的多次调用中都使用同一个Bean实例。可以通过@Scope(“session”)来指定Session作用域。
-
Application(仅适用于Web应用): 在整个Web应用中,只创建一个Bean实例。无论多少次请求该Bean,都返回同一个实例。通常用于Web应用程序中,保证在整个应用中都使用同一个Bean实例。可以通过@Scope(“application”)来指定Application作用域。
-
WebSocket是一种在Web浏览器和服务器之间进行全双工通信的协议,允许实时、双向的数据传输。WebSocket的作用域主要涉及在Web应用程序中与其他部分的通信范围。
因此知道了这些作用域之后,我们回到上面的案例,我们要怎么才能达到我们的预期效果呢.
@Scope标签既可以修饰方法也可以修饰类,@Scope有两种设置方式:
1.直接设置值:@Scope(“prototype”)
2.使用枚举设置:@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
代码如下:
@Component
public class UserBeans {
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Bean
public User user(){
User user=new User();
user.setId(11);
user.setName("关羽");
return user;
}
}
执行结果:
最后我们来做一个简单的总结
1.单例模式: singleton(默认模式)>性能的考虑2.原型模式: prototype
3.请求作用域: request,每次 HTTP请求,都会创建一个Bean 对象。【适用于Spring MVC/Spring Web】4.会话作用域: session,每次Session会话共亨一个 Bean。【Spring MVC】
5.全局作用域: appli cation,一个http servlet context中共享一个bean。【Spring MVC】6.webscoket:网络长连接,只适用于Spring WebSocket项日。
二.Bean的生命周期
Bean的生命周期主要包括以下几个阶段:
- 开辟内存空间:实例化≠初始化
- 设置属性(注入属性)
- 初始化
3.1各种通知
3.2初始化前置方法
3.3 初始化方法【两种实现方式: xml 方式、注解方式】
3.4 初始化后置方法 - 使用Bean
- 销毁Bean 对象
让我们以一个生活中的例子来演示这样的流程:购买一台新的电视。
-
开辟内存空间(实例化): 当你购买一台新的电视时,实际上是在商店里买到了一台特定型号的电视,这时候可以认为你在购买电视的过程中已经开辟了一块内存空间,这个空间对应于新的电视对象。
-
设置属性(注入属性): 购买电视后,你将其搬回家并连接到电源和其他设备(例如DVD播放器、游戏机等),这个过程类似于将电视的属性值进行设置和注入。
-
初始化:
各种通知: 开启电视后,它可能会显示欢迎信息或品牌标志,这是一种初始化阶段的通知。
初始化前置方法: 在使用电视之前,你可能需要对电视进行一些设置,如调整屏幕亮度、音量等,这可以看作是初始化前置方法。
初始化方法【两种实现方式:xml方式、注解方式】: 电视可以具有不同型号和功能,而对应于不同型号和功能,它们可能需要在初始化阶段进行不同的操作。例如,一些电视型号可能需要连接网络、搜索频道,这些可以通过在电视对象上调用初始化方法来实现。在Spring中,初始化方法可以通过在Bean定义中使用init-method属性或在Bean类上使用@PostConstruct注解来指定。
初始化后置方法: 在初始化阶段完成后,电视可以显示出预设频道或导航界面,这可以看作是初始化后置方法。 -
使用电视(使用Bean): 一旦电视初始化完成并连接好,你可以开始使用它来观看电视节目、播放DVD或玩游戏,这是电视被使用的阶段。
-
销毁电视(销毁Bean对象): 长期使用后,如果你决定不再使用这台电视,或者它出现了故障,你可能会将其处理掉或退还给商店。这个过程类似于销毁Bean对象,在Spring中,销毁Bean对象可以通过在Bean定义中使用destroy-method属性或在Bean类上使用@PreDestroy注解来指定。
综上所述,购买一台新的电视可以类比为Bean的生命周期。在整个过程中,我们可以类比内存空间的开辟(实例化)、设置电视的属性(注入属性)、电视的初始化阶段(包括各种通知、初始化前置方法、初始化方法和初始化后置方法)、使用电视(使用Bean)、销毁电视(销毁Bean对象)等。
然后用一个代码的例子说明:
代码如下:
package com.java.demo.component;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.config.BeanPostProcessor;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
public class BeanCompont implements BeanNameAware, BeanPostProcessor {
@Override
public void setBeanName(String s) {
System.out.println("执行了通知 BeanName -> " + s);
}
/**
* xml 方式的初始化方法
*/
public void myInit() {
System.out.println("XML 方式初始化");
}
@PostConstruct
public void doPostConstruct() {
System.out.println("注解初始化方法");
}
public void sayHi() {
System.out.println("执行 sayHi()");
}
@PreDestroy
public void doPreDestroy() {
System.out.println("do PreDestroy");
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("do postProcessBeforeInitialization");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("do postProcessAfterInitialization");
return bean;
}
}
具体执行:
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
BeanCompont compont = context.getBean("beanCompont", BeanCompont.class);
compont.sayHi();
context.destroy();
执行结果: