java spring - 构造函数注入和覆盖嵌套 bean 的父定义
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2799411/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
spring - constructor injection and overriding parent definition of nested bean
提问by mdma
I've read the Spring 3 reference on inheriting bean definitions, but I'm confused about what is possible and not possible.
我已经阅读了关于继承 bean 定义的 Spring 3 参考,但我对什么是可能的和什么是不可能的感到困惑。
For example, a bean that takes a collaborator bean, configured with the value 12
例如,一个带有协作者 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>
I'd then like to be able to create similar beans, with slightly different configured collaborators. Can I do something like
然后,我希望能够创建类似的 bean,配置稍有不同的协作者。我可以做类似的事情吗
<bean name="beanService13" parent="beanService12">
<constructor-arg index="0">
<bean>
<constructor-arg index="0" value="13"/>
</bean>
</constructor>
</bean>
I'm not sure this is possible and, if it were, it feels a bit clunky. Is there a nicer way to override small parts of a large nested bean definition? It seems the child bean has to know quite a lot about the parent, e.g. constructor index.
我不确定这是否可行,如果可行,感觉有点笨重。有没有更好的方法来覆盖大型嵌套 bean 定义的小部分?似乎子 bean 必须非常了解父 bean,例如构造函数索引。
This is a toy example - in practice the service is a large bean definition relying on many other collaborator beans, which have also other bean dependencies. For example, a chain of handlers were created with each bean referencing the next in the chain, which references the next. I want to create an almost identical chain with some small changes to handlers in the middle, how do I it?
这是一个玩具示例 - 在实践中,该服务是一个大型 bean 定义,它依赖于许多其他协作 bean,这些 bean 也具有其他 bean 依赖项。例如,创建了一个处理程序链,每个 bean 都引用链中的下一个 bean,后者引用下一个 bean。我想创建一个几乎相同的链,中间对处理程序进行一些小的更改,我该怎么做?
I'd prefer not to change the structure - the service beans use collaborators to perform their function, but I can add properties and use property injection if that helps.
我不想改变结构 - 服务 bean 使用协作者来执行它们的功能,但如果有帮助,我可以添加属性并使用属性注入。
This is a repeated pattern, would creating a custom schema help?
这是一个重复的模式,创建自定义模式有帮助吗?
Thanks for any advice!
感谢您的任何建议!
EDIT: The nub of my question is, if I have a really large bean definition, with a complex hiearchy of beans being created (bean having properites that are bean etc.), and I want to create a bean that is almost the same with a few changes, how to I do it? Please mention if your solution has to use properites, or if constructor injection can be used.
编辑:我的问题的核心是,如果我有一个非常大的 bean 定义,并且创建了一个复杂的 bean 层次结构(bean 具有 bean 等属性),并且我想创建一个与一些变化,我该怎么做?请说明您的解决方案是否必须使用属性,或者是否可以使用构造函数注入。
Nested vs. top-level beans are not the issue (in fact, I think all the beans are top level in practice.)
嵌套与顶级 bean 不是问题(事实上,我认为所有 bean 在实践中都是顶级的。)
EDIT2: Thank you for your answers so far. A FactoryBean might be the answer, since that will reduce the complexity of the spring context, and allow me to specify just the differences as parameters to the factory. But, pushing a chunk of context back into code doesn't feel right. I've heard that spring can be used with scripts, e.g. groovy - does that provide an alternative? Could the factory be created in groovy?
EDIT2:感谢您到目前为止的回答。FactoryBean 可能是答案,因为这将降低 spring 上下文的复杂性,并允许我仅将差异指定为工厂的参数。但是,将一大块上下文推回到代码中感觉不对。我听说 spring 可以与脚本一起使用,例如 groovy - 这是否提供了替代方案?可以在 groovy 中创建工厂吗?
回答by SingleShot
I'm not entirely sure what you are trying to achieve. I don't think you can achieve exactly what you want without creating your own custom schema (which is non-trivial for nested structures), but the following example is probably pretty close without doing that.
我不完全确定你想要实现什么。我认为如果不创建自己的自定义架构(这对于嵌套结构来说很重要),您就无法完全实现您想要的目标,但是如果不这样做,以下示例可能非常接近。
First, define an abstract bean to use as a template for your outer bean (my example uses a Car as the outer bean and an Engine as the inner bean), giving it default values that all other beans can inherit:
首先,定义一个抽象 bean 用作外部 bean 的模板(我的示例使用 Car 作为外部 bean,使用 Engine 作为内部 bean),并为其提供所有其他 bean 可以继承的默认值:
<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>
Since all Honda Civics have the same engine (in my world, where I know nothing about cars), I give it a default nested engine bean. Unfortunately, a bean cannot reference an abstract bean, so the default engine cannot be abstract. I've defined a concrete bean for the engine, but mark it as lazy-initso it will not actually be instantiated unless another bean uses it:
由于所有本田思域都有相同的引擎(在我的世界里,我对汽车一无所知),我给它一个默认的嵌套引擎 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>
Now I can define my specific car, taking all the default values by referencing the bean where they are defined via parent:
现在我可以定义我的特定汽车,通过引用通过parent以下方式定义它们的 bean 来获取所有默认值:
<bean id="myCar" parent="defaultCar"/>
My wife has a car just like mine, except its a different model (again, I know nothing about cars - let's assume the engines are the same even though in real life they probably are not). Instead of redefining a bunch of beans/properties, I just extend the default car definition again, but override one of its properties:
我的妻子有一辆和我一样的车,除了它的型号不同(同样,我对汽车一无所知 - 让我们假设发动机是一样的,即使在现实生活中它们可能不是)。我没有重新定义一堆 bean/属性,而是再次扩展默认汽车定义,但覆盖了它的一个属性:
<bean id="myWifesCar" parent="defaultCar">
<property name="model" value="Odyssey"/>
</bean>
My sister has the same car as my wife (really), but it has a different color. I can extend a concrete bean and override one or more properties on it:
我姐姐和我妻子有同样的车(真的),但它的颜色不同。我可以扩展一个具体的 bean 并覆盖它的一个或多个属性:
<bean id="mySistersCar" parent="myWifesCar">
<property name="color" value="Silver"/>
</bean>
If I liked racing minivans, I might consider getting one with a bigger engine. Here I extend a minivan bean, overriding its default engine with a new engine. This new engine extends the default engine, overriding a few properties:
如果我喜欢赛车小型货车,我可能会考虑买一辆发动机更大的。在这里,我扩展了一个小型货车 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>
You can also do this more concisely by using nested properties:
您还可以使用嵌套属性更简洁地执行此操作:
<bean id="supedUpMiniVan" parent="myWifesCar">
<property name="engine.volume" value="600"/>
<property name="engine.weight" value="750"/>
</bean>
This will use the "defaultEngine". However, if you were to create two cars this way, each with different property values, the behavior will not be correct. This is because the two cars would be sharing the same engine instance, with the second car overriding the property settings set on the first car. This can be remedied by marking the defaultEngine as a "prototype", which instantiates a new one each time it is referenced:
这将使用“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>
I think this example gives the basic idea. If your data structure is complex, you might define multiple abstract beans, or create several different abstract hierarchies - especially if your bean hierarchy is deeper than two beans.
我认为这个例子给出了基本的想法。如果您的数据结构很复杂,您可能会定义多个抽象 bean,或者创建几个不同的抽象层次结构 - 特别是当您的 bean 层次结构比两个 bean 更深时。
Side note: my example uses properties, which I believe are much clearer to understand, both in Spring xml and in Java code. However, the exact same technique works for constructors, factory methods, etc.
旁注:我的示例使用了属性,我相信这些属性在 Spring xml 和 Java 代码中都更易于理解。但是,完全相同的技术适用于构造函数、工厂方法等。
回答by skaffman
Your example will not work as specified, because the nested bean definition has no classor parentspecified. You'd need to add more information, like this:
您的示例将无法按指定方式工作,因为嵌套 bean 定义没有class或parent指定。您需要添加更多信息,如下所示:
<bean name="beanService13" parent="beanService12">
<constructor-arg index="0">
<bean parent="beanBaseNested">
<constructor-arg index="0" value="13"/>
</bean>
</constructor>
Although I'm not sure if it's valid to refer to nested beans by name like that.
尽管我不确定按这样的名称引用嵌套 bean 是否有效。
Nested bean definitions should be treated with caution; they can quickly escalate into unreadability. Consider defining the inner beans as top-level beans instead, which would make the outer bean definitions easier to read.
应谨慎对待嵌套的 bean 定义;它们会迅速升级为不可读。考虑将内部 bean 定义为顶级 bean,这将使外部 bean 定义更易于阅读。
As for the child beans needing to know the constructor index of the parent bean, that's a more basic problem with Java constructor injection, in that Java constructor arguments cannot be referred to by name, only index. Setter injection is almost always more readable, at the cost of losing the finality of constructor injection.
至于子 bean 需要知道父 bean 的构造函数索引,这是 Java 构造函数注入的一个更基本的问题,因为 Java 构造函数参数不能通过名称引用,只能通过索引引用。Setter 注入几乎总是更具可读性,代价是失去构造函数注入的最终性。
A custom schema is always an option, as you mentioned, although it's a bit of a pain to set up. If you find yourself using this pattern a lot, it might be worth the effort.
正如您所提到的,自定义架构始终是一种选择,尽管设置起来有点麻烦。如果您发现自己经常使用这种模式,那么这可能是值得的。
回答by Patrick Cornelissen
Have you thought of using a factory instead?
你有没有想过用工厂代替?
You can config beans to have a factory and you could encode the varying parameters in the factory creation...
您可以将 bean 配置为拥有一个工厂,并且您可以在工厂创建中对不同的参数进行编码...
回答by Justin
To expand on the factory pattern from Patrick: you can use a prototype bean to get pre-wired dependencies:
要扩展 Patrick 的工厂模式:您可以使用原型 bean 来获取预先连接的依赖项:
<bean id="protoBean" scope="prototype">
<property name="dependency1" ref="some bean" />
<property name="dependency2" ref="some other bean" />
...
</bean>
Now, this works best if you use setter injection (rather than constructor arguments), i'm not sure you can even do it you require constructor args.
现在,如果您使用 setter 注入(而不是构造函数参数),这效果最好,我不确定您是否可以这样做,您需要构造函数参数。
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;
}
}

