JavaWeb学习笔记(一)—— Servlet

时间:2022-10-05 13:08:47

Servlet

用户在浏览器中输入一个网址并回车,浏览器会向服务器发送一个HTTP请求,服务器端接收并且处理改请求,然后向浏览器发送响应,浏览器接收响应,在将服务器端响应的内容显示出来,这种请求-响应模式就是典型的Web应用程序访问过程。在JavaWeb应用程序中,处理请求和响应的过程就是由Servlet程序来完成的。

1. Servlet 概述

JavaWeb应用程序中所有的请求-响应都是由Servlet来完成的。Servlet没有main方法,当用户访问服务器时,服务器通过调用Servlet的某些方法来完成整个的处理过程。

1.1 Servlet 工作流程

浏览器提交的请求是遵循HTTP协议的,提交的请求通过服务器(Tomcat)接收解析,并且封装成为HttpServletRequest类型的reqeust对象,所有的HTTP头数据都可以通过request的相应方法查询到。

服务器(Tomcat)同时把输出流封装为HttpServletResponse类型的response对象,通过设置response属性,就可以控制输出的内容。服务器(Tomcat)把request和response作为参数,调用Servlet的相应方法,如doGet或者doPost方法。

1.2 Servlet 接口

Servlet 实现了javax.servlet.Servlet接口的类,Servlet接口规定了特定的方法来处理特定的请求,我们只需要实现Servlet的相关方法,用户访问Web应用程序的时候,服务器(Tomcat)会自动调用这些方法来完成业务处理。Servlet业务处理主要方法有一下两种:

  1. Get方法:表示查询信息,URL中可以附带少量的参数信息,但是URL的总长度不能超过255个字符,并且参数会显示在浏览器的地址栏中。
  2. Post方法:表示提交信息,一般用于调教大数据信息或者文件,提交的内容不受长度限制,并且不会显示在浏览器地址栏。

1.3 实现 Servlet

package com.servlet.demo;

import java.io.IOException;

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class ServletDemo implements Servlet {

    private ServletConfig config;

    public void destroy() {
        System.out.println("执行destory()方法...");
    }

    public ServletConfig getServletConfig() {
        return this.config;
    }

    public String getServletInfo() {
        return this.config.getServletName();
    }

    public void init(ServletConfig config) throws ServletException {
        System.out.println("执行init()方法...");
        this.config = config;
    }

    public void service(ServletRequest request, ServletResponse response) 
            throws ServletException, IOException {
        System.out.println("执行Service()方法...");
    }
}

1.4 配置 Servlet

在JavaWeb应用程序中,Web容器还必须知道浏览器怎么访问这个Servlet。所以我们要配置Servlet的类文件与访问方式。这个配置在Web应用程序的描述文件web.xml文件中。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns="http://xmlns.jcp.org/xml/ns/javaee"
 xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
 id="WebApp_ID" version="3.1">
    <display-name>servlet</display-name>
    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
    </welcome-file-list>
    
    <!-- 配置Servlet -->
    <servlet>
        <servlet-name>ServletDemo</servlet-name>
        <servlet-class>com.servlet.demo.ServletDemo</servlet-class>
         <!-- 将servlet随Tomcat启动时自动实例化 -->
        <load-on-startup>0</load-on-startup>
    </servlet>
    <!-- 映射Servlet -->
    <servlet-mapping>
        <servlet-name>ServletDemo</servlet-name>
        <url-pattern>/demo</url-pattern>
    </servlet-mapping>
</web-app>

1.4.1 配置 servlet 标签

<servlet></servlet>分别为Servlet配置的开始与结束标签。中间部分为Servlet的配置信息,其中 <servlet-name><servlet-class> 属性是必须配置的。

  1. <servlet-name>:配置servlet名称,可以为任意字符串,但是保证该名称在web.xml文件中唯一,该名称供<servlet-mapping><filter>等使用;
  2. <servlet-class>:Servlet类所在包及类名;
1. 在容器启动时实例化 Servlet

<load-on-startup>:配置Servlet的加载模式,可选0和1,如果配置1,服务器会在启动时自动加载该Servlet;

2. 设置Servlet初始化参数

<init-param>:可以在servlet中配置初始化参数,包括一个参数名称和参数值,servlet可以通过其ServletConfig对象的getInitParam(String paramName)方法来获取初始化参数值;

  • <param-name>:配置初始化参数名称;
  • <param-value>:配置初始化参数值;

