微服務專題03-REST

前言

前面的章節我們講了Spring Web MVC 。本節,繼續微服務專題的內容分享,共計16小節,分別是:

  • 微服務專題01-Spring Application
  • 微服務專題02-Spring Web MVC 視圖技術
  • 微服務專題03-REST
  • 微服務專題04-Spring WebFlux 原理
  • 微服務專題05-Spring WebFlux 運用
  • 微服務專題06-雲原生應用(Cloud Native Applications)
  • 微服務專題07-Spring Cloud 配置管理
  • 微服務專題08-Spring Cloud 服務發現
  • 微服務專題09-Spring Cloud 負載均衡
  • 微服務專題10-Spring Cloud 服務熔斷
  • 微服務專題11-Spring Cloud 服務調用
  • 微服務專題12-Spring Cloud Gateway
  • 微服務專題13-Spring Cloud Stream (上)
  • 微服務專題14-Spring Cloud Bus
  • 微服務專題15-Spring Cloud Stream 實現
  • 微服務專題16-Spring Cloud 整體回顧

本節內容重點爲:

  • REST 理論基礎:基本概念、架構屬性、架構約束、使用場景、實現框架(服務端、客戶端)
  • REST 服務端實踐:Spring Boot REST 應用、HATEOAS 應用、文檔生成等
  • REST 客戶端實踐:傳統瀏覽器、Apache HttpClient 、Spring RestTemplate 等相關實踐

REST理論基礎

前面介紹過Spring的MVC結合view顯示數據。那麼這些數據除了在WebBrowser中用JavaScript來調用以外,還可以用遠程服務器的Java程序、C#程序來調用。也就是說現在的程序不僅在BS中能調用,在CS中同樣也能調用,不過你需要藉助RestTemplate這個類來完成。RestTemplate有點類似於一個WebService客戶端請求的模版,可以調用http請求的WebService,並將結果轉換成相應的對象類型。

RPC ( Remote Procedure Call )

  • 語言相關
    • Java - RMI(Remote Method Invocation)
    • .NET - COM+、
  • 語言無關(重點)
    • SOA
      • Web Services
        • SOAP(傳輸介質協議)
        • HTTP、SMTP(通訊協議)
    • 微服務(MSA)
      • REST
        • HTML、JSON、XML 等等
        • HTTP(通訊協議)
          • HTTP 1.1
            • 短連接
            • Keep-Alive
            • 連接池
            • Long Polling
          • HTTP/2
            • 長連接
        • 技術
          • Spring 客戶端 : RestTemplate
          • Spring WebMVC : @RestController = @Controller + @ResponseBody + @RequestBody
          • Spring Cloud : RestTemplate 擴展 + @LoadBalanced

REST(英文)

Cacheability(可緩存性)

@ResponseBody -> 響應體(Response Body)

  • 響應(Response)

    • 響應頭(Headers)

      • 請求方法

        • HEAD
      • 元信息(Meta-Data)

        • Accept-Language -> Locale
        • Connection -> Keep-Alive
      • 實現

        多值 Map MultiValueMap

        Key : Value = 1 : N

        Name : Value = 1 : N

        參考代碼:

        public class HttpHeaders implements MultiValueMap<String, String>, Serializable {
            ...
        }
        
    • 響應體

      • 業務信息(Business Data)
      • Body:HTTP 實體、REST
        • @ResponseBody

        • HttpEntity.body 屬性(泛型結構)

          參考代碼:

    public class HttpEntity<T> {
    	...
    	private final HttpHeaders headers;
    
    	@Nullable
    	private final T body;
    }
    
    • Payload : 消息 JMS、事件、SOA

HTTP 狀態碼

