文章目录
- 一、通过案例来简单体会一下Bean的作用域问题
- 二、作用域定义
- 三、Bean的作用域分类
- singleton
- prototype
- request
- session
- application(了解)
- singleton(单例作用域) 和 application (全局作用域)的区别
- websocket(了解)
- 四、Bean作用域设置
- 总结
一、通过案例来简单体会一下Bean的作用域问题
User:
class User {
private String name;
public User() {
System.out.println("加载User");
}
public String sayHi() {
return "Hello Dada!";
}
}
public
🧡注意:
@Data
是Lombok
注解,作用大家可以自行了解一下,使用这个注解可以省去实体类中大量的get()
、set()
、toString()
等方法。
公共Bean:
class UserBean {
public User user() {
User user = new User();
user.setName("哒哒");
System.out.println("Bean原名:" + user.getName());
return user;
}
}
public
第一个用户使用公共Bean时,进行了修改操作:
class ModifyUser {
private User user1;
public User getUser() {
User user = user1;
user.setName("阿巴阿巴");
return user;
}
}
public
第二个用户使用公共Bean时:
class ReferUser {
private User user1;
public User getUser() {
User user = user1;
return user;
}
}
public
打印第一个用户和第二个用户公共 Bean 的值:
public class Demo2 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
ModifyUser modifyUser = context.getBean("modifyUser",ModifyUser.class);
System.out.println("第一位用户修改user: " + modifyUser.getUser());
ReferUser referUser = context.getBean("referUser",ReferUser.class);
System.out.println("第二位用户就是看看user: " +referUser.getUser());
}
结果:
可以看见,原来User
的name
是"哒哒",现在是"阿巴阿巴",公共Bean的值发生了改变。
原因有两点:
ModifyUser
的getUser()
表面创建了一个新的User
(User user = user1;
)但只是把user
指向了user1
,实际上修改的还是公共Bean。Bean
默认状态下是单例状态(singleton
),也就是全局只有这一个Bean,所有人用的都是这一个对象,不再创建新的对象,因为使用单例可以很大程度上提高性能,所以在Spring
中,Bean
的作用域默认也是singleton
单例模式。
二、作用域定义
这是我们之前对于作用域的理解:
限定程序中变量的可用范围叫做作用域,或者说在源码中定义变量的某个区域就叫做作用域。
而Bean
的作用域是指Bean
在Spring
整个框架中的某种行为模式,比如singleton
单例作用域,就表示Bean
在整个Spirng
中只有一份,它是全局共享的,如果别人修改了这个Bean
,另外的人读到的就是被修改的Bean
了。
那么Bean的作用域也不单单只有singleton单例作用域这一种。
三、Bean的作用域分类
singleton
- 描述:
singleton
作用域下的Bean
在IoC
容器中只存在一个实例: 获取Bean
(使用applicationContext.getBean
等方法获取)及装配Bean
(使用@Autowired
或@Resource
)都是同一个对象。 - 场景: 通常是当所有人调用这个
Bean
都只是调用,而不是修改时,就使用singleton
(通常是无状态的Bean使用该作用域。) Spring
默认使用该作用域
prototype
原型模式(多例模式)。
通俗的理解就是本身是啥样就是啥样,比如上面的ModifyUser
一开始和ReferUser
都是拿到它原生的样子,本来等于“哒哒”的样子。
要保证每次都是原生的样子,那每次请求注入这个对象,都会new
一个新对象,就是克隆(深克隆)一个原型的对象,所以我每次拿到的都是一个新的对象,要修改就是在这个新对象上进行修改,不会影响我的原型。
- 描述: 每次对该作用域下的Bean请求都会创建新的实例:获取
Bean
(使用applicationContext.getBean
等方法获取)及装配Bean
(使用@Autowired
或@Resource
)都是新的对象实例。 - 场景: 当有人调用这个
Bean
会进行修改时,就使用prototype
。(通常是有状态的Bean使用该作用域。)
request
请求作用域。
- 描述: 每次
http
请求都会创建新的Bean
实例,类似于prototype
- 场景: 一次
http
请求和响应共享一个Bean
,来回的过程这个Bean
是共享的 - 限定在
SpringMVC
中使用,就是Spring
的Web
项目。
类似于Servlet,在Servlet上进行了封装。当使用ajax或使用浏览器时,访问一个地址,可以直接触发到程序里的某一个方法。
session
会话作用域。
- 描述: 在一个
http session
中,定义一个Bean
实例 - 场景: 用户会话的共享
Bean
,比如记录一个用户的登录信息 - 限定
SpringMVC
中使用
比如我登陆了一个账号,那就是一次会话请求。
然后在整个操作期间,比如使用了30min,使用的都是一个账号,那么这个Bean
在我的会话当中是一直共享的,此时另一个人登了另外账号,那么这个另外的账号和我的账号在逻辑上就是隔离的,不能相互共享的。每个会话都有自己的Bean
。
跟多线程中的ThreadLocal
(每个线程都会有自己的变量)比较像。
application(了解)
全局作用域。
- 描述:在一个
http servlet Context
中,定义一个Bean
实例 - 场景:
Web
应用的上下文信息,比如,记录一个应用的共享信息 - 备注:限定
SpringMVC
中使用
如:
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
这一个ApplicationContext
就是一个http servlet
对象,如果此时我又新new了一个ApplicationContext
,那么这两个对象信息就是不共享的,但一个ApplicationContext
里面的信息是共享的。
singleton(单例作用域) 和 application (全局作用域)的区别
- 本身的应用范围是不一样的,
singleton
应用在Spring Core
里面,application
应用在SpringMVC
里面 singleton
作用于IoC
容器,而application
作用于Servlet
容器
websocket(了解)
socket通讯是应用层的协议,底层可以通过TCP通讯,也可以通过UDP通讯
- 描述: 在一个
HTTP WevSocket
的声明周期中,定义一个Bean
实例 - 场景:
WebSocket
的每次会话中,保存了一个Map
结构的头信息,将用来包裹客户端消息头。第一次初始化后,直到WebSocket
结束都是同一个Bean
。 - 在
Spring WebSocket
中使用
四、Bean作用域设置
设置成原型模式:
- 通过全局变量设置:
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
- 直接通过值设置:
@Scope("prototype")
在存Bean对象的时候,在Bean上加作用域。