赣州新闻网

首页 / 科技

世界500强企业,富士康为何比腾讯、华为排名还靠前?

2020-01-04 · 赣州新闻网 · 科技

Servlet 是 Java Web 开发的起点,几乎所有的 Java Web 框架都是基于 Servlet 的封装,其中最主要的就是 Servlet 和 Filter 接口。我重新学习了一遍 Servlet,对 Java Web 开发有了更深的理解。

1. Servlet 是什么

从 API 可以看出,Servlet 本质是一套接口(Interface)。那么接口的本质是什么?是规范、是协议,所以我们常说要面向接口编程,而不是面向实现。接口是连接 Servlet 和 Servlet 容器(Tomcat、Jetty 等)的关键。

Servlet 接口定义了一套处理网络请求的规范,所有实现 Servlet 的类都需要实现它的五个方法,其中最主要的是两个生命周期方法 init 和 destroy,还有一个处理请求的 service 方法。也就是说,所有实现 Servlet 接口的类,或者说,所有想要处理网络请求的类,都需要回答这三个问题:

• 你初始化时要做什么

• 你销毁时要做什么

• 你接收到请求时要做什么

这是 Java EE 给的一种规范!就像阿西莫夫的机器人三大定律一样,是规范!

看一下 Servlet 的接口定义,即 Servlet 和 Servlet 容器的规范。我们最关心的就是 service 方法,在这里处理请求。


史上Java Servlet 经典总结


2. Servlet 如何工作

Servlet 想要工作离不开 Servlet 容器,比如我们最常用的 Tomcat。它监听了某个端口,http 请求过来后,容器根据 url 等信息,确定要将请求交给哪个 Servlet 去处理,然后调用 Servlet 的 service 方法,service 方法返回一个 response 对象,容器将 respose 对象解析之后封装成一个 http 响应返回客户端。


史上Java Servlet 经典总结


Servlet 工作原理

3. Servlet 体系结构

Servlet 规范类有这么几个:

• Servlet

• ServletContext

• ServletConfig

• ServletRequest

• ServletResponse

Servlet 运行模式是典型的「握手型」交互。举个例子:

买早点的场景。找到一家早点铺(SerletContext 开始),看到牌面上写着可以加肉松(ServletConfig),就告诉老板我要加肉松的煎饼果子,拿出手机扫码支付了五块钱(ServletRequest)。老板娴熟地给我摊好,然后递给我(ServletResponse),我就美滋滋地离开了(ServletContext 结束)。

引用开源中国红薯的一段话,原文 在这里。

为什么我这么强调 HttpServletRequest 和 HttpServletResponse 这两个接口,因为 Web 开发是离不开 HTTP 协议的,而 Servlet 规范其实就是对 HTTP 协议做面向对象的封装,HTTP协议中的请求和响应就是对应了 HttpServletRequest 和 HttpServletResponse 这两个接口。

你可以通过 HttpServletRequest 来获取所有请求相关的信息,包括 URI、Cookie、Header、请求参数等等,别无它路。因此当你使用某个框架时,你想获取 HTTP 请求的相关信息,只要拿到 HttpServletRequest 实例即可。而 HttpServletResponse接口是用来生产 HTTP 回应,包含 Cookie、Header 以及回应的内容等等。

4. Servlet 实践

我们来写一个简单的 Servlet,在 doGet 方法打印所有请求的信息。

