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

关于基本的最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的异常机制来获取) :
        StackTraceElement stack[] = new Throwable().getStackTrace();
for(StackTraceElement each : stack){
System.out.println(each.getClassName() + '.' + each.getMethodName());
}
  • 上面说到一定的匹配规则, 那么这个匹配规则是什么呢, 首先明确这个匹配规则是写在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中的实现有两个版本 :

    public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
} public void init() throws ServletException {
}

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

ervlet_1.init
javax.servlet.GenericServlet.init
org.apache.catalina.core.StandardWrapper.initServlet
org.apache.catalina.core.StandardWrapper.loadServlet
org.apache.catalina.core.StandardWrapper.allocate
org.apache.catalina.core.StandardWrapperValve.invoke
org.apache.catalina.core.StandardContextValve.invoke
org.apache.catalina.authenticator.AuthenticatorBase.invoke
org.apache.catalina.core.StandardHostValve.invoke
org.apache.catalina.valves.ErrorReportValve.invoke
org.apache.catalina.valves.AbstractAccessLogValve.invoke
org.apache.catalina.core.StandardEngineValve.invoke
org.apache.catalina.connector.CoyoteAdapter.service
org.apache.coyote.http11.Http11Processor.service
org.apache.coyote.AbstractProcessorLight.process
org.apache.coyote.AbstractProtocol$ConnectionHandler.process
org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun
org.apache.tomcat.util.net.SocketProcessorBase.run
java.util.concurrent.ThreadPoolExecutor.runWorker
java.util.concurrent.ThreadPoolExecutor$Worker.run
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run
java.lang.Thread.run

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

    public String getInitParameter(String name) {
return this.getServletConfig().getInitParameter(name);
} public Enumeration<String> getInitParameterNames() {
return this.getServletConfig().getInitParameterNames();
} public ServletConfig getServletConfig() {
return this.config;
} public ServletContext getServletContext() {
return this.getServletConfig().getServletContext();
}

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. js课程 1-5 js如何测试变量的数据类型

    js课程 1-5 js如何测试变量的数据类型 一.总结 一句话总结:用typeof()方法. 1.js如何判断变量的数据类型? 用typeof()方法. 13 v=10; 14 15 if(typeo ...

  2. 【a702】贷款利率

    Time Limit: 10 second Memory Limit: 2 MB 问题描述 当一个人从银行贷款后,在一段时间内他将不得不每月尝还固定的分期付款.这个问题要求计算机出贷款者向银行支付的利 ...

  3. php的标准输入与输出是什么?

    php的标准输入与输出是什么? 一.总结 php的标准输入与输出(STDIN是一个文件句柄,等同于fopen("php://stdin", 'r')) 1.STDIN是一个文件句柄 ...

  4. ssh和SSH服务(包含隧道内容)

    ssh和SSH服务(包含隧道内容) 72.16.10.6:/etc/fstab-->/172.16.10.3:/tmp/a.txt. [root@xuexi ~]# scp 172.16.10. ...

  5. JDBC连接数据库中CallableStatement执行有参存储过程及注解其他

    Oracle的建有参存储过程的过程 procedure pro_01(v_01 in number,v_02 out varchar2) as begin select name into v_02 ...

  6. PCI的imagework已由freeview软件代替

    作者:朱金灿 来源:http://blog.csdn.net/clever101 在PCI 9.1中重要模块集成显示环境imagework还存在,但是到了PCI 10.0中imagework已经消失了 ...

  7. 链表与哈希表基本概念及Java常用集合

    -链表- 是一种物理存储单元上非连续.非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的.链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成.每个结点包括两个 ...

  8. Java之泛型<T> T与T的用法

    <T> T表示返回值是一个泛型,传递啥,就返回啥类型的数据,而单独的T就是表示限制你传递的参数类型,这个案例中,通过一个泛型的返回方式,获取每一个集合中的第一个数据, 通过返回值<T ...

  9. lucene 7.x 分词 TokenStream的使用及源码分析

    一.使用步骤 //将一个字符串创建成token流,第一个参数---fiedName,是一种标志性参数,可以写空字符串,不建议用null,因为null对于IKAnalyzer会包错 TokenStrea ...

  10. JackSon fasterxml学习

    概述 Jackson框架是基于Java平台的一套数据处理工具,被称为"最好的JavaJson解析器".  Jackson框架包含了3个核心库:streaming,databind, ...