Spring Boot學習3:web篇(中)-Spring boot Rest學習

一枚程式設計師發表於2018-02-03
--REST理論基礎

1.架構屬性
效能
可伸縮性
統一結構簡化性:如URI,RequestHeader,RequestBody等等
元件可修改性:
元件通訊可見性
元件可移植性
可靠性

2.架構約束
C/S架構
無狀態
可快取:兩個方面,服務端和客戶端
分層系統
按需程式碼
統一介面

3.統一幾口(Uniform Interface)
資源識別:URI
資源操作:GET(取資源),POST(非冪等性,實現的時候需要實現冪等性),PUT(更新資源),DELETE
自描述資訊:ContentType,MIME-Type,Meida-Type
超媒體


--REST服務端實踐
1.Spring Boot Rest
核心介面:
定義相關
@Controller
@RestController

對映相關
@RequestMapping
@PathVariable  路徑變數

請求相關
@RequestParam
@RequestHeader
@CookieValue
RequestEntity

響應相關
@ResponseBody
ResponseEntity


實踐:
在start.spring.io上建立專案,並Generate Project
Group:com.segmentfault
Artifact:spring-boot-lesson-3
Dependencies:Web,Actuator

將匯出的工程匯入到IDEA中。


rest支援多種返回格式,不僅限於JSON,XML,HTML,還可以自定義的格式
例子1:HTML例子
@Controller
public class RestDemoController {
    //HTML
    @RequestMapping("/html/demo")
    @ResponseBody
    public String htmlCode(){
        return "<html><body><h1>hello</h1></body></html>";
    }
}

訪問http://localhost:8080/html/demo地址,可以看到返回的html頁面資訊

例子2:多種Mapping語法
//HTML,可以對映到多個方法上
//@RequestMapping(value={"/html/demo","/html/demo2"},method = {RequestMethod.GET})
@GetMapping(path={"/html/demo3"})  //相對於 @RequestMapping(value={"/html/demo3"},method = {RequestMethod.GET})
@ResponseBody
public String htmlCode(){
return "<html><body><h1>hello</h1></body></html>";
}

@RequestMapping和@GetMapping是會衝突的,兩者不能共存

重新啟動SpringBoot
Mapped "{[/html/demo3],methods=[GET]}" onto public java.lang.String com.segmentfault.springbootlesson3.controller.RestDemoController.htmlCode()


例子3: @RestController
@RestController 相對於 @Controller和@ResponseBody的組合

//@Controller
@RestController
public class RestDemoController {
    //HTML,可以對映到多個方法上
    //@RequestMapping(value={"/html/demo","/html/demo2"},method = {RequestMethod.GET})
    @GetMapping(path={"/html/demo3"})
//    @PostMapping(path={"/html/demo2"})
//    @ResponseBody
    public String htmlCode(){
        return "<html><body><h1>hello</h1></body></html>";
    }
}

此時訪問網站,可以看到html返回的頁面效果。

如果去除@RestController,改成@Controller,此時因為沒有@ResponseBody,所以:
return "<html><body><h1>hello</h1></body></html>";時,springboot就去template目錄中找對應的地址找模板了,但是因為找不到,所以在頁面上返回:Whitelabel Error Page


例子4:@PathVariable
//@Controller
@RestController
public class RestDemoController {
    //HTML,可以對映到多個方法上
    //@RequestMapping(value={"/html/demo","/html/demo2"},method = {RequestMethod.GET})
    @GetMapping(path={"/html/demo3"})
//    @PostMapping(path={"/html/demo2"})
//    @ResponseBody
    public String htmlCode(){
        return "<html><body><h1>hello</h1></body></html>";
    }


    @GetMapping(path={"/html/demo/{messages}"})
    public String htmlCode(@PathVariable String messages){
        return "<html><body>"+messages+"</body></html>";
    }
}

啟動專案,在瀏覽器中輸入地址:http://localhost:8080/html/demo/hellohello
那麼在頁面上顯示了hellohello



例子5:@RequestParam

@GetMapping(path={"/html/demo/param"})
public String htmlParam(@RequestParam String param){
return "<html><body>"+param+"</body></html>";
}

在瀏覽器中輸入:http://localhost:8080/html/demo/param?param=helloworld
瀏覽器返回結果:helloworld


方法可以進一步修改為:
    @GetMapping(path={"/html/demo/param"})
    //指定該引數不是必須的
    public String htmlParam(@RequestParam(name="p",required=false,defaultValue="abc") String param){
        return "<html><body>"+param+"</body></html>";
    }

