SERVLETJSP学习(三)—— 容器对路径的处理 、Servlet特性

1. Servlet开发要点

1.1. 重定向

1.1.1. 什么是重定向

在服务器为浏览器提供响应时,回传的数据包中的状态行里面是302状态码,同时在消息头内会增加一个键值对,名称为Location,值是一个新的URL地址。当这个响应到达浏览器的时候,这一次的请求响应过程并未结束,浏览器遇见302状态码之后,会立即按照Location头信息中指定的URL地址发送新的一个请求,这样一个在接到响应后又立即发出请求的过程叫做重定向。对于客户端用户来讲,中间的变化过程不会被察觉,因为这个过程是由浏览器自动完成的。

1.1.2. 重定向原理

在重定向的过程中,影响浏览器做出动作的关键点即响应中的状态码及Location这个消息头。302状态就像一道命令一样,使得浏览器做出新的一次请求,而请求的地址会从头信息中查找。由于这个新的请求动作是由浏览器发出的,所以浏览器的地址栏上的地址会变成Location消息头中的地址。

1.1.3. 如何重定向

由于发回的响应信息由response对象控制,所以使用如下代码即可实现重定向的过程:



  1. response.sendRedirect(String url);

该方法的参数值url即Location消息头中的重定向地址。注意,该段代码后面如果还有其他代码的话也会被继续执行的。

1.1.4. 重定向特点

由于重定向动作的执行者为浏览器,所以请求的地址可以是任意地址,哪怕是当前应用以外的应用;浏览器发出请求时一定会保持地址栏与目标地址的一致,所以发生重定向时可以从地址栏中看到地址的改变;由于整个跳转过程是在浏览器收到响应后重新发起请求,所以涉及到的Web组件并不会共享同一个request和response。

图- 1

在图 – 1中,1和4是两个完全不同的请求,如果在1号请求中曾经携带了某些表单数据,但4号这个全新请求中则不会获取到这些表单数据,也就是两次请求涉及到的Web组件不会共享request和response。

1.2. Servlet容器如何处理请求资源路径

1.2.1. 什么是请求资源路径

在地址栏中输入的请求地址中,端口号之后的部分都是请求资源路径。紧跟端口号的是部署到Web服务器上的应用名(appName),紧跟应用名的则是具体的应用内的组件路径。

1.2.2. Web服务器对请求地址的处理过程

浏览器依据地址中的IP和端口号与Web服务器建立连接,服务器会获取到请求资源路径信息。根据端口号后面的应用名找到服务器上对应的应用。默认情况下容器会认为应用名后面的是一个Servlet,所以回到web.xml文件中所有是否有与该值匹配的<url-pattern>,找到匹配的值之后再按照<servlet-name>完成对应关系的查找,进而找到要执行的Servlet。如果没有找到匹配的资源服务器就会返回404错误。

1.2.3. 匹配Servlet的规则

容器在进行url-pattern比对的时候是遵循一定的匹配原则的。这些原则主要有:

精确匹配

即具体资源名称与web.xml文件中的url-pattern严格匹配相等才执行。如,配置的内容如下:



  1. <servlet>
  2. <servlet-name>someServlet</servlet-name>
  3. <servlet-class>test.MyServlet</servlet-class>
  4. </servlet>
  5. <servlet-mapping>
  6. <servlet-name>someServlet</servlet-name>
  7. <url-pattern>/abc.html</url-pattern>
  8. </servlet-mapping>

则在地址栏中输入 http://ip:port/appName/abc.html 时,服务器就会去执行test.MyServlet这个组件,就算是在应用的根目录下的确有abc.html这个文件,也不会执行。

通配符匹配

使用“*”这个符号来匹配0个或多个字符,已达到路径的批量匹配的效果。