public class HelloWorld extends HttpServlet {


@Override

public void init(ServletConfig config) throws ServletException {

super.init(config);

System.out.println(\"init helloworld: \" + config);

}


@Override

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {

System.out.println(\"********doGet********\");

resp.setContentType(\"text/html;charset=UTF-8\");

resp.setCharacterEncoding(\"UTF-8\");


System.out.println(\"method: \" + req.getMethod());

System.out.println(\"charsetEncoding: \" + req.getCharacterEncoding());

System.out.println(\"contentType: \" + req.getContentType());

System.out.println(\"contentLength: \" + req.getContentLength());

System.out.println(\"requestUrl: \" + req.getRequestURL());

System.out.println(\"servletPath: \" + req.getServletPath());

System.out.println(\"contextPath: \" + req.getContextPath());

System.out.println(\"requestUri: \" + req.getRequestURI());

System.out.println(\"locale: \" + req.getLocale());

System.out.println(\"authType: \" + req.getAuthType());

System.out.println(\"scheme: \" + req.getScheme());

System.out.println(\"protocol: \" + req.getProtocol());

System.out.println(\"serverPort: \" + req.getServerPort());

System.out.println(\"remoteHost: \" + req.getRemoteHost());

System.out.println(\"remoteAddr: \" + req.getRemoteAddr());

System.out.println(\"remoteUser: \" + req.getRemoteUser());

System.out.println(\"requestedSessionId: \" + req.getRequestedSessionId());

System.out.println(\"pathInfo: \" + req.getPathInfo());

System.out.println(\"isSecure: \" + req.isSecure());

System.out.println(\"servletName: \" + req.getServerName());

System.out.println(\"pathTranslated: \" + req.getPathTranslated());


System.out.println(\"++headers++\");

Enumeration headerNames = req.getHeaderNames();

while (headerNames.hasMoreElements()) {

String paramName = (String) headerNames.nextElement();

String paramValue = req.getHeader(paramName);

System.out.println(\"name: \" + paramName + \", value: \" + paramValue);

}

System.out.println(\"--headers--\");


System.out.println(\"++parameters++\");

Enumeration<String> parameterNames = req.getParameterNames();

while (parameterNames.hasMoreElements()) {

String name = parameterNames.nextElement();

String value = req.getParameter(name);

System.out.println(\"name: \" + name + \", value: \" + value);

}

System.out.println(\"--parameters--\");


System.out.println(\"++attributes++\");

Enumeration<String> attributeNames = req.getAttributeNames();

while (attributeNames.hasMoreElements()) {

String name = attributeNames.nextElement();

Object value = req.getAttribute(name);

System.out.println(\"name: \" + name + \", value: \" + value);

}

System.out.println(\"--attributes--\");


System.out.println(\"++cookies++\");

Cookie cookies = req.getCookies();

for (Cookie cookie : cookies) {

System.out.println(\"name: \" + cookie.getName() + \", value: \" + URLDecoder.decode(cookie.getValue(), \"utf-8\"));

}

System.out.println(\"--cookies--\");


PrintWriter writer = resp.getWriter();

try {

writer.println(\"<h1>Hello world!</h1>\");

writer.flush();

} finally {

writer.close();

}

}


@Override

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {

System.out.println(\"--------doPost--------\");

doGet(req, resp);

}


@Override

public void destroy() {

super.destroy();

System.out.println(\"destroy helloworld\");

}}

在 web.xml 配置上面的 Servlet。

<?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>HelloWorld</servlet-name>

<servlet-class>com.richie.servlet.HelloWorld</servlet-class>

</servlet>


<servlet-mapping>

<servlet-name>HelloWorld</servlet-name>

<url-pattern>/helloworld</url-pattern>

</servlet-mapping>

</web-app>

配置好 Tomcat,运行输出,然后使用 postman 发送 post 请求 http://localhost:8080/helloworld,并加上参数username=admin&password=123。

--------doPost--------********doGet********method: POSTcharsetEncoding: nullcontentType: application/x-www-form-urlencoded

contentLength: 23requestUrl: http://localhost:8080/helloworld

servletPath: /helloworld

contextPath: requestUri: /helloworld

locale: zh_CN

authType: nullscheme: http

protocol: HTTP/1.1serverPort: 8080remoteHost: 0:0:0:0:0:0:0:1remoteAddr: 0:0:0:0:0:0:0:1remoteUser: nullrequestedSessionId: F17A359B0544082FC6A6C5F62E672E8ApathInfo: nullisSecure: falseservletName: localhost

pathTranslated: null++headers++name: host, value: localhost:8080name: connection, value: keep-alive

name: content-length, value: 23name: cache-control, value: max-age=0name: origin, value: http://localhost:8080name: upgrade-insecure-requests, value: 1name: content-type, value: application/x-www-form-urlencoded

name: user-agent, value: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36name: accept, value: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8

name: referer, value: http://localhost:8080/

name: accept-encoding, value: gzip, deflate, br

name: accept-language, value: zh-CN,zh;q=0.9,en;q=0.8

name: cookie, value: __utmz=111872281.1521468435.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); Idea-734b2b82=47daaaf7-bc69-41ca-9234-dffa6c217ef8; _ga=GA1.1.2085956305.1521468435; Webstorm-717d1cc9=b6b7f7ea-d8d3-4891-8e20-0dca54d5cbd2; __utmc=111872281; __utma=111872281.2085956305.1521468435.1529898141.1530148517.11; SESSION=12913786-3c46-421d-ac2c-02c9c29ae03d; JSESSIONID=F17A359B0544082FC6A6C5F62E672E8A

--headers--

++parameters++

name: username, value: admin

name: password, value: 123

--parameters--

++attributes++

--attributes--

++cookies++

name: __utmz, value: 111872281.1521468435.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)

name: Idea-734b2b82, value: 47daaaf7-bc69-41ca-9234-dffa6c217ef8

name: _ga, value: GA1.1.2085956305.1521468435

name: Webstorm-717d1cc9, value: b6b7f7ea-d8d3-4891-8e20-0dca54d5cbd2

name: __utmc, value: 111872281

name: __utma, value: 111872281.2085956305.1521468435.1529898141.1530148517.11

name: SESSION, value: 12913786-3c46-421d-ac2c-02c9c29ae03d

name: JSESSIONID, value: F17A359B0544082FC6A6C5F62E672E8A

--cookies--

可以看出,http 请求的基本信息都能取到,每个方法都有它的含义,具体可以参考 菜鸟教程 上的解释。

5. Filter 过滤器

Filter 和 Servlet 一样重要,它可以实现许多功能,比如敏感词过滤、用户验证等。它也是一个接口,和 Servlet 类似,有 init 和 destroy 方法,最重要的是 doFilter 方法。

Filter 主要用于对用户请求进行预处理,也可以对 HttpServletResponse 进行后处理。使用 Filter 的完整流程:Filter 对用户请求进行预处理,接着将请求交给 Servlet 进行处理并生成响应,最后 Filter 再对服务器响应进行后处理。

public interface Filter {

default void init(FilterConfig filterConfig) throws ServletException {

}


void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;


default void destroy() {

}}

除此之外,规范类还有 FilterChain、FilterConfig,Filter 使用了责任链的设计模式,传递的对象就 FilterChain。

6. Filter 工作原理

当我们写好 Filter,并配置对哪个 web 资源进行拦截后,web 服务器每次在调用 Servlet 的 service 方法之前, 都会先调用一下 filter 的 doFilter 方法。因此,在该方法内编写代码可达到如下目的:


  • • 调用目标资源之前,让一段代码执行。
  • • 是否调用目标资源(即是否让用户访问 web 资源)。
  • • 调用目标资源之后,让一段代码执行。


web 服务器在调用 doFilter 方法时,会传递一个 FilterChain 对象进来,FilterChain 对象是 Filter 接口中最重要的一个对象,它也提供了一个 doFilter 方法,开发人员可以根据需求决定是否调用此方法。

Filter原理

7. Filter 实践

简单实现一个拦截器,打印它的生命周期。

public class LogFilter implements Filter {


@Override

public void init(FilterConfig filterConfig) {

System.out.println(\"init logFilter: \" + filterConfig);

}


@Override

public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

System.out.println(\"log doFilter pre\");

// 一定要调用 filterChain 的 doFilter 方法,继续传递事件

filterChain.doFilter(servletRequest, servletResponse);

System.out.println(\"log doFilter after\");

}


@Override

public void destroy() {

System.out.println(\"destroy logFilter\");

}}

然后配置 web.xml,一般把 filter 配置在所有的 servlet 前面,/* 表示匹配所有的请求。

<filter>

<filter-name>LogFilter</filter-name>

<filter-class>com.richie.servlet.LogFilter</filter-class>

</filter>


<filter-mapping>

<filter-name>LogFilter</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>

运行后输出,我们可以对请求和响应进行预处理和后处理,

log doFilter pre

Servlet 处理的方法

log doFilter after

这篇文章详细阐释了 Filter 的有关内容,推荐看看 Java 中的 Filter 过滤器。

另外还有 Listener 监听器的内容,后面再写吧。

多啰嗦几句。其实客户端和服务端的通信,本质上是一种 IO 操作。用户输入网址后(略去查询 DNS ),浏览器从某个端口发出数据包,里面有目标地址、请求参数等等(output)。然后经过网络一层层传递,跨越万水千山,到达服务器被 80 端口捕获到了(input),交给 Servlet 容器处理,根据请求信息拿到数据返回给客户端(output)。这是一对多的数据交换,如果请求数据的人多了,服务端的压力其实蛮大的。

更细一点说,客户端和服务端的通信是一种进程间的通信。运行在 A 主机上的 A1 进程和运行在 B 主机上的 B1 进程进行通信,它是基于 Socket 的通信,端口是一个重要的标识。

最后,开发这么多年我也总结了一套学习Java的资料,如果你在技术上面想提升自己的话,可以关注我,私信发送领取资料或者在评论区留下自己的联系方式,有时间记得帮我点下转发让跟多的人看到哦。


史上Java Servlet 经典总结


史上Java Servlet 经典总结




企业工作需求文章请求接口排名华为寻乌