1.4.2 配置 servlet-mapping标签

  1. <servlet-name>:配置指明采用该访问方式Servlet的名称,与<servlet>标签中的<servlet-name>须一致;
  2. <url-pattern>:配置该servlet的访问方式,该标签前面加上Web应用程序的路径,再加上服务器域名端口号信息就是访问该servlet的网址。

JavaWeb学习笔记(一)—— Servlet

1.5 Servlet生命周期和生命周期方法

Servlet 生命周期可被定义为从创建直到毁灭的整个过程。以下是 Servlet 遵循的过程:

  • Servlet 通过调用 init () 方法进行初始化;
  • Servlet 调用 service() 方法来处理客户端的请求;
  • Servlet 通过调用 destroy() 方法终止(结束);

以上的init()service()destory() 三个方法属于Servlet的生命周期方法。

  1. init():被设计成只调用一次。它在第一次创建 Servlet 时被调用,在后续每次用户请求时不再调用。
  2. service(): 执行实际任务的主要方法。Servlet 容器(即 Web 服务器)调用 service() 方法来处理来自客户端的请求,并把格式化的响应写回给客户端。每次服务器接收到一个 Servlet 请求时,服务器会产生一个新的线程并调用服务。
  3. destory():只会被调用一次,在 Servlet 生命周期结束时被调用。

2. 通过继承 HttpServlet 编写 Servlet

直接实现Servlet接口来编写Servlet不方便,需要实现的方法太多。javax.servlet.http.HttpServlet类实现了Servlet接口所有方法,通过继承HttpServlet编写Servlet,一般只需要覆盖doGet()和doPost()方法即可。

package com.servlet;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;

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

public class FirstServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        this.log("执行 doGet 方法...");
        this.execute(request, response);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        this.log("执行 doPost 方法...");
        this.execute(request, response);
    }
    
    public long getLastModified(HttpServletRequest request) {
        this.log("执行 getLastModified 方法...");
        return -1;
    }

    private void execute(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        response.setCharacterEncoding("utf-8");
        request.setCharacterEncoding("utf-8");
        //获取访问该 Servlet 的 URI
        String requestURI = request.getRequestURI();
        // 获取访问 Servlet 的方式
        String method = request.getMethod();
        
        String param = request.getParameter("param");
        
        response.setContentType("text/html");
        
        PrintWriter out = response.getWriter();
        
        out.println("<!DOCTYPE html>");
        out.println("<html>");
        out.println("<head>");
        out.println("<title>First Servlet</title>");
        out.println("</head>");
        out.println("<body>");
        
        out.println("以<span color='red'> " + method +"</span> 方式访问该页面,取得的 param 参数为:" + param+"。");
        out.println("<br/>");
        
        out.println("<form action='"+ requestURI +"' method='get'>");
        out.println("<input type='text' name='param' value='param string'/>");
        out.println("<input type='submit' value='以 GET 方式查询页面'/>");
        out.println("</form>");
        
        out.println("<form action='"+ requestURI +"' method='post'>");
        out.println("<input type='text' name='param' value='param string'/>");
        out.println("<input type='submit' value='以 Post 方式提交到页面'/>");
        out.println("</form>");
        
        out.println("<script type='text/javascript'>");
        out.println("document.wirte('本页最后更新时间为 :' + document.lastModified);");
        out.println("</script>");
        
        out.println("</body>");
        out.println("</html>");
        
        out.flush();
        out.close();
    }

}

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
id="WebApp_ID" version="3.1">
<display-name>servlet</display-name>
<welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<!-- 配置FirstServlet -->
<servlet>
    <servlet-name>FirstServlet</servlet-name>
    <servlet-class>com.servlet.FirstServlet</servlet-class>
</servlet>
<!-- 映射FirstServlet -->
<servlet-mapping>
    <servlet-name>FirstServlet</servlet-name>
    <url-pattern>/first</url-pattern>
</servlet-mapping>
</web-app>

3. 请求和响应

客户端浏览器发送给一个请求,服务器接收并处理请求,最后向客户端浏览器发送响应,结束一次Web访问过程。

3.1 获取 request 的变量

客户端浏览器发出的请求被封装成为HttpServletRequest类型对象,所有的信息包括请求地址、请求参数、提交的数据、上传的文件,客户端的IP地址,甚至是客户操作系统都包含在HttpServletRequest类型的对象中。

package com.servlet;

import java.io.IOException;
import java.io.PrintWriter;

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

