Servlet

     一、Servlet简介

  • Servlet是服务器端的重要组件,直译为服务端的小程序,它属于动态资源,用来处理请求,服务器接收到请求后会调用Servlet来处理请求。
  • Servlet 的主要做用
      • 接收请求
      • 处理请求
      • 完成响应
  • 例如:
当咱们要完成一个登陆功能时,用户会将输入的用户名和密码以 POST 请求的形式发送到服务器,可是服务器自己并不具备能力来读取用户发送的用户名和密码,也就不可能对用户名和密码进行验证,因此当服务器收到这类请求后须要将请求转个一个Servlet处理。
  • Servlet 实现类由咱们编写,而由 Web 服务器(Servlet容器)调用,每一个 Servlet 都必须实现javax.servlet.Servlet
 
 
  1. HelloServlet

  • 步骤:
      1. 建立动态WEB项目WEB_Servlet
      2. 项目下建立包com.atguigu.web.servlet
      3. 包下建立一个类HelloServlet并实现javax.servlet.Servlet接口
      4. HelloServlet 的service()方法中加入一行打印语句System.out.prin tln(“hello”);
      5. WEB-INF 目录下的web .xml 文件中注册映射Servlet
        <servlet>
                 <servlet-name> HelloServlet </servlet-name>
                                       <servlet-class> com.atguigu.web.servlet .HelloServlet</servlet-class>
              </servlet>   
                               <servlet-mapping>
        <servlet-name> HelloServlet </servlet-name>
        <url-pattern>/HelloServlet</url- pattern >
       </servlet-mapping>
  1. 启动服务器,在浏览器中访问:http://localhost:8080/WEB_Servlet / HelloServlet
  • 具体代码
类:com.atdongruan.web.servlet .HelloServlet
public class HelloServlet implements Servlet {
@Override
public void init(ServletConfig config) throws ServletException {}
@Override
public ServletConfig getServletConfig() {return null;}
@Override
public void service(ServletRequest req, ServletResponse res)throws ServletException, IOException {System.out.println("hello");}
@Override
public String getServletInfo() {return null;}
@Override
public void destroy() {}
}

 

web. xml 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
    <servlet>
       <servlet-name>HelloServlet</servlet-name>
       <servlet-class>com.atdongruan.web.servlet.HelloServlet</servlet-class>
    </servlet>
    <servlet-mapping>
       <servlet-name>HelloServlet</servlet-name>
       <url-pattern>/HelloServlet</url-pattern>
    </servlet-mapping>
</web-app>/*
  •  web.xml文件详解:
    • 由于建立好的Servlet须要由Servlet容器调用,而Servlet容器并不能知道咱们所建立的Servlet的存在,因此须要在web.xml文件中进行注册。
      • <servlet></servlet>用于注册servlet
      • <servlet-name>用于设置servlet的名字,在服务器中关于servlet的一切配置都须要servlet-name来进行配置
      • <servlet-class>用于设置servlet的全类名,用于建立servlet的实例(反射)
    • 而仅仅注册是远远不够的,由于Servlet是用来处理客户端发送的请求的,因此还须要为Servlet映射一个请求地址。
      • <servlet-mapping>用于映射须要servlet处理的请求地址
      • <servlet-name>为servlet的名字,和<servlet>中的<name>有对应关系
      • <url-pattern>须要servlet处理的请求地址
      • */

 

3.1 Servlet生命周期html

  • 生命周期,以人举例的话,从人的出生到死亡称为一我的的生命周期,在人的整个生命中有不少的阶段,但总的来讲能够大体分为三个阶段:出生、工做、死亡。每一阶段都有每一阶段要作的事。对于咱们的Servlet也是同样。
  • Servlet的生命周期指的是Servlet由实例化到被销毁的过程。一样也被分为了三个阶段:实例化、处理请求、被销毁。而每一个阶段咱们都有对应的方法来实现响应的功能,在实例化阶段须要调用init()方法来作初始化操做,处理请求阶段调用service()方法处理请求,销毁对象以前调用 destroy ()作释放资源等操做。
  • Servlet生命周期相关方法
    • public void init(ServletConfig config)
    • public void service(ServletRequest req, ServletResponse res)
    • public void destroy()

 

