4.1 Servlet概述

1.什么是Servlet

  • Servlet,全称Java Servlet,是用Java编写的服务器端程序,其主要功能在于交互式地浏览和修改数据,生成动态Web内容。狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。
  • Servlet是JavaWeb的核心程序,是Java Web三大组件之一(Servlet,Filter,Listener)。
  • 在HTTP请求过程中会把请求的参数存放在请求的URL中,或者是请求体中,这个参数需要服务器未接收解析并处理,Servlet就是用来处理这个过程的。

2.Servlet的作用

  • 用来处理客户端发送过来的请求,并对该请求做出响应
    1.获取接收客户端发送过来的请求数据
    2.处理请求
    3.将处理的结果通过响应发送到客户端

4.2 编写第一个Servlet

1.创建一个类继承继承HttpServlet:

  • Servlet程序必须编写实现类:
    1.通常是继承javax.servlet.http.HttpServlet
public class HttpServlet extends HttpServlet{
}

2.复写HttpServlet中的doGet(),doPost()

public class HelloServlet extends HttpServlet {
   @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
     System.out.println("接收到post请求");
        }
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("接收到post请求");
    }
}

  1. 配置web.xml文件
<servlet>
    <servlet-name>helloServlet</servlet-name>
    <servlet-class>net_zixue.servlet.HelloServlet</servlet-class>

</servlet>

    <servlet-mapping>
        <servlet-name>helloServlet</servlet-name>
        <url-pattern>/hi</url-pattern>
    </servlet-mapping>

其中是用来注册Servlet的
内部的 是servlet的名称,在当前配置文件中必须唯一
内部的 是servlet的实现类的类名,类名要包含包名
4. Servlet的执行流程
第一步根据浏览器地址栏中访问的接口名也就是helloServlet找到web.xml中对应的
第二步会根据对应到
第三步会根据name对应到相同名字中

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

        <session-config>
            <session-timeout>10</session-timeout>
        </session-config>
<context-param>
    <param-name>encoding</param-name>
    <param-value>utf-8</param-value>
</context-param>

    <context-param>
        <param-name>encodingq</param-name>
        <param-value>utf-8</param-value>
    </context-param>

<servlet>
    <servlet-name>helloServlet</servlet-name>
    <servlet-class>net_zixue.servlet.HelloServlet</servlet-class>

</servlet>

    <servlet-mapping>
        <servlet-name>helloServlet</servlet-name>
        <url-pattern>/hi</url-pattern>
    </servlet-mapping>
</web-app>

在这里插入图片描述

4.3 使用Servlet3.0创建Servlet

1.创建项目的时候javaEE版本选择6.0以上
2.在IDEA的代码目录下直接右键new,选择Servlet
3.配置 @WebServlet(urlPatterns="/TestServlet"),注解内的访问路径等同于web.xml下的url-parrtener
在这里插入图片描述

4.4 HttpServletRequest简介-获取请求行和请求头

HttpServletRequest简介:
在Servlet的API中,定义了一个HttpServletRequest接口,它继承自ServletRequest接口,专门用于封装HTTP的请求,由于HTTP请求包含着请求行、请求头和请求体三部分,因此,在HttpServletRequest中分别定义了接收请求行、请求头和请求体的相关方法
获取请求行相关信息的相关方法:

  1. getMethod()方法,返回请求方法,请求方法通常是GET或者POST,但也有可能是HEAD、PUT或者DELETE。
  2. getRequestURI()方法:返回URI(URI是URL的从主机和端口之后到表单数据之前的那一部分)
  3. getRemoteAddr()方法:该方法用于获取请求客户端的IP地址
  4. getRemoteport()方法:该方法用于获取请求客户端的端口号
  5. getLocalAddr()方法:该方法用于获取服务器当前接收请求的IP地址
  6. getContextPath()方法:该方法用于获取URL中属于web应用程序的路径
  7. getProtocol()方法:该方法用于获取请求行中的协议名和版本

获取请求头的相关方法:

  1. getHeader(String name),该方法用于获取一个指定头字段的值,如果请求头中不包含该字段则返回null,如果包含多个该字段的值则获取第一个值。
  2. getIntHeader(String name),该方法用于获取指定头字段的值,并且将其值转为int类型,如果不存在该字段则返回-1,如果获取到的值不能转化为int时会发生NumberFormatException异常。
  3. getDataHeaders(String name),该方法用于获取指定头字段的值,并将其按照GMT时间格式转换成一个代表日期/时间的长整数。
  4. getHeaderNames():该方法用于获取所有包含请求头字段的Enumeration

