心的方向

新的征途......
posts - 75,comments - 19,trackbacks - 0

转自:http://dev.csdn.net/article/68/68297.shtm
hibernate 三种查询方式     选择自 wangyihust 的 Blog 
 

(一)HQL

HQLHibernate Qusery Language,如果你已经熟悉它,就会发现它跟SQL非常相像。不过你不要被表面的假象迷惑,HQL是面向对象的(OO,用生命的眼光看待每一个对象,他们是如此鲜活)。如果你对JAVASQL语句有一定了解的话,那么HQL对你简直易如反掌,你完全可以利用在公车上的时间掌握它。

以下从几个方面进行慢慢深入:

1
。大小些敏感
大家知道SQL-92 Query是对大小写不敏感的,但是在HQL(前面提到它是OO的)中对对象类的名称和属性确实大小写敏感的(符合java编程语法)。

HQL 子句本身大小写无关,但是其中出现的类名和属性名必须注意大小写区分
如:sElect cat.name from Cat as catselect cat.name from Cat as cat是一样的
但是:
sElect
cat.name from CAT as catselect cat.name from Cat as cat确实不一样的。

2
from语句
最简单的:
from eg.Cat
它只是简单的返回所有eg.Cat的实例,通常我们此时会为eg.Cat其个别名,因为在query的其余部分可能会用到(参看上边关于大小写敏感时的例子情形),如:
from eg.Cat as cat 这里as可以省略。


上边只是单表查询,多表的情况如下写法:
from eg.Cat, eg.Dog
from eg.Cat as cat, eg.Dog as dog

3
join相关
(inner) join
left (outer) join
right (outer) join
full join
HQL
同样对SQL中的这些特性支持
下面插播一个小话题,关于上边的那些特性,我一直都没怎么用,今天既然说到这里,就想把上边的几个特性的用法说一下,也算对自己的一个补充:


假设有两个表:部门、员工,下面列举一些数据:
员工(Employee)
 ID     Name    DepNo
 001   Jplateau   01
 002    Jony        01
 003   Camel      02

部门(Department)
 ID  Name
 01  
研发部
 02   
营销部

Hibernate中我们操纵的都是对象,所以我们操纵的是部门类和员工


1).(inner) join
select employee.ID as id1,employee.Name as name1,

department.ID as id2,department.Name as name2  from Employee as employee

 join  Department as department on employee.DepNo=department.ID (注意到条件语句我用on 没有用where)
那么执行结果是什么呢?
id1 name1 id2 name2
++++++++++++++++++++++++++++++++++++++
001 Jplateau 01
研发部
002 Jony 01
研发部

2).left (outer) join
select employee.ID as id1,employee.Name as name1,department.ID as id2,department.Name
as name2 from Employee as employee left join Department as department on employee.DepNo=
department.ID
那么执行结果又该是什么呢?
id1 name1 id2 name2
++++++++++++++++++++++++++++++++++++++
001 Jplateau 01
研发部
002 Jony 01
研发部
003 Camel null null
{
就是说此时我要已第一个表的记录多少为准,第二个表中没有相应纪录的时候填充null}
3). right (outer) join
select employee.ID as id1,employee.Name as name1,department.ID as id2,department.Name
as name2 from Employee as employee right join Department as department on employee.DepNo=
department.ID
那么执行结果又该是什么呢?
id1 name1 id2 name2
++++++++++++++++++++++++++++++++++++++
001 Jplateau 01
研发部
002 Jony 01
研发部
null null 02
营销部
{
就是说此时我要已第二个表的记录多少为准,第一个表中没有相应纪录的时候填充null}

4
select语句
就是要确定你要从查询中返回哪些对象或者哪些对象的属性。写几个例子吧:
select employee form Employee as employee
select employee form Employee as employee where employee.Name like 'J%'
select employee.Name form Employee as employee where employee.Name like 'J%'
select employee.ID as id1,employee.Name as name1,department.ID as id2,department.Name
as name2 from Employee as employee right join Department as department on employee.DepNo=
department.ID

select
elements(employee.Name) from Employee as employee
(不明白elements到底是做什么用的?望给于说明)
等等


5。数学函数
JDO
目前好像还不支持此类特性。
avg(...), sum(...), min(...), max(...)

count(*)

count(...), count(distinct ...), count(all...)

其用法和SQL基本相同

select distinct employee.name from Employee as employee
select count(distinct employee.name),count(employee) from Employee as employee

