JSP/Servlet 工作原理(转载)
2014-03-08 23:20 1829人阅读 评论(0) 收藏 举报
Servlet
Servlet 没有 main 方法,不能够独立的运行,它的运行需要容器的支持,Tomcat 是最常用的 JSP/Servlet 容器。
Servlet 运行在 Servlet 容器中,并由容器管理从创建到销毁的整个过程。
对于用户到达Servlet的请求,Servlet容器会创建特定于这个请求的ServletRequest对象和 ServletResponse对象,然后调用Servlet的service方法。service方法从ServletRequest对象获得客户请求 信息,处理该请求,并通过ServletResponse对象向客户返回响应信息。
Servlet 的生命周期
首先引入维基百科的内容:
1、当servlet被部署在应用服务器中(应用服务器中用于管理Java组件的部分被抽象成为容器)以后,由容器控制servlet的生命周期。
2、除非特殊制定,否则在容器启动的时候,servlet是不会被加载的,servlet只会在第一次请求的时候被加载和实例化。
3、servlet一旦被加载,一般不会从容器中删除,直至应用服务器关闭或重新启动。但当容器做内存回收动作时,servlet有可能被删除。也正是因为这个原因,第一次访问servlet所用的时间要大大多于以后访问所用的时间。
1、servlet在服务器中的运行:1.加载 ->2.初始化 - > 3.调用 - 4.销毁
2、生命周期:servlet的生命周期在【2.初始化】后开始其生命周期,在【4.销毁】后结束其生命周期
3、一般情况下,一个servlet实例对应一种请求,此servlet实例可以处理多个用户请求。
-----------------------------
经过google发现,很多人都在争论Servlet是否属于单例模式。给出的这两个网页内容可以看出:
1、Servlet不是单例的.不过一般来说一个servlet只会有一个实例。
2、由于Servlet/JSP默认是以多线程模式执行的,所以,在编写代码时需要非常细致地考虑多线程的安全性问题。这里参考博客servlet单实例多线程模式和百度百科。
Servlet的实例过程一般如下:
当服务器接收一个客户端请求时,需要做一下四件事情。
(1) 加载和实例化
如果Servlet容器还没实例化一个Servlet对象,此时容器装载和实例化一个 Servlet。创建出该 Servlet 类的一个实例。如果已经存在一个Servlet对象,此时不再创建新实例。
(2) 初始化
在产生 Servlet 实例后,容器负责调用该 Servlet 实例的 init() 方法,在处理用户请求之前,来做一些额外的初始化工作。
(3) 处理请求
当 Servlet 容器接收到一个 Servlet 请求时,便运行与之对应的 Servlet 实例的 service() 方法,service() 方法根据用户的请求调用相对应的doGet或doPost
方法来处理用户请求。然后再进入对应的方法中调用逻辑层的方法,实现对客户的响应。
(4) 销毁
当 Servlet 容器决定将一个 Servlet 从服务器中移除时 ( 如 Servlet 文件被更新 ),便调用该 Servlet 实例的 destroy() 方法,在销毁该 Servlet 实例之前,来做一些其他的工作。
其中,(1)(2)(4) 在 Servlet 的整个生命周期中只会被执行一次。
0、如果在web.xml中对servlet配置了load-on-startup,那么就是在容器加载Servlet初始化。
- <servlet>
- <servlet-name>AAA</servlet-name>
- <servlet-class>servlet.AAA</servlet-class>
- <load-on-startup>1</load-on-startup>
- </servlet>
<servlet> <servlet-name>AAA</servlet-name> <servlet-class>servlet.AAA</servlet-class> <load-on-startup>1</load-on-startup> </servlet>
1、一般的Servlet启动后只有一个实例(对同一Servlet只配置一个mapping),如果一个Servlet有多个mapping,那么会有多个Servlet实例。
2、在web.xml文件中,某些Servlet只有<serlvet>元素,没有<servlet-mapping>元 素,这样我们无法通过url的方式访问这些Servlet,这种Servlet通常会在<servlet>元素中配置一个<load- on-startup>子元素,让容器在启动的时候自动加载这些Servlet并调用init()方法,完成一些全局性的初始化工作。也就是说,如果要通过某个网址加载某些Servlet时,需要写<servlet-mapping>元素。如果Servlet是在容器加载时运行时,则不需要写<servlet-mapping>元素.
3、当Servlet只有一个实例,当有多个客户端访问时,会多次调用已经实例化好的service方法处理请求。有时容器根据客户机请求生成Servlet对象实例或生成多个Servlet对象实例并将其加入SERVLET实例池中,来处理请求,参考讨论
Servlet 的工作原理
结合右边给出的流程图:
当客户端浏览器向服务器请求一个 Servlet 时,服务器收到该请求后,首先到容器中检索与请求匹配的 Servlet 实例是否已经存在。
--若不存在,则 Servlet 容器负责加载并实例化出该类 Servlet的一个实例对象,接着容器框架负责调用该实例的 init() 方法来对实例做一些初始化工作,然后Servlet 容器运行该实例的 service() 方法。
--若 Servlet 实例已经存在,则容器框架直接调用该实例的 service() 方法。
service() 方法在运行时,自动派遣运行与用户请求相对应的 doXX() 方法来响应用户发起的请求。
通常,每个 Servlet 类在容器中只存在一个实例,每当请求到来时,则分配一条线程来处理该请求。
在处理请求时:
1、Servlet容器会创建一个请求对象ServletRequst,其中封装了用户请求的信息,以便处理客户端请求,此外还会创建一个响应对象ServletResponse,用于响应客户端请求,想客户端返回数据。
2、然后Servlet容器把创建好的ServletRequst和ServletResponse对象传给用户所请求的Servlet。
3、Servlet利用ServletResponse包含的数据和自身的业务逻辑处理请求,并把处理好的结果写在ServletResponse中,最后Servlet容器把响应结果传给用户。
Servlet 的使用
1、自定义Servlet一般需要继承HttpServlet,而HttpServlet是继承GenericServlet,而GenericServlet是继承Servlet。
原因:HttpServlet是特定于HTTP协议的类,Servlet接口和GenericServlet是不特定于任何协议的。Servlet
接口中定义了五个方法,其中比较重要的三个方法涉及到Servlet的生命周期,分别是上文提到的init(),service(),destroy()
方法。GenericServlet是一个通用的,不特定于任何协议的Servlet,它实现了Servlet接口。而HttpServlet继承于
GenericServlet,因此HttpServlet也实现了Servlet接口。所以我们定义Servlet的时候只需要继承
HttpServlet即可。在HttpServlet中实现了service()方法,并将请求
ServletRequest,ServletResponse强转为HttpRequest和HttpResponse。
2、请求的实际处理者是doGet,doPost方法,自定义Servlet类需要重写doGet,doPost方法。
基类HttpServlet中的doGet,doPost方法并没有具体实现,只给出一些异常处理,返回的是错误信息。所以具体的处理代码我们需要自己写。
3、什么操作需要在doGet方法处理,什么操作需要在doPut方法处理?
Get方式的请求:直接在浏览器地址栏输入访问的地址所发送的请求或表单发送时没有指明post形式发送的(表单默认为get提交)都会调用doGet方法。
1)由于get方式请求会将请求参数的名和值转换成字符串,并附在原URl之后,因此可以在地址栏上看见请求参数的名和值,安全性比较差。
2)get请求的数据量比较小。
3)只能传递字符串,不能传递二进制数据
4)服务器随机接受GET方法的数据,一旦断电等原因,服务器也不知道信息是否发送完毕
Post方式的请求:表单以post方式发送。
1)POST方式发送的请求参数以及对应的值放在html header中传输,安全性相对较高。
2)post传递的数据量比较大,通常认为请求参数大小不受限制,但往往取决于服务器端的限制。
3)传递数据的类型没有限制,可以传递二进制数据
4)而且此外,Post方法接受数据时,服务器先接受数据信息的长度,然后再接受数据,使用post方式,服务器可以知道数据是否完整。
对比:图来自HTTP 方法:GET 对比 POST
常用的使用方法:
通常都使用dopost方法,之后再doGet方法调用doPost方法即可
这里给出Stack Overflow中关于Servlets: doGet and doPost的解释,以及其给出的实例代码
图3。。。
JSP 工作原理
结合右边给出的流程图:
当客户端浏览器向服务器请求一个 JSP 页面时,服务器收到该请求后,首先检查所请求的这个JSP
文件内容 ( 代码 ) 是否已经被更新,或者是否是 JSP 文件创建后的第一次被访问:
--如果是,那么,这个 JSP 文件就会在服务器端的
JSP 引擎作用下转化为一个 Servlet 类的 Java 源代码文件。紧接着,这个 Servlet 类会在 Java 编译器的作用下被编译成一个字节码文件,并装载到
jvm 解释执行。剩下的就等同于 Servlet 的处理过程了。
--如果被请求的 JSP 文件内容 ( 代码 ) 没有被修改,那么它的处理过程也等同于一个 Servlet 的处理过程。即直接由服务器检索出与之对应的
Servlet 实例来处理。
需要注意的是,JSP 文件不是在服务器启动的时候转换成 Servlet 类的。而是在被客户端访问的时候才可能发生转换的
( 如 JSP 文件内容没有被更新等,就不再发生 Servlet 转换 )。
就 Tomcat 而言,打开目录 %Tomcat%/work/%您的工程文件目录%,然后会看到里面有 3个子目录:org/apache/jsp,若没有这
3 个目录,说明项目的 JSP 文件还没有被访问过,打开进到 jsp 目录下,会看到一些 *_jsp.java 和 *_jsp.class 文件,这就是 JSP 文件被转换成
Servlet 类的源文件和字节码文件了。
有兴趣的话,可以使用浏览器访问服务器中的 JSP,然后观察 JSP 转换 Servlet 以及编译的时机。
Servlet 与 JSP
JSP 本质是一个 Servlet,它的运行也需要容器的支持。
在 JSP 和 Servlet 文件中都可以编写 Java 和 HTML 代码,不同的是,
Servlet 虽然也可以动态的生成页面内容,但更加偏向于逻辑的控制。
JSP 最终被转换成 Servlet 在 jvm 中解释执行,在 JSP 中虽然也可以编写 Java 代码,但它更加偏向于页面视图的展现。
在 MVC 架构模式中,就 JSP 和 Servlet 而言,C 通常由 Servlet 充当,V 通常由 JSP 来充当。