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 | 开发中 | 369 | Java EE 8 | HTTP/2 |
Servlet3.1 | 2013.05 | 340 | Java EE 7 | NIO, WebSocket等。 |
Servlet3.0 | 2009.12 | 315 | Java EE 6, Java SE 6 | 可插性,加入异步Servlet、安全以及文件上传。 |
Servlet2.5 | 2005.09 | 154 | Java EE 5, Java SE 5 | 依赖Java SE 5,支持annotation。 |
Servlet2.4 | 2003.11 | 154 | J2EE1.4, J2SE1.3 | web.xml使用了XML规范格式。 |
Servlet2.3 | 2001.08 | 53 | J2EE1.3, J2SE1.2 | 加了Filter相关。 |
Servlet2.2 | 1999.08 | 902, 903 | J2EE1.3, J2SE1.2 | 成为J2EE的一部分,引入war。 |
Servlet2.1 | 1998.11 | -- | -- | 第一个官方版本,加入RequestDispatcher, ServletContext。 |
Servlet2.0 | -- | -- | JDK 1.1 | 成为Java Servlet开发工具2.0的一部分。 |
Servlet1.0 | 1997.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
分享到:
相关推荐
1. **Servlet实例化策略**:使用Per-Thread(每个线程一个实例)模型,或者采用Prototype(原型)模式,让容器为每个请求创建一个新的Servlet实例,但这会增加内存开销。 2. **线程安全编程**:对于必须共享的成员...
`init()`方法在Servlet实例化后被调用,`service()`方法在接收到请求时调用,`destroy()`方法在Servlet卸载前调用。 5. **Servlet配置**:在`web.xml`部署描述符中,我们可以通过`<servlet>`和`<servlet-mapping>`...
1. **线程安全**:由于Servlet默认是多线程的,开发者需要注意同步问题,避免在Servlet中使用全局变量或不线程安全的对象。 2. **性能优化**:可以通过实现`SingleThreadModel`接口使每个请求都创建新的Servlet实例...
7. **多线程模型**: 由于Servlet容器会复用Servlet实例来处理多个并发请求,因此开发者需要注意线程安全问题。在编写Servlet时,应避免在实例变量中存储请求特定的数据。 8. **URL映射**: 在Web应用的部署描述...
6. **多线程与性能**: Servlet默认是线程安全的,因为每个请求都会创建一个新的线程来调用Servlet。但是,开发者需要注意避免在Servlet中使用全局变量或不安全的数据结构,以防止线程安全问题。 7. **过滤器...
7. **多线程安全问题**: 由于Servlet容器可能会复用Servlet实例,所以开发者需要注意线程安全问题。对于共享的成员变量,需要使用同步机制或者使用无状态的Servlet设计。 8. **过滤器Filter**: 除了Servlet外,...
3. **多线程安全**:Servlet默认是线程安全的,意味着服务器可能会并发地调用同一个Servlet实例的`service()`方法。因此,开发者需要确保在Servlet中不使用非线程安全的成员变量。 4. **Servlet容器**:如Tomcat,...
9. **多线程**:Servlet默认是线程安全的,一个Servlet实例可以服务于多个并发请求。开发者需要注意同步问题,避免在Servlet中使用静态变量存储请求相关数据。 10. **过滤器(Filter)**:Servlet API还提供了过滤...
由于Servlet是多线程的,同一个Servlet实例可能会服务于多个请求。因此,开发者需要注意线程安全问题,避免在Servlet中使用全局变量。 七、Servlet的会话管理 通过HttpServletRequest的getSession()方法,Servlet...
- 由于一个 Servlet 实例可能会同时处理多个请求,因此在 Servlet 中声明成员变量存储用户数据可能导致线程安全问题。 - **解决方案**: - 实现 `SingleThreadModel` 接口,但这种方式已被废弃,因为效率低下。 ...
7. 多线程与并发:Servlet容器会为每个请求创建一个新的线程来调用Servlet,因此Servlet需要考虑线程安全问题,特别是在共享数据或状态时。 8. Servlet容器:如Tomcat、Jetty等,它们负责管理Servlet的生命周期,...
7. **多线程模型**: 每个Servlet实例可能会被多个请求线程同时调用,因此Servlet需要处理线程安全问题。开发者需要确保Servlet中的状态变量正确地同步。 8. **过滤器(Filter)**: 过滤器可以在请求到达Servlet...
以及利用Servlet的多线程特性,确保并发处理的效率。 总之,理解并熟练掌握Servlet和JavaBean是成为一名合格的JavaWeb开发者的必经之路。通过实际项目的练习和不断学习,你可以深化对这些技术的理解,提升自己的...
5. **多线程模型**:Servlet默认是线程安全的,同一个Servlet实例可能服务于多个并发请求。开发者需要注意避免在Servlet中使用非线程安全的变量。 6. **URL映射**:在web.xml配置文件中,我们可以通过`<servlet-...
5. **多线程**:详细阐述了Java中线程的创建、同步、互斥和通信,包括Thread类和Runnable接口,以及synchronized关键字、wait/notify机制和并发工具类的使用。 6. **输入/输出流**:讲解了I/O流的基本概念,包括...
7. **线程模型**:Tomcat采用多线程模型处理请求,`Executor`接口及其实现允许自定义线程池,以优化并发性能。 8. **连接器与协议处理器**:Tomcat支持多种连接器,如基于NIO的` CoyoteNioProtocol`,基于APR的`...
6. **多线程与并发**:Servlet容器通常会为每个请求创建一个新的线程,因此开发者需要注意线程安全问题,避免在Servlet中使用不安全的全局变量。 7. **异步Servlet**:Java Servlet 3.0及以上版本引入了异步Servlet...
- **init()**:初始化方法,在Servlet实例创建时调用一次。 - **service()**:处理请求的方法,根据请求类型(GET/POST)选择适当的方法执行。 - **destroy()**:销毁方法,当Servlet不再需要时被调用。 - **...