JavaWeb(二):Servlet

一、本教程使用的Web容器——Tomcat

Tomcat是提供一个支持Servlet和JSP运行的容器。Servlet和JSP能根据实时需要,产生动态网页内容。而对于Web服务器来说, Apache仅仅支持静态网页,对于支持动态网页就会显得无能为力;Tomcat则既能为动态网页服务,同时也能为静态网页提供支持。尽管它没有通常的Web服务器快、功能也不如Web服务器丰富,但是Tomcat逐渐为支持静态内容不断扩充。大多数的Web服务器都是用底层语言编写如C,利用了相应平台的特征,因此用纯Java编写的Tomcat执行速度不可能与它们相提并论。一般来说,大的站点都是将Tomcat与Apache的结合,Apache负责接受所有来自客户端的HTTP请求,然后将Servlets和JSP的请求转发给Tomcat来处理。Tomcat完成处理后,将响应传回给Apache,最后Apache将响应返回给客户端。

1.1 Tomcat访问静态资源

tomcat访问所有的资源,都是用Servlet来实现的。

在Tomcat看来,资源分3种:

  • 静态资源,如css,html,js,jpg,png等     
  • Servlet   
  • JSP

运行应用程序的Web容器将会有一个或多个内建的Servlet,这些Servlet用于处理JavaServer Pages、显示目录列表(如果启用了该功能的话)和访问静态资源,例如HTML页面和图片。

JspServlet用于处理JSP页面,DefaultServlet是处理静态资源的Servlet,在tomcat的conf目录下web.xml:

<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>
        <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>

    <!-- The mapping for the default servlet -->
    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!-- The mappings for the JSP servlet -->
    <servlet-mapping>
        <servlet-name>jsp</servlet-name>
        <url-pattern>*.jsp</url-pattern>
        <url-pattern>*.jspx</url-pattern>
    </servlet-mapping>

1.2 安装tomcat

配置jdk环境变量

下载,解压,目录结构如下:

bin目录 可执行文件,如启动tomcat

conf目录 配置文件

lib目录 第三方依赖的jar包

logs目录 日志

temp目录 临时文件

webapps目录 部属的web应用

work目录 jsp翻译成servlet再翻译成class的文件

双击bin中的startup.bat可以启动tomcat

若已经启动一个tomcat应用,再启动同一个tomcat应用,会抛出端口占用异常

用bin中的shutdown.bat可以关闭tomcat

修改端口号

server.xml

<Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>

1.3 Tomcat中JavaWeb目录结构

1.3.1 目录结构

创建的Web项目,必须有一个根目录,根目录下:

  • *.html、*.jsp等外部可以公开访问的文件,看成公开目录
  • WEB-INF/web.xml,必须要有, 部属描述符
  • WEB-INF/classes,编译后的Java类文件,可选
  • WEB-INF/lib,Java类库文件

WEB-INF/目录中的文件是受到保护的,不能通过URL访问。

1.3.2 手工创建一个web项目

用eclipse创建一个普通java工程

创建com.aidata包,并创建Person类

复制代码
package com.aidata;

public class Person {

    public String getPersonInfo() {
        return "person info...";
    }
}
复制代码

下面手工创建tomcat项目目录

创建WebContent根目录

创建WEB-INF目录

创建目录WEB-INF/classes,创建com文件夹,com下创建aidata文件夹,将eclipse的bin目录下生成的Person.class拷贝过来

创建目录WEB-INF/lib

从tomcat拷贝一个web.xml到WEB-INF目录下

在根目录下创建一个jsp页面

复制代码
<%@page import="com.aidata.Person"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

    <%
      Person person = new Person();
      System.out.println(person.getPersonInfo());
    %>
    
</body>
</html>
复制代码

将根目录拷贝到tomcat的webapps目录下

启动tomcat

浏览器输入http://localhost:8080/WebContent/hello.jsp,命令行窗口会打印

Tomcat8 IDEA 乱码

