目录
singleton单例作用域和application作用域的区别
1.通过一个案例来了解bean的作用域
假设现在有一个公共的bean,供A B 两个用户来使用,但是在使用过程中,由于A修改了bean的值,导致下一次B所读到的值是已经被修改的值。这就是由于bean的作用于是全局共享,出现了这个问题。
公共的Bean:
@Component
public class Users {
@Bean
public User user1() {
User user = new User();
user.setId(1);
user.setName("Java"); // 【重点:名称是 Java】
return user;
}
}
A用户使用的同时修改了Bean:
@Controller
public class BeanScopesController {
@Autowired
private User user1;
public User getUser1() {
User user = user1;
System.out.println("Bean 原 Name:" + user.getName());
user.setName("悟空"); // 【重点:进行了修改操作】
return user;
}
}
B用户再次使用Bean:
@Controller
public class BeanScopesController2 {
@Autowired
private User user1;
public User getUser1() {
User user = user1;
return user;
}
}
打印A B两个用户此时所拥有公共Bean的值:
public class BeanScopesTest {
public static void main(String[] args) {
ApplicationContext context = new
ClassPathXmlApplicationContext("spring-config.xml");
BeanScopesController beanScopesController =
context.getBean(BeanScopesController.class);
System.out.println("A 对象修改之后 Name:" +
beanScopesController.getUser1().toString());
BeanScopesController2 beanScopesController2 =
context.getBean(BeanScopesController2.class);
System.out.println("B 对象读取到的 Name:" +
beanScopesController2.getUser1().toString());
}
}
结果:
此时由于这个公共的Bean对象的作用域默认是单例模式,所有人使用的都是同一个对象,所有对象都可以修改,因此发生了变化,那么Bean的作用域有哪些呢?我们接着往下看!
2.Bean的作用域
Spring容器在初始化一个Bean对象时会,同时会指定该Bean的作用域,Spring的作用域有如下六种:
- singleton:单例作用域
- prototype:原型作用域
- request:请求作用域
- session:回话作用域
- applicaition:全局作用域
- websocket:HTTP websocket作用域
注意 :在普通的Spring 中只有前两种,后四个是在Spring mvc的作用域
1.singleton 单例作用域(spring 的默认)
描述:在该作用域下的Bean对象在lOC容器中之村在一个实例对象,获取Bean及配装修改Bean都是同一个对象
场景:通常无状态的Bean使用该作用域,无状态是指Bean的属性不会发生变化
2.prototype 原型作用域
描述:在该作用域下,每次使用Bean对象都会创建新的实例,获取Bean和配装修改Bean都是新的实例,不是同一个对象。
场景:通常使用的是有状态的Bean
3.request 请求作用域
描述:每一个http请求会创建新的Bean实例
场景:一次请求和响应共享一个Bean,该作用域只限定于Spring mvc
4.session 回话作用域
描述:在一个http session 中创建一个Bean实例
场景:用户会话的共享Bean,比如记录一个用户的登录信息,该作用域只限定于Spring mvc
5.applicaiton 全局作用域
描述:在一个http servlet context 中定义一个Bean实例
场景:web应用的上下文信息,该作用域只限定于Spring mvc
6.websocket 作用域
描述:在一个HTTP WebSocket的生命周期中,定义一个Bean实例
场景:WebSocket的每次会话中,保存了一个Map结构的头信息,将用来包裹客户端消息头。第一次初始化后,直到WebSocket结束都是同一个Bean。
singleton单例作用域和application作用域的区别
singleton是spring core的作用域;application是spring web 的作用域
singleton作用于lOC容器当中,application作用域servlet容器当中
3.Bean的执行流程
要了解Bean的生命周期,我们先来了解Bean是如何执行的:
所以Bean的执行流程:启动Spring容器 -> 实例化Bean(分配内存空间,从无到有)-> Bean 注册到 Spring 中(存操作) -> 将 Bean 装配到需要的类中(取操作)。
4.Bean的生命周期
所谓的生命周期指的是一个对象从诞生到销毁的整个生命过程,我们把这个过程就叫做一个对象的生命周期。
Bean 的生命周期分为以下 5 大部分:
- 实例化 Bean(为 Bean 分配内存空间):通过反射调用构造方法来实现实例化
- 设置属性:Bean 注入和装配
- Bean 初始化
- 实现了各种 Aware 通知的方法如:BeanNameAware、BeanFactoryAware、ApplicationContextAware 的接口方法;
- Bean对象初始化前,循环调用了BeanPostProcessor接口的预初始化方法(postProcessBeforeInitialization);
- 执行 @PostConstruct 初始化方法,依赖注入操作之后被执行;
- 执行自己指定的 init-method 方法(如果有指定的话);
- Bean对象初始化后,循环调用了BeanPostProcessor接口的后初始化方法(postProcessAfterInitialization)
- 使用 Bean
- 销毁 Bean 销毁容器的各种方法,如
- @PreDestroy、DisposableBean 接口方法、destroy-method。
具体的执行流程如下图:
5.生命周期演示
package com.example.lifecycle;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@Component
public class BeanLifeComponent implements BeanNameAware {
@PostConstruct
public void postConstruct() {
System.out.println("执行 PostConstruct()");
}
public void init() {
System.out.println("执行 BeanLifeComponent init-method");
}
@PreDestroy
public void preDestroy() {
System.out.println("执行:preDestroy()");
}
public void setBeanName(String s) {
System.out.println("执行了 setBeanName 方法:" + s);
}
}
主方法:
package com.example;
import com.example.lifecycle.BeanLifeComponent;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainLifeCycle {
public static void main(String[] args) {
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
BeanLifeComponent life = context.getBean(BeanLifeComponent.class);
System.out.println("执行 main 方法");
// 执行销毁方法
context.destroy();
}
}