Impossible de faire le câblage automatique du champ: RestTemplate dans l'application Spring Boot


109

J'obtiens une exception ci-dessous lors de l'exécution de l'application Spring Boot au démarrage:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'testController': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private org.springframework.web.client.RestTemplate com.micro.test.controller.TestController.restTemplate; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.web.client.RestTemplate] 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)}

J'utilise RestTemplate automatiquement dans mon TestController. J'utilise Maven pour la gestion des dépendances.

TestMicroServiceApplication.java

package com.micro.test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class TestMicroServiceApplication {

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

TestController.java

    package com.micro.test.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
public class TestController {

    @Autowired
    private RestTemplate restTemplate;

    @RequestMapping(value="/micro/order/{id}",
        method=RequestMethod.GET,
        produces=MediaType.ALL_VALUE)
    public String placeOrder(@PathVariable("id") int customerId){

        System.out.println("Hit ===> PlaceOrder");

        Object[] customerJson = restTemplate.getForObject("http://localhost:8080/micro/customers", Object[].class);

        System.out.println(customerJson.toString());

        return "false";
    }

}

POM.xml

    <?xml version="1.0" encoding="UTF-8"?>
<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>com.micro.test</groupId>
    <artifactId>Test-MicroService</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>Test-MicroService</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.3.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

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

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


</project>

1
En votant pour votre question, il n'est pas évident que lorsque tout est lié par magie, un RestTemplaten'est pas automatiquement créé pour vous.
daniel.eichten

Vote positif - le tutoriel sur la propre page de Spring Boot ne dit rien sur la création d'un RestTemplate Bean !!
Matt

Réponses:


174

C'est exactement ce que dit l'erreur. Vous n'avez créé aucun RestTemplatebean, il ne peut donc pas en passer automatiquement. Si vous en avez besoin, RestTemplatevous devrez en fournir un. Par exemple, ajoutez ce qui suit à TestMicroServiceApplication.java :

@Bean
public RestTemplate restTemplate() {
    return new RestTemplate();
}

Notez que dans les versions antérieures du Spring cloud starter pour Eureka, un RestTemplatebean a été créé pour vous, mais ce n'est plus le cas.


Merci beaucoup pour votre réponse. Cela a aidé!
1616 au Khuzi

19
Vous avez voté pour la question et votre réponse car il n'est pas évident que vous deviez créer manuellement un RestTemplatemoment où tout le reste est créé et lié par magie pour vous. Surtout si on utilisait avant le spring-cloud qui fournit un autoconfiguré RestTemplate. ;-)
daniel.eichten

2
Honnêtement, c'est la raison pour laquelle j'ai mis cette question ici dans le forum. Je m'attendais à ce que RestTemplate soit lié pour moi. :-) Cela fonctionnait bien lorsque j'avais inclus la dépendance Eureka dans POM.xml. Cela fonctionnait bien sans définir le bean RestTemplate. Une des classes d'Eureka pourrait avoir défini ce bean ou quelque chose comme ça.
Khuzi

4
Juste une mise à jour. Depuis Spring Boot 1.4.0 RestTemplateBuilderpeut être utilisé pour gérer les RestTemplateinstances. Exemple ici spring.io/guides/gs/consuming-rest
Mensur

Je ne peux pas encore passer à SB 1.4.0. Je veux faire cela avec 1.3.8.RELEASE mais la solution @ g00glen00b n'a pas fonctionné pour moi. J'utilise également spring-cloud-netflixartifactid avec la version 1.1.5.RELEASE. Mon RestTemplate est appelé à partir d'une @RestControllerclasse java qui utilise @Autowiredpour le RestTemplate. Puis-je avoir une aide s'il vous plait ?
ZeroGraviti

33

En fonction des technologies que vous utilisez et des versions qui influenceront la façon dont vous définissez un RestTemplatedans votre @Configurationclasse.

Spring> = 4 sans Spring Boot

Définissez simplement un @Bean:

@Bean
public RestTemplate restTemplate() {
    return new RestTemplate();
}

Botte à ressort <= 1,3

Pas besoin d'en définir un, Spring Boot en définit automatiquement un pour vous.

Botte à ressort> = 1,4

Spring Boot ne définit plus automatiquement un RestTemplatemais définit plutôt un RestTemplateBuildervous permettant plus de contrôle sur le RestTemplate qui est créé. Vous pouvez injecter le RestTemplateBuildercomme argument dans votre @Beanméthode pour créer un RestTemplate:

@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
   // Do any additional configuration here
   return builder.build();
}