IDEA控制台乱码:找到IDEA的 idea64.exe.vmoptions 文件,添加  -Dfile.encoding=UTF-8 

浏览器乱码:确保html或jsp文件保存的编码格式为UTF-8,可以notepad++转为utf-8编码格式

 二、了解Servlet

Java Web项目的构成:

  • 需要自己的代码和它依赖的第三方库
    • Servlet、JSP、过滤器、监听器......
    • HTML、JS、CSS......
  • 部属描述符,其中包含了部属和启动应用程序的指令
  • 可以添加ClassLoader用于将自己的应用程序与同一台服务器上的其他Web应用程序隔离
  • 通过某种方式将应用程序打包,生成WAR和EAR文

Servlet是JavaEE规范的一种,主要是为了扩展Java作为Web服务的功能。Servlet就是一群人来制定java应用中使用web时的各种规范,统一接口,其他内部实现由厂商自己实现,tomcat、jetty、jboss等等应运而生,面向接口编程。我们讲Servlet,默认是说Servlet实现,Servlet的实现是一个运行在Web服务器中的Java小程序,接收和响应来自Web客户端的请求,使用HTTP进行通信。

可以这么理解,JCP指定规范,提供接口,Web容器厂商实现了Web接收、响应等功能,我们实现具体的业务逻辑功能。

2.1 Servlet容器与Servlet

Servlet容器为Java Web应用提供运行时环境,它负责管理Servlet和JSP的生命周期,以及管理它们的共享数据,从下图可知,客户Web浏览器只与Web容器打交道。

Servlet生命周期相关方法,这些方法都是Servlet容器负责调用:

  • 构造器:第一次请求Servlet时,创建Servlet实例,调用构造器,只被调用一次,单例
  • init方法:只被调用一次,在创建好实例后立即被调用,用于初始化当前Servlet
  • service:被多次调用,每次请求都会调用,实际用于响应请求
  • destroy:只被调用一次,在当前Servlet所在的WEB应用被卸载前调用,用于释放当前Servlet所占用的资源

 

 Servlet容器响应客户请求:

 2.2 Servlet的API

API包含了两个软件包,十二个接口和九个类。

软件包:javax.servlet

   所包含的接口:RequestDispatcher;Servlet;ServletConfig;ServletContext;ServletRequest;ServletResponse;SingleThreadModel。

   所包含的类:GenericServlet;ServletInputStream;ServletOutputStream;ServletException;UnavailableException。

软件包:javax.servlet.http

       所包含的接口:HttpServletRequest;HttpServletResponse;HttpSession;HttpSessionBindingListener;HttpSessionContext。

       所包含的类:Cookie;HttpServlet;HttpSessionBindingEvent;HttpUtils。

 

三、创建第一个Servlet——只实现Servlet接口的最简单Servlet

 步骤:

  • 新建web项目
  • 在src中新建包,包下新建类,该类实现Servlet接口,重写要实现的方法
  • 配置web.xml

 3.1 创建Web项目

3.1.1 Eclipse

不使用Mavenhttps://www.cnblogs.com/greenteaone/p/7929908.html

使用Mavenhttps://blog.csdn.net/huijiahekele/article/details/78589680

3.1.2 IDEA

 不使用Mavenhttps://www.cnblogs.com/cangqinglang/p/10027199.html

使用Maven

创建maven项目

 配置tomcat

选project structure

 选tomcat

 

 去 Apache网站 http://tomcat.apache.org/whichversion.html 查看tomcat的servlet版本

