文档章节

图解 & 深入浅出 JavaWeb:Servlet 再说几句

泥瓦匠BYSocket
 泥瓦匠BYSocket
发布于 2015/09/03 08:57
字数 1750
阅读 3041
收藏 110

Writer      :BYSocket(泥沙砖瓦浆木匠)

微         博:BYSocket

豆         瓣:BYSocket

FaceBook:BYSocket

Twitter    :BYSocket

上一篇的《 Servlet必会必知 》受到大家一致好评 — (感谢 读者 及 OSC 推荐 每日一’搏’)image

后来觉得还有些东西没点到,这边补充补充。

一、回到 HttpServlet 的 service方法

Servlet 基础接口定义了用于客户端请求处理的service方法。 当请求到达Servlet容器,由Servlet容器路由到一个Servlet实例

比如说 javax.servlet.http.HttpServlet 类 ,其中有一个 protected void service 方法如下:

private static final String METHOD_DELETE = "DELETE";
private static final String METHOD_HEAD = "HEAD";
private static final String METHOD_GET = "GET";
private static final String METHOD_OPTIONS = "OPTIONS";
private static final String METHOD_POST = "POST";
private static final String METHOD_PUT = "PUT";
private static final String METHOD_TRACE = "TRACE";
 
private static final String HEADER_IFMODSINCE = "If-Modified-Since";
 
private static final String LSTRING_FILE =
        "javax.servlet.http.LocalStrings";
private static ResourceBundle lStrings =
        ResourceBundle.getBundle(LSTRING_FILE);
/**
 * HTTP状态码304
 */
public static final int SC_NOT_MODIFIED = 304;
 
/**
 * 接收来自 public service方法的标准HTTP请求,
 * 并将它们分发给此类中定义的doXXX方法。
 */
protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
    // 获取请求方法名
    String method = req.getMethod();
    // 如果是GET请求
    if (method.equals(METHOD_GET)) {
        // 上一次修改HttpServletRequest对象的时间
        long lastModified = getLastModified(req);
        // 没有改变
        if (lastModified == -1) {
            doGet(req, resp);
        } else {
            long ifModifiedSince;
            try {
                // 获取请求头中服务器修改时间
                ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
            } catch (IllegalArgumentException iae) {
                // 获取无效
                ifModifiedSince = -1;
            }
            // 如果请求头服务器修改时间迟
            if (ifModifiedSince < (lastModified / 1000 * 1000)) {
                // 设置修改HttpServletResponse对象的时间,重新设置浏览器的参数
                              //maybeSetLastModified(resp, lastModified);
                // 调用doGet方法
                                doGet(req, resp);
            } else {
                // 304 HTTP状态码
                resp.setStatus(SC_NOT_MODIFIED);
            }
        }
    } else if (method.equals(METHOD_HEAD)) {
        long lastModified = getLastModified(req);
      //maybeSetLastModified(resp, lastModified);
        doHead(req, resp);
    } else if (method.equals(METHOD_POST)) {
        doPost(req, resp);
    } else if (method.equals(METHOD_PUT)) {
        doPut(req, resp);
    } else if (method.equals(METHOD_DELETE)) {
        doDelete(req, resp);
    } else if (method.equals(METHOD_OPTIONS)) {
        doOptions(req,resp);
    } else if (method.equals(METHOD_TRACE)) {
        doTrace(req,resp);
    } else {
        // 如果没有被请求到的话
        String errMsg = lStrings.getString("http.method_not_implemented");
        Object[] errArgs = new Object[1];
        errArgs[0] = method;
        errMsg = MessageFormat.format(errMsg, errArgs);
        // 501 HTTP状态码
        resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
    }
}

 

代码逻辑详解如下:

1、HttpServlet的 protected void service方法 用于接受 public service接收的标准HTTP请求

也就是说,HttpServlet 重写了父类 GenericServlet 的 service方法。如图显示的是该方法,将从容器获取的 ServletRequest 和 ServletResponse 对象强制转化成 用于HTTP处理的HttpServletRequest 和 HttpServletResponse 对象。然后将两个对象路由给了 HttpServlet的 protected void service方法(图中代码选中处)

image

2、然后根据请求的方法名,分发到此类定义的doXXX方法。如果没有被请求到的话,则返回501 HTTP 状态码。

这样子仿佛明白了什么,也就是说,如果你在 HelloServlet重写doGet方法,这里分发到就是HttpServlet的子类HelloServlet的doGet方法。

哦~ 还有,501 HTTP 状态码 — 未实现(Not implemented)表示服务器不支持实现请求所需要的功能。例如,客户发出了一个服务器不支持的PUT请求。原来如此,所谓死记硬背这些HTTP 状态码有什么用?这样的记忆才是最有效的。

