一、簡介
我們可以看到上一篇文章的消費者這邊調用Service時比較麻煩,所以我們可以使用Feign來簡化這部分操作,它底層也是使用Ribbon實現的只是Ribbon支持HTTP和TCP兩種通信協議,而Feign只支持HTTP一種,同樣支持負載均衡,所以能滿足90%以上的需求了。
二、搭建生產者模塊
其實這部分是通用的,對於消費者這邊使用什么Client並沒有影響,Eureka還是使用之前搭建好的,生產者模塊也用搭建好的,我們新建一個controller用於承接service,這邊就算完成了
import javademo.tyh.model.hotel.BaseInfoModel; import javademo.tyh.model.hotel.ResponseModel; import javademo.tyh.model.hotel.SearchModel; import javademo.tyh.service.hotel.service.HotelService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpServletRequest; import java.util.List; @Controller @RequestMapping("/api/hotel") public class HotelController { //自動注入請求對象,查看header信息 @Autowired HttpServletRequest request; @Autowired HotelService service; @ResponseBody @RequestMapping("/test") public void test() { //do something... } @ResponseBody @RequestMapping("/get") public BaseInfoModel get(@RequestParam(defaultValue = "0") int id) { String tyh = request.getHeader("taiyonghai");//獲取Header頭信息 System.out.println("參數為:" + id); return service.get(id); } //設置參數為@RequestBody可以自動序列化為對象 @ResponseBody @RequestMapping("/list") public ResponseModel<List<BaseInfoModel>> list(@RequestBody SearchModel search) { System.out.println("參數為:" + search.getName()); List<BaseInfoModel> list = service.list(); ResponseModel<List<BaseInfoModel>> respModel = new ResponseModel<>(); respModel.setCode("000000"); respModel.setMsg("操作成功"); respModel.setData(list); return respModel; } }
添加通用Model實體,同樣不生成get/set方法,自己生成吧
import com.baomidou.mybatisplus.annotations.TableName; import java.time.LocalDateTime; @TableName("base_info") public class BaseInfoModel { private int id; private String nickname; private LocalDateTime updateTime; }
public class SearchModel { private int id; private String name; }
public class ResponseModel<T> { private String code; private String msg; private T data; }
三、搭建消費者模塊——Feign
還是創建一個Maven模塊,看一下我的項目結構
1、修改pom.xml文件,增加對spring boot、spring cloud、feign的引用
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.0.3.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Finchley.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <!-- import spring boot --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- import openfeign --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
2、新建resources文件夾,新建application.yml配置文件,並修改內容如下
#設置自己的程序名稱 spring: application: name: javademo-tyh-web-hotel #thymeleaf 配置 thymeleaf: encoding: UTF-8 prefix: classpath:/templates cache: false #服務注冊中心地址(剛剛搭建的Eureka Server的地址) eureka: client: #是否向服務中心注冊自己 register-with-eureka: false #設置eureka服務中心的地址 service-url: defaultZone: http://localhost:11000/eureka #設置自己啟動的端口 server: port: 13000
3、修改main()方法的類,增加Feign的注解
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; //引入Feign注解 @EnableFeignClients @SpringBootApplication public class AppWebHotel { public static void main(String[] args) { SpringApplication.run(AppWebHotel.class, args); } }
4、新建service文件夾,新建HotelService的Interface用來執行Feign向后端服務生產者的調用,指明服務生產者名稱,接口方法指明具體的服務路由,這種方式比Ribbon簡單很多,而且更直觀更像調用后台方法
import javademo.tyh.model.hotel.BaseInfoModel; import javademo.tyh.model.hotel.ResponseModel; import javademo.tyh.model.hotel.SearchModel; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.stereotype.Service; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import java.util.List; @Service @FeignClient("javademo-tyh-service-hotel")//指明服務生產者名稱 @RequestMapping("/api/hotel")//此位置的mapping與controller上的mapping是共享全局唯一的,如果這里的名字和controller的路由相同會報錯ambiguous mapping public interface HotelService { // //無參數無返回值 // @RequestMapping("/test") // void test(); //基礎類型參數 @RequestMapping("/get") BaseInfoModel get(@RequestParam("id") int id); //自定義類型參數以及泛型返回值 @RequestMapping("/list") ResponseModel<List<BaseInfoModel>> list(@RequestBody SearchModel search); }
5、新建controller文件夾,新建HotelController的class用來接收頁面調用
import javademo.tyh.model.hotel.BaseInfoModel; import javademo.tyh.model.hotel.ResponseModel; import javademo.tyh.model.hotel.SearchModel; import javademo.tyh.web.hotel.service.HotelService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import java.util.List; @Controller @RequestMapping("/hotel") public class HotelController { //自動注入Feign接口對象 @Autowired HotelService service; @RequestMapping("/get") public String get(Model model) { //像調用方法一樣調用即可實現服務生產者端的調用 model.addAttribute("model",service.get(1)); return "/hotel/get"; } //直接返回json不寫頁面了 @ResponseBody @RequestMapping("/list") public List<BaseInfoModel> list() { SearchModel search = new SearchModel(); search.setId(1); search.setName("taiyonghai"); ResponseModel<List<BaseInfoModel>> result = service.list(search); return result.getData(); } }
6、在resources文件夾下建立templates及hotel文件夾,新建get.html頁面,用來渲染數據
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>this is /hotel/get</h1> <label th:text="${model.Id}">id</label> <label th:text="${model.Nickname}">Nickname</label> <label th:text="${model.UpdateTime}">UpdateTime</label> </body> </html>
OK,現在啟動Eureka注冊中心、啟動服務生產者、啟動服務消費者訪問http://localhost:13000/hotel/get就可以看到從service取到的結果了
本應該在這里就完成了,但有時候我們需要在Header頭信息中增加些公共內容,在Feign中就可以使用如下方式實現
在消費者這邊的service文件夾下新建FeignInterceptor實現RequestInterceptor接口
import feign.RequestInterceptor; import feign.RequestTemplate; import org.springframework.stereotype.Component; //使用攔截器添加Header頭信息 @Component public class FeignInterceptor implements RequestInterceptor { @Override public void apply(RequestTemplate requestTemplate) { System.out.println("調用了FeignInterceptor"); requestTemplate.header("taiyonghai", "1111111111111"); } }
Feign的簡單使用就OK了