从子上下文中引用在父上下文中创建的Spring单例

3

这个题目有点难想!我有一个在Spring容器中初始化的bean。它加载使用Spring类加载器从文件创建对象的类。其中一些对象可能依赖于昂贵的对象,我希望这些对象在父容器中初始化。好吧,我无法用语言解释,所以让我们看一个简化的例子:

public class MainLoader {
    public static void main(String[] args) {
        XmlBeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("top-context.xml"));

        ChildLoader childLoader = (ChildLoader)beanFactory.getBean("childLoader");

        childLoader.loadChildAndDoSomething("message1.xml");
        childLoader.loadChildAndDoSomething("message2.xml");        
    }
}

public class ChildLoader {

    public void loadChildAndDoSomething(String childContextfile){       
        XmlBeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource(childContextfile));
        ClassThatDoesStuff classThatDoesStuff = (ClassThatDoesStuff)beanFactory.getBean("classThatDoesStuff");  
        classThatDoesStuff.saySomething();
    }

}

public class ClassThatDoesStuff {
private ReallyExpensiveService reallyExpensiveService;  
private String messageStart;

public void saySomething(){
    System.out.println(messageStart + reallyExpensiveService.getName());
}

    // .. field setters removed for brevity
}

public class ReallyExpensiveService {
public String getName(){
    return "Joe";
}
}

这些文件中包含以下豆子:
top-context.xml:
<bean id="childLoader" class="com.mark.test.ChildLoader" />

message1.xml(message2.xml类似):

<bean id="classThatDoesStuff" class="com.mark.test.ClassThatDoesStuff">
    <property name="messageStart" value = "Hello! " />  
    <property name ="reallyExpensiveService" ref="theExpensiveserviceReference" />  
</bean>
<bean id="theExpensiveserviceReference" class="com.mark.test.ReallyExpensiveService" />

当运行这些代码时,你会得到预期的结果:
Hello! Joe
Goodbye! Joe

这里唯一的问题是每次都会由Spring创建和缓存"ReallyExpensiveService"。这在日志中得到了验证。最好在MainLoader初始化时加载"ClassThatDoesStuff"类(想象它是一个接口)可能需要的任何服务。也就是说(从概念上讲),更改Spring上下文文件为:

top-context.xml:

<bean id="childLoader" class="com.mark.test.ChildLoader" />
<bean id="theExpensiveserviceReference" class="com.mark.test.ReallyExpensiveService" />

message1/2.xml

    <bean id="classThatDoesStuff" class="com.mark.test.ClassThatDoesStuff"
  autoWired="byType">   
        <property name="messageStart" value = "Hello! " />  
    </bean>

我意识到解决这个问题的方法是让ClassThatDoeStuff有一个服务的setter,并从Child容器中设置该值,而Child容器本身则通过主上下文进行注入。但是想象一下,如果有任意数量的服务,每个ClassThatDoesStuff实现者都使用不同的服务... 在Spring中是否有任何方法可以解决这个问题?


1
我认为没有办法让独立的Bean工厂知道它们各自的Bean。你建议让主要上下文控制这个解析,可能是最合理的方法。当然,更明显的问题是,为什么需要加载多个Bean工厂? - Fil
谢谢Flip - 我知道这很奇怪 - ChildLoader是一个对象,适合于已经使用Spring进行装配的现有(大型)框架中。ChildLoader必须从Spring配置文件中加载和实例化业务对象(以执行一系列操作)。 - Mark D
2个回答

1

看起来你最好的希望是实例化每个ReallyExpensiveService一次。(你在其中提到了你不确定哪个ClassThatDoesStuff可能使用不同的服务。)我可能会尝试在顶层上下文中定义所有的ReallyExpensiveService bean,然后在适当的地方将它们分配给使用它们的类,无论是通过你正在使用的XML配置文件还是通过某种工厂注入到ClassThatDoesStuff bean中。

你也可以尝试寻找一种方法,推迟启动ReallyExpensiveService的昂贵操作,直到你确定它们将被使用。当然,这取决于“昂贵”的含义。是因为服务使用太多内存,如果它们没有使用就不想让它们存在,还是因为它们实例化需要太长时间?

无论如何,关键在于尽可能少地浮动昂贵的东西实例,所以你需要在顶层进行配置,以便单个实例的引用可以传递到任何地方。


1
谢谢Rafe - 这是我的想法 - 如果没有更好的答案,我会将其标记为答案。我本以为有办法! - Mark D

0

我在这方面摸索了很多,并且在此过程中学到了很多有关Spring的知识。我认为不可能让父Spring上下文动态应用这些属性。我通过在mainloader对象中实现缓存来解决这个问题,从而避免了昂贵类型从Spring定义中创建多个实例。

我还调查了另一个可能性,即使ChildLoader上下文感知并允许ClassThatDoesStuff使用父上下文来获取bean的处理方式,但这使Spring与应用程序太过融合,我不喜欢。


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