我已经阅读了inheriting bean definitions上的Spring 3参考文献,但我对可能的和不可能的内容感到困惑。
例如,一个带有协作者bean的bean,配置值为12
<bean name="beanService12" class="SomeSevice">
<constructor-arg index="0" ref="serviceCollaborator1"/>
</bean>
<bean name="serviceCollaborator1" class="SomeCollaborator">
<constructor-arg index="0" value="12"/>
<!-- more cargs, more beans, more flavor -->
</bean>
然后,我希望能够创建类似的bean,配置略有不同的协作者。我可以做点什么吗
<bean name="beanService13" parent="beanService12">
<constructor-arg index="0">
<bean>
<constructor-arg index="0" value="13"/>
</bean>
</constructor>
</bean>
我不确定这是否可行,如果是的话,感觉有点笨拙。是否有更好的方法来覆盖大型嵌套bean定义的小部分?孩子豆似乎必须对父母有很多了解,例如构造函数索引。
这是一个玩具示例 - 实际上,服务是依赖于许多其他协作者bean的大型bean定义,它们还具有其他bean依赖性。例如,创建了一系列处理程序,每个bean引用链中的下一个,引用下一个。我想创建一个几乎完全相同的链,中间的处理程序有一些小改动,我该怎么做?
我不想改变结构 - 服务bean使用协作者来执行他们的功能,但是如果有帮助我可以添加属性并使用属性注入。
这是一个重复的模式,会创建自定义架构帮助吗?
感谢您的任何建议!
编辑:我的问题的核心是,如果我有一个非常大的bean定义,正在创建一个复杂的bean的bean(bean有正确的bean等),我想创建一个几乎相同的bean同样有一些变化,我该怎么做?如果您的解决方案必须使用适当的,或者可以使用构造函数注入,请提及。嵌套与顶级bean不是问题(实际上,我认为所有bean都是实践中的顶级bean。)
EDIT2:到目前为止,感谢您的回答。 FactoryBean可能就是答案,因为这会降低spring上下文的复杂性,并允许我仅将差异指定为工厂的参数。但是,将一大块上下文推回到代码中并不合适。我听说spring可以用于脚本,例如groovy - 这提供了另一种选择吗?工厂可以在groovy中创建吗?
答案 0 :(得分:12)
我不完全确定你想要实现的目标。我不认为你可以在不创建自己的自定义模式的情况下完全实现你想要的东西(这对于嵌套结构来说是非常重要的),但下面的例子可能非常接近而没有这样做。
首先,定义一个抽象bean作为外部bean的模板(我的例子使用Car作为外部bean,Engine作为内部bean),给它所有其他bean都可以继承的默认值: p>
<bean id="defaultCar" class="Car" abstract="true">
<property name="make" value="Honda"/>
<property name="model" value="Civic"/>
<property name="color" value="Green"/>
<property name="numberOfWheels" value="4"/>
<property name="engine" ref="defaultEngine"/>
</bean>
由于所有Honda Civics都有相同的引擎(在我的世界里,我对汽车一无所知),我给它一个默认的嵌套引擎bean。不幸的是,bean无法引用抽象bean,因此默认引擎不能是抽象的。我已经为引擎定义了一个具体的bean,但是将其标记为lazy-init
,因此除非另一个bean使用它,否则它实际上不会被实例化:
<bean id="defaultEngine" class="Engine" lazy-init="true">
<property name="numberOfCylinders" value="4"/>
<property name="volume" value="400"/>
<property name="weight" value="475"/>
</bean>
现在我可以定义我的特定汽车,通过引用通过parent
定义它们的bean来获取所有默认值:
<bean id="myCar" parent="defaultCar"/>
我的妻子有一辆和我一样的车,除了它的车型不同(再一次,我对汽车一无所知 - 让我们假设发动机是相同的,即使在现实生活中它们可能不是)。我没有重新定义一堆bean /属性,而是再次扩展默认汽车定义,但覆盖其中一个属性:
<bean id="myWifesCar" parent="defaultCar">
<property name="model" value="Odyssey"/>
</bean>
我的姐姐和我的妻子(真的)有同一辆车,但它有不同的颜色。我可以扩展一个具体的bean并覆盖它上面的一个或多个属性:
<bean id="mySistersCar" parent="myWifesCar">
<property name="color" value="Silver"/>
</bean>
如果我喜欢赛车迷你车,我可能会考虑让一辆发动机更大。在这里,我扩展了一个小型货车豆,用新引擎覆盖它的默认引擎。这个新引擎扩展了默认引擎,覆盖了一些属性:
<bean id="supedUpMiniVan" parent="myWifesCar">
<property name="engine">
<bean parent="defaultEngine">
<property name="volume" value="600"/>
<property name="weight" value="750"/>
</bean>
</property>
</bean>
您还可以使用nested properties:
更简洁地完成此操作<bean id="supedUpMiniVan" parent="myWifesCar">
<property name="engine.volume" value="600"/>
<property name="engine.weight" value="750"/>
</bean>
这将使用“defaultEngine”。但是,如果您以这种方式创建两辆车,每辆车都有不同的属性值,则行为将不正确。这是因为两辆车将共享相同的引擎实例,第二辆车将覆盖第一辆车上设置的属性设置。这可以通过将defaultEngine标记为“原型”来解决,每次引用它时都会实例化一个新原型:
<bean id="defaultEngine" class="Engine" scope="prototype">
<property name="numberOfCylinders" value="4"/>
<property name="volume" value="400"/>
<property name="weight" value="475"/>
</bean>
我认为这个例子给出了基本的想法。如果您的数据结构很复杂,您可以定义多个抽象bean,或者创建几个不同的抽象层次结构 - 特别是如果您的bean层次结构比两个bean更深。
旁注:我的示例使用属性,我相信在Spring xml和Java代码中都可以更清楚地理解这些属性。但是,完全相同的技术适用于构造函数,工厂方法等。
答案 1 :(得分:6)
您的示例将无法按指定的方式工作,因为嵌套的bean定义未指定class
或parent
。您需要添加更多信息,例如:
<bean name="beanService13" parent="beanService12">
<constructor-arg index="0">
<bean parent="beanBaseNested">
<constructor-arg index="0" value="13"/>
</bean>
</constructor>
虽然我不确定按名称引用嵌套bean是否有效。
应谨慎对待嵌套bean定义;它们很快就会升级为不可读性。考虑将内部bean定义为顶级bean,这将使外部bean定义更容易阅读。
至于需要知道父bean的构造函数索引的子bean,这是Java构造函数注入的一个更基本的问题,因为Java构造函数参数不能通过名称引用,只能引用索引。 Setter注入几乎总是更具可读性,代价是失去了构造函数注入的最终结果。
正如您所提到的,自定义架构始终是一个选项,尽管设置起来有点痛苦。如果你发现自己经常使用这种模式,那么可能值得付出努力。
答案 2 :(得分:1)
您是否考虑过使用工厂?
您可以将bean配置为拥有工厂,并且可以在工厂创建中对变化的参数进行编码...
答案 3 :(得分:0)
要扩展Patrick的工厂模式:您可以使用原型bean来获取预先连接的依赖项:
<bean id="protoBean" scope="prototype">
<property name="dependency1" ref="some bean" />
<property name="dependency2" ref="some other bean" />
...
</bean>
现在,如果你使用setter注入(而不是构造函数参数),这种方法效果最好,我不确定你是否可以使用构造函数args。
public class PrototypeConsumingBean implements ApplicationContextAware {
public void dynmicallyCreateService(String serviceParam) {
// creates a new instance because scope="prototype"
MyService newServiceInstance = (MyService)springContext.getBean("protoBean");
newServiceInstance.setParam(serviceParam);
newServiceInstance.mySetup();
myServices.add(newServiceInstance);
}
public void setApplicationContext(ApplicationContext ctx) {
m_springContext = ctx;
}
}