Servlet基础知识点回顾(一)

本文最后更新于:2021年10月4日 下午

本篇文章用于简易回顾Servlet基础知识,并阅读自己之前项目里写过的一些Servlet代码。

Servlet基础知识点回顾(一)

Servlet生命周期

什么是Servlet?

Servlet是Sun公司专门用于开发动态Web网页的一门技术,Servlet API中封存了大量的接口和一些实现类来帮助程序员处理web请求和响应。Servlet规定了一系列接口规范,是软件项目工程化的重要体现。

Servlet需要在web.xml中配置路径名和映射,将Servlet映射到Web容器中,并指定Servlet需要处理的路径,Web容器才能找到这个Servlet并进行处理。

<servlet>
    <servlet-name>HelloServlet</servlet-name>
    <servlet-class>com.rootzwy.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>HelloServlet</servlet-name>
    <url-pattern>/hello</url-pattern>
</servlet-mapping>

第一个Servlet程序:

public class HelloServlet extends HttpServlet {
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        PrintWriter writer = resp.getWriter();
        writer.println("Hello, Servlet!");

    }

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

Servlet基本原理

以HTTP协议为例,浏览器端发送HTTP请求到Web容器,Web容器接收到这个请求,并将其中诸如请求头、请求体等信息封装为HttpServletRequest对象,同时也创建一个HttpServletResponse对象。在浏览器端首次访问时,创建特定的Servlet对象,这个对象用于处理收到的请求对象,并且编辑响应对象,再将响应对象回传给Web容器,Web容器将响应对象读取转换后发送给浏览器端。

Servlet映射问题

一个Servlet可以指定一个映射路径。

<servlet>
    <servlet-name>HelloServlet</servlet-name>
    <servlet-class>com.rootzwy.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>HelloServlet</servlet-name>
    <url-pattern>/hello</url-pattern>
</servlet-mapping>

一个Servlet可以指定多个映射路径。

<servlet>
    <servlet-name>HelloServlet</servlet-name>
    <servlet-class>com.rootzwy.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>HelloServlet</servlet-name>
    <url-pattern>/hello</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>HelloServlet</servlet-name>
    <url-pattern>/hello1</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>HelloServlet</servlet-name>
    <url-pattern>/hello2</url-pattern>
</servlet-mapping>

一个Servlet可以指定通用路径。

