阅读 93

JavaWeb详解(第一篇)之Servlet基础简介

1、Serlvet概述

Servlet是运行在Web服务器应用服务器上的java程序,它是一个中间层,负责连接来自web浏览器或其他HTTP客户程序和HTTP服务器上应用程序。是sun公司提供的一门用于开发动态web资源的技术。

Servlet执行下面的任务:

1)读取客户发送的显示数据。如:html表单数据

2)读取由浏览器发送的隐式请求数据。如:http请求头

3)生成结果。

4)向客户端发送显示数据(即文档)。servlet和jsp最重要的任务就是将结果包装在文本(html)、二进制(图片)等格式的文件中。

5)发送隐式的HTTP响应数据。如:http响应头

如果想要用浏览器请求一个动态web资源,我们需要完成一下2个步骤即可:a)编写一个Java类,实现servlet接口。b)把开发好的Java类部署到web服务器中。

Servlet就是Sun公司在其API中提供了一个servlet接口(其实就是java程序),只不过该java程序要遵循servlet开发规范,他能帮我们处理浏览器发送的HTTP请求,并返回一个响应给浏览器。

2、servlet开发入门

编写第一个servlet程序。

2.1、第一个servlet程序

2.1.1、步骤一

新建一个web工程,新建一个名为:FirstServletDemo的程序,并实现Servlet接口,会要求我们实现其抽象方法。init【初始化】,destroy【销毁】,service【服务】,ServletConfig【Servlet配置】,getServletInfo【Serlvet信息】

import java.io.IOException; import java.io.OutputStream; import javax.servlet.Servlet; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse;  public class FirstServletDemo implements Servlet{      @Override     public void destroy() {         System.out.println("我是destroy,我执行了");     }          @Override     public ServletConfig getServletConfig() {         return null;     }      @Override     public String getServletInfo() {         return null;     }      @Override     public void init(ServletConfig arg0) throws ServletException {         System.out.println("我是init,我执行了");     }      @Override     public void service(ServletRequest req, ServletResponse rsp) throws ServletException, IOException {         System.out.println("我是service,我执行了");         OutputStream out = rsp.getOutputStream();         out.write("Hello Servlet!".getBytes());     }  } 复制代码

2.2、步骤二

配置tomcat的web.xml,并在其中添加如下信息。

<!--servlet标签中:     servlet-name配置servlet的名字,servlet-class配置servlet对应的java类 --> <servlet>     <servlet-name>FirstServletDemo</servlet-name>     <servlet-class>com.cn.test.FirstServletDemo</servlet-class> </servlet> <!--servlet-mapping标签中:     servlet-name配置servlet的名字,url-pattern配置访问的url     "/":开头默认代表:http://localhost:8080/ --> <servlet-mapping>     <servlet-name>FirstServletDemo</servlet-name>     <url-pattern>/FirstServletDemo</url-pattern> </servlet-mapping> 复制代码

启动tomcat服务,并在浏览器中输入:http://localhost:8080/FirstServletDemo

展示页面如下:

image-20211021155101597.png

2.2、servlet的生命周期

很多程序都有自身执行的一个事件流,servlet也一样。Servlet 运行在 Servlet 容器中,并由容器管理从创建到销毁的整个过程。

2.2.1、servlet的生命周期概述

Servlet生命周期大致可以分为:

1) 加载和实例化

如果Servlet容器还没实例化一个Servlet对象,此时容器装载和实例化一个 Servlet。创建出该 Servlet 类的一个实例。如果已经存在一个Servlet对象,此时不再创建新实例。

2)初始化 init()

当Servlet被实例化后,Tomcat会调用init()方法初始化这个对象。

3)处理请求service()

当 Servlet 容器接收到一个 Servlet 请求时,便运行与之对应的 Servlet 实例的 service() 方法来处理用户请求及对客户的响应。

4)销毁destroy()

当 Servlet 容器决定将一个 Servlet 从服务器中移除时 ( 如:Servlet 文件被更新,一个Servlet如果长时间不被使用的话,也会被Servlet 容器自动销毁 ),便调用该 Servlet 实例的 destroy() 方法。

2.2.2、servlet的生命周期特别说明

