Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

@Async inside a Runnable @Component class is not letting it get Autowired

I have two classes Uploader and UploadTask. Using Spring 4.

 @Service 
 public class Uploader{
  @Autowired
  private UploadTask task;
 }
 @Component
 public class UploadTask implements Runnable{
   @Async
   public void soso(){
   }
   public void run(){
   }
 }

On application startup I get the following exception:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'uploader': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.xx.uploading.UploadTask com.xx.uploading.Uploader.task; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.xx.uploading.UploadTask] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true), @org.springframework.beans.factory.annotation.Qualifier(value=uploadTask)}

I think this may be because UploadTask is a Runnable. When I removed the @Async annotation then it does not throw any Exception. Is there an explanation?

Update: When I saw the logs I found that the UploadTask class bean is being created alright. It is not being found during autowiring.

like image 935
zafar142003 Avatar asked Nov 27 '22 04:11

zafar142003


1 Answers

I have run this code successfully. please see my code below.

     @Service 
 public class Uploader{
  @Autowired
  private UploadTask task;

  public void display(){
      task.run();
      task.soso();
  }
 }

 @Component
public class UploadTask implements Runnable{
  @Async
  public void soso(){
      System.out.println("Upload task running---");
  }
  public void run(){
      System.out.println("Running the class UploadTask---------");
  }
}

Spring.xml

  <mvc:annotation-driven/> 
    <context:component-scan base-package="com.exp" />

Below is my test class

    @RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath*:/META-INF/spring/spring.xml"})
public class TestScope {

    @Autowired
    Uploader uploader = null;

    @Test
    public void testScope()
    {
        uploader.display();

    }
}

Below is my Pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>SpringAsync-Exmp</groupId>
  <artifactId>SpringAsync-Exmp</artifactId>
  <version>0.0.1-SNAPSHOT</version>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.3.2.RELEASE</version>
    </parent>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.9</version>
            <scope>test</scope>
        </dependency>

       <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
            <scope>provided</scope>
        </dependency>

    </dependencies>


    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>   

When i run the test class , it successfully print the sysout in UploadTask.

UPDATE I have modified the coded to run with @EnableAsync. Please see below the modified code.

Uploader class

@EnableAsync
 @Service 
 public class Uploader{
  @Autowired
  private UploadTask uploadTask;


  public UploadTask getUploadTask() {
    return uploadTask;
}


public void setUploadTask(UploadTask uploadTask) {
    this.uploadTask = uploadTask;
}


public void display(){
      uploadTask.run();
      uploadTask.soso();
  }
 }

UploadTask interface

@Component
public interface UploadTask extends Runnable{
  @Async
  public void soso();
  public void run();
}

UploadTaskImpl class

public class UploadTaskImpl implements UploadTask{

      public void soso()
      {
          System.out.println("Inside the class----");
      }
      public void run()
      {
          System.out.println("Inside the class--run--");
      }
}

All others are same. This time I have created UploaderTask as an interface and write a new class to implement that. This solves the problem.

I think @EnableAsync is converting any component using @Async to proxy implementing same interfaces used by this component class, so when make autowire with concrete class will face type conflict between this concrete class and proxy.

like image 136
vineeth sivan Avatar answered Nov 29 '22 05:11

vineeth sivan