可以參考源碼:(org.springframework.http.HttpStatus

在這裏插入圖片描述
關於http狀態碼,也是很多初級面試常考的點,日常開發中,常見的有:

  • 200

    • org.springframework.http.HttpStatus#OK
  • 304

    • org.springframework.http.HttpStatus#NOT_MODIFIED
  • 400

    • org.springframework.http.HttpStatus#BAD_REQUEST
  • 404

    • org.springframework.http.HttpStatus#NOT_FOUND
  • 500

    • org.springframework.http.HttpStatus#INTERNAL_SERVER_ERROR

緩存驗證Demo
啓動類:

@EnableAutoConfiguration
@ComponentScan(basePackages = "com.test.micro.services.mvc.controller")
public class MvcRestApplication {

    public static void main(String[] args) {
        new SpringApplicationBuilder(MvcRestApplication.class)
                .run(args);
    }

}

CachedRestController

@Controller
public class CachedRestController {

    @RequestMapping
    @ResponseBody // 沒有緩存 -> 304
    // 服務端和客戶端沒有形成默契(狀態碼)
    // HTTP 協議,REST 繼承
    public String helloWorld() { // 200 / 500  / 400
        return "Hello,World"; // Body = "Hello,World" String
    }

    @RequestMapping("/cache") //  Spring MVC 返回值處理
    @OptionsMapping(name="")
    public ResponseEntity<String> cachedHelloWorld(
            @RequestParam(required = false, defaultValue = "false") boolean cached) {
        if (cached) {
            return new ResponseEntity(HttpStatus.NOT_MODIFIED);
        } else {
            return ResponseEntity.ok("Hello,World");
        }
    }

}

代碼測試:

首次訪問地址:
http://localhost:8080/cache
在這裏插入圖片描述
不加入緩存,http響應碼爲200

在url控制緩存,第二次請求的地址http://localhost:8080/cache?chched=ture

在這裏插入圖片描述
加入緩存後,http響應碼爲304

實驗分析:

  • 第一次完整請求,獲取響應頭(200),直接獲取
  • 第二次請求,只讀取頭信息,響應頭(304),客戶端(流量器)取上次 Body 結果

Uniform interface(統一接口)

資源定位 - URI

URI與URL的區別:

山東和河南都有一個張三,張三就是URI,具體的河南的張三或者山東的張三就是URL

URI 與 URL字段定義:

U : Uniform
R : Resource
I:鑑別
L : 定位

URI
舉例:
URI = scheme:[//authority]path[?query][#fragment]
scheme 通常的實現途徑爲: HTTP、wechat

URL

scheme在URL中一般指的是protocol 協議
在REST中一般常用URL

資源操作 - HTTP 動詞

GET

  • @GetMapping

    • 註解屬性別名和覆蓋
      https://github.com/spring-projects/spring-framework/wiki/Spring-Annotation-Programming-Model#attribute-aliases-and-overrides

      • Spring Framework 4.2 引入
        • Spring Boot 1.3 纔可以使用
      • Spring Boot 加以發展
      @RequestMapping(method = RequestMethod.POST) // 註解“派生性”
      public @interface PostMapping {
          ...
          @AliasFor(annotation = RequestMapping.class) // 註解別名
      	String name() default "";
          ...
      }
      

      @PostMapping 是註解,@RequestMapping@PostMapping 的註解:

      • @RequestMapping@PostMapping 的元註解
      • @RequestMapping 元標註了 @PostMapping

      @AliasFor 只能標註在目標註解的屬性,所annotation()的註解必須是元註解,該註解 attribute() 必須元註解的屬性

舉例說明:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RequestMapping(method = RequestMethod.OPTIONS)  // 如果不增加元註解的話,會報錯
public @interface OptionsMapping {
    //需要重新定義屬性

    @AliasFor(annotation = RequestMapping.class) // 指定之後,RequestMethod 的屬性
    String name() default ""; // 不加的話,只是代表自己

}

然後再看看SpringBootApplication註解:
在這裏插入圖片描述
這也是爲什麼我們可以在啓動springboot項目使用@EnableAutoConfiguration的原因:
在這裏插入圖片描述
值得注意的是@EnableAutoConfiguration來自於SpringFramework,而@@SpringBootApplication則來自於springboot的jar包,說白了,就是springboot對於spring進一步的封裝,所以爲什麼說學好springboot要學好spring,原因就在於此!
在這裏插入圖片描述
在這裏插入圖片描述

PUT

  • @PutMapping

POST

  • @PostMapping

PATCH

  • @PatchMapping

  • 限制

    • Servlet API 沒有規定 PATCH

    • Spring Web 對其做了擴展

      public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
      
          ...
      protected void service(HttpServletRequest request, HttpServletResponse response)
      			throws ServletException, IOException {
      
      		HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
      		if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
      			processRequest(request, response);
      		}
      		else {
      			super.service(request, response);
      		}
      	}
          ...
      }
      

