自定义Spring作用域?

13

除了Servlet Context ScopeThreadScope之外,还有其他自定义的Spring范围吗?

如果您已经创建了一些闭源的自定义范围,我也很感兴趣听听它是什么以及它对您的工作有何影响。(我想在桌面应用程序中可能会有人制作WindowScope?)

我对所有用例都持开放态度,我希望在这里拓展我的视野。

9个回答

10

我们实现了自己的定制Spring作用域。我们的很多代码在接近数据库的相对较低层级上工作,并且我们通过自己的数据源、链接、属性等对象模型来维护概念层级。

无论如何,很多bean需要一个所谓的StorageDictionary(这个对象图的封装)来完成它们的工作。当我们对对象图进行非平凡更改时,字典有时需要被清除并重新创建。因此,我们为那些“字典作用域”的对象实现了一个自定义作用域,并且给定字典的失效也涉及到清除这个自定义作用域。这让Spring可以为这些对象处理一种不错的自动缓存。你每次都会得到同一个对象,直到字典失效,此时你会得到一个新的对象。

这不仅有助于一致性,而且还允许对象本身缓存字典中的实体引用,因为它们知道只要它们可以被Spring检索到,缓存就是有效的。这反过来又让我们可以将它们构建为不可变对象(只要它们可以通过构造函数注入进行连接),这是无论在哪里都应该尽可能做到的好事情。

这种技术并不适用于所有情况,它严重依赖于软件的特性(例如,如果字典经常被修改,这将非常低效;如果它从未更新,这将比直接访问略微低效)。然而,它确实帮助我们以概念上简单且我认为相当优雅的方式将生命周期管理交给了Spring。


非常棒,你拓展了我的视野。我看到了“缓存”范围或类似的东西。这对于计算派生数据也很有用,这似乎是你正在做的事情。 - krosenvold

4

在我们公司中,我们创建了两个自定义范围,一个将使用线程或请求(Thread or Request),另一个将使用线程或会话(Thread or Session)。这样一来,可以使用单个范围来处理作用域的bean,而无需根据执行环境(JUnit或Servlet容器)更改配置。当使用Quartz运行项目时,没有请求或会话范围可用,因此这也非常方便。


我们通过在JUnit上下文中使用模拟会话/请求对象来解决了这个问题,因此我们可以使用请求/会话范围。这两种解决方案在功能上是否等效?(可能也可以在quartz范围内使用...) - krosenvold
对于JUnit和Servlet上下文之间的差异,我认为它们是相当的。对于Quartz,我认为情况会更加复杂。如果您将作用域Bean注入到服务中,并尝试从Quartz作业调用这些服务,那么解决这个问题会比较困难。 - cliff.meyers
我对quartz没有经验,但它是在单个线程还是不同的线程中运行? - krosenvold
准确地说,我们有自己的ContextLoader,为单元测试注册请求和会话范围。这些范围的实现基于Spring版本的这些范围。这种方法不能用于quartz吗? - krosenvold

4

背景:

我在一个单一的Web应用程序上工作,该应用程序在同一Servlet上下文中运行4个不同的网站。每个网站都有自己的域名,例如www.examplesite1.com,www.examplesite2.com等。

问题:

有时,网站需要其自己定制的bean实例来自应用程序上下文(通常用于自定义消息显示或对象格式化)。

例如,假设站点1和2都使用“standardDateFormatter” bean,站点3使用“usDateFormatter” bean,站点4使用“ukDateFormatter” bean。

解决方案:

我计划使用“site”范围。

我们有一个类似这样的Site枚举:

enum Site {
    SITE1, SITE2, SITE3, SITE4;
}

然后我们有一个过滤器,使用ThreadLocal将其中一个Site值存储在请求的线程中。这就是站点范围的“会话ID”。

然后,在应用程序上下文中,将有一个名为“dateFormatter”的bean,其'scope="site"'。然后,无论我们想在哪里使用日期格式化程序,都将使用用户当前站点的正确日期格式化程序。

稍后添加:

示例代码在此处:

http://github.com/eliotsykes/spring-site-scope


2
Oracle Coherence实现了Spring beans的数据网格范围。简单来说:
数据网格Bean是指代一个java.io.Serializable Bean实例的代理,该实例存储在一个非过期的Coherence分布式缓存中(称为near-datagridbeans)。
我自己从未使用过它们,但它们似乎很酷。

2

Apache Orchestra 提供了 SpringConversationScope


翻译: Apache Orchestra 提供了 SpringConversationScope。

1
在一个Spring Batch应用中,我们已经实现了一个作用域

背景

我们有很多@Service组件,它们根据当前批处理项计算某些东西。其中许多都需要相同的工作流程:

  1. 确定相关项目的部分。
  2. 根据项目初始化内容。
  3. 对于每个项目部分,使用内容计算某些东西。

我们将工作流程移动到基类模板方法中,因此子类仅实现findItemParts(Item)(执行1和2)和computeSomething(ItemPart) (执行3)。因此,它们变得有状态 (在findItemParts中初始化的“stuff”在computeSomething中需要),并且该状态必须在下一项之前清除。

其中一些服务还涉及从当前项目派生的注入Spring bean,之后必须将其删除。

设计

我们实现了一个AbstractScopeRegisteringItemProcessor,它注册了项目并允许子类注册派生bean。在其process方法结束时,它会从其作用域上下文中删除该项,并使用DefaultSingletonBeanRegistry.destroySingleton销毁派生bean。

它的工作方式

它可以工作,但存在以下问题:

  1. 我们无法在没有注册的情况下清除派生bean(仅基于它们的@Scope)。具体处理器必须创建并注册它们。
  2. AbstractScopeRegisteringItemProcessor最好使用组合并动态实现底层处理器的所有接口。但是,结果的@StepScope bean是声明返回类型(即AbstractScopeRegisteringItemProcessorItemProcessor)的代理,没有所需的回调接口。

编辑

通过 @Eliot Sykes 的 solution 和共享代码以及 @Cheetah 的 BeanDefinition registration 的帮助,我能够摆脱将注册作为单例 bean 的方式。相反,ItemScopeContext(处理器和 Scope 实现都使用的存储;通过静态的 @Bean 方法进行 Java 配置)实现了 BeanDefinitionRegistryPostProcessor。它注册了一个 FactoryBean,其 getObject() 返回当前项或如果没有则抛出异常。现在,使用 @Scope(scopeName = "Item", proxyMode = ScopedProxyMode.TARGET_CLASS) 注释的 @Component 可以简单地注入该项,无需为范围结束时的清理注册。

因此,最终它确实很好地工作了。


0
在我的公司里,我们也实现了Spring自定义作用域。我们有一个多租户系统,每个客户都可以自定义设置。我们的基于实例的作用域缓存了与客户相关的bean。因此,每当客户的用户登录时,这些设置就会被缓存并在同一客户的其他用户登录时再次重用。

0

基于用户语言环境的Web应用程序中的Spring区域设置范围

请参见相关维基页面


0
我曾经使用一种“会话范围”来存储一些对象在会话范围内,以便在重新进入同一页时保留它们,但仅限于单个页面,以避免在会话中留下无用的对象。实现只是存储页面URL,并在每次页面更改时清除“会话范围”。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接