I've read the Spring 3 reference on inheriting bean definitions, but I'm confused about what is possible and not possible.
For example, a bean that takes a collaborator bean, configured with the value 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 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.
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?
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.
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.
Nested vs. top-level beans are not the issue (in fact, I think all the beans are top level in practice.)
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?
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 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-init so it will not actually be instantiated unless another bean uses it:
<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:
<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 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 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 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:
<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.
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.
Your example will not work as specified, because the nested bean definition has no class or parent specified. You'd need to add more information, like this:
<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.
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.
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.
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.
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...
To expand on the factory pattern from Patrick: you can use a prototype bean to get pre-wired dependencies:
<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.
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;
}
}
Related
Problem:
I'm integrating with a library written by the other team. This library provides a set of classes that I'm using in my Spring-driven application. Every bean in my application is in singleton scope.
Also 99% of the classes from that library uses constructor injection.
I'm using XML and my configuration looks very similar to the following:
<bean id="lib.service1" class="lib.Service1"/>
<bean id="lib.service2" class="lib.Service2">
<constructor-arg ref="lib.service1"/>
</bean>
<bean id="lib.service3" class="lib.Service3">
<constructor-arg ref="lib.service1"/>
</bean>
<bean id="lib.service4" class="lib.Service3">
<constructor-arg ref="lib.service2"/>
<constructor-arg ref="lib.service3"/>
</bean>
<!-- other bean definitions -->
<bean id="lib.serviceN" class="lib.ServiceN">
<constructor-arg ref="lib.serviceX"/>
<constructor-arg ref="lib.serviceY"/>
<constructor-arg ref="lib.serviceZ"/>
<constructor-arg ref="lib.serviceK"/>
</bean>
<!-- other bean definitions -->
What I want:
I want to simplify my configuration to not to use bean IDs and ask spring to do constructor injection for me based on the type of arguments in the bean constructors. I can also ask library implementers to add #Inject annotation to the class constructors (the 99% of the classes have just one public constructor), but this is all that I can ask wrt refactoring of their library.
And eventually I want to have just following in my spring config (doesn't work, but illustrates the idea):
<bean class="lib.Service1"/>
<bean class="lib.Service2"/>
<bean class="lib.Service3"/>
<!-- ... -->
<bean class="lib.ServiceN"/>
Here I'm expecting Spring to figure out that I want to use constructor injection for all those beans and infer bean instances based on the constructor argument types.
Note that I cannot use component scan - they have one package (lib. in the example given above) and some classes in that package are useless for my application and too expensive to be needlessly created. Plus some classes that I'm not using are experimental and can be changed/renamed/removed without prior notice.
Add autowire="constructor", assuming these bean types only have one constructor and that the corresponding parameters match single beans.
<bean class="lib.Service1" autowire="constructor"/>
From the documentation
"constructor"
Analogous to "byType" for constructor arguments. If there
is not exactly one bean of the constructor argument type in the
bean factory, a fatal error is raised. Note that explicit
dependencies, i.e. "property" and "constructor-arg" elements, always
override autowiring. Note: This attribute will not be inherited by
child bean definitions. Hence, it needs to be specified per concrete
bean definition.
I am new to the Spring framework and can't find a way to achieve the following:
I am using a class whose attributes are all private and there are no setters (the intended way to use objects of that class is to set attributes once with a constructor) - I will refer to it as Preferences. I also have a few classes that each has the same instance of Preferences as an attribute. Preferences is intended to contain certain properties, among which some can only be resolved at runtime (e.g. provided by the user).
In my .xml file I would write something along the lines of:
<bean id="preferenes" class="Preferences" scope="singleton">
<constructor-arg index="0" value="defaultAttrOne" />
<constructor-arg index="1" value="defaultAttrTwo" />
<constructor-arg index="2" value="defaultAttrThree" />
</bean>
<bean id="someOtherBean" class="SomeOtherClass" scope="singleton">
<constructor-arg index="0" ref="preferences" />
</bean>
That is, I could provide default values and replace some of them with custom ones at runtime. As I cannot modify attributes of an existing instance of Preferences, I would have to construct a new object and somehow make the instance of SomeOtherClass point to that new object (is this possible through the bean mechanism?).
Rather, I'd pass the desired runtime constructor arguments to the preferences bean before instantiating any of the beans (those arguments will be known before the first call to the ApplicationContext's constructor). I know there is a flavour of the getBean() method that takes varargs as initialization parameters, though it only applies to prototype beans. In this case I want to initialize Preferenes once and have all helper classes refer to that single instance.
Thank you for any hints.
This is pretty much what Spring does for you by default so there is nothing special you'll have to do: if you create that singleton bean reference (called preferences), you'll be able to inject it to any other bean as you would expect.
Regarding your attributes with default values, there's several ways to achieve that:
Regular XML config
You can keep a purely XML-based configuration if you want and configure a PropertyPlaceholderConfigurer with default values. Something like:
<bean class="org.s.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="prefrences.properties"/>
</bean>
<bean id="preferenes" class="Preferences" scope="singleton">
<constructor-arg index="0" value="$[preferences.one:12]" />
<constructor-arg index="1" value="$[preferences.two:AUTO]" />
<constructor-arg index="2" value="$[preferences.three:false]" />
</bean>
And have a prefrences.properties at the root of the classpath hold the specific values if you don't want the default
prefrences.three=true
FactoryBean
As you're already using XML, you can go with a FactoryBean that would create the Preferences instance, something like
<bean id="preferences" class="org.myproject.PreferencesFactoryBean"/>
in the code of the factory you could use whatever mechanism you want to retrieve the non default values for your configuration, including injecting custom properties.
Java config
You can also go the java config way but as you're a beginner this may be a bit of a change. However, java config is really powerful and flexible so you may want to give it a try.
#Configuration
#PropertySource("classpath:preferences.properties")
public class AppConfig {
#Value("${preferences.one}")
private int preferenceOne = 12;
#Value("${preferences.two}")
private MyEnum preferenceTwo = MyEnum.AUTO;
#Value("${preferences.three}")
private boolean preferenceThree;
#Bean
public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
#Bean
public Preferences preferences() {
return new Preferences(preferenceOne, preferenceTwo, preferenceThree);
}
}
I have 3 projects:
framework
product-a
product-b
Each of the products depends on the framework, but they don't know each other.
I have 3 spring configuration files: one for each project. The configuration file of each product includes (with <import resource="classpath:/...) the configuration file of the framework.
In the framework there is a bean called "manager", which has a property List<AnInterface> theList. The "manager" has a addXxx(anImplementation), which adds elements to the list).
The framework, and each of the product provide implementations of AnInterface, which have to be added to theList.
So in the end, when product-a is running, the manager contains implementations from the framework, and from product-a, idem for product-b
What is the best practice to perform this initialization with Spring ?
The only solution I could think about is to create a dedicated class which contructor will take the manager and a list of contributions, and add them to the manager, but it's ugly because 1/ It manipulate external objects in the constructor, 2/ I have to create a dummy class just to initialize other classes... I don't like that.
I think that code should not know about Spring if it is not really needed. Therefore I would do all initialization in Spring config.
We can use bean definition inheritance and property overriding to do it.
Framework class
public class Manager {
private List<AnInterface> theList;
public void init() {
// here we use list initialized by product
}
}
Framework context
<bean id="manager"
init-method="init"
abstract="true"
class="Manager">
<property name="theList">
<list/> <!-- this will be overriden or extnded -->
</property>
</bean>
Product A context
<bean id="managerA"
parent="manager"
scope="singleton"
lazy-init="false">
<property name="theList">
<list>
<ref bean="impl1"/>
<ref bean="impl2"/>
</list>
</property>
</bean>
Watch out for parent and child properties in such configuration. Not all are inherited from parent. Spring documentation specifies:
The remaining settings are always taken from the child definition: depends on, autowire mode, dependency check, singleton, scope, lazy init.
Moreover, there is also collection merging in Spring so by specifing in child bean
<list merge="true">
you can merge parent and child lists.
I have observed this pattern in a number of projects and some extendable Web frameworks based on Spring.
I have accepted the answer of Grzegorz because it's a clean solution to my initial problem, but here as an alternate answer, the a technical solution to contribute to a list property of an existing bean.
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject" ref="manager"/>
<property name="targetMethod"><value>addXxx</value></property>
<property name="arguments"><list value-type="com.xxx.AnInterface">
<value ref="impl1" />
<value ref="impl2" />
...
</list></property>
</bean>
I have the following definition:
<bean id="logger" factory-method="createLog" scope="prototype" class="com.test.beans.LogBean" ></bean>
<bean id="aone" class="com.test.beans.AggregationOne">
<property name="log" ref="logger"></property>
</bean>
<bean id="atwo" class="com.test.beans.AggregationTwo">
<property name="log" ref="logger"></property>
</bean>
Is it possible to recognize for which object (aone or atwo) bean 'logger' is being created?
Why I'm asking: in a legacy application I have one log instance for all classes. I want to change level for some packages, but can't do that (except using filters, what I don't want). For that purpose I want to utilize some spring magic, if it exists for that case )
I don't think it can be done this way. What you could try is a BeanPostProcessor implementation which detects common logger object in beans and replaces it with a specific one.
The situation is that I have two different resource bundles, a general one and a more specific one. They don't share any message keys.
General one:
<bean id="messageSourceGlobal" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="messages/messagesGlobal" />
</bean>
I include the general one in my specific one (different file obviously):
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="messages/messages" />
<property name="parentMessageSource" ref="messageSourceGlobal" />
</bean>
The Java-code then autowires it:
#Autowired
private MessageSource messages;
This will cause an exception when starting the web-app as two MessageSources are found. Obviously I can use a #Qualifier to make it clear to Spring what I want. However, the general resource bundle won't be used alone. Therefore, I thought that in this case it would make sense to hide the general resource bundle from dependency injection. One benefit would be that others won't run into the "duplicates".
Is this possible? How would I do this?
Sure you can add autowire-candidate="false" on the definition of the bean you want to hide.
In many cases it could be better to promote one bean with primary="true" or #Primary instead of demoting all other candidates with autowire-candidate="false".