您的位置:首页 > 编程语言 > Java开发

Servlet技术详解

2016-09-27 21:16 423 查看

一、Servlet的概念

sun提供的一种动态web资源开发技术。本质上就是一段java小程序,可以将Servlet加入到Servlet容器中运行。

JSP的本质就是Servlet,开发者把JSP页面部署到Web容器中 后Web容器会将JSP编译成对应的Servlet。Java Web应用程序中所有的请求-响应都是由Servlet来完成的。Servlet是Java Web的核心程序,所有的网址最终都交给Servlet来处理。Servlet并没有main之类的执行方法。当用户访问服务器的时候,Tomcat是通过调用Servlet的某些方法来完成整个处理过程的。

Servlet容器 -- 能够运行Servlet的环境就叫做Servlet容器. --- tomcat
web容器 -- 能够运行web应用的环境就叫做web容器 --- tomcat


二、Servlet简单创建过程

写一个类实现sun公司定义的Servlet接口

import java.io.*;
import javax.servlet.*;

public class FirstServlet extends GenericServlet{
public void service(ServletRequest req, ServletResponse res) throws ServletException, java.io.IOException{
res.getOutputStream().write("My FirstServlet!".getBytes());
}
}


将写好的类配置到tomcat中的web应用的web.xml中(配置对外访问路径)

<servlet>
<servlet-name>FirstServlet</servlet-name>
<servlet-class>com.wjj.FirstServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>FirstServlet</servlet-name>
<url-pattern>/FirstServlet</url-pattern>
</servlet-mapping>


三、Servlet的调用过程/生命周期

生命周期:一件事物什么时候生,什么时候死,在生存期间必然会做的事情,所有这些放在一起就是该事物的生命周期。过程是:Servlet 加载—>实例化—>服务—>销毁

1、init():在Servlet的生命周期中,仅执行一次init()方法。它是在服务器装入Servlet时执行的,负责初始化Servlet对象。可以配置服务器,以在启动服务器或客户机首次访问Servlet时装入Servlet。无论有多少客户机访问Servlet,都不会重复执行init()。init方法用来初始化servlet资源。

2、service():它是Servlet的核心,负责响应客户的请求。每当一个客户请求一个HttpServlet对象,该对象的Service()方法就要调用,而且传递给这个方法一个“请求”(ServletRequest)对象和一个“响应”(ServletResponse)对象作为参数。在HttpServlet中已存在Service()方法。默认的服务功能是调用与HTTP请求的方法相应的do功能。

3、destroy(): 仅执行一次,在服务器端停止且卸载Servlet时执行该方法。当Servlet对象退出生命周期时,负责释放占用的资源。一个Servlet在运行service()方法时可能会产生其他的线程,因此需要确认在调用destroy()方法时,这些线程已经终止或完成。

通常不用重写init和destroy两个方法,除非需要在初始化servlet时,完成某些资源初始化的方法,才考虑重写init方法。如果需要在销毁servlet之前,先完成某些资源的回收,比如关闭数据连接等,才需要重新destroy方法。



步骤:

1. Web Client 向Servlet容器(Tomcat)发出Http请求

2. Servlet容器接收Web Client的请求

3. Servlet容器创建一个HttpRequest对象,将Web Client请求的信息封装到这个对象中。

4. Servlet容器创建一个HttpResponse对象

5. Servlet容器调用HttpServlet对象的service方法,把HttpRequest对象与HttpResponse对象作为参数传给 HttpServlet 对象。

6. HttpServlet调用HttpRequest对象的有关方法,获取Http请求信息。

7. HttpServlet调用HttpResponse对象的有关方法,生成响应数据。

8. Servlet容器把HttpServlet的响应结果传给Web Client。

四、Servlet接口的继承结构

Servlet接口
|----GenericServlet
|----HttpServlet


Servlet接口:定义了一个servlet应该具有的方法,所有的Servlet都应该直接或间接实现此接口。

GenericServlet:对Servlet接口的默认实现,通用Servlet,这是一个抽象类,其中的大部分方法都做了默认实现,只有service方法是一个抽象方法需要继承者自己实现。

HttpServlet:对HTTP协议进行了优化的Servlet,继承自GenericServlet类,并且实现了其中的service抽象方法,默认的实现中判断了请求的请求方式,并根据请求方式的不同分别调用不同的doXXX()方法。通常我们直接继承HttpServlet即可。

doGet :用于响应客户端的GET请求。

doPost:用于响应客户端的POST请求。

doPut:用于响应客户端的PUT请求。

doDelete:用于响应客户端的DELETE请求。

通常请求只有GET和POST两种,为了响应这两种请求,必须重新doGet和doPost方法。然而大部分时候Servlet对应不同请求的响应是一样的,所以可以只重新service()方法就可以响应客户端所有的请求。

五、Servlet的细节问题

利用标签注册一个Servlet

<servlet>
<servlet-name>FirstServlet</servlet-name>
<servlet-class>com.wjj.FirstServlet</servlet-class>
//注意:此处要的是一个Servlet的完整类名,不是包含.java或.class扩展的文件路径
</servlet>
<servlet-mapping>
<servlet-name>FirstServlet</servlet-name>
<url-pattern>/FirstServlet</url-pattern>
</servlet-mapping>


一个
<servlet>
可以对应多个
<servlet-mapping>