6
polymorphism (暂时不知道如何解释?)
from com.test.Animal as animal
不光得到所有Animal得实例,而且可以得到所有Animal的子类(如果我们定义了一个子类Cat
一个比较极端的例子
from java.lang.Object as o
可以得到所有持久类的实例

7
where语句
定义查询语句的条件,举几个例子吧:
from Employee as employee where employee.Name='Jplateau'
from Employee as employee where employee.Name like 'J%'
from Employee as employee where employee.Name like '%u'
where语句中“=”不光可以比较对象的属性,也可以比较对象,如:
select animal from com.test.Animal as animal where animal.name=dog

8
。表达式

SQL语句中大部分的表达式在HQL中都可以使用:
mathematical operators +, -, *, /

binary comparison operators =, >=, <=, <>, !=, like

logical operations and, or, not

string concatenation ||

SQL scalar functions like upper() and lower()

Parentheses ( ) indicate grouping

in, between, is null

JDBC IN parameters ?

named parameters :name, :start_date, :x1
(这种应该是另一种"?"的变通解决方法)

SQL literals 'foo', 69, '1970-01-01 10:00:01.0'

Java public static final constants eg.Color.TABBY

其他不必解释了,在这里我只想对查询中的参数问题说明一下:
大家知道在SQL中进行传递参数进行查询的时候,我们通常用PreparedStatement,在语句中写一大堆的“?”,
hql中也可以用这种方法,如:
List mates = sess.find(
"select employee.name from Employee as employee " +
"where employee.Name=? ",
name,
Hibernate.STRING
);
(
说明:上面利用Session里的find方法,在hibernateapi Session中重载了很多find方法,它可以满足你多种形式的查询)
上边是一个参数的情形,这种情况下紧接着引入参数和定义参数的类型,当为多个参数,调用另一个find方法,它的后两个
参数都是数组的形式。

还有另外一种方法来解决上边的问题,JDO也有这样的方法,不过和hibernate的表现形式上有差别,但他们两个骨子里却是
一样的,如:
Query q = sess.createQuery("select employee.name from Employee as employee where employee.Name=:name");
q.setString("name", "Jplateau");
//
当有多个参数的时候在此逐一定义
Iterator employees = q.iterate();

9
order 语句
sql语句没什么差别,如:
select employee.name from Employee as employee where employee.Name like 'J%' order by employee.ID desc (
或者asc)

10
group by 语句
同样和sql语句没什么差别,如:

select employee.name,employee.DepNo from Employee as employee group by employee.DepNo

select foo.id, avg( elements(foo.names) ), max( indices(foo.names) ) from eg.Foo foo group by foo.id
{Note: You may use the elements and indices constructs inside a select clause, even on databases with no subselects.}
谁帮我解释一下上边两句,谢过!

11
。子查询
hibernate
同样支持子查询,写几个例子:

from eg.Cat as fatcat where fatcat.weight > ( select avg(cat.weight) from eg.DomesticCat cat )

(二)条件查询Criteria  Query

。数学函数
JDO
目前好像还不支持此类特性。
avg(...), sum(...), min(...), max(...)

count(*)

count(...), count(distinct ...), count(all...)

其用法和SQL基本相同

select distinct employee.name from Employee as employee
select count(distinct employee.name),count(employee) from Employee as employee

6
polymorphism (暂时不知道如何解释?)
from com.test.Animal as animal
不光得到所有Animal得实例,而且可以得到所有Animal的子类(如果我们定义了一个子类Cat
一个比较极端的例子
from java.lang.Object as o
可以得到所有持久类的实例

7
where语句
定义查询语句的条件,举几个例子吧:
from Employee as employee where employee.Name='Jplateau'
from Employee as employee where employee.Name like 'J%'
from Employee as employee where employee.Name like '%u'
where语句中“=”不光可以比较对象的属性,也可以比较对象,如:
select animal from com.test.Animal as animal where animal.name=dog

8
。表达式

SQL语句中大部分的表达式在HQL中都可以使用:
mathematical operators +, -, *, /

binary comparison operators =, >=, <=, <>, !=, like

logical operations and, or, not

string concatenation ||

SQL scalar functions like upper() and lower()

Parentheses ( ) indicate grouping

in, between, is null

JDBC IN parameters ?

named parameters :name, :start_date, :x1
(这种应该是另一种"?"的变通解决方法)

SQL literals 'foo', 69, '1970-01-01 10:00:01.0'

Java public static final constants eg.Color.TABBY

其他不必解释了,在这里我只想对查询中的参数问题说明一下:
大家知道在SQL中进行传递参数进行查询的时候,我们通常用PreparedStatement,在语句中写一大堆的“?”,
hql中也可以用这种方法,如:
List mates = sess.find(
"select employee.name from Employee as employee " +
"where employee.Name=? ",
name,
Hibernate.STRING
);
(
说明:上面利用Session里的find方法,在hibernateapi Session中重载了很多find方法,它可以满足你多种形式的查询)
上边是一个参数的情形,这种情况下紧接着引入参数和定义参数的类型,当为多个参数,调用另一个find方法,它的后两个
参数都是数组的形式。

还有另外一种方法来解决上边的问题,JDO也有这样的方法,不过和hibernate的表现形式上有差别,但他们两个骨子里却是
一样的,如:
Query q = sess.createQuery("select employee.name from Employee as employee where employee.Name=:name");
q.setString("name", "Jplateau");
//
当有多个参数的时候在此逐一定义
Iterator employees = q.iterate();

9
order 语句
sql语句没什么差别,如:
select employee.name from Employee as employee where employee.Name like 'J%' order by employee.ID desc (
或者asc)

10
group by 语句
同样和sql语句没什么差别,如:

select employee.name,employee.DepNo from Employee as employee group by employee.DepNo

select foo.id, avg( elements(foo.names) ), max( indices(foo.names) ) from eg.Foo foo group by foo.id
{Note: You may use the elements and indices constructs inside a select clause, even on databases with no subselects.}
谁帮我解释一下上边两句,谢过!

11
。子查询
hibernate
同样支持子查询,写几个例子:

from eg.Cat as fatcat where fatcat.weight > ( select avg(cat.weight) from eg.DomesticCat cat )

(二)条件查询Criteria  Query

 Criteria criteria = osession.createCriteria(Owner.class);
   criteria.add(Expression.eq("age", new Integer(100)));
   criteria.setFirstResult(2);                   //从返回结果的第二条记录开始的5条记录
   criteria.setMaxResults(5);
   List lc=criteria.list();
   System.out.println("条件查询");
   System.out.println(lc.size());

(三)原生SQL语句查询

posted @ 2008-04-10 17:41 阿伟 阅读(273) | 评论 (0)编辑 收藏

Spring与Struts如何整合

转自:http://lihaiyan.javaeye.com/blog/127812
为了在Struts中加载Spring context,需要在struts-config.xml文件中加入如下部分:
<struts-config>
  <plug-in
         className="org.springframework.web.struts.ContextLoaderPlugIn">
     <set-property property="contextConfigLocation"
         value="/WEB-INF/applicationContext.xml" />
  </plug-in>
</struts-config>
第一种方法:
    通过Struts的plug-in在Struts和Spring之间提供了良好的结合点。通过plug-in我们实现了Spring context的加载,不过仅仅加载Spring context并没有什么实际的意义,还应该经过配置将Struts的Action交给Spring容器进行管理。
<action-mappings>
  <action path="/login"
             type="org.springframework.web.struts.DelegatingActionProxy"
             name="loginForm">
      <forward name="success" path="/main.jsp" />
      <forward name="failure" path="/login.jsp" />
</action>
    在form bean这个节点上与传统的Struts配置没有什么区别,而在Action上面则发生了变化。在传统的action节点上type属性写入action类的完整类名,而和Spring结合后在这点上是使用了Spring提供的DelegatingActionProxy
作为action的type属性,DelegatingActionProxy同样是org.apache.struts.action.Action的一个子类,它将把调用请求转交给真正的Action实现。通过这样的方式,Spring获得了Action实例的管理权,它将对Action进行调度,并为Struts提供所需的Action实例。这样,就可以将Action看作是Spring的一个bean,它就可以享受Spring的所有服务,如依赖注入、实例管理、事务管理等。
    在applicationContext.xml中相应的配置如下的节点:
<beans>
.......
    <bean name="/login" class="net.xiaxin.action.LoginAction"
                           singleton="false">
        <property name="userDAO">
           <ref bean="userDAOProxy" />
        </property>
    </bean>
</beans>
    最后这个bean的配置是关键,这个名为“/login”的bean与Struts中的
<action path="/login" ……>
……
</action>
节点相对应,这样,Spring Bean Name与Struts Action Path相关联,当Struts加载对应的Action时,DelegatingActionProxy就根据传入的path属性,在Spring Context寻找对应bean,并将其实例返回给Struts。与此同时,还可以看到,"/login" bean 中包含了一个userDAO 引用,Spring 在运行期将根据配置为其提供userDAO 实例,以及围绕userDAO 的事务管理服务。这样一来,对于Struts 开发而言,我们既可以延续Struts 的开发流程,也可以享受Spring 提供的事务管
理服务。而bean 的另外一个属性singleton="false",指明了Action 的实例获取方式为每次重新创建。这也解决了Struts中令人诟病的线程安全问题。
第二种方法:

为了在 struts-config.xml 文件中配置 DelegatingRequestProcessor,你需要重载 <controller> 元素的 “processorClass” 属性。 下面的几行应该放在 <action-mapping> 元素的后面。

<controller>
<set-property property="processorClass"
value="http://www.zhmy.com/org.springframework.web.struts.DelegatingRequestProcessor"/>
</controller>

增加这些设置之后,不管你查询任何类型的 Action,Sping都自动在它的context配置文件中寻找。 实际上,你甚至不需要指定类型。下面两个代码片断都可以工作:

<action path="/user" type="com.whatever.struts.UserAction"/>
<action path="/user"/>

如果你使用 Struts 的 modules 特性,你的 bean 命名必须含有 module 的前缀。 举个例子,如果一个 Action 的定义为 <action path="/user"/>,而且它的 module 前缀为“admin”, 那么它应该对应名为 <bean name="/admin/user"/> 的 bean

   
如果你在 Struts 应用中使用了 Tiles,你需要配置 <controller> 为 DelegatingTilesRequestProcessor

如果第二种方法不行,再用第一种方法。   
 至此,SS组合已经将Struts MVC以及Spring中的Bean管理、事务管理融为一体。如
果算上userDAO 中的Hibernate 部分,我们就获得了一个全面、成熟、高效、自顶而下的
Web 开发框架。

来源:http://deathmask1980.spaces.live.com/blog/cns!8633c46371110374!118.entry


posted @ 2008-04-10 16:46 阿伟 阅读(770) | 评论 (0)编辑 收藏

Struts+Spring+Hibernate实现上传下载(四)

转自:http://lihaiyan.javaeye.com/blog/127797

Web层实现

  1、Web层的构件和交互流程

  Web层包括主要3个功能:

  ·上传文件。

  ·列出所有已经上传的文件列表,以供点击下载。

  ·下载文件。

  Web层实现构件包括与2个JSP页面,1个ActionForm及一个Action:

  ·file-upload.jsp:上传文件的页面。

  ·file-list.jsp:已经上传文件的列表页面。

  ·FileActionForm:file-upload.jsp页面表单对应的ActionForm。

  ·FileAction:继承org.apache.struts.actions.DispatchAction的Action,这样这个Action就可以通过一个URL参数区分中响应不同的请求。

  Web层的这些构件的交互流程如图 6所示:

420){this.resized=true;this.style.width=420;}" border=0 resized="true">
图 6 Web层Struts流程图


  其中,在执行文件上传的请求时,FileAction在执行文件上传后,forward到loadAllFile出口中,loadAllFile加载数据库中所有已经上传的记录,然后forward到名为fileListPage的出口中,调用file-list.jsp页面显示已经上传的记录。

  2、FileAction功能

  Struts 1.0的Action有一个弱项:一个Action只能处理一种请求,Struts 1.1中引入了一个DispatchAction,允许通过URL参数指定调用Action中的某个方法,如http://yourwebsite/fileAction.do?method=upload即调用FileAction中的upload方法。通过这种方式,我们就可以将一些相关的请求集中到一个Action当中编写,而没有必要为某个请求操作编写一个Action类。但是参数名是要在struts-config.xml中配置的:

1. <struts-config>
2. <form-beans>
3. <form-bean name="fileActionForm" type="sshfile.web.FileActionForm" />
4. </form-beans>
5. <action-mappings>
6. <action name="fileActionForm" parameter="method" path="/fileAction"
7. type="sshfile.web.FileAction">
8. <forward name="fileListPage" path="/file-list.jsp" />
9. <forward name="loadAllFile" path="/fileAction.do?method=listAllFile" />
10. </action>
11. </action-mappings>
12. </struts-config>


  第6行的parameter="method"指定了承载方法名的参数,第9行中,我们还配置了一个调用FileAction不同方法的Action出口。

  FileAction共有3个请求响应的方法,它们分别是:

  ·upload(…):处理上传文件的请求。

  ·listAllFile(…):处理加载数据库表中所有记录的请求。

  ·download(…):处理下载文件的请求。

  下面我们分别对这3个请求处理方法进行讲解。

  2.1 上传文件

  上传文件的请求处理方法非常简单,简之言之,就是从Spring容器中获取业务层处理类FileService,调用其save(FileActionForm form)方法上传文件,如下所示:

1. public class FileAction
2. extends DispatchAction
3. {
4. //将上传文件保存到数据库中
5. public ActionForward upload(ActionMapping mapping, ActionForm form,
6. HttpServletRequest request,
7. HttpServletResponse response)
8. {
9. FileActionForm fileForm = (FileActionForm) form;
10. FileService fileService = getFileService();
11. fileService.save(fileForm);
12. return mapping.findForward("loadAllFile");
13. }
14. //从Spring容器中获取FileService对象
15. private FileService getFileService()
16. {
17. ApplicationContext appContext = WebApplicationContextUtils.
18. getWebApplicationContext(this.getServlet().getServletContext());
19. return (FileService) appContext.getBean("fileService");
20. }
21. …
22. }


  由于FileAction其它两个请求处理方法也需要从Spring容器中获取FileService实例,所以我们特别提供了一个getFileService()方法(第15~21行)。重构的一条原则就是:"发现代码中有重复的表达式,将其提取为一个变量;发现类中有重复的代码段,将其提取为一个方法;发现不同类中有相同的方法,将其提取为一个类"。在真实的系统中,往往拥有多个Action和多个Service类,这时一个比较好的设置思路是,提供一个获取所有Service实现对象的工具类,这样就可以将Spring 的Service配置信息屏蔽在一个类中,否则Service的配置名字散落在程序各处,维护性是很差的。

  2.2 列出所有已经上传的文件

  listAllFile方法调用Servie层方法加载T_FILE表中所有记录,并将其保存在Request域中,然后forward到列表页面中:

1. public class FileAction
2. extends DispatchAction
3. {
4. …
5. public ActionForward listAllFile(ActionMapping mapping, ActionForm form,
6. HttpServletRequest request,
7. HttpServletResponse response)
8. throws ModuleException
9. {
10. FileService fileService = getFileService();
11. List fileList = fileService.getAllFile();
12. request.setAttribute("fileList",fileList);
13. return mapping.findForward("fileListPage");
14. }
15. }


  file-list.jsp页面使用Struts标签展示出保存在Request域中的记录:

1. <%@page contentType="text/html; charset=GBK"%>
2. <%@taglib uri="/WEB-INF/struts-logic.tld" prefix="logic"%>
3. <%@taglib uri="/WEB-INF/struts-bean.tld" prefix="bean"%>
4. <html>
5. <head>
6. <title>file-download</title>
7. </head>
8. <body bgcolor="#ffffff">
9. <ol>
10. <logic:iterate id="item" name="fileList" scope="request">
11. <li>
12. <a href='fileAction.do?method=download&fileId=
13. <bean:write name="item"property="fileId"/>'>
14. <bean:write name="item" property="fileName"/>
15. </a>
16. </li>
17. </logic:iterate>
18. </ol>
19. </body>
20. </html>


  展现页面的每条记录挂接着一个链接地址,形如:fileAction.do?method=download&fileId=xxx,method参数指定了这个请求由FileAction的download方法来响应,fileId指定了记录的主键。

  由于在FileActionForm中,我们定义了fileId的属性,所以在download响应方法中,我们将可以从FileActionForm中取得fileId的值。这里涉及到一个处理多个请求Action所对应的ActionForm的设计问题,由于原来的Action只能对应一个请求,那么原来的ActionForm非常简单,它仅需要将这个请求的参数项作为其属性就可以了,但现在一个Action对应多个请求,每个请求所对应的参数项是不一样的,此时的ActionForm的属性就必须是多请求参数项的并集了。所以,除了文件上传请求所对应的fileContent和remark属性外还包括文件下载的fileId属性:

420){this.resized=true;this.style.width=420;}" border=0>
图 7 FileActionForm


  当然这样会造成属性的冗余,比如在文件上传的请求中,只会用到fileContent和remark属性,而在文件下载的请求时,只会使用到fileId属性。但这种冗余是会带来好处的--它使得一个Action可以处理多个请求。

  2.3 下载文件

  在列表页面中点击一个文件下载,其请求由FileAction的download方法来响应,download方法调用业务层的FileService方法,获取文件数据并写出到response的响应流中。通过合理设置HTTP响应头参数,将响应流在客户端表现为一个下载文件对话框,其代码如下所示:

  代码 10 业务接口实现类之download

1. public class FileAction
2. extends DispatchAction
3. {
4. …
5. public ActionForward download(ActionMapping mapping, ActionForm form,
6. HttpServletRequest request,
7. HttpServletResponse response)
8. throws ModuleException
9. {
10. FileActionForm fileForm = (FileActionForm) form;
11. FileService fileService = getFileService();
12. String fileName = fileService.getFileName(fileForm.getFileId());
13. try
14. {
15. response.setContentType("application/x-msdownload");
16. response.setHeader("Content-Disposition",
17. "attachment;" + " filename="+
18. new String(fileName.getBytes(), "ISO-8859-1"));
19. fileService.write(response.getOutputStream(), fileForm.getFileId());
20. }
21. catch (Exception e)
22. {
23. throw new ModuleException(e.getMessage());
24. }
25. return null;
26. }
27. }


  第15~18行,设置HTTP响应头,将响应类型设置为application/x-msdownload MIME类型,则响应流在IE中将弹出一个文件下载的对话框,如图 4所示。IE所支持的MIME类型多达26种,您可以通过这个网址查看其他的MIME类型:

http://msdn.microsoft.com/workshop/networking/moniker/overview/appendix_a.asp

  如果下载文件的文件名含有中文字符,如果不对其进行硬编码,如第18行所示,客户文件下载对话框中出现的文件名将会发生乱码。
第19行代码获得response的输出流,作为FileServie write(OutputStream os,String fileId)的入参,这样文件的内容将写到response的输出流中。

  3、web.xml文件的配置

  Spring容器在何时启动呢?我可以在Web容器初始化来执行启动Spring容器的操作,Spring提供了两种方式启动的方法:

  ·通过org.springframework.web.context .ContextLoaderListener容器监听器,在Web容器初始化时触发初始化Spring容器,在web.xml中通过<listener></listener>对其进行配置。

  ·通过Servlet org.springframework.web.context.ContextLoaderServlet,将其配置为自动启动的Servlet,在Web容器初始化时,通过这个Servlet启动Spring容器。

  在初始化Spring容器之前,必须先初始化log4J的引擎,Spring也提供了容器监听器和自动启动Servlet两种方式对log4J引擎进行初始化:

  ·org.springframework.web.util .Log4jConfigListener

  ·org.springframework.web.util.Log4jConfigServlet

  下面我们来说明如何配置web.xml启动Spring容器:

  代码 11 web.xml中对应Spring的配置内容

1. <web-app>
2. <context-param>
3. <param-name>contextConfigLocation</param-name>
4. <param-value>/WEB-INF/applicationContext.xml</param-value>
5. </context-param>
6. <context-param>
7. <param-name>log4jConfigLocation</param-name>
8. <param-value>/WEB-INF/log4j.properties</param-value>
9. </context-param>
10. <servlet>
11. <servlet-name>log4jInitServlet</servlet-name>
12. <servlet-class>org.springframework.web.util.Log4jConfigServlet</servlet-class>
13. <load-on-startup>1</load-on-startup>
14. </servlet>
15. <servlet>
16. <servlet-name>springInitServlet</servlet-name>
17. <servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
18. <load-on-startup>2</load-on-startup>
19. </servlet>
20. …
21. </web-app>


  启动Spring容器时,需要得到两个信息:Spring配置文件的地址和Log4J属性文件,这两上信息分别通过contextConfigLocationWeb和log4jConfigLocation容器参数指定,如果有多个Spring配置文件,则用逗号隔开,如:

/WEB-INF/applicationContext_1.xml, /WEB-INF/applicationContext_1.xm2

  由于在启动ContextLoaderServlet之前,必须事先初始化Log4J的引擎,所以Log4jConfigServlet必须在ContextLoaderServlet之前启动,这通过<load-on-startup>来指定它们启动的先后顺序。

  乱码是开发Web应用程序一个比较老套又常见问题,由于不同Web应用服务器的默认编码是不一样的,为了方便Web应用在不同的Web应用服务器上移植,最好的做法是Web程序自身来处理编码转换的工作。经典的作法是在web.xml中配置一个编码转换过滤器,Spring就提供了一个编码过滤器类CharacterEncodingFilter,下面,我们为应用配置上这个过滤器:

1. <web-app>
2. …
3. <filter>
4. <filter-name>encodingFilter</filter-name>
5. <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
6. <init-param>
7. <param-name>encoding</param-name>
8. <param-value>GBK</param-value>
9. </init-param>
10. </filter>
11. <filter-mapping>
12. <filter-name>encodingFilter</filter-name>
13. <url-pattern>/*</url-pattern>
14. </filter-mapping>
15. …
16. </web-app>


  Spring的过滤器类是org.springframework.web.filter.CharacterEncodingFilter,通过encoding参数指定编码转换类型为GBK,<filter-mapping>的配置使该过滤器截获所有的请示。

  Struts的框架也需要在web.xml中配置,想必读者朋友对Struts的配置都很熟悉,故在此不再提及,请参见本文所提供的源码。

  总结

  本文通过一个文件上传下载的Web应用,讲解了如何构建基于SSH的Web应用,通过Struts和FormFile,Spring的LobHandler以及Spring为HibernateBlob处理所提供的用户类BlobByteArrayType ,实现上传和下载文件的功能仅需要廖廖数行的代码即告完成。读者只需对程序作稍许的调整,即可处理Clob字段:

  ·领域对象对应Clob字段的属性声明为String类型;

  ·映射文件对应Clob字段的属性声明为org.springframework.orm.hibernate3.support.ClobStringType类型。

  本文通过SSH对文件上传下载简捷完美的实现得以管中窥豹了解SSH强强联合构建Web应用的强大优势。在行文中,还穿插了一些分层的设计经验,配置技巧和Spring所提供的方便类,相信这些知识对您的开发都有所裨益。

作者:陈雄华出处:天极开发


posted @ 2008-04-10 15:03 阿伟 阅读(353) | 评论 (0)编辑 收藏

Struts+Spring+Hibernate实现上传下载(三)

转自:http://lihaiyan.javaeye.com/blog/127796

业务层

  1、业务层接口

  "面向接口而非面向类编程"是Spring不遗余力所推荐的编程原则,这条原则也已经为大部开发者所接受;此外,JDK的动态代理只对接口有效,否则必须使用CGLIB生成目标类的子类。我们依从于Spring的倡导为业务类定义一个接口:

  代码 7 业务层操作接口

1. public interface FileService
2. {
3. void save(FileActionForm fileForm);//将提交的上传文件保存到数据表中
4. List getAllFile();//得到T_FILE所示记录
5. void write(OutputStream os,String fileId);//将某个文件的文件数据写出到输出流中
6. String getFileName(String fileId);//获取文件名
7. }

  其中save(FileActionForm fileForm)方法,将封装在fileForm中的上传文件保存到数据库中,这里我们使用FileActionForm作为方法入参,FileActionForm是Web层的表单数据对象,它封装了提交表单的数据。将FileActionForm直接作为业务层的接口入参,相当于将Web层传播到业务层中去,即将业务层绑定在特定的Web层实现技术中,按照分层模型学院派的观点,这是一种反模块化的设计,但在"一般"的业务系统并无需提供多种UI界面,系统Web层将来切换到另一种实现技术的可能性也微乎其微,所以笔者觉得没有必要为了这个业务层完全独立于调用层的过高目标而去搞一个额外的隔离层,浪费了原材料不说,还将系统搞得过于复杂,相比于其它原则,"简单"始终是最大的一条原则。

  getAllFile()负责获取T_FILE表所有记录,以便在网页上显示出来。

  而getFileName(String fileId)和write(OutputStream os,String fileId)则用于下载某个特定的文件。具体的调用是将Web层将response.getOutputStream()传给write(OutputStream os,String fileId)接口,业务层直接将文件数据输出到这个响应流中。具体实现请参见错误!未找到引用源。节下载文件部分。

  2、业务层接口实现类

  FileService的实现类为FileServiceImpl,其中save(FileActionForm fileForm)的实现如下所示:

  代码 8 业务接口实现类之save()

1. …
2. public class FileServiceImpl
3. implements FileService
4. {
5. private TfileDAO tfileDAO;
6. public void save(FileActionForm fileForm)
7. {
8. Tfile tfile = new Tfile();
9. try
10. {
11. tfile.setFileContent(fileForm.getFileContent().getFileData());
12. }
13. catch (FileNotFoundException ex)
14. {
15. throw new RuntimeException(ex);
16. }
17. catch (IOException ex)
18. {
19. throw new RuntimeException(ex);
20. }
21. tfile.setFileName(fileForm.getFileContent().getFileName());
22. tfile.setRemark(fileForm.getRemark());
23. tfileDAO.save(tfile);
24. }
25. …
26. }

  在save(FileActionForm fileForm)方法里,完成两个步骤:

  其一,象在水桶间倒水一样,将FileActionForm对象中的数据倒入到Tfile对象中;

  其二,调用TfileDAO保存数据。

  需要特别注意的是代码的第11行,FileActionForm的fileContent属性为org.apache.struts.upload.FormFile类型,FormFile提供了一个方便的方法getFileData(),即可获取文件的二进制数据。通过解读FormFile接口实现类DiskFile的原码,我们可能知道FormFile本身并不缓存文件的数据,只有实际调用getFileData()时,才从磁盘文件输入流中获取数据。由于FormFile使用流读取方式获取数据,本身没有缓存文件的所有数据,所以对于上传超大体积的文件,也是没有问题的;但是,由于数据持久层的Tfile使用byte[]来缓存文件的数据,所以并不适合处理超大体积的文件(如100M),对于超大体积的文件,依然需要使用java.sql.Blob类型以常规流操作的方式来处理。

  此外,通过FileForm的getFileName()方法就可以获得上传文件的文件名,如第21行代码所示。

  write(OutputStream os,String fileId)方法的实现,如代码 9所示:

  代码 9 业务接口实现类之write()

1. …
2. public class FileServiceImpl
3. implements FileService
4. {
5.
6. public void write(OutputStream os, String fileId)
7. {
8. Tfile tfile = tfileDAO.findByFildId(fileId);
9. try
10. {
11. os.write(tfile.getFileContent());
12. os.flush();
13. }
14. catch (IOException ex)
15. {
16. throw new RuntimeException(ex);
17. }
18. }
19. …
20. }

  write(OutputStream os,String fileId)也简单地分为两个操作步骤,首先,根据fileId加载表记录,然后将fileContent写入到输出流中。

  3、Spring事务配置

  下面,我们来看如何在Spring配置文件中为FileService配置声明性的事务

1. <beans>
2. …
3. <bean id="transactionManager"
4. class="org.springframework.orm.hibernate3.HibernateTransactionManager">
5. <property name="sessionFactory" ref="sessionFactory"/>
6. </bean>
7. <!-- 事务处理的AOP配置 //-->
8. <bean id="txProxyTemplate" abstract="true"
9. class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
10. <property name="transactionManager" ref="transactionManager"/>
11. <property name="transactionAttributes">
12. <props>
13. <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
14. <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
15. <prop key="save">PROPAGATION_REQUIRED</prop>
16. <prop key="write">PROPAGATION_REQUIRED,readOnly</prop>
17. </props>
18. </property>
19. </bean>
20. <bean id="fileService" parent="txProxyTemplate">
21. <property name="target">
22. <bean class="sshfile.service.FileServiceImpl">
23. <property name="tfileDAO" ref="tfileDAO"/>
24. </bean>
25. </property>
26. </bean>
27. </beans>

  Spring的事务配置包括两个部分:

  其一,定义事务管理器transactionManager,使用HibernateTransactionManager实现事务管理;

  其二,对各个业务接口进行定义,其实txProxyTemplate和fileService是父子节点的关系,本来可以将txProxyTemplate定义的内容合并到fileService中一起定义,由于我们的系统仅有一个业务接口需要定义,所以将其定义的一部分抽象到父节点txProxyTemplate中意义确实不大,但是对于真实的系统,往往拥有为数众多的业务接口需要定义,将这些业务接口定义内容的共同部分抽取到一个父节点中,然后在子节点中通过parent进行关联,就可以大大简化业务接口的配置了。

  父节点txProxyTemplate注入了事务管理器,此外还定义了业务接口事务管理的方法(允许通过通配符的方式进行匹配声明,如前两个接口方法),有些接口方法仅对数据进行读操作,而另一些接口方法需要涉及到数据的更改。对于前者,可以通过readOnly标识出来,这样有利于操作性能的提高,需要注意的是由于父类节点定义的Bean仅是子节点配置信息的抽象,并不能具体实现化一个Bean对象,所以需要特别标注为abstract="true",如第8行所示。

  fileService作为一个目标类被注入到事务代理器中,而fileService实现类所需要的tfileDAO实例,通过引用3.2节中定义的tfileDAO Bean注入。


posted @ 2008-04-10 15:01 阿伟 阅读(220) | 评论 (0)编辑 收藏

Struts+Spring+Hibernate实现上传下载(二)

转自:http://lihaiyan.javaeye.com/blog/127795

数据持久层

  1、领域对象及映射文件

  您可以使用Hibernate Middlegen、HIbernate Tools、Hibernate Syhchronizer等工具或手工的方式,编写Hibernate的领域对象和映射文件。其中对应T_FILE表的领域对象Tfile.java为:

  代码 1 领域对象Tfile

1. package sshfile.model;
2. public class Tfile
3.{
4. private String fileId;
5. private String fileName;
6. private byte[] fileContent;
7. private String remark;
8. …//getter and setter
9. }

  特别需要注意的是:数据库表为Blob类型的字段在Tfile中的fileContent类型为byte[]。Tfile的Hibernate映射文件Tfile.hbm.xml放在Tfile .java类文件的相同目录下:

  代码 2 领域对象映射文件

1. <?xml version="1.0"?>
2. <!DOCTYPE hibernate-mapping PUBLIC
3. "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
4. "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
5. <hibernate-mapping>
6. <class name="sshfile.model.Tfile" table="T_FILE">
7. <id name="fileId" type="java.lang.String" column="FILE_ID">
8. <generator class="uuid.hex"/>
9. </id>
10. <property name="fileContent"
11. type="org.springframework.orm.hibernate3.support.BlobByteArrayType"
12. column="FILE_CONTENT" lazy="true"/>
13. …//其它一般字段的映射
14. </class>
15. </hibernate-mapping>

  fileContent字段映射为Spring所提供的BlobByteArrayType类型,BlobByteArrayType是用户自定义的数据类型,它实现了Hibernate 的org.hibernate.usertype.UserType接口。BlobByteArrayType使用从sessionFactory获取的Lob操作句柄lobHandler将byte[]的数据保存到Blob数据库字段中。这样,我们就再没有必要通过硬编码的方式,先insert然后再update来完成Blob类型数据的持久化,这个原来难伺候的老爷终于被平民化了。关于lobHandler的配置请见本文后面的内容。

  此外lazy="true"说明地返回整个Tfile对象时,并不返回fileContent这个字段的数据,只有在显式调用tfile.getFileContent()方法时才真正从数据库中获取fileContent的数据。这是Hibernate3引入的新特性,对于包含重量级大数据的表字段,这种抽取方式提高了对大字段操作的灵活性,否则加载Tfile对象的结果集时如果总是返回fileContent,这种批量的数据抽取将可以引起数据库的"洪泛效应"。

  2、DAO编写和配置

  Spring强调面向接口编程,所以我们将所有对Tfile的数据操作的方法定义在TfileDAO接口中,这些接口方法分别是:

  ·findByFildId(String fileId)

  ·save(Tfile tfile)

  ·List findAll()

  TfileDAOHibernate提供了对TfileDAO接口基于Hibernate的实现,如代码 3所示:

  代码 3 基于Hibernate 的fileDAO实现类

1. package sshfile.dao;
2.
3. import sshfile.model.*;
4. import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
5. import java.util.List;
6.
7. public class TfileDAOHibernate
8. extends HibernateDaoSupport implements TfileDAO
9. {
10. public Tfile findByFildId(String fileId)
11. {
12. return (Tfile) getHibernateTemplate().get(Tfile.class, fileId);
13. }
14. public void save(Tfile tfile)
15. {
16. getHibernateTemplate().save(tfile);
17. getHibernateTemplate().flush();
18. }
19. public List findAll()
20. {
21. return getHibernateTemplate().loadAll(Tfile.class);
22. }
23. }

  TfileDAOHibernate通过扩展Spring提供的Hibernate支持类HibernateDaoSupport而建立,HibernateDaoSupport封装了HibernateTemplate,而HibernateTemplate封装了Hibernate所提供几乎所有的的数据操作方法,如execute(HibernateCallback action),load(Class entityClass, Serializable id),save(final Object entity)等等。

  所以我们的DAO只需要简单地调用父类的HibernateTemplate就可以完成几乎所有的数据库操作了。

  由于Spring通过代理Hibernate完成数据层的操作,所以原Hibernate的配置文件hibernate.cfg.xml的信息也转移到Spring的配置文件中:

  代码 4 Spring中有关Hibernate的配置信息

1. <beans>
2. <!-- 数据源的配置 //-->
3. <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
4. destroy-method="close">
5. <property name="driverClassName" value="http://www.zhmy.com/oracle.jdbc.driver.OracleDriver"/>
6. <property name="url" value="jdbc:oracle:thin:@localhost:1521:ora9i"/>
7. <property name="username" value="test"/>
8. <property name="password" value="test"/>
9. </bean>
10. <!-- Hibernate会话工厂配置 //-->
11. <bean id="sessionFactory"
12. class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
13. <property name="dataSource" ref="dataSource"/>
14. <property name="mappingDirectoryLocations">
15. <list>
16. <value>classpath:/sshfile/model</value>
17. </list>
18. </property>
19. <property name="hibernateProperties">
20. <props>
21. <prop key="hibernate.dialect">org.hibernate.dialect.OracleDialect</prop>
22. <prop key="hibernate.cglib.use_reflection_optimizer">true</prop>
23. </props>
24. </property>
25. </bean>
26. <!-- Hibernate 模板//-->
27. <bean id="hibernateTemplate"
28. class="org.springframework.orm.hibernate3.HibernateTemplate">
29. <property name="sessionFactory" ref="sessionFactory"/>
30. </bean>
31. <!--DAO配置 //-->
32. <bean id="tfileDAO" class="sshfile.dao.TfileDAOHibernate">
33. <property name="hibernateTemplate" ref="hibernateTemplate" />
34. </bean>
35. …
36. </beans>

  第3~9行定义了一个数据源,其实现类是apache的BasicDataSource,第11~25行定义了Hibernate的会话工厂,会话工厂类用Spring提供的LocalSessionFactoryBean维护,它注入了数据源和资源映射文件,此外还通过一些键值对设置了Hibernate所需的属性。

  其中第16行通过类路径的映射方式,将sshfile.model类包目录下的所有领域对象的映射文件装载进来,在本文的例子里,它将装载进Tfile.hbm.xml映射文件。如果有多个映射文件需要声明,使用类路径映射方式显然比直接单独指定映射文件名的方式要简便。

  第27~30行定义了Spring代理Hibernate数据操作的HibernateTemplate模板,而第32~34行将该模板注入到tfileDAO中。

  需要指定的是Spring 1.2.5提供了两套Hibernate的支持包,其中Hibernate 2相关的封装类位于org.springframework.orm.hibernate2.*包中,而Hibernate 3.0的封装类位于org.springframework.orm.hibernate3.*包中,需要根据您所选用Hibernate版本进行正确选择。

  3、Lob字段处理的配置

  我们前面已经指出Oracle的Lob字段和一般类型的字段在操作上有一个明显的区别--那就是你必须首先通过Oracle的empty_blob()/empty_clob()初始化Lob字段,然后获取该字段的引用,通过这个引用更改其值。所以要完成对Lob字段的操作,Hibernate必须执行两步数据库访问操作,先Insert再Update。

  使用BlobByteArrayType字段类型后,为什么我们就可以象一般的字段类型一样操作Blob字段呢?可以确定的一点是:BlobByteArrayType不可能逾越Blob天生的操作方式,原来是BlobByteArrayType数据类型本身具体数据访问的功能,它通过LobHandler将两次数据访问的动作隐藏起来,使Blob字段的操作在表现上和其他一般字段业类型无异,所以LobHandler即是那个"苦了我一个,幸福十亿人"的那位幕后英雄。

  LobHandler必须注入到Hibernate会话工厂sessionFactory中,因为sessionFactory负责产生与数据库交互的Session。LobHandler的配置如代码 5所示:

  代码 5 Lob字段的处理句柄配置

1. <beans>
2. …
3. <bean id="nativeJdbcExtractor"
4. class="org.springframework.jdbc.support.nativejdbc.CommonsDbcpNativeJdbcExtractor"
5. lazy-init="true"/>
6. <bean id="lobHandler"
7. class="org.springframework.jdbc.support.lob.OracleLobHandler" lazy-init="true">
8. <property name="nativeJdbcExtractor">
9. <ref local="nativeJdbcExtractor"/>
10. </property>
11. </bean>
12. …
13. </beans>

  首先,必须定义一个能够从连接池中抽取出本地数据库JDBC对象(如OracleConnection,OracleResultSet等)的抽取器:nativeJdbcExtractor,这样才可以执行一些特定数据库的操作。对于那些仅封装了Connection而未包括Statement的简单数据连接池,SimpleNativeJdbcExtractor是效率最高的抽取器实现类,但具体到apache的BasicDataSource连接池,它封装了所有JDBC的对象,这时就需要使用CommonsDbcpNativeJdbcExtractor了。Spring针对几个著名的Web服务器的数据源提供了相应的JDBC抽取器:

  ·WebLogic:WebLogicNativeJdbcExtractor

  ·WebSphere:WebSphereNativeJdbcExtractor

  ·JBoss:JBossNativeJdbcExtractor

  在定义了JDBC抽取器后,再定义lobHandler。Spring 1.2.5提供了两个lobHandler:

  ·DefaultLobHandler:适用于大部分的数据库,如SqlServer,MySQL,对Oracle 10g也适用,但不适用于Oracle 9i(看来Oracle 9i确实是个怪胎,谁叫Oracle 公司自己都说Oracle 9i是一个过渡性的产品呢)。

  ·OracleLobHandler:适用于Oracle 9i和Oracle 10g。

  由于我们的数据库是Oracle9i,所以使用OracleLobHandler。

  在配置完LobHandler后, 还需要将其注入到sessionFactory的Bean中,下面是调用后的sessionFactory Bean的配置:

  代码 6 将lobHandler注入到sessionFactory中的配置

1. <beans>
2. …
3. <bean id="sessionFactory"
4. class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
5. <property name="dataSource" ref="dataSource"/>
6. <!-- 为处理Blob类型字段的句柄声明 //-->
7. <property name="lobHandler" ref="lobHandler"/>
8. …
9. </bean>
10. …
11. </beans>

  如第7所示,通过sessionFactory的lobHandler属性进行注入。


posted @ 2008-04-10 14:55 阿伟 阅读(281) | 评论 (0)编辑 收藏
转自:http://lihaiyan.javaeye.com/blog/127794
引言

  文件的上传和下载在
J2EE编程已经是一个非常古老的话题了,也许您马上就能掰着指头数出好几个著名的大件:如SmartUpload、Apache的FileUpload。但如果您的项目是构建在Struts+Spring+Hibernate(以下称SSH)框架上的,这些大件就显得笨重而沧桑了,SSH提供了一个简捷方便的文件上传下载的方案,我们只需要通过一些配置并辅以少量的代码就可以完好解决这个问题了。

  本文将围绕SSH文件上传下载的主题,向您详细讲述如何开发基于SSH的Web程序。SSH各框架的均为当前最新版本:

  ·Struts 1.2

  ·Spring 1.2.5

  ·Hibernate 3.0

  本文选用的数据库为
Oracle 9i,当然你可以在不改动代码的情况下,通过配置文件的调整将其移植到任何具有Blob字段类型的数据库上,如MySQL,SQLServer等。

  总体实现

  上传文件保存到T_FILE表中,T_FILE表结构如下:

420){this.resized=true;this.style.width=420;}" border=0>
图 1 T_FILE表结构

  其中:

  ·FILE_ID:文件ID,32个字符,用Hibernate的uuid.hex算法生成。

  ·FILE_NAME:文件名。

  ·FILE_CONTENT:文件内容,对应Oracle的Blob类型。

  ·REMARK:文件备注。

  文件数据存储在Blob类型的FILE_CONTENT表字段上,在Spring中采用OracleLobHandler来处理Lob字段(包括Clob和Blob),由于在程序中不需要引用到oracle数据驱动程序的具体类且屏蔽了不同数据库处理Lob字段方法上的差别,从而撤除程序在多数据库移植上的樊篱。

  1.首先数据表中的Blob字段在Java领域对象中声明为byte[]类型,而非java.sql.Blob类型。

  2.数据表Blob字段在Hibernate持久化映射文件中的type为org.springframework.orm.hibernate3.support.BlobByteArrayType,即Spring所提供的用户自定义的类型,而非java.sql.Blob。

  3.在Spring中使用org.springframework.jdbc.support.lob.OracleLobHandler处理Oracle数据库的Blob类型字段。

  通过这样的设置和配置,我们就可以象持久化表的一般字段类型一样处理Blob字段了。

  以上是Spring+Hibernate将文件二进制数据持久化到数据库的解决方案,而Struts通过将表单中file类型的组件映射为ActionForm中类型为org.apache.struts.upload. FormFile的属性来获取表单提交的文件数据。

  综上所述,我们可以通过图 2,描绘出SSH处理文件上传的方案:

420){this.resized=true;this.style.width=420;}" border=0>
图 2 SSH处理文件上传技术方案

  文件上传的页面如图 3所示:

420){this.resized=true;this.style.width=420;}" border=0 resized="true">
图 3 文件上传页面

  文件下载的页面如图 4所示:

420){this.resized=true;this.style.width=420;}" border=0 resized="true">
图 4 文件下载页面

  该工程的资源结构如图 5所示:

420){this.resized=true;this.style.width=420;}" border=0>
图 5 工程资源结构

  工程的类按SSH的层次结构划分为数据持久层、业务层和Web层;WEB-INF下的applicationContext.xml为Spring的配置文件,struts-config.xml为Struts的配置文件,file-upload.jsp为文件上传页面,file-list.jsp为文件列表页面。

  本文后面的章节将从数据持久层->业务层->Web层的开发顺序,逐层讲解文件上传下载的开发过程。
posted @ 2008-04-10 14:48 阿伟 阅读(332) | 评论 (1)编辑 收藏

转自:http://ttitfly.javaeye.com/blog/133000

Spring配置初始化ApplicationContext

1. 在struts-config.xml里,以插件的形式

xml 代码
< plug-in className="org.springframework.web.struts.ContextLoaderPlugIn" / >  
    < set-property property="contextConfigLocation" value="/WEB-INF/applicationContext.xml" / >  
< / plug-in >   

这种方式如果没有配置contextConfigLocation的值,则会自动加载xx-servlet.xml.

xx的值是和web.xml里的配置org.apache.struts.action.ActionServlet的servlet-name的值一样

如下:xx的值也就是 action,所以会自动加载action-servlet.xml

xml 代码
< servlet >  
    < servlet-name >action< / servlet-name >  
    < servlet-class >org.apache.struts.action.ActionServlet< / servlet-class >  
    < load-on-startup >1< / load-on-startup >  
  < / servlet >  
  < servlet-mapping >  
    < servlet-name >action< / servlet-name >  
    < url-pattern >*.do< / url-pattern >  
  < / servlet-mapping >  

 如果sturts-config.xml里配置了contextConfigLocation的值,那么就不会自动加载xx-servlet.xml了,而只会加载contextConfigLocation所指定的xml.

 2. 第2种方式

在web.xml里配置Listener

xml 代码
<listener>  
        <  listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>  
    <  /  listener>  

 

如果在web.xml里给该Listener指定要加载的xml,如:

xml 代码
<context-param>  
        <param-name>contextConfigLocationparam-name>  
        <param-value>classpath*:spring/*.xmlparam-value>  
    context-param>  

则会去加载相应的xml,而不会去加载/WEB-INF/下的applicationContext.xml。。但是,如果没有指定的话,默认会去/WEB-INF/下加载applicationContext.xml。

 

3. 第三种方式:ContextLoaderServlet

xml 代码
< servlet>    
        < servlet-name>context< / servlet-name>    
        < servlet-class>org.springframework.web.context.ContextLoaderServlet< / servlet-class>    
        < load-on-startup>1< / load-on-startup>    
    < / servlet>  

 

 这种方式和第二种Listener方式一样,唯一的区别就是用Listener方式初始化ApplicationContext,可以和用第一种方式(struts-config.xml里 plugin方式)同时存在,而ContextLoaderServlet则不可以和第一种方式同时存在

总结:

ContextLoaderServlet已经不推荐用了,它只是为了兼容低版本的servlet.jar才用的。

总的来说:Listerner要比Servlet更好一些,而且Listerner监听应用的启动和结束,而Servlet启动要稍微延迟一些。

 

posted @ 2008-04-10 10:20 阿伟 阅读(852) | 评论 (0)编辑 收藏
  转自:http://blog.tostudy.com.cn/blog/show_3574.html

优化Web应用的性能绝不象有些人想象的那样简单易行,它涉及到诸多技术,从最简单的HTML代码修改,到复杂的EJB改造,无不涉及性能问题。但有一点是非常清楚的:要想找出和解决Web应用的性能瓶颈,就必须深入全面地了解信息在Web应用中的流程。

  改善Web应用的性能不一定要局限于Web应用的Java代码,例如有些时候,简单地改动一下HTML页面的质量、减少其传输频度和数据量就可以有效地提高应用的性能表现;有时提高性能的关键却在于修改Web应用的数据库访问部分——这只是Java代码之外影响性能的两个因素,其他还有许多因素会影响到Web应用的整体性能表现。另一方面,就Java程序本身而言,其性能优化又可以分成三个领域:基本的Java代码优化,JSP/Servlet优化,EJB优化。

  一、表现层优化

  Web应用的最大性能瓶颈常常不在其他地方,而在于最基本的网络带宽限制。如果你的Web应用也面临这类问题,提高性能最简单的办法是减少HTTP传输,例如用JavaScript实现客户端编辑功能以减少数据传输次数,避免将数据发送到服务器端再执行合法性验证之类的编辑操作。

  应当采用一切可能措施减少通过网络传输的数据。例如,你可以要求浏览器缓冲模块化的JavaScript文件,在SCRIPT标记的SRC中指定:
SCRIPT LANGUAGE="JavaScript" SRC="FormChek.js"。

  其他减少网络传输应当注意的地方还包括:避免过度使用隐藏域,减少超长Cookie值,在RADIO、CHECKBOX和SELECT域中用代码来替代长长的字符串,等等。不过在HTML优化方面本文不准备作全面的讨论,因为WebSphere应用的开发者一般不会担负设计表现层的责任,只要了解下面这个原理就足够了:

  性能技巧之一:尽可能减少HTTP数据传输的总量和频度

  二、数据库访问

  朋友小A对Java的了解极为有限,但他却成功地改进了许多WebSphere应用的性能。他是怎么做到的呢?原来,小A是一个数据库专家,他通过优化数据库访问有效地改进了整个应用的性能,但对于Java,他只是略微了解一些有关JDBC的知识。在优化数据库访问时,小A做的第一件事情总是检查数据库的设计,有时他会建议重新构造数据库的结构(必须指出的是,为了提高性能而重新构造数据库结构有时可能使数据库反规格化(De-Normalization),从而带来维护方面的问题)。 
性能技巧之二:规格化(Normalization)数据库结构

  小A做的第二件事情是执行数据库分析,根据分析结果提出增加某个索引、减少某个索引的建议。完成这一步骤后,小A通常可以让应用有令人满意的性能表现,根本不必去查看应用的Java代码。

  性能技巧之三:针对常用的SQL操作建立索引,删除多余的索引

  有时,为了进一步优化应用的性能,小A会检查Java(也许应该说是SQL)代码,经常找到Java程序没有合理运用PreparedStatement和连接缓冲池的情形。只要把Statement类的动态SQL替换成PreparedStatement类的静态SQL,从连接池提取SQL连接(而不是直接创建连接),应用的性能将得到显著的改善。注意DB2 UDB(包括其他一些数据库)的PreparedStatement是可调整和配置的。

  性能技巧之四:合理运用PreparedStatement和连接池

  进一步分析应用的工作流程之后,小A有时会建议批量执行某些SQL命令,这样就只需一个对数据库服务器的请求就可以运行大量的SQL命令。

  性能技巧之五:考虑批量执行SQL命令

  既然如此,小A有时还会指出,如果应用中有些SQL命令可以组合成单个事务逻辑,那么应该可以用一个存储过程来替代。DB2 UDB的存储过程语言(SPL,Stored Procedure Language)非常强大,如果把数据库操作逻辑从Web应用转移到数据库,一般总是对性能有益。不过需要注意的是,虽然批量执行SQL命令或使用存储过程会提高性能,但就象重新构造数据库结构一样,有时会带来维护方面的困难。

  性能技巧之六:考虑使用数据库存储过程

  检查JDBC代码的时候,小A总是留意对象有没有及时正确释放。这一点其实很重要。

  性能技巧之七:及时关闭不用的Statement、ResultSet、Connection等对象(但不是在finalize方法内)

三、Java代码

  前面我们以小A的经验为例,探讨了Web应用中数据库访问性能的重要性。调整好数据库之后,接下来要做的自然是深入分析应用的Java代码。从哪里入手呢?你最好使用Java分析工具来找出性能问题的焦点所在。优化Java代码的性能是一个艰苦的过程,因此一个重要的原则是把精力集中到那些可能引起性能问题的代码上。换句话说,就是要尊重80/20规则:利用Java分析工具的结果,调整带来80%性能开销的那20%代码。

  性能技巧之八:用Java分析工具清楚地界定性能问题所在

  目前市场上已经有许多优秀的Java分析工具,例如ej-technologie的JProfile(http://www.ej-technologies.com),Klgroup的Jprobe(http://www.klg.com),以及Intuitive Systems的OptimizeIt(http://www.optimizeit.com)。不过不要忘记WebSphere Studio Application Developer(WSAD)本身也集成了一个优秀的分析器,有条件的话,最好多用几种分析工具分析Java代码。

  考虑到资金问题,你不一定乐意购买昂贵的分析软件,但你可以用Java本身的命令行工具生成分析信息。例如,在JDK 1.3中,你可以用下面的命令将TestOrderProcessing类的CPU使用情况保存到java.hprof文件:java -Xrunhprof:cpu=times,format=a,file=java.hprof TestOrderProcessing。

  这种办法的缺点是它提供的信息条理不够清楚,比较繁杂;也许可以找到一些源代码开放的工具辅助分析,但一般不如使用WSAD本身的分析工具或商业化的分析工具方便。另外,如果你已经了解哪些代码块可能引起性能问题,可以通过保存系统时间的方式获得分析信息,例如:
long startTime = System.currentTimeMillis();
// 执行某些操作
long endTime = System.currentTimeMillis();

  3.1 基本篇

  有人建议“稳定性第一,速度第二”,一般而言遵从这个建议是不会错的,但这并不妨碍我们在编写代码的同时运用某些已经证实的性能技巧。例如,我们都知道String类是不可变的,连接两个String是一项开销很大操作。

  性能技巧之九:用StringBuffer来连接两个字符串

  也许你已经注意到,SUN的许多标准Java类是线程安全的,这些类内部的同步机制实际上很容易造成性能问题。例如,Vector类就是一个线程安全的类,除非确实要用到同步机制,否则使用Vector是不值得的,如有可能,应当尽量改用非线程安全的类如ArrayList。

  性能技巧之十:只有在必要时才运用线程安全的类

  许多人习惯使用System.out.println来输出跟踪信息,但println要占用不少资源,所以输出跟踪信息最好使用专用日志记录框架,如IBM的JRas或Apache的Log4j。

  性能技巧之十一:用日志记录框架类输出跟踪信息,而不是使用System.out.println

  最后一个提高代码性能的简单技巧是清除类里面的调试信息,减小类的体积。IBM有一个WSAD插件,它提供了一个叫做setDebugInfo的任务,可以从Ant脚本调用。

  性能技巧之十二:从正式发行的软件中删除调试信息

posted @ 2008-04-08 13:03 阿伟 阅读(193) | 评论 (0)编辑 收藏
web.xml配置的详细说明3
7 指定欢迎页

假如用户提供了一个像http: //host/webAppPrefix/directoryName/ 这样的包含一个目录名但没有包含文件名的URL,会发生什么事情呢?用户能得到一个目录表?一个错误?还是标准文件的内容?如果得到标准文件内容,是 index.html、index.jsp、default.html、default.htm或别的什么东西呢?
Welcome-file-list 元素及其辅助的welcome-file元素解决了这个模糊的问题。例如,下面的web.xml项指出,如果一个URL给出一个目录名但未给出文件名,服务器应该首先试用index.jsp,然后再试用index.html。如果两者都没有找到,则结果有赖于所用的服务器(如一个目录列表)。
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
虽然许多服务器缺省遵循这种行为,但不一定必须这样。因此,明确地使用welcom-file-list保证可移植性是一种良好的习惯。

8 指定处理错误的页面

现在我了解到,你在开发servlet和JSP页面时从不会犯错误,而且你的所有页面是那样的清晰,一般的程序员都不会被它们的搞糊涂。但是,是人总会犯错误的,用户可能会提供不合规定的参数,使用不正确的URL或者不能提供必需的表单字段值。除此之外,其它开发人员可能不那么细心,他们应该有些工具来克服自己的不足。
error-page元素就是用来克服这些问题的。它有两个可能的子元素,分别是:error-code和exception- type。第一个子元素error-code指出在给定的HTTP错误代码出现时使用的URL。第二个子元素excpetion-type指出在出现某个给定的Java异常但未捕捉到时使用的URL。error-code和exception-type都利用location元素指出相应的URL。此 URL必须以/开始。location所指出的位置处的页面可通过查找HttpServletRequest对象的两个专门的属性来访问关于错误的信息,这两个属性分别是:javax.servlet.error.status_code和javax.servlet.error.message。
可回忆一下,在web.xml内以正确的次序声明web-app的子元素很重要。这里只要记住,error-page出现在web.xml文件的末尾附近,servlet、servlet-name和welcome-file-list之后即可。

8.1 error-code元素
为了更好地了解error-code元素的值,可考虑一下如果不正确地输入文件名,大多数站点会作出什么反映。这样做一般会出现一个404错误信息,它表示不能找到该文件,但几乎没提供更多有用的信息。另一方面,可以试一下在www.microsoft.comwww.ibm.com 处或者特别是在www.bea.com 处输出未知的文件名。这是会得出有用的消息,这些消息提供可选择的位置,以便查找感兴趣的页面。提供这样有用的错误页面对于Web应用来说是很有价值得。事实上rm-error-page子元素)。由form-login-page给出的HTML表单必须具有一个j_security_check的 ACTION属性、一个名为j_username的用户名文本字段以及一个名为j_password的口令字段。
例如,程序清单5-19指示服务器使用基于表单的验证。Web应用的顶层目录中的一个名为login.jsp的页面将收集用户名和口令,并且失败的登陆将由相同目录中名为login-error.jsp的页面报告。

程序清单5-19 web.xml(说明login-config的摘录)
<?xml version="1.0" encoding="ISO-8859-1"?>
<!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>
<!-- ... -->
<security-constraint> ... </security-constraint>
<login-config>
<auth-method> FORM </auth-method>
<form-login-config>
<form-login-page>/login.jsp</form-login-page>
<form-error-page>/login-error.jsp</form-error-page>
</form-login-config>
</login-config>
<!-- ... -->
</web-app>

8.2 exception-type元素
error-code元素处理某个请求产生一个特定的HTTP状态代码时的情况。然而,对于servlet或JSP页面返回200但产生运行时异常这种同样是常见的情况怎么办呢?这正是exception-type元素要处理的情况。只需提供两样东西即可:即提供如下的一个完全限定的异常类和一个位置:
<error-page>
<exception-type>packageName.className</exception-type>
<location>/SomeURL</location>
</error-page>
这样,如果Web应用中的任何servlet或JSP页面产生一个特定类型的未捕捉到的异常,则使用指定的URL。此异常类型可以是一个标准类型,如javax.ServletException或java.lang.OutOfMemoryError,或者是一个专门针对你的应用的异常。
例如,程序清单5-15给出了一个名为DumbDeveloperException的异常类,可用它来特别标记经验较少的程序员(不是说你的开发组中一定有这种人)所犯的错误。这个类还包含一个名为dangerousComputation的静态方法,它时不时地生成这种类型的异常。程序清单5-16给出对随机整数值调用dangerousCompution的一个JSP页面。在抛出此异常时,如程序清单5-18的web.xml版本中所给出的exception-type所指出的那样,对客户机显示DDE.jsp(程序清单5-17)。图5-16和图5-17分别给出幸运和不幸的结果。

程序清单5-15 DumbDeveloperException.java
package moreservlets;

/** Exception used to flag particularly onerous
programmer blunders. Used to illustrate the
exception-type web.xml element.
* <P>
* Taken from More Servlets and JavaServer Pages
* from Prentice Hall and Sun Microsystems Press,
* http://www.moreservlets.com/.
* &copy; 2002 Marty Hall; may be freely used or adapted.
*/