L'utiliser dans votre classe

@Autowired
private RestTemplate restTemplate;

Référence


8

Si un TestRestTemplate est une option valide dans votre test unitaire, cette documentation peut être pertinente

http://docs.spring.io/spring-boot/docs/1.4.1.RELEASE/reference/htmlsingle/#boot-features-rest-templates-test-utility

Réponse courte: si vous utilisez

@SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)

puis @Autowiredfonctionnera. Si vous utilisez

@SpringBootTest(webEnvironment=WebEnvironment.MOCK)

puis créez un TestRestTemplate comme celui-ci

private TestRestTemplate template = new TestRestTemplate();

1

L'erreur indique directement que le RestTemplatebean n'est pas défini dans le contexte et qu'il ne peut pas charger les beans.

  1. Définissez un bean pour RestTemplate puis utilisez-le
  2. Utiliser une nouvelle instance du RestTemplate

Si vous êtes sûr que le bean est défini pour le RestTemplate, utilisez ce qui suit pour imprimer les beans disponibles dans le contexte chargé par l'application Spring Boot

ApplicationContext ctx = SpringApplication.run(Application.class, args);
String[] beanNames = ctx.getBeanDefinitionNames();
Arrays.sort(beanNames);
for (String beanName : beanNames) {
    System.out.println(beanName);
}

Si cela contient le bean du nom / type donné, tout va bien. Ou bien définissez un nouveau bean puis utilisez-le.


1

Étant donné que les instances RestTemplate doivent souvent être personnalisées avant d'être utilisées, Spring Boot ne fournit aucun bean RestTemplate configuré automatiquement.

RestTemplateBuilder offre un moyen approprié de configurer et d'instancier le bean de modèle rest, par exemple pour l'authentification de base ou les intercepteurs.

@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
    return builder
                .basicAuthorization("user", "name") // Optional Basic auth example
                .interceptors(new MyCustomInterceptor()) // Optional Custom interceptors, etc..
                .build();
}


0

Veuillez vous assurer de deux choses:

1- Utilisez l' @Beanannotation avec la méthode.

@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder){
    return builder.build();
}

2- La portée de cette méthode doit être publique et non privée .

Exemple complet -

@Service
public class MakeHttpsCallImpl implements MakeHttpsCall {

@Autowired
private RestTemplate restTemplate;

@Override
public String makeHttpsCall() {
    return restTemplate.getForObject("https://localhost:8085/onewayssl/v1/test",String.class);
}

@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder){
    return builder.build();
}
}

0

La façon la plus simple que j'ai pu réaliser un exploit similaire d'utiliser le code ci-dessous ( référence ), mais je suggérerais de ne pas faire d'appels API dans les contrôleurs ( principes SOLID ). Le câblage automatique de cette manière est également mieux optimisé que la manière traditionnelle de le faire.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
public class TestController {

    private final RestTemplate restTemplate;


    @Autowired
    public TestController(RestTemplateBuilder builder) {
        this.restTemplate = builder.build();
    }

    @RequestMapping(value="/micro/order/{id}", method= RequestMethod.GET, produces= MediaType.ALL_VALUE)
    public String placeOrder(@PathVariable("id") int customerId){

        System.out.println("Hit ===> PlaceOrder");

        Object[] customerJson = restTemplate.getForObject("http://localhost:8080/micro/customers", Object[].class);

        System.out.println(customerJson.toString());

        return "false";
    }
}

0

vous essayez d'injecter le restTemplate mais vous devez créer une classe de configuration. là, vous devez créer un bean qui vous renvoie un nouveau RestTemplate, voir l'exemple ci-dessous.

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
public class YourConfigClass {


    @Bean
    public RestTemplate restTesmplate() {
        return new RestTemplate();
    }

}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.