java web发展史与java学习路线

最近在准备学spring和spring boot,但是又看到servelet什么的很困惑,所以了解了以下java web的发展。同时也看到一个很好的java学习路线图。
https://www.jianshu.com/p/bec6736dcc3d
https://www.tianmaying.com/tutorial/web-history
https://www.runoob.com/servlet/servlet-intro.html
https://www.runoob.com/jsp/jsp-intro.html

Web开发技术发展历史

Web的诞生

提到Web,不得不提一个词就是“互联网”。Web是World Wide Web的简称,中文译为万维网。“万维网”和我们经常说的“互联网”是两个联系极其紧密但却不尽相同的概念。今天“互联网”三个字已经承载了太多的内涵,提到互联网,我们通常想到的一种战略思维,或者是一种颠覆传统的商业模式。抛开那些纷繁凌乱的商业化概念,回归技术本身,互联网就是指通过TCP/IP协议族互相连接在一起的计算机网络。而Web是运行在互联网上的一个超大规模的分布式系统。Web设计初衷是一个静态信息资源发布媒介,通过超文本标记语言(HTML)描述信息资源,通过统一资源标识符(URI)定位信息资源,通过超文本转移协议(HTTP)请求信息资源。HTML、URL和HTTP三个规范构成了Web的核心体系结构,是支撑着Web运行的基石。用通俗的一点的话来说,客户端(一般为浏览器)通过URL找到网站(如www.google.com),发出HTTP请求,服务器收到请求后返回HTML页面。可见,Web是基于TCP/IP协议的,TCP/IP协议把计算机连接在一起,而Web在这个协议族之上,进一步将计算机的信息资源连接在一起,形成我们说的万维网。大家开发的Web应用本质上就是可以提供信息或者功能的Web资源,成为Web这个全球超大规模分布式系统中的一部分。在技术层面进一步理解Web和互联网,建议找一本计算机网络的书去看看,了解一下计算机网络的分层结构和发展历史。

1991年8月6日,Tim Berners Lee在alt.hypertext新闻组贴出了一份关于World Wide Web的简单摘要,标志了Web页面在Internet上的首次登场。最早Web主要被一帮科学家们用来共享和传递信息,全世界的Web服务器也就几十台。第一个Web浏览器是Berners Lee在NeXT机器上实现,也只能跑在NeXT机器上,苹果和乔布斯的粉丝对NeXT的历史肯定耳熟能详。真正使得Web开始流行起来的是Mosaic浏览器,这便是曾经大名鼎鼎的Netscape Navigator的前身。Berners Lee在1993年建立了万维网联盟(World Wide Web Consortium,W3C),负责Web相关标准的制定。浏览器的普及和W3C的推动,使得Web上可以访问的资源逐渐丰富起来。这个时候Web的主要功能就是浏览器向服务器请求静态HTML信息。95年的时候马云在美国看到了互联网,更准确的说他其实看到的就是Web,阿里早先做的黄页也就是把企业信息通过进行HTML展示的Web应用。

Static-Page

动态内容的出现:CGI

最初在浏览器中主要展现的是静态的文本或图像信息,GIF图片则第一次为HTML页面引入了动态元素。不过人们已经不仅仅满足于访问放在Web服务器上的静态文件,1993年CGI(Common Gateway Interface)出现了,Web上的动态信息服务开始蓬勃兴起。CGI定义了Web服务器与外部应用程序之间的通信接口标准,因此Web服务器可以通过CGI执行外部程序,让外部程序根据Web请求内容生成动态的内容。Perl因为跨操作系统和易于修改的特性成为CGI的主要编写语言。当然,CGI可以用任何支持标准输入输出和环境变量的语言编写,比如Shell脚本,C/C++语言,只要符合接口标准即可。比如你用C语言编写CGI程序,你把希望返回的HTML内容通过printf输出就可以发送给Web服务器,进而返回给用户。

CGI

Web编程脚本语言:PHP/ASP/JSP