name="p",required=false,name是指引數的名稱,required是設定引數是否必需,defaultValue是設定預設值
當輸入http://localhost:8080/html/demo/param沒有攜帶引數的時候,就會返回abc

當然,我們也是可以使用Servlet來獲取對應的parameter的:
    @GetMapping(path={"/html/demo/param"})
    //指定該引數不是必須的
    public String htmlParam(@RequestParam(name="p",required=false,defaultValue="Empty") String param, HttpServletRequest request){
        String param2 = request.getParameter("p");
        return "<html><body>requestparam:"+param+",request:"+param2+"</body></html>";
    }

自動轉換功能:當我們設定了@ReqeustParam的引數型別為Integer的時候,那麼傳入的引數就會自動轉換型別
    @GetMapping(path={"/html/demo/param"})
    //指定該引數不是必須的
    public String htmlParam(@RequestParam(name="p",required=false,defaultValue="Empty") String param, HttpServletRequest request,
                            @RequestParam(name="age",required = false,defaultValue = "0") Integer age){
        String param2 = request.getParameter("p");
        System.out.println(age+1);
        return "<html><body>requestparam:"+param+",request:"+param2+"</body></html>";
    }
輸入地址:http://localhost:8080/html/demo/param?age=100
在Console中自動列印了101,說明引數型別字串已經自動轉換未Integer型別了



例子6:@RequestHeader

@GetMapping(path={"/html/demo/header"})
//指定該引數不是必須的
public String htmlHeader(@RequestHeader(value="Accept") String acceptHeader, HttpServletRequest request){
    return "<html><body>accept header:"+acceptHeader+"</body></html>";
}

在瀏覽器中輸入:http://localhost:8080/html/demo/header
返回結果:accept header:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
可以看到請求頭彙總的Accept


例子7:RequestEntity
可以獲取到請求頭和請求body的全部資訊,相比於Requestheader更多的資訊:
    @GetMapping(path={"/html/demo/response/entity"})
    //ResponseEntity是
    public ResponseEntity<String> htmlResponseEntity(RequestEntity request){
        System.out.println(request.getUrl());
        HttpHeaders header = new HttpHeaders();
        header.add("myheader","helloworld");
        ResponseEntity entity = new ResponseEntity("<html><body>html ResponseEntity</body></html>",header, HttpStatus.OK);
        return entity;
    }

訪問後結果:http://localhost:8080/html/demo/response/entity
http://localhost:8080/html/demo/response/entity

也可以指定獲取請求頭中的資訊:
request.getHeaders().get("Accept")

執行結果:[text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8]

例子8:ResponseEntity
ResponseEntity可以管理頭和body,而ResponseBody只能管理Body部分


    @GetMapping(path={"/html/demo/response/entity"})
    //ResponseEntity是泛型
    public ResponseEntity<String> htmlResponseEntity(){
        return ResponseEntity.ok("<html><body>html ResponseEntity</body></html>");
    }
其中ResponseEntity.ok指定了返回的status


同樣的,也可以自定義返回的Header
    @GetMapping(path={"/html/demo/response/entity"})
    //ResponseEntity是
    public ResponseEntity<String> htmlResponseEntity(){
        HttpHeaders header = new HttpHeaders();
        header.add("myheader","helloworld");
        ResponseEntity entity = new ResponseEntity("<html><body>html ResponseEntity</body></html>",header, HttpStatus.OK);
        return entity;
    }

訪問該地址:http://localhost:8080/html/demo/response/entity
頁面上顯示html ResponseEntity
開啟瀏覽器Network檢視請求頭資訊
myheader:helloworld



例子9:Json格式的例子
建立User類
package com.segmentfault.springbootlesson3.pojo;

