//2006-01-05 23:28
这篇文章我打算总结一下我认为J2EE中比较重要的东西.只希望起到抛砖引玉的作用,详细的细节有很多书可以看,此处因时间有限,只点到为止.
在我开始学习J2EE基础的时候,最开始的时候是不明白Servlet到底该是怎么配置的,为什么会有这样的配置格式.当我在寒假中第一次把我配制成功的Servlet运行起来的时候,心里别提多开心了,还认真的把配置的例子抄在了一个本上牢牢记住.不过后来理解了配置为什么要这样存在的含义以后,就不在刻意去记它了,而是融合它.因为如果你想如果你要是来设计这个东西,最好的方法之一就是这样的.
本文假设读者有J2SE的基础;会简单运行Web服务器,如resin或者tomcat;简单了解HTTP协议;简单了解WEB程序的文件结构.
OK,让我们一起来路过这奇妙神秘的J2EE大森林中的一条基石小径.
(1)
一般的Web程序的文件结构是有一个webRoot之类的根目录,webRoot目录下面有一个/WEB-INF的文件夹,此文件夹通过直接的键入URL是无法访问的,所以常把一些很多不想让外界访问的文件放入此中,并在web.xml中设置相应的映射才能进行运行.
/WEB-INF文件夹中拥有一个此Web程序的全局配置文件,名称为'web.xml'!这个文件非常重要,开发人员一定要了解其中常用的配置项的设置,它是Web应用程序的配置描述文件. web.xml is Web application deployment descriptor.
/WEB-INF文件夹中还有一个/classes的目录,它用来存放编译过的Servlet和其他的class文件(:Servlet and other class files),这些类在web.xml中配置完毕后,应用服务器会在此classes的文件夹下查找并运行.
/WEB-INF文件夹中还有一个/lib目录,此目录一般存放应用程序会用到的第三方jar文件. :Other jar files used by this application.
(2)
介绍完毕Web应用程序的最主要结构以后,下面介绍servlet,它是Web程序中很重要很底层的部分,很多其他技术都是对它进行包装和扩充定制的.
首先应该明白怎么配置和运行你的第一个Servlet,只介绍干的:
在web.xml文件中配置:
一般一个
web.xml中还有一些需要知道的配置项,我们等下再说.
(3)
下面我们简单的说一下Servlet的生命周期:
每个Servlet在WEB应用程序中只保持一份实例.在初始化或者第一次访问时只初始化一次,所以注意如果不是故意的,不要设置全局变量以免程序出错.
我以前一直疑惑的问题是如果一个类只在内存中保持一份实例,那么当很多人一起使用那块内存区域时它们之间的数据怎么会重复呢(比如说struts的Action只保持全局一个)?我想了很久到不久前才恍然大悟,不会出错是因为它们把自己所需要的参数值通过方法的参数传入的,这些参数放在堆栈中,每个线程都不同,所以它们占用了不同的内存,虽然同一个Action使用了相同的动作,但是它们处理的数据是不同的,执行相同的操作,只是把要执行的语句从唯一的那份内存区域读到CPU当中,在用自己的数据区域的内容进行加减乘除与或非的操作,互不干扰.这样就清楚多了,所以通过方法参数的使用是OK的,不能定义全局变量,否则就会出现冲突了,因为这个全局变量的内存在那块唯一一个实例的类的内存区域部分,进行了改变,其他人存取这块共享内存的时候,就会看到了.
Servlet生命周期: 容器(如resin)初始化Servlet时调用其init()方法,然后当用户请求时调用service()方法,最后Web容器需要终止Servlet(如要关闭Web服务器)时,容器会调用servlet的destory()方法是servlet停止服务并被清除掉.
//2007-01-06 00:55 ShiL 睡觉了先,有空在写.
//2007-01-06 23:53
(4)
Servlet的有很多基本类和接口, javax.servlet包和javax.servlet.http包的类和接口主要分以下几类,具体细节请查阅相关书籍:
javax.servlet.Servlet; javax.servlet.http.HttpServlet; javax.servlet.SingleThreadModel; javax.servlet.GenericServlet.
Servlet与容器(容器我理解的意思是:{web容器如resin里面的Web程序运行时所存储的属性,配置文件等相关其他资源的整合体})相联系的类有两个比较重要的,一个是ServletConfig;另一个是ServletContext.它们的区别是:
ServletConfig主要是servlet初始化时和容器进行联系,以为有一个方法为init(ServletConfig sConfig)可以在初始化时进行对容器的访问.
而ServletContext一般是在Web程序运行时,通过这个界面进行共享数据,一般用 getServletContext()方法得到.
Servlet有时会需要和其他Web资源共同作用: javax.servlet.http.RequestDispatcher.
作用分三种:
保持request和response的两种方法:(reqeust/servletContext).getRequestDispatcher("targetUrl").include(rquest,response)和(reqeust/servletContext).getRequestDispatcher("targetUrl").forward(request,response);这两种方法不会使URL进行改变.
丢失request和response的一种方法:response.sendRedirect("targetUrl");此方法会改变URL的显示.
Servlet有很多东西,关于它我们暂时介绍这些,后面的慢慢来.
(5)
其他关于Web方面比较常用和重要的类还有:
javax.servlet.http.HttpServletRequest:http请求类,包含了很多页面传入的信息,很常用,可以从中用getParameter(name)方法得到页面相应传入的Form的name值.一般还会用过滤器在此处加入编码方式.还可以用setAttribute(String,Object)来在request中保存一些属性值.用getAttribute(String)来得到所保存的值,默认的返回类型为String,需要时请强制转换成你所开始保存的值.
javax.servlet.http.HttpServletResponse:http响应类,可以在此类中设置写回到客户端所用的编码方式,Cookie值.
javax.servlet.http.HttpSession:http会话类,很重要的类,用来分辨和保持用户所需要在全局访问的信息,也用setAttribute()和getAttribute()来进行存取.不同的连接是通过session的Id来标示的,用sessionId来保证不同的用户有自己的空间,所有的session中保存的值都是保存在Web容器中,通过sessionId来判断是哪个用户,并取得其相应的信息,它一直都在服务器的内存里,没有跟着什么乱转递.因为我以前总以为它里面我所保存的所有attribute数据每次都是从服务器传递到我的IE浏览器,然后我用IE浏览器提交时又传回服务器去,多累啊,其实它就一直在Web服务器上,等着我使用,传递给我的只是一个简单的字符串表示符JsessionId,我怀疑它在服务器的实现类似于一个HashMap来保存和分辨众人的信息.
Web容器可以通过三种途径实现会话:(1)cookie机制;(2)URL重写;(3)隐藏表单输入.
session中一般保存了:识别码(ID),创建时间,最近使用时间等.它在一定时间发现用户没有任何操作就会自动失效,就是会话实效,会话超时了的意思.
这个一定时间是可以进行配置的:使用session.setMaxInactiveInterval(int second)来进行设置.如果为每个Servlet设置超时时间太费事,可以在web.xml进行全局的设置,如下:
web.xml
关于session注销时
//2007-01-07 01:11 ShiL 睡觉先
//2007-01-07 11:39
(6)
web.xml里面还有一个很重要的配置标签:
这个标签可以定义对应的所有组件(Servlet和JSP等)都可以使用的上下文.通过getservletContext().getInitParameter(String name)的方法得到.
(7)
Filter:过滤器
过滤器可以在Web请求到达servlet(或JSP)之前和在Servlet返回相应之后对两者进行操作.它的主要功能包括:(1)对Web请求进行分析,对输入数据进行预处理;(2)阻止请求和相应的进行;(3)根据功能改动请求的头信息和数据体;(4)根据功能改动响应的头信息和数据体;(5)和其他Web资源协作.
通常过滤器可以用在多种情况下,比如:安全保护,运行纪录,图像转化,数据压缩,加密解码以及XML转化等.
它的使用方法是继承Filter接口,并实现它的三个方法:init(FilterConfig fConfig);doFilter(ServletRequest,ServletResponse,FilterChain);destroy();
实现完毕Filter接口以后,就是要在web.xml中对其进行配置了,它的配置结构和servlet类似,如下:
过滤器大致就说这么多,它在现实使用中,一个最常用的功能就是给request进行编码.同时你还可以根据需要实现很多其他的功能,过滤器可以互相传递通过doFilter最后一个FilterChain,具体流程你可以找相关的图片,一看即明,GOF的过滤器设计模式的一个典型代表.
还有一点注意的是:如果一个过滤器在程序中只能用在一个servlet或JSP上而不能被其他Web程序借用,那么这个过滤器的存在就没有太大意义.
(8)
Listener:监听器
Servlet的事件监听器接口能够处理ServletContext,HttpSession和ServletRequest的生命周期和属性变化事件.它们的对应接口如下:
ServletContext事件:
生命周期: (Web程序即ServletContext刚被创建并能够处理Web请求,或者Web程序即将被关闭) javax.servlet.ServletContextListener.
属性变化: (ServletContext的属性即Attribute被添加,删除或替换) javax.servlet.ServletContextAttributeListener.
HttpSession事件:
生命周期: (HttpSession被创建,作废或过期) javax.servlet.http.HttpSessionListener.
属性变化: (HttpSession的属性即Attribute被添加,删除或替换) javax.servlet.http.HttpSessionAttributeListener.
ServletRequest事件(Servlet2.4版以后):
生命周期: (Web构件开始处理ServletRequest) javax.servlet.ServletRequestListener.
属性变化: (ServletRequest的属性被添加,删除或替换) javax.servlet.ServletRequestAttributeListener.
这样说好像不太明白他们到底能干些什么,举个例子:属性变化的那些接口,每当有setAttribute(),removeAttribute()这样的方法被调用的时候,都会触发对应的事件,实现那些接口的类就可以检测到并根据需要进行一些相应的处理.
一个很有用的接口就是ServletContextListener.它负责监听Web程序(即ServletContext)被开始启动和关闭的动作,实现这个接口后,有两个方法:contextInitialized(ServletContextEvent sce);contextDestoryed(ServletContextEvent sce).你可以在初始化时进行一些全局都会用到的数据初始化,并在关闭时清除掉,比如说可以在Web程序初始化时在内存中创建一个数据库连接池的信息缓存,从而可以对第一个数据请求也能提供快速访问.对此类的资源初始化工作,应用生命周期监听器可谓一个绝佳的工具.
监听器的功能大致就说到这里,那么怎么让容器知道它呢,其实很简单,只需要在web.xml中直接配置以下就可以了,容器加载时发现有监听器的配置项会自动进行加载执行的.格式如下:
that's it.
(9)
JSP(JavaServer Page)
JSP技术是servlet技术的延伸,是一种表现层技术,它提供一种比较自然的生成网页的方法.其实JSP在运行时先被编译成Servlet,然后再进行运行.
我认为JSP本质上还是Servlet,所以它拥有Servlet的所有功能,并且包含其所有需要应用的对象,在JSP中叫做隐含对象:
(a)application,即ServeltContext对象. 属于application范围.
(b)session,即javax.servlet.http.HttpSession对象. 属于session范围.
(c)request,即javax.servlet.http.HttpServletRequest对象. 属于page范围.
(d)response,即javax.servlet.http.HttpServletResponse对象. JSP编写者一般不直接使用. 属于page范围.
(e)config,即javax.servlet.ServletConfig对象. 属于page范围.
(f)page,即javax.servlet.jsp.HttpJspPage对象. 对应JSP生成的servlet. 属于page范围.
(g)pageContext,即javax.servlet.jsp.PageContext对象. 用于提供共享page信息的API. 属于page范围.
(h)exception,即java.lang.Throwable对象. 属于page范围.
JSP中最好不要掺入业务逻辑,所有的处理都应该在转入JSP前的那个Servlet处理好,存入Attribute中,在JSP中只是取出来显示给用户就可以了.
JSP中能使用标签库最好,用JSTL或者第三方标签库或自己编写的标签库,养成良好的习惯.
(10)
标签库
JSP中提供了很多标准的标签库(如JSTL)而且也存在很多第三方库,很多Web框架自身也提供了其标签库,用户也可以自定义标签库.
如果自己要实现自定义标签库,则有很多的接口可以供开发者的不同用途实现,实现这些接口不复杂,但是你最好要先弄清它们的流程,我记得以前有一本P2P(Programmer to Programmer)系列的红皮书,书名好像叫<(自定义)标签库开发指南>之类的,很详细的讲解了标签库的开发和流程图.这本书的书皮是一个很年轻的人的脸,带着眼镜呲着牙对你笑,我上大学时在图书馆里看这本书的时候,就常想他怎么年轻就这么厉害了,可真厉害,又看看我自己和他也差不多阿,什么时候才能混出个样子来啊,就有些黯然失色起来.:) 标签库可以很强大.
自定义标签库后要编写TLD,就是Tag Library Descriptor,标签库描述文件,JSP1.2和JSP2.0的TLD有一些不一样,主要原因是JSP2.0使用了XML模式来完成元素的声明(和验证),而JSP1.2则使用了DTD(Document Type Definition)来达到同样目的.
编写好了自定义的标签库也配置好了TLD以后,你需要在web.xml中进行对它的uri引用配置.在JSP1.2中引入了一个新的自动发现机制,从而使这个工作相当简单,但我们暂时说一下曾经引起过我疑惑的问题.
大约在J2EE1.3的时候,TLD还是需要在web.xml中配置的,格式如下:
可是有一天这样的配置在resin3(J2EE1.4的服务器)上启动时,报了web.xml配置错误异常,我找了半天原因发现原来在J2EE1.4的时候,web.xml中的
(11)
EJB*(Enterprise JavaBeans)
有人说:如果讲J2EE没有讲EJB,那就不叫做J2EE了.不过不好意思,我的水平还暂时不能写出来这些的,熬过了饱受争议的EJB2.x,现在EJB3.0也出来.也许下次我会单独把EJB写成一篇文章,不过这种想法还没列到我的任务队列中,也许还要等很长很长的一段时间.
没有讲EJB就少了很大一块,所以干脆消息,事务,安全,国际化,本地化都不说了,打字太费事. :P (在以后的
(12)
web.xml中还有几项比较重要的配置项,如下:
//在容器接受到一个标示目录(而不是一个Web应用页面或Servlet)的请求URI时,即会提供此欢迎文件.
//此元素可以定义向用户告知各种错误的页面.可以根据错误码来定向也可以根据异常类型(Exception Type)来定义页面.
(I)根据错误码:
(II)根据异常类型:
结束语:
在J2EE的书籍里,有一本书无论走到哪里我都会记得把它带在身边,因为这本书写的非常棒,全面且通俗易懂,本篇文章的大部分知识我都是从这本书中学习而来的,这本书就是
...THE END...
//2007-01-07 16:28 finished by ShiL. including have lunch.
参考书目:
<设计模式-可复用面向对象软件的基础> [Gang Of Four 著] 机械工业出版社.
<(自定义)标签库开发指南> ??? ??? P2P系列 (好像该公司(wrox?)后来倒闭被人收购了)