public class DumbDeveloperException extends Exception {
public DumbDeveloperException() {
super("Duh. What was I *thinking*?");
}

public static int dangerousComputation(int n)
throws DumbDeveloperException {
if (n < 5) {
return(n + 10);
} else {
throw(new DumbDeveloperException());
}
}
}


程序清单5-16 RiskyPage.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD><TITLE>Risky JSP Page</TITLE></HEAD>
<BODY BGCOLOR="#FDF5E6">
<H2>Risky Calculations</H2>
<%@ page import="moreservlets.*" %>
<% int n = ((int)(10 * Math.random())); %>
<UL>
<LI>n: <%= n %>
<LI>dangerousComputation(n):
<%= DumbDeveloperException.dangerousComputation(n) %>
</UL>
</BODY></HTML>


程序清单5-17 DDE.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD><TITLE>Dumb</TITLE></HEAD>
<BODY BGCOLOR="#FDF5E6">
<H2>Dumb Developer</H2>
We're brain dead. Consider using our competitors.
</BODY></HTML>


程序清单5-18 web.xml(为异常指定错误页面的摘录)
<?xml version="1.0" encoding="ISO-8859-1"?>
<!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>
<!-- ... -->
<error-page>
<exception-type>
moreservlets.DumbDeveloperException
</exception-type>
<location>/DDE.jsp</location>
</error-page>
<!-- ... -->
</web-app>

