Spring Boot

Spring Boot 2.0深度实践之核心技术篇(二)

2018-09-23  本文已影响84人  liuhuiAndroid

第6章 Web MVC REST 应用

6-1 Web MVC REST应用和REST介绍

6-2 Web MVC REST 支持

注解驱动

6-3 REST 内容协商

核心组件

图片.png

6-4 Web MVC REST 处理流程

图片.png

6-5 Web MVC REST 处理流程源码分析

@SpringBootApplication(scanBasePackages = "com.lh.diveinspringboot")
public class SpringBootRestBootstrap {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootRestBootstrap.class, args);
    }
    
}

@RestController
public class HelloWorldRestController {

    @GetMapping(value = "/hello-world")
    public String helloWorld(@RequestParam(required = false) String message) {
        return "Hello,World! : " + message;
    }

    @GetMapping("/hello")
    public String hello() {
        return "hello";
    }

}

结合处理流程图分析源码

6-6 Web MVC REST 内容协商处理流程

图片.png

6-7 Web MVC REST 内容协商处理流程源码分析

@RestController
public class UserRestController {

    @PostMapping(value = "/echo/user")
    public User user(@RequestBody User user) {
        return user;
    }

}

6-8 理解媒体类型

理解请求的媒体类型
经过 ContentNegotiationManager 的 ContentNegotiationStrategy 解析请求中的媒体类型,比如: Accept 请求头

理解可生成的媒体类型
返回 @Controller HandlerMethod @RequestMapping.produces() 属性所指定的 MediaType 列表:

理解 @RequestMapping#consumes
用于 @Controller HandlerMethod 匹配:

理解 @RequestMapping#produces
用于获取可生成的 MediaType 列表

6-9 理解媒体类型源码分析

@RestController
public class UserRestController {

    @PostMapping(value = "/echo/user",
            produces = "application/json;charset=UTF-8")
    public User user(@RequestBody User user) {
        return user;
    }

}
图片.png
@RestController
public class UserRestController {

    @PostMapping(value = "/echo/user",
            produces = "application/json;charset=GBK",
            consumes = "application/json;charset=UTF-8")
    public User user(@RequestBody User user) {
        return user;
    }

}

6-10 扩展 REST 内容协商-反序列化部分

自定义 HttpMessageConverter

需求:
实现 Content-Type 为 text/properties 媒体类型的 HttpMessageConverter

实现步骤:

代码:

public class PropertiesHttpMessageConverter extends AbstractGenericHttpMessageConverter<Properties> {

    public PropertiesHttpMessageConverter() {
        // 设置支持的 MediaType
        super(new MediaType("text", "properties"));
    }

    @Override
    protected void writeInternal(Properties properties, Type type, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
        // Properties -> String
        // OutputStream -> Writer
        HttpHeaders httpHeaders = outputMessage.getHeaders();
        MediaType mediaType = httpHeaders.getContentType();
        // 获取字符编码
        Charset charset = mediaType.getCharset();
        // 当 charset 不存在时,使用 UTF-8
        charset = charset == null ? Charset.forName("UTF-8") : charset;
        // 字节输出流
        OutputStream outputStream = outputMessage.getBody();
        // 字符输出流
        Writer writer = new OutputStreamWriter(outputStream, charset);
        // Properties 写入到字符输出流
        properties.store(writer,"From PropertiesHttpMessageConverter");
    }

    @Override
    protected Properties readInternal(Class<? extends Properties> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {

        // 字符流 -> 字符编码
        // 从 请求头 Content-Type 解析编码
        HttpHeaders httpHeaders = inputMessage.getHeaders();
        MediaType mediaType = httpHeaders.getContentType();
        // 获取字符编码
        Charset charset = mediaType.getCharset();
        // 当 charset 不存在时,使用 UTF-8
        charset = charset == null ? Charset.forName("UTF-8") : charset;

        // 字节流
        InputStream inputStream = inputMessage.getBody();
        InputStreamReader reader = new InputStreamReader(inputStream, charset);
        Properties properties = new Properties();
        // 加载字符流成为 Properties 对象
        properties.load(reader);
        return properties;
    }

    @Override
    public Properties read(Type type, Class<?> contextClass, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
        return readInternal(null, inputMessage);
    }
}

RestWebMvcConfigurer#extendMessageConverters

@Configuration
public class RestWebMvcConfigurer implements WebMvcConfigurer {

    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.set(0, new PropertiesHttpMessageConverter()); // 添加到集合首位
    }
    
}
@Controller
public class PropertiesRestController {

