没有什么固定的结构, 就是稍微总结一下学习到的, 基本上想到哪里写到哪里.

关于基本的最HttpServlet

实际上Servlet是J2EE(也就是现在的Java EE)中规范的一个接口, 用于根据客户端请求生成响应内容并将其传给服务器, 也就是Java的动态Web应用的基本. 大概的体系构架, 可以用实例代码稍微探索了一下 :

  1. 这个类是HttpServlet的子类, 对着HttpServlet按Command + b(IDEA中), 可以发现HttpServlet是GenericServlet的子类, 继续往上, 会发现GenericServlet实现了我们上面提到的Servlet接口. 稍微解释一下, 和http有关的方法(例如doGet, doPost之流)都在HttpServlet中给出了实现, 其他方法则一般在GenericServlet中给出了实现, 就是说, 实际上Servlet不单单适用于Http协议的web应用, 当然这里我们只考虑Http协议的web应用, 所以当然是继承了HttpServlet.
  2. 另一方面, 说几个很可能被override的方法, 可以看到override的init 和 destory方法, 这两个方法来源于GenericServlet, 分别在Servlet的实例创建和销毁的时刻被调用, 也只会被调用一次. 另外doGet 和 doPost当然是源于HttpServlet, 但是HttpServlet实际上还有一个方法叫做sevice(), 实际上, 当一个请求被传递给某个Servlet实例时, 最先被调用的反而是这个service(), 在service()方法中会判断http请求的方法(get, post, put...), 然后根据方法选择调用合适的doGet, doPost, doPut...

容器的概念

J2EE/Java EE实际上是个规范, 而servlet只是规范的一部分, 但是很容易可以想到, 只有Servlet是不可能可以进行JavaWeb应用的, 除了Servlet, 谁来监听来自客户端的请求? 谁来将请求转化为Java实例? 谁来根据请求来实例化特定的Servlet处理请求呢? 这里就可以引出所谓的Web容器, 我上一篇中使用的Tomcat, 实际就是一个Web容器. 那么现在我来大概说一下整个JavaWeb的过程 :

  • 客户端发出请求, 容器监听收到请求后, 实例化出ServletRequest对象和ServletResponse对象, 然后根据一定的匹配规则(这个后面会说), 找到合适的Servlet, 如果该Servlet已经实例化, 那么直接在一条新的线程中调用该实例的service方法(我们上面已经提到了), 同时传入ServletRequest对象和ServletResponse对象这两个对象, 然后service()选择根据请求方法选择对应的方法, 完成请求后, 再由容器转化为Http响应, 发给客户. 这个过程中有大概这么几点需要注意 :

    • 对于特定的Servlet, 整个过程中是实例化一次(也就是说不存在多个servlet实例), 多次请求只不过是相同实例使用多线程进行处理而已.
    • 所以对于特定Servlet而言, init和destory确实也只可能运行一次.
    • 我探究这一块的时候, 使用了一个很好用的技巧来探索某个函数的调用者(java不像js, 可以直接知道自己的调用者b是谁, 但是我们可以利用Java的异常机制来获取) :
  1. StackTraceElement stack[] = new Throwable().getStackTrace();
  2. for(StackTraceElement each : stack){
  3. System.out.println(each.getClassName() + '.' + each.getMethodName());
  4. }
  • 上面说到一定的匹配规则, 那么这个匹配规则是什么呢, 首先明确这个匹配规则是写在web.xml中的(关于web的结构下面会说到), 然后来看张图吧, 我一个一个解释:

  • 先说这个listener, 这里是在容器中注册一个listener, 如果做过GUI编程的话, 对于监听这个概念应该不会陌生, 这里有很多不同种类的listener接口分别用来监听不同的时间, 我提一个ServletContextListener, 实现这个接口必须实现contextInitialized 和 contextDestoryed方法, 这两个方法分别会在容器启动和关闭的时候(也就是服务器部署和关闭的时候调用), 可以用来做一些整个web project开始之前要做的准备工作以及结束之前要做的收尾.
  • 然后就是我们所谓的Servlet注册, 在servlet里面可以对每一个Servlet类进行名字以及初始化参数的注册(名字可以不等于类名, 这里我这么写只是懒得取名字), 然后在servlet-mapping中实现匹配规则的说明, 例如, 如果我的域名是http://www.baidu.com, 那么这里你想要调用Servlet_1你就应该使用http://www.baidu.com/s1.
  • 最后一个context-param在下面再提, 这里先略过.