获取请求参数:

  1. getParameter(String name):用于获取某个指定名称的参数值,如果请求中没有包含指定名称的参数,则返回null,如果有指定参数但是没有给设置值,则返回空串"",如果包含多个该参数的值则返回第一个出现的参数值。
  2. getParameterName():该方法用于返回一个包含请求消息中所有参数名的Enumernation。
  3. getParameterMap()该方法用于将请求中的所有参数和值装入一个map对象然后返回。

4.6 Response对象发送响应行和响应头

HttpServletResponse简介:
在Servlet的API中,定义了一个HttpServletResponse接口,它继承自、ServletResponse接口,专门用于封装HTTP的响应,由于HTTP响应包含着响应行、响应头和响应体三部分,因此,在HttpServletResponse中分别定义了发送响应行、响应头和响应体的相关方法。

发送响应行:

  1. setStatus(int status):当Servlet向客户端回送响应消息时,需要设置一个状态码,该方法用于设置HTTP响应消息的状态码,并生成响应状态行,由于响应状态行中状态的描述直接和状态码相关,而HTTP协议版本由服务器决定,因此只需要设置该方法,就可以发送一个响应行,正常情况下,web服务器会默认发送一个200的状态码。
  2. sendError(int code):该方法用于发送表示错误信息的状态码,例如404找不到访问的资源,他还有一种重载的形式sendError(int code,String errorMessage),errorMessage可以以文本形式显示在客户端浏览器

发送响应头:

  1. addHeader(String name,String value):该方法用来设置HTTP协议的响应头字段,其中name是响应头字段名,value是响应字段的值。
  2. setHeader(String name,String value):该方法和addHeader相同,唯一的区别是addHeader可以重复添加一个同名的响应头字段, setHeader会覆盖之前添加的同名的响应头。
  3. addIntHeader(String name, int value)、setIntHeader(String name,int value):这两个方法用于将value值为int的字段加入到响应头中
  4. setContentLength():该方法用于设置HTTP响应消息的 内容大小,单位是字节。
  5. setContentType():该方法用于设置Servlet输出内容的类型,也就是HTTP协议中的Content-Type响应头,例如发送到客户端的内容是jpeg格式的图像数据,就需要将响应头字段的类型设置为"image/jpeg",另外,如果过相应的内容是文本,那这个时候setContentType还可以设置字符编码,例如 text/html:charset=UTF-8
  6. setCharacterEncoding(String charset)

4.7 HttpServletResponse发送请求体

背景知识:
由于在HTTP响应消息中,大量的数据都是通过响应体传递的,因此ServletResponse遵循以IO流传递大数据的设计理念,定义了两个与输出流相关的方法,具体如下:

  1. getOutputStream()方法:该方法获取字节流输出对象为ServletOutputStream类型,它是OutputStream的子类,因此可以直接输出字节数组中的二进制数据。
  2. getWrite()方法:该方法获得的字符输出流对象是PrintWriter类型由于它可以直接输出文本类型,因此要输出网页文档,需要使用这个方法。

4.8 实验2:验证码切换

背景知识:
用户在注册或者登录的过程中经常会遇到必须输入验证码的情况,那这个验证码主要是为了防止恶意程序访问服务器而设置的。
实现原理:
浏览器显示文档和图片,是由服务器回送给浏览器的响应完成的,而一个响应要封装成一个HttpServletResponse对象,所以我们只需要将验证码的图片封装成一个HttpServletResponse对象返回给浏览器即可。

4.9 Servlet的生命周期

生命周期:
就是一个对象从创建到销毁的过程
Servlet的生命周期:
Servlet从创建到销毁的过程
Servlet接口:
javax.servlet.Servlet接口
init(ServletConfig)方法,初始化方法
service(ServletRequest, ServletResponse)方法,每次访问都会调用来处理请求
destory()方法,销毁servlet方法

HttpServlet接口:javax.servlet.http:
继承自Servlet接口,并重新实现了service方法,根据不同请求方式调用不同的处理方法。
service(HttpServletRequest, HttpServletResponse)方法,获取请求方式,分别调用doGet(),或者doPost()方法。
当服务器关闭或者项目被移除服务器,destory方法会执行,生命周期结束.
Servlet接口下的service方法并不会调用doGet或者doPost处理请求,而HttpServlet下的service方法会调用doGet或者doPost方法处理请求。
注意:
Servlet实例是单例的,即无论请求多少次Servlet,最多只有一个Servlet实例,如果是多个客户端并发,同时访问Servlet的时候,服务器会启动多个线程分别执行Servlet的service方法。
原因:
如果我们每次访问都创建一个Servlet实例,会占用和浪费过多的计算机资源。

4.10 ServletConfig对象

ServletConfig对象到底是什么?
ServletConfig对象是它所对应的Servlet对象的相关配置信息。
特点:

  • 每一个servlet对象都有一个ServletConfig对象和它相对应
  • ServletConfig对象在多个Servlet对象之间是不能共享的