这个时候我们已经可以在Web上提供动态功能了,比如网站访问的计数,表单的处理。CGI对每个请求都会启动一个进程来处理,因此性能上的扩展性不高。另外,想象一下用在Perl和C语言中的程序中去输出一大堆复杂的HTML字符串,是不是有点蛋疼,可读性和维护性是个大问题。为了处理更复杂的应用,一种方法是把HTML返回中固定的部分存起来(我们称之为模版),把动态的部分标记出来,Web请求处理的时候,你的程序先生成那部分动态的内容,再把模版读入进来,把动态内容填充进去,形成最终返回。举个例子,搜索一个关键词,搜索引擎的Web服务器可以先从后台索引服务器里拿到数据,然后把这些数据填充到返回结果的HTML模版中,返回给浏览器。但是这件事情自己来做显然太繁琐而且是重复劳动。于是1994年的时候,PHP诞生了,PHP可以把程序(动态内容)嵌入到HTML(模版)中去执行,不仅能更好的组织Web应用的内容,而且执行效率比CGI还更高。之后96年出现的ASP和98年出现的JSP本质上也都可以看成是一种支持某种脚本语言编程(分别是VB和Java)的模版引擎。96年W3C发布了CSS1.0规范。CSS允许开发者用外联的样式表来取代难以维护的内嵌样式,而不需要逐个去修改HTML元素,这让HTML页面更加容易创建和维护。此时,有了这些脚本语言,搭配上后端的数据库技术,Web更是开始大杀四方了,像电子商务这样的应用系统也可以通过Web技术来构建。Web已经从一个静态资源分享媒介真正变为了一个分布式的计算平台了。反过来看,你也应该知道,不是只有当今这些流行脚本语言可以写Web应用,C语言一样可以做这件事情。前面举的搜索引擎通过C语言来获取数据和渲染Web页面的例子在追求极致访问速度的互联网公司是非常常见的,但是脚本语言在开发效率上更胜一筹。

PHP

分布式企业计算平台:J2EE/.Net

Web开始广泛用于构建大型应用时,在分布式、安全性、事务性等方面的要求催生了J2EE(现在已更名为Java EE)平台在1999年的诞生,从那时开始为企业应用提供支撑平台的各种应用服务器也开始大行其道。Java Servlet、Java Server Pages (JSP)和Enterprise Java Bean (EJB )是Java EE中的核心规范,Servlet和JSP是运行在服务器端的Web组件,EJB运行在服务器端的业务组件,是一种分布式组件技术。2000年随之而来的.net平台,其ASP.net构件化的Web开发方式以及Visual Stidio.net开发环境的强大支持,大大降低了开发企业应用的复杂度。ASP.Net第一次让程序员可以像拖拽组件来创建Windows Form程序那样来组件化地创建Web页面,Java平台后来出现的JSF也承袭了这一思想。两大平台在相互竞争和模仿中不断向前发展。

J2EE

框架横飞的年代:MVC,ORM

两大平台诞生之后,组件化编程技术盛极一时,Web技术的发展开始了一段框架横飞的年代,各种辅助Web开发的技术框架层出不穷。虽然脚本语言大大提高了应用开发效率,但是试想一个复杂的大型Web应用,访问各种功能的URL地址纷繁复杂,涉及到的Web页面多种多样,同时还管理着大量的后台数据,因此我们需要在架构层面上解决维护性和扩展性等问题。这个时候,MVC的概念被引入到Web开发中来了。2004年出现的Struts就是当时非常流行的Java Web开发的MVC框架。MVC早在1978年就作为Smalltalk的一种设计模式被提出来了,应用到Web应用上,模型Model用于封装与业务逻辑相关的数据和数据处理方法,视图View是数据的HTML展现,控制器Controller负责响应请求,协调Model和View。Model,View和Controller的分开,是一种典型的关注点分离的思想,不仅使得代码复用性和组织性更好,使得Web应用的配置性和灵活性更好。这是Spring MVC的示意图,典型的MVC架构。

Spring-MVC

此外,数据访问也逐渐通过面向对象的方式来替代直接的SQL访问,出现了ORM(Object Relation Mapping)的概念,2001年出现的Hibernate就是其中的佼佼者,已经成为Java持久层的规范JPA的主要参考和实现。更多的全栈框架开始出现,比如2003年出现的Java开发框架Spring,同时更多的动态语言也被加入到Web编程语言的阵营中,2004年出现的Ruby开发框架Rails,2005出现的Python开发框架Django,都提供了全栈开发框架,或者自身提供Web开发的各种组件,或者可以方便的集成各种组件。比如Spring基于IoC和AOP思想可以方便得整合出全套Web开发组件,SSH(Struts+Spring+Hibernate)一度成为Java Web开发的标配。值得一提的时Rails这个MVC框架,26岁的丹麦大神David Heinemeier Hansson在开发著名项目管理软件BaseCamp的过程中形成,Ruby语言本身在快速开发上的优势,加上Rails诸如崇尚DRY(Don’t)Repeat Yourself)原则, 约定优于配置,拥抱REST等特性,使其迅速成为一个极其流行的Web开发框架。

MVC

回归Web本质:REST

