【问题标题】:Custom spring scopes?定制弹簧范围?
【发布时间】:2017-10-09 22:16:55
【问题描述】:

有人知道Servlet Context ScopeThreadScope 以外的任何其他自定义弹簧范围吗?

如果您制作了一些闭源自定义范围,我真的很想听听它的作用以及它对您的效果。 (我想有人会在桌面应用程序中制作 WindowScope 吗?)

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

【问题讨论】:

    标签: java spring


    【解决方案1】:

    我们实现了自己的自定义 Spring 范围。我们的许多代码在相对较低的级别上工作,接近数据库,并且我们在此之上维护一个概念级别,并使用其自己的数据源、链接、属性等对象模型。

    无论如何,很多 bean 都需要一个所谓的 StorageDictionary(此对象图的封装)来完成它们的工作。当我们对对象图进行重大更改时,有时需要删除并重新创建字典。因此,我们为 字典范围 的对象实现了自定义范围,并且给定字典的部分失效涉及清除此自定义范围。这让 Spring 可以为这些对象处理一种很好的自动缓存形式。每次都取回相同的对象,直到字典失效,此时您将获得一个新对象。

    这不仅有助于保持一致性,而且还允许对象本身缓存对字典中实体的引用,只要知道缓存本身可以被 Spring 检索,就可以安全地进行缓存。这反过来又让我们将它们构建为不可变对象(只要它们可以通过构造函数注入进行连接),无论如何这是一件非常好的事情。

    这种技术并不适用于任何地方,并且在很大程度上取决于软件的特性(例如,如果定期修改字典,这将是非常低效的,如果从未更新过,这将是不必要的,而且效率略低于直接使用权)。但是,它确实帮助我们以一种概念上简单且在我看来非常优雅的方式将这种生命周期管理传递给了 Spring。

    【讨论】:

    • 真的很酷,你在那里扩大了我的视野。我看到一个“缓存”范围或类似的。对于计算得出的派生数据可能也很好,这就是您正在做的事情。
    【解决方案2】:

    在我的公司中,我们创建了两个自定义范围,一个将使用 Thread 或 Request,另一个将使用 Thread 或 Session。这个想法是单个作用域可以用于作用域 bean,而无需根据执行环境(JUnit 或 Servlet 容器)更改配置。当您在 Quartz 中运行项目并且不再有可用的请求或会话范围时,这也非常有用。

    【讨论】:

    • 我们通过在 JUnit 上下文中使用模拟会话/请求对象来解决这个问题,因此我们可以使用请求/会话范围。这两种解决方案在功能上是等效的吗? (可能也可以在石英范围内使用......)
    • 对于 JUnit 和 Servlet 上下文之间的差异,我绝对认为它们是等价的。对于 Quartz,我认为它有点复杂。如果您将作用域 bean 注入您的服务并尝试从 Quartz 作业中调用这些服务,则很难解决这个问题。
    • 我没有使用石英的经验,但它是在单个线程中运行还是在不同线程中运行?
    • 准确地说,我们有自己的 ContextLoader,它为单元测试注册范围请求和会话。这些作用域的实现基于这些作用域的 spring 版本。这种方法不能用于石英吗?
    【解决方案3】:

    背景:

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

    问题:

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

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

    解决方案:

    我打算使用“站点”范围。

    我们有一个像这样的站点枚举:

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

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

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

    稍后添加:

    这里的示例代码:

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

    【讨论】:

      【解决方案4】:

      Oracle Coherence 实现了datagrid scope for Spring beans。总结一下:

      Data Grid Bean 是一个代理 java.io.Serializable Bean 实例 存储在一个不会过期的 Coherence 分布式缓存(称为 近datagridbeans)。

      我自己从未使用过它们,但它们看起来很酷。

      【讨论】:

        【解决方案5】:

        Apache Orchestra 提供SpringConversationScope

        【讨论】:

          【解决方案6】:

          在 Spring Batch 应用程序中,我们实现了一个项目范围

          背景

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

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

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

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

          设计

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

          结果如何

          有效,但存在以下问题:

          1. 我们无法在未注册的情况下清理派生 bean(仅基于它们的 @Scope)。具体处理器必须创建并注册它们。
          2. AbstractScopeRegisteringItemProcessor 使用组合并动态实现底层处理器的所有接口会更好。但随后生成的 @StepScope bean 是 declared return type(即 AbstractScopeRegisteringItemProcessorItemProcessor)的代理,没有所需的回调接口。

          编辑

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

          所以最后,它确实奏效了。

          【讨论】:

            【解决方案7】:

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

            related wiki page

            【讨论】:

              【解决方案8】:

              在我公司,我们也实现了 spring 自定义范围。我们有一个多租户系统,每个客户都可以自定义设置。我们基于实例的范围,缓存客户特定的bean。因此,每次客户的用户登录时,这些设置都会被缓存,并在同一客户的其他用户登录时再次重复使用。

              【讨论】:

                【解决方案9】:

                我曾经使用过一种conversation scope在session范围内存储一些对象,以便在重新进入同一个页面时保留它们,但限制在单个页面以避免离开会话中无用的对象。该实现只是存储了页面 URL 并在每次页面更改时清理了 对话范围

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2017-05-15
                  • 1970-01-01
                  • 2015-10-16
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多