常见的ServletConfig对象的方法:

  1. getInitParameter(String name):返回一个初始化变量的值。
  2. getInitParameterNames():返回servlet初始化参数的所有名称。
  3. getServletContext():获取ServletConfig对象。
  4. getServletName():获取Servlet的name配置值
    <session-config>
        <session-timeout>10</session-timeout>
    </session-config>


    <context-param>
    <param-name>encoding</param-name>
    <param-value>utf-8</param-value>
</context-param>

    <context-param>
        <param-name>encoding1</param-name>
        <param-value>utf-8</param-value>
    </context-param>

<servlet>
    <servlet-name>helloServlet</servlet-name>
    <servlet-class>net.zixue.servlet.HelloServlet</servlet-class>


</servlet>

    <servlet-mapping>
        <servlet-name>helloServlet</servlet-name>
        <url-pattern>/hi</url-pattern>
    </servlet-mapping>

4.11 ServletContext-获取项目初始化参数

ServletContext定义:
ServletContext即Servlet上下文对象,该对象表示当前的web应用环境信息
获取ServletContext对象:

  1. 通过ServletConfig的getServiceContext()方法可以得到ServletContext对象。
  2. HttpServlet中直接通过this.getServletContext()获取。

域对象:
(域对象就是在不同资源之前来共享数据,保存数据,获取数据)
ServletContext对象通常成为Context域对象,ServletContext是我们学习的第一个域对象。

1.使用ServletContext获取整个web项目初始化参数:

  • 在web.xml中配置初始化参数:
<context-param>
        <param-name>参数名</param-name>
        <param-value>参数值</param-value>
    </context-param>

  • String getInitParameter(String name):根据名称获取初始化参数
  • Enumeration getInitparameterNames():获取所有初始化的参数名称

4.12 ServletContext对象-在多个Servlet之间共享数据

2.使用ServletContext在多个Servlet中共享数据:

  • void setAttribute(String name,Object object):存放数据
  • Object getAttribute(String name):获取数据
  • void removeAttribute(String name):删除数据

4.13 ServletContext-读取项目的资源文件

3.使用ServletContext读取web项目的资源文件

@WebServlet(name = "ServletTes4", urlPatterns = "/test4")
public class ServletTes4 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        InputStream resourceAsStream = this.getServletContext().getResourceAsStream("/db.properties");
        Properties properties = new Properties();
        properties.load(resourceAsStream);
        String name = properties.getProperty("name");
        String pass = properties.getProperty("passWord");
        String url = properties.getProperty("url");

        System.out.println(url);
        System.out.println(pass);
        System.out.println(name);

    }

4.14 请求转发

Servlet之间的跳转
Servlet之间可以实现跳转,从一个Servlet跳转到另一个Servlet,利用Servlet的跳转技术可以和方便的把一块业务模块分开,比如使用一个Servlet接受用户提交数据,使用另一个Servlet读取数据库,最后跳转到另一个Servlet把处理结果展示出来。这也就是MVC模式(modle,view,controller)
MVC:用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑,MVC被独特的发展起来用于映射传统的输入,处理和输出功能在一个逻辑的图形化用户界面的结构中。

转发Forward简介:
在Servlet中如果当前的web资源不想处理请求时,可以通过forword方将当前的请求传递给其他的web资源处理,这种方式成为请求转发。

4.15 实验3-登录错误时显示错误界面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

</body>
<center>


    <h2>用户登录</h2>
    <form action="login" method="get">
        <p>账号: <input type="text" name="name" /></p>
        <p>密码: <input type="text" name="passWord" /></p>
        <input type="submit" value="登录" />
    </form>




</center>
</html>
package net_zixue.servlet;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;
import java.lang.annotation.Repeatable;

@WebServlet(name = "LoginServlet", urlPatterns = "/login")
public class LoginServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        String userName = "xiaoming";
        String userPass = "123456";

        String name = request.getParameter("name");
        String passWord = request.getParameter("passWord");

        if (!name.equals(userName)) {
            //账户不存在
            request.setAttribute("errorMessage", "账户不存在");
            request.getRequestDispatcher("/loginError.jsp").forward(request, response);
        } else if (!passWord.equals(userPass)) {
            //密码错误

            request.setAttribute("errorMessage", "密码错误");
            request.getRequestDispatcher("/loginError.jsp").forward(request, response);
        } else {

            response.sendRedirect("/hello/home.html");
        }

    }
}

RequestDispatcher对象,可以通过request.getRequestDispstcher()方法获取调用这个对象的forward方法就可以实现请求转发

转发过程中携带数据:
request本身也是一个域对象,request可以携带数据传递给其他web资源
setAttribute方法;
getAttribute方法;
removeAttribute方法;
getAttributeNames方法;
通过request对象的setAttribute方法