注意,看到这里的时候,你会发现Web开发的重点已经不在于HTTP/HTML/URL这样的Web基础架构了,而是各种平台下的各种框架和组件技术(MVC/ORM/分布式组件技术等等)。所以今天很多人可能会用一个MVC框架构建Web网站,但是可能并不了解Web本身。2000年的时候,Roy Fielding在他的博士论文中从构架风格的角度来剖析了Web本身,将Web内在的设计原则和思路系统得论述出来。Roy Fielding是HTTP协议的主要设计者,也是Apache服务器项目的联合创始人,他的这篇博士论文提出来的REST(Representation State Transformation)也成为一种流行的Web架构风格。REST鼓励基于URL来组织系统功能,充分利用HTTP本身的语义,而不是仅仅将HTTP作为一种远程数据传输协议。Web应用的开发应该回归Web的本质特征。Rails在发展过程中也完全拥抱REST,成为REST的坚定支持者。有些人认为REST和MVC是两种对立的风格,其实不尽然,两者是互为补充的,从Rails是一个全面支持REST的MVC框架这一点就可窥见。

浏览器端的魔术:AJAX

Web应用同时涉及到浏览器端和服务器端,之前的介绍除了简单提到了CSS规范之外,主要关注的是服务器端的技术发展。在客户端,1995年NetScape公司设计的JavaScript被用作浏览器上运行脚本语言为网页增加动态性。微软随后推出类似JScript,但是缺乏统一的语言规范,使得浏览器兼容性成为一个程序员的梦魇。JavaScript最终被提交到欧洲计算机制造商协会(ECMA),做为中立的ECMA开始了标准化脚本语言之路,并将其命名为ECMAScript。JavaScript可以响应浏览器端的用户事件,检测表单的正确性,动态修改HTML页面结构DOM,因此可以减少与服务器端的通信开销,并且做出很酷的页面动态效果。2005年出现的AJAX这个概念使得JavaScript再次大放异彩。AJAX即“Asynchronous JavaScript and XML”(异步的JavaScript与XML技术),指的是一套综合了多项技术的浏览器端网页开发技术,可以基于JavaScript的XmlHttpRequest的用于创建交互性更强的Web应用。AJAX是一种已有技术的mashup,多种技术组合在一起形成了其特色和优势,早在1998年就已经开始有人使用。Google在地图和Gmail等产品中对这项技术的深入应用,以及AJAX这个吸引眼球的名字的提出,使其正式站在了聚光灯下,开始吸引无数人的目光。我们知道Web应用中用户提交表单时就向Web服务器发送一个请求,服务器接收并处理传来的表单,并返回一个新的网页。而前后两个页面中的往往大部分HTML代码是一样的,每次都返回整个页面内容是一种带宽资源的浪费。而AJAX应用仅向服务器发送并取回必须的数据,并在客户端采用JavaScript处理来自服务器响应,更新页面的局部信息。这样不仅浏览器和服务器的数据交换大大减少,而且客户端也可以更加快速地响应用户操作。如果你用Gmail就应该知道,Gmail从来都不刷新页面,所有的请求都是通过AJAX获取数据进行局部更新。AJAX的出现,以及诸如EXTJS、DOJO等一些前端开发框架的出现,也使得单页应用(Single Page Application)在这个时候流行起来。

AJAX

前端MVC:Angular/Backbone

这种模式下,前后端的分工非常清晰,前后端的关键协作点是 Ajax 接口,规定好交互接口后,前后端工程师就可以根据约定,分头开工,开发环境中通过Mock等方式进行测试,同时在特定时间节点进行前后端集成测试。但是,随着业务功能的愈发复杂(看看现在的Gmail),这种模式本质上和JSP时代的Web开发并无本质区别,只不过是将复杂的业务逻辑从JSP文件转移到了JavaScript文件中而已。现在,对于一个前端功能、交互复杂的SPA,JavaScript代码很容易膨胀(超过10万行)。很自然地,像服务端从JSP向MVC框架转换的过程一样,前端开发也出现了大量的MVC框架,比较典型的包括BackboneJS, AngularJS, EmberJS, KnockoutJS。总的来说,MV*框架的提出是为了解决前端开发的复杂度,提供一套规则组织代码、分层(MVC),通过合理的组织和分层,前端的代码职责明确、清晰,便于开发与测试。

Frontend-MVC

JavaScript在服务器端的逆袭:Node

各大浏览器的竞争,使其引擎的性能不断提升,至今Google V8引擎的性能已经足以运行大型Javascript程序。在V8之上加以网络、文件系统等内置模块,形成了如今的Node.js。