    @PostMapping(
            value = "/add/props",
            consumes = "text/properties;charset=UTF-8"
    )
    public Properties addProperties(Properties properties) {
        return properties;
    }
    
}

6-11 扩展 REST 内容协商-序列化部分

代码:
PropertiesHttpMessageConverter#writeInternal

public class PropertiesHttpMessageConverter extends AbstractGenericHttpMessageConverter<Properties> {

    @Override
    protected void writeInternal(Properties properties, Type type, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
        // Properties -> String
        // OutputStream -> Writer
        HttpHeaders httpHeaders = outputMessage.getHeaders();
        MediaType mediaType = httpHeaders.getContentType();
        // 获取字符编码
        Charset charset = mediaType.getCharset();
        // 当 charset 不存在时,使用 UTF-8
        charset = charset == null ? Charset.forName("UTF-8") : charset;
        // 字节输出流
        OutputStream outputStream = outputMessage.getBody();
        // 字符输出流
        Writer writer = new OutputStreamWriter(outputStream, charset);
        // Properties 写入到字符输出流
        properties.store(writer,"From PropertiesHttpMessageConverter");
    }

}

6-12 自定义 Resolver 实现

自定义 HandlerMethodArgumentResolver

需求:

实现步骤:

代码:
PropertiesHandlerMethodArgumentResolver
RestWebMvcConfigurer#addArgumentResolvers

6-13 自定义 Handler 实现

自定义 HandlerMethodReturnValueHandler

需求:

实现步骤:

代码:
@Controller
PropertiesHandlerMethodReturnValueHandler

6-14 REST 内容协商CORS

Cross-Origin Resource Sharing(CORS)

代码:

127.0.0.1 api.rest.org
@Controller
public class HelloWorldController {

    @RequestMapping("")
    public String index() {
        return "index";
    }

}
@RestController
public class HelloWorldRestController {

    @CrossOrigin("*")
    @GetMapping("/hello")
    public String hello() {
        return "hello";
    }

}
@Configuration
public class RestWebMvcConfigurer implements WebMvcConfigurer {

    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**").allowedOrigins("*");
    }

}

第7章 渐行渐远的 Servlet

7-1 渐行渐远的Servlet

7-2 Servlet 核心 API

图片.png

7-3 Servlet 版本

7-4 Servlet 注册

7-5 理解 Servlet 组件生命周期

理解 Servlet 生命周期

理解 Filter 生命周期

理解 ServletContext 生命周期

DispatcherServlet初始化过程

图片.png
结合源码分析

7-6 Servlet 异步支持

代码:

@ComponentScan(basePackages = "com.lh.diveinspringboot.controller")
public class DefaultAnnotationConfigDispatcherServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    
    @Override
    protected Class<?>[] getRootConfigClasses() { // web.xml
        return new Class[0];
    }

    @Override
    protected Class<?>[] getServletConfigClasses() { // DispatcherServlet
        return new Class[]{
                getClass() // 返回当前类
        };
    }

    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }

}
@RestController
public class HelloWorldAsyncController {

    @GetMapping("/hello-world")
    public DeferredResult<String> helloWorld() {
        DeferredResult<String> result = new DeferredResult<>();
        result.setResult("Hello,World");
        result.onCompletion(() -> System.out.println("执行结束"));
        return result;
    }
}
mvn -Dmaven.test.skip -U clean package // 不执行测试用例,也不编译测试用例类
java -jar target\spring-servlet-0.1-SNAPSHOT-war-exec.jar

7-7 DeferredResult 增加线程信息

@RestController
public class HelloWorldAsyncController {

    @GetMapping("/hello-world")
    public DeferredResult<String> helloWorld() {
        DeferredResult<String> result = new DeferredResult<>();
        result.setResult("Hello,World");
        println("Hello,World");
        result.onCompletion(() -> println("执行结束"));
        return result;
    }

    private static void println(Object object) {
        String threadName = Thread.currentThread().getName();
        System.out.println("HelloWorldAsyncController[" + threadName + "]: " + object);
    }

}

查看日志发现全部是在同一个线程中处理。

7-8 DeferredResult 设置 timeout 以及处理回调

@RestController
public class HelloWorldAsyncController {

