观前提示:本篇博客演示使用的 IDEA 版本为2021.3.3版本,使用的是Java8(又名jdk1.8)
前端使用VSCode(Visual Studio Code1.78.2)
电脑使用的操作系统版本为 Windows 10
目录
前言
Bean
Spring 容器在初始化⼀个 Bean 的实例时,同时会指定该实例的作⽤域。
1. singleton:单例作用域
1.1 原因
2. prototype:原型作用域(多例作用域)
2.1 设置作用域
3. request:请求作用域
4. session:回话作用域
5. application:全局作用域
6. websocket:HTTP WebSocket 作⽤域
本篇博客将对 Spring 里面的 Bean 的六种作用域进行介绍,重点介绍单例模式与原型模式, 后面四种是 Spring MVC 中的值, 后面的博客再介绍
先来看一个栗子
先创建一个 com.java.demo.enity 的包 里面放一个 user 类, 类里面有 id 和 name, 使用 lombok 来处理 Get, Set 以及 ToString
package com.java.demo.enity;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
public class User {
private int id;
private String name;
}
再建立一个 com.java.demo.component 的包, 里面放 UserBeans, 对 User 里面的数据进行初始化
package com.java.demo.component;
import com.java.demo.enity.User;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
@Component
public class UserBeans {
@Bean
public User user() {
// 伪代码, 用来初始化 user 类里面的 name 和 id
User user = new User();
user.setId(1);
user.setName("张三");
return user;
}
}
再建立一个 com.java.demo.controller 的包, 建立 userController 类, 在局部变量修改 Bean, 分别打印局部变量和全局变量,查看结果
package com.java.demo.controller;
import com.java.demo.enity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
//依赖注入,把 User 类型的 Bean 注入 UserController 属性中。
@Autowired
private User user;
public void printUser() {
// 先打印一次 user, 看看结果是什么
System.out.println(user);
//在局部变量里面对 Bean 进行修改
User myUser = user;
myUser.setName("李四");
//查看局部变量修改后局部变量的打印结果
System.out.println("myUser -> " + myUser);
//查看局部变量修改后全局变量的打印结果
System.out.println("user -> " + user);
}
}
再建立一个 com.java.demo.controller 的包, 建立 userController2 类 , 专门打印 Bean
package com.java.demo.controller;
import com.java.demo.enity.User;
import org.springframework.stereotype.Controller;
import javax.annotation.Resource;
@Controller
public class UserController2 {
@Resource
private User user;
public void printUser2() {
// 专门打印看看能不能拿到 张三
System.out.println("user -> " + user);
}
}
在创建一个 App 类,用来进行启动
import com.java.demo.controller.UserController;
import com.java.demo.controller.UserController2;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
UserController controller = context.getBean("userController", UserController.class);
controller.printUser();
UserController2 controller2 = context.getBean("userController2", UserController2.class);
controller2.printUser2();
}
}
上述代码总共进行了四次打印, 预估结果, 第一次打印:张三(全局变量), 第二次打印:李四(局部变量修改), 第三次打印:张三(局部变量修改不会影响全局变量)
第四次打印: 张三
但是结果真的是这样吗?
结果除了第一个符合预期, 后面的全部的不符合预期,这是为什么? 明明只是修改了局部变量,他却连全局变量都被修改了
因为 Spring默认是 单例模式, 翻译就是整个 Spring 里面的 Bean 都是同一个对象, 因为 Bean 只有一份,修改一次之后(不论修改的是全局还是局部),都是永久修改
为什么他默认是单例模式呢? , 当然是为了提高性能, 他只需要初始化一次, 同样 JVM 的类加载也只需要执行一次
不想要单例模式可以进行修改, 例如修改成多例作用域
要在设置的时候就对他进行修改,不能获取到了再修改, 所以是在 UserBeans
两种写法
1. 直接设置值:@Scope("prototype")
package com.java.demo.component;
import com.java.demo.enity.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
public class UserBeans {
@Bean
@Scope("prototype")
public User user() {
// 伪代码, 用来初始化 user 类里面的 name 和 id
User user = new User();
user.setId(1);
user.setName("张三");
return user;
}
}
2. 使⽤枚举设置:@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
package com.java.demo.component;
import com.java.demo.enity.User;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
public class UserBeans {
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public User user() {
// 伪代码, 用来初始化 user 类里面的 name 和 id
User user = new User();
user.setId(1);
user.setName("张三");
return user;
}
}
更加推荐使用第二种写法, 因为第一个错了不容易发现,第二个错了 idea 会提示
官⽅说明: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中使⽤
官⽅说明: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中使⽤
官⽅说明: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中使⽤
官⽅说明: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中使⽤
本文完,感谢观看,不足之处请在评论区指出 !