9 提供安全性

利用web.xml中的相关元素为服务器的内建功能提供安全性。
9.1 指定验证的方法
使用login-confgi元素规定服务器应该怎样验证试图访问受保护页面的用户。它包含三个可能的子元素,分别是:auth-method、realm-name和form-login-config。login-config元素应该出现在web.xml部署描述符文件的结尾附近,紧跟在security-constraint元素之后。
l auth-method
login-config的这个子元素列出服务器将要使用的特定验证机制。有效值为BASIC、DIGEST、FORM和CLIENT-CERT。服务器只需要支持BASIC和FORM。
BASIC指出应该使用标准的HTTP验证,在此验证中服务器检查Authorization头。如果缺少这个头则返回一个401状态代码和一个WWW-Authenticate头。这导致客户机弹出一个用来填写Authorization头的对话框。此机制很少或不提供对攻击者的防范,这些攻击者在Internet连接上进行窥探(如通过在客户机的子网上执行一个信息包探测装置),因为用户名和口令是用简单的可逆base64编码发送的,他们很容易得手。所有兼容的服务器都需要支持BASIC验证。
DIGEST指出客户机应该利用加密Digest Authentication形式传输用户名和口令。这提供了比BASIC验证更高的防范网络截取得的安全性,但这种加密比SSL(HTTPS)所用的方法更容易破解。不过,此结论有时没有意义,因为当前很少有浏览器支持Digest Authentication,所以servlet容器不需要支持它。
FORM指出服务器应该检查保留的会话cookie并且把不具有它的用户重定向到一个指定的登陆页。此登陆页应该包含一个收集用户名和口令的常规HTML表单。在登陆之后,利用保留会话级的cookie跟踪用户。虽然很复杂,但FORM验证防范网络窥探并不比BASIC验证更安全,如果有必要可以在顶层安排诸如SSL或网络层安全(如IPSEC或VPN)等额外的保护。所有兼容的服务器都需要支持FORM验证。
CLIENT-CERT规定服务器必须使用HTTPS(SSL之上的HTTP)并利用用户的公开密钥证书(Pulic Key Certificat)对用户进行验证。这提供了防范网络截取的很强的安全性,但只有兼容J2EE的服务器需要支持它。
l realm-name
此元素只在auth-method为BASIC时使用。它指出浏览器在相应对话框标题使用的、并作为Authorization头组成部分的安全域的名称。
l form-login-config
此元素只在auth-method为FORM时适用。它指定两个页面,分别是:包含收集用户名及口令的HTML表单的页面(利用form-login-page子元素),用来指示验证失败的页面(利用form-error-page子元素)。由form-login-page给出的HTML表单必须具有一个j_security_check的ACTION属性、一个名为j_username的用户名文本字段以及一个名为j_password的口令字段。
例如,程序清单5-19指示服务器使用基于表单的验证。Web应用的顶层目录中的一个名为login.jsp的页面将收集用户名和口令,并且失败的登陆将由相同目录中名为login-error.jsp的页面报告。

