SpringBoot学习——@Autowired自动注入报:could not be found问题的理解和解决方案

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/u014745069/article/details/78801405

微服务应用程序中,我们会通过Java后台的方式发送http请求并调用其他注册在Spring Cloud Eureka server上的微服务,之前我们可能会手动封装一个Http发送请求类,然后通过其中的sendGet或者sendPost方法借由java IO的形式发送出去。

但是,上述方法过于繁琐和和臃肿,我们使用org.springframework.web.client.RestTemplate实例,通过几行代码就可以轻松发送我们需要的请求。

然而,在实际的应用程序调用时,我通过@Autowired方式将RestTemplate实例注入到类中,

@Autowired
    private RestTemplate restTemplate;

在启动springboot时,控制台报告启动失败:

***************************
APPLICATION FAILED TO START
***************************

Description:

Field restTemplate in com.seco.ad.controller.MobileAdController required a bean of 
type 'org.springframework.web.client.RestTemplate' that could not be found.

Action:

Consider defining a bean of type 'org.springframework.web.client.RestTemplate' in your configuration.

可以看到Description:中的描述是RestTemplate未能找到!!!

为什么?因为Spring容器没有注册RestTemplate实例,也就无法通过@Autowired自动注入方式“new”一个RestTemplate实例出来(通过重写无参构造器的方式,可以发现这种通过@Autowired方式自动注入的实例确实是通过new方法来完成的)。

在之前的@Autowired注解使用中,我们可以轻松的将DAO层的依赖对象JdbcTemplate实例顺利的自动注入到DAO层的服务中去,而不需要任何类似JavaConfig或者xml配置Bean定义到Spring容器中去,那为什么RestTemplate就需要?

原来JdbcTemplate 和RestTemplate一样都是都是spring框架本身提供的组件,但是项目中用到的JdbcTemplate是不需要参数的,可以通过@Autowired自动注入,而不需要初始化,但是RestTemplate需要。另外,在《@Autowired自动注入实例》这篇文章中也可以看到,当需要参数的JdbcTemplate进行自动注入的时候应用程序在启动时就会发生类似“could not be found”的空指针异常。

因此,回过头来反思@Autowired自动注入RestTemplate报“could not be found”错误的原因,即是此注入实例需要参数!

我在通过网上资料的查询中找到了正确注入RestTemplate的方法,确实需要将RestTemplate注册到spring容器中去,另外,还有我们刚刚提到的参数问题,也就是RestTemplate实例的依赖:org.springframework.http.client.ClientHttpRequestFactory。

SpringBoot提倡通过JavaConfig方式注册我们需要的Bean元素:

package com.seco.ad.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

/**
 * RestTemplate配置
 * 这是一种JavaConfig的容器配置,用于spring容器的bean收集与注册,并通过参数传递的方式实现依赖注入。
 * "@Configuration"注解标注的配置类,都是spring容器配置类,springboot通过"@EnableAutoConfiguration"
 * 注解将所有标注了"@Configuration"注解的配置类,"一股脑儿"全部注入spring容器中。
 * 
 * @author mht
 *
 */
@Configuration
public class RestTemplateConfig {
    
    @Bean
    public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
        return new RestTemplate(factory);
    }

    @Bean
    public ClientHttpRequestFactory simpleClientHttpRequestFactory() {
        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
        factory.setReadTimeout(5000);//ms
        factory.setConnectTimeout(15000);//ms
        return factory;
    }
}

(最近在研读《SpringBoot揭秘》有关springboot启动方面的知识中谈到了有关@Configuration注解的的作用我写在了这段code中的JavaDoc里)

可以看到,在此JavaConfig配置中,我注册了两个Bean,而第二个Bean正是我们最终需要的RestTemplate实例的依赖,有了这个配置类,我们回过头再通过@Autowired方式自动注入我们需要的RestTemplate就不会在springboot启动时报“无法找到”错误了。

令人欣喜的是,将这些思路理清的功臣是《@Autowired自动注入实例》这篇文章。

我试着通过文章中提到的“@Autowired自动注入有参数依赖对象的写法”编写类似的代码后,发现即便是没有RestTemplateConfig这个配置类,没有对spring容器进行bean定义的注册,依然可以成功的启动springboot应用程序。写法如下:

    private RestTemplate restTemplate;
    private ClientHttpRequestFactory factory;
    
    @Autowired
    public void setFactory() {
        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
        factory.setReadTimeout(5000);//ms
        factory.setConnectTimeout(15000);//ms
        this.factory = factory;
    }
    
    @Autowired
    public void setRestTemplate() {
        this.restTemplate = new RestTemplate(this.factory);
    }

上述代码思路很简单:设置两个私有属性 restTemplate和factory,然后下面先通过设置参数的方式自动完成对factory对象的注入,然后再讲factory注入到restTemplate中去,即完成了RestTemplate的注入工作。即便没有JavaConfig这样的Bean的注册配置类,应用程序依然可以启动成功。

实际上还是围绕了spring容器的关键两步(引自王富强的《SpringBoot解密》):第一步:收集和注册;第二步:分析和组装。

当我们依赖的实例(例如本文中提到的JdbcTemplate和RestTemplate)需要其他的依赖时,当然就不可能仅仅通过@Autowired自动注入到我们需要的类中而不去考虑它们的参数(也就是它们的依赖),因此,不管是通过JavaConfig配置方式还是利用@Autowired完成参数的配置,实际上都是在解决我们最终的依赖与它们自己的依赖的组装问题。

展开阅读全文

没有更多推荐了,返回首页