小言_互联网的博客

微服务专题03-REST

533人阅读  评论(0)

前言

前面的章节我们讲了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架构师成长之路


转载:https://blog.csdn.net/qq_34361283/article/details/106296164
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场