I am learning Bean concepts using XML
-based configuration, but I am facing an issue with the @Qualifier
annotation.
Here's my class:
@Getter
@Setter
public class CircleWithAnnotation implements Shape {
@Autowired
// @Qualifier("circleRelated")
@Qualifier("zeroCenter") // This also refers to the same bean as "circleRelated"
private Point center;
@Resource(name = "zeroCenter")
private Point centerX; // This refers to the same bean as "zeroCenter" field above
public void draw() {
System.out.println("Circle drawn: \n"
+ "center = (" + center.getX() + ", " + center.getY() + ")\n"
);
System.out.println("Circle with centerX drawn: \n"
+ "centerX = (" + centerX.getX() + ", " + centerX.getY() + ")\n"
);
}
}
public class DrawingApp {
public static void main(String[] args) {
AbstractApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
context.registerShutdownHook();
CircleWithAnnotation circleWithAnnotation = (CircleWithAnnotation) context.getBean("circleWithAnnotation");
circleWithAnnotation.draw();
}
When I run the main
method above, I am getting the following error:
WARNING: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'circleWithAnnotation': Unsatisfied dependency expressed through field 'center': No qualifying bean of type 'in.abc.xyz.practice.javabrains.spring.shapes.Point' available: expected single matching bean but found 7: zeroPoint,point2,point3,pointA,pointB,pointC,zeroCenter
Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'circleWithAnnotation': Unsatisfied dependency expressed through field 'center': No qualifying bean of type 'in.abc.xyz.practice.javabrains.spring.shapes.Point' available: expected single matching bean but found 7: zeroPoint,point2,point3,pointA,pointB,pointC,zeroCenter
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:788)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:768)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:146)
My full beans.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"
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"
default-init-method="myInit" default-destroy-method="myDestroy">
<bean id="triangle" class="in.abc.xyz.practice.javabrains.spring.shapes.Triangle">
<!-- Setter Injection -->
<!-- <property name="type" value="Equilateral" />-->
<!-- Constructor Injection -->
<constructor-arg index="0" value="Equilateral" />
<constructor-arg index="1" value="20" />
<!-- <constructor-arg type="int" value="20" />-->
</bean>
<bean id="zeroPoint" class="in.abc.xyz.practice.javabrains.spring.shapes.Point">
<property name="x" value="0" />
<property name="y" value="0" />
</bean>
<bean id="point2" class="in.abc.xyz.practice.javabrains.spring.shapes.Point">
<property name="x" value="-20" />
<property name="y" value="0" />
</bean>
<bean id="point3" class="in.abc.xyz.practice.javabrains.spring.shapes.Point">
<property name="x" value="20" />
<property name="y" value="0" />
</bean>
<bean id="triangleWithPoint" name="triangle-with-point" class="in.abc.xyz.practice.javabrains.spring.shapes.TriangleWithPoint" >
<!--init-method="init" destroy-method="destroy"--> <!-- init-method and destroy-method can be defined at bean level or at beans level -->
<!-- <property name="pointA" ref="zeroPoint" />-->
<!-- <property name="pointB" ref="point2" />-->
<!-- <property name="pointC" ref="point3" />-->
<property name="pointA" ref="zeroPoint" />
<property name="pointB">
<bean class="in.abc.xyz.practice.javabrains.spring.shapes.Point">
<property name="x" value="-20" />
<property name="y" value="0" />
</bean>
</property>
<property name="pointC">
<bean class="in.abc.xyz.practice.javabrains.spring.shapes.Point">
<property name="x" value="20" />
<property name="y" value="0" />
</bean>
</property>
</bean>
<alias name="triangleWithPoint" alias="triangle-point" />
<bean id="triangleWithPointList" class="in.abc.xyz.practice.javabrains.spring.shapes.TriangleWithPointList" parent="triangleWithPointListParent">
<property name="points">
<list merge="true">
<ref bean="zeroPoint" />
<ref bean="point2" />
</list>
</property>
</bean>
<bean id="triangleWithAutowiredPoints" class="in.abc.xyz.practice.javabrains.spring.shapes.TriangleWithAutowiredPoints" autowire="byName">
</bean>
<bean id="pointA" class="in.abc.xyz.practice.javabrains.spring.shapes.Point">
<property name="x" value="${pointA.x}" />
<property name="y" value="${pointA.y}" />
</bean>
<bean id="pointB" class="in.abc.xyz.practice.javabrains.spring.shapes.Point">
<property name="x" value="-20" />
<property name="y" value="0" />
</bean>
<bean id="pointC" class="in.abc.xyz.practice.javabrains.spring.shapes.Point">
<property name="x" value="20" />
<property name="y" value="0" />
</bean>
<bean id="triangleWithPointAndContextAware" name="triangle-with-point-ctx-aware" class="in.abc.xyz.practice.javabrains.spring.shapes.TriangleWithPointAndContextAware">
<!-- <property name="pointA" ref="zeroPoint" />-->
<!-- <property name="pointB" ref="point2" />-->
<!-- <property name="pointC" ref="point3" />-->
<property name="pointA" ref="zeroPoint" />
<property name="pointB">
<bean class="in.abc.xyz.practice.javabrains.spring.shapes.Point">
<property name="x" value="-20" />
<property name="y" value="0" />
</bean>
</property>
<property name="pointC">
<bean class="in.abc.xyz.practice.javabrains.spring.shapes.Point">
<property name="x" value="20" />
<property name="y" value="0" />
</bean>
</property>
</bean>
<bean id="parentTriangle" class="in.abc.xyz.practice.javabrains.spring.shapes.TriangleWithPoint" abstract="true">
<property name="pointA" ref="zeroPoint" />
</bean>
<bean id="triangle1" class="in.abc.xyz.practice.javabrains.spring.shapes.TriangleWithPoint" parent="parentTriangle">
<property name="pointB" ref="point2" />
<property name="pointC" ref="point3" />
</bean>
<!-- triangle2 will fail as pointC has not been inherited from parent -->
<!-- <bean id="triangle2" class="in.abc.xyz.practice.javabrains.spring.shapes.TriangleWithPoint" parent="parentTriangle">-->
<!-- <property name="pointB" ref="point2" />-->
<!-- </bean>-->
<bean id="triangleWithPointListParent" class="in.abc.xyz.practice.javabrains.spring.shapes.TriangleWithPointList" abstract="true">
<property name="points">
<list>
<ref bean="point3" />
</list>
</property>
</bean>
<!-- <bean class="in.abc.xyz.practice.javabrains.spring.DisplayNameBeanPostProcessor" />-->
<bean class="in.abc.xyz.practice.javabrains.spring.MyBeanFactoryPostProcessor" />
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:pointsconfig.properties" />
</bean>
<bean id="circle" class="in.abc.xyz.practice.javabrains.spring.shapes.Circle">
<property name="center" ref="pointA" />
</bean>
<!-- <context:annotation-config />-->
<context:component-scan base-package="in.abc.xyz.practice.javabrains.spring.shapes"/>
<bean id="zeroCenter" class="in.abc.xyz.practice.javabrains.spring.shapes.Point" parent="zeroPoint">
<qualifier value="circleRelated" />
</bean>
<bean id="circleWithAnnotation" class="in.abc.xyz.practice.javabrains.spring.shapes.CircleWithAnnotation">
<!-- <property name="center" ref="pointA" />-->
</bean>
<!-- This class is not present anymore in Spring 6.2.0 -->
<!-- <bean class="RequiredAnnotationBeanPostProcessor" />-->
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />
</beans>
NOTE:
@Qualifier
s above work, but the @Resource
annotation works!context:annotation-config />
to the above beans.xml
file (as indicated in many other answers here on Stack-overflow), but to no avail. In this case, I get the following error:WARNING: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'circleWithAnnotation': Unsatisfied dependency expressed through field 'center': No qualifying bean of type 'in.abc.xyz.practice.javabrains.spring.shapes.Point' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true), @org.springframework.beans.factory.annotation.Qualifier("zeroCenter")}
Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'circleWithAnnotation': Unsatisfied dependency expressed through field 'center': No qualifying bean of type 'in.abc.xyz.practice.javabrains.spring.shapes.Point' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true), @org.springframework.beans.factory.annotation.Qualifier("zeroCenter")}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:788)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:768)
I am quite new to Spring - so, any help would be appreciated.
In your XML, there are no bean definitions qualified with zeroCenter
. You only have one bean definition with id zeroCenter
, which you have qualified with circleRelated
though.
Autowired
tries to wire dependencies first by type, and then by qualifier if there is more than a bean definition of the same type. In your case, there are 7 bean definitions of Point
, so Spring cannot tell which one to inject by type alone. Therefore, it attempts to narrow down the match with qualifiers, where the id of a bean is used as a default qualifying value when no qualifier is provided. This means that, the bean zeroCenter
could be a match for the field center
(since its id matches the qualifier), but because you've provided a qualifier in zeroCenter
's definition, circleRelated
takes over zeroCenter
, making no beans eligible for the injection. Hence, the UnsatisfiedDependencyException
being thrown. I recommend checking the section Fine-tuning Annotation-based Autowiring with Qualifiers of the Spring docs:
For a fallback match, the bean name is considered a default qualifier value. [...] @Autowired is fundamentally about type-driven injection with optional semantic qualifiers. This means that qualifier values, even with the bean name fallback, always have narrowing semantics within the set of type matches.
In the case of @Resource
instead, the annotation does work because its matching precedence is by name (when provided with one), and by type alternatively. Therefore, since you're specifically asking for a dependency with id zeroCenter
, this is picked and injected into your field.
@Resource takes a name attribute. [...] In the exclusive case of @Resource usage with no explicit name specified, and similar to @Autowired, @Resource finds a primary type match instead of a specific named bean.
To solve your issue, you could do one of the followings:
Qualify one of the bean definitions in the XML with <qualifier value="zeroCenter"/>
.
Annotate the field center
with @Qualifier("circleRelated")
.
Annotate the field center
with @Resource(name = "zeroCenter")
.
Here is also a useful guide from Baeldung on @Autowired
and @Resource
.