public class RequestServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        
        request.setCharacterEncoding("utf-8");
        response.setCharacterEncoding("utf-8");
        response.setContentType("text/html");
        
        PrintWriter out = response.getWriter();
        
        // 获取 user-agent 请求头 
        String userAgent = request.getHeader("user-agent");
        
        String os = getOS(userAgent);
        String navigator = getNavigator(userAgent);
        
        out.println("你使用的操作系统是: "+ os +" 系统");
        out.println("<br/>");
        out.println("你使用的浏览器是: "+ navigator +" 浏览器");
        
        out.flush();
        out.close();
        
        
    }
    private static String getOS(String userAgent) {
        if (userAgent.indexOf("Ubuntu") > 0) {
            return "Ubuntu";
        }
        return "未知";
    }
    
    private static String getNavigator(String userAgent) {
        if (userAgent.indexOf("Firefox") > 0) {
            return "Mozllia Firefox";
        }
        return "未知";
    }
}

RequestServlet配置:

<servlet>
    <servlet-name>RequestServlet</servlet-name>
    <servlet-class>com.servlet.request.RequestServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>RequestServlet</servlet-name>
    <url-pattern>/request</url-pattern>
</servlet-mapping>

3.2 使用 response 生成图片验证码

package com.servlet.response;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;

public class IdentityServlet extends HttpServlet {

    public static final char[] CHARS = { '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
            'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' };

    // 获取随机数
    public static Random random = new Random();

    // 随机获取6位验证码
    public StringBuffer getIdentityCode() {

        // 创建缓存字符串
        StringBuffer buffer = new StringBuffer();

        for (int i = 0; i < 6; i++) {
            buffer.append(CHARS[random.nextInt(CHARS.length)]);
        }

        return buffer;
    }

    public static Color getRandomColor() {
        return new Color(random.nextInt(255), random.nextInt(255), random.nextInt(255));
    }

    public static Color getReverseColor(Color c) {
        return new Color(255 - c.getRed(), 255 - c.getGreen(), 255 - c.getBlue());
    }

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

        response.setContentType("image/jpeg");

        String identityCode = getIdentityCode().toString();
        request.getSession(true).setAttribute("identityCode", identityCode);
        // 设置验证码图片的高度和宽度
        int width = 100;
        int height = 30;

        Color backColor = getRandomColor();
        Color frontColor = getReverseColor(backColor);

        BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

        Graphics2D g = bi.createGraphics();
        g.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 16));

        g.setColor(backColor);
        g.fillRect(0, 0, width, height);

        g.setColor(frontColor);
        g.drawString(identityCode, 18, 20);

        for (int i = 0; i < random.nextInt(100); i++) {
            g.drawRect(random.nextInt(width), random.nextInt(height), 1, 1);
        }

        ServletOutputStream out = response.getOutputStream();
        JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);

        encoder.encode(bi);
        out.flush();

    }

}

IdentityServlet配置信息:

<servlet>
    <servlet-name>identity</servlet-name>
    <servlet-class>com.servlet.response.IdentityServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>identity</servlet-name>
    <url-pattern>/identity</url-pattern>
</servlet-mapping>

4. 读取 web.xml 参数

在JavaWeb开发中,源码上微小的变动就需要重新编译class文件,然后重新部署,会带来大量的工作。现在,可以将一些常亮信息存放于配置文件中。当程序需求发生变化,只需修改配置文件即可。web.xml提供了设置初始化参数的功能,可以将这些信息配置在web.xml文件中。

4.1 初始化参数(init-param)

web.xml配置servlet时,

  1. String getInitParameter(String parameterName) : 获取指定初始化参数的值;
  2. Enumeration<String> getInitParameterNames() : 获取所有初始化参数名称;

web.xml配置信息如下:

<!-- 配置InitParamServlet -->
<servlet>
    <servlet-name>InitParamServlet</servlet-name>
    <servlet-class>com.servlet.initParam.InitParamServlet</servlet-class>
    <!-- 配置初始化参数 -->
    <init-param>
        <param-name>admin</param-name>
        <param-value>martin0319</param-value>
    </init-param>
    <init-param>
        <param-name>adminPassword</param-name>
        <param-value>319517</param-value>
    </init-param>
</servlet>
<servlet-mapping>
    <servlet-name>InitParamServlet</servlet-name>
    <url-pattern>/param</url-pattern>
</servlet-mapping>