Servlet SpecJSP SpecEL SpecWebSocket SpecAuthentication (JASIC) SpecApache Tomcat VersionLatest Released VersionSupported Java Versions
5.0 3.0 4.0 2.0 2.0 10.0.x 10.0.0-M5 (alpha) 8 and later
4.0 2.3 3.0 1.1 1.1 9.0.x 9.0.35 8 and later
3.1 2.3 3.0 1.1 1.1 8.5.x 8.5.55 7 and later
3.1 2.3 3.0 1.1 N/A 8.0.x (superseded) 8.0.53 (superseded) 7 and later
3.0 2.2 2.2 1.1 N/A 7.0.x 7.0.104 6 and later
(7 and later for WebSocket)
2.5 2.1 2.1 N/A N/A 6.0.x (archived) 6.0.53 (archived) 5 and later
2.4 2.0 N/A N/A N/A 5.5.x (archived) 5.5.36 (archived) 1.4 and later
2.3 1.2 N/A N/A N/A 4.1.x (archived) 4.1.40 (archived) 1.3 and later
2.2 1.1 N/A N/A N/A 3.3.x (archived) 3.3.2 (archived) 1.1 and later

 根据servlet版本配置 web.xml

3.0

<?xml version="1.0" encoding="UTF-8"?>  
<web-app
        version="3.0"  
        xmlns="http://java.sun.com/xml/ns/javaee"  
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">  
   
</web-app>

4.0

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
  version="4.0">


</web-app>

 pom.xml中加入jstl和standard依赖

    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>

    <dependency>
      <groupId>taglibs</groupId>
      <artifactId>standard</artifactId>
      <version>1.1.2</version>
    </dependency>

在JSP页面引入核心标签库的代码为:<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

开启EL表达式:<%@page isELIgnored="false"%>

 3.2 实现Servlet接口

需要实现的方法:

  • public void init(ServletConfig servletConfig)  初始化,只会调用一次
  • public ServletConfig getServletConfig()  返回一个 ServletConfig 对象,该对象用来返回初始化参数和 ServletContext。ServletContext 接口提供有关 servlet 的环境信息。
  • public void service(ServletRequest servletRequest, ServletResponse servletResponse)  核心方法
  • public String getServletInfo()  可选,它提供有关 servlet 的信息,如作者、版本、版权。
  • public void destroy()  Servlet卸载前调用
  • 可以编写构造方法
package com.aidata.javawe;

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

public class HelloServlet implements Servlet {
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        System.out.println("init");
    }

    @Override
    public ServletConfig getServletConfig() {

        return null;
    }

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("service");
    }

    @Override
    public String getServletInfo() {
        System.out.println("getServletInfo");
        return null;
    }

    @Override
    public void destroy() {
        System.out.println("destroy......");
    }

    // 构造器
    public HelloServlet(){
        System.out.println("HelloServlet's constructor");
    }
}

 

init(ServletConfig servletConfig)方法

第一个请求到达之时Servlet启动,init方法在Servlet构造完成之后调用。显然,如果init方法要完成大量工作,当第一个请求达到时,init方法执行会花大量时间。

ServletConfig:封装了Servle的t配置信息,并且可以获取ServletContext对象

ServletContext接口:

一个Web应用程序中的所有Servlet都共享同一个ServletContext对象,所以,ServletContext对象被称之为 application 对象(Web应用程序对象)。 
Servlet引擎为每个Web应用程序都创建一个对应的ServletContext对象,ServletContext对象被包含在ServletConfig对象中,调用ServletConfig.getServletContext方法可以返回ServletContext对象的引用。
功能:
可以认为是当前Web的一个大管家,可以获得当前Web应用各个方面的信息

  • ·获取WEB应用程序的初始化参数
  • ·记录日志
  • ·application域范围的属性
  • ·访问资源文件
  • ·获取虚拟路径所映射的本地路径
  • ·Web应用程序之间的访问
  • ·ServletContext的其他方法

获取当前web应用的某一个文件在服务器上的绝对路径,而不是部属前的路径:
getRealPath(String path)

String realPath = servletContext.getRealPath("/index.jsp");
System.out.println(realPath);

结果

C:UsersJieZIdeaProjectsWebDemooutartifactsfirstweb_war_explodedindex.jsp
注意是在out路径里

获取当前WEB应用的名称

String contextPath = servletContext.getContextPath();
System.out.println(contextPath);

