Servlet生命周期分为三个阶段:
1,初始化阶段 调用init()方法
2,响应客户请求阶段 调用service()方法
3,终止阶段 调用destroy()方法
Servlet初始化阶段:
在下列时刻Servlet容器装载Servlet:
1,Servlet容器启动时自动装载某些Servlet,实现它只需要在web.XML文件中的<Servlet></Servlet>之间添加如下代码:<loadon-startup>1</loadon-startup>
2,在Servlet容器启动后,客户首次向Servlet发送请求
3,Servlet类文件被更新后,重新装载Servlet
Servlet被装载后,Servlet容器创建一个Servlet实例并且调用Servlet的init()方法进行初始化。在Servlet的整个生命周期内,init()方法只被调用一次。
1、WebClient 向Servlet容器(Tomcat)发出Http请求
2、Servlet容器接收WebClient的请求
3、Servlet容器创建一个HttpRequest对象,将WebClient请求的信息封装到这个对象中
4、Servlet容器创建一个HttpResponse对象
5、Servlet容器调用HttpServlet对象的service方法,把HttpRequest对象与HttpResponse对象作为参数 传给 HttpServlet对象
6、HttpServlet调用HttpRequest对象的有关方法,获取Http请求信息
7、HttpServlet调用HttpResponse对象的有关方法,生成响应数据
8、Servlet容器把HttpServlet的响应结果传给WebClient
<%@ page isThreadSafe="true|false"%> 默认值为true
并不是servlet的单线程模式,servlet永远是运行于多线程环境的,一个request一个线程。这句配置实际是指明使用servlet的单例模式,即所有request的线程都使用同一个servlet实例处理.
同步跟单例不能混为一谈。
有并发的地方就需要同步,跟单例无关。
单例如果不用惰性加载,也完全不需要同步。
isThreadSafe=false模式表示它是以Singleton模式运行。
该模式implements了接口SingleThreadMode,该模式同一时刻只有一个实例,不会出现信息同步与否的概念。
若多个用户同时访问一个这种模式的页面,那么先访问者完全执行完该页面后,后访问者才开始执行。
isThreadSafe=true模式表示它以多线程方式运行。
该模式的信息同步,需访问同步方法(用synchronized标记的)来实现。
Servlet容器如何同时来处理多个请求
工作者线程WorkThread:执行代码的一组线程
调度线程DispatcherThread:每个线程都具有分配给它的线程优先级,线程是根据优先级调度执行的
Servlet采用多线程来处理多个请求同时访问。servlet依赖于一个线程池来服务请求。线程池实际上是一系列的工作者线程集合。Servlet使用一个调度线程来管理工作者线程.
当容器收到一个Servlet请求,调度线程从线程池中选出一个工作者线程,将请求传递给该工作者线程,然后由该线程来执行Servlet的service方法。当这个线程正在执行的时候,容器收到另外一个请求,调度线程同样从线程池中选出另一个工作者线程来服务新的请求,容器并不关心这个请求是否访问的是同一个Servlet.当容器同时收到对同一个Servlet的多个请求的时候,那么这个Servlet的service()方法将在多线程中并发执行。
Servlet容器默认采用单实例多线程的方式来处理请求,这样减少产生Servlet实例的开销,提升了对请求的响应时间,对于Tomcat可以在server.xml中通过<Connector>元素设置线程池中线程的数目。
就实现来说:
调度者线程类所担负的责任如其名字,该类的责任是调度线程,只需要利用自己的属性完成自己的责任。所以该类是承担了责任的,并且该类的责任又集中到唯一的单体对象中。而其他对象又依赖于该特定对象所承担的责任,我们就需要得到该特定对象。那该类就是一个单例模式的实现了。
Servlet的多线程机制
Servlet体系结构是建立在Java多线程机制之上的,它的生命周期是由Web容器负责的。当客户端第一次请求某个Servlet时,Servlet容器将会根据web.xml配置文件实例化这个Servlet类。当有新的客户端请求该Servlet时,一般不会再实例化该Servlet类,也就是有多个线程在使用这个实例。这样,当两个或多个线程同时访问同一个Servlet时,可能会发生多个线程同时访问同一资源的情况,数据可能会变得不一致。
模式带来性能的提升(不用每次request访问都创建servlet)。但是它不是线程安全的,如果servlet中有自己的field,多个线程访问修改这个field将会导致不可预料了问题。所以配置中是isThreadSafe=”false”,告诉你这样配置是线程不安全的。
设计线程安全的Servlet
1、实现SingleThreadModel 接口
该接口指定了系统如何处理对同一个Servlet的调用。如果一个Servlet被这个接口指定,那么在这个Servlet中的service方法将不会有两个线程被同时执行,当然也就不存在线程安全的问题。
Public class ConcurrentTest extends HttpServletimplements SingleThreadModel {
…………
}
2、同步对共享数据的操作
使用synchronized关键字能保证一次只有一个线程可以访问被保护的区段,在本论文中的Servlet可以通过同步块操作来保证线程的安全。同步后的代码如下:
public class ConcurrentTest extends HttpServlet {
privatePrintWriter output;
publicvoid service (HttpServletRequest request,HttpServletResponse response) throwsServletException, IOException {
Stringusername = request.getParameter ("username");
synchronized(this){
output= response.getWriter ();
try{
Thread.Sleep (5000);
}Catch (Interrupted Exception e){}
output.println("用户名:"+Username+"<BR>");
}
}
}
3、避免使用实例变量
本实例中的线程安全问题是由实例变量造成的,只要在Servlet里面的任何方法里面都不使用实例变量,那么该Servlet就是线程安全的。
修正上面的Servlet代码,将实例变量改为局部变量实现同样的功能,代码如下:
public class ConcurrentTest extends HttpServlet {
publicvoid service (HttpServletRequest request, HttpServletResponse Response) throwsServletException, IOException {
PrintWriteroutput;
Stringusername;
response.setContentType("text/html; charset=gb2312");
...
}
}
对上面的三种方法进行测试,可以表明用它们都能设计出线程安全的Servlet程序。但是,如果一个Servlet实现了SingleThreadModel接口,Servlet引擎将为每个新的请求创建一个单独的Servlet实例,这将引起大量的系统开销。 SingleThreadModel在Servlet2.4中已不再提倡使用;同样如果在程序中使用同步来保护要使用的共享的数据,也会使系统的性能大大下降。这是因为被同步的代码块在同一时刻只能有一个线程执行它,使得其同时处理客户请求的吞吐量降低,而且很多客户处于阻塞状态。另外为保证主存内容和线程的工作内存中的数据的一致性,要频繁地刷新缓存,这也会大大地影响系统的性能。所以在实际的开发中也应避免或最小化Servlet 中的同步代码;在Servlet中避免使用实例变量是保证Servlet线程安全的最佳选择。从Java内存模型也可以知道,方法中的临时变量是在栈上分配空间,而且每个线程都有自己私有的栈空间,所以它们不会影响线程的安全。
Servlet的线程安全问题只有在大量的并发访问时才会显现出来,并且很难发现,因此在编写Servlet程序时要特别注意。线程安全问题主要是由实例变量造成的,因此在Servlet中应避免使用实例变量。如果应用程序设计无法避免使用实例变量,那么使用同步来保护要使用的实例变量,但为保证系统的最佳性能,应该同步可用性最小的代码路径。
属性的线程安全:ServletContext,HttpSession,ServletRequest对象中属性
ServletContext:(线程是不安全的)
ServletContext是可以多线程同时读/写属性的,线程是不安全的。要对属性的读写进行同步处理或者进行深度Clone()。
所以在Servlet上下文中尽可能少量保存会被修改(写)的数据,可以采取其他方式在多个Servlet中共享,比方我们可以使用单例模式来处理共享数据。
HttpSession:(线程是不安全的)
HttpSession对象在用户会话期间存在,只能在处理属于同一个Session的请求的线程中被访问,因此Session对象的属性访问理论上是线程安全的。
当用户打开多个同属于一个进程的浏览器窗口,在这些窗口的访问属于同一个Session,会出现多次请求,需要多个工作线程来处理请求,可能造成同时多线程读写属性。
这时我们需要对属性的读写进行同步处理:使用同步块Synchronized和使用读/写器来解决。
ServletRequest:(线程是安全的)
对于每一个请求,由一个工作线程来执行,都会创建有一个新的ServletRequest对象,所以ServletRequest对象只能在一个线程中被访问。ServletRequest是线程安全的。
注意:ServletRequest对象在service方法的范围内是有效的,不要试图在service方法结束后仍然保存请求对象的引用。
不要在Servlet中创建自己的线程来完成某个功能。Servlet本身就是多线程的,在Servlet中再创建线程,将导致执行情况复杂化,出现多线程安全问题。
在多个servlet中对外部对象(比方文件)进行修改操作一定要加锁,做到互斥的访问
destroy方法在容器移除servlet时执行,同样只执行一次。这个方法会在所有的线程的service()方法执行完成或者超时后执行,调用这个方法后,容器不会再调用这个servlet的方法,也就是说容器不再把请求发送给这个servlet。这个方法给servlet释放占用的资源的机会,通常用来执行一些清理任务。
一旦servlet容器检测到一个servlet要被卸载,这可能是因为要回收资源或因为它正在被关闭,服务器会在所有servlet的service()线程完成之后(或在服务器规定时间后)调用servlet的destroy()方法。然后servlet就可以进行无用存储单元收集清理。Servlet容器不需要为保留一个servlet而指定时间。
destroy() 方法仅执行一次,即在服务器停止且卸装Servlet 时执行该方法。当服务器卸装Servlet 时,将在所有service() 方法调用完成后,或在指定的时间间隔过后调用destroy() 方法。一个Servlet在运行service()方法时可能会产生其它的线程,因此请确认在调用destroy() 方法时,这些线程已终止或完成。
loadOnStartup < 0
即负数的情况下,web容器启动的时候不做实例化处理,servlet首次被调用时做实例化
这种情况和没有设置loadOnStartup是一样的。
loadOnStartup > 0
web容器启动的时候做实例化处理,顺序是由小到大,正整数小的先被实例化
loadOnStartup = 0
web容器启动的时候做实例化处理,相当于是最大整数,因此web容器启动时,最后被实例化
如何在JSP中防止页面输出被浏览器缓存?
<%
response.setHeader("Cache-Control","no-cache");
response.setHeader("Pragma","no-cache");
response.setDateHeader("Expires",0);
%>
document.getElementsByName() returns aHTMLCollection of all the elements witha given value for the name attribute
HTMLCollection is an interface representing a genericcollection of elements (in document order) and offers methods and propertiesfor traversing the list.
<c:choose> <c:when>当 多个c:when同事满足条件时,只执行第一个满足条件的语句
一个tag就是一个普通的java类,它惟一特别之处是它必须继承TagSupport或者BodyTagSupport类。这两个类提供了一些方法,负责jsp页面和你编写的类之间的交互,例如输入,输出。而这两个类是由jsp容器提供的
处理 doStartTag 或doEndTag 。这两个方法是TagSupport提供的。 还是以c:outvalue=""/为例,当jsp解析这个标签的时候,在“<”处触发doStartTag 事件,在“>”时触发doEndTag 事件。通常在doStartTag 里进行逻辑操作,在doEndTag 里控制输出。
如何支持el表达式 <c:outvalue="${tut}"/>
1, 支持el表达式:
当el表达式作为入参时,调用如下方法,在tag内即可自动把el表达式转化。
例如,你想tag的value字段支持el表达式,那么只需在set方法里如下调用:
importorg.apache.taglibs.standard.lang.support.ExpressionEvaluatorManager;
private Object value = null;
public void setValue(Object value)throws JspException{
this.value =ExpressionEvaluatorManager.evaluate("value", value.toString(),Object.class, this, pageContext);
}
evaluate()有四个参数。第一个表示tag的名字,在取el表达式出错时使用。一般和属性名 字相同。第二个要求字符串,通常简单调用输入对象的toString方法。第三个是类,通常用Object.class。第四个用this即可,第五个是pageContext变量。
通常不用对这个方法思考太多。只需改改属性名字,其他照搬即可。
注意:当你的tag属性支持el表达式时,你必须把它声明为Object对象。如上述的value.
2, 用BeanUtil取属性值
import org.apache.commons.beanutils.PropertyUtils;
private String property=null;
Object propertyValue = PropertyUtils.getProperty(value,property);
3, 设置request里的值
pageContext.setAttribute("var",propertyValue);
4, 打印
pageContext.getOut().print(outputString);
5, 取得父标签,取得想要的标签,即使它非父
getParent()
findAncestorWithClass(this,ancestorTag.class);
6, 标签自带方法和常量,方法按照容器的调用顺序排列。示例
<c:if test="...">
<c:outvalue="..."/>
</c:if>
doStartTag : 容器解析到c:if左尖括号(“<”)时调用
doInitBody : 容器解析到c:if右尖括号(“>”)和c:out左尖括号(“<”)时调用
doAfterBody : 容器解析到c:out结束标记(“/>”)时调用
doEndTag :容器解析到c:if结束标记(“/>”)时调用
EVAL_BODY_SKIP : 通常在 doStartTag 方法里调用,忽略标签包括的内容,假如返回这个值,上面的c:if忽略c:out
EVAL_BODY_INCLUDE :通常在 doAfterBody 方法里调用,再次执行body,假如返回这个值,上面的c:out被执行多次
EVAL_PAGE :可在任何方法里调用。返回jsp页面
<jsp:attribute>元素主要有两个用途:
(1) 当使用在<jsp:element>之中时,它可以定义XML 元素的属性
(2) 它可以用来设定标准或自定义标签的属性值。如下范例1:
<jsp:attribute>的语法:
<jsp:attribute trim="true | false">
本体内容
</jsp:attribute >
<jsp:attribute>有两个属性:name 和trim。其中name 的值就是标签的属性名称。trim 可为true 或false。假若为true 时,<jsp:attribute>本体内容的前后空白,将被忽略;反之,若为false,
前后空白将不被忽略。trim 的默认值为true。
<com:clock wrapperSet="#clock1" groupId="1">
<jsp:attribute name="topics">
{
'/com/clock' : ['start', 'stop']
}
</jsp:attribute>
</com:clock>
那么 com:clock有一个属性为'topics',jsp:attribute指定的是属性topics的值
<com:clock wrapperSet="#clock1" groupId="1" topics="{'/com/clock' : ['start', 'stop']}">
</jfpui:spinner>
范例:
<jsp:useBean >
Bean created! Setting foo.bar...<br>
<jsp:setProperty property="bar">
<jsp:attribute >
Hello World
</jsp:attribute>
</jsp:setProperty>
</jsp:useBean>
Result: <jsp:getProperty property="bar">
执行的结果如下:
Bean created! Setting foo.bar...
Result: Hello World
其实上述的范例和下面的例子一样:
<jsp:useBean >
Bean created! Setting foo.bar...<br>
<jsp:setProperty property="bar" value="Hello World" >
</jsp:setProperty>
</jsp:useBean>
Result: <jsp:getProperty property="bar">
一、jspf扩展名文件在可以看作是jsp文件include进来的。
二、网上有的说的jspf (Java Simple Plugin Framework) 是一个插件框架,集成了很多 IoC 框架的概念在里面。此jspf不是彼jspf。
三、jspf里可以直接读取XML。
四、用在写表格比较方便。
jspf只有用"@include"指令的时候,jspf文件的内容才会被解析并执行其中的jsp指令和tag;
使用"jsp:include"和JSTL 的"c:import"都没有用,jspf文件被当作纯文本文件处理了。
jspf可以看作是jsp里的一个片段,其相对于jsp也稍有不同.
在一个jsp页面中引入jspf文件跟普通引入的jsp文件除了后缀没有其他区别,都不能包含<html><head><body>等标签。
servlet filter listener
一、概念:
1、servlet:servlet是一种运行服务器端的java应用程序,具有独立于平台和协议的特性,并且可以动态的生成web页面,它工作在客户端请求与服务器响应的中间层。
2、filter:filter是一个可以复用的代码片段,可以用来转换HTTP请求、响应和头信息。Filter不像Servlet,它不能产生一个请求或者响应,它只是修改对某一资源的请求,或者修改从某一的响应。
3、listener:监听器,从字面上可以看出listener主要用来监听只用。通过listener可以监听web服务器中某一个执行动作,并根据其要求作出相应的响应。通俗的语言说就是在application,session,request三个对象创建消亡或者往其中添加修改删除属性时自动执行代码的功能组件。
二、生命周期:
1、servlet..
2、filter:(一定要实现javax.servlet包的Filter接口的三个方法init()、doFilter()、destroy(),空实现也行)
(1)、启动服务器时加载过滤器的实例,并调用init()方法来初始化实例;
(2)、每一次请求时都只调用方法doFilter()进行处理;
(3)、停止服务器时调用destroy()方法,销毁实例。
3、listener:类似于servlet和filter
web.xml 的加载顺序是:context- param -> listener -> filter -> servlet
三、职责
1、servlet:
创建并返回一个包含基于客户请求性质的动态内容的完整的html页面;
创建可嵌入到现有的html页面中的一部分html页面(html片段);
读取客户端发来的隐藏数据;
读取客户端发来的显示数据;
与其他服务器资源(包括数据库和java的应用程序)进行通信;
通过状态代码和响应头向客户端发送隐藏数据。
2、filter:
filter能够在一个请求到达servlet之前预处理用户请求,也可以在离开servlet时处理http响应:
在执行servlet之前,首先执行filter程序,并为之做一些预处理工作;
根据程序需要修改请求和响应;
在servlet被调用之后截获servlet的执行
3、listener:职责如概念。
servlet2.4规范中提供了8个listener接口,可以将其分为三类,分别如下:
第一类:与servletContext有关的listner接口。包括:ServletContextListener、 ServletContextAttributeListener
第二类:与HttpSession有关的Listner接口。包括:HttpSessionListner、 HttpSessionAttributeListener、HttpSessionBindingListener、 HttpSessionActivationListener;
第三类:与ServletRequest有关的Listener接口,包括:ServletRequestListner、 ServletRequestAttributeListener
四、几个区别:
1,servlet 流程是短的,url传来之后,就对其进行处理,之后返回或转向到某一自己指定的页面。它主要用来在 业务处理之前进行控制.
2,filter 流程是线性的, url传来之后,检查之后,可保持原来的流程继续向下执行,被下一个filter, servlet接收等,而servlet 处理之后,不会继续向下传递。filter功能可用来保持流程继续按照原来的方式进行下去,或者主导流程,而servlet的功能主要用来主导流程。
filter可用来进行字符编码的过滤,检测用户是否登陆的过滤,禁止页面缓存等
3, servlet,filter都是针对url之类的,而listener是针对对象的操作的,如session的创建,session.setAttribute的发生,在这样的事件发生时做一些事情。