- 浏览: 27984 次
- 性别:
- 来自: 长沙
文章分类
最新评论
一、 编译过程 每一个JSP页面都会被Web容器编译成一个Java类,供 web容器调用,并且生成HTML页面回馈给用户。而了解其中的编译方法和规则,对我们学习JSP是非常有好处的,可以说学习好了这个编译原理,就已经学习好了大部分的 JSP知识,剩下的工作就只剩下熟记一些tablib和反复应用以使自己更加熟练而已了。
JSP会被编译成 jsp名称_jsp.java文件放Tomcat/work/Catalina/localhost/项目名称/org/apache/jsp/目录下
然后编译成 jsp名称_jsp.class文件
jsp = java + html
servlet = java + out.print(html)
在第一次请求web服务时会执行如下过程:
1.客户端发送请求给web容器
2.web容器将jsp首先转译成servlet源代码
3.web容器将servlet源代码编译成.class 文件
4.web容器执行.class 文件
5.web容器将结果响应给客户端
所以web第一次为请求提供服务比较慢,从第二次请求开始会省略2、3两个步骤。
编译原理
j2ee规范中对jsp的编译有个规范:第一步,先编译出来一个xml文件, 第二部再从这个xml文件编译为一个java文件
例如: test.jsp
第一步,先编译为一个xml文件,结果如下
第二步,再编译为一个java文件, 大致结果如下
从中可以看出编译过程, 编译器依次读入文本, 遇到<%@就认为这是个jsp指令, 指令是对编译和执行这个jsp生效的.
当遇到<%!它的时候就认为这是个声明, 其中的内容会直接生成为类的类属性或者类方法, 这个看里面是怎么写的,
例如: int a = 1; 就认为这是个类属性.
当遇到<%它的时候就认为这是个脚本, 会被放置到默认的方法里面的.
以上是jsp的编译过程, 还没有说对标签怎么编译, 后面再说.
有个问题, 当编译器遇到<%的时候,会依次读入后续内容直到遇到%>, 如果里面的java代码里面包含了个字符串,这个字符串的内容是%>,怎么办?
我知道的是像tomcat是不会处理这种情况的,也就是说jsp的编译器并不做语法检查, 只解析字符串, 上面的这种情况编译出来的结果就是错的了,下一步再编译为class文件的时候就会报未结束的字符常量. 例如:
编译出来的结果大致如下:
j2ee规范还定义了jsp可以使用xml语法编写, 因为jsp是先编译为xml, 其实<%也是先编译成了<jsp:scriptlet>因此下面的两个文件是等效的:
文件1:
文件2:
不过对于规范,不同的容器在实现的时候并不一定会按照规范来做,我知道的是tomcat是按照这个来做的,并且我记得在tomcat的早期版本中还能在work目录中找到对应的xml文件.
但是websphere是不支持的,不知道现在的版本支不支持, resin好像也不支持, 也就是说在websphere中, <%必须写成<%, 不能用<jsp:script>
websphere并没有先编译为xml, 再编译为java
以上的编译过程对于编码来说是很简单的,如果不编译为xml文件,它简单到只用正则就能搞定.
EL表达式
对于el表达式的支持也很简单, 遇到${, 就开始读入, 直到遇到}, 将其中的内容生成为一个表达式对象, 直接调用该表达式的write方法即可, 例如:
abc${user.name}123
编译结果大致如下:
不同的容器在实现的时候有所不同, 例如resin, 会将所有的表达式编译为类的静态变量, 以提升性能. 因为一个jsp页面一旦写好, 表达式的数目和内容是确定的,
因此是可以编译为静态变量的.
为什么要编译为调用表达式的write方法, 而不是编译为out.write(_expr_xxx.getValue()), 我认为其中一个原因是为了表达式做null处理,任何一个表达式如果返回会空, 那么写到页面上都应该是"", 而不应该是"null"。out.write默认会将null对象转为"null"字符串写入, 如果编译为out.write(_expr_xxx.getValue()),就得 out.write((_expr_xxx.getValue() != null ? _expr_xxx.getValue() : ""));很显然这样是影响性能的, 因为如果返回结果不为null的话对表达式可能会计算两次.如果不这样做,就需要重新定义变量, 为了变量不冲突,每个地方编译器都要生成一个新的变量名, 导致最终生成的文件较大.
tag编译
对tag的编译略微麻烦,但也不复杂,这需要对源文件做html解析,但是跟一个完整的html解析器比起来,对tag的解析相对来说简单多了
只需要在遇到'<'字符的时候读出来节点名,然后在当前应用支持的标签库中去查找对应的标签类, 如果没查到,就按照上面的继续编译为out.write("<");
否则, 读入所有的属性, 创建一个标签实例, 然后根据定义的属性和标签中定义的属性,依次调用对应的setter方法, 例如:
编译结果大致为:
上面是一个标签运行的标准流程, 事实上对于不同的容器,编译结果区别很大,例如resin, 实际编译结果大致如下:
很简单的编译结果, 对于j2ee核心标签库的支持除了forEach编译为了循环之外,其他的一律编译成了很简单的代码,都没有使用循环.
这一点可能是为了减小编译结果,并且提升性能。
因为对于大部分标签来说实在没有必要按照标准的tag执行流程来编译, 对于核心标签库中定义的标签因为行为很明确,所以可以简化编译结果.
tomcat对于标签的编译, 采用的是每个标签都编译为一个方法, 并且采用的是do...while结构. resin则都编译在_jspService方法内.
标签的结束, 在编译标签的过程中,如何知道标签结束了呢?一个很简单的想法是,如果遇到开始标签,就一直读入,直到遇到结束标签,很显然这样是行不通的。
因为标签有嵌套,如果遇到嵌套标签怎么办?按照上面的流程接着读啊,读到子标签结束, 再然后呢? 稍微懂点数据结构的话,就很容易了,用栈。
同样的问题,大致的解决思路都是一样的, 比如计算器, 比如html,xml解析器, 都可以这么做, 对于html解析器,我将会写另外一篇文章专门说明.
先建立一个栈, 当遇到一个标签的时候,就先把它压入栈, 元素内容根据需要自己定义, 我们暂时假定结构如下:
注意是把TagInfo压入栈
当遇到一个结束标签的时候, 取得结束标签的nodeName, 然后从栈弹出一个元素, 如果tagInfo.nodeName == nodeName, 那么生成该标签结束的代码
对于标签的标准流程来说,只需要生成如下的代码就可以了:
如果当前nodeName != tagInfo.nodeName那么就继续弹, 直到找到一个对应的标签, 其实这种情况只是容错处理,
实际上页面最后运行出来的结果跟jsp编写者的预期是不一致的.
如果一直到栈底都没找到,那就抛异常吧。
对于栈来说,很多时候不需要pop, 只需要查看一下栈顶是否符合要求,符合的时候才pop, 否则先pop, 不符合还得push, 很麻烦
所以栈最好提供一个peek函数, 传入一个int, 默认是栈顶, 根据参数决定返回当前栈的那个元素, 这样比较方便
最后, 在jsp中,规范规定, 所有以_jsp开头的变量都不能使用, 这是留给API或者容器用的.
上面是对jsp编译过程的一个分析,对于j2ee规范定义的部分,我没有看过原文,是从一些java书上看的一些零散的东西, 更多的是
看一些容器编译出来的java源文件分析和猜测的,可能很多地方的想法跟j2ee规范定义的不一致,有兴趣的可以在java官网找一下规范原文看看。
JSP与Servlet区别
本文的主要结构:
1. Servlet是什么?
2. JSP与Servlet.
3. Servlet 概述
4. 附录一
5. 附录二
在讲述的过程中,主要是针对JSP和Servlet关系区别来写。
1. Servlet是什么?
客户机/服务器计算的发展。Java提供了一整套客户机/服务器解决方案,在这个方案中,程序可以自动地下载到客户端并执行,这就是applet。但是它仅仅是问题的一半。问题的另一半就是Servlet。
servlet可以被认为是服务器端的applet。servlet被Web服务器加载和执行,就如同applet被浏览器加载和执行一样。servlet从客户端(通过Web服务器)接收请求,执行某种作业,然后返回结果。使用servlet的基本流程如下:
1.客户端通过HTTP提出请求.
2.Web服务器接收该请求并将其发给servlet。如果这个servlet尚未被加载,Web服务器将把它加载到Java虚拟机并且执行它。
3.servlet将接收该HTTP请求并执行某种处理。
4.servlet将向Web服务器返回应答。
5·Web服务器将从servlet收到的应答发送给客户端。
由于servlet是在服务器上执行,通常与applet相关的安全性的问题并不需实现。要注意的是Web浏览器并不直接和servlet通信,servlet是由Web服务器加载和执行的。
而servlet是用Java编写的,所以它们一开始就是平台无关的。这样,Java编写一次就可以在任何平台运行(write once,run anywhere)的承诺就同样可以在服务器上实现了。servlet还有一些CGI脚本所不具备的独特优点: (本人对CGI并不是十分了解,所以这些特点不能完全的体会到,这也是摘自论坛的贴子,请见谅)
servlet是持久的。servlet只需Web服务器加载一次,而且可以在不同请求之间保持服务(例如一次数据库连接)。与之相反,CGI脚本是短暂的、瞬态的。每一次对CGI脚本的请求,都会使Web服务器加载并执行该脚本。一旦这个CGI脚本运行结束,它就会被从内存中清除,然后将结果返回到客户端。CGI脚本的每一次使用,都会造成程序初始化过程(例如连接数据库)的重复执行。
servlet是与平台无关的。如前所述,servlet是用Java编写的,它自然也继承了Java的平台无关性。
servlet是可扩展的。由于servlet是用Java编写的,它就具备了Java所能带来的所有优点。Java是健壮的、面向对象的编程语言,它很容易扩展以适应你的需求。servlet自然也具备了这些特征。
servlet是安全的。从外界调用一个servlet的惟一方法就是通过Web服务器。这提供了高水平的安全性保障,尤其是在你的Web服务器有防火墙保护的时候。
setvlet可以在多种多样的客户机上使用。由于servlet是用Java编写的,所以你可以很方便地在HTML中使用它们,就像你使用applet一样。
那么,Servlet是怎样执行的?怎样来写一个Servlet,它的基本架构是怎么样的?
这些问题,将在后面部分给予介绍。
2.JSP与Servlet
现在已经对Servlet有了大概的了解,现在我们就来说说JSP和Servlet的关系。
JSP是一种脚本语言,包装了Java Servlet系统的界面,简化了Java和Servlet的使用难度,同时通过扩展JSP标签(TAG)提供了网页动态执行的能力。尽管如此,JSP仍没有超出Java和Servlet的范围,不仅JSP页面上可以直接写Java代码,而且JSP是先被译成Servlet之后才实际运行的。JSP在服务器上执行,并将执行结果输出到客户端浏览器,我们可以说基本上与浏览器无关。它是与JavaScript不同的,JavaScript是在客户端的脚本语言,在客户端执行,与服务器无关。
JSP与Servlet之间的主要差异在于,JSP提供了一套简单的标签,和HTML融合的比较好,可以使不了解Servlet的人可以做出动态网页来。对于Java语言不熟悉的人,会觉得JSP开发比较方便。JSP修改后可以立即看到结果,不需要手工编译,JSP引擎会来做这些工作;而Servelt缺需要编译,重新启动Servlet引擎等一系列动作。但是在JSP中,HTML与程序代码混杂在一起,而Servlet却不是这样。也许大家比较混乱了,那么Servlet又是什么?下面我们对JSP的运行来做一个简单的介绍,告诉大家怎样来执行一个JSP文件:
当Web服务器(或Servlet引擎,应用服务器)支持JSP引擎时,JSP引擎会照着JSP的语法,将JSP文件转换成Servlet代码源文件,接着Servlet会被编译成Java可执行字节码(bytecode),并以一般的Servlet方式载入执行。
JSP语法简单,可以方便的嵌入HTML之中,很容易加入动态的部分,方便的输出HTML。在Servlet中输出HTML缺需要调用特定的方法,对于引号之类的字符也要做特殊的处理,加在复杂的HTML页面中作为动态部分,比起JSP来说是比较困难的。
除去了转换和编译阶段,JSP和Servlet之间的区别实在是不大。
JSP引擎通常架构在Servlet引擎之上,本身就是一个Servlet,把JSP文件转译成Servlet源代码,再调用Java编译器,编译成Servlet。这也是JSP在第一次调用时速度比较慢的原因,在第一次编译之后,JSP与Servlet速度相同.下面我们来看看为什么他们在第一次编译后再编译的速度相同:
在整个运行过程中,JSP引擎会检查编译好的JSP(以Servlet形式存在)是否比原始的JSP文件还新,如果是,JSP引擎不会编译;如果不是,表示JSP文件比较新,就会重新执行转译与编译的过程。
为了有个深刻的了解,我们看一下JSP的运行和开发环境:
浏览器:常见的浏览器有IE和Netscape两种。
数据库:常用的数据库有Oracle,SQL Server,Informix,DB2,Sybase,Access,MySQL等。
操作系统:常见的有Windows,Linux,以及各种Unix系统。
Web服务器:常见的有IIS,Apache,Netscape Enterprise Server等。
JSP引擎:一般JSP引擎都以Servlet引擎为基础,并以Servlet的形式出现。同时,在各种免费和商业引擎的实现中,Servlet引擎和Jsp引擎通常也是一起出现,我们成为Servlet/JSP引擎,或从某种成为JSP引擎。
JSP引擎是可以提供JSP和Servlet运行支持并对其生存周期进行管理的系统级实体。
在JSP页面第一次被请求时,JSP引擎会将JSP原始文件转换成Servlet源代码,然后调用Java编译器,编译成Servlet,并在Servlet引擎中执行。当再次有请求的时候,JSP引擎会见差异编译好的JSP是否比原来的JSP原始文件要新,如果是,运行Servlet;如果不是,表示文件已经更新的了,就会从新执行转换和编译的过程。
说到这里,也基本把JSP和Servlet的关系说清楚了,从我的感觉上看用JSP就可以了,简单又方便,又可以和Bean 很好的兼容使用,功能又很强大,为什么又出现了Servlet,它又有什么用?何况它的编写又相对复杂。为了把问题说得更清楚一点,我想在这里说一下历史,顺便再讲一下为什么还要用Servlet,Servlet的好处是什么。
历史简述:(摘自某论坛有删节,改写)
简单的说,SUN首先发展出SERVLET,其功能比较强劲,体系设计也很先进,只是,它输出HTML语句还是采用了老的CGI方式,是一句一句输出,所以,编写和修改HTML非常不方便。
后来SUN推出了类似于ASP的镶嵌型的JSP(是Servlet发展的产物),把JSP TAG镶嵌到HTML语句中,这样,就大大简化和方便了网页的设计和修改。新型的网络语言如ASP,PHP,JSP都是镶嵌型的SCRIPT语言。
从网络三层结构的角度看,一个网络项目最少分三层:data layer,business layer, presentation layer。当然也可以更复杂。SERVLET用来写business layer是很强大的,但是对于写presentation layer就很不方便。JSP则主要是为了方便写presentation layer而设计的。当然也可以写business layer。写惯了ASP,PHP,CGI的朋友,经常会不自觉的把presentation layer和business layer混在一起。把数据库处理信息放到JSP中,其实,它应该放在business layer中。
根据SUN自己的推荐,JSP中应该仅仅存放与presentation layer有关的内容,也就是说,只放输出HTML网页的部份。而所有的数据计算,数据分析,数据库联结处理,统统是属于business layer,应该放在JAVA BEANS中。通过JSP调用JAVA BEANS,实现两层的整合。
实际上,微软推出的DNA技术,简单说,就是ASP+COM/DCOM技术。与JSP+BEANS完全类似,所有的presentation layer由ASP完成,所有的business layer由COM/DCOM完成。通过调用,实现整合。现在微软推出的.NET也是通过这个理念,所有的presentation layer由ASP.NET完成,business layer由C#或VB.NET或VC.NET来完成。
为什么要采用这些组件技术呢?因为单纯的ASP/JSP语言是非常低效率执行的,如果出现大量用户点击,纯SCRIPT语言很快就到达了他的功能上限,而组件技术就能大幅度提高功能上限,加快执行速度。
另外一方面,纯SCRIPT语言将presentation layer和business layer混在一起,造成修改不方便,并且代码不能重复利用。如果想修改一个地方,经常会牵涉到十几页CODE,采用组件技术就只改组件就可以了。
综上所述,SERVLET是一个早期的不完善的产品,写business layer很好,写presentation layer就很不好,并且两层混杂,显得十分混乱。
所以,推出JSP+BAEN,用JSP写presentation layer,用BAEN写business layer。SUN自己的意思也是将来用JSP替代SERVLET。
看了上面的叙述,大家可能对JSP与Servlet共存有了比较好的认识。可以看到JSP和Bean结合后的的实用性,强大的表现功能,易用性都是Servlet所不能及的。那么是不是Servlet就被取代了?不是!在以后的发展中,它还是有着巨大的作用的。上面只不过是将了问题的一方面,下面我们来看看Servlet本身的特点。
由于它是由java来写的,所以相关的特点我们就不说了,上文已经有了详细的介绍,我们来看看其他的:
Servlet是用于开发服务器端应用程序的一种编程模型,如果只是一个普通的java应用,可以不使用servlet来编写,但是如果想要提供基于web的服务能力,那么就必须按照这种模型来编写,而且servlet也必须允许在符合servlet规范的java web server or app server之上,否则无法运行。除非你自己实现一个web server,但是其复杂度是比较高的,特别是在企业级应用中,对系统的稳定性和健壮性都要求比较高,所以servlet的模型实际上是简化了编写稳健的服务器端的应用开发过程。Servlet 可以作为提供web服务能力的一个接入方式
现在也许可以理解了什么是Servlet什么是JSP,它们之间的关系是怎样的。下面我就对Servlet这个技术做一个简要的介绍。
3.Servlet概述
3.1 Servlet的结构
在Servlet API中最重要的是Servlet interface. 所有的servlets implement(执行)这个interface, 方式多种:或者是直接的,或者通过extending 这个class执行它,如 HttpServlet. 这个Servlet interface 提供安排servlet与客户端联系的方法. Servlet 编写者可以在他们开发servlet程序时提供更多一些或所有的这样方法.
当一个servlet接收来自客户端的调用请求, 它接收两个对象: 一个是ServletRequest,另外一个是ServletResponse. 这个ServletRequest class 概括从客户端到服务器之间的联系, 而 ServletResponse class 概括从servlet返回客户端的联系.
ServletRequest interface 可以获取到这样一些信息如由客户端传送的阐述名称,客户端正在使用的协议, 产生请求并且接收请求的服务器远端主机名. 它也提供获取数据流的servlet, ServletInputStream, 这些数据是客户端引用中使用HTTP POST 和 PUT 方法递交的. 一个ServletRequest的子类可以让servlet获取更多的协议特性数据. 例如: HttpServletRequest 包含获取HTTP-specific头部信息的方法.
ServletResponse interface 给出相应客户端的servlet方法. 它允许servlet设置内容长度和回应的mime类型, 并且提供输出流, ServletOutputStream, 通过编写者可以发回相应数据. ServletResponse子类可以给出更多protocol-specific容量的信息。 例如: HttpServletResponse 包含允许servlet操作HTTP-specific头部信息的方法.
上面有关classes 和 interfaces描述构成了一个基本的Servlet框架. HTTP servlets有一些附加的可以提供session-tracking capabilities的方法. servlet编写者可以用这些API在有他人操作时维护servlet与客户端之间的状态.
3.2 编写Servlet
Servlets 执行 javax.servlet.Servlet interface. servlet编写者可以通过直接implement interface开发servlet, 但这样通常没有必要. 因为大多数servlet是针对用HTTP协议的web服务器, 这样最通用开发servlet办法是用 javax.servlet.http.HttpServlet 内.HttpServlet 类通过extend GenericServlet基类执行 Servlet interface, 提供了处理HTTP协议的功能. 他的service方法支持标准HTTP/1.1请求. 一般地, 用HttpServlet指定的类编写的servlets可以多线程地并发运行service方法.
Servlet编写者注意HttpServlet类有几个欠缺的方法,你可以自己定义方法中内容,但是必须使用这些方法名称以使servlet知道你想做什么,
doGet, 用于处理 GET、有条件的GET 和头部 HEAD请求
doPost, 用户处理 POST 请求
doPut, 用于处理 PUT 请求
doDelete, 用于处理 DELETE请求
HttpServlet的service方法, 一般地, 当它接收到一个OPTIONS请求时,会调用doOptions 方法, 当接收一个TRACE请求是调用doTrace . doOptions缺省执行方式是自动决定什么样的HTTP被选择并且返回哪个信息.
在你使用这些方法时,必须带两个阐述. 第一个包含来自客户端的数据HttpServletRequest. 第二个参数包含客户端的响应HttpServletResponse. 在下例中是这样的情况.
一个HttpServletRequest对象提供到达HTTP 头部数据, 也允许你获取客户端的数据. 怎样获取这些数据取决于HTTP端请求方法.
不管任何HTTP方式, 你可以用]getParameterValues方法, 这个用来返回特定名称的参数值.
对于用 HTTP GET 请求的方式, 这个getQueryString方法将会返回一个可以用来解剖分析的。对于用HTTP POST, PUT, 和 DELETE请求的方式, 你有两种方法可以选择. 如果是文本数据,你能通过getReader方法用BufferedReader获取;如果是二进制数据, 能通过getReader 方法用 ServletInputStream获取.
为了响应客户端, 一个HttpServletResponse对象提供返回数据给用户的两个方法. 你可以用getWriter 方法返回,或者 getOutputStream 方法以输出流返回. 你应该用getWriter返回文本数据,而用getOutputStream返回二进制数据.
在使用Writer 或 OutputStream之前, HTTP 头部应该先被设置. HttpServletResponse内提供这样一个方法,之后可以用writer 或 outputstream 将响应主体部分发回用户. 完成后要关闭 writer 或 output stream以便让服务器知道响应已经完毕.
附录一
运行你的Servlet!
当一个servlet已经写好怎样来运行测试呢?我花了好长时间来研究这个,也许是因为我太笨。但其实现在想想也不是很难。我想通过一个例子详细的说说,这样会有一个感性的把握。我会把我当时遇到的主要问题用黑体字写出,那时我当时主要浪费时间的地方,希望大家也注意。(我用的运行环境是Tomcat5.0)
首先我们来写一个最简单的servlet:
由于我们把它进行了打包,所以把这个编译好的.class文件放到/Tomcat文件夹/webapps/ourappfiles/WEB-INF/classes/test的文件夹下。
接着我们需要写一个调用该Servlet的html文件:
注意:这里的method不能用post,不然不会在IE中正常显示,我当初就是在这个地方没有弄好,浪费了好多时间。至于为什么这样写,我也弄不太清楚,由于水平有限,多多包涵。
现在我们还差最后一步,编写我们的web.xml文件。
把编写好的web.xml文件放到/Tomcat文件夹/webapps/ourappfiles/WEB-INF下。
好了,现在万事俱备,就差启动Tomcat运行我们的Servlet了。
以上就是运行Servlet的几个步骤。
附录二
这个附录纯粹是另一个读书笔记,我感觉比较好,所以摘录下来。里面写的是经验的结晶,我还没有这么多的经验,所以只有摘抄别人的以作补充了。
1.ServletConfig
l 一个ServletConfig对象是servlet container在servlet initialization的时候传递给servlet的。
ServletConfig包涵 ServletContext 和 一些 Name/Value pair (来自于deployment descriptor)
l ServletContext接口封装了Web应用程序的上下文概念。
2.会话跟踪
1) Session
l 当一个Client请求多个Servlets时,一个session可以被多个servlet共享。
l 通常情况下,如果server detect到browser支持cookie,那么URL就不会重写。
2) cookie
l 在Java Servlet中,如果你光 Cookie cookie = new Cookie(name,value)
那么当用户退出Browser时,cookie会被删除掉,而不会被存储在客户端的硬盘上。
如果要存储 cookie,需加一句 cookie.setMaxAge(200)
l cookie是跟某一个server相关的,运行在同一个server上的servlet共享一个cookie.
3) URL Rewriting
在使用URL Rewriting来维护Session ID的时候,每一次HTTP请求都需要EncodeURL()
典型的用在两个地方
1) out.print(“form action=/” ”);
out.print(response.encodeURL(“sessionExample”));
out.print(“form action=/” ”);
out.print(“method = GET>”);
2) out.print(“<p><a href=/” ”);
out.print(response.encodeURL(“SessionExample?database=foo&datavalue=bar”));
out.println(“/” >URL encoded </a>”);
3.SingleThreadModel
默认的,每一个servlet definition in a container只有一个servlet class的实例。
只有实现了SingleThreadModel,container才会让servlet有多个实例。
Servlet specification上建议,不要使用synchronized,而使用SingleThreadModel。
SingleThreadModel(没有方法)
保证servlet在同一时刻只处理一个客户的请求。
SingleThreadModel是耗费资源的,特别是当有大量的请求发送给Servlet时,SingleThreadModel的作用是使包容器以同步时钟的方式调用service方法。
这等同于在servlet的service()方法种使用synchronized.
Single Thread Model一般使用在需要响应一个heavy request的时候,比如是一个需要和数据库打交道的连接。
2. 在重载Servlet地init( )方法后,一定要记得调用super.init( );
3. the client通过发送一个blank line表示它已经结束request
而the server通过关闭the socket来表示response已结束了。
4. 一个Http Servlet可以送三种东西给Client
1) a single status code
2) any number of http headers
3) a response body
5. Servlet之间信息共享的一个最简单的方法就是
System.getProperties().put(“key”,”value”);
6. Post和Get
Post:将form内各字段名称和内容放置在html header内传送给server
Get: ?之后的查询字符串要使用URLEncode,经过URLEncode后,这个字符串不再带有空格,以后将在server上恢复所带有的空格。
Get是Web上最经常使用的一种请求方法,每个超链接都使用这种方法。
7. Web.xml就是Web Applicatin 的deployment descriptor
作用有:组织各类元素
设置init param
设置安全性
8. Request Dispatcher用来把接收到的request forward processing到另一个servlet
要在一个response里包含另一个servlet的output时,也要用到Request Dispatcher.
9. Servlet和Jsp在同一个JVM中,可以通过ServeltContext的
setAttribute( )
getAttribute( )
removeAttribute( )
来共享对象
10. 利用request.getParameter( )得到的String存在字符集问题。
可以用 strTitle = request.getParameter(“title”);
strTitle = new String(strTitle.getBytes(“8859-1”),”gb2312”);
如果你希望得到更大得兼容性
String encoding = response.getCharacterEncoding(); //确定Application server用什么编码来读取输入的。
strTitle = new String(strTitle.getBytes(encoding),”gb2312”);
JSP会被编译成 jsp名称_jsp.java文件放Tomcat/work/Catalina/localhost/项目名称/org/apache/jsp/目录下
然后编译成 jsp名称_jsp.class文件
jsp = java + html
servlet = java + out.print(html)
在第一次请求web服务时会执行如下过程:
1.客户端发送请求给web容器
2.web容器将jsp首先转译成servlet源代码
3.web容器将servlet源代码编译成.class 文件
4.web容器执行.class 文件
5.web容器将结果响应给客户端
所以web第一次为请求提供服务比较慢,从第二次请求开始会省略2、3两个步骤。
编译原理
j2ee规范中对jsp的编译有个规范:第一步,先编译出来一个xml文件, 第二部再从这个xml文件编译为一个java文件
例如: test.jsp
<%! int a = 1; private String sayHello(){return "hello";} %> <% int a = 1; %> <h1>Hello World</h1>
第一步,先编译为一个xml文件,结果如下
<jsp:declare> int a = 1; private String sayHello(){return "hello";} </jsp:declare> <jsp:scriptlet> int a = 1; </jsp:scriptlet> <h1>Hello World</h1>
第二步,再编译为一个java文件, 大致结果如下
public class _xxx_test{ int a = 1; private String sayHello(){return "hello";} public void _jspService(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException{ JspWriter out = xxxx.getWriter(); // 创建其他的隐含对象 int a = 1; out.write("<h1>Hello World</h1>"); // 释放资源 } }
从中可以看出编译过程, 编译器依次读入文本, 遇到<%@就认为这是个jsp指令, 指令是对编译和执行这个jsp生效的.
当遇到<%!它的时候就认为这是个声明, 其中的内容会直接生成为类的类属性或者类方法, 这个看里面是怎么写的,
例如: int a = 1; 就认为这是个类属性.
当遇到<%它的时候就认为这是个脚本, 会被放置到默认的方法里面的.
以上是jsp的编译过程, 还没有说对标签怎么编译, 后面再说.
有个问题, 当编译器遇到<%的时候,会依次读入后续内容直到遇到%>, 如果里面的java代码里面包含了个字符串,这个字符串的内容是%>,怎么办?
我知道的是像tomcat是不会处理这种情况的,也就是说jsp的编译器并不做语法检查, 只解析字符串, 上面的这种情况编译出来的结果就是错的了,下一步再编译为class文件的时候就会报未结束的字符常量. 例如:
<% String s = "test%>" %>
编译出来的结果大致如下:
public class _xxx_test{ public void _jspService(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException{ JspWriter out = xxxx.getWriter(); // 创建其他的隐含对象 String s = "test out.write("\"\r\n%>"); // 释放资源 } }
j2ee规范还定义了jsp可以使用xml语法编写, 因为jsp是先编译为xml, 其实<%也是先编译成了<jsp:scriptlet>因此下面的两个文件是等效的:
文件1:
[java] view plaincopy <% int a = 1; %>
文件2:
<jsp:scriptlet>int a = 1;</jsp:scriptlet>
不过对于规范,不同的容器在实现的时候并不一定会按照规范来做,我知道的是tomcat是按照这个来做的,并且我记得在tomcat的早期版本中还能在work目录中找到对应的xml文件.
但是websphere是不支持的,不知道现在的版本支不支持, resin好像也不支持, 也就是说在websphere中, <%必须写成<%, 不能用<jsp:script>
websphere并没有先编译为xml, 再编译为java
以上的编译过程对于编码来说是很简单的,如果不编译为xml文件,它简单到只用正则就能搞定.
EL表达式
对于el表达式的支持也很简单, 遇到${, 就开始读入, 直到遇到}, 将其中的内容生成为一个表达式对象, 直接调用该表达式的write方法即可, 例如:
abc${user.name}123
编译结果大致如下:
public class _xxx_test{ public void _jspService(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException{ JspWriter out = xxxx.getWriter(); ExprEnv exprEnv = xxx.create(); out.write("abc"); org.xxx.xxx.Expr _expr_xxx = xxx.createExpr("${user.name}"); _expr_xxx.write(out, exprEnv); out.write("123\r\n"); } }
不同的容器在实现的时候有所不同, 例如resin, 会将所有的表达式编译为类的静态变量, 以提升性能. 因为一个jsp页面一旦写好, 表达式的数目和内容是确定的,
因此是可以编译为静态变量的.
为什么要编译为调用表达式的write方法, 而不是编译为out.write(_expr_xxx.getValue()), 我认为其中一个原因是为了表达式做null处理,任何一个表达式如果返回会空, 那么写到页面上都应该是"", 而不应该是"null"。out.write默认会将null对象转为"null"字符串写入, 如果编译为out.write(_expr_xxx.getValue()),就得 out.write((_expr_xxx.getValue() != null ? _expr_xxx.getValue() : ""));很显然这样是影响性能的, 因为如果返回结果不为null的话对表达式可能会计算两次.如果不这样做,就需要重新定义变量, 为了变量不冲突,每个地方编译器都要生成一个新的变量名, 导致最终生成的文件较大.
tag编译
对tag的编译略微麻烦,但也不复杂,这需要对源文件做html解析,但是跟一个完整的html解析器比起来,对tag的解析相对来说简单多了
只需要在遇到'<'字符的时候读出来节点名,然后在当前应用支持的标签库中去查找对应的标签类, 如果没查到,就按照上面的继续编译为out.write("<");
否则, 读入所有的属性, 创建一个标签实例, 然后根据定义的属性和标签中定义的属性,依次调用对应的setter方法, 例如:
<c:if test="${user.name == 'tom'}"><h1>a</h1></c:if>
编译结果大致为:
Expr expr_0 = xxx.createExpr("${user.name == 'tom'}"); Tag _tag_0 = new xxx.xxx.IfTag(); _tag_0.setter(...); int _tag_flag_0 = _tag_0.doStartTag(); if(_tag_flag_0 != SKIP_BODY) { while(true) { // doInitBody, doBody等 _tag_flag_0 = _tag_0.doAfterBody(); if(_tag_flag_0 != EVAL_BODY_AGAIN) { break; } } _tag_flag_0 = _tag_0.doEndTag(); }
上面是一个标签运行的标准流程, 事实上对于不同的容器,编译结果区别很大,例如resin, 实际编译结果大致如下:
Expr expr_0 = xxx.createExpr("${user.name == 'tom'}"); if(expr_0.getBoolean()) { }
很简单的编译结果, 对于j2ee核心标签库的支持除了forEach编译为了循环之外,其他的一律编译成了很简单的代码,都没有使用循环.
这一点可能是为了减小编译结果,并且提升性能。
因为对于大部分标签来说实在没有必要按照标准的tag执行流程来编译, 对于核心标签库中定义的标签因为行为很明确,所以可以简化编译结果.
tomcat对于标签的编译, 采用的是每个标签都编译为一个方法, 并且采用的是do...while结构. resin则都编译在_jspService方法内.
标签的结束, 在编译标签的过程中,如何知道标签结束了呢?一个很简单的想法是,如果遇到开始标签,就一直读入,直到遇到结束标签,很显然这样是行不通的。
因为标签有嵌套,如果遇到嵌套标签怎么办?按照上面的流程接着读啊,读到子标签结束, 再然后呢? 稍微懂点数据结构的话,就很容易了,用栈。
同样的问题,大致的解决思路都是一样的, 比如计算器, 比如html,xml解析器, 都可以这么做, 对于html解析器,我将会写另外一篇文章专门说明.
先建立一个栈, 当遇到一个标签的时候,就先把它压入栈, 元素内容根据需要自己定义, 我们暂时假定结构如下:
class TagInfo{ // 节点的名称 String nodeName; // 节点属性 例如: test: ${user.name == 'tom'} Map<String, String> attributes; // 当前标签可能需要用到的变量列表, 例如 flagName: _flag_0, exprName: expr_0等 Map<String, String> variables; }
注意是把TagInfo压入栈
当遇到一个结束标签的时候, 取得结束标签的nodeName, 然后从栈弹出一个元素, 如果tagInfo.nodeName == nodeName, 那么生成该标签结束的代码
对于标签的标准流程来说,只需要生成如下的代码就可以了:
// out.write("<h1>1</h1>"); // 这之前的代码可能都是out.write之类的 // _tag_flag_0之类的变量都从tagInfo获取 _tag_flag_0 = _tag_0.doAfterBody(); // doAfterBody等 if(_tag_flag_0 != EVAL_BODY_AGAIN) { break; } } _tag_flag_0 = _tag_0.doEndTag();
如果当前nodeName != tagInfo.nodeName那么就继续弹, 直到找到一个对应的标签, 其实这种情况只是容错处理,
实际上页面最后运行出来的结果跟jsp编写者的预期是不一致的.
如果一直到栈底都没找到,那就抛异常吧。
对于栈来说,很多时候不需要pop, 只需要查看一下栈顶是否符合要求,符合的时候才pop, 否则先pop, 不符合还得push, 很麻烦
所以栈最好提供一个peek函数, 传入一个int, 默认是栈顶, 根据参数决定返回当前栈的那个元素, 这样比较方便
最后, 在jsp中,规范规定, 所有以_jsp开头的变量都不能使用, 这是留给API或者容器用的.
上面是对jsp编译过程的一个分析,对于j2ee规范定义的部分,我没有看过原文,是从一些java书上看的一些零散的东西, 更多的是
看一些容器编译出来的java源文件分析和猜测的,可能很多地方的想法跟j2ee规范定义的不一致,有兴趣的可以在java官网找一下规范原文看看。
JSP与Servlet区别
本文的主要结构:
1. Servlet是什么?
2. JSP与Servlet.
3. Servlet 概述
4. 附录一
5. 附录二
在讲述的过程中,主要是针对JSP和Servlet关系区别来写。
1. Servlet是什么?
客户机/服务器计算的发展。Java提供了一整套客户机/服务器解决方案,在这个方案中,程序可以自动地下载到客户端并执行,这就是applet。但是它仅仅是问题的一半。问题的另一半就是Servlet。
servlet可以被认为是服务器端的applet。servlet被Web服务器加载和执行,就如同applet被浏览器加载和执行一样。servlet从客户端(通过Web服务器)接收请求,执行某种作业,然后返回结果。使用servlet的基本流程如下:
1.客户端通过HTTP提出请求.
2.Web服务器接收该请求并将其发给servlet。如果这个servlet尚未被加载,Web服务器将把它加载到Java虚拟机并且执行它。
3.servlet将接收该HTTP请求并执行某种处理。
4.servlet将向Web服务器返回应答。
5·Web服务器将从servlet收到的应答发送给客户端。
由于servlet是在服务器上执行,通常与applet相关的安全性的问题并不需实现。要注意的是Web浏览器并不直接和servlet通信,servlet是由Web服务器加载和执行的。
而servlet是用Java编写的,所以它们一开始就是平台无关的。这样,Java编写一次就可以在任何平台运行(write once,run anywhere)的承诺就同样可以在服务器上实现了。servlet还有一些CGI脚本所不具备的独特优点: (本人对CGI并不是十分了解,所以这些特点不能完全的体会到,这也是摘自论坛的贴子,请见谅)
servlet是持久的。servlet只需Web服务器加载一次,而且可以在不同请求之间保持服务(例如一次数据库连接)。与之相反,CGI脚本是短暂的、瞬态的。每一次对CGI脚本的请求,都会使Web服务器加载并执行该脚本。一旦这个CGI脚本运行结束,它就会被从内存中清除,然后将结果返回到客户端。CGI脚本的每一次使用,都会造成程序初始化过程(例如连接数据库)的重复执行。
servlet是与平台无关的。如前所述,servlet是用Java编写的,它自然也继承了Java的平台无关性。
servlet是可扩展的。由于servlet是用Java编写的,它就具备了Java所能带来的所有优点。Java是健壮的、面向对象的编程语言,它很容易扩展以适应你的需求。servlet自然也具备了这些特征。
servlet是安全的。从外界调用一个servlet的惟一方法就是通过Web服务器。这提供了高水平的安全性保障,尤其是在你的Web服务器有防火墙保护的时候。
setvlet可以在多种多样的客户机上使用。由于servlet是用Java编写的,所以你可以很方便地在HTML中使用它们,就像你使用applet一样。
那么,Servlet是怎样执行的?怎样来写一个Servlet,它的基本架构是怎么样的?
这些问题,将在后面部分给予介绍。
2.JSP与Servlet
现在已经对Servlet有了大概的了解,现在我们就来说说JSP和Servlet的关系。
JSP是一种脚本语言,包装了Java Servlet系统的界面,简化了Java和Servlet的使用难度,同时通过扩展JSP标签(TAG)提供了网页动态执行的能力。尽管如此,JSP仍没有超出Java和Servlet的范围,不仅JSP页面上可以直接写Java代码,而且JSP是先被译成Servlet之后才实际运行的。JSP在服务器上执行,并将执行结果输出到客户端浏览器,我们可以说基本上与浏览器无关。它是与JavaScript不同的,JavaScript是在客户端的脚本语言,在客户端执行,与服务器无关。
JSP与Servlet之间的主要差异在于,JSP提供了一套简单的标签,和HTML融合的比较好,可以使不了解Servlet的人可以做出动态网页来。对于Java语言不熟悉的人,会觉得JSP开发比较方便。JSP修改后可以立即看到结果,不需要手工编译,JSP引擎会来做这些工作;而Servelt缺需要编译,重新启动Servlet引擎等一系列动作。但是在JSP中,HTML与程序代码混杂在一起,而Servlet却不是这样。也许大家比较混乱了,那么Servlet又是什么?下面我们对JSP的运行来做一个简单的介绍,告诉大家怎样来执行一个JSP文件:
当Web服务器(或Servlet引擎,应用服务器)支持JSP引擎时,JSP引擎会照着JSP的语法,将JSP文件转换成Servlet代码源文件,接着Servlet会被编译成Java可执行字节码(bytecode),并以一般的Servlet方式载入执行。
JSP语法简单,可以方便的嵌入HTML之中,很容易加入动态的部分,方便的输出HTML。在Servlet中输出HTML缺需要调用特定的方法,对于引号之类的字符也要做特殊的处理,加在复杂的HTML页面中作为动态部分,比起JSP来说是比较困难的。
除去了转换和编译阶段,JSP和Servlet之间的区别实在是不大。
JSP引擎通常架构在Servlet引擎之上,本身就是一个Servlet,把JSP文件转译成Servlet源代码,再调用Java编译器,编译成Servlet。这也是JSP在第一次调用时速度比较慢的原因,在第一次编译之后,JSP与Servlet速度相同.下面我们来看看为什么他们在第一次编译后再编译的速度相同:
在整个运行过程中,JSP引擎会检查编译好的JSP(以Servlet形式存在)是否比原始的JSP文件还新,如果是,JSP引擎不会编译;如果不是,表示JSP文件比较新,就会重新执行转译与编译的过程。
为了有个深刻的了解,我们看一下JSP的运行和开发环境:
浏览器:常见的浏览器有IE和Netscape两种。
数据库:常用的数据库有Oracle,SQL Server,Informix,DB2,Sybase,Access,MySQL等。
操作系统:常见的有Windows,Linux,以及各种Unix系统。
Web服务器:常见的有IIS,Apache,Netscape Enterprise Server等。
JSP引擎:一般JSP引擎都以Servlet引擎为基础,并以Servlet的形式出现。同时,在各种免费和商业引擎的实现中,Servlet引擎和Jsp引擎通常也是一起出现,我们成为Servlet/JSP引擎,或从某种成为JSP引擎。
JSP引擎是可以提供JSP和Servlet运行支持并对其生存周期进行管理的系统级实体。
在JSP页面第一次被请求时,JSP引擎会将JSP原始文件转换成Servlet源代码,然后调用Java编译器,编译成Servlet,并在Servlet引擎中执行。当再次有请求的时候,JSP引擎会见差异编译好的JSP是否比原来的JSP原始文件要新,如果是,运行Servlet;如果不是,表示文件已经更新的了,就会从新执行转换和编译的过程。
说到这里,也基本把JSP和Servlet的关系说清楚了,从我的感觉上看用JSP就可以了,简单又方便,又可以和Bean 很好的兼容使用,功能又很强大,为什么又出现了Servlet,它又有什么用?何况它的编写又相对复杂。为了把问题说得更清楚一点,我想在这里说一下历史,顺便再讲一下为什么还要用Servlet,Servlet的好处是什么。
历史简述:(摘自某论坛有删节,改写)
简单的说,SUN首先发展出SERVLET,其功能比较强劲,体系设计也很先进,只是,它输出HTML语句还是采用了老的CGI方式,是一句一句输出,所以,编写和修改HTML非常不方便。
后来SUN推出了类似于ASP的镶嵌型的JSP(是Servlet发展的产物),把JSP TAG镶嵌到HTML语句中,这样,就大大简化和方便了网页的设计和修改。新型的网络语言如ASP,PHP,JSP都是镶嵌型的SCRIPT语言。
从网络三层结构的角度看,一个网络项目最少分三层:data layer,business layer, presentation layer。当然也可以更复杂。SERVLET用来写business layer是很强大的,但是对于写presentation layer就很不方便。JSP则主要是为了方便写presentation layer而设计的。当然也可以写business layer。写惯了ASP,PHP,CGI的朋友,经常会不自觉的把presentation layer和business layer混在一起。把数据库处理信息放到JSP中,其实,它应该放在business layer中。
根据SUN自己的推荐,JSP中应该仅仅存放与presentation layer有关的内容,也就是说,只放输出HTML网页的部份。而所有的数据计算,数据分析,数据库联结处理,统统是属于business layer,应该放在JAVA BEANS中。通过JSP调用JAVA BEANS,实现两层的整合。
实际上,微软推出的DNA技术,简单说,就是ASP+COM/DCOM技术。与JSP+BEANS完全类似,所有的presentation layer由ASP完成,所有的business layer由COM/DCOM完成。通过调用,实现整合。现在微软推出的.NET也是通过这个理念,所有的presentation layer由ASP.NET完成,business layer由C#或VB.NET或VC.NET来完成。
为什么要采用这些组件技术呢?因为单纯的ASP/JSP语言是非常低效率执行的,如果出现大量用户点击,纯SCRIPT语言很快就到达了他的功能上限,而组件技术就能大幅度提高功能上限,加快执行速度。
另外一方面,纯SCRIPT语言将presentation layer和business layer混在一起,造成修改不方便,并且代码不能重复利用。如果想修改一个地方,经常会牵涉到十几页CODE,采用组件技术就只改组件就可以了。
综上所述,SERVLET是一个早期的不完善的产品,写business layer很好,写presentation layer就很不好,并且两层混杂,显得十分混乱。
所以,推出JSP+BAEN,用JSP写presentation layer,用BAEN写business layer。SUN自己的意思也是将来用JSP替代SERVLET。
看了上面的叙述,大家可能对JSP与Servlet共存有了比较好的认识。可以看到JSP和Bean结合后的的实用性,强大的表现功能,易用性都是Servlet所不能及的。那么是不是Servlet就被取代了?不是!在以后的发展中,它还是有着巨大的作用的。上面只不过是将了问题的一方面,下面我们来看看Servlet本身的特点。
由于它是由java来写的,所以相关的特点我们就不说了,上文已经有了详细的介绍,我们来看看其他的:
Servlet是用于开发服务器端应用程序的一种编程模型,如果只是一个普通的java应用,可以不使用servlet来编写,但是如果想要提供基于web的服务能力,那么就必须按照这种模型来编写,而且servlet也必须允许在符合servlet规范的java web server or app server之上,否则无法运行。除非你自己实现一个web server,但是其复杂度是比较高的,特别是在企业级应用中,对系统的稳定性和健壮性都要求比较高,所以servlet的模型实际上是简化了编写稳健的服务器端的应用开发过程。Servlet 可以作为提供web服务能力的一个接入方式
现在也许可以理解了什么是Servlet什么是JSP,它们之间的关系是怎样的。下面我就对Servlet这个技术做一个简要的介绍。
3.Servlet概述
3.1 Servlet的结构
在Servlet API中最重要的是Servlet interface. 所有的servlets implement(执行)这个interface, 方式多种:或者是直接的,或者通过extending 这个class执行它,如 HttpServlet. 这个Servlet interface 提供安排servlet与客户端联系的方法. Servlet 编写者可以在他们开发servlet程序时提供更多一些或所有的这样方法.
当一个servlet接收来自客户端的调用请求, 它接收两个对象: 一个是ServletRequest,另外一个是ServletResponse. 这个ServletRequest class 概括从客户端到服务器之间的联系, 而 ServletResponse class 概括从servlet返回客户端的联系.
ServletRequest interface 可以获取到这样一些信息如由客户端传送的阐述名称,客户端正在使用的协议, 产生请求并且接收请求的服务器远端主机名. 它也提供获取数据流的servlet, ServletInputStream, 这些数据是客户端引用中使用HTTP POST 和 PUT 方法递交的. 一个ServletRequest的子类可以让servlet获取更多的协议特性数据. 例如: HttpServletRequest 包含获取HTTP-specific头部信息的方法.
ServletResponse interface 给出相应客户端的servlet方法. 它允许servlet设置内容长度和回应的mime类型, 并且提供输出流, ServletOutputStream, 通过编写者可以发回相应数据. ServletResponse子类可以给出更多protocol-specific容量的信息。 例如: HttpServletResponse 包含允许servlet操作HTTP-specific头部信息的方法.
上面有关classes 和 interfaces描述构成了一个基本的Servlet框架. HTTP servlets有一些附加的可以提供session-tracking capabilities的方法. servlet编写者可以用这些API在有他人操作时维护servlet与客户端之间的状态.
3.2 编写Servlet
Servlets 执行 javax.servlet.Servlet interface. servlet编写者可以通过直接implement interface开发servlet, 但这样通常没有必要. 因为大多数servlet是针对用HTTP协议的web服务器, 这样最通用开发servlet办法是用 javax.servlet.http.HttpServlet 内.HttpServlet 类通过extend GenericServlet基类执行 Servlet interface, 提供了处理HTTP协议的功能. 他的service方法支持标准HTTP/1.1请求. 一般地, 用HttpServlet指定的类编写的servlets可以多线程地并发运行service方法.
Servlet编写者注意HttpServlet类有几个欠缺的方法,你可以自己定义方法中内容,但是必须使用这些方法名称以使servlet知道你想做什么,
doGet, 用于处理 GET、有条件的GET 和头部 HEAD请求
doPost, 用户处理 POST 请求
doPut, 用于处理 PUT 请求
doDelete, 用于处理 DELETE请求
HttpServlet的service方法, 一般地, 当它接收到一个OPTIONS请求时,会调用doOptions 方法, 当接收一个TRACE请求是调用doTrace . doOptions缺省执行方式是自动决定什么样的HTTP被选择并且返回哪个信息.
在你使用这些方法时,必须带两个阐述. 第一个包含来自客户端的数据HttpServletRequest. 第二个参数包含客户端的响应HttpServletResponse. 在下例中是这样的情况.
一个HttpServletRequest对象提供到达HTTP 头部数据, 也允许你获取客户端的数据. 怎样获取这些数据取决于HTTP端请求方法.
不管任何HTTP方式, 你可以用]getParameterValues方法, 这个用来返回特定名称的参数值.
对于用 HTTP GET 请求的方式, 这个getQueryString方法将会返回一个可以用来解剖分析的。对于用HTTP POST, PUT, 和 DELETE请求的方式, 你有两种方法可以选择. 如果是文本数据,你能通过getReader方法用BufferedReader获取;如果是二进制数据, 能通过getReader 方法用 ServletInputStream获取.
为了响应客户端, 一个HttpServletResponse对象提供返回数据给用户的两个方法. 你可以用getWriter 方法返回,或者 getOutputStream 方法以输出流返回. 你应该用getWriter返回文本数据,而用getOutputStream返回二进制数据.
在使用Writer 或 OutputStream之前, HTTP 头部应该先被设置. HttpServletResponse内提供这样一个方法,之后可以用writer 或 outputstream 将响应主体部分发回用户. 完成后要关闭 writer 或 output stream以便让服务器知道响应已经完毕.
附录一
运行你的Servlet!
当一个servlet已经写好怎样来运行测试呢?我花了好长时间来研究这个,也许是因为我太笨。但其实现在想想也不是很难。我想通过一个例子详细的说说,这样会有一个感性的把握。我会把我当时遇到的主要问题用黑体字写出,那时我当时主要浪费时间的地方,希望大家也注意。(我用的运行环境是Tomcat5.0)
首先我们来写一个最简单的servlet:
package test; import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class HelloServlet extends HttpServlet{ public void doGet(HttpServletRequest request,HttpServletResponse response)throws ServletException,IOException{ response.setContentType("text/html;charset=UTF-8"); response.setCharacterEncoding("UTF-8"); PrintWriter out=response.getWriter(); out.println("<HTML>"); out.println("<BODY>"); out.println("<p>Hello!这是我的第一个Java Servlet程序。</p>"); out.println("</BODY>"); out.println("</HTML>"); } }
由于我们把它进行了打包,所以把这个编译好的.class文件放到/Tomcat文件夹/webapps/ourappfiles/WEB-INF/classes/test的文件夹下。
接着我们需要写一个调用该Servlet的html文件:
<html> <head> <title>Java Servlets Sample-Properties</title> </head> <body> <form method="get" action="test.HelloServlet"> <input name="test" type="submit" value="Test HelloServlet servlet"> </body> </html>
注意:这里的method不能用post,不然不会在IE中正常显示,我当初就是在这个地方没有弄好,浪费了好多时间。至于为什么这样写,我也弄不太清楚,由于水平有限,多多包涵。
现在我们还差最后一步,编写我们的web.xml文件。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd";> <web-app> <servlet> <servlet-name>helloservlet</servlet-name> <servlet-class>test.HelloServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>helloservlet</servlet-name> <url-pattern>/test.HelloServlet</url-pattern> </servlet-mapping> </web-app>
把编写好的web.xml文件放到/Tomcat文件夹/webapps/ourappfiles/WEB-INF下。
好了,现在万事俱备,就差启动Tomcat运行我们的Servlet了。
以上就是运行Servlet的几个步骤。
附录二
这个附录纯粹是另一个读书笔记,我感觉比较好,所以摘录下来。里面写的是经验的结晶,我还没有这么多的经验,所以只有摘抄别人的以作补充了。
1.ServletConfig
l 一个ServletConfig对象是servlet container在servlet initialization的时候传递给servlet的。
ServletConfig包涵 ServletContext 和 一些 Name/Value pair (来自于deployment descriptor)
l ServletContext接口封装了Web应用程序的上下文概念。
2.会话跟踪
1) Session
l 当一个Client请求多个Servlets时,一个session可以被多个servlet共享。
l 通常情况下,如果server detect到browser支持cookie,那么URL就不会重写。
2) cookie
l 在Java Servlet中,如果你光 Cookie cookie = new Cookie(name,value)
那么当用户退出Browser时,cookie会被删除掉,而不会被存储在客户端的硬盘上。
如果要存储 cookie,需加一句 cookie.setMaxAge(200)
l cookie是跟某一个server相关的,运行在同一个server上的servlet共享一个cookie.
3) URL Rewriting
在使用URL Rewriting来维护Session ID的时候,每一次HTTP请求都需要EncodeURL()
典型的用在两个地方
1) out.print(“form action=/” ”);
out.print(response.encodeURL(“sessionExample”));
out.print(“form action=/” ”);
out.print(“method = GET>”);
2) out.print(“<p><a href=/” ”);
out.print(response.encodeURL(“SessionExample?database=foo&datavalue=bar”));
out.println(“/” >URL encoded </a>”);
3.SingleThreadModel
默认的,每一个servlet definition in a container只有一个servlet class的实例。
只有实现了SingleThreadModel,container才会让servlet有多个实例。
Servlet specification上建议,不要使用synchronized,而使用SingleThreadModel。
SingleThreadModel(没有方法)
保证servlet在同一时刻只处理一个客户的请求。
SingleThreadModel是耗费资源的,特别是当有大量的请求发送给Servlet时,SingleThreadModel的作用是使包容器以同步时钟的方式调用service方法。
这等同于在servlet的service()方法种使用synchronized.
Single Thread Model一般使用在需要响应一个heavy request的时候,比如是一个需要和数据库打交道的连接。
2. 在重载Servlet地init( )方法后,一定要记得调用super.init( );
3. the client通过发送一个blank line表示它已经结束request
而the server通过关闭the socket来表示response已结束了。
4. 一个Http Servlet可以送三种东西给Client
1) a single status code
2) any number of http headers
3) a response body
5. Servlet之间信息共享的一个最简单的方法就是
System.getProperties().put(“key”,”value”);
6. Post和Get
Post:将form内各字段名称和内容放置在html header内传送给server
Get: ?之后的查询字符串要使用URLEncode,经过URLEncode后,这个字符串不再带有空格,以后将在server上恢复所带有的空格。
Get是Web上最经常使用的一种请求方法,每个超链接都使用这种方法。
7. Web.xml就是Web Applicatin 的deployment descriptor
作用有:组织各类元素
设置init param
设置安全性
8. Request Dispatcher用来把接收到的request forward processing到另一个servlet
要在一个response里包含另一个servlet的output时,也要用到Request Dispatcher.
9. Servlet和Jsp在同一个JVM中,可以通过ServeltContext的
setAttribute( )
getAttribute( )
removeAttribute( )
来共享对象
10. 利用request.getParameter( )得到的String存在字符集问题。
可以用 strTitle = request.getParameter(“title”);
strTitle = new String(strTitle.getBytes(“8859-1”),”gb2312”);
如果你希望得到更大得兼容性
String encoding = response.getCharacterEncoding(); //确定Application server用什么编码来读取输入的。
strTitle = new String(strTitle.getBytes(encoding),”gb2312”);
相关推荐
JSP和Servlet之间的主要区别在于它们的角色和使用方式。JSP主要用于展示视图,它的优势在于将HTML和Java代码分离,使得页面设计更为直观。而Servlet则专注于业务逻辑和数据处理。尽管JSP看起来更像HTML,但其本质...
在深入探讨JSP与Servlet的联系与区别的过程中,我们首先需要理解这两个概念的基本定义以及它们在Web开发中的角色。 #### JSP(JavaServer Pages) JSP是一种基于Java技术的动态网页技术,它允许在标准的HTML、XML...
**JSP(Java Server Pages)与Servlet是Java Web开发中的两个核心技术,它们在构建动态Web应用程序方面发挥着关键作用。本课件旨在帮助学习者深入理解这两者的基础知识及其交互方式,以提升Web开发能力。** **JSP...
3. **Servlet编译**:服务器对生成的Servlet源代码进行编译,生成.class文件。 4. **Servlet实例化**:服务器创建Servlet的实例,并调用其init()方法进行初始化。 5. **服务请求**:服务器调用Servlet的service()...
通过上述知识点的介绍,我们可以看出《JSP与Servlet程序设计实践教程》主要围绕着JSP和Servlet这两种核心技术展开,不仅覆盖了基本概念和技术要点,还深入探讨了它们在实际项目中的应用技巧及注意事项。这对于学习...
**JSP与Servlet之间的主要区别**: 1. **编写方式**:JSP页面使用HTML作为基础模板,并嵌入Java代码片段,而Servlet则是纯Java类,需要手动编写处理HTTP请求的方法。 2. **适用场景**:JSP更适合用于展示数据和构建...
**JSP(Java Server Pages)与Servlet是Java Web开发中的两个核心技术,它们在构建动态、交互式的Web应用程序中起着核心作用。** **1. JSP简介:** JSP是Java平台上的一个标准视图技术,它允许开发人员将HTML或者...
当客户端发起对JSP页面的请求时,Web服务器会检查该JSP是否已经编译为Servlet。如果未编译,服务器会解析JSP内容生成Servlet源码,然后编译为字节码,并执行。Servlet处理请求,生成响应内容,再以HTML格式返回给...
### servlet与jsp的区别 在Web开发领域中,servlet与JSP是两种非常重要的技术,它们都是Java EE标准的一部分,并且被广泛应用于构建动态网页。虽然它们都可以用来处理客户端请求并生成动态页面,但它们之间还是存在...
【标题】"jsp+servlet项目"涉及的是Java服务器页面(JSP)与Servlet技术的结合应用,这是一种在Web开发中常见的技术组合。JSP和Servlet是Java EE平台上的两个核心组件,它们主要用于构建动态Web应用程序。 【JSP...
**JSP(Java Server Pages)与Servlet是Java Web开发中的两个核心技术,它们在构建动态网页和处理客户端请求方面起着至关重要的作用。** **JSP技术:** JSP是一种服务器端脚本语言,用于创建动态网页。它允许开发者...
同时,本章节还介绍了JSP的生命周期、JSP的编译和执行过程等。 在第二章中,本笔记讨论了Servlet的基本概念,包括Servlet的定义、Servlet的生命周期、Servlet的请求和响应等。此外,本章节还介绍了Servlet的多线程...
本文将深入探讨JSP和Servlet中的几个关键编码设置的作用及原理,帮助开发者更好地理解和解决实际开发中遇到的编码问题。 #### 二、主要编码设置及其作用 ##### 1. `pageEncoding="UTF-8"` - **作用**:`...
JSP最终会被编译成Servlet,因此理解Servlet的工作原理对于深入JSP是必要的。 Servlet是Java编写的一种服务器端程序,负责处理客户端请求并生成响应。Servlet生命周期包括加载、初始化、服务、销毁四个阶段。通过...
### Servlet与JSP与Filter的使用详解 #### 一、Servlet与JSP的基本概念 Servlet是一种Java技术,用于创建动态Web应用程序。它本质上是一个Java类,由Web服务器或应用服务器加载,以响应客户端请求。Servlet可以...
下面我们将详细探讨JSP与Servlet的关系,以及如何将JSP代码转换为Servlet。 1. JSP简介: JSP是一种基于Java的服务器端技术,它允许开发者在HTML页面中嵌入Java代码,以实现动态网页。JSP页面在服务器上被编译成...
开发者需要将编译后的JSP、Servlet类文件及相关的配置文件放入服务器的特定目录下,然后通过浏览器访问应用。 9. **错误处理与日志记录**: 为了确保系统的健壮性,项目可能包含错误处理和日志记录机制。错误处理...
生成的Java文件通常位于WEB-INF/classes目录下,其类名基于JSP文件的URL路径,并带有_JSPServlet后缀。 例如,一个名为"index.jsp"的文件会被转化为"org_yourdomain_yourapp_index_jsp.java"。这个Java类继承自...
【JSP(Java Server Pages)与Servlet概述】 JSP(Java Server Pages)是Java平台上的一个服务器端技术,主要用于创建动态网页。它允许开发者将HTML、XML或者其他标记语言与Java代码混合在一起,使得网页能够根据...
在本项目"经典的jsp+servlet项目"中,我们将探讨如何使用这两种技术来开发一个电子商务网站。 1. **JSP(JavaServer Pages)**:JSP是一种服务器端脚本语言,用于创建动态网页。在电子商务网站中,JSP主要用于展示...