获取当前WEB应用的某一个文件对应的输入流
getResourceAsStream(String path): path的/为当前的根目录

 try {
     ClassLoader classLoader = getClass().getClassLoader();
     InputStream is = classLoader.getResourceAsStream("jdbc.properties");
     System.out.println("1." + is);
 }catch (Exception e){
     e.printStackTrace();
 }

try {
    InputStream is2 = servletContext.getResourceAsStream("/WEB-INF/classes/jdbc.properties");
    System.out.println("2." + is2);
}catch (Exception e){
    e.printStackTrace();
}

结果

1.java.io.BufferedInputStream@400c929d
2.java.io.FileInputStream@7d42e4bb

3.3 在web.xml中配置Servlet

Servlet程序必须通过Servlet容器来启动运行,并且储存目录有特殊要求,通需要存储在<WEB应用程序目录>WEB-INFclasses目录中。 Servlet程序必须在WEB应用程序的web.xml文件中进行注册和映射其访问路径,才可以被Servlet引擎加载和被外界访问。

web.xml中重要的两个标签:

  • <servlet>元素用于注册一个Servlet,它包含有两个主要的子元素:<servlet-name><servlet-class>,分别用于设置Servlet的注册名称和Servlet的完整类名。
  • <servlet-mapping>元素用于映射一个已注册的Servlet的一个对外访问路径,它包含有两个子元素:<servlet-name><url-pattern>,分别用于指定Servlet的注册名称和Servlet的对外访问路径。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!-- 配置和映射Servlet -->
    <servlet>
        <servlet-name>helloServlet</servlet-name>
        <servlet-class>com.aidata.javawe.HelloServlet</servlet-class>
        
    </servlet>
    <servlet-mapping>
        <servlet-name>helloServlet</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>
</web-app>