休息休息,小广告插一下 :(维持生计,O(∩_∩)O~)

涉及到的代码都会在开源项目 servlet-core-learning 。简介 — Servlet/JSP学习积累的例子,是Java EE初学者及Servlet/JSP核心技术巩固的最佳实践

大致就是这两步骤。这就是service的工作流程

1、接受 public service接收的标准HTTP请求。

2、分发到定义的doXXX方法

二、GET 请求的处理详解

上面对于GET请求代码处理如下:

// 如果是GET请求
if (method.equals(METHOD_GET)) {
    // 上一次修改HttpServletRequest对象的时间
    long lastModified = getLastModified(req);
    // 没有改变
    if (lastModified == -1) {
        doGet(req, resp);
    } else {
        long ifModifiedSince;
        try {
            // 获取请求头中服务器修改时间
            ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
        } catch (IllegalArgumentException iae) {
            // 获取无效
            ifModifiedSince = -1;
        }
        // 如果请求头服务器修改时间迟
        if (ifModifiedSince < (lastModified / 1000 * 1000)) {
            // 设置修改HttpServletResponse对象的时间,重新设置浏览器的参数
          //maybeSetLastModified(resp, lastModified);
            // 调用doGet方法
            doGet(req, resp);
        } else {
            // 304 HTTP状态码
            resp.setStatus(SC_NOT_MODIFIED);
        }
    }
}

这里,

1、定义了 getLastModified(req) 方法。用于获取上一次修改HttpServletRequest对象的时间。如果lastModified为默认的 –1L,则总是刷新

这个getLastModified,是HttpServlet定义了用于支持有条件GET操作。即当客户端通过GET请求获取资源时,当资源自第一次获取那个实际点发生更改后才再次发生数据,否则将使用客户端缓存的数据。

在一些适当的场合,实现此方法可以更有效的利用网络资源,减少不必要的数据发送。

2、如果getLastModified方法的返回值是一个正数,那就要分以下两种情况考虑:

    (1)如果请求头没有包含If-Modified-Since头字段(应该是第一次访问资源时候) 或者 其getLastModified返回值比If-Modified-Since头字段指定时间,则调用doGet返回生成response 和 设置Last-Modified 消息头

    (2)如果其getLastModified返回值比If-Modified-Since头字段指定时间,则返回一个304状态给客户端,表示让客户端继续使用以前缓存的页面

比如说 304 这个场景我在《 JavaEE 要懂的小事:一、图解Http协议 》文章中提到,第一次访问 百度 首页时,有些资源会成功获取 返回200再次F5,有些资源或直接调用客户端的缓存数据,则返回304

image1_thumb

三、Servlet线程问题

Servlet容器可以并发路由多个请求到 Servlet 的 service方法。为了处理这些请求,Servlet必须在并发及线程安全问题做好处理。上一篇的《 Servlet必会必知 》提到定义全局变量会造成线程安全问题。在开发Servlet时,考虑线程安全问题提出了一下解决

1、实现 SingleThreadModel 接口

    Servlet2.4 已经提出不提倡使用。实现此接口,Servlet容器为每个新的请求创建一个单独的Servlet实例。这会有严重性能问题

2、同步锁

    使用synchronized关键字,虽然可以保证只有一个线程可以访问被保护区段,已达到保证线程安全。但是系统性能及并发量大大降低。不可取~

3、避免使用实例变量,即Servlet中全局变量。使用局部变量 (推荐)

    方法中的局部变量分配在空间,每个线程有私有的栈空间。因此访问是线程安全的。

我想到了以下一个问题:

既然Sevlet的全局变量是线程不安全的,那SpringMVC Controller 也一样。那我们在Controller定义个 XXXService 变量会不会造成线程安全呢?
答:因为这是Spring的一个Service Bean,是线程安全的,所以可以作为单例使用,不会造成线程安全。

四、总结(别忘了点赞哦)

补充文章内容要点:

HttpServlet service 方法详解

深入理解 代码 对HTTP状态码的运用

Servlet的线程安全问题

欢迎点击我的博客及GitHub — 博客提供RSS订阅哦

———- http://www.bysocket.com/ ————- https://github.com/JeffLi1993 ———-

© 著作权归作者所有

泥瓦匠BYSocket

泥瓦匠BYSocket

粉丝 954
博文 180
码字总数 247406
作品 4
杭州
程序员
私信 提问
加载中

评论(13)

泥瓦匠BYSocket
泥瓦匠BYSocket 博主