随着Node.js的出现,JavaScript开始拥有在服务端运行的能力,它的异步本质使得Node.js在处理I/O密集型业务中优势凸显,而大多Web业务中I/O性能都是瓶颈。eBay、Yahoo、甚至Microsoft Azure纷纷引入Node.js以提升性能。Node.js的package每天都有几千万的下载量。这对前端工程师来说可是一个好消息,精通JavaScript的他们也能够做服务端开发了!虽然现实中并不是这样美好(服务端开发涉及的不仅仅是语言层面),但一种新的开发模式也因此兴起:浏览器端处理展现层逻辑、而服务端Controller这一层以及相关的模板渲染、路由、数据接口以及Session/Cookie先关处理实际上交给了Nodejs来做。通过Nodejs, 意味着前后端很多代码可以复用(例如数据验证逻辑),在需要SEO的场景下也可以选择服务端模板渲染。

但另一方面,JavaScript刚被引入到服务器端开发,其生态环境还未成熟,甚至大量的常用package主版本号都是0。长期用来实现页面逻辑,天生自由的JavaScript,在服务器端开发中,仍未形成统一的开发范型。不同开发原则和编码风格的应用,都将对Node.js项目的性能、可维护性产生重大影响。现在而言,服务器端javascript开发究竟是魔鬼还是天使,仍取决于团队中的开发者。

Node

结语

Web技术依然在快速发展,Web本身的基础规范也在不断完善,HTML5和CSS3引入了更多激动人心的特性。回顾Web的发展历史,从某个角度看,就是抽象层次不断提高的一个过程,更高的抽象层次屏蔽更低层的复杂性,从而提高开发效率。每当技术发展到一定程度,出现某些局限性的时候,就会有更优秀的技术出现来突破这些局限性。其实这是计算机技术发展的一个普遍规律,比如高级语言的出现屏蔽了汇编语言的复杂性,帮助我们更快速的编程;数据库技术的出现使得我们无需关心物理存储和访问细节,写简单的SQL语句就能搞定,更进一步,ORM框架使得我们通过一条语句调用一个类的一个方法就能方便就行数据操作。我们应该让自己的技术视野具备一定的高度和广度,看到一门技术的发展规律和发展历程,这是一种技术修养的体现,其实跟人文修养是一样的。同时也应该具有一定的深度,因为我们往往站在比较高的抽象层次,比如今天你写几行代码就能把数据库创建好,增删改查的功能也自动生成好了,但是成为高手需要你对底层的原理机制有更透彻的理解,真正遇到问题的时候才能抽丝剥茧迎刃而解。

Java Web 开发发展简介

远古期 - 静态页面时代

讲Java Web开发的历史进程,不得不提Web开发的历史进程。
在互联网刚发展的时候,那时候的网站功能是很简单的。那时候的网站还都是静态的。这里所说的静态是指,请求访问的网页都是事先编辑好的,不能改变的。

这里先讲下当时一个请求是如何返回结果的。

比如,你想访问新浪上的一张图片,会在浏览器键入这个图片的地址:

img

img

浏览器会根据地址像新浪服务器发送HTTP请求。新浪服务器上的HTTP Server接收到请求后,会根据路径地址/img/12345.jpg查找的这个文件,然后read文件,再把图片数据发送给客户端,客户端的浏览器就能正确展示图片了。

img

img

也就是说,这里的URL对服务器来说就是查找文件的地址,而文件必须实实在在存在于服务器中的特定目录下的。

缺点
很明显,访问的资源必须事先已经存在,否则访问不到。而动态展示也是没法实现的。比如:某人刚发布了一篇文章,想在首页立即看到是不可能的。只能重新手动编辑首页,把文章链接加进去

混沌期 - CGI时代

然而,如果页面一直是静态的额,也就不会有现在纷繁复杂的网站了。那么动态展示页面的解决方案是什么呢?是CGI
CGI全称是通用网关接口(Common Gateway Interface)。那么它的作用是啥呢?

CGI是啥

首先,要清楚CGI是啥?
CGI是一个可执行的程序或者可运行的脚本。几乎所有语言都能写CGI,像python,C,甚至shell。

举个例子。下面一段C代码,经过编译成可执行程序后,就是一个CGI。

1
2
3
4
5
6
int _tmain(int argc, _TCHAR* argv[])
{
printf("Content-type:text/html\n\n");
printf("%s",getenv("QUERY_STRING")); //打印get获取的信息
return 0;
}

再或者,下面一个python脚本,也是一个CGI

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/usr/bin/python
# -*- coding: UTF-8 -*-

print "Content-type:text/html"
print # 空行,告诉服务器结束头部
print '<html>'
print '<head>'
print '<meta charset="utf-8">'
print '<title>Hello Word - 我的第一个 CGI 程序!</title>'
print '</head>'
print '<body>'
print '<h2>Hello Word! 我是来自菜鸟教程的第一CGI程序</h2>'
print '</body>'
print '</html>'

OK,知道了CGI是可执行的程序或脚本,但是怎么工作的呢?

CGI怎么用

img

img