如配置文件中的节点为如下代码所示:



  1. <servlet>
  2. <servlet-name>someServlet</servlet-name>
  3. <servlet-class>test.MyServlet</servlet-class>
  4. </servlet>
  5. <servlet-mapping>
  6. <servlet-name>someServlet</servlet-name>
  7. <url-pattern>/*</url-pattern>
  8. </servlet-mapping>

则,在地址栏中输入以下任何地址时都是匹配成功的。



  1. http://ip:port/appName/abc.html
  2. http://ip:port/appName/abc/def/ghi.html

后缀匹配

在配置url-pattern节点时,不使用斜杠开头,用“*.”开头来匹配任意多个字符的模式叫做后缀匹配。

如配置文件中的节点为如下代码所示:



  1. <servlet>
  2. <servlet-name>someServlet</servlet-name>
  3. <servlet-class>test.MyServlet</servlet-class>
  4. </servlet>
  5. <servlet-mapping>
  6. <servlet-name>someServlet</servlet-name>
  7. <url-pattern>*.do</url-pattern>
  8. </servlet-mapping>

则,在地址栏中输入以下任何地址时都是匹配成功的。



  1. http://ip:port/appName/abc.do
  2. http://ip:port/appName/abc/def/ghi.do

在这三种匹配方式中,优先级最高的是精确匹配。如果容器在使用以上原则都不能找到相匹配的资源来执行时,就按照地址到应用中查找对应的文件。此时如果找到文件则返回,找不到资源来执行就返回404错误。

1.3. 一个Servlet实现多请求

1.3.1. 为什么要将多Servlet合并

Servlet作为Web应用中最核心的环节是因为这个组件不仅能接受请求,还能够为该请求提供响应,所以Servlet一般都会充当整个应用的控制器来进行请求的分发,为不同的请求找到对应的资源。于是程序中大多只需要一个Servlet完成这个分发工作即可,合并多个Servlet为一个Servlet会让程序的处理逻辑更加明确。

要想完成多个Servlet合并为一个Servlet,需要完成以下两个步骤:

  • 使用后缀模式完成请求资源路径的匹配
  • 分析请求资源路径中的请求目标,完成分发的动作

1.3.2. 使用后缀匹配模式完成请求资源路径的匹配

修改web.xml文件,将更多的servlet配置节点删除,只保留一个节点即可,代码如下:



  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <web-app version="2.4"
  3. xmlns="http://java.sun.com/xml/ns/j2ee"
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5. xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
  6. http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
  7. <servlet>
  8. <servlet-name>someServlet</servlet-name>
  9. <servlet-class>web.SomeServlet</servlet-class>
  10. </servlet>
  11. <servlet-mapping>
  12. <servlet-name>someServlet</servlet-name>
  13. <url-pattern>*.do</url-pattern>
  14. </servlet-mapping>
  15. </web-app>

1.3.3. 分析请求资源后分发

配置完web.xml文件后,不同请求都会发送到Web.SomeServlet来处理,要想起到分发的作用,则需要分析调过来的请求中具体的请求目标是什么。使用如下代码逻辑来完成分发动作。



  1. package web;
  2. import java.io.IOException;
  3. import javax.servlet.ServletException;
  4. import javax.servlet.http.HttpServlet;
  5. import javax.servlet.http.HttpServletRequest;
  6. import javax.servlet.http.HttpServletResponse;
  7. public class SomeServlet extends HttpServlet{
  8. public void service(HttpServletRequest request,
  9. HttpServletResponse response)     throws
  10. ServletException,IOException{
  11. //获得请求资源路径
  12. String uri = request.getRequestURI();
  13. System.out.println("uri:" + uri);
  14. if(uri.equals("/test/list.do")){
  15. System.out.println("进行员工列表的处理...");
  16. }else if(uri.equals("/test/add.do")){
  17. System.out.println("添加员工的处理...");
  18. }
  19. }
  20. }

1.4. Servlet的生命周期

1.4.1. 什么是Servlet生命周期

Servlet容器如何创建Servlet对象、如何为Servlet对象分配、准备资源、如何调用对应的方法来处理请求以及如何销毁Servlet对象的整个过程即Servlet的生命周期。

1.4.2. 生命周期的四个阶段

阶段一、实例化

实例化阶段是Servlet生命周期中的第一步,由Servlet容器调用Servlet的构造器创建一个具体的Servlet对象的过程。而这个创建的时机可以是在容器收到针对这个组件的请求之后,即用了才创建;也可以在容器启动之后立刻创建实例,而不管此时Servlet是否使用的上。使用如下代码可以设置Servlet是否在服务器启动时就执行创建。



  1. <servlet>
  2. <servlet-name>someServlet</servlet-name>
  3. <servlet-class>test/SomeServlet</servlet-class>
  4. <load-on-startup>1</load-on-startup>
  5. </servlet>
  6. <servlet-mapping>
  7. <servlet-name>someServlet</servlet-name>
  8. <url-pattern>/*</url-pattern>
  9. </servlet-mapping>

配置文件中的load-on-startup节点用于设置该Servlet的创建时机。

当其中的值大于等于0时,表示容器在启动时就会创建实例

小于0时或没有指定时,代表容器在该Servlet被请求时再执行创建

正数的值越小,优先级越高,应用启动时就越先被创建。

阶段二、初始化

Servlet在被加载实例化之后,必须要初始化它。在初始化阶段,init()方法会被调用。这个方法在javax.servlet.Servlet接口中定义。其中,方法以一个ServletConfig类型的对象作为参数。ServletConfig对象由Servlet引擎负责创建,从中可以读取到事先在web.xml文件中通过<init-param>节点配置的多个name-value名值对。ServletConfig对象还可以让Servlet接受一个ServletContext对象。

一般情况下,init方法不需要编写,因GenericServlet已经提供了init方法的实现,并且提供了getServletConfig方法来获得ServletConfig对象。

注:init方法只被执行一次。

以下代码为在servlet配置中,增加初始化参数



  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <web-app version="2.4"
  3. xmlns="http://java.sun.com/xml/ns/j2ee"
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5. xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
  6. http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
  7. <servlet>
  8. <servlet-name>someServlet</servlet-name>
  9. <servlet-class>test/SomeServlet</servlet-class>
  10. <init-param>
  11. <param-name>debug</param-name>
  12. <param-value>true</param-valule>
  13. </init-param>
  14. </servlet>
  15. <servlet-mapping>
  16. <servlet-name>someServlet</servlet-name>
  17. <url-pattern>/*</url-pattern>
  18. </servlet-mapping>
  19. </web-app>

使用以下代码可以读取Servlet配置中增加的初始化参数



  1. package test;
  2. import java.io.IOException;
  3. import javax.servlet.ServletConfig;
  4. import javax.servlet.ServletException;
  5. import javax.servlet.http.HttpServlet;
  6. import javax.servlet.http.HttpServletRequest;
  7. import javax.servlet.http.HttpServletResponse;
  8. public class SomeServlet extends HttpServlet{
  9. public void service(HttpServletRequest request,
  10. HttpServletResponse response)
  11. throws ServletException,IOException{
  12. System.out.println("SomeServlet‘s service...");
  13. ServletConfig config = getServletConfig();
  14. String debug = config.getInitParameter("debug");
  15. System.out.println("debug:" + debug);
  16. }
  17. }

阶段三、就绪

Servlet被初始化以后就处于能够响应请求的就绪状态。每个对Servlet的请求由一个ServletRequest对象代表,Servlet给客户端的响应由一个ServletResponse对象代表。当客户端有一个请求时,容器就会将请求与响应对象转给Servlet,以参数的形式传给service方法。service方法由javax.servlet.Servlet定义,由具体的Servlet实现。

阶段四、销毁

Servlet容器在销毁Servlet对象时会调用destroy方法来释放资源。通常情况下Servlet容器停止或者重新启动都会引起销毁Servlet对象的动作,但除此之外,Servlet容器也有自身管理Servlet对象的准则,整个生命周期并不需要人为进行干预。

1.4.3. Servlet接口

在ServletAPI中最重要的是Servlet接口,所有Servlet都会直接或间接的与该接口发生联系,或是直接实现该接口,或间接继承自实现了该接口的类。

该接口包括以下四个方法:

  • init(ServletConfig config)
  • service(ServletRequest req,ServletResponse res)
  • destroy( )

在最开始制定Servlet规范时,设计者希望这套规范能够支持多种协议的组件开发,所以Servlet接口是最重要的一个接口。虽然我们写的程序中编写的Servlet都是继承自HttpServlet,但本质上都是对该接口的实现,因为HttpServlet就是针对Servlet这个接口的一个抽象的实现类。可以理解为HttpServlet是支持HTTP协议的分支的一部分。设计Servlet接口中的service方法时,也是希望该方法能够处理多种协议下的请求及响应,所以参数类型是ServletRequest,而在HttpServlet这个支持HTTP协议的分支中,service方法的参数则变成了HttpServletRequest和HttpServletResponse,这两个类分别继承于ServletRequest和ServletResponse,也就是对这两个类的一个具体协议的包装,区别是增加了很多与HTTP协议相关的使用API。

制定的这种规范在实际使用中发现,并不会扩展为HTTP协议之外,所以有了过度设计的缺陷,也为在编写HTTP协议的Web应用时添加了一些不必要的操作。

1.4.4. Servlet涉及到的抽象类

Servlet API中另一个重要的类就是GenericServlet这个抽象类,它对Servlet接口中的部分方法(init和destroy)添加了实现,使得开发时只需要考虑针对service方法的业务实现即可。

HttpServlet又是在继承GenericServlet的基础上进一步的扩展,一个是public voidinit(ServletConfig config),另一个是 public void init()。他们有如下的关系: init(ServletConfig config)方法由tomcat自动调用,它读取web工程下的web.xml,将读取的信息打包传给此参数,此方法的参数同时将接收的信息传递给GenericServlet类中的成员变量config,同时调用init()。以后程序员想重写init方法可以选择init(ServletConfig
config)或者init(),但是选择init(ServletConfig config)势必会覆盖此方法已实现的内容,没有为config变量赋值,此后若是调用getServletConfig()方法返回config时会产生空指针异常的,所以想重写init(ServletConfig config)方法,必须在方法体中第一句写上 super.init(config),为了防止程序员忘记重写super.init(config)方法sun公司自动为用户生成一个public void init()的方法。GenericServlet具体的定义如下所示



  1. GenericServlet{
  2. ServletConfig config;
  3. public void init()
  4. { } //此方法什么也没做,可以说是为编程人员预留的接口
  5. public void init(ServletConfig config)
  6. {
  7. this.config=config;
  8. this.init();
  9. }
  10. getServletConfig()
  11. {
  12. return config;
  13. }
  14. }

1.5. ServletContext

1.5.1. 什么是Servlet上下文

WEB容器在启动时,它会为每个WEB应用程序都创建一个对应的ServletContext对象,它代表当前web应用,是一个全局的环境变量。该应用中的任何组件,在任何时候都可以访问到该对象,所以Servlet上下文具有唯一性。

1.5.2. 如何获得Servlet上下文

获取该对象的方式有以下四种:

  • GenericeServlet(HttpServlet)提供的 getServletContext()
  • ServletConfig提供的 getServletContext()
  • HttpSession提供的 getServletContext()
  • FilterConfig提供的 getServletContext()

1.5.3. Servlet上下文的作用及特点

Servlet上下文的作用:

  • 在Web应用范围内存取共享数据:如 setAttribute(),getAttribute()
  • 访问Web应用的静态资源:如getRealPath(String path)
  • 跨多个请求、用户和Servlet

例如,以下是两个Servlet的完整代码,实现了跨Servlet的数据共享



  1. import java.io.IOException;
  2. import java.io.PrintWriter;
  3. import javax.servlet.*
  4. import javax.servlet.http.*
  5. public class SomeServlet extends HttpServlet {
  6. public void service(HttpServletRequest request,
  7. HttpServletResponse response)
  8. throws ServletException, IOException {
  9. response.setContentType("text/html;charset=utf-8");
  10. PrintWriter out = response.getWriter();
  11. ServletContext sctx = getServletContext();
  12. sctx.setAttribute("name", "Lisa");
  13. out.close();
  14. }
  15. }
  16. import java.io.IOException;
  17. import java.io.PrintWriter;
  18. import javax.servlet.*
  19. import javax.servlet.http.*
  20. public class OtherServlet extends HttpServlet {
  21. public void service(HttpServletRequest request,
  22. HttpServletResponse response)
  23. throws ServletException, IOException {
  24. response.setContentType("text/html;charset=utf-8");
  25. ServletContext sctx =    getServletContext();
  26. String name = (String) sctx.getAttribute("name");
  27. out.close();
  28. }
  29. }

1.6. Servlet线程安全问题

1.6.1. 为什么会有线程安全问题

当浏览器访问服务器的通讯模块SomeServlet时,会启动一个线程T1来进行一系列的创建动作来处理这个请求。一般的web服务器的编程模型如下:



  1. while(flag){
  2. Socket s = ss.accept();
  3. Thread t = new Thread(s);
  4. t.start();
  5. }

如果刚好同时也有一个请求来访问SomeServlet,但是服务器只有一个servlet实例,所以服务器会启动线程T2,此时就有可能产生T1和T2同时访问someservlet的情况,如果要修改属性就会有安全隐患

1.6.2. 如何保证Servlet线程安全

使用synchronized对代码加锁即可。代码结构如下



  1. import java.io.IOException;
  2. import java.io.PrintWriter;
  3. import javax.servlet.*;
  4. import javax.servlet.*;
  5. public class SomeServlet extends HttpServlet {
  6. private int count = 0;
  7. public void service(HttpServletRequest request,
  8. HttpServletResponse response)
  9. throws ServletException, IOException {
  10. synchronized(this){
  11. count ++;
  12. try {
  13. Thread.sleep(1000);
  14. } catch (InterruptedException e) {
  15. e.printStackTrace();
  16. }
  17. System.out.println(
  18. Thread.currentThread().getName()
  19. + ":" + count);
  20. }
  21. }
  22. }
时间: 2024-10-11 17:21:06

SERVLETJSP学习(三)—— 容器对路径的处理 、Servlet特性的相关文章

Jetty学习三:配置概览-需要配置什么

上一节讲述了怎么配置Jetty,这节将告诉你使用Jetty你需要配置些什么. 配置Server Server实例是Jetty服务端的中心协调对象,它为所有其他Jetty服务端组件提供服务和生命周期管理.在标准Jetty发布中,核心的服务端配置是在etc/jetty.xml文件中,你也能在其中包含其他服务端配置,可以包括: 1)ThreadPool Server实例提供了一个线程池,你可以在etc/jetty.xml中配置最大线程数和最小线程数. 2)Handlers Jetty服务端只能有一个H

算法学习三阶段

?? 第一阶段:练经典经常使用算法,以下的每一个算法给我打上十到二十遍,同一时候自己精简代码, 由于太经常使用,所以要练到写时不用想,10-15分钟内打完,甚至关掉显示器都能够把程序打 出来. 1.最短路(Floyd.Dijstra,BellmanFord) 2.最小生成树(先写个prim,kruscal 要用并查集,不好写) 3.大数(高精度)加减乘除 4.二分查找. (代码可在五行以内) 5.叉乘.判线段相交.然后写个凸包. 6.BFS.DFS,同一时候熟练hash 表(要熟,要灵活,代码要

c++ boost库学习三:实用工具

noncopyable 大家都知道定义一个空类的时候,它实际包含了构造函数,拷贝构造函数,赋值操作符和析构函数等. 这样就很容易产生一个问题,就是当用户调用A a(“^_^") 或者A c="^_^" 时会发生一些意想不到的行为,所以很多时候我们需要禁用这样的用法. 一种方法就是把拷贝构造函数和赋值操作符显式的定义为private,但是这样需要很多代码. 于是boost库为大家提供了一个简单的方法:只需要将类继承于noncopyable就可以了. #include "

nodejs学习三 process对象

rocess对象,我说的是对象.这个对象包含的方法和属性非常的多,它向我们打开了一个通往Node.js的大门,让我们队Node.js有更多的了解. 你知道安装的Node.js的版本吗? 你知道你的Node安装在上面平台下吗? 你知道你的Node可执行文件的绝对路径吗? 你想得到你env环境变量内容吗? 上面输入的信息你可以更具体点,比如console.log(process.env.OS) 你想得到命令行上的参数吗? 好了,下面我们来写一个js.命名process.js 在命令行上运行它: 我们

Struts2框架学习(三) 数据处理

Struts2框架学习(三) 数据处理 Struts2框架框架使用OGNL语言和值栈技术实现数据的流转处理. 值栈就相当于一个容器,用来存放数据,而OGNL是一种快速查询数据的语言. 值栈:ValueStack一种数据结构,操作数据的方式为:先进后出 OGNL : Object-GraphNavigation Language(对象图形导航语言)将多个对象的关系使用一种树形的结构展现出来,更像一个图形,那么如果需要对树形结构的节点数据进行操作,那么可以使用 对象.属性 的方式进行操作,OGNL技

【挨踢人物传】马永亮:感悟学习三境界 引领马哥教育的崛起(第19期)

[编者有话]        本期的嘉宾马永亮,一次误以为是"擅长"的选择,开始结缘计算机,然而当真正接触后才发现犹如"井底之蛙",此前的擅长根本不值一提,从天堂到地狱的落差,没有挫败他的信心和追求,反而激起了他更加强烈的求知欲望,在IT的道路上不断的成长感悟-- [本期人物档案] 个人信息: 51CTO账号:马哥教育 姓名:马永亮 性别:男 所在地:河南郑州 教育信息:研究生 关键词:马哥教育创办人 Linux系统运维专家 51CTO专家博主 51CTO学院签约讲师

Jquery Easy UI初步学习(三)数据增删改

第二篇只是学了加载用datagrid加载数据,数据的增删改还没有做,今天主要是解决这个问题了. 在做增删改前需要弹出对应窗口,这就需要了解一下EasyUi的弹窗控件. 摘自:http://philoo.cnblogs.com/ 我的理解,就是panel有的属性Window.dialog都有,同时保留自己的扩展属性方法 , 所以主要展示pannel的属性. Pannel 属性 名称 类型 说明 默认值 title string 显示在Panel头部的标题文字. null iconCls strin

SERVLETJSP学习(一)——Servlet基础 、 HTTP协议

1. Servlet基础 1.1. Web应用的演变 1.1.1. 单机程序 软件从附着于电脑硬件之日起,就在不断的进行着自我完善和演变.从其使用模式的角度出发,可以简单分为单机程序和网络程序.发展到今时今日仍有大量的不依赖网络的单机程序被我们使用,如记事本.Excel.PPT.ZIP压缩等软件都是大家熟知的装机必备软件. 1.1.2. 网络程序 当电脑越来越多的参与到日常生产生活中,单机程序已经不能满足企业的需要.企业级应用要求能够最大程度的让更多的客户端参与到协同办公之中,所以依赖于网络的程

【Mac + Appium + Java1.8学习(三)】之IOS自动化环境安装配置以及简单测试用例编写(模拟器、真机)

前提条件: =========================================== 1.Xcode版本为Xcode10及以上2.Appium版本必须为1.9及以上,因为Xcode为10.0 3.appium-desktop4.安装所需依赖库,包括: a.Homebrew b.Git c.node (brew install node) d.npm (brew install npm)e.carthage (brew install carthage)f.libimobiledev