Servlet


前言

之前我们学习Tomcat,Servlet的运行强依赖Tomcat这种Web容器。

Servlet是Server Applet的简称,中文意思是运行在Web服务器端中的小程序。整个Java Web的核心知识体系都是围绕Servlet来展开的。

Servlet是什么?

Servlet就是接口,定义了Java类可以被Tomcat所识别并实例化的规则。

如果定义1个类,实现Servlet接口,实现Serlet中的规定的方法,就可以被Web服务器软件(Tomcat)所识别和调用,从而开发1个Java Web程序。 

Servlet快速入门

1.web.xm设置如下

在WEB-INF目录下配置web.xml

<?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-name>zhanggenservlet-name>
        <servlet-class>com.zhanggen.servlet.ServletDemo1servlet-class>
    servlet>
    <servlet-mapping>
        <servlet-name>zhanggenservlet-name>
        <url-pattern>/zhanggenurl-pattern>
    servlet-mapping>
    
web-app>
web.xml

2.创建类

在src目录下新增如下包和类

package com.zhanggen.servlet;

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

public class ServletDemo1 implements Servlet {
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {

    }

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

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

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

    @Override
    public void destroy() {

    }
}
ServletDemo1.java

3.servlet执行原理

用户通过http://127.0.0.1:8080/zhanggen这个URL地址访问Tomcat。

Tomcat接收用户请求之后读取web.xml配置,查询url-pattern标签中内容,锁定ServletDemo1这个Java类。

Tomcat将ServletDemo1类对应的Class字节文件加载到内存。

Tomcat实例化ServletDemo1类创建其对象,调用其对象中定义的service()方法。

4.servlet的生命周期

Servlet的实现类(ServletDemo1)中的方法就是servlet的生命周期。

package com.zhanggen.servlet;

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

public class ServletDemo1 implements Servlet {
    /*
     * init()方法:Servlet的初始化方法
     * 当用户访问Tomcat,Tomcat读取web.xml配置文件,Servelt被创建的时,执行。只会执行一次。
     * */
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        System.out.println("初始化");
    }

    /*
     *getServletConfig()方法:获取ServletConfig对象的
     * ServletConfig:Servlet的配置对象
     * */
    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    /*
     * service()方法:提供Serlet服务
     * 每1次Servlet被访问时,执行。会被执行多次。
     *
     * */
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("1111111");
    }
    /*
    * getServletInfo()方法:获取Servlet的信息,版本,作者等。
    * */
    @Override
    public String getServletInfo() {
        return null;
    }

    /*
     * destroy() 销毁方法
     *在servlet被销毁时执行,只会执行1次
     * */
    @Override
    public void destroy() {
        System.out.println("销毁");
    }
}
ServletDemo1.java

 A.Servlet实现类的对象被Tomcat创建,执行init()方法,只执行1次。单例模式

B.对象提供服务:执行service()方法,执行多次。

C.对象被Tomcat销毁:执行destroy()方法,只执行1次。

5.Servlet生命周期详解

A.init()方法

Servlet实现类的对象被创建时,只会执行一次init方法。

默认情况下,Servlet第一次被访问时,Servlet实现类的对象被创建

可以通过配置web.xml控制执行Servlet的创建时机:

   <servlet>
        <servlet-name>zhanggenservlet-name>
        <servlet-class>com.zhanggen.servlet.ServletDemo1servlet-class>
        
        
                
        
        
        <load-on-startup>6load-on-startup>
    servlet>

Servlet的实现类对象是单例的,在内存中只会存在1个。

多个用户同时访问Servlet时,可能存在线程安全的问题

尽量不要再Servlet中定义成员变量,即使定义了成员变量,也不要对其进行修改。

B.service()方法

每次访问Servlet时,service()方法都会被调用一次。

C.destroy()方法

只有Tomcat服务器正常关闭时,才会执行destory()方法。

在destory()执行在Servlet实现类对象被销毁之前,一般用于释放资源。

6.注解配置

通过web.xml配置Servlet,一旦新增了URL就需要增加1对servlet和servlet-mapping配置项。

在Servlet 3.0版本之后开始支持通过注解的方式配置Servlet,而不是通过web.xml配置文件配置Servlet。

package cn.zhanggen.web.servlet;

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

//@WebServlet(urlPatterns = "/index")
@WebServlet("/index")
public class ServletDemo implements Servlet {
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {

    }

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

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

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

    @Override
    public void destroy() {

    }
}
ServletDemo.java

Servlet的体系结构

Servlet是1个接口,该接口下面有2个抽象类分别为:

GenericServlet抽象类:GenericServlet是Servlet接口的抽象类,将Servlet接口中其他方法做了默认空实现,只将service()方法做为抽象。

将来定义Servlet类时,可以继承GenericServlet,实现service()方法即可,简化操作。

package cn.zhanggen.web.servlet;


import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;

public class ServletDemo extends GenericServlet {
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
    }
}