如上图,当浏览器发送一个CGI请求后,服务器会启动一个进程运行CGI程序或脚本,由CGI来处理数据,并将结果返回给服务器,服务器再将结果返回给浏览器。

举个表单提交的例子:

1
2
3
4
5
6
7
8
<form id="form" name="form" method="post" action="http://localhost/cgi-bin/test/cgi_test.cgi">
<p>输入内容:
<input type="text" name="user" id="user" />
</p>
<p>
<input type="submit" name="submit" id="submit" value="提交" />
</p>
</form>

上面是一个表单提交的html代码,展示的效果是下面这个样子:

img

img

细心的你会发现,action的值是http://localhost/cgi-bin/test/cgi_test.cgi。这里,cgi_test.cgi就是一个cgi程序。
还记得上面那段C++代码吗?

1
2
3
4
5
6
int _tmain(int argc, _TCHAR* argv[])
{
printf("Content-type:text/html\n\n");
printf("%s",getenv("QUERY_STRING")); //打印get获取的信息
return 0;
}

cgi_test.cgi就是这段代码编译出来的可执行程序。
这段代码的作用是什么呢?
作用是将表单提交的信息直接打印出来。
如何做到的?
只有两行代码,第二行代码是关键。getenv()是C函数库中的函数getenv("QUERY_STRING")的意思是读取环境变量QUERY_STRING的值。而QUERY_STRING的值就是表单提交的信息。
OK,这个CGI的功能就清晰了。表单提交后展示下面的结果也就不奇怪了:

img

img

我们再通过一个图梳理下上述流程:

img

img

综上,CGI工作模式示意图如下:

img

img

CGI的特点

  • 由Http Server唤起。常见的Http Server如Apache,Lighttpd,nginx都支持CGI
  • CGI单独启动进程,并且每次调用都会重新启动进程
  • 可以用任何语言编写,只要该语言支持标准输入、输出和环境变量

CGI的缺点

  • 消耗资源多:每个请求都会启动一个CGI进行,进程消耗资源15M内存的话,同时到达100个请求的话,就会占用1.5G内存。如果请求更多,资源消耗是不可想象的。
  • :启动进程本身就慢。每次启动进程都需要重新初始化数据结构等,会变得更慢。

引申

为了解决CGI重复启动进程和初始化的问题,后来出现了FastCGI

开荒期 - Servlet时代

在CGI繁荣发展的时代,Java还没有发展起来。当Java开始参与历史,引领潮流的时候,也必然会借鉴和改进之前的技术和思想。

鉴于CGI的一些缺点,Java Web在开始设计的时候就想出了一种解决方案 – Servlet
同样,第一个问题,Servlet是啥?

Servlet是啥?

举个例子,网站一般都有注册功能。当用户填写好注册信息,点击“注册”按钮时,谁来处理这个请求?用户名是否重复谁来校验?用户名和密码需要写入数据库,谁来写入?是Servlet!

Servlet是实现javax.servlet.Servlet接口的类。一般处理Web请求的Servlet还需要继承javax.servlet.http.HttpServlet

1
2
3
4
abstract class HttpServlet implements Servlet{
void doGet();
void doPost();
}

doGet()方法处理GET请求
doPost()方法处理POST请求

浏览器发来的请求是怎么被Servlet处理的呢?还是举表单提交的例子。
我们假设表单样式如下,只是简单提交两个数据:网址名和网址。并假设处理URL为http://localhost:8080/TomcatTest/HelloForm

img

img

浏览器工作
当表单使用GET方式提交时,浏览器会把表单数据组装成这样的URL:http://localhost:8080/TomcatTest/HelloForm?name=菜鸟教程&url=www.runoob.com

好,现在浏览器的任务暂时告一段落,开始Java Web服务工作了。

Java Web服务
首先,我们得指定http://localhost:8080/TomcatTest/HelloForm这个URL由谁来处理。这个映射关系需要在web.xml中配置:

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8"?>
<web-app>
<servlet>
<servlet-name>HelloForm</servlet-name>
<servlet-class>com.runoob.test.HelloForm</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloForm</servlet-name>
<url-pattern>/TomcatTest/HelloForm</url-pattern>
</servlet-mapping>
</web-app>

web.xml中配置的意思是:当URI为/TomcatTest/HelloForm时,交给com.runoob.test.HelloForm处理。而HelloForm正是个Servlet。

