Can we have duplicate names for the same bean id that is mentioned in the XML?
If not, then how do we override the bean in Spring?
Any given Spring context can only have one bean for any given id or name. In the case of the XML id attribute, this is enforced by the schema validation. In the case of the name attribute, this is enforced by Spring's logic.
However, if a context is constructed from two different XML descriptor files, and an id is used by both files, then one will "override" the other. The exact behaviour depends on the ordering of the files when they get loaded by the context.
So while it's possible, it's not recommended. It's error-prone and fragile, and you'll get no help from Spring if you change the ID of one but not the other.
I will add that if your need is just to override a property used by your bean, the id approach works too like skaffman explained :
In your first called XML configuration file :
<bean id="myBeanId" class="com.blabla">
<property name="myList" ref="myList"/>
</bean>
<util:list id="myList">
<value>3</value>
<value>4</value>
</util:list>
In your second called XML configuration file :
<util:list id="myList">
<value>6</value>
</util:list>
Then your bean "myBeanId" will be instantiated with a "myList" property of one element which is 6.
Not sure if that's exactly what you need, but we are using profiles to define the environment we are running at and specific bean for each environment, so it's something like that:
<bean name="myBean" class="myClass">
<constructor-arg name="name" value="originalValue" />
</bean>
<beans profile="DEV, default">
<!-- Specific DEV configurations, also default if no profile defined -->
<bean name="myBean" class="myClass">
<constructor-arg name="name" value="overrideValue" />
</bean>
</beans>
<beans profile="CI, UAT">
<!-- Specific CI / UAT configurations -->
</beans>
<beans profile="PROD">
<!-- Specific PROD configurations -->
</beans>
So in this case, if I don't define a profile or if I define it as "DEV" myBean will get "overrideValue" for it's name argument. But if I set the profile to "CI", "UAT" or "PROD" it will get "originalValue" as the value.
An example from official spring manual:
<bean id="inheritedTestBean" abstract="true"
class="org.springframework.beans.TestBean">
<property name="name" value="parent"/>
<property name="age" value="1"/>
</bean>
<bean id="inheritsWithDifferentClass"
class="org.springframework.beans.DerivedTestBean"
parent="inheritedTestBean" init-method="initialize">
<property name="name" value="override"/>
<!-- the age property value of 1 will be inherited from parent -->
</bean>
Is that what you was looking for?
Updated link
Since Spring 3.0 you can use #Primary annotation. As per documentation:
Indicates that a bean should be given preference when multiple
candidates are qualified to autowire a single-valued dependency. If
exactly one 'primary' bean exists among the candidates, it will be the
autowired value. This annotation is semantically equivalent to the
element's primary attribute in Spring XML.
You should use it on Bean definition like this:
#Bean
#Primary
public ExampleBean exampleBean(#Autowired EntityManager em) {
return new ExampleBeanImpl(em);
}
or like this:
#Primary
#Service
public class ExampleService implements BaseServive {
}
Another good approach not mentioned in other posts is to use PropertyOverrideConfigurer in case you just want to override properties of some beans.
For example if you want to override the datasource for testing (i.e. use an in-memory database) in another xml config, you just need to use <context:property-override ..."/> in new config and a .properties file containing key-values taking the format beanName.property=newvalue overriding the main props.
application-mainConfig.xml:
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource"
p:driverClassName="org.postgresql.Driver"
p:url="jdbc:postgresql://localhost:5432/MyAppDB"
p:username="myusername"
p:password="mypassword"
destroy-method="close" />
application-testConfig.xml:
<import resource="classpath:path/to/file/application-mainConfig.xml"/>
<!-- override bean props -->
<context:property-override location="classpath:path/to/file/beanOverride.properties"/>
beanOverride.properties:
dataSource.driverClassName=org.h2.Driver
dataSource.url=jdbc:h2:mem:MyTestDB
Whether can we declare the same bean id in other xml for other reference e.x.
Servlet-Initialize.xml
<bean id="inheritedTestBean" class="org.springframework.beans.TestBean">
<property name="name" value="parent"/>
<property name="age" value="1"/>
</bean>
Other xml (Document.xml)
<bean id="inheritedTestBean" class="org.springframework.beans.Document">
<property name="name" value="document"/>
<property name="age" value="1"/>
</bean>
Question was more about XML but as annotation are more popular nowadays and it works similarly I'll show by example.
Let's create class Foo:
public class Foo {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
and two Configuration files (you can't create one):
#Configuration
public class Configuration1 {
#Bean
public Foo foo() {
Foo foo = new Foo();
foo.setName("configuration1");
return foo;
}
}
and
#Configuration
public class Configuration2 {
#Bean
public Foo foo() {
Foo foo = new Foo();
foo.setName("configuration2");
return foo;
}
}
and let's see what happens when calling foo.getName():
#SpringBootApplication
public class OverridingBeanDefinitionsApplication {
public static void main(String[] args) {
SpringApplication.run(OverridingBeanDefinitionsApplication.class, args);
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext(
Configuration1.class, Configuration2.class);
Foo foo = applicationContext.getBean(Foo.class);
System.out.println(foo.getName());
}
}
in this example result is: configuration2.
The Spring Container gets all configuration metadata sources and merges bean definitions in those sources. In this example there are two #Beans. Order in which they are fed into ApplicationContext decide. You can flip new AnnotationConfigApplicationContext(Configuration2.class, Configuration1.class); and result will be configuration1.
Related
I'm working my way through the Spring Framework reference documentation with some very basic application code. So far, I've created an ApplicationContext from an XML file and loaded some beans. I believe I understand this process pretty well. I've loaded some basic beans with attributes based on fundamental types and found that straight-forward.
I'm now working on a composite bean with other beans as its attributes. So far, I've been able to set these attributes using a direct reference to a bean and an inner bean. However, when I try to get the idref element to work (see http://docs.spring.io/spring/docs/3.2.4.RELEASE/spring-framework-reference/html/beans.html#beans-idref-element) I get the following exception:
java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [com.example.BasicBean] for property 'idRef': no matching editors or conversion strategy found
Code snippets below:
Application Context XML
<?xml version="1.0" encoding="UTF-8"?>
<beans ...>
<bean id="id-bean" class="com.example.BasicBean" scope="singleton">
<property name="value" value="31"/>
</bean>
<bean id="ref-bean" class="com.example.BasicBean" scope="singleton">
<property name="value" value="37"/>
</bean>
<bean id="cb" class="com.example.CompositeBean" scope="singleton">
<property name="id" ref="id-bean"/> <!-- works -->
<property name="inner"> <!-- works -->
<bean class="com.example.BasicBean">
<property name="value" value="43"/>
</bean>
</property>
<property name="idRef"> <!-- exception thrown -->
<idref bean="ref-bean"/>
</property>
</bean>
</beans>
Java App Code
public void main()
{
context = new ClassPathXmlApplicationContext("beans.xml");
CompositeBean cb = context.getBean("cb", CompositeBean.class);
}
Java Bean Code
public class CompositeBean
{
private BasicBean id;
private BasicBean idRef;
private BasicBean inner;
// Default constructor exists
// Setters, getters for each attribute exist
...
}
public class BasicBean
{
private int value;
// Default constructor exists
// Setters, getters for each attribute exist
...
}
Version info: I'm using Eclipse IDE (Kepler), Maven 3.1, Java 7 (1.7.45), Spring 3.2.4
Any thoughts on what I'm doing wrong?
Thanks #Farid for pointing out that I was mis-using the idref attribute in this instance. As the Spring doco points out (which I'd read several times and still missed this) it's designed for passing the id of a bean around a container and not the actual bean. So, stick to the first two means above (ref and inner bean) for passing a bean around.
This following does not behave like I expect, using Spring 3.2.1.RELEASE, groovy 1.8.8. I've elided quite a bit of code. If this isn't sufficient, I'll put together a (not) working full example.
groovy bean:
package foo
import javax.annotation.Resource
class SomeBean {
#Resource
String someProp
}
spring xml:
<context:annotation-config />
<context:component-scan base-package="other.packages.not.foo" />
<bean id="someBean" class="foo.SomeBean">
<property name="someProp" value="bar" />
</bean>
This fails with an error about not being able to find a bean to satisfy the String property. To work around this, I ended up doing this:
groovy bean:
#Resource( name = 'someProp' )
String someProp
spring xml:
<bean id="someProp" class="java.lang.String">
<constructor-arg value="bar" />
</bean>
<bean id="someBean" class="foo.SomeBean" />
I don't like this solution, because it couples the bean source code to the spring config, and that's coupling in the wrong direction. And it creates an unnecessary bean for a simple String. Am I missing an obvious solution?
#Resource is not meant for property injection.
For this to work:
<bean id="someBean" class="foo.SomeBean">
<property name="someProp" value="bar" />
</bean>
you need to implement a setter method
public void setSomeBean(String value) {
someValue = value;
}
#Resource could be used if you want to inject a different bean from your context.
#Resource(name="someBean")
SomeBean someBean
I have the bean config:
<bean id="PostLoginUpdater" class="xyz.auth.PostLoginUpdater" autowire="byType" />
and
public class PostLoginUpdater implements PostLoginStatePersonalizer {
//#Qualifier("CustomerManager")
#Inject
//#Resource(name = "CustomerManager")
private CustomerManager customerManager;
public void setCustomerManager(CustomerManager customerManager)
{
this.customerManager = customerManager;
}
}
Because there are two beans that implement CustomerManager I get this error:
No unique bean of type [CustomerManager] is defined: expected single
matching bean but found 2
As you can see, I'v tried several combinations (#Inject along with #Qualifier, #Ressource, only #Qualifier) But I don't get rid of this error message.
According to Spring In Depth, #Qualifier can be used along with #Inject. Can't I used them together if I defined autowire="byType" in bean config?
And I don't use <context:annotation-config /> or <context:component-scan />
You should be able to use a combination of '#Inject' and '#Qualifier', if you have multiple beans of the same type. Here is how to configure it -
<bean id="PostLoginUpdater" class="xyz.auth.PostLoginUpdater" autowire="byType" />
<bean id="firstManager" class="xyz.manager.CustomerManager" autowire="byType" >
<qualifier>first</qualifier>
</bean>
<bean id="secondManager" class="xyz.manager.CustomerManager" autowire="byType" >
<qualifier>second</qualifier>
</bean>
If you had two beans of type 'CustomerManager' as shown above, you could use the snippet shown below for injection -
public class PostLoginUpdater implements PostLoginStatePersonalizer {
#Inject
#Qualifier("first")
private CustomerManager customerManager;
public void setCustomerManager(CustomerManager customerManager)
{
this.customerManager = customerManager;
}
}
Also, on a side note -
If you keep using one of the beans more often than another you could use the 'primary' attribute.
For example, in the above example, if you always tend to use 'firstManager', you could mark it as primary as shown below.
<bean id="PostLoginUpdater" class="xyz.auth.PostLoginUpdater" autowire="byType" />
<bean id="firstManager" class="xyz.manager.CustomerManager" autowire="byType" primary="true" >
</bean>
<bean id="secondManager" class="xyz.manager.CustomerManager" autowire="byType" >
<qualifier>second</qualifier>
</bean>
If you have above configuration, the following code will always injects 'firstManager' when no qualifier is used -
public class PostLoginUpdater implements PostLoginStatePersonalizer {
#Inject
private CustomerManager customerManager;
public void setCustomerManager(CustomerManager customerManager)
{
this.customerManager = customerManager;
}
}
It doesn't make any sense to try to autowire by type, and simultaneously specify a name. Choose one approach or the other.
I have also had trouble in the past trying to use #Qualifier with #Inject. One thing to note is that there are two annotations with that name, one in Spring and one in Java:
org.springframework.beans.factory.annotation.Qualifier
javax.inject.Qualifier
However, if using the spring framework one, then the suggested usage is to explicitly name your component via #Component or #Named [#Component("beanName")] (if annotated), or in the <bean> definition. Be aware that autowiring wants the bean name, not the Class name as in your example (i.e. do not use "CustomerManager"). For example, if you have two bean definitions like in Sashi's example:
<bean id="firstManager" class="xyz.manager.CustomerManager" autowire="byType" >
<qualifier>first</qualifier>
</bean>
<bean id="secondManager" class="xyz.manager.CustomerManager" autowire="byType" >
<qualifier>second</qualifier>
</bean>
then declare the field like this:
#Inject
#Qualifier("firstManager")
private CustomerManager customerManager;
I need an ANT task to validate spring configuration. I need to find problems at build time before runtime ? For example, In spring context file contains a property a bean, but this bean doesnt have this property.
In eclipse, there is a tool Spring Explorer that do this validation.
thanks,
org.springframework.web.context.ContextLoaderListener failed: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'readController' defined in class path resource [applicationContext.xml]: Error setting property values; nested exception is org.springframework.beans.NotWritablePropertyException: Invalid property 'productOperations' of bean class [com.bee.view.json.ReadController]: Bean property 'productOperations' is not writable or has an invalid setter method.
Does the parameter type of the setter match the return type of the getter?.
An easy way to ensure that your context is valid would be to create a JUnit test, which loads the context. Using the spring-test.jar support classes makes that easy:
public class MyTest extends AbstractDependencyInjectionSpringContextTests {
// this will be injected by Spring
private QueryDao queryDao;
private MyBusinessObject myBusinessObject;
// ensure that spring will inject the objects to test by name
public MyTest () {
setAutowireMode(AUTOWIRE_BY_NAME);
}
#Override
protected String[] getConfigLocations() {
return new String[] { "applicationContextJUnit.xml" };
}
public void testQueryDao() {
List<SomeData> list = queryDao.findSomeData();
assertNotNull(list);
// etc
}
public void testMyBusinessObject() {
myBusinessObject.someMethod();
}
public void setQueryDao(QueryDao queryDao) {
this.queryDao = queryDao;
}
}
The problem with loading a context that is used in a web application is that JUnit does not necessarily have access to the same resources (e.g. JNDI data sources), so if you've got the following in your "applicationContext.xml":
<beans ...>
<bean id="myBusinessObject" class="com.test.MyBusinessObject">
<property name="queryDao" ref="queryDao"/>
</bean>
<bean id="queryDao" class="com.test.QueryDao">
<property name="dataSource" ref="dataSource"/>
</bean>
<jee:jndi-lookup
id="dataSource"
jndi-name="jdbc/mydatasource"
resource-ref="true"
cache="true"
lookup-on-startup="false"
proxy-interface="javax.sql.DataSource"/>
</beans>
and your "applicationContextJUnit.xml" would import your "real" application context and redefine resources:
<beans ...>
<import resource="classpath:applicationContext.xml"/>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="oracle.jdbc.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:..."/>
<property name="username" value="scott"/>
<property name="password" value="tiger"/>
</bean>
</beans>
That way your unit tests will load the application context (even the ones that you don't explicitly test in your unit test), and you can have the confidence that your context is correct, because Spring itself loaded it. If you have an error, then the unit tests will fail.
I have a properties-file with a lot of values and I do not want to list them in my bean-configuration-file separately. E.g.:
<property name="foo">
<value>${foo}</value>
</property>
<property name="bar">
<value>${bar}</value>
</property>
and so on.
I imagine to inject all completely as java.util.Properties or less as a java.util.Map.
Is there a way to do so?
For Java config you can use something like this:
#Autowired #Qualifier("myProperties")
private Properties myProps;
#Bean(name="myProperties")
public Properties getMyProperties() throws IOException {
return PropertiesLoaderUtils.loadProperties(
new ClassPathResource("/myProperties.properties"));
}
You can also have multiple properties this way, if you assign a unique bean name (Qualifier) to each instance.
Yes, you can use <util:properties> to load a properties file and declare the resulting java.util.Properties object as a bean. You can then inject that as you would any other bean property.
See section C.2.2.3 of the Spring manual, and their example:
<util:properties id="myProps" location="classpath:com/foo/jdbc-production.properties"
Remember to declare the util: namespace as per these instructions.
For Java Config, use PropertiesFactoryBean:
#Bean
public Properties myProperties() {
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
propertiesFactoryBean.setLocation(new ClassPathResource("/myProperties.properties"));
Properties properties = null;
try {
propertiesFactoryBean.afterPropertiesSet();
properties = propertiesFactoryBean.getObject();
} catch (IOException e) {
log.warn("Cannot load properties file.");
}
return properties;
}
And then, set the properties object:
#Bean
public AnotherBean myBean() {
AnotherBean myBean = new AnotherBean();
...
myBean.setProperties(myProperties());
...
}
Hope this helps for those interested in Java Config way.
It's possible with the PropertyOverrideConfigurer mechanism:
<context:property-override location="classpath:override.properties"/>
Properties file:
beanname1.foo=foovalue
beanname2.bar.baz=bazvalue
The mechanism is explained in the section 3.8.2.2 Example: the PropertyOverrideConfigurer
This is an echo of #skaffman's response in this SO question. I am adding more details to help others and myself when I try to solve this in the future.
There are three ways to inject the property file
Method 1
<bean id="myProps" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="locations">
<list>
<value>classpath:com/foo/jdbc-production.properties</value>
</list>
</property>
</bean>
Reference ( link )
Method 2
<?xml version="1.0" encoding="UTF-8"?>
<beans
...
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="...
...
http://www.springframework.org/schema/util/spring-util.xsd"/>
<util:properties id="myProps" location="classpath:com/foo/jdbc-production.properties"/>
Reference ( link )
Method 3
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:com/foo/jdbc-production.properties" />
</bean>
Reference ( link )
Essentially, all the methods can create a Properties bean out of the properties file. You may even directly inject a value from the property file by using #Value injector
#Value("#{myProps[myPropName]}")
private String myField;