程序清单5-19 web.xml(说明login-config的摘录)
<?xml version="1.0" encoding="ISO-8859-1"?>
<!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>
<!-- ... -->
<security-constraint> ... </security-constraint>
<login-config>
<auth-method> FORM </auth-method>
<form-login-config>
<form-login-page>/login.jsp</form-login-page>
<form-error-page>/login-error.jsp</form-error-page>
</form-login-config>
</login-config>
<!-- ... -->
</web-app>


9.2 限制对Web资源的访问
现在,可以指示服务器使用何种验证方法了。"了不起,"你说道,"除非我能指定一个来收到保护的 URL,否则没有多大用处。"没错。指出这些URL并说明他们应该得到何种保护正是security-constriaint元素的用途。此元素在 web.xml中应该出现在login-config的紧前面。它包含是个可能的子元素,分别是:web-resource-collection、 auth-constraint、user-data-constraint和display-name。下面各小节对它们进行介绍。
l web-resource-collection
此元素确定应该保护的资源。所有security-constraint元素都必须包含至少一个web-resource-collection项。此元素由一个给出任意标识名称的web-resource-name元素、一个确定应该保护的URL的url-pattern元素、一个指出此保护所适用的 HTTP命令(GET、POST等,缺省为所有方法)的http-method元素和一个提供资料的可选description元素组成。例如,下面的 Web-resource-collection项(在security-constratint元素内)指出Web应用的proprietary目录中所有文档应该受到保护。
<security-constraint>
<web-resource-coolection>
<web-resource-name>Proprietary</web-resource-name>
<url-pattern>/propritary/*</url-pattern>
</web-resource-coolection>
<!-- ... -->
</security-constraint>
重要的是应该注意到,url-pattern仅适用于直接访问这些资源的客户机。特别是,它不适合于通过MVC体系结构利用 RequestDispatcher来访问的页面,或者不适合于利用类似jsp:forward的手段来访问的页面。这种不匀称如果利用得当的话很有好处。例如,servlet可利用MVC体系结构查找数据,把它放到bean中,发送请求到从bean中提取数据的JSP页面并显示它。我们希望保证决不直接访问受保护的JSP页面,而只是通过建立该页面将使用的bean的servlet来访问它。url-pattern和auth-contraint元素可通过声明不允许任何用户直接访问JSP页面来提供这种保证。但是,这种不匀称的行为可能让开发人员放松警惕,使他们偶然对应受保护的资源提供不受限制的访问。
l auth-constraint
尽管web-resource-collention元素质出了哪些URL应该受到保护,但是auth-constraint元素却指出哪些用户应该具有受保护资源的访问权。此元素应该包含一个或多个标识具有访问权限的用户类别role- name元素,以及包含(可选)一个描述角色的description元素。例如,下面web.xml中的security-constraint元素部门规定只有指定为Administrator或Big Kahuna(或两者)的用户具有指定资源的访问权。
<security-constraint>
<web-resource-coolection> ... </web-resource-coolection>
<auth-constraint>
<role-name>administrator</role-name>
<role-name>kahuna</role-name>
</auth-constraint>
</security-constraint>
重要的是认识到,到此为止,这个过程的可移植部分结束了。服务器怎样确定哪些用户处于任何角色以及它怎样存放用户的口令,完全有赖于具体的系统。
例如,Tomcat使用install_dir/conf/tomcat-users.xml将用户名与角色名和口令相关联,正如下面例子中所示,它指出用户joe(口令bigshot)和jane(口令enaj)属于administrator和kahuna角色。
<tomcat-users>
<user name="joe" password="bigshot" roles="administrator,kahuna" />
<user name="jane" password="enaj" roles="kahuna" />
</tomcat-users>
l user-data-constraint
这个可选的元素指出在访问相关资源时使用任何传输层保护。它必须包含一个transport-guarantee子元素(合法值为NONE、 INTEGRAL或CONFIDENTIAL),并且可选地包含一个description元素。transport-guarantee为NONE值将对所用的通讯协议不加限制。INTEGRAL值表示数据必须以一种防止截取它的人阅读它的方式传送。虽然原理上(并且在未来的HTTP版本中),在 INTEGRAL和CONFIDENTIAL之间可能会有差别,但在当前实践中,他们都只是简单地要求用SSL。例如,下面指示服务器只允许对相关资源做 HTTPS连接:
<security-constraint>
<!-- ... -->
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
l display-name
security-constraint的这个很少使用的子元素给予可能由GUI工具使用的安全约束项一个名称。
9.3 分配角色名
迄今为止,讨论已经集中到完全由容器(服务器)处理的安全问题之上了。但servlet以及JSP页面也能够处理它们自己的安全问题。
例如,容器可能允许用户从bigwig或bigcheese角色访问一个显示主管人员额外紧贴的页面,但只允许bigwig用户修改此页面的参数。完成这种更细致的控制的一种常见方法是调用HttpServletRequset的isUserInRole方法,并据此修改访问。
Servlet的 security-role-ref子元素提供出现在服务器专用口令文件中的安全角色名的一个别名。例如,假如编写了一个调用 request.isUserInRole("boss")的servlet,但后来该servlet被用在了一个其口令文件调用角色manager而不是boss的服务器中。下面的程序段使该servlet能够使用这两个名称中的任何一个。
<servlet>
<!-- ... -->
<security-role-ref>
<role-name>boss</role-name> <!-- New alias -->
<role-link>manager</role-link> <!-- Real name -->
</security-role-ref>
</servlet>
也可以在web-app内利用security-role元素提供将出现在role-name元素中的所有安全角色的一个全局列表。分别地生命角色使高级IDE容易处理安全信息。

10 控制会话超时

如果某个会话在一定的时间内未被访问,服务器可把它扔掉以节约内存。可利用HttpSession的setMaxInactiveInterval方法直接设置个别会话对象的超时值。如果不采用这种方法,则缺省的超时值由具体的服务器决定。但可利用session-config和session- timeout元素来给出一个适用于所有服务器的明确的超时值。超时值的单位为分钟,因此,下面的例子设置缺省会话超时值为三个小时(180分钟)。
<session-config>
<session-timeout>180</session-timeout>
</session-config>

11 Web应用的文档化

越来越多的开发环境开始提供servlet和JSP的直接支持。例子有Borland Jbuilder Enterprise Edition、Macromedia UltraDev、Allaire JRun Studio(写此文时,已被Macromedia收购)以及IBM VisuaAge for Java等。
大量的web.xml元素不仅是为服务器设计的,而且还是为可视开发环境设计的。它们包括icon、display-name和discription等。
可回忆一下,在web.xml内以适当地次序声明web-app子元素很重要。不过,这里只要记住icon、display-name和description是web.xml的web-app元素内的前三个合法元素即可。
l icon
icon元素指出GUI工具可用来代表Web应用的一个和两个图像文件。可利用small-icon元素指定一幅16 x 16的GIF或JPEG图像,用large-icon元素指定一幅32 x 32的图像。下面举一个例子:
<icon>
<small-icon>/images/small-book.gif</small-icon>
<large-icon>/images/tome.jpg</large-icon>
</icon>
l display-name
display-name元素提供GUI工具可能会用来标记此Web应用的一个名称。下面是个例子。
<display-name>Rare Books</display-name>
l description
description元素提供解释性文本,如下所示:
<description>
This Web application represents the store developed for
rare-books.com, an online bookstore specializing in rare
and limited-edition books.
</description>

12 关联文件与MIME类型

服务器一般都具有一种让Web站点管理员将文件扩展名与媒体相关联的方法。例如,将会自动给予名为mom.jpg的文件一个image/jpeg的MIME 类型。但是,假如你的Web应用具有几个不寻常的文件,你希望保证它们在发送到客户机时分配为某种MIME类型。mime-mapping元素(具有 extension和mime-type子元素)可提供这种保证。例如,下面的代码指示服务器将application/x-fubar的MIME类型分配给所有以.foo结尾的文件。
<mime-mapping>
<extension>foo</extension>
<mime-type>application/x-fubar</mime-type>
</mime-mapping>
或许,你的Web应用希望重载(override)标准的映射。例如,下面的代码将告诉服务器在发送到客户机时指定.ps文件作为纯文本(text/plain)而不是作为PostScript(application/postscript)。
<mime-mapping>
<extension>ps</extension>
<mime-type>application/postscript</mime-type>
</mime-mapping>


13 定位TLD

JSP taglib元素具有一个必要的uri属性,它给出一个TLD(Tag Library Descriptor)文件相对于Web应用的根的位置。TLD文件的实际名称在发布新的标签库版本时可能会改变,但我们希望避免更改所有现有JSP页面。此外,可能还希望使用保持taglib元素的简练性的一个简短的uri。这就是部署描述符文件的taglib元素派用场的所在了。Taglib包含两个子元素:taglib-uri和taglib-location。taglib-uri元素应该与用于JSP taglib元素的uri属性的东西相匹配。Taglib-location元素给出TLD文件的实际位置。例如,假如你将文件chart-tags- 1.3beta.tld放在WebApp/WEB-INF/tlds中。现在,假如web.xml在web-app元素内包含下列内容。
<taglib>
<taglib-uri>/charts.tld</taglib-uri>
<taglib-location>
/WEB-INF/tlds/chart-tags-1.3beta.tld
</taglib-location>
</taglib>
给出这个说明后,JSP页面可通过下面的简化形式使用标签库。
<%@ taglib uri="/charts.tld" prefix="somePrefix" %>

14 指定应用事件监听程序

应用事件监听器程序是建立或修改servlet环境或会话对象时通知的类。它们是servlet规范的版本2.3中的新内容。这里只简单地说明用来向Web应用注册一个监听程序的web.xml的用法。
注册一个监听程序涉及在web.xml的web-app元素内放置一个listener元素。在listener元素内,listener-class元素列出监听程序的完整的限定类名,如下所示:
<listener>
<listener-class>package.ListenerClass</listener-class>
</listener>
虽然listener元素的结构很简单,但请不要忘记,必须正确地给出web-app元素内的子元素的次序。listener元素位于所有的servlet 元素之前以及所有filter-mapping元素之后。此外,因为应用生存期监听程序是serlvet规范的2.3版本中的新内容,所以必须使用 web.xml DTD的2.3版本,而不是2.2版本。
例如,程序清单5-20给出一个名为ContextReporter的简单的监听程序,只要Web应用的Servlet-Context建立(如装载Web应用)或消除(如服务器关闭)时,它就在标准输出上显示一条消息。程序清单5-21给出此监听程序注册所需要的web.xml文件的一部分。

程序清单5-20 ContextReporterjava
package moreservlets;

import javax.servlet.*;
import java.util.*;

/** Simple listener that prints a report on the standard output
* when the ServletContext is created or destroyed.
* <P>
* Taken from More Servlets and JavaServer Pages
* from Prentice Hall and Sun Microsystems Press,
* http://www.moreservlets.com/.
* © 2002 Marty Hall; may be freely used or adapted.
*/