因此,我们需要编写HelloForm这样一个Servlet:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* Servlet implementation class HelloForm
*/
@WebServlet("/HelloForm")
public class HelloForm extends HttpServlet {
private static final long serialVersionUID = 1L;

/**
* @see HttpServlet#HttpServlet()
*/
public HelloForm() {
super();
// TODO Auto-generated constructor stub
}

/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 设置响应内容类型
response.setContentType("text/html;charset=UTF-8");

PrintWriter out = response.getWriter();
String title = "使用 GET 方法读取表单数据";
// 处理中文
String name =new String(request.getParameter("name").getBytes("ISO8859-1"),"UTF-8");
String docType = "<!DOCTYPE html> \n";
out.println(docType +
"<html>\n" +
"<head><title>" + title + "</title></head>\n" +
"<body bgcolor=\"#f0f0f0\">\n" +
"<h1 align=\"center\">" + title + "</h1>\n" +
"<ul>\n" +
" <li><b>站点名</b>:"
+ name + "\n" +
" <li><b>网址</b>:"
+ request.getParameter("url") + "\n" +
"</ul>\n" +
"</body></html>");
}

// 处理 POST 方法请求的方法
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}

由于请求方式是GET,因此需要doGet()方法来处理。仔细阅读doGet()方法的代码,发现处理逻辑只是把表单数据放入到了一段html代码中。这段html代码会被传输给浏览器,然后浏览器渲染出结果,如下图所示:

img

img

Servlet的特点

Servlet相对于CGI有了很大的改进,效率更高,功能更强大,更容易移植。主要表现在一下几个方面:

  • CGI每个请求启动一个进程,而Servlet是更轻量的线程。线程和进程的对比和优劣请自行Google。
  • CGI每个进程都需要初始化,Servlet只初始化一次实例就行
  • Servlet依托于Java语言,具有很好的跨平台型。CGI根据语言的不同,跨平台型不同
  • CGI与数据库连接需要重连,Servlet可以使用数据库连接池。
  • Java有丰富的、各种各样的库函数

Servlet的缺点

看上面的代码,会发现,html代码是写在Java代码中的。对于前端人员来说,这种形式非常非常难以开发和修改。

Servlet的升级 – JSP

Servlet是在Java代码中写HTML代码。与之对应的就是在HTML代码中写Java代码,这就是JSP。

JSP是啥?

JSP:JavaServer Pages
简单点说,就是可以在html中写Java代码。

还是先从例子中大概了解下JSP:

还是上面表单处理的例子。表单的html代码就不展示了,我们直接模拟GET请求,即在浏览器中输入地址:http://localhost:8080/testjsp/main.jsp?name=菜鸟教程&url=http://www.runoob.com

很明显,这个URL的关键是main.jsp。这个文件的内容是啥呢?
main.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import="java.io.*,java.util.*" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
</head>
<body>
<h1>使用 GET 方法读取数据</h1>
<ul>
<li><p><b>站点名:</b>
<%= request.getParameter("name")%>
</p></li>
<li><p><b>网址:</b>
<%= request.getParameter("url")%>
</p></li>
</ul>
</body>
</html>

这就是JSP,在html代码中插入Java代码。java代码被<% %>所包围。
<%= request.getParameter("name")%>表示获取请求参数name的值,<%= request.getParameter("url")%>表示获取请求参数url的值。最终展示结果是怎样的呢?看下图:

img

img

JSP是如何工作的?

为啥html代码中可以写Java代码呢?看下图:

img

img

其实原理是这样的:

就像其他普通的网页一样,您的浏览器发送一个HTTP请求给服务器。

Web服务器识别出这是一个对JSP网页的请求,并且将该请求传递给JSP引擎。通过使用URL或者.jsp文件来完成。

JSP引擎从磁盘中载入JSP文件,然后将它们转化为servlet。这种转化只是简单地将所有模板文本改用println()语句,并且将所有的JSP元素转化成Java代码。

JSP引擎将servlet编译成可执行类,并且将原始请求传递给servlet引擎。

Web服务器的某组件将会调用servlet引擎,然后载入并执行servlet类。在执行过程中,servlet产生HTML格式的输出并将其内嵌于HTTP response中上交给Web服务器。

Web服务器以静态HTML网页的形式将HTTP response返回到您的浏览器中。

最终,Web浏览器处理HTTP response中动态产生的HTML网页,就好像在处理静态网页一样。

用一句话来讲:每个JSP都最终会变成对应的Servlet执行

JSP的缺点

在HTML代码中写Java代码,方便了前端人员,但是苦了后端人员。因此,单纯使用JSP,开发效率依旧不高。

后来,有牛人发现,Servlet天生非常适合逻辑处理(因为主要是Java代码),而JSP非常适合页面展示(因为主要是html代码),那么在结合Servlet和JSP各自的优缺点后,诞生了Web开发中最常用和最重要的架构设计模式:MVC

发展期 - MVC时代

MVC模式(Model-View-Controller)是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller):

  • Controller——负责转发请求,对请求进行处理
  • View——负责界面显示
  • Model——业务功能编写(例如算法实现)、数据库设计以及数据存取操作实现