同一个Servlet可以被映射到多个URL上,即多个<servlet-mapping>元素的<servlet-name>子元素的设置值可以是同一个Servlet的注册名。
在Servlet映射到的URL中也可以使用*通配符,但是只能有两种固定的格式:一种格式是“*.扩展名”,另一种格式是以正斜杠(/)开头并以“/*”结尾。

配置步骤

  • 向描述符中添加Servlet
  • 将Servlet映射到URL

load-on-startup参数

前面提到过init方法如果有大量工作,当一个请求达到时才开始执行,会大大影响请求回应的效率

可以指定Servlet被创建的时机,若为负数,则在第一次请求时被创建,若为0或正数,则在当前WEB应用被Servlet容器加载时创建实例。当多个Servlet配置都包含该标签,它们将按照标签内的值的大小顺序启动,且数值越小越早被创建。

<servlet>
    <servlet-name>helloServlet</servlet-name>
    <servlet-class>com.aidata.javawe.HelloServlet</servlet-class>
    <load-on-startup>2</load-on-startup>
</servlet>

Servlet的初始化参数

可以在web.xml中的servlet标签的init-param子标签内配置Servlet的初始化参数

<servlet>
    <servlet-name>helloServlet</servlet-name>
    <servlet-class>com.aidata.javawe.HelloServlet</servlet-class>

    <!--配置Servlet的初始化参数,且该节点必须在load-on-startup的前面-->
    <init-param>
        <!--参数名-->
        <param-name>user</param-name>
        <!--参数值-->
        <param-value>root</param-value>
    </init-param>
    <init-param>
        <param-name>password</param-name>
        <param-value>1230</param-value>
    </init-param>
    <load-on-startup>-1</load-on-startup>
</servlet>

获取Servlet初始化参数

servletConfig.getInitParameter(String name):获取指定参数名的初始化参数
servletConfig.getInitParameterNames():获取参数名组成的Enumeration对象

public class HelloServlet implements Servlet {
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        System.out.println("init");
        String user = servletConfig.getInitParameter("user");
        System.out.println("user:"+user);

        Enumeration<String> names = servletConfig.getInitParameterNames();
        while (names.hasMoreElements()){
            String name = names.nextElement();
            String value = servletConfig.getInitParameter(name);
            System.out.println("^^"+name+":"+value);
        }
    }

结果

init
user:root
^^password:1230
^^user:root

Web应用的初始化参数

可以在web.xml中配置整个应用的初始化参数

使用标签:

<context-param>
        <param-name>xxx</param-name>
        <param-value>xxx</param-value>
</context-param>
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!--配置当前Web应用的初始化参数-->
    <context-param>
        <param-name>driver</param-name>
        <param-value>com.mysql.jdbc.Driver</param-value>
    </context-param>
    <context-param>
        <param-name>jdbcUrl</param-name>
        <param-value>jdbc:mysql:///aidata</param-value>
    </context-param>

    <!-- 配置和映射Servlet -->
    <servlet>
        <servlet-name>helloServlet</servlet-name>
        <servlet-class>com.aidata.javawe.HelloServlet</servlet-class>

        <!--配置Servlet的初始化参数,且该节点必须在load-on-startup的前面-->
        <init-param>
            <!--参数名-->
            <param-name>user</param-name>
            <!--参数值-->
            <param-value>root</param-value>
        </init-param>
        <init-param>
            <param-name>password</param-name>
            <param-value>1230</param-value>
        </init-param>
        <load-on-startup>-1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>helloServlet</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>
</web-app>

获取整个应用的初始化参数则使用:

servletContext.getInitParameter(String name):获取指定参数名的初始化参数
servletContext.getInitParameterNames():获取参数名组成的Enumeration对象

@Override
public void init(ServletConfig servletConfig) throws ServletException {
    System.out.println("init");
 
    ServletContext servletContext = servletConfig.getServletContext();
    String driver = servletContext.getInitParameter("driver");
    System.out.println("driver:"+driver);

    Enumeration<String> name2 = servletContext.getInitParameterNames();
    while (name2.hasMoreElements()){
        String name = name2.nextElement();
        System.out.println("--->"+name);
    }
}

这里设置的初始化参数可以被所有的Servlet所获取,而Servlet的初始化参数只有那个Sevlet可以获取

四、HTTP、ServletRequest和ServletResponse

 4.1 HTTP协议、Get方法和Post方法

Web浏览器与Web服务器之间的一问一答的交互过程必须遵循一定的规则,这个规则就是HTTP协议,HTTP是超文本传输协议的简写,它是TCP/IP协议集中的一个应用层协议,用于定义Web浏览器与Web服务器之间交换数据的过程以及数据本身的格式。
HTTP协议的版本HTTP/1.0、HTTP/1.1、HTTP-NG

 请求数据包

 响应数据包

 响应状态码

 HTTP的会话方式

 浏览器访问多图页面的过程,每个图片都是一对单独的请求和响应:

 Get和Post

GET请求把请求参数附在url的后面,用?连接,是一个个键值对。在浏览器地址栏中输入某个URL地址或单击网页上的一个超链接时,浏览器发出的HTTP请求消息的请求方式为GET,使用GET方式传递的数据量一般限制在1KB以下。

 Post主要用于传递表单信息,传递的信息要大的多。

 4.2 ServletRequest和ServletResponse

 我们知道了浏览器会像服务端发送HTTP请求,那么我们的Servlet是如何处理请求的呢?

两个接口:

ServletRequest:封装了请求信息,可以从中获取到任何的请求信息。官方文档http://tomcat.apache.org/tomcat-5.5-doc/servletapi/javax/servlet/ServletRequest.html
ServletResponse:封装了响应信息,如果想给给用户什么响应,具体可以使用接口的方法实现。官方文档http://tomcat.apache.org/tomcat-5.5-doc/servletapi/javax/servlet/ServletResponse.html
这两个接口的实现类,都是服务器给予实现的,并在服务器调用service方法时传入。

ServletRequest

封装了请求信息. 可以从中获取到任何的请求信息。

获取请求参数

String getParameter(String name)

根据请求参数的名字, 返回参数值,
该方法只能获取到第一个提交的值,若请求参数有多个值(例如 checkbox), 要用下一个方法

 HTML

<form action="loginServlet" method="post">
    user: <input type="text" name="user"/>
    password: <input type="text" name="password">
    <input type="submit" value="Submit"/>
</form>

Servlet

public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
    System.out.println("请求来了...");
    System.out.println(servletRequest);
    String user = servletRequest.getParameter("user");
    String password = servletRequest.getParameter("password");
    System.out.println(user + "," + password);
}

结果

"请求来了..."
org.apache.catalina.connector.RequestFacade@44fe4cd9                        
john,
123

String[] getParameterValues(String name)

根据请求参数的名字, 返回请求参数对应的字符串数组。

需要一组参数,用多选框。

 HTML

<form action="loginServlet" method="post">
    user: <input type="text" name="user"/>
    password: <input type="text" name="password">
    <br><br>
    interesting:
    <input type="checkbox" name="interesting" value="reading"/>Reading
    <input type="checkbox" name="interesting" value="game"/>Game
    <input type="checkbox" name="interesting" value="party"/>Party
    <input type="checkbox" name="interesting" value="sport"/>Sport
    <input type="checkbox" name="interesting" value="tv"/>TV
    <input type="submit" value="Submit"/>
</form>

Servlet

public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
    System.out.println("请求来了...");

    String interesting = servletRequest.getParameter("interesting");
    System.out.println(interesting);
    String[] interestings = servletRequest.getParameterValues("interesting");
    for (String interest: interestings){
        System.out.println("-->" + interest);
    }
}

结果

"请求来了..."
reading  
-->reading
-->game
-->party
-->sport    

Enumeration getParameterNames()

返回参数名对应的 Enumeration 对象,
类似于 ServletConfig(或 ServletContext) 的 getInitParameterNames() 方法。


Map getParameterMap()

返回请求参数的键值对,

key:参数名, value: 参数值, String 数组类型。

 Servlet

@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
    System.out.println("请求来了...");
   
    String[] interestings = servletRequest.getParameterValues("interesting");
    for (String interest: interestings){
        System.out.println("-->" + interest);
    }
    Enumeration<String> names = servletRequest.getParameterNames();
    while (names.hasMoreElements()){
        String name = names.nextElement();
        String val = servletRequest.getParameter(name);
        System.out.println("^^"+name + ": " + val);
    }
    Map<String, String[]> map = servletRequest.getParameterMap();
    for (Map.Entry<String, String[]> entry: map.entrySet()){
        System.out.println("**" + entry.getKey() + ":" + Arrays.asList(entry.getValue()));
    }
}

结果

-->reading
-->game
-->party
-->sport
^^user: zhaojie
^^password: 123
^^interesting: reading 
**user:[zj]
**password:[123]
**interesting:[reading, game, party, sport]

HttpServletRequest

是 SerlvetRequest 的子接口,针对于 HTTP 请求所定义,里边包含了大量获取 HTTP 请求相关的方法。 

获取请求的 URI

获取请求方式

 httpServletRequest.getMethod()  

HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
String requestURI = httpServletRequest.getRequestURI();
System.out.println(requestURI);

String method = httpServletRequest.getMethod();
System.out.println(method);

结果

/firstweb/loginServlet
POST

若是一个 GET 请求, 获取请求参数对应的那个字符串, 即?后的那个字符串

String queryString = httpServletRequest.getQueryString();
System.out.println(queryString); 

结果

user=aidata&password=123456&interesting=game&interesting=party&interesting=shopping

获取请求的 Serlvet 的映射路径

String servletPath = httpServletRequest.getServletPath();
System.out.println(servletPath); 

结果

/loginServlet

attribute 相关的几个方法

ServletResponse

封装了响应信息, 如果想给用户什么响应, 具体可以使用该接口的方法实现。

getWriter()

返回 PrintWriter 对象. 调用该对象的 print() 方法, 将把 print() 中的参数直接打印到客户的浏览器上。

设置响应的内容类型

response.setContentType("application/msword");
指定为word类型,点击提交会下载word文档

servletResponse.setContentType("application/msword"); //word类型
PrintWriter out = servletResponse.getWriter();
out.println("helloworld...");

void sendRedirect(String location)

请求的重定向. (此方法为 HttpServletResponse 中定义,需要强转)

五、从自定义GenericServlet到HttpServlet

 5.1 编写一个Servlet,发现问题

在 web.xml 文件中设置两个Web应用的初始化参数user和password。定义一个 login.html, 里边定义两个请求字段: user, password. 发送请求到 loginServlet。创建一个 LoginServlet, 在其中获取请求的user和password,比对其和 web.xml 文件中定义的请求参数是否一致。若一致, 响应 Hello:xxx, 若不一致, 响应 Sorry: xxx xxx 为 user。

web.xml

<!-- 配置当前WEB应用初始化参数-->
<context-param>
    <param-name>user</param-name>
    <param-value>atguigu</param-value>
</context-param>
<context-param>
    <param-name>password</param-name>
    <param-value>123567</param-value>
</context-param>
<servlet>
    <servlet-name>loginServlet</servlet-name>
    <servlet-class>com.atguigu.javaweb.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>loginServlet</servlet-name>
    <url-pattern>/loginServlet</url-pattern>
</servlet-mapping>

HTML

<form action="loginServlet" method="post">
    user: <input type="text" name="username"/>
    password: <input type="password" name="password">
    <input type="submit" value="Submit"/>
</form>

<form action="loginServlet" ...>中的loginServlet是url不是Servlet的名称

 Servlet

private ServletConfig servletConfig;
@Override
public void init(ServletConfig servletConfig) throws ServletException {
    this.servletConfig = servletConfig; // 为了在service方法中使用servletConfig
}

@Override
public ServletConfig getServletConfig() {
    return null;
}

    @Override
public String getServletInfo() {
      // TODO Auto-generated method stub
      return null;
}

 @Override
    public void destroy() {
        // TODO Auto-generated method stub       
}

@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
     //1.获取请求参数:username,password
     String username = servletRequest.getParameter("username");
     String password = servletRequest.getParameter("password");
     //2.获取WEB应用的初始化参数:user,password
     ServletContext servletContext = servletConfig.getServletContext();
     String initUser = servletContext.getInitParameter("user");
     String initPassword = servletContext.getInitParameter("password");

    PrintWriter out = servletResponse.getWriter();
     //3.比对
    //4.打印响应字符串
     if (initUser.equals(username) && initPassword.equals(password)){
         out.println("Hello" + username);
     }else {
         out.println("sorry" + username);
     }
}

我们发现当实现Servlet接口的时候,有很多方法如getServletInfo()根本用不到,但是还是不得不实现,导致了大量的空方法。

为了在service()中使用servletConfig,还定义了一个属性。

其实,有一个抽象类GenericServlet可以简化我们的工作。

官方文档http://tomcat.apache.org/tomcat-5.5-doc/servletapi/javax/servlet/GenericServlet.html

5.2 编写自己的GenericServlet

方便开发,编写如下抽象类,实现Serclet和ServletConfig接口

import javax.servlet.*;
import java.io.IOException;
import java.util.Enumeration;

/**
 * 自定义的一个Servlet接口的一个实现类,目标是让开发的任何Servlet都继承,以简化开发
 */
