微服务应用程序中,我们会通过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完成参数的配置,实际上都是在解决我们最终的依赖与它们自己的依赖的组装问题。