img

img

简而言之,请求发来后,会首先经过Controller层处理,需要返回的结果封装成对象传递给JSP,然后JSP负责取出数据展示就够了。这样,后端开发人员只负责编写Servlet,前端人员负责JSP,极大提升了开发效率。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@WebServlet("/userPosts")
public class UserPostController extends HttpServlet {

private static final long serialVersionUID = -4208401453412759851L;

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
User user = Data.getByUsername(username);
List<Post> posts = Data.getPostByUser(user);

req.setAttribute("posts", posts);
req.setAttribute("user", user);
RequestDispatcher dispatcher = req.getRequestDispatcher("/templates/userPost.jsp");
dispatcher.forward(req, resp);
}
}

像上面这段代码,UserPostController就是一个Servlet,负责逻辑处理。需要返回的数据封装到HttpServletRequest对象中,传递给jsp页面。而负责展示的就是/templates/userPost.jsp这个jsp文件。

繁盛期 - 框架时代

有了Servlet和JSP,相当于有了武器。有了MVC,相当于有了战术。但是武器和战术之间还缺少一层,就是具体实施者。

实践证明,单纯使用Servlet、JSP和MVC开发,依然会面临诸多的问题。而程序员普遍存在一种特质,就是懒。因为懒,所以才想着能有更简单的解决办法。因为懒,针对一些通用问题,才会想出通用解决方法。可以说,因为懒,科技才有了进步。。。这时候,为了解放劳动力,一些开源框架营运而出。这些框架的目的只有一个:让开发简单,简单,更简单

提到Java Web框架,就不得不提几乎所有开发者都知道的三大框架:SSH

SSH

关于三大框架,这里不做介绍了,网上的文章铺天盖地。想要说的是:无论什么框架,都是对常见问题的抽象和封装,再出什么新的框架,也万变不离其宗,脱离不了Servlet这个根基。学习的时候千万不能跟着框架走,框架让做什么就做什么,而是要想为什么这样做。否则,今天学会了一个框架,明天又出了新的框架,又会抓瞎了。

Java学习路线

Roadmap

Roadmap

Java EE and Spring

