Can you enlighten me on this problem I encountered while experimenting with Spring.
I have 2 context here. let's name them springA.xml and springB.xml
springA.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="springB.xml" />
<bean name="name2" class="java.lang.String"/>
</beans>
springB.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="name2,name3" class="java.lang.String"/>
</beans>
springC.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="name3,name2" class="java.lang.String"/>
</beans>
And this is my Java File.
public static void main(String[] args) {
BeanFactory factory = new XmlBeanFactory(new ClassPathResource("springA.xml"));
Object obj1 = factory.getBean("name2");
Object obj2 = factory.getBean("name3");
System.out.println(obj1.getClass().getName() + " " + obj2.getClass().getName());
}
And the result, I get a "java.lang.String java.lang.String". If I change the position of
the name "name2,name3" to "name3,name2" (springC.xml), I get a "java.lang.Object java.lang.Object".
I am just confused as to why the result is like that. I was expecting that the function will return java.lang.String for name2 and java.lang.Object for name3 (since name2 is already used in the springA.xml, I am assuming this name will not be used and instead, will use name3 for springB.xml)
Thanks!
PS:
Spring 2.5
Eclipse 3.5
From Spring's documentation:
Every bean has one or more ids (also
called identifiers, or names; these
terms refer to the same thing). These
ids must be unique within the
BeanFactory or ApplicationContext the
bean is hosted in.
According to this, your combined application context is invalid as it contains two different beans which have the same ID - your bean named "name2" from ContextA.xml and your bean named "name2", aliased "name3" in ContextC.xml. I would expect Spring to issue at least a warning about this.
To answer your question: You shouldn't expect any sane results from this kind of setup. Bean names have to be unique and if they aren't the results are undefined. And by "undefined" I mean "unlikely to be helpful" :)
Hope this helps.
I believe you are seeing different results because Spring is loading the beans in the context in different orders in each scenario. Spring makes no guarantee as to which order it will load it's beans other than the fact that any beans used as "ref"'s in other bean definitions will be loaded before the beans that depend on them.
The correct solution to your problem is DO NOT use duplicate bean identifiers and then you won't have to guess as to which bean you will get when you look one up.
I've ran your code on Spring 2.5.6 and 3.0.0.M1 and both version produce the same result.
java.lang.String java.lang.String
My advice is if you want two strings and you are getting strange results with 2.5, then bump to 2.5.6 or 3.0.0.M1.
Related
I am trying to write a spring program with using Annotation config in xml file, It is working without this configuration initiating also. I wrote method() in one file and and declared CLassPathXmlApplicationContext with Beans.xml file, I wrote a Student bean and applied the #Required annotation for setter methods, Please find the Bean configuration xml file below:
<?xml version = "1.0" encoding = "UTF-8"?>
<beans xmlns = "http://www.springframework.org/schema/beans"
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xmlns:context = "http://www.springframework.org/schema/context"
xsi:schemaLocation = "http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:annotation-config/>
<!-- Definition for student bean -->
<bean id = "student" class = "com.tutorialspoint.Student">
<property name = "name" value = "Zara" />
<property name = "age" value = "11"/>
</bean>
</beans>
When i run this program with or without following Annotation indication in xml file, the code works fine, Please guide me the underlying reason for it.
<context:annotation-config/>
The #Required annotation applies to bean property setter methods and it indicates that the affected bean property must be populated in XML configuration file at configuration time.
Once <context:annotation-config/> is configured, you can start annotating your code to indicate that Spring should automatically wire values into properties, methods, and constructors. It does not means that some things will wrong without it when you using the #Required
In your xml, you have defined your properties, so the #Required will run well
I'm learning spring dependency injection with Struts2, beased on a web project. In my example, I created a zoo having animals. Animal will talk if injection is succeed. E.g. in the console, we will see dog's talk :
Wowowo ฅ^•ﻌ•^ฅ
However, if injection failed, then we'll see :
zooService bean has not been injected.
Here's the architecture of my application :
com.zoo.controller.ZooController is the controller for receiving web actions.
com.zoo.service.ZooService is the interface for animal's talk
com.zoo.service.ZooServiceForDog is the implementation for dog's talk
Problem
Up to the step, everything is OK. And the dependency injection is handled by Spring using an XML file called applicationContext.xml. However, I've 2 types of configuration for this file, the first one Config 1 works but the second Config 2 doesn't.
Injection succeed using config 1.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="zooService" class="com.zoo.service.ZooServiceForDog" />
</beans>
Injection failed using config 2.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="zooController" class="com.zoo.controller.ZooController">
<property name="zooService" ref="zooServiceBean" />
</bean>
<bean id="zooServiceBean" class="com.zoo.service.ZooServiceForDog" />
</beans>
Can somebody explain why the Config 2 cannot work ?
Here're other codes that might be helpful to the issue :
Class com.zoo.controller.ZooController:
package com.zoo.controller;
import com.zoo.service.ZooService;
import com.opensymphony.xwork2.ActionSupport;
public class ZooController extends ActionSupport {
private static final long serialVersionUID = 1L;
private ZooService zooService;
public String live () {
if (zooService != null) {
zooService.talk();
} else {
System.out.println("zooService bean has not been injected.");
}
return SUCCESS;
}
public ZooService getZooService() {
return zooService;
}
public void setZooService(ZooService zooService) {
this.zooService = zooService;
}
}
It cannot work because the scope of the zooController is singleton. You should make the scope prototype.
<bean id="zooController" class="com.zoo.controller.ZooController" scope="prototype" >
<property name="zooService" ref="zooServiceBean" />
</bean>
The dependency management is defined by the container:
If your actions managed by Struts container, then Struts is creating them in the default scope. If your actions is managed by Spring container then you need to define the scope of the action beans, because Spring by default uses singleton scope and if you don't want to share your action beans between user's requests you should define the corresponding scope. You can use prototype scope, which means a new instance is returned by the Spring each time Struts is being built an action instance.
The Struts integrates to Spring via plugin. Make sure it has
<constant name="struts.objectFactory" value="spring" />
then you can delegate actions to Spring
References:
Struts2 and Spring
Spring plugin
EDIT:
In your first config you declared a bean zooService that will be injected by Struts using spring object factory.
In your second config you declared two beans zooController and zooServiceBean, but you changed the name of the second bean. Then you tried to build the action bean using spring object factory like in the first case. And because there's no bean with name zooService the autowiring has been failed. Because by default Struts is configured to autowire beans from the application context by name.
Then you changed struts.xml and used a bean reference in the action class attribute. It means that Struts will use app context to get a bean from Spring. And because it has an explicit dependency on the second bean, it would be wired before the bean is returned.
I am new to Spring.
Currently, I am trying to achieve following:
A method in class A calls a method in class B with a String parameter. Class B then creates a bean after processing the string (just doing some basic string operations) as:
GenericApplicationContext context = new GenericApplicationContext();
DefaultListableBeanFactory factory = (DefaultListableBeanFactory) context.getBeanFactory();
BeanDefinitionBuilder bean1 = BeanDefinitionBuilder.rootBeanDefinition("className");
bean1.addPropertyReference("sampleString", "sampleBean");
bean1.addPropertyValue("sampleString", "processed string here");
factory.registerBeanDefinition("sampleBean", bean1.getBeanDefinition());
context.refresh();
(I am not sure if my approach to creating bean here is correct. If not, please suggest the appropriate way).
I want this bean to be passed to class A (ofcourse the Spring Way). The name of variable is sampleString.
So far I had been using something like:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="proc" class="A">
</bean>
<bean id="result" class="B">
<property name="sampleString" value="#{proc.sampleString}"/>
</bean>
</beans>
But this copies null value to the variable in class A (ofcourse because the method executes only when a button is pressed).
So is there a cleaner way to do this?
So I have been using the 3.2.0.RELEASE Spring XML configurations for most of my beans but now I am faced with a unique situation where the Getters and Setters can't be used (bad legacy code - can't get around it).
As such, I want to use Spring #Configuration class and the XML to workaround this problem.
However, I am getting "Class Not Found" exception when it tries to read my #Configuration Class.
Caused by: java.lang.ClassNotFoundException: v1.inventory.item.myJavaConfig
My XML file which is failing looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<bean class="v1.inventory.item.myJavaConfig"/>
</beans>
My #Configuration class looks like this:
package v1.inventory.item;
#Configuration
#ImportResource("classpath:v1/inventory/item/baseItemConfigs.xml")
public class myJavaConfig {
#Autowired
#Qualifier("parentItem")
Item baseItem;
#Bean
public Item realItem(){
Item modifiedBean = baseItem;
modifiedBean.setManufacturer("Fake Setter for Manufacturer");
modifiedBean.setDesigner("Fake Setter for Designer");
return modifiedBean;
}
}
I need this to be read by the ApplicationContext so I need to make sure these beans can be found. Is this a bug with Spring 3.2.0.RELEASE? Or my code?
For the record, I am pulling in the #Configuration last (parentItem is scanned first in XML).
I figured out the issue here.
It seems that maven/spring (not sure which) wasn't looking in my "resource" directory for the file. Only my "java" directory. When I moved my file into the "java" directory, Spring found the file just fine.
The tests which were passing in JUnit now pass using the Maven compiler (which was throwing the above error during the test phase)
I have a property file with key value pairs:
key1=value1
key2=value2
Now, in my controller, I want to directly print the value of a property file (of course after loading the property file using web.xml / app-servlet.xml), like:
System.out.printl(${key1});
Is it possible to do that?
If not, I want to create an interface with all constant variable to read values from property file. How do I do it??
public interface MyConstants
{
#Value("${key1}")
public static final KEY_1="";
}
But as expected only empty string is assigned.
How do I solve this issue? Or, what is the best way to using property files to retrieve values? Thanks in advance...
There are two reasons why having an interface for 'MyConstants' instead of a class is incorrect :
1) Spring cannot inject values to an interface which has no implementation. Simply because you wont be able instantiate the interface. Remember, Spring is just a factory and it can play only with 'things' which can be instantiated.
2) Another reason is that having an interface for storing your constants is an anti-pattern in itself. That is not what interfaces are designed for. You might want to refer to the Constant interface anti-pattern.
http://en.wikipedia.org/wiki/Constant_interface
It's possible! You need to use the util namespace in your app-servlet.xml as below:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd">
<util:properties id="props" location="classpath:yourfile.properties" />
<!-- other -->
</beans>
And your controller is something like
#org.springframework.beans.factory.annotation.Value("#{props.key1}")
public void setFoo(String foo) {
System.out.println("props.key1: " + foo);
}
update for another way:
You also can use namespace context
<context:property-placeholder location="classpath:yourfile.properties" />
In controller, declare a property as below
#Value("${pros.key1}")
private String foo;
Creating a ''Constants'' class / interface is a widely used approach, but I think its a flawed approach. It creates a weird coupling where classes from different layers in your system suddenly start depending on one Constants class. It also becomes difficult to understand by looking at the constants class, as to which constant is being used by who? Not to mention the fact that it completely mocks abstraction. You suddenly have a constants class which contains information about the error message to show on the jsp, username and password of a third party api, thread pool size etc.. all in one "I know everything" class
So avoid a constant class / interface as far as possible. Look at your controllers / services, if a particular service class needs a particular configuration value that you want exposed in a property file, inject it into the class and store it as an instance level constant. This design is much cleaner from an abstraction point of view, it also helps to unit test this class easily.
In Spring, you can create a handle to a property file as follows:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="classpath:my-application.properties" />
</bean>
As the code suggests, you can mention multiple property files here. After you do this, you can reference a key from the mentioned property file, elsewhere in the context like so:
<bean id="xx" class="com.xx.SomeClass" p:imageUrl="${categories.images}"/>
The SomeClass instance here has a property called imageUrl which is now injected with the value mentioned against the categories.images key from the property file called my-application.properties
Hope this helps.