引用来自“谷粑糖”的评论

这篇博文多个地方出现了。http://ifeve.com/javaweb-servlet/
别怕 都是本人~
谷粑糖
谷粑糖
这篇博文多个地方出现了。http://ifeve.com/javaweb-servlet/
泥瓦匠BYSocket
泥瓦匠BYSocket 博主

引用来自“itChen”的评论

很不错的
谢谢评价~
itChen
itChen
很不错的
泥瓦匠BYSocket
泥瓦匠BYSocket 博主

引用来自“zzzzzzing”的评论

mark
谢谢评价~
泥瓦匠BYSocket
泥瓦匠BYSocket 博主

引用来自“hww”的评论

记得淘宝的员工写过一本java Web相关不错的书
谢谢 那一本?
泥瓦匠BYSocket
泥瓦匠BYSocket 博主

引用来自“learn_more”的评论

如果楼主把 GET 、POST 方式乱码的原因以及解决方式详细讲述一遍就更好了!期待...................13
乱码之道 准备写当中 http://www.bysocket.com/?p=397
zzzzzzing
zzzzzzing
mark
已销号
已销号
记得淘宝的员工写过一本java Web相关不错的书
learn_more
learn_more
如果楼主把 GET 、POST 方式乱码的原因以及解决方式详细讲述一遍就更好了!期待...................13
shtml和jsp是什么 关系呀?

我学习一个java web项目,里面的页面好像都是shtml,为什么要这么写? 这是web.xml里的一段 controller org.springframework.web.servlet.DispatcherServlet 1 controller *.shtml...

Vek_lip
2016/10/26
408
2
javaweb

我真是草他妈的弄了整整2天!就这么一个JSP页面无论如何都是空白页面 没有任何效果,if(request.getAttribute("aa")!=null) if(request.getAttribute("aa")==null)都写过了 要不是我的问题马......

地瓜地瓜我是土豆876
2017/07/06
151
4
图解 & 深入浅出 JavaWeb:Servlet必会必知

Writer :BYSocket(泥沙砖瓦浆木匠) 微 博:BYSocket 豆 瓣:BYSocket FaceBook:BYSocket Twitter :BYSocket “眨眼间,离上一篇写技术博文时隔1个月。怕自己真的生疏了,都是备案太慢惹...

泥沙砖瓦浆木匠
2015/08/21
7.4K
50
图解 & 深入浅出 JavaWeb:Servlet必会必知

Writer :BYSocket(泥沙砖瓦浆木匠) 微 博:BYSocket 豆 瓣:BYSocket FaceBook:BYSocket Twitter :BYSocket “眨眼间,离上一篇写技术博文时隔1个月。怕自己真的生疏了,都是备案太慢惹...

KeepMoving
2015/08/25
1K
0
客户端通过http协议 post xml型字符串数据到后台工作流程(请指点)

一.客户端流程 两种方法:1.通过httpurlconnection 2.通过apache开源的httpclient 代码示例为 urlconnection方法: StringBuilder responseBuilder = null;BufferedReader reader = null;Bu......

Enjoy前端开发
2013/08/23
678
0

没有更多内容

加载失败,请刷新页面

加载更多

规则引擎

解决问题 版本迭代速度更不上业务变化,但是若多个业务同时变化,除了为每个业务设计专属配置项也不利于操作。就想服务接口单纯化,将复杂多变的业务逻辑交给规则引擎,让用户在web端或cs端自...

无极之岚
44分钟前
7
0
OSChina 周三乱弹 —— 欢迎你来做产品经理

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @巴拉迪维 :10多次劲歌金曲获奖,更多叱咤歌坛排名,黎明才应该是四大天王之首,只可惜拍的电影太少。单曲循环一个多月的歌,力荐 《无名份的...

小小编辑
今天
449
10
500行代码,教你用python写个微信飞机大战

这几天在重温微信小游戏的飞机大战,玩着玩着就在思考人生了,这飞机大战怎么就可以做的那么好,操作简单,简单上手。 帮助蹲厕族、YP族、饭圈女孩在无聊之余可以有一样东西让他们振作起来!...

上海小胖
今天
10
0
关于AsyncTask的onPostExcute方法是否会在Activity重建过程中调用的问题

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/XG1057415595/article/details/86774575 假设下面一种情况...

shzwork
今天
7
0
object 类中有哪些方法?

getClass(): 获取运行时类的对象 equals():判断其他对象是否与此对象相等 hashcode():返回该对象的哈希码值 toString():返回该对象的字符串表示 clone(): 创建并返此对象的一个副本 wait...

happywe
今天
7
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部