DELETE

  • @DeleteMapping

自定義註解

基於以上的知識點,我們來模仿springboot自定義一個註解:
比如既實現注入bean也同時實現事務的註解:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Service // 它是 @Service 組件
@Transactional // 它是事務註解
public @interface TransactionalService { //  @Service + @Transactional

    @AliasFor(annotation = Service.class)
    String value(); // 服務名稱

    @AliasFor(annotation = Transactional.class,attribute = "value")
    String txName();
}

在service層調用:

@TransactionalService(value = "echoService-2020", txName = "myTxName") // @Service Bean + @Transactional
// 定義它的 Bean 名稱
public class EchoService {

    public void echo(String message) {
        System.out.println(message);
    }
}

測試類:

@ComponentScan(basePackages = "com.test.micro.services.mvc.service")
@EnableTransactionManagement
public class SpringApplication {

    @Component("myTxName")
    public static class MyPlatformTransactionManager implements PlatformTransactionManager {

        @Override
        public TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {
            return new DefaultTransactionStatus(
                    null, true, true,
                    definition.isReadOnly(), true, null
            );
        }

        @Override
        public void commit(TransactionStatus status) throws TransactionException {
            System.out.println("Commit()....");
        }

        @Override
        public void rollback(TransactionStatus status) throws TransactionException {
            System.out.println("rollback()....");
        }
    }

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        // 註冊 SpringApplication 掃描  com.test.micro.services.mvc.service
        context.register(SpringApplication.class);

        context.refresh(); // 啓動

        context.getBeansOfType(EchoService.class).forEach((beanName, bean) -> {
            System.err.println("Bean Name : " + beanName + " , Bean : " + bean);

            bean.echo("Hello,World");
        });

        context.close(); // 關閉
    }
}

測試結果:
在這裏插入圖片描述
說明此註解生效,注入了service也實現了事務!

自描述消息

註解驅動

  • @RequestBody

    JSON -> MappingJackson2HttpMessageConverter

    TEXT -> StringHttpMessageConverter

  • @ResponseBody

    JSON -> MappingJackson2HttpMessageConverter

    TEXT -> StringHttpMessageConverter

返回值處理類:RequestResponseBodyMethodProcessor

接口編程

ResponseEntity extends HttpEntity

RequestEntity extends HttpEntity

返回值處理類:HttpEntityMethodProcessor

媒體類型(MediaType

  • org.springframework.http.MediaType#APPLICATION_JSON_UTF8_VALUE
    • “application/json;charset=UTF-8”

HTTP 消息轉換器(HttpMessageConverter

  • application/json
    • MappingJackson2HttpMessageConverter
  • text/html
    • StringHttpMessageConverter

代碼導讀

@EnableWebMvc

  • 導入 DelegatingWebMvcConfiguration(配置 Class)
    • 註冊 WebMvcConfigurer
      • 裝配各種 Spring MVC 需要的Bean
      • 註解驅動擴展點
        • HandlerMethodArgumentResolver
        • HandlerMethodReturnValueHandler
        • @RequestBody@ResponseBody 實現類
          • RequestResponseBodyMethodProcessor
          • HttpEntityMethodProcessor

實現 WebMvcConfigurer

WebMvcConfigurerAdapter 實現

後記

Q: 前後端分離和服務端渲染的區別

A: 服務端渲染,數據計算 + HTML 渲染均有服務端完成。

前後端:數據計算有服務端提供,JSON Web 端點(REST Web 接口),HTML 主要前端 JS 完成,以React、Vue.js(前端模板引擎)

下節預告

  • Reactive 原理
  • WebFlux 使用場景
  • WebFlux 整體架構

本節示例代碼:https://github.com/harrypottry/microservices-project/spring-mvc-rest

更多架構知識,歡迎關注本套Java系列文章Java架構師成長之路

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章