Servlet中参数的初始化以及属性的共享

ServletContext

先来说说我们上面提到的context-param里面的设置, 这里可以设置一个键值都是字符串的键值对, 这个键值对对于整个web project都是可见的, 在Servlet中可以用 getServletContext().getInitParameter(String 初始化参数名)来获得初始化参数的值, 那么可以看到这里首先调用了getServletContext(), 这个方法返回一个ServletContext对象, 这个对象是相对于整个项目而言的, 也就是说, 这个对象被每一个Servlet共享, 它的一些方法都是针对整个项目而言的, 例如我们上面提到的getInitParameter, 就能用来获得来web.xml中设置的整个的初始化参数. 所以不难想到, 这个实例实际上是在项目开始部署的时候创建的, 并且早于ServletContextListener的contextInitialized()方法, 在这个方法被调用的时候, 伴随这个这个方法的ServletContextEvent实例中实际上已经带有了ServletContext的引用, 也就是这个listener中完全可以使用context-param中设置的参数. 另外, 除了获取初始化参数之外, 这个对象还能够被用来传递变量, 例如你想在contextInitialized的时候创建一个大家都能使用的变量(例如数据库连接的引用), 那么可以使用该对象的 setAttribute()和getAttribute(), 前者接受一个key(String), 和value(Object), 后者接受key返回value, 用法简单, 实际上就是一个字典, 全局有效, 但是要注意线程安全问题.

ServletConfig

然后我们再说说servlet 里面的init-param, 这个和context-param很像, 实际上都能猜出来, 这个是写在某个servlet内部的, 当然就是只属于这个servlet了, 它也是String, String, 但是有一点要注意的是, 它只属于某个特定的Servlet, 所以初始化的时间也是在创建Servlet的阶段, 实际上并不是在构造函数中, 而是在构造函数之后的init中, 之前我们提到过init, 它是源自于GenericServlet, 实际上这个init在GenericServlet中的实现有两个版本 :

  1. public void init(ServletConfig config) throws ServletException {
  2. this.config = config;
  3. this.init();
  4. }
  5. public void init() throws ServletException {
  6. }

在容器构造Servlet的时候, 首先调用的是构造函数, 然后将web.xml中有关于该Servlet的初始化参数保存在ServletConfig当中, 并通过这个有参数的init传递给GenericServlet, 然后这个有参数的init会调用这个无参数的init, 所以当我们override这个无参数的init的时候, 实际上已经可以使用servlet的初始化参数了. 为了证明这一猜想, 我使用了上面说到的技巧, 我们可以看一下具体的调用栈 :

  1. ervlet_1.init
  2. javax.servlet.GenericServlet.init
  3. org.apache.catalina.core.StandardWrapper.initServlet
  4. org.apache.catalina.core.StandardWrapper.loadServlet
  5. org.apache.catalina.core.StandardWrapper.allocate
  6. org.apache.catalina.core.StandardWrapperValve.invoke
  7. org.apache.catalina.core.StandardContextValve.invoke
  8. org.apache.catalina.authenticator.AuthenticatorBase.invoke
  9. org.apache.catalina.core.StandardHostValve.invoke
  10. org.apache.catalina.valves.ErrorReportValve.invoke
  11. org.apache.catalina.valves.AbstractAccessLogValve.invoke
  12. org.apache.catalina.core.StandardEngineValve.invoke
  13. org.apache.catalina.connector.CoyoteAdapter.service
  14. org.apache.coyote.http11.Http11Processor.service
  15. org.apache.coyote.AbstractProcessorLight.process
  16. org.apache.coyote.AbstractProtocol$ConnectionHandler.process
  17. org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun
  18. org.apache.tomcat.util.net.SocketProcessorBase.run
  19. java.util.concurrent.ThreadPoolExecutor.runWorker
  20. java.util.concurrent.ThreadPoolExecutor$Worker.run
  21. org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run
  22. java.lang.Thread.run

