Summary: I am working with some sample Spring code (from javabrains.io). Basically this code illustrates the use of the #Qualifier annotation in the Java file. One of the bean elements (in spring.xml) has a qualifier that matches the value of the #Qualifier annotation but even then UnsatisfiedDependencyException is thrown.
Details: The Circle class has a method which accepts a Point object and which has the following annotations:
#Autowired
#Qualifier("circleRelated")
In spring.xml we have three Point beans: pointA, pointB and pointC. pointA has the property attribute set to circleRelated. One would expect that since this bean meets the qualifier criterion (i.e. value = "circleRelated"), at initialization time autowiring should complete without any hitch. However, UnsatisfiedDependencyException is thrown. The source code follows:
Point.java
package org.koushik.javabrains;
public class Point {
private int x;
private int y;
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
Circle.java
package org.koushik.javabrains;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
public class Circle {
private Point center;
public Point getCenter() {
return center;
}
#Autowired
#Qualifier("circleRelated")
public void setCenter(Point center) {
this.center = center;
}
public void draw() {
System.out.println("Drawing Circle");
System.out.println("Circle: Center point is: " + center.getX() + ", " + center.getY());
}
}
DrawingApp.java
package org.koushik.javabrains;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class DrawingApp {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
Circle circle = (Circle) context.getBean("circle");
circle.draw();
}
}
spring.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 https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"
xmlns:context="http://www.springframework.org/schema/context">
<bean id="pointA" class="org.koushik.javabrains.Point">
<qualifier value="circleRelated"/>
<property name="x" value ="0"/>
<property name="y" value ="0"/>
</bean>
<bean id="pointB" class="org.koushik.javabrains.Point">
<property name="x" value ="-20"/>
<property name="y" value ="0"/>
</bean>
<bean id="pointC" class="org.koushik.javabrains.Point">
<property name="x" value ="20"/>
<property name="y" value ="0"/>
</bean>
<bean id="circle" class="org.koushik.javabrains.Circle">
</bean>
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>
</beans>
You are missing
<context:annotation-config/>
tag in your spring.xml
Source
Use of these annotations also requires that certain BeanPostProcessors
be registered within the Spring container. As always, these can be
registered as individual bean definitions, but they can also be
implicitly registered by including the following tag in an XML-based
Spring configuration (notice the inclusion of the 'context'
namespace):
On a side note , any specific reason you are using xml based configuration ? Though spring supports xml configurations , it is long since people have moved to Annotations based configuration.I recommend that it is worth exploring annotations based route.
Related
I am trying DI with autowiring and I came across #Qualifier annotation annd tried the following code:
Car.java
package beans;
import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.beans.factory.annotation.Qualifier;
public class Car {
#Autowired
#Qualifier("e1")
private Engine engine;
// no need to have setter or constructor
public void showData(){
System.out.println("Engine Model Year : "+engine.getModelyear());
}
}
Engine.java
package beans;
public class Engine {
private String modelyear;
public void setModelyear(String modelyear) {
this.modelyear = modelyear;
}
public String getModelyear() {
return modelyear;
}
}
Spring.xml
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN"
"http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans>
<!-- activate autowire annotation -->
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>
<bean id="e1" class="beans.Engine">
<property name="modelyear" value="2017"/>
</bean>
<bean id="e2" class="beans.Engine">
<property name="modelyear" value="2018"/>
</bean>
<bean id="c" class="beans.Car">
</bean>
</beans>
Main.java
package MainClass;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import beans.Car;
public class AutoAnno_Main {
public static void main(String[] args) {
ApplicationContext ap=new ClassPathXmlApplicationContext("resources/spring.xml");
Car c=(Car)ap.getBean("c");
c.showData();
}
}
And the error I am getting is:
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'beans.Engine' available: expected single matching bean but found 2: e1,e2
what's wrong in this I think the syntax is correct is there any problem with version
I am using eclipse Oxygen
You just need to add <context:annotation-config /> in your spring.xml. Certain spring annotations are not activated unless this is added. In your case, spring is not reading the #Qualifier annotation without the <context:annotation-config />.
I have tested by adding this and it seems to work.
Update:
Your spring xml needs to have the spring schema for that to detect <context:annotation-config>. Your final xml looks like this.
<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.xsd">
<!-- activate autowire annotation -->
<context:annotation-config />
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>
<bean id="e1" class="beans.Engine">
<property name="modelyear" value="2017"/>
</bean>
<bean id="e2" class="beans.Engine">
<property name="modelyear" value="2018"/>
</bean>
<bean id="c" class="beans.Car">
</bean>
</beans>
I am trying to auto wire my Circle class. I am using a qualifier in order for Spring to able to distinguish between the two Point class beans i have created a qualifier.
public class Circle{
private Point center;
#Autowired
#Qualifier("circleRelated")
public void setCenter(Point center) {
this.center = center;
}
public void draw() {
System.out.println("Drawing Circle");
System.out.println("Centre point is " + center.getX());
}
}
The Point class is as follows
public class Point {
private int x;
public int getX() { return x; }
public void setX(int x) { this.x = x;}
}
The beans.xml is
<?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="circle" class="com.example.Circle"/>
<bean id="pointA" class="com.example.Point">
<qualifier value="circleRelated" />
<property name="x" value="0"/>
<property name="y" value="0"/>
</bean>
<bean id="pointB" class="com.example.Point">
<property name="x" value="-20"/>
<property name="y" value="0"/>
</bean>
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>
</beans>
My main class is
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
#SpringBootApplication
public class AutowiredAnnotationApplication {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Circle circle = (Circle) context.getBean("circle");
circle.draw();
}
}
The error I get is
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.example.Point] is defined: expected single matching bean but found 2: pointA,pointB
Why is spring unable to autowire the bean with the given qualifier?
You have to remove this line from your beans.xml:
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>
For some reason, it is meddling with your otherwise correct configuration.
The #Qualifier gets the bean id as value. So you have to use "pointA" or "pointB"
public class Circle{
private Point center;
#Autowired
#Qualifier("pointA")
public void setCenter(Point center) {
this.center = center;
}
public void draw() {
System.out.println("Drawing Circle");
System.out.println("Centre point is " + center.getX());
}
}
In general if you skip the #Qualifier - you autowire by type. That means "search in the Spring context a bean of that type and throw an exception if none or more than one is found".
If you add the #Qualifier("someName") that means - "search in the Spring context a bean of that type and with that name and throw an exception if none is found"
Edit:
Take a look at this. I don't think that you need tag at all.
If you wan't to inject some bean using #Autowired the #Qualifier needs the bean id as it's value. For example #Qualifier("pointA") will require a bean with id: pointA.
If you want to inject the bean using only xml you can use ref. You don't need Qualifier for that, only for autowireing.
<?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="circle" class="com.example.Circle">
<property name="center" ref="pointA"/>
</bean>
<bean id="pointA" class="com.example.Point">
<qualifier value="circleRelated" />
<property name="x" value="0"/>
<property name="y" value="0"/>
</bean>
<bean id="pointB" class="com.example.Point">
<property name="x" value="-20"/>
<property name="y" value="0"/>
</bean>
</beans>
Edit 2:
See this. I believe that the qualifier tag is taken into account only if you skip the bean id, otherwise the bean id is used.
I found something not clear about the autowire=byType behavior.
Java code under package my:
public class User {
private String name;
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
#Component
public class UserService {
#Autowired
private User user1;
#Autowired
private User user2;
public String getNames() {
return user1.getName() + " & " + user2.getName();
}
}
Spring config:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd">
<context:component-scan base-package="my"/>
<context:annotation-config/>
<bean id="user1" class="my.User" autowire="byType">
<constructor-arg value="Freewind"/>
</bean>
<bean id="user2" class="my.User" autowire="byType">
<constructor-arg value="Lily"/>
</bean>
</beans>
Running code:
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService service = context.getBean(UserService.class);
System.out.println(service.getNames());
}
}
It's working well and prints:
Freewind & Lily
But I was expecting it should not work, because I used autowire="byType" when I defined the beans, and there are two beans with the same type User in UserService.
And, if I changed the name of the bean, say, user1 -> user999, it will report some error like No qualifying bean of type [my.User] is defined error.
It seems spring will automatic check the name even if I specified byType, which is strange.
PS: I've tested with spring 4.1.3.RELEASE and 3.2.2.RELEASE, same behavior.
<bean id="user2" class="my.User" autowire="byType">
<constructor-arg value="Lily"/>
</bean>
The autowire="byType" here means that you want to have (missing) dependencies injected into this bean byType. It only applies to the bean the attribute is placed on. In the xml namespace the default for all the beans could be set.
However in your case you are using actually using annotations (note <context:annotation-config /> is already implied by the usage of </context:component-scan />). Annotation driven injection (#Autowired, #Inject are always by type, #Resource uses a name or jndi lookup and as fallback by name).
When starting the application and scanning for components for each needed dependency the DependencyDescriptor is created. This class contains the details used for autowiring, it amongst other things contains the type and the name. The name, in case of a Field is derived from the actual name.
I have written a small code to check #Autowired annotation in Spring, here is my piece of code
public class Address
{
private String street;
private String City;
private String State;
private String Country;
/* getter setter here */
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
public class Customer
{
private String name;
private Address address;
// other getter setter here
#Autowired
public void setAddress(Address address)
{
this.address = address;
}
}
and springexample.xml file
<?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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean id="address1" class="org.springexamples.Address">
<property name="street" value="vihar" />
<property name="city" value="dehradun" />
<property name="state" value="Uttarakhand" />
<property name="country" value="India" />
</bean>
<bean id="addres2" class="org.springexamples.Address">
<property name="street" value="triveni vihar" />
<property name="city" value="dehradun" />
<property name="state" value="Uttarakhand" />
<property name="country" value="India" />
</bean>
<bean id="customer" class="org.springexamples.Customer">
<property name="name" value="deepak" />
</bean>
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />
</beans>
and main class
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AutowiredQualifierTest
{
public static void main(String[] args)
{
ApplicationContext context= new ClassPathXmlApplicationContext("springexample.xml");
Customer cust = (Customer)context.getBean("customer");
System.out.println(cust.getName() + " " + cust.getAddress().getStreet());
}
}
Ideally it should show an exception as two beans of the same type exist however its picking up bean with id="address1" so i am getting this bean behaviour.
No unique bean of type [org.springexamples.Address] is defined: expected single matching bean but found 2: [address1, addres2]..exception is coming..and it is clearly saying right..so you have to use #Qualifier(your_required_beanid)
for example:
#Qualifier("Address1")
then it will consider id with address1 bean
The exception is thrown. I bet you are doing something wrong. I have tested you code, just to be hundred percent sure and it throws the exception.
Let's take a look to the documentation:
3.4.5.1 Limitations and disadvantages of autowiring
Multiple bean definitions within the container may match the type specified by the setter method or constructor argument to be autowired. For arrays, collections, or Maps, this is not necessarily a problem. However for dependencies that expect a single value, this ambiguity is not arbitrarily resolved. If no unique bean definition is available, an exception is thrown
Also, take a look to this post.
This will give you exception, when spring tries to autoware the address field, it will not find any bean with id address ...rather user Qualifer with proper id so that while autowaring it will pinck the porper object of address from 2 id [address1, address2].
I have destroy method in my bean but it is not showing in the out put. Could you please help me here.
package com.vaannila;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class HelloWorldApp {
public static void main(String[] args) {
AbstractApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Ticket helloWorld = (Ticket) context.getBean("ticket");
helloWorld.setTicketNo("ABC009");
helloWorld.display();
context.close();
}
}
below is my xml file
<?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="helloWorld" class="com.vaannila.HelloWorld">
<property name="message" value="Hello World!"></property>
</bean>
<bean id="ticket" class="com.vaannila.Ticket"
scope="prototype" init-method="init" destroy-method="destroy"/>
</beans>
and Ticket class is below
package com.vaannila;
public class Ticket {
private String ticketNo="";
public String getTicketNo() {
return ticketNo;
}
public void setTicketNo(String ticketNo) {
this.ticketNo = ticketNo;
}
public void display()
{
System.out.println("Your Ticket No. is"+ ticketNo);
}
public void init()
{
System.out.println("Bean is ready You can use it now");
}
public void destroy()
{
System.out.println("Bean is going to destroy");
}
}
The out put is giving for init method but not for destroy method..
If i changed the init-method and destroy-method as default as below it is giving error in destroying the bean called "helloWorld"
<?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"
default-init-method="init" default-destroy-method="destroy">
<bean id="helloWorld" class="com.vaannila.HelloWorld">
<property name="message" value="Hello World!"></property>
</bean>
<bean id="ticket" class="com.vaannila.Ticket"
scope="prototype"/>
</beans>
When a bean is defined as prototype, the bean container creates new instances of this been whenever it is asked for that bean. That's the idea behind prototype-scoped beans.
After they are created, the container gives up responsibility for the bean. It cannot know if you are still holding a reference to it, or when is the moment you drop the last reference. This is true even after the container is closed. (The container is not the garbage collector.) So it cannot possibly know when is the right moment to call the destroy method.
If you need deinitialization for your ticket, you will have to call such a method from your code directly I think (assuming that it makes no sense to have singleton tickets).