public class ContextReporter implements ServletContextListener {
public void contextInitialized(ServletContextEvent event) {
System.out.println("Context created on " +
new Date() + ".");
}

public void contextDestroyed(ServletContextEvent event) {
System.out.println("Context destroyed on " +
new Date() + ".");
}
}

程序清单5-21 web.xml(声明一个监听程序的摘录)
<?xml version="1.0" encoding="ISO-8859-1"?>
<!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>
<!-- ... -->
<filter-mapping> … </filter-mapping>
<listener>
<listener-class>package.ListenerClass</listener-class>
</listener>
<servlet> ... </servlet>
<!-- ... -->
</web-app>

15 J2EE元素

本节描述用作J2EE环境组成部分的Web应用的web.xml元素。这里将提供一个简明的介绍,详细内容可以参阅http://java.sun.com/j2ee/j2ee-1_3-fr-spec.pdf的Java 2 Plantform Enterprise Edition版本1.3规范的第5章。
l distributable
distributable 元素指出,Web应用是以这样的方式编程的:即,支持集群的服务器可安全地在多个服务器上分布Web应用。例如,一个可分布的应用必须只使用 Serializable对象作为其HttpSession对象的属性,而且必须避免用实例变量(字段)来实现持续性。distributable元素直接出现在discription元素之后,并且不包含子元素或数据,它只是一个如下的标志。
<distributable />
l resource-env-ref
resource -env-ref元素声明一个与某个资源有关的管理对象。此元素由一个可选的description元素、一个resource-env-ref- name元素(一个相对于java:comp/env环境的JNDI名)以及一个resource-env-type元素(指定资源类型的完全限定的类),如下所示:
<resource-env-ref>
<resource-env-ref-name>
jms/StockQueue
</resource-env-ref-name>
<resource-env-ref-type>
javax.jms.Queue
</resource-env-ref-type>
</resource-env-ref>
l env-entry
env -entry元素声明Web应用的环境项。它由一个可选的description元素、一个env-entry-name元素(一个相对于java: comp/env环境JNDI名)、一个env-entry-value元素(项值)以及一个env-entry-type元素(java.lang程序包中一个类型的完全限定类名,java.lang.Boolean、java.lang.String等)组成。下面是一个例子:
<env-entry>
<env-entry-name>minAmout</env-entry-name>
<env-entry-value>100.00</env-entry-value>
<env-entry-type>minAmout</env-entry-type>
</env-entry>
l ejb-ref
ejb -ref元素声明对一个EJB的主目录的应用。它由一个可选的description元素、一个ejb-ref-name元素(相对于java: comp/env的EJB应用)、一个ejb-ref-type元素(bean的类型,Entity或Session)、一个home元素(bean的主目录接口的完全限定名)、一个remote元素(bean的远程接口的完全限定名)以及一个可选的ejb-link元素(当前bean链接的另一个 bean的名称)组成。
l ejb-local-ref
ejb-local-ref元素声明一个EJB的本地主目录的引用。除了用local-home代替home外,此元素具有与ejb-ref元素相同的属性并以相同的方式使用

posted @ 2008-04-06 17:42 阿伟 阅读(480) | 评论 (0)编辑 收藏

 web.xml配置的详细说明 2
4 禁止激活器servlet
对servlet或JSP页面建立定制URL的一个原因是,这样做可以注册从 init(servlet)或jspInit(JSP页面)方法中读取得初始化参数。但是,初始化参数只在是利用定制URL模式或注册名访问 servlet或JSP页面时可以使用,用缺省URL http://host/webAppPrefix/servlet/ServletName 访问时不能使用。因此,你可能会希望关闭缺省URL,这样就不会有人意外地调用初始化servlet了。这个过程有时称为禁止激活器servlet,因为多数服务器具有一个用缺省的servlet URL注册的标准servlet,并激活缺省的URL应用的实际servlet。
有两种禁止此缺省URL的主要方法:
l 在每个Web应用中重新映射/servlet/模式。
l 全局关闭激活器servlet。
重要的是应该注意到,虽然重新映射每个Web应用中的/servlet/模式比彻底禁止激活servlet所做的工作更多,但重新映射可以用一种完全可移植的方式来完成。相反,全局禁止激活器servlet完全是针对具体机器的,事实上有的服务器(如ServletExec)没有这样的选择。下面的讨论对每个Web应用重新映射/servlet/ URL模式的策略。后面提供在Tomcat中全局禁止激活器servlet的详细内容。
4.1 重新映射/servlet/URL模式
在一个特定的Web应用中禁止以http://host/webAppPrefix/servlet/ 开始的URL的处理非常简单。所需做的事情就是建立一个错误消息servlet,并使用前一节讨论的url-pattern元素将所有匹配请求转向该 servlet。只要简单地使用:
<url-pattern>/servlet/*</url-pattern>
作为servlet-mapping元素中的模式即可。
例如,程序清单5-5给出了将SorryServlet servlet(程序清单5-6)与所有以http://host/webAppPrefix/servlet/ 开头的URL相关联的部署描述符文件的一部分。
程序清单5-5 web.xml(说明JSP页命名的摘录)
<?xml version="1.0" encoding="ISO-8859-1"?>
<!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>Sorry</servlet-name>
<servlet-class>moreservlets.SorryServlet</servlet-class>
</servlet>
<!-- ... -->
<servlet-mapping>
<servlet-name> Sorry </servlet-name>
<url-pattern>/servlet/*</url-pattern>
</servlet-mapping>
<!-- ... -->
</web-app>

程序清单5-6 SorryServlet.java
package moreservlets;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

/** Simple servlet used to give error messages to
* users who try to access default servlet URLs
* (i.e., http://host/webAppPrefix/servlet/ServletName)
* in Web applications that have disabled this
* behavior.
* <P>
* Taken from More Servlets and JavaServer Pages
* from Prentice Hall and Sun Microsystems Press,
* http://www.moreservlets.com/.
* © 2002 Marty Hall; may be freely used or adapted.
*/

public class SorryServlet extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String title = "Invoker Servlet Disabled.";
out.println(ServletUtilities.headWithTitle(title) +
"<BODY BGCOLOR=\"#FDF5E6\">\n" +
"<H2>" + title + "</H2>\n" +
"Sorry, access to servlets by means of\n" +
"URLs that begin with\n" +
"http://host/webAppPrefix/servlet/\n" +
"has been disabled.\n" +
"</BODY></HTML>");
}

public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}


4.2 全局禁止激活器:Tomcat
Tomcat 4中用来关闭缺省URL的方法与Tomcat 3中所用的很不相同。下面介绍这两种方法:
1.禁止激活器: Tomcat 4
Tomcat 4用与前面相同的方法关闭激活器servlet,即利用web.xml中的url-mapping元素进行关闭。不同之处在于Tomcat使用了放在 install_dir/conf中的一个服务器专用的全局web.xml文件,而前面使用的是存放在每个Web应用的WEB-INF目录中的标准 web.xml文件。
因此,为了在Tomcat 4中关闭激活器servlet,只需在install_dir/conf/web.xml中简单地注释出/servlet/* URL映射项即可,如下所示:
<!--
<servlet-mapping>
<servlet-name>invoker</servlet-name>
<url-pattern>/servlet/*</url-pattern>
</servlet-mapping>
-->
再次提醒,应该注意这个项是位于存放在install_dir/conf的Tomcat专用的web.xml文件中的,此文件不是存放在每个Web应用的WEB-INF目录中的标准web.xml。
2.禁止激活器:Tomcat3
在Apache Tomcat的版本3中,通过在install_dir/conf/server.xml中注释出InvokerInterceptor项全局禁止缺省 servlet URL。例如,下面是禁止使用缺省servlet URL的server.xml文件的一部分。
<!--
<RequsetInterceptor
className="org.apache.tomcat.request.InvokerInterceptor"
debug="0" prefix="/servlet/" />
-->

5 初始化和预装载servlet与JSP页面
这里讨论控制servlet和JSP页面的启动行为的方法。特别是,说明了怎样分配初始化参数以及怎样更改服务器生存期中装载servlet和JSP页面的时刻。
5.1 分配servlet初始化参数
利用init-param元素向servlet提供初始化参数,init-param元素具有param-name和param-value子元素。例如,在下面的例子中,如果initServlet servlet是利用它的注册名(InitTest)访问的,它将能够从其方法中调用getServletConfig(). getInitParameter("param1")获得"Value 1",调用getServletConfig().getInitParameter("param2")获得"2"。
<servlet>
<servlet-name>InitTest</servlet-name>
<servlet-class>moreservlets.InitServlet</servlet-class>
<init-param>
<param-name>param1</param-name>
<param-value>value1</param-value>
</init-param>
<init-param>
<param-name>param2</param-name>
<param-value>2</param-value>
</init-param>
</servlet>
在涉及初始化参数时,有几点需要注意:
l 返回值。GetInitParameter的返回值总是一个String。因此,在前一个例子中,可对param2使用Integer.parseInt获得一个int。
l JSP中的初始化。JSP页面使用jspInit而不是init。JSP页面还需要使用jsp-file元素代替servlet-class。
l 缺省URL。初始化参数只在通过它们的注册名或与它们注册名相关的定制URL模式访问Servlet时可以使用。因此,在这个例子中,param1和 param2初始化参数将能够在使用URL http://host/webAppPrefix/servlet/InitTest 时可用,但在使用URL http://host/webAppPrefix/servlet/myPackage.InitServlet 时不能使用。
例如,程序清单5-7给出一个名为InitServlet的简单servlet,它使用init方法设置firstName和emailAddress字段。程序清单5-8给出分配名称InitTest给servlet的web.xml文件。
程序清单5-7 InitServlet.java
package moreservlets;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

/** Simple servlet used to illustrate servlet
* initialization parameters.
* <P>
* Taken from More Servlets and JavaServer Pages
* from Prentice Hall and Sun Microsystems Press,
* http://www.moreservlets.com/.
* © 2002 Marty Hall; may be freely used or adapted.
*/