<servlet>
    <servlet-name>HelloServlet</servlet-name>
    <servlet-class>com.rootzwy.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>HelloServlet</servlet-name>
    <url-pattern>/hello/*</url-pattern>
</servlet-mapping>

<!-- 或者指定以特定后缀结尾的路径 -->
<servlet-mapping>
    <servlet-name>HelloServlet</servlet-name>
    <!-- 例如:localhost:8080/asdf/hello.zwy -->
    <url-pattern>*.rootzwy</url-pattern>
</servlet-mapping>

一个Servlet可以覆盖index

<servlet>
    <servlet-name>HelloServlet</servlet-name>
    <servlet-class>com.rootzwy.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>HelloServlet</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>

<!-- 对于具体的路径,Servlet会优先走具体路径,比如 /hello -->

Servlet中的常用对象和方法

HttpServlet

HttpServlet是一个抽象类,继承自GenericServlet类,实现了Servlet接口,封装了一些处理Web请求响应和Servlet管理的方法,例如经典的doGet()doPost()getServletContext()等。程序员通过继承此类来重写对应方法,经过service()方法来处理请求和响应。

ServletContext

Web容器启动时,Web容器为每一个Web程序都创建一个ServletContext对象,来充当Web程序和Web容器的上下文,每个Servlet对象代表一个Web程序。以下列出ServletContext的一些常见基本功能。

Servlet间共享数据:

// In Servlet1
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
    ServletContext context = this.getServletContext();
    String userName = "zwy";
    // 将userName存放进ServletContext中,将在Servlet2中访问这个属性
    context.setAttribute("userName", userName);
}

// In Servlet2
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    ServletContext context = this.getServletContext();
    // 从ServletContext获取userName
    String userName = (String) context.getAttribute("userName");
    PrintWriter writer = resp.getWriter();
    resp.setCharacterEncoding("utf-8");
    resp.setContentType("text/html");
    // 输出userName
    writer.println("Hello, " + userName);

}

获取web.xml相应元素:

<!-- In web.xml -->
<contxt-param>
    <param-name>mysqlUrl</param-name>
    <param-value>jdbc:mysql://localhors:3306</param-value>
</contxt-param>
// In Servlet
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    ServletContext context = this.getServletContect();
    String url = context.getInitParameter("mysqlUrl");
    resp.getwriter().println(url);
}

读取资源文件

// In Servlet
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    // 此处是编译之后资源文件的路径
    InputStream is = this.getServletContext().getResourceAsStream("/WEB-INF/classes/test.properties");
    Properties properties = new Properties();
    properties.load(is);
    String name = properties.getProperty("username");
    String pwd = properties.getProperty("password");

    PrintWriter writer = resp.getWriter();
    resp.setContentType("text/html");
   	resp.setCharacterEncoding("utf-8");
    writer.println("User: " + name);
    writer.println("Password: " + pwd);

}
# In properties
username=root
password=123456

HttpServletResponse

HttpServletResponse是一个接口,继承了ServletResponse。Web容器在接收到请求时,会针对这个请求,默认创建一个实现这个响应接口的对象,用于封装响应,让程序员通过代码去操作响应信息。

常用方法

PrintWriter getWriter() throws IOException;
ServletOutputStream getOutputStream() throws IOException;

void setCharacterEncoding(String var1);
void setContentLength(int var1);
void setContentType(String var1);

void setDateHeader(String var1, long var2);
void addDateHeader(String var1, long var2);
void setHeader(String var1, String var2);
void addHeader(String var1, String var2);
void setIntHeader(String var1, int var2);
void addIntHeader(String var1, int var2);

// ......

常见应用

向浏览器输出文本或其它数据。

例如下载文件:

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    // 获取下载文件的路径
    ServletContext context = this.getServletContext();
    String path = context.getRealPath("/WEB-INF/classes/txt/yinsha.txt");
    String fileName = path.substring(path.lastIndexOf("\\") + 1);
    System.out.println("输出文件的名称: " + fileName);
    // 设置浏览器下载头和编码格式
    resp.setHeader("Content-Disposition", "attachment;filename="
            + URLEncoder.encode(fileName, StandardCharsets.UTF_8));
	// resp.setContentType("text/html"); // 若指定当前页面为html,则下载的文件都是html文件
    resp.setCharacterEncoding("utf-8");
    // 获取文件流
    FileInputStream in = new FileInputStream(path);
    // 创建缓冲区
    int len;
    byte[] fileBuffer = new byte[1024];
    // 获取输出流
    ServletOutputStream out = resp.getOutputStream();
    // 输出流从Buffer获取文件流
    while ((len = in.read(fileBuffer)) != -1) {
        out.write(fileBuffer, 0, len);
    }
    out.close();
    in.close();
}

实现简易的动态验证码:

public class SecurityCodeImgServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 网页定时刷新
        resp.setHeader("refresh", "3");

        // 在缓存中创建一个图片
        BufferedImage image = new BufferedImage(80, 20, BufferedImage.TYPE_INT_RGB);
        // 得到图片的画笔,并写入验证码
        Graphics2D imgGraphics = (Graphics2D) image.getGraphics();
//        Graphics2D imgGraphics = image.createGraphics();
        imgGraphics.setColor(Color.WHITE);
        imgGraphics.fillRect(0 ,0, 80, 20);
        imgGraphics.setColor(Color.BLACK);
        imgGraphics.setFont(new Font(null, Font.BOLD, 20));
        imgGraphics.drawString(randomSecurityCodeString(), 0, 20);

        // 告诉浏览器响应页面的格式
        resp.setContentType("image/png");
        // Expires表示存在时间,允许客户端在这个时间之前不去检查(发请求)
        resp.setDateHeader("expires", -1);
        // Cache-control 用于控制HTTP缓存(在HTTP/1.0中可能部分没实现,仅仅实现了 Pragma: no-cache)
        resp.setHeader("Cache-Control", "no-cache");
        resp.setHeader("Pragma", "no-cache");

        // 把图片写给浏览器(formatName必须是可以使用的图片后缀名)
        ImageIO.write(image, "png", resp.getOutputStream());
    }

    private String randomSecurityCodeString() {
        Random random = new Random();
        // 设置编码为7位
        int digit = 7;
        // 生成编码字符串
        int limit = 1;
        for (int i = 0; i < digit; i++) {
            limit *= 10;
        }
        String randomNum = "" + random.nextInt(limit);
        
        // 字符串前置补0
        StringBuffer stringBuffer = new StringBuffer();
        for (int i = 0; i < digit - randomNum.length(); i++) {
            stringBuffer.append("0");
        }
        stringBuffer.append(randomNum);
        return stringBuffer.toString();
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}
重定向
void sendRedirect(String var1) throws IOException; // var1是web路径名,如:hello(没有web主路径的"/")

登录重定向和请求转发流程:

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    // 在一个Servlet中重定向到负责登录的jsp或Servlet
    resp.sendRedirect("login.jsp");
}
<%--
 在此页面(login.jsp)获取用户名信息,然后POST到action下的Servlet页面
--%>
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
    <title>Title</title>
</head>
<body>

<form action="${pageContext.request.contextPath}/waiting" method="post">
    用户名:
    <label>
        <input type="text" name="username">
    </label> <br>
    密 码:
    <label>
        <input type="password" name="password">
    </label> <br>
    识别码:
    <label>
        <input type="text" name="uid">
    </label> <br>
    <button type="submit">登录</button>
</form>

</body>
</html>
// 在此Servlet中可以获取用户名密码,并进行操作,登录成功后请求转发或重定向到WelcomeServlet
public class loginWaitingServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String uid = req.getParameter("uid");
        String uname = req.getParameter("username");
        if (!uid.equals("root")) {
            resp.sendRedirect("pwdError");
        } else {
//            resp.sendRedirect("welcome");
            // 请求转发传递数据
            RequestDispatcher dispatcher = req.getRequestDispatcher("welcome");
            dispatcher.forward(req, resp);
        }
    }

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

HttpServletRequest

HttpServletRequest是一个接口,继承了ServletRequest。Web容器在接收到通过HTTP协议发送过来的请求时,会针对这个请求,默认创建一个实现这个请求接口的对象,用于封装请求的所有信息,让程序员通过代码去操作请求信息。

常用方法

// 几乎所有...

常见应用

获取前端参数
// 用于获取普通的前端参数
String getParameter(String var1);
// 用于获取有多个值的前端参数
 String[] getParameterValues(String var1);
请求转发
RequestDispatcher getRequestDispatcher(String var1);