    @GetMapping("/hello-world")
    public DeferredResult<String> helloWorld() {
        DeferredResult<String> result = new DeferredResult<>(50L);
        println("Hello,World");
        result.onCompletion(() -> println("执行结束"));
        result.onTimeout(() -> println("执行超时"));
        return result;
    }

    private static void println(Object object) {
        String threadName = Thread.currentThread().getName();
        System.out.println("HelloWorldAsyncController[" + threadName + "]: " + object);
    }

}

查看日志发现回调是在另一个线程中处理。

7-9 DeferredResult 异步执行

@RestController
@EnableScheduling
public class HelloWorldAsyncController {

    private final BlockingQueue<DeferredResult<String>> queue = new ArrayBlockingQueue<>(5);

    // 超时随机数
    private final Random random = new Random();

    // 这个注解在容器启动时便会生效,5秒执行一次任务
    @Scheduled(fixedRate = 5000)
    public void process() throws InterruptedException { // 定时操作
        DeferredResult<String> result = null;
        do {
            result = queue.take();
            // 随机超时时间
            long timeout = random.nextInt(100);
            // 模拟等待时间,RPC 或者 DB 查询
            Thread.sleep(timeout);
            // 计算结果
            result.setResult("Hello,World");
            println("执行计算结果,消耗:" + timeout + " ms.");
        } while (result != null);
    }

    @GetMapping("/hello-world")
    public DeferredResult<String> helloWorld() {
        DeferredResult<String> result = new DeferredResult<>(50L);
        // 将指定元素插入此队列中
        queue.offer(result);
        println("Hello,World");
        result.onCompletion(() -> println("执行结束"));
        result.onTimeout(() -> println("执行超时"));
        return result;
    }

    private static void println(Object object) {
        String threadName = Thread.currentThread().getName();
        System.out.println("HelloWorldAsyncController[" + threadName + "]: " + object);
    }

}

7-10 Callable 异步执行

@RestController
public class HelloWorldAsyncController {

    @GetMapping("/callable-hello-world")
    public Callable<String> callableHelloWorld() {
        final long startTime = System.currentTimeMillis();
        println("Hello,World");
        return () -> {
            long costTime = System.currentTimeMillis() - startTime;
            println("执行计算结果,消耗:" + costTime + " ms.");
            return "Hello,World";
        };
    }

    private static void println(Object object) {
        String threadName = Thread.currentThread().getName();
        System.out.println("HelloWorldAsyncController[" + threadName + "]: " + object);
    }

}

7-11 CompletionStage 异步执行

@RestController
public class HelloWorldAsyncController {

    @GetMapping("/completion-stage")
    public CompletionStage<String> completionStage(){
        final long startTime = System.currentTimeMillis();
        println("Hello,World");
        return CompletableFuture.supplyAsync(()->{
            long costTime = System.currentTimeMillis() - startTime;
            println("执行计算结果,消耗:" + costTime + " ms.");
            return "Hello,World"; // 异步执行结果
        });
    }

    private static void println(Object object) {
        String threadName = Thread.currentThread().getName();
        System.out.println("HelloWorldAsyncController[" + threadName + "]: " + object);
    }

}

7-12 MVC 异步支持原理分析

7-13 异步 Servlet 实现

@WebServlet(
        asyncSupported = true, // 激活异步特性
        name = "asyncServlet", // Servlet 名字
        urlPatterns = "/async-servlet"
)
public class AsyncServlet extends HttpServlet {

    @Override
    public void service(HttpServletRequest request, HttpServletResponse resp) throws IOException {
        // 判断是否支持异步
        if (request.isAsyncSupported()) {
            // 创建 AsyncContext
            AsyncContext asyncContext = request.startAsync();
            // 设置超时时间
            asyncContext.setTimeout(1000L);
            asyncContext.addListener(new AsyncListener() {
                @Override
                public void onComplete(AsyncEvent event) {
                    println("执行完成");
                }

                @Override
                public void onTimeout(AsyncEvent event) {
                    HttpServletResponse servletResponse = (HttpServletResponse)event.getSuppliedResponse();
                    servletResponse.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
                    println("执行超时");
                }

                @Override
                public void onError(AsyncEvent event) {
                    println("执行错误");
                }

                @Override
                public void onStartAsync(AsyncEvent event) {
                    println("开始执行");
                }
            });

            println("Hello,World");
            ServletResponse servletResponse = asyncContext.getResponse();
            // 设置响应媒体类型
            servletResponse.setContentType("text/plain;charset=UTF-8");
            // 获取字符输出流
            PrintWriter writer = servletResponse.getWriter();
            writer.println("Hello,World");
            writer.flush();
        }
    }