https://www.zhihu.com/question/268742981/answer/341770209
在回答题主的问题之前,我先要简单介绍一下什么是JavaEE,什么是Spring。JavaEE是一组建立在JavaSE之上的标准,解决企业级开发中的一系列问题。请特别留意,它仅仅是个标准,是对一系列接口的约定,众多厂商围绕这个标准做实现。如JBoss,WebSphere等。第一个版本的JavaEE 1.2在1999年被发布,到2017年的JavaEE 8,已经经历了将近20年。那么JavaEE都有哪些标准,解决了什么问题呢?我这里简单列举一下主要的标准:Servlet:定义了如何处理Web请求,这个相信大家最熟悉Java Server Faces:定义了如何使编写Web界面JAX-RS:定义了如何编写RESTFul的接口EJB:定义了如何编写“企业Bean”JPA:定义了如何编写ORM和数据存取JTA:定义了如何编写事务相关的代码JMS:定义了如何编写消息队列程序CDI:定义了如何编写依赖注入JAX:定义了如何编写XML程序JAX-WS: 定义了如何编写基于XML的网络服务,即SOAP……看到这些,你可能机会发现,你平时其实经常使用其中一些标准接口,即便你认为你在用Spring。什么是Spring呢?Spring最早可以追溯到2002~2004年。在那几年作者Rod Johnson出版了两本书:“Expert One-on-One J2EE Design and Development“和“Expert One-on-One J2EE Development without EJB“,和最初几个版本的Springframework。早期的Spring定位于解决J2EE在实际使用上的一系列问题,因为JavaEE的API实在是太难用了。Rod估计是趟了不少大坑,于是总结了一套最佳实践,并总结到了一套框架里。其中最重要的,就是所谓IoC(控制反转)。经过多年发展,Spring发布了很多组件:spring-core:Spring的Bean的管理,控制反转和程序上下文spring-mvc: web开发中的model-view-controllerspring-data: 数据层访问和封装spring-boot: spring全家桶自助配置和部署管理工具spring-batch:一个简单的批处理框架spring-cloud:支持与许多云服务接口的整合spring-security:认证和权限管理……spring中其实大量使用或者实现了JavaEE标准。比如spring-mvc是在servlet基础之上的封装。spring本身并不提供容器,而是支持使用任何支持servlet标准的容器(如tomcat,jetty等)。spring-data也实现了JPA,通过标准接口对进行CRUD等。归根到底Spring只是想更好的解决实际问题。JavaEE的实现做得好的就用,做得不好的用比较恰当的方式独立实现或者封装。俗称“接地气”。见过不少人喜欢用“JavaEE vs Spring”来提问引战。但是,由上面的介绍可以看到JavaEE和Spring并不对立。作为开发工程师,其实还是哪个能解决问题,生态好,支持好,成本低就用哪个。而且混着用也没有什么大的问题。随着时间的发展,JavaEE已经越来越落后,这是由于它的体制造成的。JavaEE的制定是由几大巨头定期开会协商通过,发布。然后个大容器实现厂商跟进。但这样太慢了。互联网的发展速度已经远不是这样一个僵化的体制能够适应的。反观Spring相对就快速的多。Spring自己就是一家公司,觉得整个社区什么东西最前沿,最急缺就立刻响应去做了。比如,Restful刚流行,你是愿意等一年半载出JAX-RS标准,然后再出Jersey,才能用;还是选择直接用Spring MVC直接就把事办了?结果不言而喻。对解决问题的态度是二者目前境遇不同的主要原因。最早期JavaEE是领导者,所有的技术厂商要想在这个圈子里混,必须跟着标准走。而Spring逐渐占据领导地位之后,JavaEE的一些标准反而要跟着Spring走。CDI就是一个很好的例子。Spring IoC是整个框架的核心。它解决了在Java中只能import类,却import不了组件的问题。这个问题在js,python之类的环境中都是小菜一碟。但是Java中,如果没有IoC,程序员就要花大量的时间写new和set,组装整个服务的引用关系(你不觉得这很不人道吗?)。Spring流行之后,JavaEE在2009年才发布CDI标准。其样子就是活脱脱的把Spring的Annotation换了个名字而已。如果说未来的发展,我认为JavaEE早已经没有了未来。JavaEE里那些做得很好的,或者和其他框架能够很容易相融的标准继续存在;那些被骂成翔的,比如JSF;或者一开始就没有市场的,比如CDI,会成为Wiki上的一段段文字记录。但,Spring也不一定好过。在Java体系内他也要面临play,vert.x的冲击;在体系外,它会受到其他语言环境的巨大压力,如nodejs,python和go。说Spring灵活是相对于JavaEE而言。无论标准、框架、服务,都是为了解决问题而存在,而不是如“多么的OO”,“多么的标准”,“多么的概念清晰“。哪个工具解决的好,解决的快,就用哪个。最近2~3年,docker+微服务的发展进一步的强化了这个现实。大量的新技术会随着业务领域形成自己的生态。这其实是好事,不是吗?P.S. 题主在题目中给了一个文章,我认为其JavaEE和IoT的说法完全是扯淡。一个不注重解决问题,总想画个大饼的东东,还想抢占新领域?先老老实实做几个能跑的东西再说吧。2018年3月17日更新。有知友询问还是不理解什么是规范。那我索性再唠叨一些。Java各种标准的制定是通过Java Community Process - JCP进行的。JCP的成员可以根据需要提出Java Specification Request - JSR。每个JSR都要经过提交给JCP,然后JCP讨论,修订,表决通过,并最终定稿;当然,运气不好的就被拒绝/废弃了。而JavaEE是一组被通过的JSR的合集。JSR和JavaEE的关系就好像是文章和经过编辑整理过的文集。以Servlet为例。Servlet的标准有很多个版本,从1.1到目前最新的4.0。Servlet从2.2版本被纳入到JavaEE。目前比较常见的Servlet 3.0是JSR-315,在2009年年底发布。在JCR Servlet 3.0可以看到这个提案整体的流程,并可下载原文。那么到底什么是Specification呢?是一组实现约定。还拿Servlet规范举例子,它的其中一个规定是:为了支持处理一个Web请求,必须实现一个HttpServlet类,这个类必须继承Servlet接口,当然也得实现其中的所有接口方法,比如doGet,doPost。doGet方法必须接受两个参数,HttpServletRequest req, HttpServletResponse resp,分别表示请求和返回,出了问题可以返回ServletException或者IOException。整个规范大概几十上百页。所规定的项目包括接口定义,抽象类和关系,状态和生命周期等等。而所有开发基于Java的企业应用的厂商就可以选择支持这些规范。Tomcat、Jetty、glassfish等都支持Servlet。所以你不管用哪个,编写出来的Java代码是基本一样的。当然,这些实现除了支持规范外都有各自独有功能和接口,不然咋竞争……。但白话了这么多,我其实是希望大家不要过于在意JavaEE到底是什么这件事情。在开发时,并不会以“一个库是不是遵守标准“为最优先的考量(尽管一些标准狂热分子的确会这样,还会时不时圣战一把)。如果有能干活的框架/库,符合一个广泛接受的标准,当然好;如果不符合,也不用太纠结。毕竟我们是为自己的工作任务打工,而不是为标准和编程语言打工,对吧?