【问题标题】:How do I inject beans into a scope implementation?如何将 bean 注入作用域实现?
【发布时间】:2012-08-13 21:30:42
【问题描述】:

我正在编写自己的作用域(即实现org.springframework.beans.factory.config.Scope 的类),我需要注入一些bean。我该怎么做?

背景:Spring 必须首先创建所有范围 bean,以便我可以定义哪些 bean 进入范围。但是我首先需要构建范围的 bean 呢?

【问题讨论】:

  • 当您尝试启动您的应用程序时,Spring 是否抱怨找不到那些bean
  • 不,字段只保留null。我可以创建范围ApplicationContextAware,但正如我所说,上下文处于其初始化的中间,因此大多数 bean 要么尚未创建,要么仅初始化一半(因此设置了一些 @Autowired 字段,其他不是)。
  • 出于好奇,您是否碰巧知道任何现有的Scope 实现是否以这种方式使用注入...?这看起来就像你试图在框架可用之前使用它(我认为范围是注入的先决条件,可以这么说)......
  • 我同意,这是一个极端情况,不,我还没有看到任何其他示波器尝试过这样的特技。但我仍然认为在作用域和 appContext 之间共享 bean 很有用。
  • 在黑暗中射击:尝试在要注入自定义范围的 bean 中实现 AopInfrastructureBean

标签: java spring dependency-injection scope


【解决方案1】:

我想出了这个似乎很安全的解决方法,但我想听听 cmets(或者我的回答可能会给你一些更好的想法):

  1. 定义范围并给它设置器(而不是使用@Autowired
  2. 创建一个“范围配置器”bean:

    public CustomScopeConfigurer {
        @Autowired private Foo foo;
        private CustomScope scope;
    
        public CustomScopeConfigurer( CustomScope scope ) {
            this.scope = scope;
        }
    
        @PostConstruct
        public void initScope() {
            scope.setFoo( foo );
        }
    }
    

    这个配置器 bean 不能是惰性的。

推理:

  1. 作用域本身不能使用自动装配,因为它是在第一个 bean 之前创建的。虽然它可能会在以后创建,但您可以确定它将在所有其他 bean 之前创建。所以自动装配不能可靠地工作。

  2. 配置器 bean 将与所有其他 bean 一起创建,但在作用域之后。所以自动装配会起作用。

  3. 由于配置器 bean 不是惰性初始化的,它将在应用程序的其余部分可以看到应用程序上下文之前创建。这意味着此时不能创建范围内的任何 bean(即带有 @Scope("custom") 的 bean) - 范围不能是“活动的”,但是 -> Spring 不会尝试将任何 bean 放入其中,还没有。

  4. 作用域本身通常在某处创建为静态常量。这就是为什么我们必须将它作为参数传递给构造函数。

【讨论】:

  • 这真的有效吗?我假设您必须手动实例化范围对象,那么第 3 行如何检索该实例而不创建新实例?
  • @TomJenkinson:很好。没错,您必须将作用域作为构造函数参数传递。
  • @AaronDigulla 可能会遇到一些错误。考虑以下场景:首先将创建 CustomScopeConfigurer bean,然后立即创建 CustomScope bean,现在我们的自定义范围已准备好使用。一段时间后,将创建将 foo 初始化为 CustomScope 的 CustomScopeConfigurer。但是,如果在 CustomScope 注册之后和 CustomScopeConfigurer bean 创建之前创建了一些自定义范围的 bean,会发生什么?对于那些 bean,CustomScope 中不会有 foo。我试图找到解决方案,但我没有得到一个。你可以试一下吗? :)
  • @AaronDigulla 我为您的问题提供了简单的解决方案。请关注并建议我改进。 :)
【解决方案2】:

您可以非常简单地做到这一点。

考虑以下自定义范围类:

package com.way2learn;

import java.util.Map;

import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;

public class MyCustomScope implements Scope{

    private Map<String, Object> scope;

    public void setScope(Map<String, Object> scope) {
        this.scope = scope;
    }

    @Override
    public Object get(String name, ObjectFactory<?> objectFactory) {
        checkAndClear();
        Object bean=scope.get(name);
        if(bean==null){
            bean=objectFactory.getObject();
            scope.put(name,bean);
        }
        return bean;
    }
    private void checkAndClear() {
        //Some logic to check condition and clear the scope
    }
    //optional methods
    @Override
    public Object remove(String name) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void registerDestructionCallback(String name, Runnable callback) {

    }

    @Override
    public Object resolveContextualObject(String key) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public String getConversationId() {
        // TODO Auto-generated method stub
        return null;
    }

}

它依赖于java.util.Map

您不能使用@Autowired 自动装配它,因为@Autowired 注释仅在AutoWiredAnnotationBeanPostProcessor 之后有效。

但是自定义范围会在AutoWiredAnnotationBeanPostProcessor之前注册。

因此您可以手动将Map 注入MyCustomScope 类,如下所示:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:util="http://www.springframework.org/schema/util"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.1.xsd">

        <util:map key-type="java.lang.String" value-type="java.lang.Object" id="custScopeMap"/>

        <bean id="myCustomScope" class="com.way2learn.MyCustomScope">
            <property name="scope" ref="custScopeMap"/>
        </bean>

        <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
            <property name="scopes">
                <map>
                    <entry key="myScope" value-ref="myCustomScope"/>
                </map>
            </property>
        </bean>

</beans>

我试过了。它工作正常。我在Aaron Digulla's 答案中发现了一个错误,即

考虑以下场景:首先将创建Spring's CustomScopeConfigurer bean,然后立即创建CustomScope bean,现在我们的自定义范围可以使用了。一段时间后,Aaron Digulla's CustomScopeConfigurer 将被创建,它将 foo 初始化为CustomScope。但是如果在CustomScope registrationAaron Digulla's CustomScopeConfigurer bean 创建之前创建了一些自定义范围的bean,会发生什么?对于那些 bean,CustomScope bean 中不会有 foo。

【讨论】:

  • 我想这里的限制是你不能用纯 Java 配置来做;您必须回退到 XML 配置文件。对吗?
  • 至于您发现的错误,请参阅我回答的原因#3。在将 bean 放入作用域之前,应用程序上下文必须是完整的。即使您连接一个作用域 bean,您也只需创建一个代理。上下文存在后,选择范围的帮助代码将处于活动状态。所以在上下文准备好之前应该不可能/不允许调用Scope.get()
猜你喜欢
  • 1970-01-01
  • 2011-10-02
  • 2011-07-14
  • 1970-01-01
  • 1970-01-01
  • 2013-06-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多