zafar142003
zafar142003

Reputation: 2159

@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.

Upvotes: 5

Views: 3734

Answers (4)

vineeth sivan
vineeth sivan

Reputation: 510

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.

Upvotes: 1

Nandu cg
Nandu cg

Reputation: 84

configure your service classes in the context.xml file.
like the below codes.

<bean id="Uploader" class="packageName.Uploader">
    <property name="task" ref="task"/>
</bean>

<bean id="task" class="packageName.UploadTask ">
    <property name="xxx" ref="xxx"/>
</bean>
change xxx with your datasource name.

Upvotes: -1

Use @EnableAsync at class level.

@Component
@EnableAsync
public class UploadTask implements Runnable{
   @Async
   public void soso(){
   }
   public void run(){
   }
 }

Upvotes: 0

Nadeem Shukoor
Nadeem Shukoor

Reputation: 353

Have a public setter for private UploadTask task; and test.

Upvotes: -1

Related Questions