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

时间:2011-06-17 16:24:07

标签: java spring dependency-injection

很难想到这个的标题!我有一个在弹簧容器中初始化的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";
}
}

这些文件中包含以下bean:

顶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上下文文件更改为:

顶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容器的值,该容器本身通过主上下文注入。但是想象一下有任意服务,每个ClassThatDoesStuff实现者都使用不同的服务。有没有办法让它在Spring中工作..?

2 个答案:

答案 0 :(得分:1)

您希望最好的方法是一次实例化每个ReallyExpensiveService。 (你在那里提到你不确定哪个ClassThatDoesStuff可能使用不同的。)我可能会尝试在顶级上下文中定义所有的ReallyExpensiveService bean,然后将它们交给使用它们的类,只要它合适。 ,或者通过您正在使用的XML配置文件,或者通过您注入ClassThatDoesStuff bean的某种工厂。

您可能还会尝试寻找一种方法来推迟启动ReallyExpensiveService的昂贵操作,直到您确定它们将被使用为止。当然,这取决于“昂贵”的含义。这些服务是否使用了太多的内存而且如果它们没有被使用或者它们需要花费太长时间才能实例化,那么它们是不是希望它们存在?

在任何情况下,这里的关键是让尽可能少的昂贵东西的实例浮动,因此您需要在顶层配置它们,以便可以在任何地方传递对单个实例的引用。

答案 1 :(得分:0)

我用这个摆弄了很多东西,并在这个过程中学到了很多关于弹簧的知识。我认为不可能让父spring语句动态地应用这些属性。我通过在mainloader对象中实现缓存来解决这个问题,这样就不会在spring定义中多次创建昂贵的类型。

我调查的另一个可能性是让ChildLoader上下文感知并允许ClassThatDoesStuff使用父上下文来获取bean的句柄,但这对于应用程序来说太过于适合我的喜好。