<%--
  Created by IntelliJ IDEA.
  User: 随身汀
  Date: 2021/3/24
  Time: 17:51
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>

</body>
<center>


    <h2>用户登录错误界面</h2>

    <div >

        <%=request.getAttribute("errorMessage")%>
    </div>



</center>
</html>

4.16 重定向介绍

重定向的简介:
重定向是根据服务器返回的状态码来实现的,客户端浏览器在请求服务器的时候,服务端会返回一个状态码,服务器通过HttpServletResponse的setStatus(int status)方法来设置状态码,如果服务器这个时候返回的状态码是301或者是302,则浏览器就会到新的网址重新请求该资源,服务器的响应中会带着这个新资源的地址。

请求重定向相关代码:
在这里插入图片描述
//设置状态码为302,SC_MOVED_TEMPORAILY就是302的静态常量
response.setStatus(HttpServletResponse, SC_MOVED_TEMPORAILY);
//在请求头中携带新的资源地址
response.setHeader(“Location”, “http://www.baidu.com”);

为了使用方便HttpServletResponse中将setStatus和.setHeader这两种方法合并到一起叫做sendRedirect(String location);

4.17 实验4-登录后跳转网站首页

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>home</title>
</head>
<body>
<h2>我是网站的首页,欢迎访问</h2>
</body>
</html>

4.18 请求转发和重定向的区别

  1. 重定向的地址栏会发生变化,转发不会
  2. 重定向是两次请求两次响应,转发是一次请求一次响应
  3. 重定向路径需要加工程名,转发路径不需要加工程名
  4. 重定向可以跳转到任何网站,转发只能在服务器内部进行
    在这里插入图片描述

4.19 网页自动刷新

response.setHeader(“refresh”,"3);

package net_zixue.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;


@WebServlet(name = "RefreshServlet",urlPatterns = "/refresh")
public class RefreshServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        String message="<meta http-equiv='refresh' content='3;url=/hello/home.html'>3秒后会自动跳转到首页,如果没有跳转,请点击<a href='/hello/home.html'>跳转链接</a>";
        request.setAttribute("message",message);
        request.getRequestDispatcher("/index.jsp").forward(request,response);

    }

    private void RefreshDemo1(HttpServletResponse response) throws IOException {
        response.setContentType("text/html;charset=utf-8");
        response.setHeader("refresh","3;url='/hello/home.html'");
        response.getWriter().print("3秒后自动刷新");
    }
}


<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
  <title>$Title$</title>
</head>
<body>

<%=request.getAttribute("message")%>
</body>
</html>

JSP中实现自动刷新:
<meta http-equiv='refresh' content='3:url=/hello/home.html'>

4.20 Servlet线程安全

Servlet线程安全相关:
当多个客户端并发访问同一个Servlet的时候,web服务器为每一个客户端的访问创建一个线程,并在这个线程上调用Servlet的servlet方法,因此service方法内部如果访问了同一个共享资源的话,就可能引发线程安全问题。

package net_zixue.servlet;

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

@WebServlet(name = "ThreadServlet", value = "/ThreadServlet")
public class ThreadServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        int i = 0;
        synchronized (this){
            i++;//第一次访问i已经是1,第二次访问=2
            try {
                Thread.sleep(5 * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            response.setContentType("text/html;charset=utf-8");
            response.getWriter().write(i + "");
        }

    }
}

如何解决servlet线程安全?

  1. 同步代码块
  2. 实现SingleThreadModle接口
  3. 尽量不要再Servlet实例内使用共享变量

4.21 实验5-文件下载

文件的下载操作:

  1. 通过文件输入输出流将文件返回给客户端浏览器
  2. 通过对应的http响应头通知浏览器下载该文件
  • 告知浏览器文件的类型:response.setContentType(文件的MIME类型)
  • 告知浏览器文件的打开方式是下载:response.setHeader(“Content-Disposition”,“attachment:filement=文件名称”);
package net_zixue.servlet;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

@WebServlet(name = "DownloadServlet",urlPatterns = "/download")
public class DownloadServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {



        String filename=request.getParameter("filename");


        String mimeType = this.getServletContext().getMimeType(filename);
        response.setContentType(mimeType);
        response.setHeader("Content-Disposition","attachment;filename="+filename);


        String realPath = this.getServletContext().getRealPath("download/"+filename);
        InputStream in=new FileInputStream(realPath);
        ServletOutputStream outputStream = response.getOutputStream();


        int len=0;
        byte[] buffer=new byte[1024];
        while ((len=in.read(buffer))>0){
            outputStream.write(buffer,0,len);

        }
        in.close();


    }
}
Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