一.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对象
-
-
通过代码来演示回顾一下:
创建需要注入的对象
public class Dog {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}
}
把这个对象进行实例化,并且交给Spring进行管理
@Configuration
public class DogConfig {@Beanpublic Dog dog() {Dog dog = new Dog();dog.setName("旺财");return dog;}
}
通过ApplicationContext来获取对象.
@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容器
@SpringBootTest
class SpringPrincipleApplicationTests {@Autowiredpublic ApplicationContext context;@Testvoid 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)
@Configuration
public class DogConfig {@Bean@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)public Dog singletonDog() {return new Dog();}
}
@RestController
public class DogController {@Autowiredprivate ApplicationContext applicationContext;@Autowiredprivate 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)
@Bean@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)public Dog prototypeDog() {return new Dog();}
@Autowiredprivate 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)
@Bean@RequestScopepublic Dog requestDog() {return new Dog();}
@Autowiredprivate 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)
@Bean@SessionScopepublic Dog sessionDog() {return new Dog();}
@Autowiredprivate 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(全局作用域)
@Bean@ApplicationScopepublic Dog applicationDog() {return new Dog();}
@Autowiredprivate 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 代码实现.
@Component
public class BeanLifeComponent implements BeanNameAware {private Dog singletonDog;public BeanLifeComponent() {System.out.println("执行构造函数....");}@Autowiredpublic void setSingletonDog(Dog singletonDog) {this.singletonDog = singletonDog;System.out.println("执行了setSingletonDog");}@Overridepublic void setBeanName(String name) {System.out.println("setBeanName"+name);}@PostConstructpublic void init() {System.out.println("执行PostConstruct方法");}public void use(){System.out.println("执行use方法");}@PreDestroypublic void destroy(){System.out.println("执行destroy");}
}
执行结果:
通过运行结果观察
- 先执行构造函数
- 设置属性
- Bean初始化
- 使用Bean
- 销毁Bean
3.3 生命周期的源码阅读.
- 创建Bean的代码入口在 AbstractAutowireCapableBeanFactory#createBean
== 点进去继续看源码: AbstractAutowireCapableBeanFactory#createBean ==
//---------------------------------------------------------------------// 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*/@Overrideprotected 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
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大部分: 实例化, 属性复制, 初始化, 使用和销毁