Spring configuration for dependency injection - java

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.

Related

Spring: Is it possible to specify the default-lazy-init value when you're new-ing the application context?

I'm creating my Spring application context from an XML file like so:
this.applicationContext = new ClassPathXmlApplicationContext("classpath*:/spring-configuration/application-context.xml");
I know there's an option to specify the default-lazy-init in the XML file itself, like in this example:
<?xml version="1.0" encoding="UTF-8"?>
<beans
...
default-lazy-init="true">
...
</beans>
However, I want to decide whether or not to use default lazy init based on the context when this code is invoked... is there a way to do this? I basically want an extra configuration boolean I could pass to the ClassPathXmlApplicationContext constructor which would tell it whether to use default lazy init or not.
Read Spring Docs On Lazy-initialized Beans :
ApplicationContext eagerly create singleton bean during startup. This
type of instantation is good because errors in the configuration are
found at the beginning. But if you
don't want the pre-instantiation of a singleton bean at the beginning, then you can mark that
bean definition as being lazy-initialized.
Note : A lazy-initialized singleton bean is instantiated only when it is requested.
You can achieve lazy initialization of bean in two ways :
-> At <bean> level :
<bean id="demoBean" class="com.example.DemoBean" lazy-init="true"/>
-> At container level (where no beans will be pre-instantiated) :
<beans default-lazy-init="true">
...
</beans>
So, select the desired once according to your need.
Example to understand :
DemoBean.java :
package com.example;
public class DemoBean {
public DemoBean(){
System.out.println("DemoBean is initialized on the request.");
}
}
Main.java :
public class Main {
public static void main(String[] args) {
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("application-context.xml");
context.getBean("demoBean");
}
}
Output:
DemoBean is initialized on the request.

Spring - correct use of #Autowired with Spring MVC [duplicate]