init()方法执行初始化有两种类型:常规初始化和参数化初始化。

1)常规初始化

在没有配置web.xml中配置的时候, init()只初始化一次,并且只有访问对应的Servlet的时候才去执行init()方法。

2)参数化初始化

当然,Servlet多起来了,如果想要有个加载顺序,程序员可以通过代码去实现什么时候初始化以及初始化顺序,但如果突然想要调整,这需要开发人员修改代码,为了方便,于是提供了一种可以通过参数化配置实现初始化顺序,即参数化初始化。

import java.io.IOException; import java.io.PrintWriter; import javax.servlet.Servlet; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse;  public class FirstServletDemo implements Servlet{      int num = 0;     @Override     public void destroy() {         System.out.println("我是destroy,我执行了");     }      @Override     public ServletConfig getServletConfig() {         return null;     }      @Override     public String getServletInfo() {         return null;     }      @Override     public void init(ServletConfig arg0) throws ServletException {         System.out.println("我是init,我执行了");         num = (int) (Math.random()*100);     }      @Override     public void service(ServletRequest req, ServletResponse rsp) throws ServletException, IOException {         System.out.println("我是service,我执行了");         PrintWriter out = rsp.getWriter();         out.println(num);     }  } 复制代码

修改web.xml

<servlet>     <servlet-name>FirstServletDemo</servlet-name>     <servlet-class>com.cn.test.FirstServletDemo</servlet-class>     <!--小于0:表示不执行初始化方法         大于或等于0: 当服务器启动的时候就会按照配置的顺序依次创建Servlet类的实例,并且优先级0最大。     -->     <load-on-startup>2</load-on-startup> </servlet> 复制代码

案例效果:

image-20211021165646434.png 每次启动的时候,就初始化了。再次访问时,页面直接显示71了。

配置参数化初始化的作用:

1)为web应用写一个InitServlet,这个servlet配置为启动时装载,为整个web应用创建必要的数据库表和数据。

2)完成一些定时的任务【定时写日志,定时备份数据】

3、serlvet三种开发方式

实现Servlet接口有三种方式:

1)实现Servlet接口;2)通过继承GenericServlet;3)通过继承HttpServlet

前面我们已经知道怎么使用Servlet接口了,对Servlet接口SUN公司定义了两个默认实现类:GenericServletHttpServlet

3.1、继承GenericServlet

GenericServlet是一个抽象类,它实现了Servlet接口。只需要我们重写service方法。

 public abstract class GenericServlet implements Servlet, ServletConfig, Serializable{     public abstract void service(ServletRequest req, ServletResponse res)     throws ServletException, IOException;      ......  } 复制代码

使用案例:

import java.io.IOException; import java.io.PrintWriter; import javax.servlet.GenericServlet; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse;  public class GenericServletDemo extends GenericServlet{      @Override     public void service(ServletRequest req, ServletResponse rsp) throws ServletException, IOException {         // TODO Auto-generated method stub         PrintWriter out = rsp.getWriter();         out.println("GenericServletDemo!");     } } 复制代码

3.2、继承HttpServlet

HttpServlet在实现Servlet接口时,覆写了service方法,该方法体内的代码会自动判断用户的请求方式,如为GET请求,则调用HttpServlet的doGet方法,如为Post请求,则调用doPost方法。因此,开发人员在编写Servlet时,通常只需要覆写doGet或doPost方法,而不要去覆写service方法。

public abstract class javax.servlet.http.HttpServlet extends javax.servlet.GenericServlet{     protected void service(HttpServletRequest req, HttpServletResponse resp)         throws ServletException, IOException     {         String method = req.getMethod();         if (method.equals(METHOD_GET)) {             long lastModified = getLastModified(req);             if (lastModified == -1) {                 doGet(req, resp);             } else {                 long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);                 if (ifModifiedSince < lastModified) {                     maybeSetLastModified(resp, lastModified);                     doGet(req, resp);                 } else {                     resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);                 }             }         } else if (method.equals(METHOD_HEAD)) {             long lastModified = getLastModified(req);             maybeSetLastModified(resp, lastModified);             doHead(req, resp);         } else if (method.equals(METHOD_POST)) {             doPost(req, resp);          } else if (method.equals(METHOD_PUT)) {             doPut(req, resp);            } else if (method.equals(METHOD_DELETE)) {             doDelete(req, resp);            } else if (method.equals(METHOD_OPTIONS)) {             doOptions(req,resp);          } else if (method.equals(METHOD_TRACE)) {             doTrace(req,resp);            } else {             String errMsg = lStrings.getString("http.method_not_implemented");             Object[] errArgs = new Object[1];             errArgs[0] = method;             errMsg = MessageFormat.format(errMsg, errArgs);             resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);         }     }     ...... } 复制代码

