Search code examples
springdependency-injectionjavabeans

@Component and @Autowired not resolving the dependent bean


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-3.0.xsd"
            >
            
    
        <!-- bean definitions here --> 
        
        <bean id="point" class="com.example.demo.Point">
            <qualifier value="circleRelated" />
            <property name="x" value="0"></property>
            <property name="y" value="0"></property>
        </bean>
        
        <context:annotation-config/>
        
        <context:component-scan base-package="com.example.demo" /> 

Class with main method:

package com.example.demo;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    @SpringBootApplication
    public class SpringSecondApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(SpringSecondApplication.class, args);
            
            ApplicationContext context = new ClassPathXmlApplicationContext ("beans.xml");
            
            Shape shape = (Shape) context.getBean("circle");
            shape.draw();
    
        }
    
    }

Circle.java

package com.example.demo;



import javax.annotation.Resource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

@Component
@ComponentScan(basePackages="com.example.demo")
public class Circle implements Shape {
    
    @Autowired
    @Qualifier("circleRelated")
    private Point point;

    public Circle(Point point) {
        super();
        this.point = point;
    }


    public Point getPoint() {
        return point;
    }


    public void setPoint(Point point) {
        this.point = point;
    }


    public void draw() {
        System.out.println("Draw is called");
        System.out.println("center.x:" + point.getX());
        System.out.println("center.y:" + point.getY());
    }

}

Point.java

package com.example.demo;

import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

@Component
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;
    }
    
    
}

Shape.java

package com.example.demo;

public interface Shape {
    
    public void draw();

}

Getting error as below:

Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'circle': Unsatisfied dependency expressed through field 'point'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.example.demo.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(value=circleRelated)} [2m2021-03-28 12:17:31.958[0;39m [32m INFO[0;39m [35m2747[0;39m [2m---[0;39m [2m[ main][0;39m [36mConditionEvaluationReportLoggingListener[0;39m [2m:[0;39m

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled. [2m2021-03-28 12:17:31.977[0;39m [31mERROR[0;39m [35m2747[0;39m [2m---[0;39m [2m[ main][0;39m [36mo.s.b.d.LoggingFailureAnalysisReporter [0;39m [2m:[0;39m


APPLICATION FAILED TO START


Description:

Field point in com.example.demo.Circle required a bean of type 'com.example.demo.Point' that could not be found.

The injection point has the following annotations: - @org.springframework.beans.factory.annotation.Autowired(required=true) - @org.springframework.beans.factory.annotation.Qualifier(value=circleRelated)

The following candidates were found but could not be injected: - User-defined bean

Action:

Consider revisiting the entries above or defining a bean of type 'com.example.demo.Point' in your configuration.


Solution

  • You could import your xml-beans via @ImportResource

    And just @Autowire the applicationContext or access it via an EventListener.

    Here is an example to draw the circle when the application is ready (ApplicationReadyEvent)

    @SpringBootApplication
    @ImportResource("classpath:beans.xml")
    public class SpringSecondApplication {
        
       public static void main(String[] args) {
          SpringApplication.run(SpringSecondApplication.class, args);
       }
    
       @EventListener
       public void init(ApplicationReadyEvent event){
          Shape shape = (Shape) event.getApplicationContext().getBean("circle");
          shape.draw();
       }
    
    

    EDIT: reviewed my answer

    @ImportResource("classpath:beans.xml") is not strictly needed but recommend it for clarity. (beans.xml will be auto-detected)

    If you want to get a bean in the main method ( here circle ) you can, but here again, I would not recommend it.

    This should work:

    @SpringBootApplication
    public class DemoApplication {
    
        public static void main(String[] args) {
            ApplicationContext context = SpringApplication.run(DemoApplication.class, args);
    
            Shape shape = (Shape) context.getBean("circle");
            shape.draw();
        }
    }
    

    Things that are conflicting in your code, or could cause a conflict:

    • @Component on Point and the same bean in your beans.xml
    • @ComponentScan(basePackages="com.example.demo") on Circle: this makes no sense...
    • ApplicationContext context = new ClassPathXmlApplicationContext ("beans.xml") while SpringApplication.run(...) returns an ApplicationContext