public class User {
    private String name;
    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

建立JsonDemoController:
@RestController
public class JsonDemoController {
    @GetMapping("/json/user")
    public User JsonMethod(){
        User user = new User();
        user.setName("xiaoming");
        user.setAge(10);
        return user;
    }
}


訪問地址:
http://localhost:8080/json/user
返回結果:
{"name":"xiaoming","age":10}


那麼我們如何改成xml格式的呢?

配置pom

<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
</dependency>

重啟後,訪問地址:http://localhost:8080/json/user
This XML file does not appear to have any style information associated with it. The document tree is shown below.
<User>
<name>xiaoming</name>
<age>10</age>
</User>

因為這個時候系統訪問時,會先精確匹配是否有xml,如果有,優先匹配返回xml格式,其次才會考慮json格式
可以在請求頭中找到Accept:
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8

我們也可以指定某個Controller的某個方法的返回格式是某種格式,如Json格式等,通過produces=MediaType.xxx來指定
@RestController
public class JsonDemoController {
    @GetMapping(value="/json/user",produces= MediaType.APPLICATION_JSON_VALUE)
    public User JsonMethod(){
        User user = new User();
        user.setName("xiaoming");
        user.setAge(10);
        return user;
    }
}

此時再次訪問同樣的地址:返回結果
{"name":"xiaoming","age":10}



例子10:建立一個XMLDemoController

@RestController
public class XMLDemoController {
    @GetMapping(value="/xml/user",produces= MediaType.APPLICATION_XML_VALUE)
    public User JsonMethod(){
        User user = new User();
        user.setName("xiaoming XML");
        user.setAge(30);
        return user;
    }
}
啟動過程中,可以看列印控制檯中的資訊,可以看到對映資訊
Mapped "{[/json/user],methods=[GET],produces=[application/json]}"
Mapped "{[/xml/user],methods=[GET],produces=[application/xml]}"



11)我們可以手動指定消費者的處理型別,如我們上面看到的Accept中支援處理xml格式等型別,我們也可以要求消費者的處理型別
例子11:
@RestController
public class XMLDemoController {
    @GetMapping(value="/xml/user",
            produces = MediaType.APPLICATION_XML_VALUE,
            consumes = MediaType.APPLICATION_JSON_VALUE
    )
    public User JsonMethod(){
        User user = new User();
        user.setName("xiaoming XML");
        user.setAge(30);
        return user;
    }
}

此時瀏覽器訪問地址localhost:8080/xml/user,提示不支援型別,因為Accept中沒有支援Json型別的處理
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.

Mon Feb 05 11:25:21 CST 2018
There was an unexpected error (type=Unsupported Media Type, status=415).
Content type 'null' not supported


那麼我們如果需要讓瀏覽器處理,我們需要在程式中配置consumes中,加上瀏覽器支援的型別
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8

修改後的程式碼:
@RestController
public class XMLDemoController {
    @GetMapping(value="/xml/user",
            produces = MediaType.APPLICATION_XML_VALUE,
            consumes = {MediaType.APPLICATION_JSON_VALUE,MediaType.APPLICATION_XML_VALUE,MediaType.TEXT_HTML_VALUE,"application/xhtml+xml"}
    )
    public User JsonMethod(){
        User user = new User();
        user.setName("xiaoming XML");
        user.setAge(30);
        return user;
    }
}





2.HATEOAS
我們少了一個發現服務的入口!!!外面的人不知道我們釋出了服務!!!
HATEOAS提供了服務發現的功能

找到spring.io中的Spring HATEOAS,找到引包的配置
<dependencies>
    <dependency>
        <groupId>org.springframework.hateoas</groupId>
        <artifactId>spring-hateoas</artifactId>
        <version>0.24.0.RELEASE</version>
    </dependency>
</dependencies>

引入成功後,就可以使用HATEOAS了。

參考:https://docs.spring.io/spring-hateoas/docs/0.24.0.RELEASE/reference/html/#fundamentals.links


按Alt+Enter,選擇Import static method,注入mvc相關的依賴

package com.segmentfault.springbootlesson3.controller;

import com.segmentfault.springbootlesson3.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.awt.*;

import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;

@RestController
public class JsonDemoController {

    @Bean
    public User currentUser(){
        User user = new User();
        user.setName("Json");
        user.setAge(20);
        return user;
    }

    @Autowired
    @Qualifier(value="currentUser") //等於currentUser
    public User user;

    @GetMapping(path="/json/user",produces= MediaType.APPLICATION_JSON_VALUE)
    public User user(){
        return user;
    }

    //setName
    @GetMapping(path="/json/user/set/name",
                produces = MediaType.APPLICATION_JSON_VALUE
    )
    public User setUserName(@RequestParam String name){
        user.setName(name);
        //暴露服務介面
        user.add(linkTo(methodOn(JsonDemoController.class).setUserName(name)).withSelfRel());
        return user;
    }

    //setAge
    @GetMapping(path="/json/user/set/age",
            produces = MediaType.APPLICATION_JSON_VALUE
    )
    public User setUserAge(@RequestParam Integer age){
        user.setAge(age);
        user.add(linkTo(methodOn(JsonDemoController.class).setUserAge(age)).withSelfRel());
        return user;
    }
}


