20191225 Spring官方文档(Core 1.5)
1.5。Bean Scopes
创建bean定义时,将创建一个配方,用于创建该bean定义所定义的类的实际实例。Bean定义是配方的想法很重要,因为它意味着与类一样,您可以从一个配方中创建许多对象实例。
Spring Framework支持六个作用域,四个仅在Web环境的Spring ApplicationContext中可用。您还可以创建自定义作用域。
范围 | 描述 |
---|---|
singleton | (默认)将每个Spring IoC容器的单个bean定义范围限定为单个对象实例。 |
prototype | 将单个bean定义的作用域限定为任意数量的对象实例。 |
request | 将单个bean定义的范围限定为单个HTTP请求的生命周期。也就是说,每个HTTP请求都有一个在单个bean定义后面创建的bean实例。仅在Web环境的Spring ApplicationContext中可用。 |
session | 将单个bean定义的范围限定为HTTP Session的生命周期。仅在Web环境的Spring ApplicationContext中可用。 |
application | 将单个bean定义的作用域限定为ServletContext的生命周期。仅在Web环境的Spring ApplicationContext中可用。 |
websocket | 将单个bean定义的作用域限定为WebSocket的生命周期。仅在Web环境的Spring ApplicationContext中可用。 |
从Spring 3.0开始,线程作用域可用,但默认情况下未注册。有关更多信息,请参见的文档 SimpleThreadScope
。
通常,应将原型作用域用于所有有状态Bean,将单例作用域用于无状态Bean。
Spring Boot读取xml配置文件的代码过程:
lambda$loadBeanDefinitionsFromImportedResources$0:379, ConfigurationClassBeanDefinitionReader (org.springframework.context.annotation)
accept:-1, 351656492 (org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader$$Lambda$167)
forEach:684, LinkedHashMap (java.util)
loadBeanDefinitionsFromImportedResources:346, ConfigurationClassBeanDefinitionReader (org.springframework.context.annotation)
loadBeanDefinitionsForConfigurationClass:147, ConfigurationClassBeanDefinitionReader (org.springframework.context.annotation)
loadBeanDefinitions:120, ConfigurationClassBeanDefinitionReader (org.springframework.context.annotation)
processConfigBeanDefinitions:337, ConfigurationClassPostProcessor (org.springframework.context.annotation)
postProcessBeanDefinitionRegistry:242, ConfigurationClassPostProcessor (org.springframework.context.annotation)
invokeBeanDefinitionRegistryPostProcessors:275, PostProcessorRegistrationDelegate (org.springframework.context.support)
invokeBeanFactoryPostProcessors:95, PostProcessorRegistrationDelegate (org.springframework.context.support)
invokeBeanFactoryPostProcessors:706, AbstractApplicationContext (org.springframework.context.support)
refresh:532, AbstractApplicationContext (org.springframework.context.support)
refresh:747, SpringApplication (org.springframework.boot)
refreshContext:397, SpringApplication (org.springframework.boot)
run:315, SpringApplication (org.springframework.boot)
run:1226, SpringApplication (org.springframework.boot)
run:1215, SpringApplication (org.springframework.boot)
main:13, Application (study.hwj.spring)
1.5.2。原型作用域
与其他作用域相比,Spring不能管理原型Bean的完整生命周期。容器实例化,配置或组装原型对象,然后将其交给客户端,而无需对该原型实例的进一步记录。因此,尽管在不考虑作用域的情况下在所有对象上都调用了初始化生命周期回调方法,但在原型的情况下,不会调用已配置的销毁生命周期回调。客户端代码必须清除原型作用域内的对象并释放原型Bean拥有的昂贵资源。为了使Spring容器释放原型作用下的bean所拥有的资源,请尝试使用自定义bean后置处理器,其中包含对需要清理的bean的引用。【???怎么用后置处理器处理原型Bean的销毁方法】
在某些方面,Spring容器在原型作用域bean方面的角色是Java new
运算符的替代。超过该时间点的所有生命周期管理必须由客户端处理。
1.5.3。具有原型Bean依赖关系的Singleton Bean
当您使用对原型Bean有依赖性的单例作用域Bean时,请注意,依赖性在实例化时已解决。因此,如果您将原型范围的bean注入到单例范围的bean中,则将实例化新的原型bean,然后将依赖项注入到该singleton Bean中。原型实例是曾经提供给单例范围的bean的唯一实例。
但是,假设您希望单例作用域的bean在运行时重复获取原型作用域的bean的新实例。您不能将原型作用域的bean依赖项注入到您的单例bean中,因为当Spring容器实例化单例bean并解析并注入其依赖项时,该注入仅发生一次。如果在运行时不止一次需要原型bean的新实例,请参见方法注入。
1.5.4。Request, Session, Application, 和WebSocket范围
只有当你使用一个基于web的Spring ApplicationContext实现(例如 XmlWebApplicationContext),request
,session
,application
,和websocket
作用域可用。如果您将这些作用域与常规的Spring IoC容器(例如ClassPathXmlApplicationContext)一起使用,则会抛出未知bean作用域的IllegalStateException
。
request 作用域
<bean id="loginAction" class="com.something.LoginAction" scope="request"/>
@RequestScope
@Component
public class LoginAction {
// ...
}
session 作用域
<bean id="userPreferences" class="com.something.UserPreferences" scope="session"/>
@SessionScope
@Component
public class UserPreferences {
// ...
}
Application 作用域
<bean id="appPreferences" class="com.something.AppPreferences" scope="application"/>
@ApplicationScope
@Component
public class AppPreferences {
// ...
}
Spring容器通过对整个Web应用程序使用appPreferences Bean定义来创建一个AppPreferences Bean 的新实例。也就是说, appPreferences bean的作用域在ServletContext级别,并存储为常规 ServletContext属性。这有点类似于Spring单例bean,但有两个重要的区别:它是每个 ServletContext
而不是每个Spring ApplicationContext 的单例(在任何给定的Web应用程序中可能都有多个),并且实际上是公开的,因此可见作为ServletContext属性。
范围Bean作为依赖项
Bean的作用域为request,session和自定义作用域级别需要使用<aop:scoped-proxy/>
元素。
<bean id="userPreferences" class="com.something.UserPreferences" scope="session">
<aop:scoped-proxy/>
</bean>
<bean id="userManager" class="com.something.UserManager">
<property name="userPreferences" ref="userPreferences"/>
</bean>
您还可以在范围为singleton
的Bean之间使用<aop:scoped-proxy/>
,引用然后通过可序列化的中间代理,因此可以在反序列化时重新获得目标单例Bean。
在prototype
范围bean声明<aop:scoped-proxy/>
时,共享代理上的每个方法调用都会导致创建新的目标实例,然后将该调用转发到该目标实例。
同样,作用域代理不是以生命周期安全的方式从较短的作用域访问bean的唯一方法。您还可以将注入点(即,构造函数或setter参数或自动装配的字段)声明为ObjectFactory<MyTargetBean>
,从而允许每次需要时getObject()调用按需检索当前实例,而无需保留该实例或将其单独存储。
作为扩展变体,您可以声明ObjectProvider<MyTargetBean>
,它提供了几个附加的访问变体,包括getIfAvailable
和getIfUnique
。
被称为Provider
的JSR-330变体, 在每次检索尝试中与Provider<MyTargetBean>
声明和相应的get()
调用一起使用。有关JSR-330总体的更多详细信息,请参见此处。
选择要创建的代理类型
默认情况下,当Spring容器为使用<aop:scoped-proxy/>
元素标记的bean创建代理时,将创建基于CGLIB的类代理。
CGLIB代理仅拦截公共方法调用!不要在这样的代理上调用非公共方法。它们没有被委派给实际的作用域目标对象。
另外,您可以通过指定<aop:scoped-proxy/>
元素的proxy-target-class
属性值为false
,将Spring容器配置为为此类作用域的Bean创建基于标准JDK接口的代理。使用基于JDK接口的代理意味着您不需要应用程序类路径中的其他库即可影响此类代理。但是,这也意味着作用域Bean的类必须实现至少一个接口,并且作用域Bean注入到其中的所有协作者必须通过其接口之一引用该Bean。
1.5.5。自定义作用域
bean的作用域机制是可扩展的。您可以定义自己的作用域,甚至重新定义现有作用域,尽管后者被认为是不好的做法,您不能覆盖内置作用域singleton
和prototype
。
创建自定义作用域
需要实现org.springframework.beans.factory.config.Scope
接口。
使用自定义作用域
org.springframework.beans.factory.config.ConfigurableBeanFactory#registerScope
Scope threadScope = new SimpleThreadScope();
beanFactory.registerScope("thread", threadScope);
使用自定义Scope
实现,您不仅限于作用域的程序注册。您还可以使用CustomScopeConfigurer
类以声明方式进行Scope注册 ,如以下示例所示:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="thread">
<bean class="org.springframework.context.support.SimpleThreadScope"/>
</entry>
</map>
</property>
</bean>
<bean id="thing2" class="x.y.Thing2" scope="thread">
<property name="name" value="Rick"/>
<aop:scoped-proxy/>
</bean>
<bean id="thing1" class="x.y.Thing1">
<property name="thing2" ref="thing2"/>
</bean>
</beans>
当您在FactoryBean实现中放置<aop:scoped-proxy/>
时,作用域是工厂Bean本身,而不是从getObject()
中返回的对象。