使用案例:

import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;  public class HttpServletDemo extends HttpServlet{      @Override     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {         PrintWriter out = resp.getWriter();         out.println("HttpServletDemo!");     }      @Override     protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {         super.doPost(req, resp);     }  } 复制代码

Servlet的继承体系:自定义的Servlet一般继承》抽象类HttpServlet》继承抽象类GenericServlet》继承接口Servlet。

扩展:Servlet调用过程分析

image-20211021183010233.png

4、Servlet开发细节

4.1、Servlet可以被多次映射

web.xml配置如下:

<servlet>     <servlet-name>HttpServletDemo</servlet-name>     <servlet-class>com.cn.test.HttpServletDemo</servlet-class> </servlet> <!-- 多个url映射同一个servlet --> <servlet-mapping>     <servlet-name>HttpServletDemo</servlet-name>     <url-pattern>/HttpServletDemo</url-pattern> </servlet-mapping> <servlet-mapping>     <servlet-name>HttpServletDemo</servlet-name>     <url-pattern>/HttpServletDemo/Demo2</url-pattern> </servlet-mapping> 复制代码

案例效果:

props_test10107.gif

4.2、使用通配符

在Servlet映射到的URL中也可以使用通配符,但是只能有两种固定的格式:一种格式是“ .扩展名”,另一种格式是以正斜杠(/)开头并以“/*”结尾。

4.2.1、/*结尾

image-20211021232645034.png

4.2.2、 *.扩展名

props_test10108.gif 案例分析:

假如我有:

Servlet1 映射到 /abc/*

Servlet2 映射到 /*

Servlet3 映射到 /abc

Servlet4 映射到 *.do

问题1:当请求URL为“/abc/a.html”,“/abc/ ”和“/ ”都匹配,对应的哪个servlet会响应请求?

答案1:Servlet引擎将调用Servlet1

问题2:当请求URL为“/abc/a.do”时,“/abc/ ”和“ .do”都匹配,对应的哪个servlet会响应请求?

答案2:Servlet引擎将调用Servlet1

注:在匹配的时候,要参考的标准:1)谁的匹配度高,谁就被选择;2)* .do 的优先级最低。

5、Servlet的线程安全问题

因为Servlet是单例,当多个客户端并发访问同一个Servlet时,web服务器会为每一个客户端的访问请求创建一个线程,并在这个线程上调用Servlet的service方法,因此service方法内如果访问了同一个资源的话,就有可能引发线程安全问题。

案例:创建一个ServletDemo1的servlet

import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;  public class ServletDemo1 extends HttpServlet{     int i = 0;     public void doGet(HttpServletRequest request, HttpServletResponse response)             throws ServletException, IOException {           response.setContentType("text/html;charset=gbk");           PrintWriter out = response.getWriter();           out.println("<form action='/ServletDemo1' method='post' >");           out.println("<input type='submit' value='点我' /><br />");           out.println("</form><br />");           out.println("我被点了:"+i+"次");           out.flush();           out.close();     }      public void doPost(HttpServletRequest request, HttpServletResponse response)             throws ServletException, IOException {           i++;           try {               //为了突出并发量,这里设置一个延时,量大反映慢了的时候               Thread.sleep(5000);           } catch (InterruptedException e) {               e.printStackTrace();           }           doGet(request, response);     } } 复制代码

web.xml配置:

<servlet>     <servlet-name>ServletDemo1</servlet-name>     <servlet-class>com.cn.test.ServletDemo1</servlet-class> </servlet> <servlet-mapping>     <servlet-name>ServletDemo1</servlet-name>     <url-pattern>/ServletDemo1</url-pattern> </servlet-mapping> 复制代码

案例效果:

props_test10110.gif 我们发现在不同的客户端操作,共享变量i的值也被共享了。右边先点一次,左边再点两次,还未等右边反映过来,变量i其实已经发生了变化,所以左边也出现了3次,实际只点了一次。

5.1、解决线程安全问题

1)如果一个变量需要多个用户共享,则应当在访问该变量的时候,加同步机制:synchronized (对象){..}

2)如果一个变量不需要共享,则直接在 doGet() 或者 doPost()定义.这样不会存在线程安全问题。(实例变量线程共享,局部变量线程私有)

方案一:使用synchronized关键字

import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;  public class ServletDemo1 extends HttpServlet{     int i = 0;     public void doGet(HttpServletRequest request, HttpServletResponse response)             throws ServletException, IOException {           response.setContentType("text/html;charset=gbk");           PrintWriter out = response.getWriter();           out.println("<form action='/ServletDemo1' method='post' >");           out.println("<input type='submit' value='点我' /><br />");           out.println("</form><br />");           out.println("我被点了:"+i+"次");           out.flush();           out.close();     }      public void doPost(HttpServletRequest request, HttpServletResponse response)             throws ServletException, IOException {         synchronized (this){           i++;           try {               //为了突出并发量,这里设置一个延时,量大反映慢了的时候               Thread.sleep(5000);           } catch (InterruptedException e) {               e.printStackTrace();           }           doGet(request, response);     }     } } 复制代码

使用synchronized 关键字能保证一次只有一个线程可以访问被保护的区段,仅解决了左右次数显示问题。但是并未实质上解决变量共享问题。

方案二:使用SingleThreadModel接口,Servlet引擎将以单线程模式来调用其service方法

public class ServletDemo1 extends HttpServlet implements SingleThreadModel{     int i = 0;     public void doGet(HttpServletRequest request, HttpServletResponse response)             throws ServletException, IOException {           response.setContentType("text/html;charset=gbk");           PrintWriter out = response.getWriter();           out.println("<form action='/ServletDemo1' method='post' >");           out.println("<input type='submit' value='点我' /><br />");           out.println("</form><br />");           out.println("我被点了:"+i+"次");           out.flush();           out.close();     }      public void doPost(HttpServletRequest request, HttpServletResponse response)             throws ServletException, IOException {           i++;           try {               //为了突出并发量,这里设置一个延时,量大反映慢了的时候               Thread.sleep(5000);           } catch (InterruptedException e) {               e.printStackTrace();           }           doGet(request, response);     } } 复制代码

案例效果:

props_test10111.gif SingleThreadModel以另一种方式(单线程模式调用)解决多线程并发问题,而真正意义上解决多线程安全问题是指一个Servlet实例对象被多个线程同时调用的问题。事实上,在Servlet API 2.4中,已经将SingleThreadModel标记为Deprecated(过时的)。

真正避免线程安全问题的:可以将变量写在方法里,局部变量为线程私有,则不存在线程安全问题。

6、ServletConfig和ServletContext详解

6.1、ServletConfig详解

Servlet给我们提供了一个ServletConfig对象,该对象主要用于读取Servlet的配置信息。允许我们将配置信息写在web.xml中。

web容器(Tomcat)在创建servlet实例对象时,会自动将这些初始化参数封装到ServletConfig对象中,并在调用servlet的init方法时,将ServletConfig对象传递给servlet。

返回类型方法&描述
java.lang.StringgetInitParameter(java.lang.String name) 返回一个 String包含值指定初始化参数,或 null如果参数不存在。
java.util.EnumerationgetInitParameterNames() 返回servlet的名称的初始化参数作为一个 Enumeration的 String对象,或一个空 Enumeration如果servlet没有初始化参数。
ServletContextgetServletContext() 返回一个正在执行ServletContext 的引用.
java.lang.StringgetServletName() 返回这个servlet实例的名称.

ServletConfig类的三大作用:

1)可以获取Servlet程序的别名servlet-name的值;2)获取初始化参数init-param;3)获取ServletContext对象。

使用案例:

import java.io.IOException; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;  public class ServletConfigDemo extends HttpServlet{      @Override     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {         // TODO Auto-generated method stub         doPost(req,resp);     }      @Override     protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {         resp.setContentType("text/html");         //ServletConfig对象是整个Servlet         String charset = this.getServletConfig().getInitParameter("charset");         String sname = this.getServletConfig().getServletName();         resp.getWriter().println(charset);//输出: GBK         resp.getWriter().println(sname);//输出: ServletConfigDemo     }      @Override     public void init(ServletConfig config) throws ServletException {         super.init(config);         //可用于启动时初始化时连接数据库         System.out.println(config.getInitParameter("username")); //控制台输出:root         System.out.println(config.getInitParameter("url")); //控制台输出:jdbc:mysql://localhost:3306/test     }  } 复制代码

web.xml配置

<servlet>     <servlet-name>ServletConfigDemo</servlet-name>     <servlet-class>com.cn.test.ServletConfigDemo</servlet-class>     <!-- 初始化参数 -->     <init-param>         <param-name>charset</param-name>         <param-value>GBK</param-value>     </init-param>     <init-param>         <param-name>username</param-name>         <param-value>root</param-value>     </init-param>     <init-param>         <param-name>url</param-name>         <param-value>jdbc:mysql://localhost:3306/test</param-value>     </init-param> </servlet> <servlet-mapping>     <servlet-name>ServletConfigDemo</servlet-name>     <url-pattern>/ServletConfigDemo</url-pattern> </servlet-mapping> 复制代码

6.2、ServletContext 详解

ServletConfig中返回的ServletContext对象的引用。这个对象是干嘛的呢?ServletConfig是对应的单个servlet,而ServletContext对象,代表的是当前web应用,它会为每个WEB应用程序都创建一个对应的ServletContext对象。

ServletContext对象被包含在ServletConfig对象中,可以通过ServletConfig.getServletContext方法获得对ServletContext对象的引用。Servlet对象之间可以通过ServletContext对象来实现通讯。ServletContext对象通常也被称之为context域对象(可以像Map一样存取数据:setAttribute、getAttribute、removeAttribute)。

ServletContext 是当web应用启动的时候,自动创建,当web应用关闭/tomcat关闭/对web应用reload,会造成servletContext销毁。

ServletContext的主要作用:

1)获取web.xml中配置的上下文参数context-param;(getInitParameter(name))

2)获取当前工程路径,格式:/工程路径;

3)获取工程部署在服务器上的绝对路径;

4)像Map一样存取数据。

5)读取资源文件。

6.2.1、读取配置文件及获取路径

使用案例一:

import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;  public class ServletContextDemo extends HttpServlet{      @Override     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {         // TODO Auto-generated method stub         doPost(req,resp);     }      @Override     protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {         resp.setContentType("text/html;charset=gbk");         //ServletConfig对象是整个Servlet         ServletContext sc = this.getServletConfig().getServletContext();         String username = (String) sc.getInitParameter("username");         PrintWriter out = resp.getWriter();         out.println("获取的上下文配置getInitParameter(username):"+username);//输出:context         //如果在:server.xml中的Context中没有配置path,则此处获取到的getContextPath()为""         out.println("<br/>当前工程路径getContextPath():"+sc.getContextPath());//         out.println("<br/>当前工程部署路径getRealPath():"+sc.getRealPath("/"));//         //设置上下文配置         sc.setAttribute("password", "test");     } } 复制代码

web.xml配置:

<context-param>     <description>我是整个工程的配置</description>     <param-name>username</param-name>     <param-value>context</param-value> </context-param>  <servlet>     <servlet-name>ServletContextDemo</servlet-name>     <servlet-class>com.cn.test.ServletContextDemo</servlet-class> </servlet> <servlet-mapping>     <servlet-name>ServletContextDemo</servlet-name>     <url-pattern>/ServletContextDemo</url-pattern> </servlet-mapping> 复制代码

server.xml配置:

<Context docBase="JspWeb" path="/JspWeb" reloadable="true" source="org.eclipse.jst.jee.server:JspWeb"/></Host> 复制代码

案例效果:

image-20211022111258705.png

6.2.2、存取数据

使用案例二:修改下ServletConfigDemo

import java.io.IOException; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;  public class ServletConfigDemo extends HttpServlet{      @Override     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {         // TODO Auto-generated method stub         doPost(req,resp);     }      @Override     protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {         ServletContext sc = this.getServletConfig().getServletContext();         String password = (String) sc.getAttribute("password");         resp.getWriter().println(password);//     }  } 复制代码

案例效果:能取到ServletContextDemo中设置的值

image-20211022111646928.png

6.2.3、读取资源文件

使用案例三:读取资源文件

import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.util.Properties; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;  public class ServletContextDemo2 extends HttpServlet{     @Override     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {         // TODO Auto-generated method stub         doPost(req,resp);     }      @Override     protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {         //首先读取到文件         resp.setContentType("text/html;charset=gbk");         PrintWriter out = resp.getWriter();         //文件放到WEB-INF目录下         InputStream in=this.getServletContext().getResourceAsStream("/WEB-INF/info.properties");         //文件放到src目录下 //        ClassLoader classLoader = ServletContextDemo2.class.getClassLoader(); //        InputStream in =  classLoader.getResourceAsStream("info.properties");         Properties pro=new Properties();         pro.load(in);         out.println("username="+pro.getProperty("username")+"<br />");         out.println("password="+pro.getProperty("password")+"<br />");         out.flush();         out.close();     } } 复制代码

info.properties

username=root password=123456 复制代码

7、HttpServletRequest、HttpServletResponse详解

前面我们看到HttpServlet中的service(doGet或doPost)方法中接收两个参数:service(HttpServletRequest req, HttpServletResponse resp)。下面分别介绍下这两个对象。

7.1、HttpServletRequest

HttpServletRequest对象代表客户端的请求。当客户端通过HTTP协议访问Tomcat服务器时,服务器救护把请求过来的HTTP请求头中的所有信息都封装到Request这个对象中,然后传递给service方法(doGet或doPost)中给我们使用。开发人员通过这个对象的方法,可以获得客户这些信息。

常用API:

返回类型方法描述
java.lang.StringgetMethod()返回这个请求使用的HTTP方法(例如:GET、POST、PUT)
HttpSessiongetSession() getSession(boolean create)获取到当前客户端请求的session对象, 如果不存在,则创建。
java.lang.StringgetParameter(String name)获取客户端提交的数据
java.lang.String[]getParameterValues(String name)获取所有参数
java.util.MapgetParameterMap()获取到客户端传递过来装有数据的map集合
java.lang.ObjectgetAttribute(String name)获取到上一个页面传递过来的键值对
voidsetAttribute(String name, Object o)将键值对设置到request中传递到下一个Servlet或者页面
voidsetCharacterEncoding(String env)设置请求的字符集
RequestDispatchergetRequestDispatcher(String path)当处理完用户的请求之后:请求跳转到xxx页面(请求转发)
java.lang.StringBuffergetRequestURL()返回客户端发出请求时的完整URL
java.lang.StringgetQueryString()返回请求行中的参数部分
java.lang.StringgetHeader(String name)获取请求头参数

使用案例:

import java.io.IOException; import java.io.PrintWriter; import java.util.Arrays; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;  public class ReqServletDemo extends HttpServlet{          public void doGet(HttpServletRequest request, HttpServletResponse response)             throws ServletException, IOException {           response.setContentType("text/html;charset=gbk");           PrintWriter out = response.getWriter();           out.println("<form action='/ReqServletDemo' method='post' >");           out.print("<input type='txt' name='username' /><br /><br />");           out.print("<input type='checkbox' name='hobby' value='java'/>java<br />");           out.print("<input type='checkbox' name='hobby' value='python'/>python<br />");           out.print("<input type='checkbox' name='hobby' value='html'/>html<br />");           out.println("<input type='submit' value='提交' /><br />");           out.println("</form><br />");           out.flush();           out.close();     }      public void doPost(HttpServletRequest request, HttpServletResponse response)             throws ServletException, IOException {         //设置请求响应字符编码,默认是ISO-8859-1,不设置的就会出现乱码         request.setCharacterEncoding("GBK");         //response.setCharacterEncoding("GBK");         //推荐使用下面这种         response.setHeader("Content-Type","text/html;charset=GBK");                  //回显请求数据         PrintWriter out = response.getWriter();         out.println("url:"+request.getRequestURL().toString());//         out.println("User-Agent:"+request.getHeader("User-Agent"));//         out.println("获取请求参数username:"+request.getParameter("username"));//         out.println("获取请求参数hobby:"+Arrays.asList(request.getParameterValues("hobby")));//     }  }  复制代码

案例效果: props_test10112.gif

7.1.1、RequestDispatcher请求转发

HttpServletRequest对象返回RequestDispatcher对象,叫做请求转发:服务器收到请求后,从一次资源跳转到另一个资源的操作。

首先我们在WEB-INF目录下方一张图片,然后在浏览器中输入:http://localhost:8080/WEB-INF/logo.jpg,去访问,会出现如下页面。默认浏览器是不能直接访问WEB-INF目录的。

image-20211023185913993.png 使用案例:

import java.io.IOException; import java.io.PrintWriter; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;  public class ReqServletDemo extends HttpServlet{          public void doGet(HttpServletRequest request, HttpServletResponse response)             throws ServletException, IOException {           response.setContentType("text/html;charset=gbk");           PrintWriter out = response.getWriter();           out.println("<form action='/ReqServletDemo' method='post' >");           out.print("<input type='txt' name='username' /><br /><br />");           out.print("<input type='checkbox' name='hobby' value='java'/>java<br />");           out.print("<input type='checkbox' name='hobby' value='python'/>python<br />");           out.print("<input type='checkbox' name='hobby' value='html'/>html<br />");           out.println("<input type='submit' value='提交' /><br />");           out.println("</form><br />");           out.flush();           out.close();     }      public void doPost(HttpServletRequest request, HttpServletResponse response)             throws ServletException, IOException {         //请求转发         RequestDispatcher rd = request.getRequestDispatcher("/WEB-INF/logo.jpg");         //前进到转发的目录:正常浏览器是访问不到WEB-INF目录的资源的,但是请求转发可以         rd.forward(request, response);     }  } 复制代码

案例效果:

props_test10113.gif 请求转发的特点:

1)浏览器地址栏没有变化,他们是一次请求;

2)共享Request域中的数据;

3)可以转发到WEB-INF目录下的资源,但是不可以访问工程以外的资源。

使用案例二:refresh的应用

我们经常看到一个网站贴出即将跳转到哪里,5秒后没跳转请点击链接。

import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;  public class RspServletDemo extends HttpServlet{          public void doGet(HttpServletRequest request, HttpServletResponse response)             throws ServletException, IOException {           response.setContentType("text/html;charset=gbk");           PrintWriter out = response.getWriter();           out.println("<form action='/first' method='post' >");           out.println("<input type='submit' value='跳转' /><br />");           out.println("</form><br />");           out.flush();           out.close();     }      public void doPost(HttpServletRequest request, HttpServletResponse response)             throws ServletException, IOException {         response.setHeader("refresh", "3;url='https://www.baidu.com/'");         //页面跳转         request.getRequestDispatcher("first.jsp").forward(request, response);     } } 复制代码

web.xml

<servlet-mapping>     <servlet-name>RspServletDemo</servlet-name>     <url-pattern>/RspServletDemo</url-pattern> </servlet-mapping> <servlet-mapping>     <servlet-name>RspServletDemo</servlet-name>     <url-pattern>/first</url-pattern> </servlet-mapping> 复制代码

first.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html>   <head>     <title>RequestDispatcher</title>   </head>   <body>       3秒后跳转百度       <a href="https://www.baidu.com/">未跳转请点击</a>   </body> </html> 复制代码

案例效果: props_test10114.gif 虽然不可以访问外部资源,但是我们可以使用设置响应头response.setHeader("refresh", "3;url='www.baidu.com/'");的方式,让浏览器跳转到指定的url。

7.2、HttpServletResponse

HttpServletResponse对象服务器的响应。这个对象中封装了向客户端发送数据、发送响应头,发送响应状态码的方法。

常用API:

返回类型方法描述
voidsetCharacterEncoding(String charset)设置响应客户端信息编码的编码方式
ServletOutputStreamgetOutputStream()返回一个 ServletOutputStream 适合在响应中写入二进制数据的
java.io.PrintWritergetWriter()返回一个PrintWriter对象,可以发送字符的文本给客户端
voidsetHeader(String name, jString value)设置一个响应头和给定的名称和值
voidsetStatus(int sc)设置状态代码响
voidsendRedirect(String location)发送一个临时重定向响应给客户端使用指定URL重定向的位置
7.2.1、getOutputStream和getWriter的区别

字符流:getWriter() 用于向客户机回送字符数据

字节流:getOutputStream() 回送字节数据(二进制数据,常用于下载),当然它也可以回送字符,但是没有PrintWriter对象 ,效率高。

OutputStream os=response.getOutputStream();

os.write("hello,world".getBytes());

注:两个流同时只能使用一个,Web服务器会自动检查并关闭流。

使用案例:

import java.io.IOException; import java.io.OutputStream; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;  public class RspServletDemo2 extends HttpServlet{      public void doGet(HttpServletRequest request, HttpServletResponse response)             throws ServletException, IOException {           response.setContentType("text/html;charset=gbk");           PrintWriter out = response.getWriter();           out.println("<form action='/first' method='post' >");           out.println("<input type='submit' value='跳转' /><br />");           out.println("</form><br />");           OutputStream out2 = response.getOutputStream();           out2.write("hello,world".getBytes());     } } 复制代码

案例效果:

image-20211024110028091.png

7.2.2、sendRedirect重定向的使用

请求重定向是指:一个web资源收到客户端请求后,通知客户端去访问另外一个web资源,这称之为请求重定向。

实现方式一:设置Location(不推荐)

import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;  public class sendRedirectDemo1 extends HttpServlet{      public void doGet(HttpServletRequest request, HttpServletResponse response)             throws ServletException, IOException {           response.setContentType("text/html;charset=gbk");           PrintWriter out = response.getWriter();           out.println("<form action='/sendRedirectDemo1' method='post' >");           out.println("<input type='submit' value='设置Location' /><br />");           out.println("</form><br />");           out.flush();           out.close();     }      public void doPost(HttpServletRequest request, HttpServletResponse response)             throws ServletException, IOException {         response.setStatus(302);         response.setHeader("Location", "http://localhost:8080/");     } } 复制代码

案例效果:

props_test10115.gif 实现方式二:请求重定向,效果一样

import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;  public class sendRedirectDemo2 extends HttpServlet{      public void doGet(HttpServletRequest request, HttpServletResponse response)             throws ServletException, IOException {           response.setContentType("text/html;charset=gbk");           PrintWriter out = response.getWriter();           out.println("<form action='/sendRedirectDemo2' method='post' >");           out.println("<input type='submit' value='设置Location' /><br />");           out.println("</form><br />");           out.flush();           out.close();     }      public void doPost(HttpServletRequest request, HttpServletResponse response)             throws ServletException, IOException {         response.sendRedirect("http://localhost:8080/");     } } 复制代码

请求重定向的特点:

1)浏览器地址会发送变化;

2)两次请求,不共享Request域中的数据;

3)不能访问WEB-INF下的资源,可以访问工程外的资源。

总结:请求重定向(sendRedirect)和请求转发(forward)的区别

  • RequestDispatcher.forward方法只能将请求转发给同一个WEB应用中的组件;而HttpServletResponse.sendRedirect 方法还可以重定向到同一个站点上的其他应用程序中的资源,甚至是使用绝对URL重定向到其他站点的资源。

  • 调用HttpServletResponse.sendRedirect方法重定向的访问过程结束后,浏览器地址栏中显示的URL会发生改变,由初始的URL地址变成重定向的目标URL;调用RequestDispatcher.forward 方法的请求转发过程结束后,浏览器地址栏保持初始的URL地址不变。

  • RequestDispatcher.forward方法的调用者与被调用者之间共享相同的request对象和response对象,它们属于同一个访问请求和响应过程;而HttpServletResponse.sendRedirect方法调用者与被调用者使用各自的request对象和response对象,它们属于两个独立的访问请求和响应过程。


作者:穆瑾轩
链接:https://juejin.cn/post/7056352963010723871

文章分类
代码人生
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