public abstract class MyGenericServlet  implements Servlet, ServletConfig {
    /**
     * Servlet的方法
     */
    private ServletConfig servletConfig;
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
         this.servletConfig = servletConfig;
    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    @Override
    public abstract void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException;

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {}



    /**
     * ServletConfig接口的方法
     */

    @Override
    public String getServletName() {
        return servletConfig.getServletName();
    }

    @Override
    public ServletContext getServletContext() {
        return servletConfig.getServletContext();
    }

    @Override
    public String getInitParameter(String s) {
        return servletConfig.getInitParameter(s);
    }

    @Override
    public Enumeration<String> getInitParameterNames() {
        return servletConfig.getInitParameterNames();
    }

}

GenericServlet是Servlet接口和ServletConfig接口的实现类,但是一个抽象类,其中的service方法为抽象方法
如果新建的Servlet程序直接继承GenericServlet会使开发更简洁
官方具体实现:

  • 在GenericServlet中声明了一个ServletConfig类的成员变量,在init(ServletConfig)方法中对器进行初始化
  • 利用ServletConfig成员变量的方法实现了ServletConfig接口的方法
  • 定义了一个init()方法,在init(ServletConfig)方法中对其进行调用,子类可直接覆盖init()在其中实现对Servlet的初始化
  • 不建议直接覆盖init(ServletConfig),因为如果忘记编写super(ServletConfig),而还是用了ServletConfig接口的方法,则会出现空指针异常
  • 新建的init(){}并非Servlet的生命周期方法,而init(ServletConfig)是生命周期相关的方法

 5.3 编写自己的HttpServlet

