关于JavaWeb中请求响应的处理:Servlet

关于JavaWeb中请求响应的处理:Servlet

Servlet是什么?

首先要指出servlet是一个interface,这个接口需要手动添加servlet-api类库到项目。 这里使用了maven进行项目管理,贴出一下dependecy

<dependency>  
 <groupId>javax.servlet</groupId>  
 <artifactId>javax.servlet-api</artifactId>  
 <version>4.0.1</version>  
</dependency>

再来看看这个接口

/*
 * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
 * ...
 * limitations under the License.
 */
// 从上面的注释可看到 Oracle 字眼,可以认为是Oracle公司开发的一个接口
package javax.servlet;

import java.io.IOException;

//这里删除了其它的注释,之后的使用上在展示每个方法的用处

public interface Servlet {
    public void init(ServletConfig config) throws ServletException;
	
    public ServletConfig getServletConfig();
	
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
	
    public String getServletInfo();
	
    public void destroy();
}

由于有Oracle字眼,所以去官方文档找了一下,找到了这么一段描述:

What Is a Servlet?

A servlet is a Java programming language class that is used to extend the capabilities of servers that host applications accessed by means of a request-response programming model. Although servlets can respond to any type of request, they are commonly used to extend the applications hosted by web servers. For such applications, Java Servlet technology defines HTTP-specific servlet classes.

The javax.servlet and javax.servlet.http packages provide interfaces and classes for writing servlets. All servlets must implement theServletinterface, which defines life-cycle methods. When implementing a generic service, you can use or extend theGenericServletclass provided with the Java Servlet API. TheHttpServletclass provides methods, such asdoGetanddoPost, for handling HTTP-specific services.

This chapter focuses on writing servlets that generate responses to HTTP requests.

连接地址:OracleJava文档第5版:Servlet描述

#机翻
Servlet是Java编程语言类,用于扩展服务器的功能,该服务器承载通过请求-响应编程模型访问的应用程序。尽管Servlet可以响应任何类型的请求,但它们通常用于扩展Web服务器托管的应用程序。对于此类应用程序,Java Servlet技术定义了HTTP特定的Servlet类。

javax.servlet和javax.servlet.http包提供用于编写​​servlet的接口和类。所有Servlet必须实现Servlet接口,该接口定义了生命周期方法。实现通用服务时,可以使用或扩展Java Servlet API随附的GenericServlet类。 HttpServlet类提供用于处理特定于HTTP的服务的方法,如doGet and doPost。

结合Oracle给的文档,得到关于Servlet以下定义:

  1. servletOracle公司开发的一个类库。不在JRE
  2. servlet是一个接口。里面有5个方法,分别是initgetServletConfigservicegetServletInfodestroy,这5个方法就是每个servlet生命周期的某个阶段的对应
  3. servlet是基于请求-响应编程的应用程序,可以响应任何类型的请求。要实现请求响应编程就必须实现servlet接口。
  4. javax.servletjavax.servlet.http 这两个包下提供了基于servlet编写请求-响应编程的其他类。

Servlet该怎么用?

这里用LoginServlet来做一个登录的操作,结合打印结果来看遍代码

package yongy.javaspring.servlet;

import javax.servlet.*;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.stream.Collectors;

public class LoginServlet implements Servlet {

    private ServletConfig servletConfig;

    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        this.servletConfig = servletConfig;
        System.out.println(String.format("%s`init", getServletInfo()));
		/**
		打印结果:login`init
		结合整个日志来看:
		1、该方法只会执行一次
		2、在service前执行
		3、在整个servlet生命周期中,servletConfig都是同一个
		从这里可以证明该方法是servlet的初始化阶段,且保存的servletConfig在整个生命周期是同一个
		*/
    }

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

    @Override
    public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
		/**
		1、用request的getReader方法,以字符流的形式读取了所有的数据并赋值给collect
		2、转编码再赋值给collect
		3、进行输出
		*/
        String collect = request.getReader().lines().collect(Collectors.joining());
        collect = new String(collect.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
        System.out.println(String.format("%s`requestJson`%s", getServletInfo(), collect));
		
		/**
		打印结果:
		login`requestJson`{	"service":"/login",	"data":{		"username":"yongy",		"password":"123456"	},	"client":{		"vCode":1,		"vName":"1.0",		"appCode":"X1"	}}
		login`requestJson`{	"service":"/login",	"data":{		"username":"yongy",		"password":"123456"	},	"client":{		"vCode":1,		"vName":"1.0",		"appCode":"X1"	}}
		这表示请求了2次,service方法被执行了2次
		结合整个日志来看,这就说明在整个生命周期内service方法就是业务处理核心。
		request可以读取请求数据,response可以输出响应数据
		*/
    }

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

    @Override
    public void destroy() {
        System.out.println(String.format("%s`destroy", getServletInfo()));
		/**
		打印出:login`destroy
		结合整个日志来看:
		1、在Tomcat关闭之前,会调用该方法
		2、不管service方法运行多少次,该方法只会被调用一次
		3、可获得servletConfig对象
		因此,该方法也就是servlet生命周期中的销毁阶段,在容器关闭前会执行该servlet的销毁程序。
		*/
    }
}
--web.xml的配置
<servlet> <!-- 声明一个servlet,以及servlet的class位置 -->
 <servlet-name>login</servlet-name>  
 <servlet-class>yongy.javaspring.servlet.LoginServlet</servlet-class>  
</servlet>  
<servlet-mapping> <!-- 对声明的servlet进行路径映射 -->
 <servlet-name>login</servlet-name>  <!-- 前面的servlet-name -->
 <url-pattern>/login</url-pattern> <!-- 结合图片,说明url-pattern是用来配置请求路径 -->
</servlet-mapping>

在Tomcat容器发布项目,并使用postman访问接口,参考下图

关闭Tomcat容器,检查日志的输出

login`init
login`requestJson`{	"service":"/login",	"data":{		"username":"yongy",		"password":"123456"	},	"client":{		"vCode":1,		"vName":"1.0",		"appCode":"X1"	}}
login`requestJson`{	"service":"/login",	"data":{		"username":"yongy",		"password":"123456"	},	"client":{		"vCode":1,		"vName":"1.0",		"appCode":"X1"	}}
login`destroy
把这里的日志,结合到前面的代码来看。往上翻!

OK,每当包含该程序的容器被运行,每个访问该路径的请求就能得到响应。


这样用对不对,好不好

以上面的LoginServlet来说,能正确的接收到请求。就代表了这么写代码是对的,完全OK的。

但是,这样做绝对不好!没有任何响应请求的程序会这么设计!为什么呢?!下篇文章说