How to avoid the "two bean definitions found" error - java

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.

Related

from Spring context to Spring annotation

I'm moving my project package from Spring xml file configuration to class annotation configuration.
Im stuck with a bean instantiation failed on a bean defined in a another context xml file.
This is the definition :
<bean id="mglsChecker" class="DefaultMglsAdapter" destroy-method="close">
<constructor-arg value="${mgls.server.address}"/>
<constructor-arg value="${mgls.fname}"/>
<constructor-arg value="${mgls.lcount}"/>
</bean>
the mglsChecker class is defined in an infrastucture package common to the entire "solution".
The issue is that the variables "${}" are not defined so now this class is not instantiated.
I dont understand how it works when my project is xml file configured.
In the original applicationContext.xml I dont see any references to this mglsChecker context file.
Any help where should i look into ? what am i missing ?
thanks,
You can use
#Configuration
class YourConfig {
// you usually don't need to explicitly give the bean name
// if you don't, Spring gives it the config's method name
#Bean(name = "mglsChecker", destroyMethod = "close")
MglsAdapter mglsChecker(#Value("${mgls.server.address}") String address,
#Value("${mgls.fname}") String fname,
#Value("${mgls.lcount}") long lcount) {
return new DefaultMglsAdapter(address, fname, lcount);
}
}
Personally, I prefer creating #Component classes, but for that you need to be able to edit the DefaultMglsAdapter class.
#Component
class DefaultMglsAdapter implements MglsAdapter {
// fields to hold the configs
DefaultMglsAdapter(#Value("${mgls.server.address}") String address,
#Value("${mgls.fname}") String fname,
#Value("${mgls.lcount}") long lcount) {
// set fields
}
#PreDestroy
void close() {
// cleanup
}
}
EDIT: incorporated Andreas' correction :)
Load the properties in the java file via
#Configuration
#PropertySource("classpath:foo.properties")
public class DefaultMglsAdapter{
//...
}
Inject the properties via
#Value( "${mgls.server.address}" )
private String serverAddress;
The variables which are mentioned with "${}" syntax are key/place-holders of properties.
Please search or find such key from *.properties or *.config or *.xml or any such custom properties files. If you find any such properties file then specify classpath or location of that file where you want to configure it as given below:
By XML:
<context:property-placeholder location="classpath:path/to/PropertiesFile"/>
By Annotation:
#Configuration
#PropertySource("classpath:path/to/PropertiesFile")
#Value("${Property}")
Thanks and Regards.

SpringFramework: set bean name programmatically

For springframwork based application, when using xml to declare beans, bean id can be configured by passing a unique value and even a parameter and then solve the value at runtime.
Now I hope to replace all xml configuration to java annotation.
Say I want to create two database beans with different id.
bean.xml
<bean id="A.database" class="org.apache.commons.dbcp.BasicDataSource">
<!-- collaborators and configuration for this bean go here -->
</bean>
<bean id="B.database" class="org.apache.commons.dbcp.BasicDataSource">
<!-- collaborators and configuration for this bean go here -->
</bean>
Then I optimize the upper code to one bean with two property file for two application
bean.xml
<bean id="${appName.database}" class="org.apache.commons.dbcp.BasicDataSource">
<!-- collaborators and configuration for this bean go here -->
</bean>
applicationA.properties
appName.database=A.database
applicationB.properties
appName.database=B.database
The whole application is composed of "framework" module which provides beans common for each application, like database bean, jdbcTemplate bean, and "application" module which provides property value for placeholder and initializes beans with unique id. So even if I start multiple application at the same time, they will find corresponding bean from the context.
Generally speaking, I hope to do
#Bean(name = "${beanName}")
public ABean getBean() {}
and resolve ${beanName} at application level.
By reading SpringFramwork document, I found the answer: BeanNameGenerator
NameGenerator.class
public class NameGenerator implements BeanNameGenerator{
#Override
public String generateBeanName(BeanDefinition definition,
BeanDefinitionRegistry registry) {
if(definition.getBeanClassName().contains("Toto")) {
return "toto";
}
return return definition.getBeanClassName();
}
}
AppConfiguration.class
#Configuration
#ComponentScan(basePackages = {"com.example.domain"}, nameGenerator = NameGenerator.class)
public class Config {
}
Domain class with #Component
#Component
public class Toto {
private int id;
}
BootApplication with domain bean name : toto
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(
DemoApplication.class, args);
for (String name : ctx.getBeanNamesForType(Toto.class)) {
System.out.println(name);
}
}
}
If you want to follow that type of approach create multiple configuration classes you annotated with different Spring profiles.
At start up you can pass a parameter on which profile to use and hence what beans to load within the associated profile.
A more efficient way to do it is use the same property naming convention across all application .properties files. Set a parameter placeholder for the file name which resolves to a JVM arg passed at runtime which is loaded by the#PropertySource annotation.
There's no need to have duplicate beans defined for different environments if it's just properties that are changing.

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.

Spring configuration for dependency injection

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.

SpringMVC: Variables in Annotations

I have the following controller defined:
#Controller
#RequestMapping("/test")
public class MyController extends AbstractController
{
#Autowired
public MyController(#Qualifier("anotherController") AnotherController anotherController))
{
...
}
}
I'm wondering if it's possible to use variables in the #Qualifier annotation, so that I can inject different controllers for different .properties files, e.g.:
#Controller
#RequestMapping("/test")
public class MyController extends AbstractController
{
#Autowired
public MyController(#Qualifier("${awesomeController}") AnotherController anotherController))
{
...
}
}
Whenever I try I get:
org.springframework.beans.factory.NoSuchBeanDefinitionException:
No matching bean of type [com.example.MyController] found for dependency:
expected at least 1 bean which qualifies as autowire candidate for this
dependency. Dependency annotations:
{#org.springframework.beans.factory.annotation.Qualifier(value=${awesomeController})
I've included the following bean in my config.xml file:
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:config/application.properties</value>
</list>
</property>
</bean>
But the bean doesn't work unless I declare the bean explicitly in the xml file.
How do I do this with annotations??
First I think it is bad practice to make the dependency injection rely on configuration properties. You are probably going a wrong direction trying to do this.
However to answer your question: accessing placeHolder properties requires the dependency injection to be finished. To make sure it is, you can put your code that accesses the property inside a #PostContruct annotated method.
You will need to retrieve the bean manually from the applicationContext using getBean() method.
#Value("${awesomeController}")
private String myControllerName;
#PostConstruct
public void init(){
AnotherController myController = (AnotherController) appContext.getBean(myControllerName);
}
I'm not sure if what you're doing is possible but I can suggest a slightly different approach, but only if you're using Spring 3.1+. You could try using Spring Profiles.
Define the different controllers you want, one per profile:
<beans>
<!-- Common bean definitions etc... -->
<beans profile="a">
<bean id="anotherController" class="...AnotherController" />
</beans>
<beans profile="b">
<!-- Some other class/config here... -->
<bean id="anotherController" class="...AnotherController"/>
</beans>
</beans>
Your Controller would lose the #Qualifier and become something like:
#Autowired
public MyController(AnotherController anotherController) {
...
}
Then at runtime you can specify which controller bean you want to use by activating the corresponding profile using a system property, e.g.:
-Dspring.profiles.active="a"
or:
-Dspring.profiles.active="b"
It may be possible to set profiles based on a property file but you can find out more about Spring Profiles from this post on the Spring blog. I hope that helps somewhat.

Categories