这次开的坑是关于java web的。不得不说这是一个大坑,因为这个主题实在太大了。经过二十几年的进化,java web已经成为了java最主要的应用方向。根据我的粗略估计,java开发中80%以上的技术是和java web相关的。因此我今天开始这个系列,不求全面,但求将从业过程中积累的一点经验尽可能准确地论述出来。尽管如此,为了追求逻辑上的完整性,我们还是尽可能的将一些相关的基础知识描述清楚,以便让更多的开发人员能够从这个系列中受益。
动态网页技术
从根源上说,java web技术是java对动态网页技术的实现。动态网页技术与静态网页技术相对应,我们先从静态网页说起。静态网页,就是以html(超文本标记语言)为核心,将供人们浏览的网页使用html编写好,放到网页服务器上。当人们在浏览器中输入网址后,浏览器直接获取并解析html页面,并展示给用户。静态网页的特点是没有后台数据库,页面上几乎不可交互(一般来讲,仅在页面上使用js做一些简单交互的网页也视为静态网页)。以静态网页不同,动态网页使用了web服务器技术,浏览器不是直接从服务器上获得html网页,而是通过http协议向服务器发送一些请求,web服务器接受请求后,通过程序做出响应,动态生成html并返回至浏览器。借助web服务器,不同用户或者同一用户不同时间可以通过浏览器可以动态获取到不同的网页内容。
用于实现动态网页的技术有很多,比如:
- php
- asp.net
- jsp
还有一些流行的技术,仍然使用html格式的网页,但是内容是通过服务器实时更新的,我们可以称之为伪静态网页,本质上也是属于动态网页的范畴,这些技术基本上是基于js的ajax的,如:
- restful风格的java web技术
- node.js
- python的django框架
总而言之呢,只要是浏览器通过http请求从服务器动态获取网页内容的技术,我们都可以认为是动态网页技术。那么回到java上,java通过一系列技术对动态网页进行了实现,这一系列技术我们统称为java web技术。由于java web技术的主要使用者是企业,我们有时将这些web开发技术统一归类为java EE (java企业应用版本)。
Servlet概述
Servlet正是上述一系列java web技术的核心。Servlet是server和applet两个单词的缩写。早期java曾经在浏览器端做了一些尝试,形成了一些称为applet的网页端小应用,后来由于实际效果不理想,java放弃了在浏览器端的尝试,转而专攻服务器端,因此发展了“服务器端的小应用”,即为Servlet。Servlet封装了整个http请求及响应的过程,由于良好的封装性,Servlet可以独立于平台和协议,但多数情况下,Servlet仅用于web容器中基于http协议的请求。
Servlet在java中被抽象为一个接口,它位于javax包中,需要引入servlet-api的jar包方可正常使用,目前最新版本为4.0。广义上,我们将实现Servlet接口的一个类称为一个Servlet。上面提到,Servlet主要用于基于http协议的请求,因此,java提供了Servlet接口的默认实现类——HttpServlet,专门用于实现对http请求的处理。
Servlet的生命周期
通过对Servlet接口和HttpServlet实现类代码的分析,我们可以清晰地看到Servlet的生命周期。Servlet生命周期主要分为三个阶段:初始化、服务期、卸载。这三个阶段分别对应init()、service()和destroy()方法。
初始化
当浏览器将请求发送至服务器时,web容器(如tomcat、jetty,web容器的概念在后续的文章中会详细讨论)会加载一个Servlet的对象,并调用其init()方法将其初始化,如果初始化失败,方法将抛出ServletException异常,而Servlet对象也会被垃圾回收器回收。
服务期
Servlet 被初始化以后,就处于能响应请求的就绪状态。接到请求时,Servlet调用service()方法对请求进行处理,service方法有两个参数,分别是ServletRequest、ServletResponse类型,分别封装了客户端的请求对象和服务器端的响应对象。如果是http请求,service方法会根据请求类型,分别调用HttpServlet类中的doGet和doPost方法,对特定类型的请求进行处理。
在服务期,service()方法可能被调用多次,web容器将每个请求封装成一个独立的线程,这些线程分别调用service()方法对请求进行处理,需要注意多线程同时处理同一对象时,有可能出现数据并发访问的错误。
卸载
当服务器不再需要Servlet实例时,会调用其destroy()方法,将其从web容器中卸载。使用这个方法,Servlet可以释放掉所有在init方法申请的资源。destroy()方法在整个生命周期中只能调用一次,一旦调用此方法,Servlet将不再能够响应请求,只能由垃圾回收器对其进行回收。如果需要再次处理相同的请求,web容器会加载新的Servlet实例。
基于Servlet的web开发
时至今日,直接使用Servlet进行web开发的场景已经几乎不存在了,因此,我们这里仅对此进行简要描述。首先是新建Servlet实现类,通常继承自HttpServlet类,根据具体需要覆盖其中的service()方法,或者doGet()或doPost()方法。第二步是配置web.xml文件,通过<servlet></servlet>和<servlet-mapping></servlet-mapping>标签分别指定servlet对应的类以及servlet能处理的url类型。如:
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>
还可以指定加载的顺序以及一些初始化参数,url类型也可以使用一部分通配符,这里不作展开。
疑问及解答
Q: 现在不直接基于Servlet进行java web开发了,为什么还要提servlet?
A: 上面提到了,Servlet是整个java web开发的核心,大多数web开发技术仍是对Servlet的进一步封装。如jsp本质上就是servlet,Spring MVC中封装了一个全局的DispatcherServlet,JSF中封装了全局的FacesServlet。Struts2比较特殊,它是对Filter(过滤器)的一个封装,但是原理与Servlet非常相似。因此可以说,只有理解了Servlet的原理和生命周期,才能更好地掌握更高级的java web开发技术。
思考与提升
想想Servlet的高度抽象性,考虑一下哪些方法放在了Servlet接口中,哪些方法放在了HttpServlet实现类中,为什么这么设计?(Servlet接口和HttpServlet实现类之间还有一层GenericServlet实现类,这个类的存在又有什么意义?)
网友评论