要成为牛逼的JavaWeb程序员, Java Web的基础非常重要,现在有各种成熟的设计框架例如JQuery、Spring、Struts、Mybatis,将Java Web基础的复杂且通用的逻辑进行封装,减少了程序员的代码量,提高了编码效率,但是这些框架非常不利于我们了解Java Web底层如何运作,甚至不知道HTML(HyperText MarkupLanguage)、HTTP(HyperText Transfer Protocol)、URL(Uniform Resource Locator)以及文字编码的问题。当我们习惯使用这些框架而不去探究其实现原理时,我们的开发水平也就被这些框架所禁锢了,学不到核心,也就无法创新。
我就是一个被这些框架所坑的小程序猿。由于学东西很快,所以在项目中很快就能上手,产生劳动力。但是越写越觉得不安,“为什么要这样写呢?”、“Struts2是怎么把JSP页面、Controller、Model联系起来的?”、“前台的请求是如何被后台准确接收的?”、“Servlet是什么?”……如果你没想过这些问题,说明你已经被当前的工作禁锢了思维,仅仅为了实现某个功能而编程,几乎没有任何技术水平的提高。
在认识到这些之后,我就开始了Java Web基础的学习,下面是我的学习笔记,供大家参考。
关于HTML
这个想必大家都知道是什么,HTML是以便签(Tag)的方式定义文件结构,是构建界面的基础,被放在服务器上供客户端访问。浏览器会根据这些标签绘制出页面的样子。下面这个网站有HTML的教程:http://www.w3school.com.cn/html/
关于URL、URI、URN
HTML是被放在服务器上的,客户端如何访问HTML呢?有的人说“通过URL”、“通过URI”,可能说这个的人也不知道啥是URL、URI,下面是这三个缩写的全拼:
- URL:Uniform Resource Locator——统一资源定位
- URI:Uniform Resource Identifier——统一资源标识
- URN:Uniform Resource Name——统一资源名称
URL代表的资源的地址信息,它的结构是<协议>:<特定协议部分>,例如http://www.baidu.com、ftp://192.168.1.1/新建文件夹/test.txt
URN代表某个资源独一无二的名称,比如我的这台电脑的URN为“联想-T450s-CUSTOM-S3-123456789”,标识世界上就这一台电脑,再比如现在有一台跟我这个品牌配置相同的电脑可能叫“联想-T450s-CUSTOM-S3-987654321”。
URI是一个用于标识某一互联网资源名称的字符串。 该种标识允许用户对任何(包括本地和互联网)的资源通过特定的协议进行交互操作。URL是URI的其中一种使用方式,除此之外,URI还可以用特定的保留字符、不同的编码指定需要访问的资源,例如给某人发邮件,可以构建一个这样的URI:mailto:xxx@xxx.xx。
动态网页、静态网页
静态网页就是纯HTML的,里面的内容不会随着时空变化的网页。
动态网页就是可以根据客户端的请求的参数、时空不同,让网页在不同客户端上看到的内容有是不同的。(这是最傻瓜式的解释)
动态网页是如何动的呢?是服务器根据客户端的请求参数等信息,通过执行程序的方式来产生响应内容,然后把内容返回给客户端,因为执行的程序是百变的,所以产生的响应内容也是千变万化的。
这种服务器上的执行程序有哪些呢?目前处理动态网页的技术有CGI、PHP、ASP、Servlet/JSP。
大家注意到Servlet/JSP是写在一起的,这说明这俩是一个东西,通过后面的知识你就能知道为什么了。
Servlet/JSP简介
终于说道Servlet/JSP了,什么是Servlet?什么是JSP?
我们知道Java程序是一个个.class文件构成的,这些.class文件被运行在JVM——Java虚拟机中,所以在编写java代码时,我们知道我们写的这些代码最终能够被JVM解析、使用,有时候还需要考虑JVM如何管理Java程序中的对象。
做一个类比:Servlet/JSP运行在HTTP服务器上,而包含有Web容器的服务器才叫做HTTP服务器,所以Servlet/JSP实际运行在Web容器中,它是客户端与Servlet/JSP之间通信的桥梁。我们写的Servlet/JSP最终能够被Web容器解析、使用,有时候还需要考虑JVM如何管理Servlet/JSP中的各种对象。
重要概念:Web容器
Web容器就是定义了一大堆规范、标准,只要按照这些规范/标准来写,它就能实现特定的功能(产生静态/动态网页,实现各种效果,完成客户端和HTTP服务器之间的通信等)。而Servlet/JSP就是其中一项实现这一大堆规范和标准的技术。
你一定听过大名鼎鼎的Apache Tomcat(http://tomcat.apache.org),它就是Web容器。
那么Web容器是如何处理请求/响应的呢?下面是一个例子:
- 浏览器对Web服务器发出HTTP请求(例如访问一个网址)
- HTTP服务器收到了HTTP请求,将请求交给Web容器处理,容器解析HTTP请求的一些信息,创建各种对象(如HttpServletRequest、HttpServletResponse、HttpSession等)
- 容器根据请求的URL,将下一步处理交给指定的Servlet
- Servlet根据已经封装好的请求对象(HttpServletRequest)的信息决定如何处理,然后通过相应对象(HttpServletResponse)来创建响应(关键的一步,开发人员可以编写各种业务逻辑)
- Web容器通知HTTP服务器“响应已经创建好,并转换为HTTP响应”,并将响应传回浏览器。
Servlet与JSP的关系
终于讲到正题了,你可能急切的想用HTML写一个JSP页面看一下效果,但其实JSP不只是用HTML写的,所有的JSP页面,最终都会被web容器编译成“.class”文件,然后加载到容器之中。为什么是“.class”文件呢?因为web容器会将JSP页面首先转译为“.java”文件。这个“.java”文件通过直接或间接(直接或间接的原因,马上就能看到)的继承HttpServlet,使得这个“.java”文件成为了一个Servlet,所以不管是Servlet还是JSP页面,最终提供服务的还是Servlet实例。要掌握JSP,必须首先对Servlet有一定的了解。我们先来看看一个Servlet是怎么写的,上代码!
<span style="font-family:Microsoft YaHei;font-size:14px;">package com.web;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class SimpleServlet extends HttpServlet {
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");
out.println("<HTML>");
out.println(" <HEAD><TITLE>A Servlet</TITLE></HEAD>");
out.println(" <BODY>");
out.print(" This is ");
out.print(this.getClass());
out.println(", using the GET method, " + new java.util.Date());
out.println(" </BODY>");
out.println("</HTML>");
out.flush();
out.close();
}
}
</span>
通过上面的程序,我们注意到:首先Servlet类必须继承自HttpServlt(直接继承);然后如果要输出HTML,必须通过java的输入/输出功能,并且可以通过java代码生成动态的HTML内容(例如上面的日期)。
接下来再看一个可以达到同样效果的JSP页面长啥样:
<span style="font-family:Microsoft YaHei;font-size:14px;"><%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>A Servlet</title>
</head>
<body>
<h1><%= new java.util.Date() %></h1>
</body>
</html>
</span>
以上两段代码能达到同样的效果,但是用JSP书写起来更为简单。Servlet是通过java代码实现页面的输出和逻辑的处理,JSP是通过HTML中填充java代码实现界面的输出和逻辑的处理。那JSP是如何实现Servlet同样功能的呢?它底层的操作原理是啥?
首先我们把项目发布在Tomcat(web容器)中并启动Tomcat,然后访问以下这个JSP页面,接下来到Tomcat的根目录,找到work/Catalina/localhost目录并进入,找到你发布的工程目录并进入,在里面你会发现,你写的JSP页面被编译为xxx_jsp.class、xxx_jsp.java,xxx是你的JSP页面的名字,我们打开xxx_jsp.java:
看到了吧,这就是web容器给我们做的工作:它把JSP页面转译为了“.java”文件,这个java文件继承了HttpJspBase,而HttpJspBase继承自HttpServlet(所以说是间接继承),而HTML的输出方式与之前是一样的。
综上所述,Servlet和JSP其实是一体两面的东西,知道了这一点,对于我们以后的编程生涯会有很大的帮助,至少在我们遇到问题时,可以多一层思路解决。
MVC/Model 2的概念
讲到这里,虽然Web框架都建立在我们的Servlet/JSP基础之上,但我觉得还有一点非常重要的基础知识,那就是MVC和Model 2的概念。它是绝大部分Web框架的基础,万变不离其宗,了解MVC和Model 2之后,能让我们更进一步的认识一个web框架的实现原理。
从上面的程序中你可能注意到一个特别别扭的地方(如果你的码感强的话),那就是不管是Servlet还是JSP,都会把java代码和html混合在一起,这是非常不好的,一方面代码不容易理解,另一方面不容易维护,对于日后团队合作也是非常大的困扰。
针对这个问题,不知道是哪位大神级的前辈,通过改造MVC模式,创造了适合web使用的Model2模型。
我们首先来了解一下啥是MVC, MVC 是 Model、View、Controller的缩写,通常译作模型、视图、控制器。最早MVC模型是为实现桌面应用程序中数据的不同展现方式和更新设计的,关于MVC的详细介绍请点击这里,我们在这里只需要关心一下几点:
- 模型不会有画面相关的程序代码
- 视图负责画面相关的逻辑
- 控制器知道某个操作必须调用哪些模型
- 模型被更新后,可以通知视图做下一步操作——查询模型状态
有人认为,MVC这样的职责分配,可以套用在web应用上:
- 视图部分可以由网页来实现
- 服务器上的数据访问和业务逻辑可以由模型来负责
- 控制器接收到浏览器的请求,决定调用哪些模型来处理,并把处理结果返回给浏览器
然而这里有一个非常重要的流程走不通,那就是上面加粗标黄的第四点:模型被更新后,无法直接通知视图下一步该如何操作。造成这个的原因是web是基于HTTP的,必须基于请求/响应模型,没有请求就不会有响应,服务器也就无法“主动滴”通知浏览器做视图的改变。
基于这个问题,我们大神级的前辈提出了一个名为Model 2的解决方案,它的互动示意图如下:
这里我就不详细说了,仔细看上面的就能发现,Model 2是在MVC的基础上,HTTP请求拦截,然后控制器先从模型那里获取模型状态,然后通知视图查询模型状态,最后把视图返回给HTTP响应。理解了这个以后,你就能明白为什么我们访问一个网站的时候会有一个加载过程了,因为HTTP请求到响应的这段时间,服务器做了好多事情呀!
所谓设计模式的目的就是将变化的和不变的分离开,使用MVC使项目结构清晰了很多,搞起来似乎也更容易理解了。
Java EE
最后放一个可能很多人都不知道但是也挺重要的知识吧。
Java 代表了一个开放的平台,根据领域,划分为Java SE(Java Platform,Standard Edition)、Java ME(Java Platform,Micro Edition)、Java EE(Java Platform,Enterprise Edition)。
Java SE书初学Java的基础版本,解决标准桌面应用程序需求。Java ME面向微型装置手机PDA啥的。Java EE就牛逼多了,可以全面性解决各个领域的问题,Servlet/JSP就属于Java EE。