本系列文章旨在记录和总结自己在Java Web开发之路上的知识点、经验、问题和思考,原来已经分享在我的CSDN博客,现在分享在头条,希望能帮助更多码农和想成为码农的人。版权声明:本文为CSDN博主「普通的码农」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/liyongyan1202/article/details/89064944
前面的文章多次使用到了HTML(HyperText Markup Language,中文就是超文本标记语言)。这门语言可以使用任何的文本编辑器进行编写,写出的文档就是网页,只要将文件名以后缀 .html 结尾,浏览器就可以解释该文档,并以一定的格式呈现出来。
HTML是Web前端三剑客之一,主要是负责数据的结构、框架或骨架,表示哪些数据是标题啊、主体啊、导航啊、链接啊、表格啊、段落啊、图片啊、音频、代码啊等。简而言之,就是表示数据是什么。
HTML的核心思想很简单,就是给你的信息打标记,举个例子:
<这是一篇文章> <这是标题>XX爆炸性新闻</这是标题> <这是段落> 某年某月某日,某某发生某事。。。 </这是段落> <这是段落> 其他事情。。。 </这是段落> </这是一篇文章>
尖括号 < > 及其里面的词就是标记或者标签,只不过HTML标准里面用的是英文单词,我这只是用中文的词来说明这个思想。
标记有开始标记和结束标记,结束标记里面多一个正斜杠,就是 </ > 。
真正的信息(就是要呈现给用户看的)就写在开始标记和结束标记之间,这就是标记的内容。有时候没有内容,开始标记和结束标记可以合二为一,变成 < /> ,就是把正斜杠写到右尖括号前面,或者省略正斜杠。
标记及其内容合起来叫做元素,比如例子中的开始标记<文章>和结束标记</文章>及其之间的内容就是一个元素。可以看到元素可以嵌套,就是元素里面的内容可以再次包含元素,不过开始标记和结束标记要注意遥相呼应,事实上,编写的时候可以采用缩进来增加层次感且不易出错。
元素还有属性,属性可以有属性值,也可以没有,这些后面再讨论。这里要提到的是一个编写规范,不管是标记、属性还是值,习惯上都采用英文小写单词用连字符(就是短横线、减号)相连。表单中的需要发往Web服务器的数据可以使用后端的开发规范。
每一个元素都可以设置一个id属性,其值必须在该网页中是唯一的。
目前HTML的版本是HTML5,大多数浏览器版本都支持大部分常用的特性。再次强调,HTML的主要思想就是给你的信息打标记,这些标记表示数据的结构、框架或骨架,就是语义,至于数据如何呈现(比如呈现在哪个位置,什么颜色、字体、背景等等)和动态行为是由CSS(层叠样式表,Cascading Style Sheets)和JavaScript来负责。它们都由浏览器来解释执行。
HTML5的网页基本都有如下结构:
<!DOCTYPE html> <html lang="zh-cmn-hans"> <head> <meta charset="utf-8" /> <title>这里是网页的标题</title> </head> <body> </body> </html>
可以看到,基本结构就是由各种标记组成的,只不过标记使用的是英文单词,而且这些标记都是标准化(这样各家浏览器都能识别)了的,浏览器必须识别它们。这些标记也很语义化(这样人类能容易识别,便于开发和维护网页),基本上都是使用语义相对应的英文单词或缩写,这也是理所当然的,便于记忆和使用嘛。
基本结构里面包含以下几点:
但是,仅仅告诉浏览器使用何种字符编码还不行,网页文档本身在使用文本编辑器编写完保存的时候,必须使用该字符编码来保存;通过网络传输的话,那传输时也必须使用该字符编码来传输。
就好像我给你写了一封信,信上指示你说要用英语来读这封信,但信的内容却是用中文写的,那么你还是不能读这封信。这里我使用了普遍采用的utf-8这种字符编码,Web上基本都用这个,它能表示全球各个语言中的字符,而且占用字节数较少。字符编码也是比较复杂但很重要的内容,这里暂且不说;
OK,每个网页都有这个基本结构,剩下的就是往<body>元素里面添加你的各种信息了。
首先,我们可以向<body>元素里面添加文本,毕竟文字在我们的生活中占据很重要的位置,特别是在古代。文字可以写成小说、剧本、新闻、资讯等等。添加文本很简单,直接在元素里面敲文字(各种语言都行)就可以了。
如果光是这样的话,那就谈不上说HTML负责数据的结构、框架或骨架了。不错,HTML还提供了很多标记来描述数据,这里先说一些常用的。
写文章的时候通常要为文章起标题,而且有文章的总标题,副标题,文章内容相关的放在同一个标题下,标题下又可能有若干个子标题,就类似文档的大纲似的。所以,HTML提供了<h1>、<h2>、<h3>、<h4>、<h5>、<h6>这六个标记来说明标题的语义。我们试试给信息打上这些标记会是什么样子,先看代码:
<!DOCTYPE html> <html lang="zh-cmn-hans"> <head> <meta charset="utf-8" /> <title>这里是网页的标题</title> </head> <body> <h1>这是一级标题</h1><h2>这是一级标题</h2> <h3>这是一级标题</h3><h4>这是一级标题</h4> <h5>这是一级标题</h5><h6>这是一级标题</h6> </body> </html>
我特地把这些信息两个一行来写,看看浏览器是怎么处理的:
可以看到,浏览器会把各级标题的字体大小显示的不太一样,一级标题最大,六级标题最小。大多数浏览器都会这么处理。
那么问题来了,不是说HTML只负责信息的语义,不负责信息是如何展示的吗?话虽如此,给不同语义的信息来个默认的展示效果,不就省的我们还要再写信息如何展示的代码了嘛。这又再一次体现了契约优先(本质就是由默认值)的思想。这就是说我们还是可以修改这些各级标题是如何展示的,比如字体、颜色等等,这就要用到CSS了。
注意,浏览器可以关闭CSS和JavaScript,就是让这两个技术不起作用,从这个角度看,给标记设置默认的展示效果也有这方面的原因。
虽然标记有默认的展示效果,但我们一定不能为了获得这些类似的展示效果而强行给某些信息打上标记,而是应该使用CSS去获得这些展示效果。比如,不能为了加大字体就强行把不是标题的内容打上<h1>标记,这应该交给CSS去做,是否打上<hn>标记(n代表1、2、3、4、5、6)应该考虑该内容在语义上是否是标题!
第二点很重要的是,明明代码里面两个标题一行,为何浏览器展示的效果确是每个标题占一行?
这是因为HTML规定了某些标记有个作用,浏览器遇到它就必须另起一行来展示,有这个作用的标记就叫块级标记,没有这个作用的就是行级标记(不知道是否可以这么划分,通常的划分方式是块级标记是里面可以嵌套其他标记的,行级标记是里面不可以嵌套其他标记的)。
事实上,HTML规定文本里面的格式(缩进、换行、多个连续的空格等等)都没用,浏览器会把这些格式压缩成一个空格。比如下面的代码实际展示的是一行:
<body> 这是第一段。。。 这是第二段。。。 这是第三段。。。 </body>
展示效果成了:
这是第一段。。。 这是第二段。。。 这是第三段。。。
除了表示标题的标记以外,还有其他很多作用于文本,甚至是整个语义上的布局的标记,下面仅仅使用一段代码来展示比较常用的标记,不做过多解释说明。
<!DOCTYPE html> <html lang="zh-cmn-hans"> <head> <meta charset="utf-8" /> <title>这里是网页的标题</title> </head> <body> <header> <h1>这里是网页的页眉,里面可以有导航</h1> <nav> 这里是整个页面的导航按钮。。。 </nav> </header> <main><!-- 一个页面往往只有一个主体部分 --> <article><!-- 这里也可以使用通用容器标记<div> --> <header> <h2>这里是主体的页眉,可以有主体的导航/目录、介绍等等</h2> <nav> 这里是目录 </nav> <p>这里是介绍</p> </header> <article> <h2>这里是第一篇文章</h2> <p>这里是段落一</p> <p>这里是段落二</p> <p>。。。</p> </article> <article> <h2>这里是第二篇文章</h2> <section> <h3>这里是第二篇文章的第一部分</h3> <p>与上面类似</p> </section> <section> <h3>这里是第二篇文章的第二部分</h3> <p>与上面类似</p> </section> </article> </article> </main> <aside> <h2>这里是附注,往往跟主体内容相关性没那么强,但比较独立,浏览器上通常会显示主体的两侧</h2> </aside> <footer> <p>这里是整个页面的页脚,通常放一些版权声明、隐私政策之类的。</p> </footer> </body> </html>
涉及到布局的标记有:
涉及到文本内容的标记有:
还有很多用来标记文本的,比如表示重要性的<strong>、表示强调的<em>、表示图的<figure>和<figcaption>、表示引用或参考的<cite>、表示引述文本的<blockquote>和<q>、表示代码的<code>等等。
以上这些标记在我们的Java Web开发中暂时用不到,所以都不讨论。
链接可以说是网页的灵魂,正是它才能形成Web。表示信息是一个链接的标记是<a>。下面举例:
<!DOCTYPE html> <html lang="zh-cmn-hans"> <head> <meta charset="utf-8" /> <title>这里是网页的标题</title> </head> <body> <nav> <ul> <li><a href="https://www.csdn.net/">CSDN</a></li> <li><a href="https://www.baidu.com/">百度</a></li> </ul> </nav> <p> 这里是某些文本。。。有些<a href="dir/file.html">东西</a>需要人们在另一个网页中查看 </p> <nav> <ul> <li><a href="#question1">What is HTML?</a></li> <li><a href="#question2">How does HTML work?</a></li> </ul> </nav> <article> <h1 id="question1">What is HTML?</h1> <p> HTML is ... <br /><br /><br /><br /><br /><br /><br /><br /><br /> <br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /> </p> <h2 id="question2">How does HTML work?</h2> <p> HTML is explained by browser ... </p> </article> <p></p> </body> </html>
在浏览器上呈现这样:
上面的例子中<ul>标记表示是一个无序列表,<li>表示是列表中的一项。我们将CSDN、百度和东西打上<a>标记,表示它们都是一个链接,用鼠标点击它们可以看到指定的另一个网页。<br>表示需要换行,这里用来模拟页面很长导致看不见question2。
可以看到,浏览器为列表、链接都设置了默认的样式,鼠标移到链接上会变成手型。
链接必须要有一个href属性,其值就是目标网页的URL,当然可以是本网站内部的其他网页,也可以是外部网站某个网页,甚至是本网页中的其他部分(需要使用元素的id属性,如例子所示)。
表单允许用户向Web服务器提交数据,而不是仅仅用浏览器向Web服务器索要网页。
最常用的表单应该要数登录表单了。用户通过输入用户名和密码,点击登录按钮向Web服务器发起登录请求。
表单使用标记<form>,在<form>添加供用户输入信息的其他内容,常用的有文本框、密码框、单选按钮、复选按钮、下拉列表选择框以及最后的提交按钮。我们先简单介绍文本框、密码框以及提交按钮,其他的以后再介绍。
先上代码:
<!DOCTYPE html> <html lang="zh-cmn-hans"> <head> <meta charset="utf-8" /> <title>这里是网页的标题</title> </head> <body> <form method="post" action="login-success.html"> <label for="user-name">用户名:</label><input type="text" id="user-name" name="userName" /> <label for="password">密码:</label><input type="password" id="password" name="password" /> <input type="submit" value="登录"/> </form> </body> </html>
再看看浏览器上的呈现:
第一点,从呈现上看,用户名和密码都是文本框,不过,密码的文本框会将输入信息屏蔽,<input>标记使用type属性来区分各类输入方式:text是文本框、password是密码框、radio是单选按钮、checkbox是复选按钮等。
其次,我为标记指定了id和name两个属性,id属性用于<label>标记的for属性,从而将两个元素关联起来,这样,鼠标如果点击其内容,其关联的元素也会获得焦点(就是在文本框内出现了不断闪烁的输入光标)。
而name属性用于标识用户输入的该项数据,这样Web服务器才能根据该名字取出对应的数据,可以理解为Web服务器应用程序中的变量名,或者参数名。
所以,name属性的值通常遵循后端应用程序的开发规范,由于我们以后是使用Java Servlet技术作为后端应用的开发技术(还有PHP、Python、Go等技术),所以遵循Java的变量名命名规范(采用驼峰形式,比如userName)。
通常,表单中的每一项输入都会有一个关联的<label>元素,用于呈现,总不能光出现一个文本框在那吧,这样用户如何知道输入什么数据呢!它有一个for属性,用来指定关联元素的id。
提交按钮也是使用<input>标记,type属性值是submit,还有一个value属性,用来指定按钮上呈现的文字。
最后,回过头来看<form>标记,它必须指定一个action属性,它的值通常是后端应用中处理用户提交数据的一个程序的URL,比如可以是login.servlet、login.jsp、login.php、login.asp等等,后缀名通常表明了采用的后端应用开发技术。当然,我的例子里直接指定了一个HTML页面(login-success.html,你需要创建这么一个页面,相信你已经知道怎么创建了!),相当于不处理用户提交的数据,一般不能这么做!
那么,用户提交的数据是如何提交到Web服务器的呢?答案当然就是HTTP啊,浏览器使用HTTP来向Web服务器请求网页,再用它来向Web服务器提交数据也就理所当然了啊。浏览器会将用户输入的信息封装成HTTP的报文格式,最后通过网卡发送出去。
在这篇文章提到过,HTTP报文里面含有HTTP方法,常用的有GET、POST等。<form>标记的method属性就是用于指定提交数据的HTTP方法。默认值是get,但通常使用post,一方面get的语义就是获取而不是提交。另一方面get会将表单数据作为参数直接暴露在浏览器的地址栏里面。最后get提交的数据不能太大。
我们把上述代码中的元素的method属性值修改为get,保存,然后用浏览器打开,输入用户名和密码后点击提交,效果呈现如下:
可以看到,使用GET方法提交表单数据会在URL上附加上你的数据,同时也可以看到<input>标记的name属性值在这所起的作用,就是参数名的作用。
到此为止,我们就先介绍这么多,我们来总结一下:
习是一件很痛苦的事情,很多人们总问别人有没有捷径,问学习路线,好像问完了就学会了一样。其实我想说是:要是你真的想做一件事,那么就立刻去做好了,因为时间是不会等你的,在你犹豫的时候,时间早就流走了。所以与其犹豫不决不如理科开始行!
有的人说学Java要先学HTML,那就一起来看HTML是什么吧!
首先HTML 并不是一种编程语言,而是一种标记语超文本标记语言,负责展示网站的外观,用来控制各种属性的展示,要想做JavaWeb开发,HTML是必须学会的基础。
既然要学习开发,那么必须要有工具呀!
EditPlus 3
嗯!就先来认识一下HTML的一些标签
其中的主要的标签如下
<html>-------------------开始html文档
<head>--------------------开始文档头部
<title>---------------------开始文档标题
This is first page
</title>---------------------结束文档标题
</head>----------------------结束文档头部
<body>-----------------------开始文档体
Hello World.
</body>------------------------结束文档体
</html>------------------------结束html文档
新建 HTML 页面就会出现代码如下(绿色文字为注释):
一个页面的整体结构就是:
所有的内容都在<html></html>这两个标签内部。然后分为 <head> </head>头部和 <body> </body>身体两部分。
文档的头部描述了文档的各种属性和信息,包括文档的标题、在 Web 中的位置以及和其他文档的关系等。
绝大多数文档头部包含的数据都不会真正作为内容显示给读者。
<body>标签包含文档的所有内容
(比如文本、超链接、图像、表格和列表等等。)
<title> 定义文档的标题。
浏览器会以特殊的方式来使用标题,并且通常把它放置在浏览器窗口的标题栏或状态栏上。同样,当把文档加入用户的链接列表或者收藏夹或书签列表时,标题将成为该文档链接的默认名称。
下面的就算是一个简单的网页了。
让我们保存执行一下看看。
hello world!Java学习我来了!
要: 响应式宣言如何解读,Java中如何进行响应式编程,Reactor Streams又该如何使用?热衷于整合框架与开发工具的阿里云技术专家杜万,为大家全面解读响应式编程,分享Spring Webflux的实践。
本篇文章来自于2018年12月22日举办的《阿里云栖开发者沙龙—Java技术专场》,杜万专家是该专场第四位演讲的嘉宾,本篇文章是根据杜万专家在《阿里云栖开发者沙龙—Java技术专场》的演讲视频以及PPT整理而成。
摘要:响应式宣言如何解读,Java中如何进行响应式编程,Reactor Streams又该如何使用?热衷于整合框架与开发工具的阿里云技术专家杜万,为大家全面解读响应式编程,分享Spring Webflux的实践。从响应式理解,到Reactor项目示例,再到Spring Webflux框架解读,本文带你进入Java响应式编程。
演讲嘉宾简介:
杜万(倚贤),阿里云技术专家,全栈工程师,从事了12年 Java 语言为主的软件开发工作,热衷于整合框架与开发工具,Linux拥趸,问题终结者。合作翻译《Elixir 程序设计》。目前负责阿里云函数计算的工具链开发,正在实践 WebFlux 和 Reactor 开发新的 Web 应用。
本次直播视频精彩回顾,戳这里!https://yq.aliyun.com/live/721
PPT下载地址:https://yq.aliyun.com/download/3187
以下内容根据演讲嘉宾视频分享以及PPT整理而成。
本文围绕以下三部分进行介绍:
1.Reactive
2.Project Reactor
3.Spring Webflux
一.Reactive
1.Reactive Manifesto
下图是Reactive Manifesto官方网站上的介绍,这篇文章非常短但也非常精悍,非常值得大家去认真阅读。
响应式宣言是一份构建现代云扩展架构的处方。这个框架主要使用消息驱动的方法来构建系统,在形式上可以达到弹性和韧性,最后可以产生响应性的价值。所谓弹性和韧性,通俗来说就像是橡皮筋,弹性是指橡皮筋可以拉长,而韧性指在拉长后可以缩回原样。这里为大家一一解读其中的关键词:
1)响应性:快速/一致的响应时间。假设在有500个并发操作时,响应时间为1s,那么并发操作增长至5万时,响应时间也应控制在1s左右。快速一致的响应时间才能给予用户信心,是系统设计的追求。
2)韧性:复制/遏制/隔绝/委托。当某个模块出现问题时,需要将这个问题控制在一定范围内,这便需要使用隔绝的技术,避免连锁性问题的发生。或是将出现故障部分的任务委托给其他模块。韧性主要是系统对错误的容忍。
3)弹性:无竞争点或中心瓶颈/分片/扩展。如果没有状态的话,就进行水平扩展,如果存在状态,就使用分片技术,将数据分至不同的机器上。
4)消息驱动:异步/松耦合/隔绝/地址透明/错误作为消息/背压/无阻塞。消息驱动是实现上述三项的技术支撑。其中,地址透明有很多方法。例如DNS提供的一串人类能读懂的地址,而不是IP,这是一种不依赖于实现,而依赖于声明的设计。再例如k8s每个service后会有多个Pod,依赖一个虚拟的服务而不是某一个真实的实例,从何实现调用1 个或调用n个服务实例对于对调用方无感知,这是为分片或扩展做了准备。错误作为消息,这在Java中是不太常见的,Java中通常将错误直接作为异常抛出,而在响应式中,错误也是一种消息,和普通消息地位一致,这和JavaScript中的Promise类似。背压是指当上游向下游推送数据时,可能下游承受能力不足导致问题,一个经典的比喻是就像用消防水龙头解渴。因此下游需要向上游声明每次只能接受大约多少量的数据,当接受完毕再次向上游申请数据传输。这便转换成是下游向上游申请数据,而不是上游向下游推送数据。无阻塞是通过no-blocking IO提供更高的多线程切换效率。
2.Reactive Programming
响应式编程是一种声明式编程范型。下图中左侧显示了一个命令式编程,相信大家都比较熟悉。先声明两个变量,然后进行赋值,让两个变量相加,得到相加的结果。但接着当修改了最早声明的两个变量的值后,sum的值不会因此产生变化。而在Java 9 Flow中,按相同的思路实现上述处理流程,当初始变量的值变化,最后结果的值也同步发生变化,这就是响应式编程。这相当于声明了一个公式,输出值会随着输入值而同步变化。
响应式编程也是一种非阻塞的异步编程。下图是用reactor.ipc.netty实现的TCP通信。常见的server中会用循环发数据后,在循环外取出,但在下图的实现中没有,因为这不是使用阻塞模型实现,是基于非阻塞的异步编程实现。
响应式编程是一种数据流编程,关注于数据流而不是控制流。下图中,首先当页面出现点击操作时产生一个click stream,然后页面会将250ms内的clickStream缓存,如此实现了一个归组过程。然后再进行map操作,得到每个list的长度,筛选出长度大于2的,这便可以得出多次点击操作的流。这种方法应用非常广泛,例如可以筛选出双击操作。由此可见,这种编程方式是一种数据流编程,而不是if else的控制流编程。
之前有提及消息驱动,那么消息驱动(Message-driven)和事件驱动(Event-driven)有什么区别呢。
1)消息驱动有确定的目标,一定会有消息的接受者,而事件驱动是一件事情希望被观察到,观察者是谁无关紧要。消息驱动系统关注消息的接受者,事件驱动系统关注事件源。
2)在一个使用响应式编程实现的响应式系统中,消息擅长于通讯,事件擅长于反应事实。
3.Reactive Streams
Reactive Streams提供了一套非阻塞背压的异步流处理标准,主要应用在JVM、JavaScript和网络协议工作中。通俗来说,它定义了一套响应式编程的标准。在Java中,有4个Reactive Streams API,如下图所示:
这个API中定义了Publisher,即事件的发生源,它只有一个subscribe方法。其中的Subscriber就是订阅消息的对象。
作为订阅者,有四个方法。onSubscribe会在每次接收消息时调用,得到的数据都会经过onNext方法。onError方法会在出现问题时调用,Throwable即是出现的错误消息。在结束时调用onComplete方法。
Subscription接口用来描述每个订阅的消息。request方法用来向上游索要指定个数的消息,cancel方法用于取消上游的数据推送,不再接受消息。
Processor接口继承了Subscriber和Publisher,它既是消息的发生者也是消息的订阅者。这是发生者和订阅者间的过渡桥梁,负责一些中间转换的处理。
Reactor Library从开始到现在已经历经多代。第0代就是java包Observable 接口,也就是观察者模式。具体的发展见下图:
第四代虽然仍然是RxJava2,但是相比第三代的RxJava2,其中的小版本有了不一样的改进,出现了新特性。
Reactor Library主要有两点特性。一是基于回调(callback-based),在事件源附加回调函数,并在事件通过数据流链时被调用;二是声明式编程(Declarative),很多函数处理业务类似,例如map/filter/fold等,这些操作被类库固化后便可以使用声明式方法,以在程序中快速便捷使用。在生产者、订阅者都定义后,声明式方法便可以用来实现中间处理者。
二.Project Reactor
Project Reactor,实现了完全非阻塞,并且基于网络HTTP/TCP/UDP等的背压,即数据传输上游为网络层协议时,通过远程调用也可以实现背压。同时,它还实现了Reactive Streams API和Reactive Extensions,以及支持Java 8 functional API/Completable Future/Stream /Duration等各新特性。下图所示为Reactor的一个示例:
首先定义了一个words的数组,然后使用flatMap做映射,再将每个词和s做连接,得出的结果和另一个等长的序列进行一个zipWith操作,最后打印结果。这和Java 8 Stream非常类似,但仍存在一些区别:
1)Stream是pull-based,下游从上游拉数据的过程,它会有中间操作例如map和reduce,和终止操作例如collect等,只有在终止操作时才会真正的拉取数据。Reactive是push-based,可以先将整个处理数据量构造完成,然后向其中填充数据,在出口处可以取出转换结果。
2)Stream只能使用一次,因为它是pull-based操作,拉取一次之后源头不能更改。但Reactive可以使用多次,因为push-based操作像是一个数据加工厂,只要填充数据就可以一直产出。
3)Stream#parallel()使用fork-join并发,就是将每一个大任务一直拆分至指定大小颗粒的小任务,每个小任务可以在不同的线程中执行,这种多线程模型符合了它的多核特性。Reactive使用Event loop,用一个单线程不停的做循环,每个循环处理有限的数据直至处理完成。
在上例中,大家可以看到很多Reactive的操作符,例如flatMap/concatWith/zipWith等,这样的操作符有300多个,这可能是学习这个框架最大的压力。如何理解如此繁多的操作符,可能一个归类会有所帮助:
1)新序列创建,例如创建数组类序列等;
2)现有序列转换,将其转换为新的序列,例如常见的map操作;
3)从现有的序列取出某些元素;
4)序列过滤;
5)序列异常处理。
6)与时间相关的操作,例如某个序列是由时间触发器定期发起事件;
7)序列分割;
8)序列拉至同步世界,不是所有的框架都支持异步,再需要和同步操作进行交互时就需要这种处理。
上述300+操作符都有如下所示的弹珠图(Marble Diagrams),用表意的方式解释其作用。例如下图的操作符是指,随着时间推移,逐个产生了6个元素的序列,黑色竖线表示新元素产生终止。在这个操作符的作用下,下方只取了前三个元素,到第四个元素就不取了。这些弹珠图大家可以自行了解。
三.Spring Webflux
1.Spring Webflux框架
Spring Boot 2.0相较之前的版本,在基于Spring Framework 5的构建添加了新模块Webflux,将默认的web服务器改为Netty,支持Reactive应用,并且Webflux默认运行在Netty上。而Spring Framework 5也有了一些变化。Java版本最低依赖Java 8,支持Java 9和Java 10,提供许多支持Reactive的基础设施,提供面向Netty等运行时环境的适配器,新增Webflux模块(集成的是Reactor 3.x)。下图所示为Webflux的框架:
左侧是通常使用的框架,通过Servlet API的规范和Container进行交互,上一层是Spring-Webmvc,再上一层则是经常使用的一些注解。右侧为对应的Webflux层级,只要是支持NIO的Container,例如Tomcat,Jetty,Netty或Undertow都可以实现。在协议层的是HTTP/Reactive Streams。再上一层是Spring-Webflux,为了保持兼容性,它支持这些常用的注解,同时也有一套新的语法规则Router Functions。下图显示了一个调用的实例:
在Client端,首先创建一个WebClient,调用其get方法,写入URL,接收格式为APPLICATION_STREAM_JSON的数据,retrieve获得数据,取得数据后用bodyToFlux将数据转换为Car类型的对象,在doOnNext中打印构造好的Car对象,block方法意思是直到回调函数被执行才可以结束。在Server端,在指定的path中进行get操作,produces和以前不同,这里是application/stream+json,然后返回Flux范型的Car对象。传统意义上,如果数据中有一万条数据,那么便直接返回一万条数据,但在这个示例返回的Flux范型中,是不包含数据的,但在数据库也支持Reactive的情况下,request可以一直往下传递,响应式的批量返回。传统方式这样的查询很有可能是一个全表遍历,这会需要较多资源和时间,甚至影响其他任务的执行。而响应式的方法除了可以避免这种情况,还可以让用户在第一时间看到数据而不是等待数据采集完毕,这在架构体验的完整性上有了很大的提升。application/stream+json也是可以让前端识别出,这些数据是分批响应式传递,而不会等待传完才显示。
现在的Java web应用可以使用Servlet栈或Reactive栈。Servlet栈已经有很久的使用历史了,而现在又增加了更有优势的Reactive栈,大家可以尝试实现更好的用户体验。
2.Reactive编程模型
下图中是Spring实现的一个向后兼容模型,可以使用annotation来标注Container。这是一个非常清晰、支持非常细节化的模型,也非常利于同事间的交流沟通。
下图是一个Functional编程模型,通过写函数的方式构造。例如下图中传入一个Request,返回Response,通过函数的方法重点关注输入输出,不需要区分状态。然后将这些函数注册至Route。这个模型和Node.js非常接近,也利于使用。
3.Spring Data框架
Spring Data框架支持多种数据库,如下图所示,最常用的是JPA和JDBC。在实践中,不同的语言访问不同的数据库时,访问接口是不一样的,这对编程人员来说是个很大的工作量。
Spring Data便是做了另一层抽象,使你无论使用哪种数据库,都可以使用同一个接口。具体特性这里不做详谈。
下图展示了一个Spring Data的使用示例。只需要写一个方法签名,然后注解为Query,这个方法不需要实现,因为框架后台已经采用一些技术,直接根据findByFirstnameAndLastname就可以查询到。这种一致的调用方式无疑提供了巨大的方便。
现在Reactive对Spring Data的支持还是不完整的,只支持了MongoDB/Redis/Cassandra和Couchbase,对JPA/LDAP/Elasticsearch/Neo4j/Solr等还不兼容。但也不是不能使用,例如对JDBC数据库,将其转为同步即可使用,重点在于findAll和async两个函数,这里不再展开详述,具体代码如下图所示:
Reactive不支持JDBC最根本的原因是,JDBC不是non-blocking设计。但是现在JavaOne已经在2016年9月宣布了Non-blocking JDBC API的草案,虽然还未得到Java 10的支持,但可见这已经成为一种趋势。
四.总结
Spring MVC框架是一个命令式逻辑,方便编写和调试。Spring WebFlux也具有众多优势,但调试却不太容易,因为它经常需要切换线程执行,出现错误的栈可能已经销毁。当然这也是现今Java的编译工具对WebFlux不太友好,相信以后会改善。下图中列出了Spring MVC和Spring WebFlux各自的特性及交叉的部分。最后也附上一些参考资料。
作者:李博bluemind
*请认真填写需求信息,我们会在24小时内与您取得联系。