可以用
\*
匹配符配置
<serlvet-mapping>
,但是要注意:必须是
\*.do
或者/开头的以/*结束的路径。由于匹配符的引入有可能一个虚拟路径会对应多个servlet-mapping,此时哪个最像找哪个servlet,并且*.do级别最低。

可以为
<servlet>
配置
<load-on-startup>
子标签指定servlet随着服务器的启动而加载,其中配置的数值指定启动的顺序。

<servlet>
<servlet-name>invoker</servlet-name>
<servlet-class>
org.apache.catalina.servlets.InvokerServlet
</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>


缺省servlet

如果一个servlet的对外访问路径被设置为/,则该servlet就是一个缺省servlet,其他servlet不处理的请求都由它来处理。~在conf/web.xml中配置了缺省servlet,对静态资源的访问和错误页面的输出就是由这个缺省servlet来处理的。如果我们自己写一个缺省servlet把web.xml中的缺省servlet覆盖的话,会导致静态web资源无法访问。所以不推荐配置。

servlet的线程安全问题

由于通常情况下,一个servlet在内存只有一个实例处理请求,当多个请求发送过来的时候就会有多个线程操作该servlet对象,此时可能导致线程安全问题。

(1)serlvet的成员变量可能存在线程安全问题

*实验:定义一个成员变量 int i = 0;在doXXX()方法中进行i++操作并输出i值到客户端,此时由于延迟可能导致线程安全问题

(2)serlvet操作资源文件时,多个线程操作同一文件引发线程安全问题

*实验:请求带着一个参数过来,servlet将请求参数写入到一个文件,再读取该文件,将读取到的值打印到客户端上,有可能有线程安全问题

6.2 解决方法

(1)利用同步代码块解决问题。缺陷是,同一时间同步代码块只能处理一个请求,效率很低下,所以同步代码块中尽量只包含核心的导致线程安全问题的代码。

(2)为该servlet实现SingleThreadModel接口,此为一个标记接口,被标记的servlet将会在内存中保存一个servlet池,如果一个线程来了而池中没有servlet对象处理,则创建一个新的。如果池中有空闲的servlet则直接使用。这并不能真的解决线程安全问题。此接口已经被废弃。

(3)两种解决方案都不够完美,所以尽量不要在servlet中出现成员变量。

六、ServletConfig

ServletConfig代表servlet配置的对象,可以在web.xml的
<servlet>
中配置

<servlet>

<servlet-name>Demo5Servlet</servlet-name>

<servlet-class>com.wjj.Demo5Servlet</servlet-class>

<init-param>

<param-name>data1</param-name>

<param-value>value1</param-value>

</init-param>

</servlet>


然后在servlet中利用this.getServletConfig()获取ServletConfig对象,该对象提供了getInitParameter()和getInitParameterNames()方法,可以遍历出配置中的配置项。不想在servlet中写死的内容可以配置到此处。

String getServletName()  -- 获取当前Servlet在web.xml中配置的名字
String getInitParameter(String name) -- 获取当前Servlet指定名称的初始化参数的值
Enumeration getInitParameterNames()  -- 获取当前Servlet所有初始化参数的名字组成的枚举
ServletContext getServletContext()  -- 获取代表当前web应用的ServletContext对象


七、ServletContext

代表当前web应用的对象。

作为域对象使用,在不同servlet之间传递数据,作用范围是整个web应用

生命周期:当web应用被加载进容器时创建代表整个web应用的ServletContext对象。当服务器关闭或web应用被移除出容器时,ServletContext对象跟着销毁。

域:一个域就理解为一个框,这里面可以放置数据,一个域既然称作域,他就有一个可以被看见的范围,这个范围内都可以对这个域中的数据进行操作,那这样的对象就叫做域对象。

在web.xml可以配置整个web应用的初始化参数,利用ServletContext去获得

<context-param>
<param-name>param1</param-name>
<param-value>pvalue1</param-value>
</context-param>
this.getServletContext().getInitParameter("param1")
this.getServletContext().getInitParameterNames()


在不同servlet之间进行转发

this.getServletContext().getRequestDispatcher("/servlet/Demo10Servlet").forward(request, response);


方法执行结束,service就会返回到服务器,再有服务器去调用目标servlet,其中request会重新创建,并将之前的request的数据拷贝进去。

读取资源文件

5.1 由于相对路径默认相对的是java虚拟机启动的目录,所以我们直接写相对路径将会是相对于tomcat/bin目录,所以是拿不到资源的。如果写成绝对路径,当项目发布到其他环境时,绝对路径就错了。

5.2 为了解决这个问题ServletContext提供了
this.getServletContext().getRealPath("/1.properties")
,给进一个资源的虚拟路径,将会返回该资源在当前环境下的真实路径

this.getServletContext().getResourceAsStream("/1.properties")
,给一个资源的虚拟路径返回到该资源真实路径的流。

5.3 当在非servlet下获取资源文件时,就没有ServletContext对象用了,此时只能用类加载器
classLoader.getResourceAsStream("../../1.properties")
,此方法利用类加载器直接将资源加载到内存中,有更新延迟的问题,以及如果文件太大,占用内存过大。
classLoader.getResource("../1.properties").getPath()
,直接返回资源的真实路径,没有更新延迟的问题。

八、servlet作为控制器

如果使用servlet作为表现层,工作量很大,所有的HTML标签都需要使用页面输出流生成。使用servlet作为表现层主要有三个劣势:

1、开发效率低,所有HTML标签都需使用Servlet界面的开发

2、不利于团队协作开发,美工人员无法参与servlet界面的开发

3、程序可维护性差,即使修改一个按钮的标题,都必须重新编辑java代码,并重新编译
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息