此時訪問:http://localhost:8080/json/user
返回:{"name":"Json","age":20,"links":[]}

訪問:http://localhost:8080/json/user/set/name?name=Hello123
{"name":"Hello123","age":20,"links":[{"rel":"self","href":"http://localhost:8080/json/user/set/name?name=Hello123","hreflang":null,"media":null,"title":null,"type":null,"deprecation":null}]}

此時再次訪問:http://localhost:8080/json/user ,此時的user就有狀態了
{"name":"Hello123","age":20,"links":[{"rel":"self","href":"http://localhost:8080/json/user/set/name?name=Hello123","hreflang":null,"media":null,"title":null,"type":null,"deprecation":null}]}

此時訪問setAge:http://localhost:8080/json/user/set/age?age=120
{"name":"Hello123","age":120,"links":[{"rel":"self","href":"http://localhost:8080/json/user/set/name?name=Hello123","hreflang":null,"media":null,"title":null,"type":null,"deprecation":null},{"rel":"self","href":"http://localhost:8080/json/user/set/age?age=120","hreflang":null,"media":null,"title":null,"type":null,"deprecation":null}]}

可以看到user上有兩個url了


上面的註冊服務介面可以開始的時候就放到User獲取的時候進行注入



修改:
    @GetMapping(path="/json/user",produces= MediaType.APPLICATION_JSON_VALUE)
    public User user(){
        //暴露服務介面
        user.add(linkTo(methodOn(JsonDemoController.class).setUserName(user.getName())).withSelfRel());
        user.add(linkTo(methodOn(JsonDemoController.class).setUserAge(user.getAge())).withSelfRel());
        return user;
    }
去除方法中的暴露服務介面的方法

這樣,我們在啟動的時候,就可以獲取到服務的入口。告訴了我們連結的相關操作
重啟專案,輸入:http://localhost:8080/json/user
{"name":"Json","age":20,"links":[{"rel":"self","href":"http://localhost:8080/json/user/set/name?name=Json","hreflang":null,"media":null,"title":null,"type":null,"deprecation":null},{"rel":"self","href":"http://localhost:8080/json/user/set/age?age=20","hreflang":null,"media":null,"title":null,"type":null,"deprecation":null}]}







3.REST文件生成
1)spring提供了Spring REST Docs,可以生成rest介面文件

2)swagger,動態生成!

3)spring boot/mappings endpoint, spring boot釋出後提供的功能

檢視的時候,需要配置,因為高版本的springboot對許可權的認證比較嚴格
在EndpointProperties中可以看到:
public class EndpointProperties {
    private static final String ENDPOINTS_ENABLED_PROPERTY = "endpoints.enabled";
    private static final String ENDPOINTS_SENSITIVE_PROPERTY = "endpoints.sensitive";
    。。。

在application.properties中配置
endpoints.enabled=true
endpoints.sensitive = false


重啟專案後,輸入:http://localhost:8080/mappings
可以看到專案中所有的地址對映
{[/xml/user],methods=[GET],consumes=[text/html || application/xhtml+xml || application/xml],produces=[application/xml]}    
bean    "requestMappingHandlerMapping"
method
{[/json/user/set/name],methods=[GET],produces=[application/json]}    
bean    "requestMappingHandlerMapping"
method

可以看到,基本的資訊都已經提供了,只是引數的名稱和型別沒有提供


4.REST客戶端實踐
1)Web瀏覽器


2)Apache HttpClient

在user中註冊xmlDemoController中的返回xml格式的方法:
@RestController
public class XMLDemoController {
    @GetMapping(value="/xml/user",
            produces = MediaType.APPLICATION_XML_VALUE
    )
    public User JsonMethod(){
        User user = new User();
        user.setName("xiaoming XML");
        user.setAge(30);
        return user;
    }
}


    @GetMapping(path="/json/user",produces= MediaType.APPLICATION_JSON_VALUE)
    public User user(){
        //暴露服務介面
        user.add(linkTo(methodOn(JsonDemoController.class).setUserName(user.getName())).withSelfRel());
        user.add(linkTo(methodOn(JsonDemoController.class).setUserAge(user.getAge())).withSelfRel());
        user.add(linkTo(methodOn(XMLDemoController.class).JsonMethod()).withSelfRel());
        return user;
    }

重啟程式:http://localhost:8080/xml/user
<User><name>xiaoming XML</name><age>30</age><links/></User>


3)Spring RestTemplate