    private static void println(Object object) {
        String threadName = Thread.currentThread().getName();
        System.out.println("AsyncServlet[" + threadName + "]: " + object);
    }

}

Java Specification Requests (JSR) : https://github.com/mercyblitz/jsr

7-14 DefferedResult 实现原理

HandlerMethodReturnValueHandler 源码
DeferredResultMethodReturnValueHandler 源码
AsyncContext 源码

7-15 Spring Boot 嵌入式 Servlet 容器限制

代码:
spring-boot-servlet 项目
SpringBootServletBootstrap

7-16 Spring Boot 嵌入式 Servlet 容器限制 原理分析

没听懂

7-17 Spring Boot 应用传统 Servlet 容器部署

7-18 扩展 SpringBootServletInitializer

7-19 构建应用

7-20 渐行渐远的Servlet总结

第8章 从 Reactive 到 WebFlux

8-1 从 Reactive 到 WebFlux

温故知新

8-2 关于 Reactive 的一些说法

Reactive 实现框架

8-3 理解阻塞的弊端和并行的复杂

Reactor 认为阻塞可能是浪费的

理解阻塞的弊端
DataLoader.java
结论:由于加载过程串行执行的关系,导致消耗实现线性累加。Blocking 模式即串行执行 。

理解并行的复杂
ParallelDataLoader.java
结论:明显地,程序改造为并行加载后,性能和资源利用率得到提升,消耗时间取最大者。

8-4 Reactor 认为异步不一定能够救赎

Reactor 认为异步不一定能够救赎

8-5 理解 Callback Hell

JavaGUI.java
结论:

8-6 理解 Future 阻塞问题

FutureBlockingDataLoader.java
结论:

8-7 理解 Future 链式问题

ChainDataLoader.java
结论:

8-8 Reactive Streams JVM 认为异步系统和资源消费需要特殊处理

观点归纳:

8-9 Reactive Programming 定义

定义:

8-10 Reactive Manifesto 定义

关键字:
侧重点:

8-11 维基百科

关键字:
侧重点:
技术连接:

8-12 Spring Framework 定义

关键字:
侧重点:
技术连接:

8-13 ReactiveX 定义

关键字:
侧重点:
技术连接:

8-14 Reactor 定义

关键字:
侧重点:
技术连接:

8-15 andrestaltz 定义

关键字:
侧重点:
技术连接:

8-16 Reactive Programming 特性:编程模型

Reactive 编程模型

小结:

8-17 Reactive Programming 特性:数据结构

Reactive 数据结构

小结:

Reactive 设计模式

小结:

8-18 Reactive Programming 特性:并发模型

小结:

8-19 Reactive Programming 使用场景

Reactive Streams JVM
Spring Framework
ReactiveX
Reactor

8-20 Reactive Streams 规范:定义

关键字:

见Github:https://github.com/reactive-streams/reactive-streams-jvm

8-21 Reactive Streams 规范:API和事件

Subscriber信号事件:

Subscription信号操作:

8-22 Reactive Streams 规范:背压

总结背压

8-23 Reactor 框架运用 - 核心 API

核心 API

8-24 Reactor 框架运用实战(上)

依赖
FluxDemo

8-25 Reactor 框架运用实战(下)

FluxDemo 背压

8-26 走向 Spring WebFlux

Reactive Spring Web

第9章 WebFlux 核心

9-1 WebFlux 核心

9-2 官方引入WebFlux的动机分析

9-3 回顾Reactive

9-4 编程模型:注解驱动

9-5 Java 函数编程基础

springboot-webflux

9-6 编程模型:函数式端点 - Functional Endpoints

WebFluxApplication
hello-world

9-7 WebFlux 核心 - 并发模型

代码:
WebFluxApplication
mvc、mono

9-8 WebFlux 核心 - 核心组件

9-9 WebFlux 核心处理流程 - 函数式端点组件请求处理流程

见大纲

9-10 WebFlux 核心处理流程 - 注解驱动组件请求处理流程

见大纲

9-11 WebFlux 核心 - 使用场景

9-12 WebFlux 核心 - 课堂总结

9-13 WebFlux 核心 - 课程彩蛋

源码

上一篇下一篇

猜你喜欢

热点阅读