`
angelbill3
  • 浏览: 252916 次
  • 性别: Icon_minigender_2
  • 来自: 杭州
社区版块
存档分类
最新评论

结合源码谈谈Servlet的实例化、变量以及多线程

阅读更多
1. Servlet官方文档:
http://docs.oracle.com/javaee/7/api/javax/servlet/Servlet.html

javax.servlet包包含了一系列的接口和类,它们共同描述和定义了servlet容器(如Apache tomcat)与servlet类的规范,以及servlet容器运行时的环境。

为什么要学习servlet,因为我们平常熟悉的Tomcat和Spring都与servlet有分不开的关系。Tomcat是著名的servlet容器,Spring的DispatcherServlet就实现了Servlet接口。


2. Servlet版本
Version版本时间JSR Number平台主要改进
Servlet4.0开发中369Java EE 8HTTP/2
Servlet3.12013.05340Java EE 7NIO, WebSocket等。
Servlet3.02009.12315Java EE 6, Java SE 6可插性,加入异步Servlet、安全以及文件上传。
Servlet2.52005.09154Java EE 5, Java SE 5依赖Java SE 5,支持annotation。
Servlet2.42003.11154J2EE1.4, J2SE1.3web.xml使用了XML规范格式。
Servlet2.32001.0853J2EE1.3, J2SE1.2加了Filter相关。
Servlet2.2 1999.08902, 903J2EE1.3, J2SE1.2成为J2EE的一部分,引入war。
Servlet2.11998.11----第一个官方版本,加入RequestDispatcher, ServletContext。
Servlet2.0----JDK 1.1成为Java Servlet开发工具2.0的一部分。
Servlet1.01997.06------



3. 类图
用StarUML画的
3.1 javax.servlet.Servlet相关类图:


3.2 javax.servlet.ServletRequest类图:(ServletResponse略)



4. Servlet生命周期
面试经常被问的问题之一。从3.1的Servlet接口方法中可以看到,Servlet接口定义的方法主要有init(cfg)、service(req, res)、destroy()。
init()、destory()方法只会被调用一次,即在初始化时以及销毁时。
service(req, res)会被调用很多很多次,客户访问web网页,Servlet容器(如Apache Tomcat)会生成一个线程来处理这个用户的请求。这样的设计的优点是节省资源,试想如果客户每请求一次都生成一个新的Servlet,经历init()、service()、destory()生命周期,实在是太浪费资源了。

此外,HttpServlet中的doGet(req, res)或是doPost(req, res)只是对不同的req.getMethod()进行了分类处理,常见的request method有以下几种(摘自HttpServlet类):
    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";



5. 重要的类
5.1 javax.servlet.ServletContext
当servlet容器(如Apache Tomcat)运行时,它会布署、加载所有的web应用。当一个应用被加载时,servlet容器会创建一个ServletContext,然后将它放在内存中(即只有一个实例,不会过期)。接着读取web应用中的web.xml的<servlet>、<filter> (Since Servlet 2.3)以及<listener>,(或是annotation@WebServlet, @WebFilter, @WebListener)配置并实例化它们,将他们放在server的内存中。在创建的时候,init()方法会被调用。

也就是说,
在一个Servlet容器中(single node模式下的话,就是只有一个JVM),可能会有多个Servlet,但只会有一个ServletContext。ServletContext接口定义了一系列的方法,用来给Servlet与容器之间进行“交流”,如返回文件的MIME类型,dispatch requests等。

5.2 HttpServletRequest和HttpServletResponse
javax.servlet中只定义了接口,具体的实现是在Servlet容器中。如运行在Tomcat中的话,具体的装饰者类为:
  • org.apache.catalina.connector.RequestFacada
  • org.apache.catalina.connector.ResponseFacada

当一个用户访问web项目,servlet容器会生成HttpServletResponse、HttpServletResponse,并将它们传递给定义过的Filter链,最终会传给Servlet实例。


6. 多线程
Servlet是线程不安全的。Server在处理用户的request请求通常是以多线程的模式处理,如Tomcat的默认线程池最大数为150。

总结:
  • a. ServletContext实例在web app启动时创建,在web app关闭时销毁。所有的request,session共享同一个ServletContext。
  • b. Servlet, Filter, Listener实例在web app启动时创建,在web app关闭时销毁。所有的request,session共享同一个ServletContext。
  • c. HttpServletRequest和HttpServletResponse起于servlet接收用户的HTTP request,止于response完全返回给用户(网页内容),它们的数据是不会被共享的。

既然容器是以多线程的模式处理request的,那么在Servlet中,在doGet(或doPost等)方法外定义的私有变量可能会有同步问题。在Servlet官方文档也明确提示,并发访问共享资源时要特别小心。共享资源包括内存数据(如类的私有变量,文件,数据库连接等)。

引用官方API:
引用
Servlets typically run on multithreaded servers,so be aware that a servlet must handle concurrent requests and be careful to synchronize access to shared resources. Shared resources include in-memory data such as instance or class variables and external objects such as files, database connections, and network connections.


具体示例:
public class ExampleServlet extends HttpServlet {
    private Object thisIsNOTThreadSafe;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Object thisIsThreadSafe;

        thisIsNOTThreadSafe = request.getParameter("foo"); // BAD!! Shared among all requests!
        thisIsThreadSafe = request.getParameter("foo"); // OK, this is thread safe.
    } 
}



7. 分析Tomcat源码来看Servlet特性
7.1 org.apache.catalina.core.StandardWrapper
Servlet的wrapper类,每个Servlet都有自己的wrapper,负责Servlet的初始化等。
API:http://tomcat.apache.org/tomcat-8.0-doc/api/org/apache/catalina/core/StandardWrapper.html

部分源码:
public class StandardWrapper extends ContainerBase
    implements ServletConfig, Wrapper, NotificationEmitter {
    
    protected volatile Servlet instance = null;
    protected volatile boolean singleThreadModel = false;

    @Override
    public synchronized void load() throws ServletException {
        instance = loadServlet();

        if (!instanceInitialized) {
            initServlet(instance);
        }
    // 略
    }

    private synchronized void initServlet(Servlet servlet)
            throws ServletException {

        if (instanceInitialized && !singleThreadModel) return;
    // 略
        servlet.init(facade);
    }
}

变量singleThreadModel默认值为false,即默认情况下是多线程模式。load()方法中也能看到先是对instance进行实例化,接着判断是否是多线程模式,如果是,那么进行initServlet,调用initServiet(servlet),再进行一系列的操作后,最后调用servlet.init(cfg)方法,也就是3.1类图中标出的(也是第4节生命周期中说到的)init方法。

由此可见,若已经被实例化过(instanceInitialized),那么代码就直接return了,并不会进行servlet.init(cfg),也就是说这个方法只会被调用一次。即servlet生命周期的奥义。

7.2 org.apache.tomcat.util.net.NioEndpoint
如果要看Tomcat的线程池实现,可以看NioEndpoint类。此类继承了抽象类AbstractEndpoint<S>,其它的实现还有AprEndpoint, JIoEndpoint, Nio2Endpoint。
具体API戳:[url]http://tomcat.apache.org/tomcat-8.0-doc/api/org/apache/tomcat/util/net/AbstractEndpoint.html [/url]


参考:
Servlet维基百科:https://en.wikipedia.org/wiki/Java_servlet
Tomcat官方API:http://tomcat.apache.org/tomcat-8.0-doc/api/overview-summary.html
Tomcat在线源码(用Maven下载下来更方便):http://grepcode.com/snapshot/repo1.maven.org/maven2/org.apache.tomcat/tomcat-catalina/8.0.24/
文章:https://blogs.oracle.com/arungupta/entry/what_s_new_in_servlet
文章:http://www.tuicool.com/articles/AZb2ai
在线讨论:http://stackoverflow.com/questions/3106452/how-do-servlets-work-instantiation-sessions-shared-variables-and-multithreadi
分享到:
评论

相关推荐

    JAVA 范例大全 光盘 资源

    实例133 碰撞的球(多线程) 382 实例134 钟表(多线程) 387 实例135 模拟生产者与消费者 392 实例136 仿迅雷下载文件 396 第15章 图形编程 403 实例137 多变的按钮 403 实例138 自制对话框 405 实例139 ...

    java范例开发大全(pdf&源码)

    实例154 使用静态成员变量计算内存中实例化的对象数目 239 实例155 实现加减乘除的方法 240 8.3 面向对象的设计模式 241 实例156 Singleton单例模式 242 实例157 招聘(简单工厂模式) 243 实例158 同学聚会(工厂...

    Java范例开发大全(全书源程序)

    实例154 使用静态成员变量计算内存中实例化的对象数目 239 实例155 实现加减乘除的方法 240 8.3 面向对象的设计模式 241 实例156 Singleton单例模式 242 实例157 招聘(简单工厂模式) 243 实例158 同学聚会...

    java图书馆swing源码-rcaller:用于调用R的Java库

    的应用程序可以实例化许多 RCaller 对象,也可以通过使用顺序命令调用能力使用单个对象。 前者使用多个不共享同一个变量池的环境,而后者共享一个相互的变量池,客户端也可以进行通信。 RCaller 纯粹是用 Java 编写...

    java 面试题 总结

    与cgi的区别在于servlet处于服务器进程中,它通过多线程方式运行其service方法,一个实例可以服务于多个请求,并且其实例一般不会销毁,而CGI对每个请求都产生新的进程,服务完成后就销毁,所以效率上低于servlet。...

    超级有影响力霸气的Java面试题大全文档

    与cgi的区别在于servlet处于服务器进程中,它通过多线程方式运行其service方法,一个实例可以服务于多个请求,并且其实例一般不会销毁,而CGI对每个请求都产生新的进程,服务完成后就销毁,所以效率上低于servlet。...

    Java JDK实例宝典

    7 一个支持多线程的服务器框架 13. 8 代理服务器 13. 9 Telnet客户端 13. 10 UDP编程 13. 11 聊天室服务器端 13. 12 聊天室客户端 13. 13 FTP客户端 第14章 数据库 14. 1 连接各种...

    Java语言程序设计

    图书详细描述: 本书将Java语言作为大学生的计算机程序设计入门语言,其特色是内容全面、深入浅出、辅助教材立体...多线程第9章 图形用户界面第10章 JDBC与数据库访问第11章 Servlet程序设计第12章 JSP程序设计参考文献

    Java数据库编程宝典3

    4.9.3 多线程 4.10 批更新 4.11 ResultSet 4.12 可滚动的ResultSet 4.12.1 创建可滚动的ResultSet 4.12.2 游标控制 4.12.3 将游标移动到指定行 4.12.4 获得游标位置 4.13 可更新的ResultSet 4.13.1 更新...

    疯狂JAVA讲义

    5.3.2 成员变量的初始化和内存中的运行机制 128 5.3.3 局部变量的初始化和内存中的运行机制 130 5.3.4 变量的使用规则 130 5.4 隐藏和封装 132 5.4.1 理解封装 132 5.4.2 使用访问控制符 132 5.4.3 package和...

    Python Cookbook

    17.5 在多线程环境中使用SWIG生成的模块 603 17.6 用PySequence_Fast将Python序列转为 C数组 604 17.7 用迭代器逐个访问Python序列的元素 608 17.8 从Python可调用的C函数中返回None 611 17.9 用gdb调试动态载入...

Global site tag (gtag.js) - Google Analytics