Bean 的作用域是指 Bean 在 Spring 整个框架中的某种行为模式。Spring Bean的常见作用域,后3种作用域,只适用于Spring MVC框架:
singleton
(Default) Scopes a single bean definition to a single object instance for each Spring IoC container.
描述:该作用域下的 Bean 在 IoC 容器中只存在一个实例:获取 Bean(即通过 applicationContext.getBean等方法获取)及装配 Bean(即通过 @Autowired 注入)都是同一个对象。
场景:通常无状态的 Bean 使用该作用域。无状态表示 Bean 对象的属性状态不需要更新。
备注:Spring 默认选择该作用域。singleton 单例作用域,就表示 Bean 在整个 Spring 中只有一份,它是全局共享的,当有人修改了这个值之后,那么另一个人读取到的就是被修改后的值。
prototype
Scopes a single bean definition to any number of object instances.
描述:每次对该作用域下的 Bean 的请求都会创建新的实例:获取 Bean(即通过applicationContext.getBean
等方法获取)及装配 Bean(即通过 @Autowired 注入)都是新的对象实例。
场景:通常有状态的 Bean 使用该作用域。
request
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。
备注:限定 Spring MVC 框架中使用。
session
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, 比如:记录一个用户的登陆信息。
备注:限定 Spring MVC 框架中使用。
application
Scopes a single bean definition to the lifecycle of a global HTTP Session. Typically only valid when used in a portlet context. Only valid in the context of a web-aware Spring ApplicationContext.
描述:在一个 Http Servlet Context 中,定义一个 Bean 实例。
场景:Web 应用的上下文信息,比如:记录一个应用的共享信息。
备注:限定 Spring MVC 框架中使用。
websocket:bean被定义为在websocket的生命周期中复用一个单例对象,不太常见。
可通过@Scope
设置Bean的作用域:
@Scope("prototype")
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Scope(WebApplicationContext.SCOPE_APPLICATION)
如果一个bean被声明为单例的时候,在处理多次请求的时候在Spring容器里只实例化出一个bean,后续的请求都公用这个对象,这个对象会保存在一个map里面。当有请求来的时候会先从缓存(map)里查看有没有,有的话直接使用这个对象,没有的话才实例化一个新的对象,所以这是个单例的。但是对于原型(prototype)bean来说当每次请求来的时候直接实例化新的bean,没有缓存以及从缓存查的过程。
AbstractBeanFactory.doGetBean()
源码找到证据:
// Create bean instance.
if (mbd.isSingleton()) {sharedInstance = getSingleton(beanName, () -> {try {return createBean(beanName, mbd, args);}catch (BeansException ex) {// Explicitly remove instance from singleton cache: It might have been put there// eagerly by the creation process, to allow for circular reference resolution.// Also remove any beans that received a temporary reference to the bean.destroySingleton(beanName);throw ex;}});bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
} else if (mbd.isPrototype()) {// It's a prototype -> create a new instance.Object prototypeInstance = null;try {beforePrototypeCreation(beanName);prototypeInstance = createBean(beanName, mbd, args);}finally {afterPrototypeCreation(beanName);}bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
} else {String scopeName = mbd.getScope();if (!StringUtils.hasLength(scopeName)) {throw new IllegalStateException("No scope name defined for bean ´" + beanName + "'");}Scope scope = this.scopes.get(scopeName);if (scope == null) {throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");}try {Object scopedInstance = scope.get(beanName, () -> {beforePrototypeCreation(beanName);try {return createBean(beanName, mbd, args);}finally {afterPrototypeCreation(beanName);}});bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);}catch (IllegalStateException ex) {throw new BeanCreationException(beanName,"Scope '" + scopeName + "' is not active for the current thread; consider " +"defining a scoped proxy for this bean if you intend to refer to it from a singleton", ex);}
}
源码跟进去,DefaultSingletonBeanRegistry
里面定义有如下Map:
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {/** Cache of singleton objects: bean name to bean instance. */private final Map singletonObjects = new ConcurrentHashMap<>(256);
singleton bean优势,也是Spring为啥默认把bean设计成单例:
劣势
不能保证线程安全。所有请求都共享一个bean实例,有状态bean在并发场景下有线程安全问题。原型bean,因为给每个请求都新创建实例,则不会有这样问题(但也有例外,如被单例bean依赖)。
Bean的生命周期是指:Bean 在 Spring(IoC)中从创建到销毁的整个过程,主要包含5部分:
完整涉及到接口和类的详细版的生命周期:
实例化只是给 Bean 分配内存空间,而初始化则是将程序的执行权,从系统级别转换到用户级别,并开始执行用户业务代码。
singleton bean被定义为一个单例对象,该对象的生命周期是与Spring IOC容器一致的(但在第一次被注入时才会创建)
方法:
setup,在容器加载bean时被调用,teardown在容器卸载类时被调用。bean标签有两个重要属性(init-method和destroy-method),可用于定制初始化和注销方法,有相应的注解(@PostConstruct和@PreDestroy)。
有时需要在Bean的初始化中使用Spring框架自身的一些对象来执行一些操作,如获取ServletContext的参数,ApplicaitionContext中的BeanDefinition的名字,Bean在容器中的名字等。为了让Bean可以获取到框架自身的一些对象,Spring提供一组名为*Aware
的接口。
这些接口均继承于org.springframework.beans.factory.Aware
标记接口,并提供一个将由Bean实现的set*
方法,Spring通过基于setter的依赖注入方式使相应的对象可以被Bean使用。这些接口是利用观察者模式实现的,类似于servlet listeners。
Aware接口:
代码:
@Slf4j
public class DemoService implements ApplicationContextAware,ApplicationEventPublisherAware, BeanClassLoaderAware, BeanFactoryAware,BeanNameAware, EnvironmentAware, ImportAware, ResourceLoaderAware{@Overridepublic void setBeanClassLoader(ClassLoader classLoader){log.info("执行setBeanClassLoader,ClassLoader Name = " + classLoader.getClass().getName());}@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {log.info("执行setBeanFactory,setBeanFactory:: giraffe bean singleton=" + beanFactory.isSingleton("giraffeService"));}@Overridepublic void setBeanName(String s) {log.info("执行setBeanName:: Bean Name defined in context=" + s);}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {log.info("执行setApplicationContext:: Bean Definition Names=" + Arrays.toString(applicationContext.getBeanDefinitionNames()));}@Overridepublic void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {log.info("执行setApplicationEventPublisher");} @Overridepublic void setEnvironment(Environment environment) {log.info("执行setEnvironment");}@Overridepublic void setResourceLoader(ResourceLoader resourceLoader) {Resource resource = resourceLoader.getResource("classpath:spring-beans.xml");log.info("执行setResourceLoader:: Resource File Name=" + resource.getFilename()); }@Overridepublic void setImportMetadata(AnnotationMetadata annotationMetadata) {log.info("执行setImportMetadata");}
}
BeanPostProcessor
上面的*Aware接口是针对某个实现这些接口的Bean定制初始化的过程,Spring同样可以针对容器中的所有Bean,或者某些Bean定制初始化过程,只需提供一个实现BeanPostProcessor接口的类即可。 该接口中包含两个方法,postProcessBeforeInitialization和postProcessAfterInitialization。 postProcessBeforeInitialization方法会在容器中的Bean初始化之前执行, postProcessAfterInitialization方法在容器中的Bean初始化之后执行:
@Slf4j
@Componet
public class DemoBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {log.info("执行BeanPostProcessor的postProcessBeforeInitialization方法,beanName=" + beanName);return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {log.info("执行BeanPostProcessor的postProcessAfterInitialization方法,beanName=" + beanName);return bean;}
}
问题:
能不能先执行初始化再执行设置属性呢?步骤2和3的执行顺序交换一下?