我們再來看一下RestTemplate這個模板類,
查詢HttpComponentsClientHttpRequestFactory類,
其中一個建構函式:org.apache.http.impl.client.AbstractHttpClient的引數類
我們在search.maven.org中查詢這個類的原始碼,引用對應的maven配置

在pom.xml中加入引用:

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
</dependency>

我們可整合Apache Client和HttpClient,適配底層的方法HttpClient

修改RestClient中的方法:
package com.segmentfault.springbootlesson3.client;

import com.segmentfault.springbootlesson3.pojo.User;

import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;


public class RestClient {
    public static void main(String[] args) {
        HttpClientBuilder builder = HttpClientBuilder.create();
        HttpClient httpClient  = builder.build();
        HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
        RestTemplate restTemplate = new RestTemplate(factory);
//        String result = restTemplate.getForObject("http://localhost:8080/json/user",String.class);
        //執行反序列化
        User user = restTemplate.getForObject("http://localhost:8080/json/user",User.class);
        System.out.println(user);

    }
}



此時再次啟動springboot,然後使用RestTemplate訪問底層介面
看控制檯列印:可以看到和之前的不同,列印出了底層的呼叫方式
"C:\Program Files (x86)\Java\jdk1.8.0_91\bin\java" -javaagent:E:\ideaIU\lib\idea_rt.jar=25583:E:\ideaIU\bin -Dfile.encoding=UTF-8 -classpath "C:\Program Files (x86)\Java\jdk1.8.0_91\jre\lib\charsets.jar;C:\Program Files (x86)\Java\jdk1.8.0_91\jre\lib\deploy.jar;C:\Program Files (x86)\Java\jdk1.8.0_91\jre\lib\ext\access-bridge-32.jar;C:\Program Files (x86)\Java\jdk1.8.0_91\jre\lib\ext\cldrdata.jar;C:\Program Files (x86)\Java\jdk1.8.0_91\jre\lib\ext\dnsns.jar;C:\Program Files (x86)\Java\jdk1.8.0_91\jre\lib\ext\jaccess.jar;C:\Program Files (x86)\Java\jdk1.8.0_91\jre\lib\ext\jfxrt.jar;C:\Program Files (x86)\Java\jdk1.8.0_91\jre\lib\ext\localedata.jar;C:\Program Files (x86)\Java\jdk1.8.0_91\jre\lib\ext\nashorn.jar;C:\Program Files (x86)\Java\jdk1.8.0_91\jre\lib\ext\sunec.jar;C:\Program Files (x86)\Java\jdk1.8.0_91\jre\lib\ext\sunjce_provider.jar;C:\Program Files (x86)\Java\jdk1.8.0_91\jre\lib\ext\sunmscapi.jar;C:\Program Files (x86)\Java\jdk1.8.0_91\jre\lib\ext\sunpkcs11.jar;C:\Program Files (x86)\Java\jdk1.8.0_91\jre\lib\ext\zipfs.jar;C:\Program Files (x86)\Java\jdk1.8.0_91\jre\lib\javaws.jar;C:\Program Files (x86)\Java\jdk1.8.0_91\jre\lib\jce.jar;C:\Program Files (x86)\Java\jdk1.8.0_91\jre\lib\jfr.jar;C:\Program Files (x86)\Java\jdk1.8.0_91\jre\lib\jfxswt.jar;C:\Program Files (x86)\Java\jdk1.8.0_91\jre\lib\jsse.jar;C:\Program Files (x86)\Java\jdk1.8.0_91\jre\lib\management-agent.jar;C:\Program Files (x86)\Java\jdk1.8.0_91\jre\lib\plugin.jar;C:\Program Files (x86)\Java\jdk1.8.0_91\jre\lib\resources.jar;C:\Program Files (x86)\Java\jdk1.8.0_91\jre\lib\rt.jar;E:\springboot\spring-boot-lesson-3\target\classes;E:\maven\localwarehouse\org\springframework\boot\spring-boot-starter-actuator\1.5.10.RELEASE\spring-boot-starter-actuator-1.5.10.RELEASE.jar;E:\maven\localwarehouse\org\springframework\boot\spring-boot-starter\1.5.10.RELEASE\spring-boot-starter-1.5.10.RELEASE.jar;E:\maven\localwarehouse\org\springframework\boot\spring-boot\1.5.10.RELEASE\spring-boot-1.5.10.RELEASE.jar;E:\maven\localwarehouse\org\springframework\boot\spring-boot-autoconfigure\1.5.10.RELEASE\spring-boot-autoconfigure-1.5.10.RELEASE.jar;E:\maven\localwarehouse\org\springframework\boot\spring-boot-starter-logging\1.5.10.RELEASE\spring-boot-starter-logging-1.5.10.RELEASE.jar;E:\maven\localwarehouse\ch\qos\logback\logback-classic\1.1.11\logback-classic-1.1.11.jar;E:\maven\localwarehouse\ch\qos\logback\logback-core\1.1.11\logback-core-1.1.11.jar;E:\maven\localwarehouse\org\slf4j\jcl-over-slf4j\1.7.25\jcl-over-slf4j-1.7.25.jar;E:\maven\localwarehouse\org\slf4j\jul-to-slf4j\1.7.25\jul-to-slf4j-1.7.25.jar;E:\maven\localwarehouse\org\slf4j\log4j-over-slf4j\1.7.25\log4j-over-slf4j-1.7.25.jar;E:\maven\localwarehouse\org\yaml\snakeyaml\1.17\snakeyaml-1.17.jar;E:\maven\localwarehouse\org\springframework\boot\spring-boot-actuator\1.5.10.RELEASE\spring-boot-actuator-1.5.10.RELEASE.jar;E:\maven\localwarehouse\org\springframework\boot\spring-boot-starter-web\1.5.10.RELEASE\spring-boot-starter-web-1.5.10.RELEASE.jar;E:\maven\localwarehouse\org\springframework\boot\spring-boot-starter-tomcat\1.5.10.RELEASE\spring-boot-starter-tomcat-1.5.10.RELEASE.jar;E:\maven\localwarehouse\org\apache\tomcat\embed\tomcat-embed-core\8.5.27\tomcat-embed-core-8.5.27.jar;E:\maven\localwarehouse\org\apache\tomcat\tomcat-annotations-api\8.5.27\tomcat-annotations-api-8.5.27.jar;E:\maven\localwarehouse\org\apache\tomcat\embed\tomcat-embed-el\8.5.27\tomcat-embed-el-8.5.27.jar;E:\maven\localwarehouse\org\apache\tomcat\embed\tomcat-embed-websocket\8.5.27\tomcat-embed-websocket-8.5.27.jar;E:\maven\localwarehouse\org\hibernate\hibernate-validator\5.3.6.Final\hibernate-validator-5.3.6.Final.jar;E:\maven\localwarehouse\javax\validation\validation-api\1.1.0.Final\validation-api-1.1.0.Final.jar;E:\maven\localwarehouse\org\jboss\logging\jboss-logging\3.3.1.Final\jboss-logging-3.3.1.Final.jar;E:\maven\localwarehouse\com\fasterxml\classmate\1.3.4\classmate-1.3.4.jar;E:\maven\localwarehouse\com\fasterxml\jackson\core\jackson-databind\2.8.10\jackson-databind-2.8.10.jar;E:\maven\localwarehouse\org\springframework\spring-web\4.3.14.RELEASE\spring-web-4.3.14.RELEASE.jar;E:\maven\localwarehouse\org\springframework\spring-webmvc\4.3.14.RELEASE\spring-webmvc-4.3.14.RELEASE.jar;E:\maven\localwarehouse\org\springframework\spring-expression\4.3.14.RELEASE\spring-expression-4.3.14.RELEASE.jar;E:\maven\localwarehouse\org\springframework\spring-core\4.3.14.RELEASE\spring-core-4.3.14.RELEASE.jar;E:\maven\localwarehouse\com\fasterxml\jackson\dataformat\jackson-dataformat-xml\2.8.10\jackson-dataformat-xml-2.8.10.jar;E:\maven\localwarehouse\com\fasterxml\jackson\core\jackson-core\2.8.10\jackson-core-2.8.10.jar;E:\maven\localwarehouse\com\fasterxml\jackson\core\jackson-annotations\2.8.0\jackson-annotations-2.8.0.jar;E:\maven\localwarehouse\com\fasterxml\jackson\module\jackson-module-jaxb-annotations\2.8.10\jackson-module-jaxb-annotations-2.8.10.jar;E:\maven\localwarehouse\org\codehaus\woodstox\stax2-api\3.1.4\stax2-api-3.1.4.jar;E:\maven\localwarehouse\com\fasterxml\woodstox\woodstox-core\5.0.3\woodstox-core-5.0.3.jar;E:\maven\localwarehouse\org\springframework\hateoas\spring-hateoas\0.24.0.RELEASE\spring-hateoas-0.24.0.RELEASE.jar;E:\maven\localwarehouse\org\springframework\spring-aop\4.3.14.RELEASE\spring-aop-4.3.14.RELEASE.jar;E:\maven\localwarehouse\org\springframework\spring-beans\4.3.14.RELEASE\spring-beans-4.3.14.RELEASE.jar;E:\maven\localwarehouse\org\springframework\spring-context\4.3.14.RELEASE\spring-context-4.3.14.RELEASE.jar;E:\maven\localwarehouse\org\slf4j\slf4j-api\1.7.25\slf4j-api-1.7.25.jar;E:\maven\localwarehouse\org\apache\httpcomponents\httpclient\4.5.5\httpclient-4.5.5.jar;E:\maven\localwarehouse\org\apache\httpcomponents\httpcore\4.4.9\httpcore-4.4.9.jar;E:\maven\localwarehouse\commons-codec\commons-codec\1.10\commons-codec-1.10.jar" com.segmentfault.springbootlesson3.client.RestClient
15:45:58.371 [main] DEBUG org.springframework.web.client.RestTemplate - Created GET request for "http://localhost:8080/json/user"
15:45:58.540 [main] DEBUG org.springframework.web.client.RestTemplate - Setting request Accept header to [application/xml, text/xml, application/json, application/*+xml, application/*+json]
15:45:58.564 [main] DEBUG org.apache.http.client.protocol.RequestAddCookies - CookieSpec selected: default
15:45:58.593 [main] DEBUG org.apache.http.client.protocol.RequestAuthCache - Auth cache not set in the context
15:45:58.597 [main] DEBUG org.apache.http.impl.conn.PoolingHttpClientConnectionManager - Connection request: [route: {}->http://localhost:8080][total kept alive: 0; route allocated: 0 of 2; total allocated: 0 of 20]
15:45:58.638 [main] DEBUG org.apache.http.impl.conn.PoolingHttpClientConnectionManager - Connection leased: [id: 0][route: {}->http://localhost:8080][total kept alive: 0; route allocated: 1 of 2; total allocated: 1 of 20]
15:45:58.643 [main] DEBUG org.apache.http.impl.execchain.MainClientExec - Opening connection {}->http://localhost:8080
15:45:58.654 [main] DEBUG org.apache.http.impl.conn.DefaultHttpClientConnectionOperator - Connecting to localhost/127.0.0.1:8080
15:45:58.662 [main] DEBUG org.apache.http.impl.conn.DefaultHttpClientConnectionOperator - Connection established 127.0.0.1:25590<->127.0.0.1:8080
15:45:58.663 [main] DEBUG org.apache.http.impl.execchain.MainClientExec - Executing request GET /json/user HTTP/1.1
15:45:58.663 [main] DEBUG org.apache.http.impl.execchain.MainClientExec - Target auth state: UNCHALLENGED
15:45:58.665 [main] DEBUG org.apache.http.impl.execchain.MainClientExec - Proxy auth state: UNCHALLENGED
15:45:58.676 [main] DEBUG org.apache.http.headers - http-outgoing-0 >> GET /json/user HTTP/1.1
15:45:58.676 [main] DEBUG org.apache.http.headers - http-outgoing-0 >> Accept: application/xml, text/xml, application/json, application/*+xml, application/*+json
15:45:58.676 [main] DEBUG org.apache.http.headers - http-outgoing-0 >> Host: localhost:8080
15:45:58.676 [main] DEBUG org.apache.http.headers - http-outgoing-0 >> Connection: Keep-Alive
15:45:58.676 [main] DEBUG org.apache.http.headers - http-outgoing-0 >> User-Agent: Apache-HttpClient/4.5.5 (Java/1.8.0_91)
15:45:58.676 [main] DEBUG org.apache.http.headers - http-outgoing-0 >> Accept-Encoding: gzip,deflate
15:45:58.676 [main] DEBUG org.apache.http.wire - http-outgoing-0 >> "GET /json/user HTTP/1.1[\r][\n]"
15:45:58.676 [main] DEBUG org.apache.http.wire - http-outgoing-0 >> "Accept: application/xml, text/xml, application/json, application/*+xml, application/*+json[\r][\n]"
15:45:58.676 [main] DEBUG org.apache.http.wire - http-outgoing-0 >> "Host: localhost:8080[\r][\n]"
15:45:58.676 [main] DEBUG org.apache.http.wire - http-outgoing-0 >> "Connection: Keep-Alive[\r][\n]"
15:45:58.676 [main] DEBUG org.apache.http.wire - http-outgoing-0 >> "User-Agent: Apache-HttpClient/4.5.5 (Java/1.8.0_91)[\r][\n]"
15:45:58.676 [main] DEBUG org.apache.http.wire - http-outgoing-0 >> "Accept-Encoding: gzip,deflate[\r][\n]"
15:45:58.676 [main] DEBUG org.apache.http.wire - http-outgoing-0 >> "[\r][\n]"
15:45:59.066 [main] DEBUG org.apache.http.wire - http-outgoing-0 << "HTTP/1.1 200 [\r][\n]"
15:45:59.066 [main] DEBUG org.apache.http.wire - http-outgoing-0 << "X-Application-Context: application[\r][\n]"
15:45:59.066 [main] DEBUG org.apache.http.wire - http-outgoing-0 << "Content-Type: application/json;charset=UTF-8[\r][\n]"
15:45:59.066 [main] DEBUG org.apache.http.wire - http-outgoing-0 << "Transfer-Encoding: chunked[\r][\n]"
15:45:59.066 [main] DEBUG org.apache.http.wire - http-outgoing-0 << "Date: Mon, 05 Feb 2018 07:45:59 GMT[\r][\n]"
15:45:59.066 [main] DEBUG org.apache.http.wire - http-outgoing-0 << "[\r][\n]"
15:45:59.066 [main] DEBUG org.apache.http.wire - http-outgoing-0 << "1c6[\r][\n]"
15:45:59.066 [main] DEBUG org.apache.http.wire - http-outgoing-0 << "{"name":"Json","age":20,"links":[{"rel":"self","href":"http://localhost:8080/json/user/set/name?name=Json","hreflang":null,"media":null,"title":null,"type":null,"deprecation":null},{"rel":"self","href":"http://localhost:8080/json/user/set/age?age=20","hreflang":null,"media":null,"title":null,"type":null,"deprecation":null},{"rel":"self","href":"http://localhost:8080/xml/user","hreflang":null,"media":null,"title":null,"type":null,"deprecation":null}]}[\r][\n]"
15:45:59.072 [main] DEBUG org.apache.http.headers - http-outgoing-0 << HTTP/1.1 200
15:45:59.072 [main] DEBUG org.apache.http.headers - http-outgoing-0 << X-Application-Context: application
15:45:59.072 [main] DEBUG org.apache.http.headers - http-outgoing-0 << Content-Type: application/json;charset=UTF-8
15:45:59.072 [main] DEBUG org.apache.http.headers - http-outgoing-0 << Transfer-Encoding: chunked
15:45:59.072 [main] DEBUG org.apache.http.headers - http-outgoing-0 << Date: Mon, 05 Feb 2018 07:45:59 GMT
15:45:59.086 [main] DEBUG org.apache.http.impl.execchain.MainClientExec - Connection can be kept alive indefinitely
15:45:59.094 [main] DEBUG org.springframework.web.client.RestTemplate - GET request for "http://localhost:8080/json/user" resulted in 200 ()
15:45:59.096 [main] DEBUG org.springframework.web.client.RestTemplate - Reading [class com.segmentfault.springbootlesson3.pojo.User] as "application/json;charset=UTF-8" using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@15c2241]
15:45:59.121 [main] DEBUG org.apache.http.wire - http-outgoing-0 << "0[\r][\n]"
15:45:59.121 [main] DEBUG org.apache.http.wire - http-outgoing-0 << "[\r][\n]"
15:45:59.121 [main] DEBUG org.apache.http.impl.conn.PoolingHttpClientConnectionManager - Connection [id: 0][route: {}->http://localhost:8080] can be kept alive indefinitely
15:45:59.122 [main] DEBUG org.apache.http.impl.conn.DefaultManagedHttpClientConnection - http-outgoing-0: set socket timeout to 0
15:45:59.122 [main] DEBUG org.apache.http.impl.conn.PoolingHttpClientConnectionManager - Connection released: [id: 0][route: {}->http://localhost:8080][total kept alive: 1; route allocated: 1 of 2; total allocated: 1 of 20]
links: [<http://localhost:8080/json/user/set/name?name=Json>;rel="self", <http://localhost:8080/json/user/set/age?age=20>;rel="self", <http://localhost:8080/xml/user>;rel="self"]

Process finished with exit code 0


這個在後面的Spring Cloud的負載均衡中會用到,修改底層的訪問方式。



5.Q&A
rpc框架難點:負載均衡,註冊服務發現服務,效能問題






















相關文章