GenericServlet是一个不依赖具体协议的Servlet,它只包含了一个抽象的service方法和几个辅助方法。缺乏对HTTP的支持,比如,当ServletRequest想要获取请求方式是GET还是POST,需要调用HttpServlet,需要强转,还是麻烦。

编写一个支持HTTP的Servlet

/**
 * 针对HTTP协议定义的一个Servlet类
 */
public class MyHttpServlet extends MyGenericServlet{
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        if (servletRequest instanceof HttpServletRequest){
            HttpServletRequest request = (HttpServletRequest) servletRequest;

        if (servletResponse instanceof HttpServletResponse){
            HttpServletResponse response = (HttpServletResponse)servletResponse;
            service(request, response);
        }}
    }

    private void service(HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws ServletException, IOException{
        //1.获取请求方式
        String method = servletRequest.getMethod();
        //2.根据请求方式再创建对应的处理方法
        if ("GET".equalsIgnoreCase(method)){
            doGet(servletRequest, servletResponse);
        }
        if ("POST".equalsIgnoreCase(method)){
            doPost(servletRequest, servletResponse);
        }

    }
    public void doGet(HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws ServletException, IOException{

    }
    public void doPost(HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws ServletException, IOException {

    }
}

于是,只要继承上面的类就可以大大简化开发

public class LoginServlet2 extends MyHttpServlet {
    @Override
    public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       String method = req.getMethod(); //不再用强转
        System.out.println(method);

        //1.获取请求参数:username,password
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        System.out.println("--------------" + username);
        //2.获取WEB应用的初始化参数:user,password

        String initUser = getServletContext().getInitParameter("user");
        String initPassword = getServletContext().getInitParameter("password");

        PrintWriter out = resp.getWriter();
        //3.比对
        //4.打印响应字符串
        if (initUser.equals(username) && initPassword.equals(password)){
            out.println("Hello" + username);
        }else {
            out.println("sorry" + username);
        }
    }
}

上面的定义的类基本实现了一般开发中常用的HttpServlet
HttpServlet继承自GenericServlet,针对于HTTP协议定制
在service()方法中,直接把ServletRequest和ServletResponse转为HttpServletRequest和HttpServletResponse,并调用了重载的service(HttpServletRequest, HttpServletResponse)
在service(HttpServletRequest , HttpServletResponse)获取了请求方式:reque.getMethod(),根据请求方式又创建了doXxx()方法(Xxx为具体的请求方式,比如doGet、doPost)。

实际开发中,直接继承HttpServlet,并根据请求方式复写doXxx()方法接口
好处:直接有针对性覆盖doXxx()方法,直接使用HttpServletRequest和HttpServletResponse,不再需要强转

一个字:

棒!

原文地址:https://www.cnblogs.com/aidata/p/11974648.html