HttpServlet抽象类:HttpServlet是继承了GenericServlet的抽象类,对http协议进行了封装,简化操作。

package cn.zhanggen.web.servlet;


import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
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("/zhanggen")
public class ServletDemo extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("doGet方法在执行");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
}

Servlet的请求和响应

当用户发起http请求到Tomcat服务器之后,Tomcat服务器会创建1对request和response对象。

开发人员使用request对象获取请求信息,使用response对象来设置响应消息。

1.request对象继承关系

servletRequest 接口
|继承
HttpServletRequest接口
|实现
org.apache.catalina.connector.RequestFacade

Tomcat通过org.apache.catalina.connector.RequestFacade类来创建request对象,并且传递给service()方法。

2.request对象获取请求消息

HTTP请求信息可分为请求行(请求的URL)、请求头和请求体信息。

A.获取请求行信息

package cn.zhanggen.web.servlet;


import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
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("/zhanggen")
public class ServletDemo extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.获取请求方式:GET或者POST
        System.out.println(req.getMethod());
        //2.获取虚拟目录
        System.out.println(req.getContextPath());
        //3.获取Servlet 路径
        System.out.println(req.getServletPath());
        //4.获取请求参数字符串
        System.out.println(req.getQueryString());
        //5.获取URI(统一资源标识符):/zhanggen
        System.out.println(req.getRequestURI());
        //6.获取URL(统一资源定位符):http://127.0.0.1:8080/zhanggen
        System.out.println(req.getRequestURL());
        //7.获取协议版本
        System.out.println(req.getProtocol());
        //8.获取客户机的IP地址
        System.out.println(req.getRemoteAddr());
        System.out.println("doGet方法在执行");

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
}

B.获取请求头信息

package cn.zhanggen.web.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;
import java.util.Enumeration;

@WebServlet("/index")
public class ServletDemo1 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.获取所有请求头名称
        Enumeration headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String name = headerNames.nextElement();
            String value = request.getHeader(name);
            System.out.println(name + "<------------------->" + value);
        }
        //2.获取agent使用的浏览器信息:可用于浏览器兼容性
        String agent = request.getHeader("user-agent");
        System.out.println(agent);
        //3.获取refer信息可以进行防盗链操作:http://localhost:8080/login.html
        System.out.println(request.getHeader("referer"));
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse resp) throws ServletException, IOException {
    }
}

C.获取请求体信息

只有POST请求才会有请求体信息。

获取字符输入流:

BufferedReader bufferReader = request.getReader();
 protected void doPost(HttpServletRequest request, HttpServletResponse resp) throws ServletException, IOException {
        //1.获取字符流
        BufferedReader bufferReader = request.getReader();
        String line = null;
        while ((line = bufferReader.readLine()) != null) {
            System.out.println(line);
        }

    }

获取字节输入流:

ServletInputStream inputStream = request.getInputStream();

3.获取请求参数通用方式

以下方法可以获取GET和POST请求携带的参数和值

package cn.zhanggen.web.servlet;

import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Map;
import java.util.Set;

@WebServlet("/index")
public class ServletDemo1 extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse resp) throws ServletException, IOException {
        //1.获取请求参数信息:根据名称获取参数值,GET和POST请求通用。
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        //2.获取多选框的值
        String[] cars = request.getParameterValues("cars");
        System.out.println(username);
        System.out.println(password);
        System.out.println(Arrays.toString(cars));
        //3.获取所有参数的名称
        Enumeration parameterNames = request.getParameterNames();
        while (parameterNames.hasMoreElements()) {
            String s = parameterNames.nextElement();
            System.out.println(s);
        }
        //4.获取所有参数和值的map集合
        Map parameterMap = request.getParameterMap();
        Set keyset = parameterMap.keySet();
        for (String name : keyset) {
            String[] values = parameterMap.get(name);
            System.out.println(Arrays.toString(values));
        }


    }
}

4.获取请求参数乱码问题

package cn.zhanggen.web.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("/zhanggen66")
public class ServletDemo3 extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse resp) throws ServletException, IOException {
        //1.设置字符流的编码
        request.setCharacterEncoding("utf-8");
        String username = request.getParameter("username");
        System.out.println(username);
    }
}

5.请求转发

package cn.zhanggen.web.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("/zhanggen66")
public class ServletDemo3 extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");
        //1.转发请求
        //request.getRequestDispatcher("/index").forward(request, resp);
        //2.响应字符串
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-Type", "text/html;charset=utf-8");
        response.getWriter().write("登录成功");

    }
}

6.Request对象数据共享

1个servlet对应1个URL,那么当不同的Servlet直接如何共享数据呢?

request.setAttribute("msg","222");                 //设置数据
Object msg = request.getAttribute("msg"); //获取数据
msg=(String)msg;
System.out.println(msg);
request.removeAttribute("msg"); //删除数据

参考