一.Bean的作用域.
1.1 Bean的相关概念.
-
通过Spring IoC和DI的学习(不清楚的可以看的前面写过的总结,可以快速入门, http://t.csdnimg.cn/K8Xr0),我们知道了Spring是如何帮助我们管理对象的
-
- 通过 @Controller , @Service , @Repository , @Component , @Configuration ,
@Bean 来声明Bean对象.
- 通过 @Controller , @Service , @Repository , @Component , @Configuration ,
-
- 通过 ApplicationContext 或者 BeanFactory 来获取对象
-
- 通过 @Autowired , Setter 方法或者构造方法等来为应用程序注入所依赖的Bean对象
-
-
通过代码来演示回顾一下:
创建需要注入的对象
java
public class Dog {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
把这个对象进行实例化,并且交给Spring进行管理
java
@Configuration
public class DogConfig {
@Bean
public Dog dog() {
Dog dog = new Dog();
dog.setName("旺财");
return dog;
}
}
通过ApplicationContext来获取对象.
java
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class SpringPrincipleApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(com.tuanzi.springprinciple.SpringPrincipleApplication.class, args);
Dog bean = context.getBean(Dog.class);
System.out.println(bean.getName());
}
}
也可以通过在代码中直接注入ApplicationContext的方式来获取Spring容器
java
@SpringBootTest
class SpringPrincipleApplicationTests {
@Autowired
public ApplicationContext context;
@Test
void contextLoads() {
Dog bean1 = context.getBean("dog", Dog.class);
System.out.println(bean1);
Dog bean2 = context.getBean("dog", Dog.class);
System.out.println(bean2);
}
}
根据运行结果我们可以看出, 这两次获取的对象是同一个对象, 默认情况下从Spring容器中取出来的对象是同一个
- 默认情况下, Spring容器中的bean都是单例的, 这种行为模式, 我们就称之为Bean的作用域
二.Bean的作用域
2.1 七种Bean的作用域
- Bean的作用域定义了Bean实例的生命周期及其在Spring容器中的可见性。
- 在Spring中支持6中作用域, 后四种在MVC环境下才能生效:
名称 | 作用域 | 说明 |
---|---|---|
单例作用域 | singleton | 每个Spring IoC容器内同名称的Bean只有一个实例(单例) 默认情况 |
原型作用域 | prototype | 每次使用该Bean的时侯,都会创建新的实例 |
请求作用域 | request | 每个HTTP请求都会创建一个新的Bean实例,请求结束时销毁 |
会话作用域 | session | 每个HTTP会话都会创建一个新的Bean实例,会话结束时销毁 |
全局作用域 | Application | 在ServletContext范围内,整个Web应用程序共享同一个Bean实例 |
HTTP WebSocket作用域 | websocket | 在ServletContext范围内,整个Web应用程序共享同一个Bean实例 |
全局作用域 | Application | 一个Bean定义对应于单个websocket的生命周期,仅在WebApplicationContext环境中有效 |
自定义作用域 | custom | 除了以上六种标准作用域,Spring还允许开发人员创建自定义作用域,通过实现Scope接口并注册到Spring容器中 |
参考文档: https://docs.spring.io/spring-framework/reference/core/beans/factory-scopes.html
2.2 代码演示.
2.2.1 单例作用域(singleton)
java
@Configuration
public class DogConfig {
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public Dog singletonDog() {
return new Dog();
}
}
java
@RestController
public class DogController {
@Autowired
private ApplicationContext applicationContext;
@Autowired
private Dog singletonDog;
@RequestMapping("/single")
public String single(){
Dog contextDog = applicationContext.getBean("singletonDog", Dog.class);
return "contextDog = " + contextDog+", "+"autowiredDog = " + singletonDog;
}
观察Bean的作用域. 单例作⽤域: http://127.0.0.1:8080/single
多次访问, 得到的都是同⼀个对象, 并且 @Autowired 和 applicationContext.getBean()
也是同⼀个对象
2.2.2 原型作用域( prototype)
java
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Dog prototypeDog() {
return new Dog();
}
java
@Autowired
private Dog prototypeDog;
@RequestMapping("/prototype")
public String prototype(){
Dog contextDog = applicationContext.getBean("prototypeDog", Dog.class);
return "contextDog = " + contextDog+", "+"autowiredDog = " + prototypeDog;
}
多例作用域: http://127.0.0.1:8080/prototype
观察ContextDog, 每次获取的对象都不一样==(注入的对象在Spring容器启动时, 就已经注入了, 所以多次请求也不会发生变化==)
2.2.3 请求作用域(request)
java
@Bean
@RequestScope
public Dog requestDog() {
return new Dog();
}
java
@Autowired
private Dog requestDog;
@RequestMapping("/request")
public String request(){
Dog contextDog = applicationContext.getBean("requestDog", Dog.class);
return "contextDog = " + contextDog+", autowiredDog = " + requestDog;
}
请求作用域: http://127.0.0.1:8080/request
在一次请求中, @Autowired 和 applicationContext.getBean() 也是同一个对象.但是每次请求(刷新浏览器当前页面), 都会重新创建对象.
2.2.4 会话作用域(session)
java
@Bean
@SessionScope
public Dog sessionDog() {
return new Dog();
}
java
@Autowired
private Dog sessionDog;
@RequestMapping("/session")
public String session(){
Dog contextDog = applicationContext.getBean("sessionDog", Dog.class);
return "contextDog = " + contextDog+", autowiredDog = " + sessionDog;
}
在一个session中, 多次请求, 获取到的对象都是同一个, 换一个浏览器访问, 发现会重新创建对象.(另一个Session)
Google浏览器运行结果:
Edge浏览器运行结果:
2.2.5 Application(全局作用域)
java
@Bean
@ApplicationScope
public Dog applicationDog() {
return new Dog();
}
java
@Autowired
private Dog applicationDog;
@RequestMapping("/application")
public String application(){
Dog contextDog = applicationContext.getBean("applicationDog", Dog.class);
return "contextDog = " + contextDog+", autowiredDog = " + applicationDog;
}
在一个应用中, 多次访问都是同一个对象(不同的浏览器也是同一个对象).
Application scope就是对于整个web容器来说, bean的作⽤域是ServletContext级别的. 这个和
singleton有点类似,==区别在于: Application scope是ServletContext的单例, singleton是⼀个
ApplicationContext的单例. 在⼀个web容器中ApplicationContext可以有多个. ==
Google浏览器运行结果:
Edge浏览器运行结果:
三.Bean的生命周期.
3.1 基本概念
- 生命周期指的是一个对象从诞生到销毁的整个生命过程, 我们把这个过程就叫做一个对象的生命周期.
- Bean 的生命周期分为以下5个部分:
-
1.实例化(为Bean分配内存空间)
-
2.属性赋值(Bean注入和装配, 比如 @AutoWired )
-
3.初始化
- a. 执行各种通知, 如 BeanNameAware , BeanFactoryAware ,ApplicationContextAware 的接口方法.
- b. 执行初始化方法
- xml定义 init-method
- 使用注解的方式 @PostConstruct
- 执行初始化后置方法( BeanPostProcessor )
-
- 使用Bean
-
- 销毁Bean
- a. 销毁容器的各种方法, 如 @PreDestroy , DisposableBean 接口方法, destroy method.
-
实例化和属性赋值对应构造⽅法和setter⽅法的注⼊. 初始化和销毁是⽤⼾能⾃定义扩展的两个阶段,
可以在实例化之后, 类加载完成之前进⾏⾃定义"事件"处理.
⽐如我们现在需要买⼀栋房⼦, 那么我们的流程是这样的:
- 先买房(实例化, 从⽆到有)
- 装修(设置属性)
- 买家电, 如洗⾐机, 冰箱, 电视, 空调等([各种]初始化,可以⼊住);
- ⼊住(使⽤ Bean)
- 卖房(Bean 销毁)
- 执⾏流程如下图所⽰:
3.2 代码实现.
java
@Component
public class BeanLifeComponent implements BeanNameAware {
private Dog singletonDog;
public BeanLifeComponent() {
System.out.println("执行构造函数....");
}
@Autowired
public void setSingletonDog(Dog singletonDog) {
this.singletonDog = singletonDog;
System.out.println("执行了setSingletonDog");
}
@Override
public void setBeanName(String name) {
System.out.println("setBeanName"+name);
}
@PostConstruct
public void init() {
System.out.println("执行PostConstruct方法");
}
public void use(){
System.out.println("执行use方法");
}
@PreDestroy
public void destroy(){
System.out.println("执行destroy");
}
}
执行结果:
通过运行结果观察
- 先执行构造函数
- 设置属性
- Bean初始化
- 使用Bean
- 销毁Bean
3.3 生命周期的源码阅读.
- 创建Bean的代码入口在 AbstractAutowireCapableBeanFactory#createBean
== 点进去继续看源码: AbstractAutowireCapableBeanFactory#createBean ==
java
//---------------------------------------------------------------------
// Implementation of relevant AbstractBeanFactory template methods
//---------------------------------------------------------------------
/**
* Central method of this class: creates a bean instance,
* populates the bean instance, applies post-processors, etc.
* @see #doCreateBean
*/
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
if (logger.isTraceEnabled()) {
logger.trace("Creating instance of bean '" + beanName + "'");
}
RootBeanDefinition mbdToUse = mbd;
// Make sure bean class is actually resolved at this point, and
// clone the bean definition in case of a dynamically resolved Class
// which cannot be stored in the shared merged bean definition.
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
mbdToUse = new RootBeanDefinition(mbd);
mbdToUse.setBeanClass(resolvedClass);
try {
mbdToUse.prepareMethodOverrides();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
beanName, "Validation of method overrides failed", ex);
}
}
try {
// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
}
catch (Throwable ex) {
throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
"BeanPostProcessor before instantiation of bean failed", ex);
}
try {
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isTraceEnabled()) {
logger.trace("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
// A previously detected exception with proper bean creation context already,
// or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(
mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
}
}
三方法与三个生命周期阶段一一对应
- createBeanInstance() -> 实例化
- populateBean() -> 属性赋值
- initializeBean() -> 初始化
继续点进去 initializeBean
java
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
invokeAwareMethods(beanName, bean);
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null), beanName, ex.getMessage(), ex);
}
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
private void invokeAwareMethods(String beanName, Object bean) {
if (bean instanceof Aware) {
if (bean instanceof BeanNameAware beanNameAware) {
beanNameAware.setBeanName(beanName);
}
if (bean instanceof BeanClassLoaderAware beanClassLoaderAware) {
ClassLoader bcl = getBeanClassLoader();
if (bcl != null) {
beanClassLoaderAware.setBeanClassLoader(bcl);
}
}
if (bean instanceof BeanFactoryAware beanFactoryAware) {
beanFactoryAware.setBeanFactory(AbstractAutowireCapableBeanFactory.this);
}
}
}
可以看到,调用了三个Bean开头的Aware方法.
四. 总结
- Bean的作用域共分为6种: singleton, prototype, request, session, application和websocket.
- Bean的生命周期共分为5大部分: 实例化, 属性复制, 初始化, 使用和销毁