0
votes

I have the following structure of by Spring MVC API with a single end-point getAnnotation:

@SpringBootApplication
public class Application {
    @Bean
    public javax.validation.Validator localValidatorFactoryBean() {
        return new LocalValidatorFactoryBean();
    }

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}


@Service
public class MyAnalyzerImpl implements MyAnalyzer
{

    @Autowired
    public MyAnalyzerImpl() {}

    @Override
    public Annotations getAnnotation(MyRequest request)
    {
        // ...
    }
}

Interface

public interface MyAnalyzer {
    Annotations getAnnotation(MyRequest request);
}

Controller

@RestController
@RequestMapping("/thisapi/{id}")
public class MyController {

    @Autowired
    @Qualifier("MyAnalysis")
    MyAnalyzer myAnalyzer;

    @RequestMapping(value = "/getAnnotation", method = RequestMethod.GET)
    public Annotations getAnnotation(@PathVariable String docId,
                                     @RequestParam(value = "document", defaultValue = "{'id':'1','title':'bla-bla'}") String text) {
        MysRequest myRequest = new MyRequest(MyRequest.TYPE_ANNOTATION, text);
        return myAnalyzer.getAnnotation(myRequest);
    }
}

To test the API, I firstly created src/test/java/MyAnalyzerImplTest.java and was able to successfully execute it:

@RunWith(SpringJUnit4ClassRunner.class)
public class MyAnalyzerImplTest {

    private MyAnalyzerImpl myAnalyzer;
    private String sampleText;

    @Test
    public void testEndpoint() throws Exception {
        MyRequest request = new MyRequest(  MyRequest.TYPE_ANNOTATION,
                                                    "1",
                                                    sampleText
                                                 );
        Annotations results = myAnalyzer.getAnnotation(request);
        Assert.assertTrue("This " + results.getPayload().getWords().size() + ") " +
                "should be greater than 0", results.getPayload().getWords().size() > 0);
    }

    @Before
    public void setUp() throws Exception {
        myAnalyzer = new MyAnalyzerImpl();
        File f = new File("src/test/resources/texsts/text.json");
        if (f.exists()){
            InputStream is = new FileInputStream("src/test/resources/texts/text.json");
            samplePublication = IOUtils.toString(is);
        }
        Thread.sleep(1000);
    }

}

Now I want to run Application.java to launch API at the address http://localhost:8080. I get the following error:

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'myController': Unsatisfied dependency expressed through field 'myAnalyzer': No qualifying bean of type [org.api.thistool.MyAnalyzer] found for dependency [org.api.thistool.MyAnalyzer]: 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=MyAnalysis)}; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.api.thistool.MyAnalyzer] found for dependency [org.api.thistool.MyAnalyzer]: 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=MyAnalysis)}

Just in case I also provide the RAML file.

#%RAML 0.8
title: MY API
version: v1
baseUri: http://localhost:8080
resourceTypes:
  - annotate-type:
      description: Bla-bla
      get:
        description: bla-bla
        queryParameters:
          text:
            description: json of a document
            type: string
            required: true
            default: "{'id':'1','title':'bla-bla'}"
        responses:
          200:
            body:
              application/json:
                example: |
                  {
                    "words": "['aaa', 'bbb']"
                  }
/thisapi:
    /{id}/getAnnotation:
        type:
          annotate-type:
        uriParameters:
          id:
            description: document id
            type: string
1
Spring cannot autowire dependency for "MyAnalyzer" because it cannot find a bean with id "MyAnalysis". If there's only a single bean of that type in the context you can remove the @Qualifier("MyAnalysis"). If there is multiple beans of type MyAnalysis add proper @Qualifier for MyAnalyzerImpljp86
@jp86: Do you mean I should use @Autowired MyAnalyzer myAnalyzer; instead of @Autowired @Qualifier("MyAnalysis") MyAnalyzer myAnalyzer;?Klue
Yes, it should help. I'll try to explain: Your controller needs a single bean of type MyAnalyzer so you're using @Autowired on that dependency. This will instruct spring to look up a bean of that type in the context and inject it automatically. When you also declare the @Qualifier it will limit the scan to bean with that id, in this case "MyAnalysis". When application starts MyAnalyzerImpl will (by default) get the bean id "myAnalyzerImpl" without further instruction, and hence you see the error of "unsatisfied dependency expressed..." when dependencies are being autowired.jp86
@jp86: Thanks for your explanations.Klue
np, tell me if it helped.jp86

1 Answers

0
votes

As discussed in the comments, original error was due to the use of invalid qualifier @Qualifier("MyAnalysis"). No bean for id "MyAnalysis" is present in the context. Since there's only one suitable implementation using additional @Qualifier is useless.

Second error was because of the invalid use of @Autowired with constructor. Similiar issue is described here