3.1.1 Servlet初始化java

  • 服务器会在Servlet第一次处理请求、或服务器启动时建立Servlet实例,默认是在第一次处理请求时进行实例化的。
  • 对于每一个Servlet,服务器只会建立一个Servlet实例。以咱们的 HelloServlet 为例,当咱们经过浏览器访问http://localhost:8080/WEB_Servlet / HelloServlet时,服务器会根据路径/HelloServlet从配置文件中找到url-pattern值为/HelloServlet的servlet -mapping ,而后在 servlet-mapping 中找到servlet-name为 HelloServlet 。接下来,找到servlet-name为HelloServlet的< servlet >。最后,得到 servlet 的全类名,经过全类名建立类的实例,这个实例会放到一个集合中。这一过程后容器不再会对该servlet作实例化操做,而是直接从集合中获取servlet实例处理请求。
  • Servlet实例化后会当即调用public void init(ServletConfig config)方法。这里主要作一些获取配置信息等在处理请求前须要作的准备工做。
  • init() 方法在Servlet的整个生命周期中只会被调用一次。
  • init(ServletConfig config) 方法被调用时,容器会传递一个 ServletConfig 对象做为参数,该对象能够获取Servlet相关的配置信息。

3.1.2 Servlet处理请求

  • Servlet 收到请求时,服务器会调用Servlet的service()方法来处理请求。每收到一次请求就调用一次 service() 方法,因此service()方法是被屡次调用的。所以,咱们开发时主要的业务都发生在service()方法中。
  • service(ServletRequest req, ServletResponse res) 被调用时,荣器会建立一个ServletRequest和一个ServletResponse对象做为参数传递进来,ServletRequest对象主要封装了客户端发送过来的请求信息,ServletResponse对象主要用来封装了服务器响应给客户端的数据。关于这两个对象后边咱们还要详细的学习,这里就先不讨论了。
    1. Servlet销毁

  • 通常状况下Servlet对象不会被销毁,但当服务器关闭或者重启时Servlet对象也将被销毁,这时在servlet被销毁前它的destroy()方法会被调用,该方法主要作一些销毁前的收尾工做,好比:释放资源、保存数据等。在实际应用中,这类工做主要由其余对象完成因此该方法并不经常使用。
  • 思考:
    • Servlet容器是如何销毁servlet对象的?
    • 如何测试Servlet的生命周期?
  1. Servlet相关接口

  • ServletRequestServletResponse

    • ServletRequest是由容器建立并传递到service () 方法中,容器所建立的对象其实是HttpServletRequest,因此开发中咱们都会将 ServletRequest 强转成HttpServletRequest。
    • HttpServletRequest的方法:
      • String getParameter(String paramName):获取指定请求参数的值;
      • String getMethod():获取请求方法,例如GET或POST;
      • String getHeader(String name):获取指定请求头的值;
      • void setCharacterEncoding(String encoding):设置请求体的编码!由于GET请求没有请求体,因此这个方法只只对POST请求有效。当调用request.setCharacterEncoding( utf-8 )以后,再经过getParameter()方法获取参数值时,那么参数值都已经经过了转码,即转换成了UTF-8编码。因此,这个方法必须在调用getParameter()方法以前调用!
    • HttpServletResponse的方法
      • PrintWriter getWriter():获取字符响应流,使用该流能够向客户端输出响应信息。例如response.getWriter().print( <h1>Hello JavaWeb!</h1> );
      • ServletOutputStream getOutputStream():获取字节响应流,当须要向客户端响应字节数据时,须要使用这个流,例如要向客户端响应图片;
      • void setCharacterEncoding(String encoding):用来设置字符响应流的编码,例如在调用setCharacterEncoding( utf-8 );以后,再response.getWriter()获取字符响应流对象,这时的响应流的编码为utf-8,使用response.getWriter()输出的中文都会转换成utf-8编码后发送给客户端;
      • void setHeader(String name, String value):向客户端添加响应头信息,例如setHeader( Refresh ,  3;url=http://www. atguigu.com” ),表示3秒后自动刷新到http:// www. atguigu.com
      • void setContentType(String contentType):该方法是setHeader( content-type ,  xxx )的简便方法,即用来添加名为content-type响应头的方法。content-type响应头用来设置响应数据的MIME类型,例如要向客户端响应jpg的图片,那么能够setContentType( image/jepg ),若是响应数据为文本类型,那么还要台同时设置编码,例如setContentType( text/html;chartset=utf-8 )表示响应数据类型为文本类型中的html类型,而且该方法会调用setCharacterEncoding( utf-8 )方法;
      • void sendError(int code, String errorMsg):向客户端发送状态码,以及错误消息。例如给客户端发送404:response( 404, “ 您要查找的资源不存在! )。
  • ServletConfig

    • ServletConfig对象对应着web.xml文件中的一个< servlet >元素,例如:想要获取元素中的<servlet-name>的值,那么可使用servletConfig.getServletName () 方法来获取。
    • Servlet Config 对象一样有容器建立,而后做为参数传递给init()方法,能够在init()方法中使用。
    • Servlet Config 的方法:
      • String getServletName():获取Servlet在web.xml文件中的配置名称,即<servlet-name>指定的名称;
      • ServletContext getServletContext():用来获取ServletContext对象,ServletContext会在后面讲解;
      • String getInitParameter(String name):用来获取在web.xml中配置的初始化参数,经过参数名来获取参数值;
      • Enumeration getInitParameterNames():用来获取在web.xml中配置的全部初始化参数名称;
      • web.xml 中为servlet配置初始化参数
        <servlet>
        <servlet-name>HelloServlet</servlet-name>
        <servlet-class>省略</servlet-class>
        <init-param>
        <param-name>username</param-name>
        <param-value>root</param-value>
        </init-param>
        </servlet>
 
  • 每个 init-param 表示一个初始化参数,经过getInitParameter( String name )能够根据param-name的值获取到param-value的值。
  • 每一个 servlet 中能够配置多个init-param, servlet 只能获取自身的初始化参数,而不能得到其余servlet的初始化参数。
  1. GenericServlet

  • 在显示开发中咱们若是直接实现servlet接口功能也是能够正常实现的,可是所面临的问题是:若是我直接实现Servlet接口,那接口中的全部方法都必需要实现,可是这些方法有的咱们用不到,而有的方法实现起来很麻烦并且没给servlet实现的代码都差很少,因而咱们就须要一个抽象类来帮助咱们实现servlet接口,实现一些通用的方法,而咱们只须要继承 GenericServlet 就能够了,这样的好处是咱们不在须要重写所有方法,而只须要重写必须的和咱们须要的方法就能够。
  • 代码:
public abstract class GenericServlet implements Servlet, ServletConfig, java.io.Serializable{
    private transient ServletConfig config;
    public GenericServlet() { }
    public void destroy() {}
    public String getInitParameter(String name) {
                   return getServletConfig().getInitParameter(name);
    }
    public Enumeration getInitParameterNames() {
                   return getServletConfig().getInitParameterNames();
    }  
    public ServletConfig getServletConfig()  {return config;  }
    public ServletContext getServletContext() {
                   return getServletConfig().getServletContext();
    }
    public String getServletInfo() { return ""; }
    public void init(ServletConfig config) throws ServletException {
                   this.config = config;
                   this.init();
    }
    public void init() throws ServletException {}
    public void log(String msg) {
                   getServletContext().log(getServletName() + ": "+ msg);
    }
    public void log(String message, Throwable t) {
                   getServletContext().log(getServletName() + ": " + message, t);
    }
    public abstract void service(ServletRequest req, ServletResponse res)
         throws ServletException, IOException;
    public String getServletName() {  return config.getServletName(); }
}

 

  • GenericServlet中有两个重载的init()方法,因为 ServletConfig 对象是在容器调用init(ServletConfig config)方法是传过来的,只能在该方法中使用因此咱们在类中定义了一个类型为ServletConfig的对象,而在init(ServletConfig config)方法中对该变量进行赋值。这样作就会有一个问题存在,若是子类在继承父类时重写了该方法,那赋值操做将不会被调用,这时若是使用ServletConfig的方法将致使空指针异常。因此,在这个类中又定义了一个init()方法,这个方法将在init(ServletConfig config)方法中被调用,而且是在config对象赋值以后调用,而初始化的操做咱们能够经过重写config()方法来完成,这样作既保证了对ServletConfig的赋值,又能够正常作初始化操做。
  • 像这种将固定的代码编写到一个方法中,而将有可能发生变化的代码交给子类编写。其实是一种叫作模板模式的设计模式,而像 init() 这种没有方法体,而又被父类方法调用,须要子类重写的方法称为钩子方法。
  • GenericServlet 同时还实现了ServletConfig接口,实现这个接口的好处是咱们直接经过this就能够调用ServletConfig对象的方法了,而这些方法实际调用的都是,成员变量config的方法。
  1. HttpServlet

  • Http Servlet 是GenericServlet的子类,他是 Tomcat 中专门处理HttpServlet的 Servlet (实际上Tomcat只处理Http类型的请求)因此在通常状况下咱们都会经过继承 HttpServlet 来实现servlet。
  • HttpServlet和GenericServlet同样都是一个抽象类,也就是不能对它们进行实例化,与GenericServlet不一样HttpServlet中没有任何抽象方法,因此须要咱们根据实际需求来重写它的方法。
  • 在GenericServlet中service(ServletRequest req, ServletResponse res)方法是一个抽象方法,必需要由子类实现,而在HttpServlet中已经被实现,而在HttpServlet的实现的方法中会被强制转换成Http ServletRequest 和Http ServletResponse (不用担忧会出现类型转换异常,由于传过来的对象自己就是该类型)。转换类型以后会调用HttpServlet中的一个重载的service(HttpServletRequest req, HttpServletResponse resp)方法。也就是说若是咱们要重写service()方法没必要再去重写GenericServlet的service()方法了,而能够直接重写HttpServlet重载的service()方法就能够了。
  • service(HttpServletRequest req, HttpServletResponse resp) 方法中,会对它的请求类型进行判断,而后根据请求类型的不一样再去调用不一样的方法。如: post 类型的请求回去调用doPost(), get 请求回去调用doGet(),delete请求回去调用doDelete(),以此类推。可是在实际应用中咱们只会使用到doPost()和 doGet() ,因此通常状况下咱们不会去重写service()方法,而是去重写更简单的doGet()或者 doPost()
  1. Servlet扩展

 

问题1:Servlet的构造器调用了几回?web

  • 这个问题实际上很容易测试,只须要在Servlet的中写一个无参构造器,在方法中写一个打印语句,而后向该Servlet发送请求,会发现打印语句仅仅输出了一次,由此证实构造器只调用了一次。上边咱们也说过,Servlet是单实例的,而调用构造器就是用来建立实例的。因此构造器只会被调用一次。

 

问题2:Servlet是线程安全的吗?apache

  • 因为 Servlet 是单实例的,因此当容器调用service方法处理请求时是以多线程的方式调用的,可是由于性能问题因此在这个方法中并无考虑同步的问题,因此Servlet并非线程安全的,可是这样作的好处是性能较好。
  • 因为 Servlet 不是线程安全的,因此尽可能不要在使用 Servlet 处理请求时操做变量,由于有可能会出现同步的问题。(实际应用中只有很是高的并发的状况下才有可能出现这个问题,虽然如此但仍是尽可能不要那么作)。

 

问题3:Servlet实例只能在第一次请求时被建立吗?小程序

  • 通常状况下Servlet会在第一次收到请求时被建立,因此当咱们第一次访问某个Servlet时会比平时慢一些,这种咱们称为第一次惩罚。
  • 若是但愿在服务器启动时就建立Servlet的实例,能够在web.xml中进行配置,在servlet标签中还有一个load-on-startup标签,这个标签须要一个大于等于0的整数做为参数,当配置了这个属性后,该Servlet将会在服务器启动时就被建立,且值越小建立的时机越早。

问题4:url-pattern映射的规则是什么?

  • url-pattern 配置的Servlet映射的地址,他的配置规则以下:
    • 精确匹配:
      • 当前项目下指定的URL必须彻底
      • 如: /path/ServletName
      • 只有URL为:http://localhost:8080/项目名 /path/ServletName
      • 由此也可知/是表明的项目根目录,而在html中/表明的是服务器根目录
    • 路径匹配:
      • 当前项目下指定路径的URL地址
      • 如:/path/*
      • URL 为:http://localhost:8080/项目名 /path/ 任意值
    • 全匹配:
      • 当前项目下全部的 URL 地址
      • 如:/*
      • 全部URL均可以
    • 后缀匹配:
      • 当前项目下指定后缀的 URL 地址
      • 如:*.action
      • URL 为:http://localhost:8080/项目名 / 任意值 .  action
  • 优先级:
    • 一、精确匹配
    • 二、路径匹配
    • 三、全匹配
    • 四、后缀匹配
    • 五、还有一种 / ,这种也是所有匹配,优先级最低
  • 关于 *
    • * 就是通配符,匹配任意字符
    • * 只能出现前面和后面,不能出如今中间
      • 如:/*.action错误
    • * 只能出现一次
      • 如: */* 错误
    • * 不能单独出现
      • 如: *  错误

 

问题5:web.xml文件仅仅是看到的那么简单吗?设计模式

  • web.xml 文件整个项目中的配置文件,很是重要。可是纵观咱们项目下的web.xml文件彷佛内容很少,那如此重要的文件为何只配置的这么少的内容呢?实际上在 Tomcat 中还有一个总的web.xml文件,就在% CATALINA_HOME % /conf 目录下。
  • 重要配置:
  • DefaultServlet :用于处理静态资源的默认Servlet
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>default</servlet-name>
  <url-pattern>/</url-pattern>
</servlet-mapping>

  

  • JspServlet :用于处理JSP的Servlet
    <servlet>
    <servlet-name>jsp</servlet-name>
    <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
    <init-param>
    <param-name>fork</param-name>
    <param-value>false</param-value>
    </init-param>
    <init-param>
    <param-name>xpoweredBy</param-name>
       <param-value>false</param-value>
     </init-param>
     <load-on-startup>3</load-on-startup>
    </servlet>
    <servlet-mapping>
      <servlet-name>jsp</servlet-name>
      <url-pattern>*.jsp</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
      <servlet-name>jsp</servlet-name>
      <url-pattern>*.jspx</url-pattern>
    </servlet-mapping>
    

      

  • s ession的过时时间
<session-config>
        <session-timeout>30</session-timeout>
    </session-config>

 

  • MIME类型
 
  1. ServletContext

 

<mime-mapping>
<extension>123</extension>
  <mime-type>application/vnd.lotus-1-2-3</mime-type>
 </mime-mapping>

  

8.1 ServletContext简介浏览器

  • 每一个 WEB 应用服务器都会为其建立一个ServletContext对象,项目启动时ServletContext对象被建立,项目中止或从新加载时ServletContext对象被销毁
  • Servlet Context 对象主要做用就是在 Servlet 之间共享数据和加载WEB应用的配置信息,还记得ServletConfig对象能够获取到每一个Servlet的配置信息吧,而咱们的ServletContext能够或取整个WEB应用的配置信息。

 

8.2 获取ServletContexttomcat

  • Serlet 接口中,能够经过init方法中的ServletConfig调用 getServletContext() 方法来得到ServletContext对象。
  • GenericServlet和HttpServlet能够直接调用getServletContext()方法。(实际上也是 ServletConfig 的getServletContext()方法)。
  • HttpSession 对象的 getServletContext ()方法一样也能够获取。

 

8.3 域对象安全

  • ServletContext 是JavaWeb的四个域对象之一
    • PageContext
    • ServletRequest
    • HttpSession
    • ServletContext
  • 域对象主要用来存储传递数据,每一个域对象的内部都有一个map用来存储对象
  • 读取数据的方法:
    • void setAttribute(String name, Object value):用来存储一个对象,也能够称之为存储一个域属性,例如:servletContext.setAttribute( “key” ,  “value” ),在ServletContext中保存了一个域属性,域属性名称为 key ,域属性的值为 value
    • Object getAttribute(String name):用来获取ServletContext中的数据;
    • void removeAttribute(String name):用来移除ServletContext中的域属性,若是参数name指定的域属性不存在,那么本方法什么都不作;
    • Enumeration getAttributeNames():获取当前域中全部属性的名称;

 

8.4 获取当前应用的初始化参数服务器

  • ServletConfig 功能相似,ServletContext也能够获取初始化参数,这不过config获取的是当前Servlet的而context得到的是整个Web应用的;
  • 因为整个应用中都拥有同一个ServletContext因此全部Servlet都能得到相同的初始化参数。
  • web.xml 中配置初始化参数,在根标签中建立context-param元素,经过元素中param-name和param-value标签分别配置key和value,每个 context-param 表明一条键值对的初始化参数。
 
  <context-param>
    <param-name>username</param-name>
    <param-value>root</param-value>
  </context-param>
  <context-param>
    <param-name>password</param-name>
    <param-value>1234</param-value>
  </context-param>
</web-app>

  

 

8.5 获取项目根目录

  • 项目根目录就是端口号后边的第一个路径。如:http://localhost:8080 /hello /HelloServlet,红色部分/hello就是项目的根目录。这样作的目的是,由于在项目在开发时或交付后,项目名颇有可能被修改,也就是红色部分会变成其余内容,当项目根目录修改后,页面中不少资源的路径会失效,因此须要动态获取根目录。
  • 主要经过servletContext.  getContextPath() 来获取。
  • 经过 HttpServletRequest 对象也能够得到该值。

 

8.6 获取资源的流和真实路径(物理路径)

  • web 项目中,咱们访问服务器上的资源通常都是使用URL地址或者相对路径,可是有时咱们会须要获取服务器中的文件流,或者获取文件的物理地址(好比在上传或下载文件时)。
  • 获取文件流:
    • InputStream in = getServletContext().getResourceAsStream("/1.jpg");
    • 该方法会获取项目根目录下1. jpg 的输入流
  • 获取文件真实路径
    • String realPath = getServletContext().getRealPath("/1.jpg");
    • 该方法得到项目根目录下1. jpg 的路径地址
    • 注意:给方法并不会判断文件是否存在
  1. 请求和响应

    1. 请求和响应的过程

    1. HttpServletResponse对象

  • HttpServletResponse对象封装了服务器响应给客户端的信息,该对象由服务器建立,在Servlet处理请求时,服务器会调用Servlet的service方法并将HttpServletResponse对象做为参数传递。因此,咱们能够直接在service方法中使用该对象。通常咱们习惯简称他为response。
  • response的主要功能有:
    • 设置响应头信息
      • response.setHeader("Refresh", "3;URL=http://www.baidu.com")
      • response.setContentType("text/html;charset=UTF-8");
    • 设置状态码
      • response.sendError(404,"访问的资源未找到");
    • 设置响应体
      • response.getWriter().print("<h1>Hello World</h1>");
    • 重定向
      • response.sendRedirect("index.html");
    1. HttpServletRequest对象

  • HttpServletRequest对象封装了客户端发送给服务器的信息,该对象由服务器建立,在Servlet处理请求时,服务器会调用Servlet的service方法并将HttpServletRequest对象做为参数传递。因此,咱们能够直接在service方法中使用该对象。通常咱们习惯简称他为request。
  • request的主要功能有:
    • 获取请求参数
      • String username = request.getParameter("username");
    • 在请求域中读写数据
      • request.setAttribute("key1", "value1");
      • String key1 = (String) request.getAttribute("key1");
    • 获取项目名
      • String contextPath = request.getContextPath();
    • 转发请求
      • request.getRequestDispatcher("/index.html").forward(request, response);
    1. 转发和重定向

 

9.4.1 转发

  • 转发是经过 request 对象发起的,经过request对象获取一个RequestDispatcher对象,经过RequestDispatcher的forward方法发起转发。
  • 转发是在服务器内部进行的:
    • 整个过程浏览器只发送了一个请求。
    • 浏览器不能知道转发行为的发生。
    • 因为在服务器内部进行,因此转发以项目路径为根目录,输入地址时不须要输入项目名。
  • 转发是一次请求,因此request中的数据能够共享。
  • 转发只能转发到应用内部的资源,而不能转发到其余应用

 

9.4.2 重定向

  • 重定向是经过response对象发起的,经过response的sendRedirect()方法进行重定向。
  • 重定向是在浏览器中进行的:
    • 整个过程当中,浏览器发送了两次请求。
    • 浏览器知道转发行为的发生。
    • 因为在浏览器端进行,重定向的路径是以服务器目录为根目录,因此输入地址时须要输入项目名。
  • 重定向是两次请求,不能共享request中的数据。
  • 重定向不仅限定于内部资源,能够重定向到任意web资源。

 

9.5 路径问题

  • 一般咱们访问一个web应用地址格式以下:http://localhost:8080/MyWeb/HelloServlet
  • http://localhost:8080 这一部分咱们称它为服务器的根目录
  • /MyWeb  这一部分咱们称它为项目的根目录
  • /HelloServlet  这一部分是咱们Servlet的映射地址
  • 绝对路径和相对路径
    • 绝对路径:使用 /  开头的路径称为决定路径,绝对路径表示从根目录开始寻找资源。
    • 相对路径:不使用   /  开头的路径称为相对路径,相对路径表示从当前资源所在目录开始寻找资源

 

9.5.1 服务器端路径

  • 服务器端路径,主要指在Servlet中使用转发时的路径。
  • 服务器端的根目录指的是项目的根目录,也就是咱们的项目名。
  • 例如,咱们如今访问以下地址的Servlet:
    • request.getRequestDispatcher(" index.html ").forward(request, response);
  • 在实际应用中,因为咱们的资源(Servlet和JSP)所在的位置有可能会发生变更,因此一般咱们会使用绝对路径。

9.5.2 客户端路径

  • 客户端路径,主要是值在页面中引用外部资源,以及在Servlet中作重定向操做时的路径。
  • 客户端路径的根目录指的是咱们tomcat的服务器的根目录,也就是项目名前面那段路径。
  • 例1:咱们如今访问以下地址的Servlet:
    • response.sendRedirect("index.html");
  • 例2:在 MyWeb 项目中有 form.html 页面,目录结构以下:
    • webapps/MyWeb/hello/form.html
    • 如今我在form.html中建立超连接访问/ hello/HelloServlet
    • 链接格式以下:
      • <a  href=”/HelloServlet” > HelloServlet </ a >
        • 使用绝对路径,网页和重定向的根目录相同,都是服务器的根目录
        • 所以点击超连接后会访问以下地址
          • http://localhost:8080 /HelloServlet
          • 这个地址明显不对,因此应该从项目名开始写起
          • 正确以下:
            • <a  href=”/MyWeb/hello/HelloServlet” > HelloServlet </ a >
            • 点击后访问地址:
              • http://localhost:8080 /MyWeb/hello/HelloServlet
    • <a  href=”HelloServlet” > HelloServlet </a>
      • 使用相对路径,会从当前html所在目录开始寻找资源,也就是从/MyWeb/hello/开始。
      • 所以点击超连接后会访问以下地址:
        • http://localhost:8080 /MyWeb/hello/HelloServlet
  • 一样的,在实际开发中客户端的路径咱们也会使用绝对路径,而不使用相对路径。
  • 可是,这块有一个问题,在实际开发中咱们项目名有可能会改变,好比:开发中的名字可能为DMS,而实际部署时就变成了Baidu_DMS。可是这是咱们在项目中的路径是以/DMS开头的,那就意味着,咱们要把项目中全部的页面中、 Servlet 中的/DMS修改为/Baidu_DMS,如此一来工做量是十分大的,那要如何解决呢?实际上咱们能够经过request对象动态的获取项目名来解决这个问题,可是这还须要配合JSP使用,因此咱们在这里先不考虑该问题。
    1. 编码问题

 

9.5.1 为何要使用编码

9.5.2 常见的编码

  • ASKII
    • 最先的字符编码,使用一个字节中的7位来表示,一共只有128个。
  • ISO-8859-1
    • ISO 组织在ASKII基础上定义的编码,用来对其进行扩展,使用一个字节中的8位来表示,一共有256个。
  • GB2312
    • 国标码,中国规范,主要加入的许多中文。
  • GBK
    • 一样也是国标码,对 GB2312 的扩充其中添加了更多的中文。
  • GB18030
    • 也是国标码,国家强制标准,于GB2312兼容,但实际系统中应用并不普遍。
  • UTF-8
    • 万国码,支持所有字符,存储效率较高,咱们所使用的编码

 

9.5.3 请求编码

  • 请求编码是浏览器发送给服务器的编码格式。
  • 浏览器发送给服务器的请求编码主要由页面中的Content-Type响应头的编码决定,例如值为: Content-Type: text/html; charset=utf-8 ,那么页面请求将以utf-8的编码发送。那么,就要求咱们必需要将Content-Type的编码设置成utf-8。
  • 虽然浏览器发送来的编码格式已经肯定,可是还要注意咱们这里服务器中解析编码的格式并非 utf-8 还须要咱们设置。
  • Tomcat 中默认以iso解析请求,而浏览器是以utf-8发送过来请求,如此一来一定会出现乱码。因此还须要设置解析请求的编码:
    • POST 请求
      • POST请求比较简单,只须要在经过 request 对象获取请求参数以前调用request.setCharacterEncoding("utf-8")来指定编码。
    • GET请求
      • 因为 Tomcat 收到请求后默认会已iso对咱们的url在进行解码,因此若是直接设置request的编码是不行的还须要设置咱们Tomcat解析uri的默认编码。
      • 在% CATALINA_HOME%/conf/server.xml 文件中,找到<Connector />标签在标签中加入属性URIEncoding="utf-8"
< Connector  connectionTimeout = "20000"  port = "8080"  protocol = "HTTP/1.1"  redirectPort = "8443"  URIEncoding = "utf-8"  />

 

9.5.4 响应编码

  • 响应编码是服务器发送浏览器的编码格式。
  • Tomcat默认以ISO-8859-1编码响应请求,可是该编码并不支持中文,因此咱们在响应中文数据时不能使用该编码,而应该使用 UTF-8
  • 设置响应编码:
    • response.setCharacterEncoding("UTF-8");
      • 经过 setCharacterEncoding 设置的是字符输出流的编码。
  • 这里设置的只是输出的字符编码,可是做为浏览器来讲会默认已GBK去解析,因此还须要经过设置请求头告诉浏览器如何解析。
    • response.setContentType("text/html;charset=UTF-8");
  • 实际上咱们在调用response.setContentType("text/html;charset=UTF-8")时,就已经将咱们的输出编码也同时设置了,因此只须要调用此方法便可。

 

9.5.5 URL编码

  • 客户端在向服务器发送请求时须要对非英文数据进行编码操做,编码后在将请求发送服务器,而服务器在收到请求后会自动进行解码操做来解析请求。还记得咱们在 server.xml 中配置的URIEncoding= UTF-8 吧,它就是来配置 Tomcat 服务器以何种编码对URL进行解码操做的。
  • URL编码并非字符编码,而是浏览器在字符编码的基础上将其转换成试用于互联网传输的编码格式。
  • 例如 字的三中编码分别为:
    • Byte 编码:[-26, -99, -114]
    • URL 编码: %E6%9D%8E
    • 解码后:李
  • java 中可使用两个类分别进行编码和解码操做
    • 编码:import java.net.URLEncoder;
      • 例如:String encode = URLEncoder.encode("李 ","utf-8");
    • 解码 import java.net.URLDecoder;
      • 例如:String decode = URLDecoder.decode("%E6%9D%8E ","utf-8");
相关文章
相关标签/搜索