看到这个应该很明确了. 然后由于GenericServlet中保存了ServletConfig的引用, 所以可以用getServletConfig来获得这个引用, 这里实际上是Aggregation, 所以 GenericServlet本身又对一些ServletConfig一些常用的方法进行了一层包装 :

  1. public String getInitParameter(String name) {
  2. return this.getServletConfig().getInitParameter(name);
  3. }
  4. public Enumeration<String> getInitParameterNames() {
  5. return this.getServletConfig().getInitParameterNames();
  6. }
  7. public ServletConfig getServletConfig() {
  8. return this.config;
  9. }
  10. public ServletContext getServletContext() {
  11. return this.getServletConfig().getServletContext();
  12. }

HttpSession

这个和初始化参数就没什么关系了, 主要是属性的共享. ServletContext中设置属性是全项目共享, 而HttpSession是相对于一个Session共享. 那么怎么才算是一个HttpSession呢? 靠的就是session id, 当用户发送请求的时候, 如果服务器端需要用到session, 那么服务器端会在响应请求中附带这个session id, 下次用户请求的时候只需要带上这个session id即可. 附带的方式有多种, 最主要的是利用cookie, 实际上cookie就是响应请求中附带的一组或者多组键值对String: String, 它保存在客户端. 经常用来完成账号, 密码自动填充等功能. 可以利用req.getCookies()来获得Cookie[], 如果要找出里面的某个值需要遍历. 另外可以使用response.addCookie(Cookie cookie)来添加/跟新cookie, 不存在显示的跟新方式, 调用addCookie如果碰到同名的cookie, 会直接替换. 可以用cookie.setMaxAge(int seconds)来设置cookie失效的时间, 如果不设置则默认为-1, 此时浏览器关闭时失效. 但是由于cookie保存在客户端, 所以安全性并不好. 而Session大部分是基于cookie的, 就是因为Session实际内容是保存在服务器端的, 而识别标志session id完全可以保存在cookie当中. 所以安全性能更好, 一般用来做保存登录状态等等, 默认保存session id的cookie的MaxAge显然也是-1, 也就是关闭浏览器就消失, 这也是通常来说我们关闭浏览器再开启就需要重新登录但是登录框的账号总是常常已经填好的原因. Session中可以保存一组或者多组键值对String: Object, 但是同样要考虑到线程安全, 因为用户完全可以用两个tab窗口对同一个网站进行请求, 此时两个tab窗口会处于相同的session, 此时如果两者同时修改session中的属性, 是有可能出现线程安全问题的. 当然, 我们仍然需要考虑定期清除服务器保存的session, 主要来讲有如下几种途径 :

  1. 服务器关闭.
  2. session.invalidate()可以让session立即失效.
  3. session.setMaxInactiveInterval(int seconds)可以设置其生命周期, 如果一旦超时, 则失效. 另外还可以在web.xml中设置, 但是这里是以分钟为单位 :

现在来考虑另外一个问题, 如果用户浏览器不支持Cookie或者干脆就禁用了Cookie, 那怎么办? 实际上, 解决方案是URL重写. 在tomcat中是这样的, 在实际的url后面加上;jsessionid=123456. 在用户第一次访问网站的时候, 服务器会同时使用cookie和url重写, 如果在用户下次请求到来时发现Cookie, 说明用户可以使用Cookie, 那么以后就不存在url重写, 否则使用url重写. 具体方法就不细说了, 要知道有这么回事, 以后碰到具体问题有思路就行了.

req.getAttribute

可以使用这个方法来获得属性, 另一方便还可以添加一部分属性之后用RequestDispatcher转发给另一个Servlet或者jsp进行处理. 这里的属性仍然一组或者多组键值对String: Object. 由于这个属性是相对于request而言的, 所以不需要担心线程安全的问题. (关于转发器在后面再细说)

未完待续...