public class InitServlet extends HttpServlet {
private String firstName, emailAddress;

public void init() {
ServletConfig config = getServletConfig();
firstName = config.getInitParameter("firstName");
emailAddress = config.getInitParameter("emailAddress");
}

public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String uri = request.getRequestURI();
out.println(ServletUtilities.headWithTitle("Init Servlet") +
"<BODY BGCOLOR=\"#FDF5E6\">\n" +
"<H2>Init Parameters:</H2>\n" +
"<UL>\n" +
"<LI>First name: " + firstName + "\n" +
"<LI>Email address: " + emailAddress + "\n" +
"</UL>\n" +
"</BODY></HTML>");
}
}


程序清单5-8 web.xml(说明初始化参数的摘录)
<?xml version="1.0" encoding="ISO-8859-1"?>
<!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>InitTest</servlet-name>
<servlet-class>moreservlets.InitServlet</servlet-class>
<init-param>
<param-name>firstName</param-name>
<param-value>Larry</param-value>
</init-param>
<init-param>
<param-name>emailAddress</param-name>
<param-value>Ellison@Microsoft.com</param-value>
</init-param>
</servlet>
<!-- ... -->
</web-app>

5.2 分配JSP初始化参数
给JSP页面提供初始化参数在三个方面不同于给servlet提供初始化参数。
1)使用jsp-file而不是servlet-class。因此,WEB-INF/web.xml文件的servlet元素如下所示:
<servlet>
<servlet-name>PageName</servlet-name>
<jsp-file>/RealPage.jsp</jsp-file>
<init-param>
<param-name>...</param-name>
<param-value>...</param-value>
</init-param>
...
</servlet>
2) 几乎总是分配一个明确的URL模式。对servlet,一般相应地使用以http://host/webAppPrefix/servlet/ 开始的缺省URL。只需记住,使用注册名而不是原名称即可。这对于JSP页面在技术上也是合法的。例如,在上面给出的例子中,可用URL http://host/webAppPrefix/servlet/PageName 访问RealPage.jsp的对初始化参数具有访问权的版本。但在用于JSP页面时,许多用户似乎不喜欢应用常规的servlet的URL。此外,如果 JSP页面位于服务器为其提供了目录清单的目录中(如,一个既没有index.html也没有index.jsp文件的目录),则用户可能会连接到此 JSP页面,单击它,从而意外地激活未初始化的页面。因此,好的办法是使用url-pattern(5.3节)将JSP页面的原URL与注册的 servlet名相关联。这样,客户机可使用JSP页面的普通名称,但仍然激活定制的版本。例如,给定来自项目1的servlet定义,可使用下面的 servlet-mapping定义:
<servlet-mapping>
<servlet-name>PageName</servlet-name>
<url-pattern>/RealPage.jsp</url-pattern>
</servlet-mapping>
3)JSP页使用jspInit而不是init。自动从JSP页面建立的servlet或许已经使用了inti方法。因此,使用JSP声明提供一个init方法是不合法的,必须制定jspInit方法。
为了说明初始化JSP页面的过程,程序清单5-9给出了一个名为InitPage.jsp的JSP页面,它包含一个jspInit方法且放置于 deployDemo Web应用层次结构的顶层。一般,http://host/deployDemo/InitPage.jsp 形式的URL将激活此页面的不具有初始化参数访问权的版本,从而将对firstName和emailAddress变量显示null。但是, web.xml文件(程序清单5-10)分配了一个注册名,然后将该注册名与URL模式/InitPage.jsp相关联。

程序清单5-9 InitPage.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD><TITLE>JSP Init Test</TITLE></HEAD>
<BODY BGCOLOR="#FDF5E6">
<H2>Init Parameters:</H2>
<UL>
<LI>First name: <%= firstName %>
<LI>Email address: <%= emailAddress %>
</UL>
</BODY></HTML>
<%!
private String firstName, emailAddress;

public void jspInit() {
ServletConfig config = getServletConfig();
firstName = config.getInitParameter("firstName");
emailAddress = config.getInitParameter("emailAddress");
}
%>


程序清单5-10 web.xml(说明JSP页面的init参数的摘录)
<?xml version="1.0" encoding="ISO-8859-1"?>
<!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>InitPage</servlet-name>
<jsp-file>/InitPage.jsp</jsp-file>
<init-param>
<param-name>firstName</param-name>
<param-value>Bill</param-value>
</init-param>
<init-param>
<param-name>emailAddress</param-name>
<param-value>gates@oracle.com</param-value>
</init-param>
</servlet>
<!-- ... -->
<servlet-mapping>
<servlet-name> InitPage</servlet-name>
<url-pattern>/InitPage.jsp</url-pattern>
</servlet-mapping>
<!-- ... -->
</web-app>


5.3 提供应用范围内的初始化参数
一般,对单个地servlet或JSP页面分配初始化参数。指定的servlet或JSP页面利用ServletConfig的getInitParameter方法读取这些参数。但是,在某些情形下,希望提供可由任意servlet或JSP页面借助ServletContext的getInitParameter方法读取的系统范围内的初始化参数。
可利用context-param元素声明这些系统范围内的初始化值。context-param元素应该包含param-name、param-value以及可选的description子元素,如下所示:
<context-param>
<param-name>support-email</param-name>
<param-value>blackhole@mycompany.com</param-value>
</context-param>
可回忆一下,为了保证可移植性,web.xml内的元素必须以正确的次序声明。但这里应该注意,context-param元素必须出现任意与文档有关的元素(icon、display-name或description)之后及filter、filter-mapping、listener或 servlet元素之前。
5.4 在服务器启动时装载servlet
假如servlet或JSP页面有一个要花很长时间执行的init (servlet)或jspInit(JSP)方法。例如,假如init或jspInit方法从某个数据库或ResourceBundle查找产量。这种情况下,在第一个客户机请求时装载servlet的缺省行为将对第一个客户机产生较长时间的延迟。因此,可利用servlet的load-on- startup元素规定服务器在第一次启动时装载servlet。下面是一个例子。
<servlet>
<servlet-name> … </servlet-name>
<servlet-class> … </servlet-class> <!-- Or jsp-file -->
<load-on-startup/>
</servlet>
可以为此元素体提供一个整数而不是使用一个空的load-on-startup。想法是服务器应该在装载较大数目的servlet或JSP页面之前装载较少数目的servlet或JSP页面。例如,下面的servlet项(放置在Web应用的WEB-INF目录下的web.xml文件中的web-app元素内)将指示服务器首先装载和初始化SearchServlet,然后装载和初始化由位于Web应用的result目录中的index.jsp文件产生的 servlet。
<servlet>
<servlet-name>Search</servlet-name>
<servlet-class>myPackage.SearchServlet</servlet-class> <!-- Or jsp-file -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>Results</servlet-name>
<servlet-class>/results/index.jsp</servlet-class> <!-- Or jsp-file -->
<load-on-startup>2</load-on-startup>
</servlet>

6 声明过滤器

servlet版本2.3引入了过滤器的概念。虽然所有支持servlet API版本2.3的服务器都支持过滤器,但为了使用与过滤器有关的元素,必须在web.xml中使用版本2.3的DTD。
过滤器可截取和修改进入一个servlet或JSP页面的请求或从一个servlet或JSP页面发出的相应。在执行一个servlet或JSP页面之前,必须执行第一个相关的过滤器的doFilter方法。在该过滤器对其FilterChain对象调用doFilter时,执行链中的下一个过滤器。如果没有其他过滤器,servlet或JSP页面被执行。过滤器具有对到来的ServletRequest对象的全部访问权,因此,它们可以查看客户机名、查找到来的cookie等。为了访问servlet或JSP页面的输出,过滤器可将响应对象包裹在一个替身对象(stand-in object)中,比方说把输出累加到一个缓冲区。在调用FilterChain对象的doFilter方法之后,过滤器可检查缓冲区,如有必要,就对它进行修改,然后传送到客户机。
例如,程序清单5-11帝国难以了一个简单的过滤器,只要访问相关的servlet或JSP页面,它就截取请求并在标准输出上打印一个报告(开发过程中在桌面系统上运行时,大多数服务器都可以使用这个过滤器)。

程序清单5-11 ReportFilter.java
package moreservlets;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;

/** Simple filter that prints a report on the standard output
* whenever the associated servlet or JSP page is accessed.
* <P>
* Taken from More Servlets and JavaServer Pages
* from Prentice Hall and Sun Microsystems Press,
* http://www.moreservlets.com/.
* © 2002 Marty Hall; may be freely used or adapted.
*/

public class ReportFilter implements Filter {
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain)
throws ServletException, IOException {
HttpServletRequest req = (HttpServletRequest)request;
System.out.println(req.getRemoteHost() +
" tried to access " +
req.getRequestURL() +
" on " + new Date() + ".");
chain.doFilter(request,response);
}

public void init(FilterConfig config)
throws ServletException {
}

public void destroy() {}
}

一旦建立了一个过滤器,可以在web.xml中利用filter元素以及filter-name(任意名称)、file-class(完全限定的类名)和(可选的)init-params子元素声明它。请注意,元素在web.xml的web-app元素中出现的次序不是任意的;允许服务器(但不是必需的)强制所需的次序,并且实际中有些服务器也是这样做的。但这里要注意,所有filter元素必须出现在任意filter-mapping元素之前, filter-mapping元素又必须出现在所有servlet或servlet-mapping元素之前。
例如,给定上述的ReportFilter类,可在web.xml中作出下面的filter声明。它把名称Reporter与实际的类ReportFilter(位于moreservlets程序包中)相关联。
<filter>
<filter-name>Reporter</filter-name>
<filter-class>moresevlets.ReportFilter</filter-class>
</filter>
一旦命名了一个过滤器,可利用filter-mapping元素把它与一个或多个servlet或JSP页面相关联。关于此项工作有两种选择。
首先,可使用filter-name和servlet-name子元素把此过滤器与一个特定的servlet名(此servlet名必须稍后在相同的 web.xml文件中使用servlet元素声明)关联。例如,下面的程序片断指示系统只要利用一个定制的URL访问名为SomeServletName 的servlet或JSP页面,就运行名为Reporter的过滤器。
<filter-mapping>
<filter-name>Reporter</filter-name>
<servlet-name>SomeServletName</servlet-name>
</filter-mapping>
其次,可利用filter-name和url-pattern子元素将过滤器与一组servlet、JSP页面或静态内容相关联。例如,相面的程序片段指示系统只要访问Web应用中的任意URL,就运行名为Reporter的过滤器。
<filter-mapping>
<filter-name>Reporter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
例如,程序清单5-12给出了将ReportFilter过滤器与名为PageName的servlet相关联的web.xml文件的一部分。名字 PageName依次又与一个名为TestPage.jsp的JSP页面以及以模式http: //host/webAppPrefix/UrlTest2/ 开头的URL相关联。TestPage.jsp的源代码已经JSP页面命名的谈论在前面的3节"分配名称和定制的URL"中给出。事实上,程序清单5- 12中的servlet和servlet-name项从该节原封不动地拿过来的。给定这些web.xml项,可看到下面的标准输出形式的调试报告(换行是为了容易阅读)。
audit.irs.gov tried to access
http://mycompany.com/deployDemo/UrlTest2/business/tax-plan.html
on Tue Dec 25 13:12:29 EDT 2001.

程序清单5-12 Web.xml(说明filter用法的摘录)
<?xml version="1.0" encoding="ISO-8859-1"?>
<!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>
<filter>
<filter-name>Reporter</filter-name>
<filter-class>moresevlets.ReportFilter</filter-class>
</filter>
<!-- ... -->
<filter-mapping>
<filter-name>Reporter</filter-name>
<servlet-name>PageName</servlet-name>
</filter-mapping>
<!-- ... -->
<servlet>
<servlet-name>PageName</servlet-name>
<jsp-file>/RealPage.jsp</jsp-file>
</servlet>
<!-- ... -->
<servlet-mapping>
<servlet-name> PageName </servlet-name>
<url-pattern>/UrlTest2/*</url-pattern>
</servlet-mapping>
<!-- ... -->
</web-app>


posted @ 2008-04-06 17:39 阿伟 阅读(221) | 评论 (0)编辑 收藏
仅列出标题
共8页: 上一页 1 2 3 4 5 6 7 8 下一页