I am trying to autowire some beans (for dependency injection) using Spring for a webapp. One controller bean contains another bean which in turn holds a hashmap of another set of beans. For now the map only has one entry. When i run in tomcat and call the service I get an error saying that the second bean (held in the controller) is not unique
No unique bean of type [com.hp.it.km.search.web.suggestion.SuggestionService] is defined: expected single matching bean but found 2: [suggestionService, SuggestionService]
I cannot see where I am defining the bean twice however am new to Spring and autowiring so I may be missing something fundamental. Source code for xml and 2 class listed below...
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<context:component-scan base-package="com.hp.it.km.search.web.suggestion" />
<mvc:annotation-driven />
<context:annotation-config />
<bean id="SuggestionController" class="com.hp.it.km.search.web.suggestion.SuggestionController">
<property name="service">
<ref bean="SuggestionService" />
</property>
</bean>
<bean id="SuggestionService" class="com.hp.it.km.search.web.suggestion.SuggestionService">
<property name="indexSearchers">
<map>
<entry key="KMSearcher"> <ref bean="KMSearcherBean"></ref></entry>
</map>
</property>
</bean>
<bean id="KMSearcherBean" class="com.hp.it.km.search.web.suggestion.SuggestionIndexSearcher">
<constructor-arg index="0" value="KMSearcher" />
<constructor-arg index="1" value="C://dev//workspace//search-restful-webapp//src//main//resources//indexes//keyword" />
</bean>
The class asscoaites with the autowired controller and service bean are here...
#Controller
public class SuggestionController {
private SuggestionService service;
#Autowired
public void setService(SuggestionService service) {
this.service = service;
}
public SuggestionService getService() {
return service;
}
and...
#Component
public class SuggestionService {
private Map<String, IndexSearcher> indexSearchers = new HashMap<String, IndexSearcher>();
#Autowired
public void setIndexSearchers(Map<String, IndexSearcher> indexSearchers) {
this.indexSearchers = indexSearchers;
}
public SuggestionService() {
super(); }
Please Help!
The issue is because you have a bean of type SuggestionService created through #Component annotation and also through the XML config . As explained by JB Nizet, this will lead to the creation of a bean with name 'suggestionService' created via #Component and another with name 'SuggestionService' created through XML .
When you refer SuggestionService by #Autowired, in your controller, Spring autowires "by type" by default and find two beans of type 'SuggestionService'
You could do one of the following
Remove #Component from your Service and depend on mapping via XML - Easiest
Remove SuggestionService from XML and autowire the dependencies - use util:map to inject the indexSearchers map.
Use #Resource instead of #Autowired to pick the bean by its name .
#Resource(name="suggestionService")
private SuggestionService service;
or
#Resource(name="SuggestionService")
private SuggestionService service;
both should work.The third is a dirty fix and it's best to resolve the bean conflict through other ways.
If you have 2 beans of the same class autowired to one class you shoud use #Qualifier (Spring Autowiring #Qualifier example).
But it seems like your problem comes from incorrect Java Syntax.
Your object should start with lower case letter
SuggestionService suggestion;
Your setter should start with lower case as well and object name should be with Upper case
public void setSuggestion(final Suggestion suggestion) {
this.suggestion = suggestion;
}
For me it was case of having two beans implementing the same interface. One was a fake ban for the sake of unit test which was conflicting with original bean.
If we use
#component("suggestionServicefake")
, it still references with suggestionService.
So I removed #component and only used
#Qualifier("suggestionServicefake")
which solved the problem
If I'm not mistaken, the default bean name of a bean declared with #Component is the name of its class its first letter in lower-case. This means that
#Component
public class SuggestionService {
declares a bean of type SuggestionService, and of name suggestionService. It's equivalent to
#Component("suggestionService")
public class SuggestionService {
or to
<bean id="suggestionService" .../>
You're redefining another bean of the same type, but with a different name, in the XML:
<bean id="SuggestionService" class="com.hp.it.km.search.web.suggestion.SuggestionService">
...
</bean>
So, either specify the name of the bean in the annotation to be SuggestionService, or use the ID suggestionService in the XML (don't forget to also modify the <ref> element, or to remove it, since it isn't needed). In this case, the XML definition will override the annotation definition.

Get Spring profile name with spring EL

Consider a web based application with spring 4. The spring bean profiles is defined in web.xml like:
<context-param>
<param-name>spring.profiles.active</param-name>
<param-value>prod,edb,cas</param-value>
</context-param>
Now consider a bean is defined in spring-applicaiton-context.xml as
<util:properties id="myPolicy"
location=
"classpath:/configs/${ACCESS-ACTIVE-PROFILE-SECOND-ITEM}/my-policy.properties" />
Is it possible that I can access the list of active profiles and select the second one (in my example edb). In this way I can make my resource load dynamically when active profile changes.
This may help! I could get the active profile when web application starts with below code:
public void contextInitialized(ServletContextEvent event){
ApplicationContext applicationContext = WebApplicationContextUtils
.getWebApplicationContext(event.getServletContext());
String activeProfiles[] = applicationContext.getEnvironment().getActiveProfiles();
system.out.print(activeProfiles[1])
}
The syntax would be "#{environment.activeProfiles[1]}" - however, it's too early in the context life cycle; the activeProfiles is not set up before the SpEL is evaluated in this case.
What's wrong with
<beans profile="foo">
<util:properties id="myPolicy"
location="classpath:/configs/foo/my-policy.properties" />
</beans>
<beans profile="bar">
<util:properties id="myPolicy"
location="classpath:/configs/bar/my-policy.properties" />
</beans>
?
Actually, I just found that
"#{environment.getActiveProfiles()[1]}"
works - explicitly calling the getter causes the property to be loaded.

How to avoid the "two bean definitions found" error

I'm developing a huge application with thousands of Spring beans registered using annotations and wired with each others with #Autowired
The application will be released as a "core" application and our customers should be able to customize it adding or overwriting beans.
If they hace to modify a bean the regular way would be by extending the class and making the Spring context to register the customized class instead of the "core" one, but doing that Spring throws an error because it finds two implementation for the same interface.
How can I achieve that?
How can our customers "de-register" the core class and regster the customizaed one?
Thanks a lot!
You can use a Qualifier. They identify beans by name, not by type.
You also can set the field to a list of beans.
#Autowire
private Foobar[] customizedAndCore;
The simplest way of handling that is by using the #Primary annotation. Spring will inject the primary instead of failing with the duplicate bean exception.
Here a basic draft to use #Qualifier plus #Autowired annotation
Here is the content of Student.java file:
public class Student {
private Integer age;
private String name;
public void setAge(Integer age) {
this.age = age;
}
public Integer getAge() {
return age;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
Here is the content of Profile.java file:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
public class Profile {
#Autowired
#Qualifier("student1")
//#Qualifier("student2")
private Student student;
public Profile(){
System.out.println("Inside Profile constructor." );
}
public void printAge() {
System.out.println("Age : " + student.getAge() );
}
public void printName() {
System.out.println("Name : " + student.getName() );
}
}
Following is the content of the MainApp.java file:
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("Beans.xml");
Profile profile = (Profile) context.getBean("profile");
profile.printAge();
profile.printName();
}
}
Consider the example of following configuration file Beans.xml:
<?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/>
<!-- Definition for profile bean -->
<bean id="profile" class="com.Profile">
</bean>
<!-- Definition for student1 bean -->
<bean id="student1" class="com.Student">
<property name="name" value="Zara" />
<property name="age" value="11"/>
</bean>
<!-- Definition for student2 bean -->
<bean id="student2" class="com.Student">
<property name="name" value="Nuha" />
<property name="age" value="2"/>
</bean>
</beans>
The way I see it you have 3 general approaches...
Create a child context and in the child context override beans in the parent context (they will replace them)
Use a profile
Use a customisation of the annotation when injecting the bean
The first option means that customers when they wish to replace the "core" beans with their own ones simply need to create a new factory which is a child of the core factory. As long as the bean in the child has the same name as the on in core all is well.
The second one means the default bean will be in the "default" (no profile) and then the replacement will be in a specific profile. These replacement beans will then override the beans in no profile when that profile is active. Replacement only happens against bean name not against type so when using this approach you have to ensure the new bean has the same name as the original and the injection annotation specifies the name of the bean ala
#Inject
#Named("dataSource")
private DataSource storageRepository;
The third option requires the following to appear in the annotation when using a bean
#Resource(name = "${dataSource}")
private DataSource dataSource;
Then when using this you will need a parameter called dataSource and needs to be set to the specific bean name you want to inject into that location.
e.g.
dataSource=enterpriseDataSource
then the bean named enterpriseDataSource will be injected into that location.
The way I see it approach 1 is arguably the closest fit to what you're looking for. It sounds like you have a "core" factory that you supply that customers depend on so they don't really have ownership of your source code. AFAIK approach 1 is also the only one that will allow autowire by type to work.
Approach 2 is a better fit for when you want to run in different modes... i.e. in dev, test or production mode. The reason for this is you can only override beans that are not in a profile. You can't override a bean already in a profile with a bean in another profile with this approach.
Approach 3 is in fact what I tend to use the most because it does not require profiles or factory hierarchy and allows swapping in of a different bean simply by changing a parameter's value. I wish however I did not have to keep specifying the bean name however. Something else that is possible - and something I use a lot - is swapping in a whole new config file via activation of a different profile.

spring injection works within user created instance?

I'm working with Spring, and I'm getting the injected instance as null when I create the instance with new operator, I can elobrate the scenario.
For example,
Let the class A and class B are injected into the class Main
class Main
{
#autowired
A a;
#autowired
B b;
//getter and setter
}
class MainExecute
{
public static void main()
{
// loading the spring config xml
Main main = new Main();
A a=main.getA();
// whether a will get the instance ( I'm getting a as null)
}
what could be the reason for this scenario
Please guide me on the same
Thanks in advance.
No, it won't get dependency injection.
Only objects created/managed by the spring container will get the facilities offered by spring dependency injection.
To elaborate on the above answers:
You need to let the spring IOC container create your objects for you, you don't explicitly create them. You can do this by creating a spring config xml file, here is a quick example:
META-INF\spring\my-spring-config.xml:
<?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-2.0.xsd">
<bean id="myMainClass" class="org.foo.Main" />
<bean id="myA" class="org.foo.A" />
<bean id="myB" class="org.foo.B" />
</beans>
org.foo.MainExecute:
class MainExecute
{
public static void main()
{
ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext("META-INF\\spring\\my-spring-config.xml");
Main main = (Main) appContext.getBean("myMainClass");
}
}
In this example the Spring IOC container will instantiate an "A" bean and a "B" bean. It will then autowire these into the "Main" bean.

Categories