`
eyesmore
  • 浏览: 378673 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

servlet生命周期和持续性

阅读更多

1、大多数servlet容器都是将所有servlet放在一个JVM中运行的,当然也有些高端容器,支持分布式servlet。

2、servlet实例的持续性 ,也就是说:一个servlet实例在多个http请求之间保持持续性。通俗地讲就是,当servlet被载入时,服务器生成一个实例对象,这个实例处理所有对该servlet的请求。注意:我们的业务框架和前置框架的交易处理类都不是持续性的,而是来个请求生成一个处理类。

(如果以后某个同事开发一个什么容器,首先就可以问问生命周期和对象的持续性方面的问题。比如,我们现在的业务框架中的service容器中service对象是非持续的。)

持续性有几个好处:(1)内存消耗小,速度快,不用临时去生成一个对象;(2)保证持续性,一般建议在servlet.init中把该servlet处理业务逻辑时所需要的资源全部一次性载入,而不要在servlet.service方法中不断载入,并在servlet.destory中释放这些资源。那么,一个servlet初始化的时候,可能是需要用到一些配置信息的,这些在servlet.init中都有个ServletConfig输入参数,把初始化需要的参数信息从web.xml中携带进来。

 

这样做,也有麻烦的地方,因为所有对该servlet资源的请求,都是同一个servlet实例来处理,那么也就是说:客户A对servlet状态的修改,其实是会影响到客户B所看到的servlet的状态的。比如,我们给servlet做个统计,用来统计被访问了多少次,实际上统计的是该servlet一共被访问了多少次,而不是说某个用户对它访问了多少次(这属于会话跟踪的话题)。

 

还有麻烦的问题是,服务端都是多线程环境,做为servlet的开发人员来看,模型应该是这样的: 该servlet极其可能在同一个时刻被两个客户端(在服务端表现为两个并发的线程)访问。所以,我们说servlet实例的持续性包括两方面:(1)在多个请求之间保持持续性;(2)在多线程中保持持续性。

package com.eyesmore.lifecycle;





import java.io.IOException;


import java.io.PrintWriter;





import javax.servlet.ServletException;


import javax.servlet.http.HttpServlet;


import javax.servlet.http.HttpServletRequest;


import javax.servlet.http.HttpServletResponse;





public class SimpleCounter extends HttpServlet {





	private static final long serialVersionUID = -8007930646306363635L;





	private int counter = 0;


	


	@Override


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


		PrintWriter writer = response.getWriter();


		String clientInfo = " Remote="+request.getRemotePort()+"  ";


		String threadInfo = " in Thread = "+Thread.currentThread().toString() +" ID="+Thread.currentThread().getId();


		try {


			Thread.sleep(2000);


		} catch (InterruptedException e) {


			e.printStackTrace(writer);


		}


		++ counter;


		try {


			Thread.sleep(2000);


		} catch (InterruptedException e) {


			e.printStackTrace(writer);


		}


		writer.println("Counter = "+ counter +clientInfo + "  "+threadInfo);


	}





	/*


	 * 通过多个浏览器访问,有一下结果:


	 * 【遨游】


	 * Counter = 1 Remote=2135 in Thread = Thread[http-8080-1,5,main] ID=18 


	 * Counter = 4 Remote=2169 in Thread = Thread[http-8080-1,5,main] ID=18


	 * Counter = 5 Remote=2169 in Thread = Thread[http-8080-1,5,main] ID=18


	 * 【firefox】


	 * Counter = 2 Remote=2137     in Thread = Thread[http-8080-2,5,main] ID=19


	 * Counter = 6 Remote=2172     in Thread = Thread[http-8080-3,5,main] ID=20


	 * 【Opera】(由于没有同步counter,两个请求获得了一样的结果)


	 * Counter = 3 Remote=2164     in Thread = Thread[http-8080-2,5,main] ID=19


	 * Counter = 3 Remote=2164     in Thread = Thread[http-8080-2,5,main] ID=19


	 * 


	 * 结论是:由于http1.1会重用原有的连接,所以而且服务端作业处理时间短的话,可能在同一个线程中处理多个作业。


	 * 但是,我们从结果中还是可以看出servlet实例会在多个线程中持续的事实的。


	 * */


	


}


 

当然,servlet容器还有可以支持运行一组实例的配置——单线程模式。所谓单线程模式就是保证某个servlet实例不可能同时被两个线程操作,如果某时候有两个请求同时到达某个servlet,那么这个时会构建另外一个servlet实例,并运行在另一个线程里面。很显然,如果某个servlet具有单线程模式,那么它一定是线程安全的。SingleThreadModel的出现就是设计者为了让servlet开发人员尽可能少得关注多线程的问题,但是作为高级开发人员必须对服务端的多线程特性有深刻了解 。SingleThreadModel是个标签接口,仅仅是用来告诉servlet容器需要把它作为SingleThreadModel。

 

package com.eyesmore.lifecycle;





import java.io.IOException;


import java.io.PrintWriter;





import javax.servlet.ServletException;


import javax.servlet.SingleThreadModel;


import javax.servlet.http.HttpServlet;


import javax.servlet.http.HttpServletRequest;


import javax.servlet.http.HttpServletResponse;





@SuppressWarnings("deprecation")


public class SingleThreadCounter extends HttpServlet implements SingleThreadModel {





	private static final long serialVersionUID = 6688319173103711221L;





	private int instanceNo;





	private volatile static int instanceCount = 0;


	


	public SingleThreadCounter() {


		++ instanceCount;


		instanceNo = instanceCount;


	}








	@Override


	protected void doGet(HttpServletRequest request, HttpServletResponse response) 


	throws ServletException, IOException 


	{


		PrintWriter writer = response.getWriter();


		writer.println("instanceNo = "+ instanceNo);


		


		String clientInfo = " Remote="+request.getRemotePort()+"  ";


		String threadInfo = " in Thread = "+Thread.currentThread().toString() +" ID="+Thread.currentThread().getId();


		try {


			Thread.sleep(2000);


		} catch (InterruptedException e) {


			e.printStackTrace(writer);


		}


		try {


			Thread.sleep(2000);


		} catch (InterruptedException e) {


			e.printStackTrace(writer);


		}


		writer.println("instanceNo = "+ instanceNo +clientInfo + "  "+threadInfo);


		


	}


	/*


	 * 【遨游】


	 * instanceNo = 2 


	 * instanceNo = 2 Remote=2271 in Thread = Thread[http-8080-1,5,main] ID=18 


	 * instanceNo = 3 


	 * instanceNo = 3 Remote=2272 in Thread = Thread[http-8080-2,5,main] ID=19 


	 * 【firefox】


	 * instanceNo = 2


	 * instanceNo = 2 Remote=2274     in Thread = Thread[http-8080-3,5,main] ID=20


	 * instanceNo = 2


	 * instanceNo = 2 Remote=2274     in Thread = Thread[http-8080-3,5,main] ID=20 


	 * */


	


	


	


}


 

3、对象的init和destory,并且理解动态加载(servlet的动态加载 ,这也是服务器设计的一个基本而且重要的指标)

【“servlet重新载入”  ====== 非常高级的一个话题,就是servlet的动态加载】

【我们在实践总发现,“新增”,“删除”一个servlet可以被动态反映出来;但是,如果“修改一个类”,却动态加载不了。有兴趣可以深入了解下servlet的动态加载的话题。】

当服务器将一个请求分配给servlet处理时,它首先检查磁盘上的servlet类文件是否有改动。如果有改动,则放弃旧版本,并用自定义的ClassLoader来加载新的实例。(也是使用ClassLoader来实现动态加载的)。环境中的所有的servlet类和支持类都是用一个ClassLoader来加载的,所以在运行时不会出现不可预料的ClassCastException的问题 (以前是每不同的servlet由不同的classloader来加载,这样容易出现classCastException的错误)。注意: 当只有一个支持类文件改变时,类不会被重新载入。

 

servlet接口包括:servlet本身的描述信息,配置描述信息,初始化,服务,结束。

package javax.servlet;





import java.io.IOException;





// Referenced classes of package javax.servlet:


//            ServletException, ServletConfig, ServletRequest, ServletResponse





public interface Servlet


{





    public abstract void init(ServletConfig servletconfig)


        throws ServletException;





    public abstract ServletConfig getServletConfig();





    public abstract void service(ServletRequest servletrequest, ServletResponse servletresponse)


        throws ServletException, IOException;





    public abstract String getServletInfo();





    public abstract void destroy();


}

ServletConfig包括:servlet的名字,servlet的初始化用的初始化参数。更重要的是,还可以取得ServletContext的整个servlet的大家庭,就好比spring 的Bean容器,能够依据Bean的名字,在一个Bean中通过BeanContext获取其他Bean的引用。这一点对于Servlet的协作是很有价值的。

package javax.servlet;





import java.util.Enumeration;





// Referenced classes of package javax.servlet:


//            ServletContext





public interface ServletConfig


{





    public abstract String getServletName();





    public abstract ServletContext getServletContext();





    public abstract String getInitParameter(String s);





    public abstract Enumeration getInitParameterNames();


}

 

 

【GenericServlet 一般的servlet模板   典型的Template-callback】

package javax.servlet;

import java.io.IOException;
import java.io.Serializable;
import java.util.Enumeration;
import java.util.ResourceBundle;


public abstract class GenericServlet
    implements Servlet , ServletConfig , Serializable

//实现ServletConfig接口,实际上有点“零适配的”味道, ServletConfig的实例对象来自于init中的servletConfig参数。所以,如果我们需要对自己写的Servlet做初始化工作,请尽可能在init方法中进行,而不要在构造函数中进行,因为那时超类的init方法还没来得即进行。
{
//【第一部分】
    public GenericServlet()
    {
    }

    public void destroy()  //注意:如果服务器宕机,在destroy中是做不到existHooking的。
    {
    }

    public String getInitParameter(String name) //所有的这些都来元于init中的servletConfig参数
    {
        ServletConfig sc = getServletConfig();
        if(sc == null)
            throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized"));
        else
            return sc.getInitParameter(name);
    }

    public Enumeration getInitParameterNames()  //所有的这些都来元于init中的servletConfig参数
    {
        ServletConfig sc = getServletConfig();
        if(sc == null)
            throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized"));
        else
            return sc.getInitParameterNames();
    }

    public ServletConfig getServletConfig ()  //所有的这些都来元于init中的servletConfig参数
    {
        return config;
    }

    public ServletContext getServletContext ()  //所有的这些都来元于init中的servletConfig参数
    {
        ServletConfig sc = getServletConfig();
        if(sc == null)
            throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized")); //对于未能恰当的初始化的东西,提前执行某个方法,一般抛IllegalStateException异常。
        else
            return sc.getServletContext();
    }

    public String getServletInfo()    //描述信息,umpay的框架也喜欢留个这个东西。
    {
        return "";
    }

    public void init(ServletConfig config) //两个初始化接口,一个是可以从ServletConfig中提取参数的,另一个是没有的。
        throws ServletException
    {
        this.config = config;   //这个config信息被GenericServlet保存下来了,所以可以在后面任何地方重新获取。getServletConfig
        init();
    }

    public void init()   //如果我们自己要重写init方法,建议直接重写不带参数的,如果要获取参数信息,可以直接在超类的方法中进行。当然,如果重写带参的,那么,我们还是先调用超类的super.init(config)下。

//这样,我们以后使用ServletConfig接口下的一些方法,就不至于抛出IllegalStateException异常了。

//servlet还可以配置成是服务器一起来时就开始初始化,还在第一次访问时开始初始化。 可以在web.xml中配置。
        throws ServletException
    {
    }

 

//【第二部分】

// Servlet容器真是为开发者提供了一体化的服务,把日志的接口都流出来了,而我们服务器框架这方面做得还不好。

//所以servlet的开发这应该尽可能不用System.out这个输出日志,尽管tomcat也会把它重定向到日志文件。
    public void log(String msg)
    {
        getServletContext().log((new StringBuilder()).append(getServletName()).append(": ").append(msg).toString());
    }

    public void log(String message, Throwable t)
    {
        getServletContext().log((new StringBuilder()).append(getServletName()).append(": ").append(message).toString(), t);
    }

 

【第三部分】
    public abstract void service(ServletRequest servletrequest, ServletResponse servletresponse)
        throws ServletException, IOException;

// 此时的servlet还仅仅跟通信协议有关系,比如是短连接什么的。tomcat是如何把ServletRequest解析成一个HttpServletRequest的呢,这个是很有意思的一个话题。

//我们又能否在servlet容器上跑我们自定义的报文结构呢?而不仅仅是http。



    public String getServletName()
    {
        ServletConfig sc = getServletConfig();
        if(sc == null)
            throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized"));
        else
            return sc.getServletName();
    }

    private static final String LSTRING_FILE = "javax.servlet.LocalStrings";
    private static ResourceBundle lStrings = ResourceBundle.getBundle("javax.servlet.LocalStrings");
    private transient ServletConfig config;

}

分享到:
评论

相关推荐

    servlet帮助文档

    二、Servlet生命周期 Servlet的生命周期包括三个主要阶段:加载和实例化、初始化、服务和销毁。当Web服务器接收到对Servlet的首次请求时,它会加载Servlet类并创建一个Servlet实例。接着,调用Servlet的`init()`方法...

    atlassian-plugins-servlet-2.6.0.jar.zip

    1. **Servlet生命周期**:Servlet的生命周期包括初始化、服务、销毁三个阶段,理解这些阶段对于正确管理和配置Servlet至关重要。 2. **Servlet API**:学习如何使用Servlet API,包括`HttpServletRequest`和`...

    基于Servlet的Web图表编程

    - **`init(ServletConfig config)`:** 在Servlet的生命周期中,该方法只被调用一次,主要用于初始化Servlet。`ServletConfig`对象由容器提供,用于获取Servlet的配置信息。 - **`getServletConfig()`:** 返回一...

    JSP与Servlet有什么关系

    3. **生命周期管理**:JSP页面和Servlet都遵循类似的生命周期管理机制。例如,它们都可以利用`init()`方法进行初始化,使用`service()`方法处理客户端请求等。 **JSP与Servlet之间的主要区别**: 1. **编写方式**:...

    Servlet3.0 新特性

    这意味着每个Servlet或Filter可以有自己的生命周期,提供了更细粒度的控制和隔离。 4. **零停机时间部署**: 新版本允许在应用运行时进行热部署,无需停止服务器即可更新Servlet、Filter或Listener。这对于持续...

    servlet定时器功能完整版已测试过

    为了提高代码的可维护性和灵活性,可以考虑使用Spring框架的`@Scheduled`注解,结合Spring的`TaskScheduler`或`ThreadPoolTaskScheduler`,这样可以更方便地管理定时任务,同时避免了Servlet生命周期管理的复杂性。...

    jsp&servlet_notebook

    - **Servlet生命周期**:包括加载、实例化、初始化、服务、销毁五个阶段。 - **Servlet API**:提供了诸如`HttpServlet`类,开发者通过继承此类来处理HTTP请求和响应。 - **Servlet配置**:在`web.xml`文件中定义...

    JSP Servlet资料搜集

    Servlet的生命周期包括加载、初始化、服务、销毁四个阶段。 **JSP与Servlet的关系:** JSP和Servlet都是用于处理Web请求的技术,但它们分工不同。JSP主要关注视图部分,即页面展示,而Servlet则更专注于控制逻辑和...

    ORACLE电子商务项目jsp+servlet

    在这个过程中,开发者需要熟悉Oracle的SQL语法,理解HTTP协议,掌握JSP和Servlet的生命周期及工作原理。 5. **集成开发环境(IDE)**:开发这样的项目通常会用到像Eclipse或IntelliJ IDEA这样的Java IDE,它们提供...

    利用servlet动态生成验证码

    1. Servlet基础知识:了解Servlet的生命周期和HTTP请求处理。 2. HttpServlet的doGet和doPost方法:理解何时以及如何使用这两个方法。 3. 随机数生成:使用Java的Random类生成随机字符串。 4. 图像处理:利用Java的...

    自己写的jsp+servlet博客系统

    3. **Servlet生命周期**:包括加载、实例化、初始化、服务、销毁五个阶段,理解这些可以帮助优化性能和资源管理。 4. **HTTP协议**:Servlet是基于HTTP协议工作的,理解HTTP请求方法(GET、POST等)和响应状态码...

    java-servlet-api.doc

    一个Javaservlet具有一个生命周期,这个生命周期定义了一个Servlet如何被载入并被初始化,如何接收请求并作出对请求的响应,如何被从服务中清除。Servlet的生命周期被javax.servlet.Servlet这个接口所定义。 所有的...

    servlet3.0推送聊天室

    在实际开发中,我们可以使用Servlet 3.0的异步特性来处理WebSocket连接的生命周期,例如在接收到新消息时,通过异步上下文将消息分发给所有在线的WebSocket连接。同时,我们还需要一个后台服务来处理聊天室的消息...

    jsp、servlet知识总结

    1. **Servlet生命周期**:Servlet同样经历初始化、服务和销毁三个阶段。在多线程环境中,Servlet容器通常只创建一个Servlet实例来服务多个请求。 2. **核心方法**:Servlet的核心方法包括`init()`(初始化)、`...

    Java_Servlet入门教程

    Servlet的生命周期包括三个主要阶段:初始化、服务请求和销毁。当Servlet第一次被请求时,Web容器(如Tomcat)会加载Servlet类,创建一个Servlet实例并调用init方法进行初始化。随后,Servlet对象会处理来自客户端...

    Servlet_chuanjiang.pdf

    - **Servlet生命周期**: - **初始化阶段**:当Servlet第一次被加载到Web容器时,容器会调用`init()`方法初始化Servlet。 - **服务阶段**:每当接收到新的HTTP请求时,容器都会调用`service()`方法处理该请求。 -...

    深入研究Servlet线程安全性问题

    Servlet的生命周期由Web容器管理,当客户端首次请求Servlet时,容器会创建Servlet实例。后续请求则复用这个实例,由容器通过线程池分配线程来处理。这意味着,单个Servlet实例可能同时被多个线程访问,这就可能导致...

    servlet学习笔记

    1. **Servlet生命周期**:包括初始化、服务、销毁三个阶段。初始化通过`init()`方法,服务通过`service()`或`doGet()`/`doPost()`,销毁通过`destroy()`方法。 2. **请求与响应处理**:`HttpServletRequest`和`...

    SSM+Servlet图书管理系统

    在图书管理系统中,Spring负责管理对象的生命周期和对象间的依赖关系,通过配置文件或注解实现组件的装配。 2. **SpringMVC**:SpringMVC是Spring框架的一部分,专门用于处理Web请求。它遵循Model-View-Controller...

Global site tag (gtag.js) - Google Analytics