Servlet的基础知识的更多相关文章

  1. Servlet学习笔记【1】--- 背景和基础知识(CGI、Web服务器发展史、Servlet简介、任务、继承结构)

    本文主要讲Servlet的基础知识和背景知识. 1 CGI简介 CGI(Common Gateway Interface 公共网关接口)是WWW技术中最重要的技术之一,有着不可替代的重要地位.CGI是 ...

  2. JSP SERVLET 基础知识

    jsp(java server page)和servlet是JAVA EE规范的两个基本成员,是JAVA WEB开发的重点也是基础知识.JSP本质上也需要编译成SERVLET运行. JSP比较简单,可 ...

  3. Servlet基础知识总结

    Servlet是JavaWeb应用开发的核心组件.Servlet运行在Servlet容器中(例如最常用的Tomcat),它可以为各种客户请求提供相应服务.Servlet可以轻松完成以下任务: 动态生成 ...

  4. Ajax基础知识《一》

    对于网站开发人员,一定不会陌生的Ajax技术,本篇就让我们认识一下它,或许在日后的开发过程中我们就可以使用到.Ajax在那方面使用的比较多呢?答案:表单注册,传统的表单注册,有时需要填写大量的信息,当 ...

  5. maven基础知识

    1.maven基础知识 1.1maven坐标 maven坐标通常用冒号作为分割符来书写,像这样的格式:groupId:artifactId:packaging:version.项目包含了junit3. ...

  6. Struts2入门1 Struts2基础知识

    Struts2入门1 Struts2基础知识 20131130 代码下载: 链接: http://pan.baidu.com/s/11mYG1 密码: aua5 前言: 之前学习了Spring和Hib ...

  7. springMVC1 springmvc的基础知识

    springmvc第一天 springmvc的基础知识 springmvc课程安排: 第一天: 基础知识 springmvc框架(重点) mvc在b/s系统中应用方式 springmvc框架原理(Di ...

  8. [JIT_APP]Java基础知识总结

    一.Java语言的基础知识 1. 开发Java语言的公司 美国Sun(Sum Microsystems)公司开发.   2.Java的3个版本 J2SE(Java2 Standard Edition) ...

  9. JAVAEE规范基础知识

    JavaEE规范基础知识 本人博客文章网址:https://www.peretang.com/basic-knowledge-of-javaee-standard/ JavaEE简介 JavaEE,J ...

随机推荐

  1. 使用LAMP创建基于wordpress的个从博客网站 分类: B3_LINUX 2014-07-15 16:45 800人阅读 评论(0) 收藏

    参考: http://blog.csdn.net/ck_boss/article/details/27866117 一.mysql配置 1.安装mysql yum install mysql-serv ...

  2. [Compose] 8. A curated collection of Monoids and their uses

    const { List } = require('immutable-ext'); const Right = x => ({ chain : f => f(x), ap : other ...

  3. 文本处理之可视化wordcloud

    什么是词云 词云又叫文字云,是对文本数据中出现频率较高的“关键词”在视觉上的突出呈现,形成关键词的渲染形成类似云一样的彩色图片,从而一眼就可以领略文本数据的主要表达意思. 准备工作: python开发 ...

  4. Android 多个Fragment嵌套导致的三大BUG

    Android有碎片化的问题,当然本文说的碎片化不是指的系统版本碎片化的问题,而是Fragment组件碎片化的问题. 很久之前,在Android 3.1系统发布的时候,Google推出了使用Fragm ...

  5. js进阶 9-9 html控件如何实现回车键切换焦点

    js进阶 9-9 html控件如何实现回车键切换焦点 一.总结 一句话总结:在onkeydown事件中判断event对象的键位码,然后focus事件. 二.js进阶 9-9 html控件如何实现回车键 ...

  6. iPad和iPhone开发的异同

    niPad和iPhone开发的异同   niPad简介 n什么是iPad p一款苹果公司于2010年发布的平板电脑 p定位介于苹果的智能手机iPhone和笔记本电脑产品之间 p跟iPhone一样,搭载 ...

  7. echarts改变颜色属性的demo

    一:柱状图改变颜色 图片.png 代码: <!DOCTYPE html> <html> <head> <meta charset="UTF-8&qu ...

  8. 【BZOJ 1012】 [JSOI2008]最大数maxnumber(线段树做法)

    [题目链接]:http://www.lydsy.com/JudgeOnline/problem.php?id=1012 [题意] [题解] 预开一个20W长度的线段树; 这里a[1..20W]={0} ...

  9. 【26.87%】【codeforces 712D】Memory and Scores

    time limit per test2 seconds memory limit per test512 megabytes inputstandard input outputstandard o ...

  10. Java:JSON解析工具-org.json

    一.简介 org.json是Java常用的Json解析工具,主要提供JSONObject和JSONArray类,现在就各个类的使用解释如下. 二.准备 1.在使用org.json之前,我们应该先从该网 ...