
目录说明: 3Bean作用域 4singleton作用域 4prototype作用域 5具有原型Bean依赖的单例Bean 7Request, Session, Application, 和 WebSocket 作用域 7初始网络配置 7request作用域 8session作用域 8application作用域 9websocket作用域 9作用域 Bean 作为依赖项 9选择要创建的代理类型 11直接注入Request/Session引用 11自定义作用域 11创建自定义作用域 11使用自定义作用域 12附录一:原型作用域(Prototype)测试代码 13附录二:具有原型Bean依赖的单例Bean测试代码 16附录三:四个web作用域测试代码 191、@RequestScope 192、@SessionScope 213、@ApplicationScope 254、@WebSocketScope 28附录四:作用域 Bean 作为依赖项测试代码 40附录五:演示ObjectFactory的作用 43附录六:演示ObjectProvider的作用 48附录七:火狐浏览器清除任意网站的 Cookie 55说明:1.Spring Framework版本:7.0.62.开发框架:Spring boot(版本4.0.5)3.开发工具:eclipse 2025-09 (4.37.0)4.jdk版本:255.操作系统:debian12Bean作用域当你创建一个bean定义时,你实际上是在为创建由该bean定义所定义的类的实际实例制定一个配方。bean定义是配方的概念很重要,因为这意味着,就像使用一个类一样,你可以根据一个配方创建出许多对象实例。您不仅可以控制从特定bean定义创建的对象中要插入的各种依赖项和配置值,还可以控制从特定bean定义创建的对象的作用域。这种方法功能强大且灵活,因为您可以通过配置来选择所创建对象的范围,而不必在Java类级别上硬性规定对象的范围。Bean 可以被定义部署在多个作用域中的任意一个。Spring Framework 支持六个作用域,其中四个仅在使用支持 Web 的 ApplicationContext 时可用。您还可以创建自定义作用域。下表描述了支持的作用域:Table 1. Bean scopes作用域描述singleton(默认)将单个Bean定义的作用域限定为每个Spring IoC容器中的一个对象实例。prototype将单个Bean定义作用域扩展到任意数量的对象实例。request将单个Bean定义的作用域限定在单个HTTP请求的生命周期内。也就是说,每个HTTP请求都会根据单个Bean定义创建自己的Bean实例。仅在支持Web的Spring ApplicationContext上下文中有效。session将单个Bean定义的作用域限定在HTTP Session的生命周期内。仅在支持Web的Spring ApplicationContext上下文中有效。application将单个Bean定义的作用域限定在ServletContext的生命周期内。仅在支持Web的SpringApplicationContext上下文中有效。websocket将单个Bean定义的作用域限定在WebSocket的生命周期内。仅在支持Web的Spring ApplicationContext上下文中有效。注意:线程作用域可用,但默认情况下未注册。有关更多信息,请参阅SimpleThreadScope的文档。有关如何注册此作用域或任何其他自定义作用域的说明,请参阅使用自定义作用域。singleton作用域只有一个单例Bean的共享实例受到管理,对于具有与该Bean定义匹配的一个或多个ID的Bean的所有请求,都会导致Spring容器返回该特定的Bean实例。换言之,当你定义一个 bean 定义并且其作用域为单例时,Spring IoC 容器会为该 bean 定义所定义的对象创建一个实例。这个单一实例存储在这样一个单例Bean的缓存中,所有后续对该命名Bean的请求和引用都会返回缓存的对象。下图展示了单例作用域的工作原理:Spring中的单例Bean概念与《四人组(GoF)模式》一书中所定义的单例模式有所不同。GoF 单例模式对对象的范围进行了硬编码,使得每个ClassLoader(类加载器)只能创建一个且只能创建一个特定类的实例。Spring 单例的作用域最好描述为容器内和 bean 内。这意味着,如果你在单个Spring容器中为某个特定类定义了一个bean,那么Spring容器将创建该bean定义所定义的类的一个且仅一个实例。在Spring中,单例作用域是默认的作用域。要在XML中将一个bean定义为单例,可以按照以下示例定义一个bean:bean/ !-- 以下内容是等价的,但有些冗余(默认作用域为单例) -- bean scope="singleton"/prototype作用域在 bean 部署中,非单例原型作用域会导致每次请求特定 bean 时都会创建一个新的 bean 实例。也就是说,当将该 bean 注入到另一个 bean 中,或者通过在容器上调用getBean()方法请求该 bean 时,都会如此。通常,您应该将所有有状态的 bean 使用原型作用域,将无状态的 bean 使用单例作用域。下图展示了Spring原型作用域:数据访问对象(DAO)通常不配置为原型,因为典型的DAO不包含任何会话状态。对我们来说,重用单例图的核心部分更容易。)以下示例在XML中将一个bean定义为原型:bean scope="prototype"/与其他作用域相比,Spring 并不管理原型 bean 的完整生命周期。容器会实例化、配置并以其他方式组装原型对象,然后将其交给客户端,而不再保留该原型实例的任何记录。因此,尽管初始化生命周期回调方法会在所有对象上调用,而不考虑其作用域,但在原型的情况下,配置的销毁生命周期回调方法则不会被调用。客户端代码必须清理原型作用域内的对象,并释放原型Bean占用的昂贵资源。要让Spring容器释放原型作用域Bean占用的资源,可以尝试使用一个自定义Bean后处理器,该处理器持有需要清理的Bean的引用。在某些方面,Spring容器在原型作用域Bean中的作用相当于Java new操作符的替代品。此后所有的生命周期管理都必须由客户端处理。(有关Spring容器中bean生命周期的详细信息,请参阅生命周期回调。)注:每一次@Autowired注入都会生成一个全新不同的实例,单例作用域除外。每次依赖注入 / 每次 getBean () 都新建对象永不自动销毁Spring容器本身不会保存任何原型 Bean 实例引用!触发销毁方法的两种方法:使用DisposableBean接口(原生销毁接口)。不用注解,实现接口就行调用ConfigurableBeanFactory.destroyBean(beanName,beanInstance)。@PreDestroy依然生效,必须保留触发销毁方法的两种方法测试代码详见附录一。想要每次调用都拿到全新原型,不能直接字段注入,要用:ObjectProviderBean或者ApplicationContext.getBean(),或者ConfigurableBeanFactory.getBean()也可以。@Autowired private ObjectProviderProtoBean provider; public void getNew(){ ProtoBean b1 = provider.getObject(); ProtoBean b2 = provider.getObject(); // b1 != b2 }具有原型Bean依赖的单例Bean当您使用具有原型Bean依赖的单例作用域Bean时,请注意依赖是在实例化时解析的。因此,如果你将一个原型作用域的Bean依赖注入到一个单例作用域的Bean中,一个新的原型Bean将被实例化,然后被依赖注入到单例Bean中。这个原型实例是唯一提供给单例作用域Bean的实例。然而,假设您希望单例作用域的Bean在运行时反复获取原型作用域Bean的新实例。你不能将原型作用域的Bean依赖注入到单例Bean中,因为这种注入只在Spring容器实例化单例Bean并解析和注入其依赖项时发生一次。如果你在运行时需要多次获取原型Bean的新实例,请参阅方法注入。测试代码详见附录二。Request, Session, Application, 和 WebSocket 作用域只有在使用支持 Web 的 Spring ApplicationContext 实现(如 XmlWebApplicationContext)时,request,session,application和websocket作用域才可用。如果你将这些作用域与常规的Spring IoC容器(如ClassPathXmlApplicationContext)一起使用,则会抛出一个IllegalStateException,指出存在未知的Bean作用域。四个 Web作用域的测试代码详见附录三。初始网络配置为了支持在request,session,application和websocket(Web范围Bean)对Bean的范围界定,在定义Bean之前需要进行一些初步的简单配置。(对于标准范围:单例和原型,则不需要进行此初始设置。)如何完成这一初始设置取决于你特定的Servlet环境。如果你在Spring Web MVC中访问作用域Bean,实际上是在Spring DispatcherServlet处理的请求中,无需进行特殊设置。DispatcherServlet已经公开了所有相关状态。如果你使用一个Servlet Web容器,且请求是在Spring的DispatcherServlet之外处理的(例如,在使用JSF时),那么你需要注册org.springframework.web.context.request.RequestContextListener这个ServletRequestListener。这可以通过使用WebApplicationInitializer接口以编程方式实现。或者,将以下声明添加到您的Web应用程序的web.xml文件中:web-app ... listener listener-class org.springframework.web.context.request.RequestContextListener /listener-class /listener ... /web-app或者,如果您的监听器设置存在问题,可以考虑使用Spring的RequestContextFilter。该过滤器的映射依赖于周围的Web应用程序配置,因此您必须根据实际情况进行更改。以下清单显示了Web应用程序的过滤器部分:web-app ... filter filter-namerequestContextFilter/filter-name filter-classorg.springframework.web.filter.RequestContextFilter/filter-class /filter filter-mapping filter-namerequestContextFilter/filter-name url-pattern/*/url-pattern /filter-mapping ... /web-appDispatcherServlet、RequestContextListener和RequestContextFilter都做着完全相同的事情,即将HTTP请求对象绑定到处理该请求的线程上。这使得请求作用域和会话作用域的Bean在调用链的下游也可用。request作用域考虑以下用于 bean 定义的 XML 配置:bean scope="request"/Spring容器会为每个HTTP请求使用loginAction bean的定义来创建一个新的LoginAction bean实例。也就是说,loginAction bean的作用域是在HTTP请求级别。你可以随意更改所创建实例的内部状态,因为从同一loginAction bean定义创建的其他实例看不到这些状态变化。它们特定于单个请求。当请求处理完毕后,作用域仅限于该请求的Bean将被丢弃。在使用注解驱动组件或Java配置时,可以使用@RequestScope注解将组件分配给请求作用域。以下示例展示了如何执行此操作:@RequestScope @Component public class LoginAction { // ... }Request作用域 Bean销毁时机:在一次 HTTP 请求处理完全结束、响应返回客户端 之后,立刻销毁。session作用域考虑以下用于 bean 定义的 XML 配置:bean scope="session"/Spring容器在单个HTTP会话的生命周期内,通过使用userPreferences bean的定义来创建UserPreferences bean的新实例。换言之,userPreferences bean的作用域实际上是在HTTPSession级别。与请求作用域的Bean一样,您可以随意更改所创建实例的内部状态,因为其他同样使用从同一userPreferences Bean定义创建的实例的HTTP Session实例看不到这些状态更改,因为这些更改特定于单个HTTP Session。当HTTP Session最终被丢弃时,作用域限定在该特定HTTP Session内的Bean也会被丢弃。在使用注解驱动组件或Java配置时,您可以使用@SessionScope注解将组件分配到会话作用域。@SessionScope @Component public class UserPreferences { // ... }session作用域bean在会话结束后不会立即销毁,要等到时间到期后销毁。除非立即调用HttpSession的invalidate()方法。application作用域考虑以下用于 bean 定义的 XML 配置:bean scope="application"/Spring 容器会根据 appPreferences 的 bean 定义在整个 Web 应用程序中仅创建一次 AppPreferences 类的新实例。也就是说,appPreferences bean的作用域是ServletContext级别,并作为常规的ServletContext属性进行存储。这在一定程度上类似于Spring的单例bean,但在两个重要方面有所不同: 它是一个针对每个ServletContext的单一实例,而不是针对每个Spring ApplicationContext(在任何给定的Web应用程序中可能存在多个)的单一实例,并且它实际上作为ServletContext的属性被公开,因此是可见的。在使用注解驱动组件或Java配置时,您可以使用@ApplicationScope注解将组件分配到application作用域。以下示例展示了如何操作:@ApplicationScope @Component public class AppPreferences { // ... }websocket作用域WebSocket作用域与WebSocket会话的生命周期相关联,并适用于基于WebSocket的STOMP应用,更多详情请参阅WebSocket作用域。作用域 Bean 作为依赖项Spring 的 IoC 容器不仅管理您的对象(即 bean)的实例化,还负责将协作对象(或依赖项)连接起来。如果您想将(例如)具有 HTTP 请求作用域的 bean 注入到作用域更长的另一个 bean 中,您可以选择注入一个 AOP 代理来替代该作用域的 bean。也就是说,您需要注入一个代理对象,该对象公开与作用域对象相同的公共接口,但还能从相关作用域(例如 HTTP 请求)中检索出真正的目标对象,并将方法调用委托给该真实对象。注意:您还可以在作用域为单例的Bean之间使用aop:scoped-proxy/,这样引用就会通过一个可序列化的中间代理,从而能够在反序列化时重新获取目标单例Bean。当对原型作用域的Bean声明aop:scoped-proxy/时,对共享代理的每次方法调用都会创建一个新的目标实例,然后将调用转发到该实例。此外,作用域代理并不是以生命周期安全的方式从较短作用域访问Bean的唯一方法。您还可以将注入点(即构造函数、setter方法参数或自动注入字段)声明为ObjectFactoryMyTargetBean