InitParamServlet

package com.servlet.initParam;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class InitParamServlet extends HttpServlet {

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

        response.setCharacterEncoding("utf-8");
        request.setCharacterEncoding("utf-8");

        response.setContentType("text/html");

        PrintWriter out = response.getWriter();

        String requestURI = request.getRequestURI();

        out.println("<!DOCTYPE HTML>");
        out.println("<html>");
        out.println("<head>");
        out.println("<title>Test Init-Param</title>");
        out.println("</head>");
        out.println("<body>");
        out.println("<form action='" + requestURI + "' method='post'>");
        out.println("账号:<input type='text' name='loginName' />"
                + "<br />"
                + "密码:<input type='password' name='loginPassword' />"
                + "<br />"
                + "<input type='submit' value='Log In Now' />");
        out.println("</form>");
        out.println("</body>");
        out.println("</html>");

    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) 
        throws ServletException, IOException {
        
        response.setCharacterEncoding("utf-8");
        request.setCharacterEncoding("utf-8");

        response.setContentType("text/html");

        PrintWriter out = response.getWriter();
        
        // 获取由表达提交的数据 
        String username = request.getParameter("loginName");
        String password = request.getParameter("loginPassword");
        
        /*
 * 获取web.xml中配置参数
 * 1、获取ServletConfig对象
 * 2、获取所有初始化参数名称
 * 3、通过初始化参数名称获取初始化参数值
 */
        ServletConfig config = this.getServletConfig();
        
        String initName = config.getInitParameter("admin");
        String initPassword = config.getInitParameter("adminPassword");
        
        if (username.equalsIgnoreCase(initName) && password.equalsIgnoreCase(initPassword)) {
            out.println("欢迎登陆...");
        } else {
            out.println("用户名或密码错误...");
        }

    }

}

4.2 上下文参数(context-param)

由于 init-param 是配置在<servlet>标签中的,只能由这个servlet类调用,不能被其他的servlet调用,不是全局参数。如果需要配置全局参数,需要用到上下文参数(文档参数),上下文参数使用<context-param>标签配置。

上下文参数可以使用ServletConfig对象来获取。Servlet中通过getServletConfig().getServletContext()方法来获取一个ServletContext对象。

  1. String getInitParameter(String parameterName): 获取指定上下文参数的值;
  2. Enumeration<String> getInitParameterNames() : 获取所有上下文参数名称;

ContextParamServlet配置信息:

<context-param>
    <param-name>upload folder</param-name>
    <param-value>attachment</param-value>
</context-param>
<context-param>
    <param-name>allowed file type</param-name>
    <param-value>.jpg, .gif, .bmp</param-value>
</context-param>
<!-- 配置 ContextParamServlet -->
<servlet>
    <servlet-name>ContextParamServlet</servlet-name>
    <servlet-class>com.contextParam.ContextParamServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>ContextParamServlet</servlet-name>
    <url-pattern>/context</url-pattern>
</servlet-mapping>

ContextParamServlet类:

package com.contextParam;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ContextParamServlet extends HttpServlet {
    
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        
        response.setCharacterEncoding("utf-8");
        request.setCharacterEncoding("utf-8");
        
        response.setContentType("text/html");
        
        PrintWriter out = response.getWriter();
        // 获取ServletContext对象
        ServletContext context = this.getServletConfig().getServletContext();
        // 获取上下文参数
        String uploadFolder = context.getInitParameter("upload folder");
        String allowFileType = context.getInitParameter("allowed file type");
        // 获取实际磁盘路径
        String realPath = context.getRealPath(uploadFolder);
        
        out.println("<!DOCTYPE html>");
        out.println("<html>");
        out.println("<head>");
        out.println("<title>读取上下文参数</title>");
        out.println("</head>");
        out.println("<body>");
        
        out.println("<table>");
        out.println("<tr>");
        out.println("<td>上传文件夹:</td>");
        out.println("<td>" + uploadFolder + "</td>");
        out.println("</tr>");
        
        out.println("<tr>");
        out.println("<td>实际磁盘路径:</td>");
        out.println("<td>" + realPath + "</td>");
        out.println("</tr>");
        
        out.println("<tr>");
        out.println("<td>允许上传类型:</td>");
        out.println("<td>" + allowFileType + "</td>");
        out.println("</tr>");
        out.println("</table>");
        
        out.println("</body>");
        out.println("</html>");
        
        out.flush();
        out.close();
        
    }

}