??xml version="1.0" encoding="utf-8" standalone="yes"?>亚洲欧洲日产v特级毛片,国产亚洲一区二区三区在线不卡,亚洲精品第一国产综合精品99http://www2.blogjava.net/kapok/category/984.html垃圾?嘿嘿Q我藏的q么׃们还能找到啊Q真牛!zh-cnWed, 28 Feb 2007 03:26:04 GMTWed, 28 Feb 2007 03:26:04 GMT60struts源代码阅读(struts 初始化)http://www.tkk7.com/kapok/archive/2005/05/02/3987.htmlW笨W笨Mon, 02 May 2005 01:28:00 GMThttp://www.tkk7.com/kapok/archive/2005/05/02/3987.htmlhttp://www.tkk7.com/kapok/comments/3987.htmlhttp://www.tkk7.com/kapok/archive/2005/05/02/3987.html#Feedback0http://www.tkk7.com/kapok/comments/commentRss/3987.htmlhttp://www.tkk7.com/kapok/services/trackbacks/3987.html    
    同样调用如下Ҏ(gu)Q?BR>    initModuleMessageResources(moduleConfig);
    initModuleDataSources(moduleConfig);
    initModulePlugIns(moduleConfig);
    moduleConfig.freeze();

    最后调用destroyConfigDigester()释放内存?/FONT>

W笨 2005-05-02 09:28 发表评论
]]>
Struts Config的DTDhttp://www.tkk7.com/kapok/archive/2005/05/01/3984.htmlW笨W笨Sun, 01 May 2005 14:27:00 GMThttp://www.tkk7.com/kapok/archive/2005/05/01/3984.htmlhttp://www.tkk7.com/kapok/comments/3984.htmlhttp://www.tkk7.com/kapok/archive/2005/05/01/3984.html#Feedback0http://www.tkk7.com/kapok/comments/commentRss/3984.htmlhttp://www.tkk7.com/kapok/services/trackbacks/3984.html阅读全文

W笨 2005-05-01 22:27 发表评论
]]>
Struts下的MapFormhttp://www.tkk7.com/kapok/archive/2005/04/30/3966.htmlW笨W笨Sat, 30 Apr 2005 05:55:00 GMThttp://www.tkk7.com/kapok/archive/2005/04/30/3966.htmlhttp://www.tkk7.com/kapok/comments/3966.htmlhttp://www.tkk7.com/kapok/archive/2005/04/30/3966.html#Feedback0http://www.tkk7.com/kapok/comments/commentRss/3966.htmlhttp://www.tkk7.com/kapok/services/trackbacks/3966.htmlhttp://www.jetmaven.net/documents/j_mapformInStruts.php

我们知道Struts的ActionForm一直被大家视ؓ(f)~陷Q觉得多余,但我个h认ؓ(f)ActionFormq是有它存在的理由。我们徏立ActionForm通常和W(xu)eb面的Form元素l定Q用于数据的攉和校验等。ActionForm的属性必d明,然后才能用于和W(xu)eb面中,我们l常遇到一些属性不需要全部声明,如查询条件等Q而且ActionForm的属性太多时理也是个问题,再另一些情况下Q如采购单,使用master/detail方式QActionForm的创建变的困难,好多属性均不确定,如采购明lؓ(f)Ҏ(gu)记录Q这样处理比较麻烦,在这文章中Q我们将向你讲述如何使用Struts的MapForm机制实现q样的功能?BR>我们希望ActionForm能够接收Map数据Q这h们的理变的容易多啦,在Struts 1.1以后版本q样的处理变得非常简单,我们在ActionForm中声明一个Map变量?/P>


public class MapForm extends ActionForm
{
private Map map = null;
public void setMap(Map map) {
this.map = map;
}
public Map getMap() {
return this.map;
}
同时增加一个属性方法去讄和获取Map中的数据?BR>public void setAttribute(String attributeKey, Object attributeValue)
{
getMap().put(attributeKey, attributeValue);
}
public Object getAttribute(String attributeKey)
{
Object keyValue = getMap().get(attributeKey);
return keyValue;
}
q样我们在jsp面中,我们可以用Struts的标{接触这些Map数据?BR><html:text property="attribute(key)"/>
q样q些数据可l护啦,q对查询条g较多的情况非帔R用Q你无需在维护这些查询信息在各个面的过渡,Struts帮?zhn)完成了一切?/P>

下面我们q一下如何用MapForml织master/detail方式的数据,我们以一个订单做为样例?/P>

1 首先建立一个Form对象Q承MapFormQ同时声明主要的属性,如订单编码、定购h{?BR>public class OrderForm extends MapForm
{
private Integer id;
private String orderMan;

2 我们拟定以Map方式保存采购信息,同一采购w用统一前缀Q可选择行编码,如row123_ productCode,row123_ productId,row123_ amount{,q样某一采购信息将被输入到Map中,不同的采购项的前~不一P前缀由row+行编码组成,同时~写可获取行~码的函敎ͼq样可取得某一采购的所有信息,参数rowPrefix为某一字符Ԍ如“row”、“item”等Q不包含数字~码信息Q同时你可以~写C(j)omparatorQ进行排序?
public Collection getRowIdList(String rowPrefix)
{
if (map.isEmpty()) return new ArrayList();
Collection allRowId = new TreeSet(new RowIdComparator(rowPrefix));
Iterator allKey = map.keySet().iterator();
while (allKey.hasNext())
{
String key = (String) allKey.next();
if (key.indexOf(rowPrefix) != -1)
{
key = key.substring(0, key.indexOf('_'));
allRowId.add(key);
}
}
return allRowId;
}

3 在jsp面中你可以通过jstlQ就可以完成采购明细的显C?BR><c:forEach var="rowId" items="${OrderForm.getRowIdList('row')}">
<tr align="center" id="${rowId}" onclick="clickRow()">
<td ><html:text property="attribute(${rowId}_productCode)" size="8" onkeydown="fillProductInfoWithKeyDown('${rowId}',this)" /><html:hidden property="attribute(${rowId}_productId)"/> &nbsp;<a href="javascript:selectproduct('${rowId}')">选择</a></td>
<td ><html:text property="attribute(${rowId}_productQty)" size="8" /> </td>
<td ><html:text property="attribute(${rowId}_productPrice)" size="8" /></td>
<td ><html:text property="attribute(${rowId}_productName)" readonly="true" size="16" /></td>
<td ><html:text property="attribute(${rowId}_productPackaging)" readonly="true" size="12"/></td>
<td ><html:text property="attribute(${rowId}_productUnit)" size="6" readonly="true" /></td>
</tr>
</c:forEach>

4 q样Struts帮你完成了所有的信息处理Q你需要完成你的保存就可以啦?/P>

提示QMap中的数据值默认都是Stringcd的,如果你想转换成你惌的类型,可以在你的Form~写一个工h法,完成cd的{换?BR>public Map getTypedMap() {
Map map = this.getMap();
String keyString = (String) map.get("key");
Integer keyInteger = new Integer(keyString);
map.put("key",keyInteger);
return map;
}

ȝQMapForm各个功能很少被开发h员用,如果使用得当Q功能非常强大。ActionForm个h认ؓ(f)q是个设计~陷Q结合BeanUtils{工具包Q{换和信息处理非常方便?/P>

W笨 2005-04-30 13:55 发表评论
]]>
Digester学习(fn)W记(?http://www.tkk7.com/kapok/archive/2005/04/15/3300.htmlW笨W笨Fri, 15 Apr 2005 01:28:00 GMThttp://www.tkk7.com/kapok/archive/2005/04/15/3300.htmlhttp://www.tkk7.com/kapok/comments/3300.htmlhttp://www.tkk7.com/kapok/archive/2005/04/15/3300.html#Feedback0http://www.tkk7.com/kapok/comments/commentRss/3300.htmlhttp://www.tkk7.com/kapok/services/trackbacks/3300.html

Digester学习(fn)W记(?

http://www.infomall.cn/cgi-bin/mallgate/20040310/http://hedong.3322.org/archives/000336.html

  Z于理解,笔记的内容l构作了一些调整?/P>

对象?/H4>  对digester技术最普通的应用Q是用来动态创Z个由Java对象构成的树(wi)l构Q各对象的属性以?qing)对象间的关p,ZXML文档的内Ҏ(gu)讄(XML文档是一|(wi))。ؓ(f)实现q种应用QDigester提供了一个对象栈Q以供在相关的模板识别后被激zȝ处理规则操作。此栈的基本操作包括Q?BR>
  1. clear(),清空栈的内容
  2. peek(),q回Ҏ(gu)对象的引用
  3. pop(),栈对象弹出ƈq回
  4. push(),一个新的对象压入栈?/LI>

  用栈的原因,是当识别出一个XML元素的“开始”时Q将相关对象生成q压入栈Ӟq个对象在处理该元素的子元素的过E中一直在栈中Q当所有子元素都处理完后,解析器遇到这个元素的“结束”时Q则弹出此对象,q进行相关的处理?BR>  如何描述对象间的关系呢?栈的对象做ؓ(f)一个参敎ͼ传递给W二栈顶(卛_于栈对象入栈的那个对象Q在栈顶对象的下?的一个方法,可以简单地建立起一U“父子关pZ,从而可以简单地建立?Q?的关p?W二栈顶对象与栈对象之??QN的关p?W二栈顶对象不动QNơ压栈顶Ҏ(gu)对?.
  如果取得生成的第一个对象呢Q可以让parse()Ҏ(gu)q回Q或者在调用parse()Ҏ(gu)前,先行压入一个对象,在parse()Ҏ(gu)l束后弹个对象,则其子对象即为我们想要的W一个对象?BR>

日志(logging)


  日志是一个调试Digester规则集的非常重要的工P它可以记录非怸富的信息Q因它在使用Digester之前有必要了解日志是如何工作的?BR>  Digester使用Jakarta Commons LoggingQ这个模块ƈ不是具体的日志实玎ͼ而只是一个可讄的接口。可以设|它?yu)各U日志信息传递它自n带的基本记录器,或者传递给其它的更复杂的日志工兗具体请参考commons logging的文档,?A >Jakarta Commons Logging学习(fn)W记
  Digester主要使用两个记录器:(x)
  1. SAX相关的信息,被送往org.apache.commons.digester.Digester.sax记录器,记录了Digester收到的SAX的事件的信息?
  2. 其它的所有信息,都被送往org.apache.commons.digester.Digester记录器,q个记录器在调试Digester时打开而在产品中常其关闭

  假定用commons logging自带的基本日志工Pq以DEBUGU别记录Digester调试信息以及(qing)INFOU别记录SAX事g信息Q则对logging的配|文件设|如下:(x)
org.apache.commons.logging.Log=org.apache.commons.logging.impl.SimpleLog org.apache.commons.logging.simplelog.log.org.apache.commons.digester.Digester=debug org.apache.commons.logging.simplelog.log.org.apache.commons.digester.Digester.sax=info

Digester包中的例?/H4>
***********Example.xml**********
<address-book>
  <person id="1" category="acquaintance" try="would be ignored">
    <name>Gonzo</name>
    <email type="business">gonzo@muppets.com</email>
    <gender result="the whole tag would be ignored">male</gender>
  </person>
  <person id="2" category="rolemodel">
    <name>Kermit</name>
    <email type="business">kermit@muppets.com</email>
    <email type="home">kermie@acme.com</email>
  </person>
</address-book>**********Person.java************
import java.util.HashMap;
import java.util.Iterator;
public class Person {
  private int id;
  private String category;
  private String name;
  private HashMap emails = new HashMap();
  //下面的两个方法的名字中set以后的部分,?lt;person>的属性名字对映。当从xml文g中识别出<person>的属性时Q如果有要求(卌用过addSetPropertiesҎ(gu)),Digester?x)依据这U对映关p自动调用相应的Ҏ(gu)?BR>  public void setId(int id) {
      this.id = id;
  }
  public void setCategory(String category) {
      this.category = category;
  }
  //对name而言Q因为其值来?lt;name>标签的内容而非属性|需要用addCallMethod指定识别<name>后的要调用此Ҏ(gu)(惌动调用也要可以,需要addBeanPropertySetterQ参见第下一个例??BR>  public void setName(String name) {
      this.name = name;
  }
  //同nameQ此时还要一一指定addEmail的参数值的来源?BR>  public void addEmail(String type, String address) {
      emails.put(type, address);
  }
  public void print() {
      System.out.println("Person #" + id);
      System.out.println("  category=" + category);
      System.out.println("  name=" + name);
      for(Iterator i = emails.keySet().iterator(); i.hasNext(); ) {
          String type = (String) i.next();
          String address = (String) emails.get(type);
          System.out.println("  email (type " + type + ") : " + address);
      }
  }
}
**********AddressBook.java***********
import java.util.LinkedList;
import java.util.Iterator;
public class AddressBook {
    LinkedList people = new LinkedList();
    public void addPerson(Person p) {
        people.addLast(p);
    }
    public void print() {
        System.out.println("Address book has " + people.size() + " entries");

        for(Iterator i = people.iterator(); i.hasNext(); ) {
            Person p = (Person) i.next();
            p.print();
        }
    }
}
************AddressBookDigester*********
import org.apache.commons.digester.Digester;
/**
 * Usage: java Example1 example.xml
 */
public class AddressBookDigester {
    public static void main(String[] args) {
        if (args.length != 1) {
            usage();
            System.exit(-1);
        }
        String filename = args[0];
        // 创徏一个Digester实例
        Digester d = new Digester();
        // 创徏AddressBook实例Qƈ其压入栈顶?BR>        AddressBook book = new AddressBook();
        d.push(book);
        // 增加规则
        addRules(d);
        // 处理输入的xml文g
        try {
            java.io.File srcfile = new java.io.File(filename);
            d.parse(srcfile);
        }
        catch(java.io.IOException ioe) {
            System.out.println("Error reading input file:" + ioe.getMessage());
            System.exit(-1);
        }
        catch(org.xml.sax.SAXException se) {
            System.out.println("Error parsing input file:" + se.getMessage());
            System.exit(-1);
        }
        
        
        // 解析出的地址数据打印出来
        book.print();
    }
    
    private static void addRules(Digester d) {
        // 当遇?lt;person>Ӟ创徏cPerson的一个实例,q将其压入栈?BR>        d.addObjectCreate("address-book/person", Person.class);
        
        // ?lt;person>标签的属?attribute)与栈Personcd象的属?property)讄Ҏ(gu)Ҏ(gu)各自的名字进行映,(例如Q将标签属性id与属性设|方法setIdq行映射Q将标签属性category与属性设|方法setCategoryq行映射)Q然后将属性的g参数传递给执行相应的方法?BR>        // 如果某标{ֱ性没法通过名字扑ֈ相应的属性设|方法,则此标签属性被忽略(如example.xml中第一?lt;person>的try属??BR>        d.addSetProperties("address-book/person");

        // 调用W二栈顶对象(AddressBook实例)的addPersonҎ(gu)Q以栈对?Person实例)的对象ؓ(f)参数
        d.addSetNext("address-book/person", "addPerson");        
        
        // 当遇?lt;person>的子元素<name>Ӟ调用栈顶对象(Person实例)的setNameҎ(gu)?BR>        // 此处addCallMethodҎ(gu)的第一参数是规则,W二个参数是Ҏ(gu)的名字,W三个是参数的数??Ӟ表示只有一个参敎ͼ且参数的值是元素的内?
        d.addCallMethod("address-book/person/name", "setName", 0);
        
        // 当遇?lt;person>的子元素<email>Ӟ调用栈顶对象(Person实例)的addEmailҎ(gu),addEmailҎ(gu)有两个参敎ͼ取值分别来?lt;email>的属性type的值和<email>本n的内宏V?BR>        // 此处addCallParamҎ(gu)的第一参数是规则,W二个参数是指明被调用方?addEmail)参数的序PW三个是参数为字W串时指属性的名字)
        d.addCallMethod("address-book/person/email", "addEmail", 2);
        d.addCallParam("address-book/person/email", 0, "type");
        d.addCallParam("address-book/person/email", 1);
    }

    private static void usage() {
        System.out.println("Usage: java Example1 example.xml");
    }
}

q行l果如下Q运行时可能需要xml-crimson,一个源sun的XML解析器,可到http://xml.apache.org/crimson/下蝲)
Address book has 2 entries
Person #1
  category=acquaintance
  name=Gonzo
  email (type business) : gonzo@muppets.com
Person #2
  category=rolemodel
  name=Kermit
  email (type business) : kermit@muppets.com
  email (type home) : kermie@acme.com

Posted by Hilton at October 26, 2003 11:46 PM | TrackBack


W笨 2005-04-15 09:28 发表评论
]]>Digesterhttp://www.tkk7.com/kapok/archive/2005/04/14/3294.htmlW笨W笨Thu, 14 Apr 2005 10:07:00 GMThttp://www.tkk7.com/kapok/archive/2005/04/14/3294.htmlhttp://www.tkk7.com/kapok/comments/3294.htmlhttp://www.tkk7.com/kapok/archive/2005/04/14/3294.html#Feedback0http://www.tkk7.com/kapok/comments/commentRss/3294.htmlhttp://www.tkk7.com/kapok/services/trackbacks/3294.htmlhttp://www.infomall.cn/cgi-bin/mallgate/20040310/http://hedong.3322.org/archives/000337.html
总觉得,Digester不仅仅能作配|文件解析,而且可以作得更多?A name=more>

配置属?/H4>  Digester用来解析应用pȝ的配|文Ӟ其本w也有很可配|的属性?BR>
属?/TD> 描述
classLoader 指定c装载器(class loader)。ObjectCreateRule ?FactoryCreateRule两个规则中,需要动态加载一些类Q如那些盛放XML解析出来的数据的javaBean{)Q装载器可以在次指定。如果不指定Q对q此cȝ加蝲会(x)利用U程上下文中的加载器Q当useContextClassLoadergؓ(f)真时Q或利用加蝲Digester的那个加载器?/TD>
errorHandler 指定 SAX ErrorHandlerQ以在出现此c错误时调用。默认情况下QQ何解析错误都?x)被记入日志QDigest?x)l进行解析?/TD>
namespaceAware 一个布?yu)(dng)|为真时对XML文g的解析时?x)考虑元素的域名空_(d)如不同的域名I间的同名元素会(x)视ؓ(f)不同的元素)
ruleNamespaceURI 指定后箋加入的规则所属的命名I间Q如果此gؓ(f)null,则加入的规则不与M命名I间相联pR?/TD>
rules 讑֮规则模板与XML元素的匹配处理程序。由于这个匹配程序是插g式的Q所以匹配工作的完成可以用用户定义的匚wE序未完成。默认情况下Q用Digester提供的匹配器?/TD>
useContextClassLoader 一个布?yu)(dng)|为真时FactoryCreateRule ?ObjectCreateRule 两个规则中对cȝ装蝲会(x)采用当前U程上下文中指定的加载器。默认情况下Q对cȝ动态加载会(x)利用加蝲Digester的那个装载器?/TD>
validating 一个布?yu)(dng)|为真时解析器?x)根据DTD内容对XML文档q行合法性检查,默认值是假,解析器只是检查XML是否格式良好(well formed).

  除了上述属性外Q还可以注册一个本地DTDQ以供DOCTYPE声明引用。这L(fng)注册告诉XML解析器,当遇到DOCTYPE声明Ӟ应用刚注册的DTD的内容,而不是DOCTYPE声明中的标识W?identifier)?BR>  例如QStruect框架控制器中Q用下q的注册Q告诉Structs使用一个本地的DTD中的相关内容来处理Structs配置文gQ这样可以适用于那些没有连接到互联|的应用环境Q而在q到互联|的环境中可以加快运行速度(因ؓ(f)它避免了通过|络d相关的资??
digester.register("-//Apache Software Foundation//DTD Struts Configuration 1.0//EN",url.toString());

规则集打?/H4>
  通常情况下,一个规则被创徏后,接着便注册,然后{在event时被调用Q这些规则集很难为其它应用程序直接复用。一个解x法是所有规则都攑֜一个类中,此由q些规则可以很简单地被装载然后被注册使用。RuleSet接口是Z而设计,一般是通过扩展RuleSetBasecL开发规则集cR如例:(x)
public class MyRuleSet extends RuleSetBase {
  public MyRuleSet() {
    this("");
  }
  public MyRuleSet(String prefix) {
    super();
    this.prefix = prefix;
    this.namespaceURI = "http://www.mycompany.com/MyNamespace";
  }
  protected String prefix = null;
  public void addRuleInstances(Digester digester) {
    digester.addObjectCreate(prefix + "foo/bar",
      "com.mycompany.MyFoo");
    digester.addSetProperties(prefix + "foo/bar");
  }
}
可以q样使用q个规则?
Digester digester = new Digester();
... 一些配|Digester ...
digester.addRuleSet(new MyRuleSet("baz/"));

带命名空间的XML解析


  q种情况下,使用Digester的步骤ؓ(f)Q?
  1. 在Digester初始化部分,指明要考虑命名I间?
    digester.setNamespaceAware(true);

  2. 指明一些规则的命名I间,?
    digester.setRuleNamespaceURI("http://www.mycompany.com/MyNamespace");

  3. 接下来定义一些与此命名空间有关的规则Q此时可以省却前~Q如
    digester.addObjectCreate("foo/bar", "com.mycompany.MyFoo");
    digester.addSetProperties("foo/bar");

  4. 对其它命名空_(d)重复前面??/LI>

  另外Q在指明要digester考虑命名I间之后Q在定义匚w模板Ӟ可以命名空间别名加“:(x)”作为元素名U的一部分使用。这与无命名I间时是一致的?BR>

开发定制的匚w处理q程


  通过实现 org.apache.commons.digester.Rules接口或扩展org.apache.commons.digester.RulesBasecL辑ֈ定制匚wq程的目的?BR>  Digester提供ExtendedBaseRules来扩展了匚w模板的定义,引入了特D通配字符?和*以及(qing)!,提供RegexRules来支持以正则式的语法定义匚w模板Q提供WithDefaultsRulesWrapper来支持默认规则(卛_它规则都不匹配时的处理规则)?BR>

一些认?/H4>
  通过看说明材料,其在学?fn)Digester包中的Catalog例子以后Q有一些认识:(x)
  1、由于xml对属性名字的定义要求Q与Java中对Ҏ(gu)名字的定义要求不一_(d)D出现不能自动映射的情况,如year-made标签属性,׃可能有方法setYear-made;
  2、对于根元素Q与其子元素建立联系Q有几种办法Q一U是先生成根元素实例Q压入栈Q然后解析,调用方法规则徏立联p;另一U是解析的过E中W一个创建它Q然后用getRoot的方法得到?BR>  3、如果某对象cL造都要参敎ͼ则此旉要扩展AbstractObjectCreationFactorycMؓ(f)q种对象建立一个Factory,在这个Factory中取得初始化参数值然后再创徏一个对象实例?BR>  4、设有某个标{,要想自动用该标签子元素的内容填充该标{֯应的对象的属性,则需要用digester.setRules(new ExtendedBaseRules()),然后addRules(),然后再调用addBeanPropertySetter("bala/lala/?");q行规则定义,注意此模板中有通配W?BR>  5、如果对象的属性是整型Q则Digester自动xml文g中字W串D{换ؓ(f)整型?BR>  6、在指明要digester考虑命名I间之后Q如果不?x)引h义,完全可以忽略命名I间的存在,除非你要针对特定的命名空间进行特定的处理?BR>



W笨 2005-04-14 18:07 发表评论
]]>Struts-menu源码分析(转脓(chung))http://www.tkk7.com/kapok/archive/2005/04/14/3293.htmlW笨W笨Thu, 14 Apr 2005 10:00:00 GMThttp://www.tkk7.com/kapok/archive/2005/04/14/3293.htmlhttp://www.tkk7.com/kapok/comments/3293.htmlhttp://www.tkk7.com/kapok/archive/2005/04/14/3293.html#Feedback1http://www.tkk7.com/kapok/comments/commentRss/3293.htmlhttp://www.tkk7.com/kapok/services/trackbacks/3293.htmlhttp://dev.csdn.net/article/43/43114.shtm

  好的代码读v来让人如饮醍醐,d以后清气爽。如果你x高你的编E水qI如果你想提高你的设计能力Q如果你也想成ؓ(f)大师Q那么就去阅M码吧。以本h十几q来的编E经验,阅读代码能让你得到的比阅L?那怕是大师的文?得到的更多。优U而且实用的代码有很多Q比如Junit,比如JiveQ比如petStoreQ甚xtomcat的Example、Log4j的Example?BR>
       Struts-Menu也来自一位大师的作品Q?Matt Raible。有很多优秀的作品,比如使用struts和hibernate的struts-resume。官方网站是http://raibledesigns.com/wiki/Wiki.jsp?page=Main。Struts-Menu的最新版本是2.1。功能是使用struts技术,构徏?wi)Ş菜单。应该说是一个非常实用的技术,极大的方便了q大的开发h员。与此同Ӟ个h认ؓ(f)它的作用q不止于些。比如,同时它也是一个?/FONT>Maven?/FONT>velocity的一个很好的例子?BR>

        首先Q我们去看一下它的效果?/FONT>http://www.raibledesigns.com/struts-menu/。可以看刎ͼ如此丰富多彩的菜单效果,都是在演CZ个配|文仉的内宏V这是一个非常好的数据与表示相分ȝ实现。我们打开它的源码来看。首先看一下它的包?/FONT>

共有五个包,其中menu自然是完成数据组l功能,是核心之一Qdisplayer是显C方式包Q完成数据显C大部分功能。也是核心之一。taglib意义明显。example自然是一些example。util是读取资源文件的包。因些,我们重点研究的包只有三个menu,displayer和taglib?/FONT>

首先我们来看menu包的cd

首先是MenuPlugInq个cR这个类的功能很明显Q就是一个struts的plug-in。可以看刎ͼ它只有一个参数menuConfigQ就是menu的配|文件\径。果Ӟ在struts-conf文g中有q么一D?/FONT>

  <!-- ========== Plug Ins Configuration ================================== -->

<plug-in className="net.sf.navigator.menu.MenuPlugIn">
<set-property property="menuConfig" value="/WEB-INF/menu-config.xml"/>
</plug-in>

 

说明配置文g来自?WEB-INF/menu-config.xmlQ当Ӟ我们可以扑ֈ相应路径下找到这个文件。如果你以前没有做过struts的plug-inQ现在该知道怎么做了吧,p么简单。通过阅读初始化函敎ͼ知道它的功能是调用MenuRepository来徏立菜单。因此。我们知道MenuRepository必然是一个组l管理管理菜单的l织cR?/FONT>

public void init(ActionServlet servlet, ModuleConfig config)
		throws ServletException {
		if (log.isDebugEnabled()) {
			log.debug("Starting struts-menu initialization");
		}

		this.servlet = servlet;
		repository = new MenuRepository();
		repository.setLoadParam(menuConfig);
		repository.setServlet(servlet);

		try {
			repository.load();
			servlet.getServletContext().setAttribute(
				MenuRepository.MENU_REPOSITORY_KEY,
				repository);

			if (log.isDebugEnabled()) {
				log.debug("struts-menu initialization successfull");
			}
		} catch (LoadableResourceException lre) {
			throw new ServletException(
				"Failure initializing struts-menu: " + lre.getMessage());
		}
	}

打开MenuRepositoryc,我们可以看到q个cM很简单,不过已经有少可以学习(fn)的了。首先是FastHashMapQ可以看刎ͼq个c里有三个FastHashMap。顾名思议Q是快速HashMap了,再看一下,它来自org.apache.commons.collections.FastHashMap;。看到org.apache.commonsq个著名的包?如果你以前从没用过它,那么你花上一D|间去研究使用它,我保证物有所倹{?/FONT>

protected FastHashMap menus = new FastHashMap();
protected FastHashMap displayers = new FastHashMap();
protected FastHashMap templates = new FastHashMap();

接下来我们看到l(f)og的定义。对了,logQ调试的核心之一。而下面这一句则是commons log的最常用的用方法。快快让你的E序使用上commons log吧,W一Q它功能强大Q第二,它用简单,是q么单?/FONT>

private Log log = LogFactory.getLog(getClass().getName());

下面看一个的函数

 protected Digester initDigester() {
        Digester digester = new Digester();
        digester.setClassLoader(Thread.currentThread().getContextClassLoader());
        digester.push(this);

        //digester.setDebug(getDebug());
        // 1
        digester.addObjectCreate("MenuConfig/Menus/Menu",
            "net.sf.navigator.menu.MenuComponent", "type");
        digester.addSetProperties("MenuConfig/Menus/Menu");
        digester.addSetNext("MenuConfig/Menus/Menu", "addMenu");

        // 2
        digester.addObjectCreate("MenuConfig/Menus/Menu/Item",
            "net.sf.navigator.menu.MenuComponent", "type");
        digester.addSetProperties("MenuConfig/Menus/Menu/Item");
        digester.addSetNext("MenuConfig/Menus/Menu/Item", "addMenuComponent",
            "net.sf.navigator.menu.MenuComponent");

        // 3        
        digester.addObjectCreate("MenuConfig/Menus/Menu/Item/Item",
            "net.sf.navigator.menu.MenuComponent", "type");
        digester.addSetProperties("MenuConfig/Menus/Menu/Item/Item");
        digester.addSetNext("MenuConfig/Menus/Menu/Item/Item",
            "addMenuComponent", "net.sf.navigator.menu.MenuComponent");

        // 4
        digester.addObjectCreate("MenuConfig/Menus/Menu/Item/Item/Item",
            "net.sf.navigator.menu.MenuComponent", "type");
        digester.addSetProperties("MenuConfig/Menus/Menu/Item/Item/Item");
        digester.addSetNext("MenuConfig/Menus/Menu/Item/Item/Item",
            "addMenuComponent", "net.sf.navigator.menu.MenuComponent");

        // 5
        digester.addObjectCreate("MenuConfig/Menus/Menu/Item/Item/Item/Item",
            "net.sf.navigator.menu.MenuComponent", "type");
        digester.addSetProperties("MenuConfig/Menus/Menu/Item/Item/Item/Item");
        digester.addSetNext("MenuConfig/Menus/Menu/Item/Item/Item/Item",
            "addMenuComponent", "net.sf.navigator.menu.MenuComponent");

        // 6
        digester.addObjectCreate("MenuConfig/Menus/Menu/Item/Item/Item/Item",
            "net.sf.navigator.menu.MenuComponent", "type");
        digester.addSetProperties("MenuConfig/Menus/Menu/Item/Item/Item/Item");
        digester.addSetNext("MenuConfig/Menus/Menu/Item/Item/Item/Item",
            "addMenuComponent", "net.sf.navigator.menu.MenuComponent");

        // 7
        digester.addObjectCreate("MenuConfig/Menus/Menu/Item/Item/Item/Item",
            "net.sf.navigator.menu.MenuComponent", "type");
        digester.addSetProperties("MenuConfig/Menus/Menu/Item/Item/Item/Item");
        digester.addSetNext("MenuConfig/Menus/Menu/Item/Item/Item/Item",
            "addMenuComponent", "net.sf.navigator.menu.MenuComponent");

        digester.addObjectCreate("MenuConfig/Displayers/Displayer",
            "net.sf.navigator.displayer.MenuDisplayerMapping", "mapping");
        digester.addSetProperties("MenuConfig/Displayers/Displayer");
        digester.addSetNext("MenuConfig/Displayers/Displayer",
            "addMenuDisplayerMapping",
            "net.sf.navigator.displayer.MenuDisplayerMapping");
        digester.addSetProperty("MenuConfig/Displayers/Displayer/SetProperty",
            "property", "value");
            
        return digester;
    }

q里又是一个经?digesterQDigester的用,如果你需要读一个XML配置文gQƈ且不想与DOM直接打交道的话,Digester是一个很好的选择。实际上我们看到l(f)oad函数调用一?digester.parse(input);已l把menu-config.xml建立到内存里了,p么简单。如果你惌初始化你的系l,q种Ҏ(gu)是不是可以学?fn)呢Q?工欲善其事,必先利其?。我们可以看到Raible是怎么样利用现有的工具来减d发量的?/FONT>

׃MenuRepositoryN若轻的初始化q程Q甚至都没有让我们看到树(wi)形结构是怎么建立到内存里ȝ。不q不要着急,cdl我们了明示?/FONT>

看到MenuBasecM吗?对了Q看名字q道是一个Menu的基cR可以看刎ͼ它是一个简单的JavaBean。而且怿它的每个属性大家根据名字也能猜出来。所以重点讲解是MenuComponentQ一个简化的 "Composite"模式?/FONT>

如上图所C。由于此处的Leaf没有MҎ(gu)Q只有属性。因此Leaf和Composite收羃成了一个MenuComponentcR大安知道QComposite模式是实现树(wi)形结构最好的Ҏ(gu)。如果你以前没有Z(x)实现或者没有从Composite模式得到好处Q那么,从这里看一下用Composite模式得到的好处。首先看它的单,MenuComponet的实际代码很,加v来不到十行?/FONT>

public void addMenuComponent(MenuComponent menuComponent) {
        menuComponents.add(menuComponent);
        menuComponent.setParent(this);

        if ((menuComponent.getName() == null) ||
                (menuComponent.getName().equals(""))) {
            menuComponent.setName(this.name + menuComponents.size());
        }
    }

    public MenuComponent[] getMenuComponents() {
        MenuComponent[] menus =
            (MenuComponent[]) menuComponents.toArray(_menuComponent);

        return menus;
    }

如果你用十行来实C个树(wi)型结?q且q是通用?Q你愿不愿意Q就是通过单的q么一些代码,实现的在内存中徏立树(wi)型结构的目标?/FONT>

下面我们来看DispLay包,q个包的功能也是很清楚的Q就是用来显C啦。这个包的类N常漂亮,遗憾的是也非常大。只能羃?yu)了l大家看了?/FONT>

从类图中可以看到一个非常极漂亮的面象对象的设计思\。通过一个接口,利用模板Ҏ(gu)。最后具体实现树(wi)型结构的昄。其主要Ҏ(gu)是displayComponents和displayq两Ҏ(gu)QinitҎ(gu)则实C初始化的工作Q读取javascript和图片等文g。displayComponents是一个P代函数。从而可以遍历一个MenuCompont?wi)。ƈ其昄出来?/FONT>

应该_(d)Menu包是一个M层,而Dispplya包是一个view层,而加上TagLib包,实CMVC的完整结构?/FONT>

两个Tagcd清楚Q首先我们从怎么使用它来看它们实现的功能

<menu:useMenuDisplayer name="ListMenu"
bundle="org.apache.struts.action.MESSAGE">
        <menu:displayMenu name="ToDoListMenuFile"/>
        <menu:displayMenu name="ToDoListMenuEdit"/>
        <menu:displayMenu name="CaseDetailMenuCase"/>
        <menu:displayMenu name="Standalone"/>
</menu:useMenuDisplayer>

显而易见。useMenuDisplayerq个cL实现使用哪一U显C方式。在menu-config里我们看到ListMenu的定?/FONT>

<Displayer name="ListMenu"
type="net.sf.navigator.displayer.ListMenuDisplayer"/>

displayMenu则是取得一菜单Qƈ其昄出来Q同样在menu-config也能扑ֈ?/FONT>

<Menu  name="ToDoListMenuEdit"  title="EDIT">
<Item name="TDLselect" title="SELECT_ALL" image="images/select-all.png"
location="index.jsp" width="100" />
<Item name="TDLprefs" title="USER_PREFERENCES" image="images/prefs.png"
location="index.jsp" width="150" />
<Item title="Action Test" action="setPermissions?displayer=${displayer}"/>
</Menu>

查看 DisplayMenu的代码,可以看到。它完成的功能只是从context里取得MenuComponent对象Q然后通过 displayer.display(menu);把它交给一个MenuDisplayer的实例来负责d来?/FONT>

因此QControl层很好的完成了控制的功能?/FONT>

lg所q。通过q样一个优的设计Q把各个功能都他d来了。如果我们需要增加一U显C方式,只要l承MenuDisplayer或它的一个子c,然后写出我们的方法,而不需要修改系l的其他部分。同L(fng)Q如果我们的菜单不准备存攑֜ServletContext而准备存攑֜比如Session里了Q那么我们也只需要修改control部分和生成部??SPAN class=style1>MenuRepository)部分。而不影响Display部分?/FONT>

OKQ对struts-menu的介l结束了Q下一文章将是如果用struts-menu和数据库技术动态生成菜单了。请大家l箋x我的Blog?/FONT>




W笨 2005-04-14 18:00 发表评论
]]>
Zstruts+spring+ibatis?J2EE 开?http://www.tkk7.com/kapok/archive/2005/04/10/3064.htmlW笨W笨Sun, 10 Apr 2005 03:40:00 GMThttp://www.tkk7.com/kapok/archive/2005/04/10/3064.htmlhttp://www.tkk7.com/kapok/comments/3064.htmlhttp://www.tkk7.com/kapok/archive/2005/04/10/3064.html#Feedback0http://www.tkk7.com/kapok/comments/commentRss/3064.htmlhttp://www.tkk7.com/kapok/services/trackbacks/3064.htmlhttp://tech.ccidnet.com/pub/article/c1112_a216699_p5.html

1. 前言

Struts 是目前Java Web MVC框架中不争的王者。经q长达五q的发展QStruts已经逐渐成长Z个稳定、成熟的框架Qƈ且占有了MVC框架中最大的市场份额。但是Struts某些技术特性上已经落后于新兴的MVC框架。面对Spring MVC、Webwork2 q些设计更精密,扩展性更强的框架QStruts受到了前所未有的挑战。但站在产品开发的角度而言QStruts仍然是最E_的选择。本文的原型例子JpetStore 4.0是ZStruts开发的Q但是不拘惔于Struts的传l固定用法,例如只用了一个自定义Actionc,q且在form beancȝ定义上也是开创性的Qo(h)目一斎ͼE后具体剖析一下?BR>
Spring Framework 实际上是Expert One-on-One J2EE Design and Development 一书中所阐述的设计思想的具体实现。Spring Framework的功能非常多。包含AOP、ORM、DAO、Context、Web、MVC{几个部分组成。Web、MVC暂不用考虑QJpetStore 4.0用的是更成熟的Struts和JSPQDAO׃目前Hibernate、JDO、ibatis的流行,也不考虑QJpetStore 4.0用的是ibatis。因此最需要用的是AOP、ORM、Context。Context中,最重要的是BeanfactoryQ它能将接口与实现分开Q非常强大。目前AOP应用最成熟的还是在事务理上?BR>
ibatis 是一个功能强大实用的SQL Map工具Q不同于其他ORM工具Q如hibernateQ,它是SQL语句映射成Java对象Q而对于ORM工具Q它的SQL语句是根据映定义生成的。ibatis 以SQL开发的工作量和数据库移植性上的让步,为系l设计提供了更大的自q间。有ibatis代码生成的工P可以Ҏ(gu)DDL自动生成ibatis代码Q能减少很多工作量?BR>
2. JpetStoreq?BR>
2.1. 背景

最初是Sun公司的J2EE petstoreQ其最主要目的是用于学?fn)J2EEQ但是其~点也很明显Q就是过度设计了。接着Oracle用J2EE petstore来比较各应用服务器的性能。微软推ZZ.Netq_?Pet shopQ用于竞争J2EE petstore。而JpetStore则是l过改良的基于struts的轻便框架J2EE web应用E序Q相比来_(d)JpetStore设计和架构更优良Q各层定义清晎ͼ使用了很多最?jng)_践和模式Q避免了很多"反模?Q如使用存储q程Q在java代码中嵌入SQL语句Q把HTML存储在数据库中等{。最新版本是JpetStore 4.0?BR>
2.2. JpetStore开发运行环境的建立

1、开发环?BR>
Java SDK 1.4.2
Apache Tomcat 4.1.31
Eclipse-SDK-3.0.1-win32
HSQLDB 1.7.2

2、Eclipse插g

EMF SDK 2.0.1QEclipse建模框架Qlomboz插g需要,可以使用runtime版本?BR>lomboz 3.0QJ2EE插gQ用来在Eclipse中开发J2EE应用E序
Spring IDE 1.0.3QSpring Bean配置理插g
xmlbuddy_2.0.10Q编辑XMLQ用免费版功能即?BR>tomcatPluginV3Qtomcat理插g
Properties EditorQ编辑java的属性文?q可以预览以?qing)自动存盘?f)Unicode格式。免M手工或者ANT调用native2ascii的麻烦?BR>2.3. 架构



? JpetStore架构?/CENTER>

? 是JPetStore架构图。参照这个架构图Q让我们E微剖析一下源代码Q得出JpetStore 4.0的具体实现图Q图2Q,思\一下子p然开朗了。前a中提到的非传l的struts开发模式,关键在struts Actioncdform beancM?BR>
struts Actioncd有一个:(x)BeanAction。没错,实是一个!与传l的struts~程方式很不同。再仔细研究BeanActionc,发现它其实是一个通用c,利用反射原理Q根据URL来决定调用formbean的哪个方法。BeanAction大大化了struts的编E模式,降低了对struts的依赖(与struts以及(qing)WEB容器有关的几个类都放在com.ibatis.struts包下Q其它的c都可以直接复用Q。利用这U模式,我们?x)很?gu)的把它移植到新的框架如JSFQspring?BR>
q样重心?yu)p{Udform bean上了Q它已经不是普通意义上的form bean了。查看源代码Q可以看到它不仅仅有数据和校?重置Ҏ(gu)Q而且已经h了行为,从这个意义上来说Q它更像一个BO(Business Object)。这是前文讲到的,BeanActioncd用反原理,Ҏ(gu)URL来决定调用form bean的哪个方法(行ؓ(f)Q。form bean的这些方法的{֐很简单,例如Q?BR>
public String myActionMethod() 
 {
   //..work
   return "success";
 }


Ҏ(gu)的返回值直接就是字W串Q对应的是forward的名Uͼ而不再是ActionForward对象Q创建ActionForward对象的Q务已l由BeanActioncM劳了。另外,E序q提供了ActionContext工具c,该工L(fng)装了request 、response、form parameters、request attributes、session attributes?application attributes中的数据存取操作Q简单而线E安全,form beancM用该工具cd以进一步从表现层框架解耦。在q里需要特别指出的是,BeanActioncL对struts扩展的一个有益尝试,虽然提供了非常好的应用开发模式,但是它还非常斎ͼ一直在发展中?



? JpetStore 4.0具体实现


.4. 代码剖析

下面p我们开始进一步分析JpetStore4.0的源代码Qؓ(f)下面的改造铺路。BeanAction.java是唯一一个Struts actionc,位于com.ibatis.struts包下。正如上文所aQ它是一个通用的控制类Q利用反机Ӟ把控制{Udform bean的某个方法来处理。详l处理过E参考其源代码,单明晰?BR>
Form beancM于com.ibatis.jpetstore.presentation包下Q命名规则ؓ(f)***Bean。Form beancd部承于BaseBeanc,而BaseBeancd际承于ActionFormQ因此,Form beancd是Struts?ActionFormQForm beancȝ属性数据就由struts框架自动填充。而实际上QJpetStore4.0扩展了struts中ActionForm的应用:(x) Form beanc还h行ؓ(f)Q更像一个BO,其行为(Ҏ(gu)Q由BeanActionҎ(gu)配置Qstruts-config.xmlQ的URL来调用。虽然如此,我们q是把Form beancd位于表现层。Struts-config.xml的配|里?U映方式,来告诉BeanAction把控制{到哪个form bean对象的哪个方法来处理。以q个hq接Zhttp://localhost/jpetstore4/shop/viewOrder.do

1. URL Pattern

<action path="/shop/viewOrder" type="com.ibatis.struts.BeanAction"
    name="orderBean" scope="session"
    validate="false">
    <forward name="success" path="/order/ViewOrder.jsp"/>
  </action>


此种方式表示Q控制将被{发到"orderBean"q个form bean对象 ?viewOrder"Ҏ(gu)Q行为)来处理。方法名?path"参数的以"/"分隔的最后一部分?

2. Method Parameter

<action path="/shop/viewOrder" type="com.ibatis.struts.BeanAction"
    name="orderBean" parameter="viewOrder" scope="session"
    validate="false">
    <forward name="success" path="/order/ViewOrder.jsp"/>
  </action>


此种方式表示Q控制将被{发到"orderBean"q个form bean对象?viewOrder"Ҏ(gu)Q行为)来处理。配|中?parameter"参数表示form beancM的方法?parameter"参数优先?path"参数?

3. No Method call

<action path="/shop/viewOrder" type="com.ibatis.struts.BeanAction"
    name="orderBean" parameter="*" scope="session"
    validate="false">
    <forward name="success" path="/order/ViewOrder.jsp"/>
  </action>


此种方式表示Qform bean上没有Q何方法被调用。如果存?name"属性,则struts把表单参数等数据填充到form bean对象后,把控制{发到"success"。否则,如果name为空Q则直接转发控制?success"。这q当于struts内置的org.apache.struts.actions.ForwardAction的功?

<action path="/shop/viewOrder" type="org.apache.struts.actions.ForwardAction"
    parameter="/order/ViewOrder.jsp " scope="session" validate="false">
 </action>


ServicecM于com.ibatis.jpetstore.service包下Q属于业务层。这些类装了业务以?qing)相应的事务控制。Servicecȝform beancL调用?

com.ibatis.jpetstore.persistence.iface包下的类是DAO接口Q属于业务层Q其屏蔽了底层的数据库操作,供具体的ServicecL调用。DaoConfigcL工具c(DAO工厂c)QServicec通过DaoConfigcL获得相应的DAO接口Q而不用关心底层的具体数据库操作,实现了如?中{耦合2}的解耦?

com.ibatis.jpetstore.persistence.sqlmapdao包下的类是对应DAO接口的具体实玎ͼ在JpetStore4.0中采用了ibatis来实现ORM。这些实现类l承BaseSqlMapDaoc,而BaseSqlMapDaocdl承ibatis DAO 框架中的SqlMapDaoTemplatecRibatis的配|文件存攑֜com.ibatis.jpetstore.persistence.sqlmapdao.sql目录下。这些类和配|文件位于数据层

DomaincM于com.ibatis.jpetstore.domain包下Q是普通的javabean。在q里用作数据传输对象QDTOQ,贯穿视图层、业务层和数据层Q用于在不同层之间传输数据。剩下的部分比较简单了Q请看具体的源代码,非常清晰?

2.5. 需要改造的地方

JpetStore4.0的关键就在struts Actioncdform beancMQ这也是其精华之一Q虽然该实现方式是试验性,待扩充和验证Q,在此ơ改造中我们要保留下来,x制层一点不变,表现层获取相应业务类的方式变了(要加载spring环境Q,其它保持不变。要特别x的改动是业务层和持久层,q运的是JpetStore4.0设计非常好,需要改动的地方非常,而且由模式可循,如下Q?BR>
1. 业务层和数据层用Spring BeanFactory机制理?BR>2. 业务层的事务由spring 的aop通过声明来完成?BR>3. 表现层(form beanQ获取业务类的方法改p定义工厂cL实现Q加载spring环境Q?BR>
3. JPetStore的改?BR>
3.1. 攚w后的架?BR>


其中U色部分是要增加的部分,蓝色部分是要修改的部分。下面就让我们逐一剖析?BR>
3.2. Spring Context的加?BR>Z在Struts中加载Spring ContextQ一般会(x)在struts-config.xml的最后添加如下部分:(x)

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


Spring在设计时充分考虑C与Struts的协同工作,通过内置的Struts Plug-in在两者之间提供了良好的结合点。但是,因ؓ(f)在这里我们一点也不改动JPetStore的控制层(q是JpetStore4.0的精华之一)Q所以本文不准备采用此方式来加蝲ApplicationContext。我们利用的是spring framework 的BeanFactory机制,采用自定义的工具c(bean工厂c)来加载spring的配|文Ӟ从中可以看出Spring有多灉|Q它提供了各U不同的方式来用其不同的部?层次Q?zhn)只需要用你想用的Q不需要的部分可以不用?

具体的来_(d)是在com.ibatis.spring包下创徏CustomBeanFactoryc,spring的配|文件applicationContext.xml也放在这个目录下。以下就是该cȝ全部代码Q很单:(x)

public final class CustomBeanFactory {
	static XmlBeanFactory factory = null;
	static {
		Resource is = new
InputStreamResource( CustomBeanFactory.class.getResourceAsStream("applicationContext.xml"));
		factory = new XmlBeanFactory(is);			
	}
	public static Object getBean(String beanName){
		return factory.getBean(beanName);
	}
}


实际上就是封装了Spring 的XMLBeanFactory而已Qƈ且Spring的配|文件只需要加载一ơ,以后可以直接用CustomBeanFactory.getBean("someBean")来获得需要的对象?例如someBean)Q而不需要知道具体的cRCustomBeanFactorycȝ于{耦合1}的解耦。CustomBeanFactorycd本文中只用于表现层的form bean对象获得servicecȝ对象Q因为我们没有把form bean对象配置在applicationContext.xml中。但是,Z么不把表现层的form beancM配置h呢,q样q不着qC(j)ustomBeanFactory个类了,Spring?x)帮助我们创建需要的一切?问题的答案就在于form beancLstruts的ActionFormc!如果大家熟?zhn)strutsQ就?x)知道ActionFormcLstruts自动创徏的:(x)在一ơ请求中Qstruts判断Q如果ActionForm实例不存在,创Z个ActionForm对象Q把客户提交的表单数据保存到ActionForm对象中。因此formbeancȝ对象׃能由spring来创建,但是servicecM?qing)数据层的DAOcd以,所以只有他们在spring中配|。所以,很自然的Q我们就创徏了CustomBeanFactoryc,在表现层来衔接struts和spring。就q么单,实现了另一U方式的{耦合一}的解耦?

3.3. 表现?

面分析到Qstruts和spring是在表现层衔接v来的Q那么表现层p做稍微的更改Q即所需要的servicecȝ对象创徏上。以表现层的AccountBeancMؓ(f)例:(x)原来的源代码如下

private static final AccountService accountService 
= AccountService.getInstance();
private static final CatalogService catalogService 
= CatalogService.getInstance();


攚w后的源代码如下

private static final AccountService accountService 
= (AccountService)CustomBeanFactory.getBean("AccountService");
private static final CatalogService catalogService
= (CatalogService)CustomBeanFactory.getBean("CatalogService");


其他的几个presentationcM同样方式攚w。这P表现层就完成了。关于表现层的其它部分如JSP{一概不动。也许?zhn)会(x)说Q没有看Z么特别之处的好处啊?你还是额外实C一个工厂类。别着急,帷幕刚刚开启,spring是在表现层引入,但?zhn)发没发现Q?

presentationcM仅面向servicecȝ接口~程Q具?AccountService"是哪个实现类QpresentationcM知道Q是在spring的配|文仉配置。(本例中,Z最大限度的保持原来的代码不作变化,没有抽象出接口)。Spring鼓励面向接口~程Q因为是如此的方便和自然Q当然?zhn)也可以不q么做?

CustomBeanFactoryq个工厂cMؓ(f)什么会(x)如此单,因ؓ(f)其直接用了Spring的BeanFactory。Spring从其核心而言Q是一个DI容器Q其设计哲学是提供一U无侵入式的高扩展性的框架。ؓ(f)了实现这个目标,Spring 大量引入了Java 的Reflection机制Q通过动态调用的方式避免编码方式的U束Qƈ在此基础上徏立了其核心组件BeanFactoryQ以此作为其依赖注入机制的实现基。org.springframework.beans包中包括了这些核心组件的实现c,核心中的核心为BeanWrapper和BeanFactorycR?

3.4. 持久层在讨论业务层之前,我们先看一下持久层Q如下图所C:(x)
















在上文中Q我们把iface包下的DAO接口归ؓ(f)业务层,在这里不需要做修改。ibatis的sql配置文g也不需要改。要改的是DAO实现c,q在spring的配|文件中配置h?BR>
1、修改基c?BR>
所有的DAO实现c都l承于BaseSqlMapDaocR修改BaseSqlMapDaocd下:(x)

public class BaseSqlMapDao extends SqlMapClientDaoSupport
{
  protected static final int PAGE_SIZE = 4;
  protected SqlMapClientTemplate smcTemplate 
  = this.getSqlMapClientTemplate();
  public BaseSqlMapDao()
  { 
	}
}


使BaseSqlMapDaocL为承于Spring提供的SqlMapClientDaoSupportc,q定义了一个保护属性smcTemplateQ其cd为SqlMapClientTemplate?

2、修改DAO实现c?

所有的DAO实现c还是承于BaseSqlMapDaoc,实现相应的DAO接口Q但其相应的DAO操作委托SqlMapClientTemplate来执行,以AccountSqlMapDaocMؓ(f)例,部分代码如下Q?

public List getUsernameList() 
{
    return smcTemplate.queryForList("getUsernameList", null);
}
  public Account getAccount(String username, String password)
{
    Account account = new Account();
    account.setUsername(username);
    account.setPassword(password);
    return (Account)
	smcTemplate.queryForObject
	("getAccountByUsernameAndPassword", account);
}
  public void insertAccount(Account account)
{
 smcTemplate.update("insertAccount", account);
 smcTemplate.update("insertProfile", account);
 smcTemplate.update("insertSignon", account);
}


p么简单,所有函数的{֐都是一L(fng)Q只需要查找替换就可以了!

3、除d厂类以及(qing)相应的配|文?BR>
除去DaoConfig.javaq个DAO工厂cd相应的配|文件dao.xmlQ因为DAO的获取现在要用spring来管理?BR>
4、DAO在Spring中的配置QapplicationContext.xmlQ?BR>










<bean id="dataSource" 
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName">
            <value>org.hsqldb.jdbcDriver</value>
        </property>
        <property name="url">
            <value>jdbc:hsqldb:hsql://localhost/xdb</value>
        </property>
        <property name="username">
            <value>sa</value>
        </property>
        <property name="password">
            <value></value>
        </property>
    </bean>    
    <!-- ibatis sqlMapClient config -->
    <bean id="sqlMapClient" 
        class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
        <property name="configLocation">
            <value> 
   classpath:com\ibatis\jpetstore\persistence\sqlmapdao\sql\sql-map-config.xml
            </value>
        </property>
        <property name="dataSource">
            <ref bean="dataSource"/>
        </property>    
    </bean>
    <!-- Transactions -->
    <bean id="TransactionManager" 
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource">
            <ref bean="dataSource"/>
        </property>
    </bean>
    <!-- persistence layer -->
    <bean id="AccountDao" 
        class="com.ibatis.jpetstore.persistence.sqlmapdao.AccountSqlMapDao">
        <property name="sqlMapClient">
            <ref local="sqlMapClient"/>
        </property>
    </bean>


1. 我们首先创徏一个数据源dataSourceQ在q里配置的是hsqldb数据库。如果是ORACLE数据库,driverClassName的值是"oracle.jdbc.driver.OracleDriver"QURL的值类g"jdbc:oracle:thin:@wugfMobile:1521:cdcf"。数据源现在由spring来管理,那么现在我们可以去掉properties目录下database.propertiesq个配置文g了;q有不要忘记修改sql-map-config.xmlQ去?PROPERTIES resource="properties/database.properties" />对它的引用?

2. sqlMapClient节点。这个是针对ibatis SqlMap的SqlMapClientFactoryBean配置。实际上配置了一个sqlMapClient的创建工厂类。configLocation属性配|了ibatis映射文g的名U。dataSource属性指向了使用的数据源Q这h有用sqlMapClient的DAO都默认用了该数据源Q除非在DAO的配|中另外昑ּ指定?

3. TransactionManager节点。定义了事务Q用的是DataSourceTransactionManager?

4. 下面可以定义DAO节点了,如AccountDaoQ它的实现类是com.ibatis.jpetstore.persistence.sqlmapdao.AccountSqlMapDaoQ用的SQL配置从sqlMapClient中读取,数据库连接没有特别列出,那么是默认使用sqlMapClient配置的数据源datasource?

q样Q我们就把持久层攚w完了,其他的DAO配置cM于AccountDao。怎么P单吧。这ơ有接口了:(x)Q?AccountDao接口Q?gt;AccountSqlMapDao实现?

3.5. 业务?

业务层的位置以及(qing)相关c,如下图所C:(x)在这个例子中只有3个业务类Q我们以OrderServicecMؓ(f)例来攚w,q个cL最复杂的,其中涉及(qing)了事务?

1、在ApplicationContext配置文g中增加bean的配|:(x)

<bean id="OrderService" 
        class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
        <property name="transactionManager">
            <ref local="TransactionManager"></ref>
        </property>
        <property name="target">
            <bean class="com.ibatis.jpetstore.service.OrderService">
                <property name="itemDao">
                    <ref bean="ItemDao"/>
                </property>
                <property name="orderDao">
                    <ref bean="OrderDao"/>
                </property>
                <property name="sequenceDao">
                    <ref bean="SequenceDao"/>
                </property>
            </bean>
        </property>
        <property name="transactionAttributes">
            <props>
                <prop key="insert*">PROPAGATION_REQUIRED</prop>
            </props>
        </property>
    </bean>


定义了一个OrderServiceQ还是很Ҏ(gu)懂的。ؓ(f)了简单v见,使用了嵌套beanQ其实现cLcom.ibatis.jpetstore.service.OrderServiceQ分别引用了ItemDaoQOrderDaoQSequenceDao。该bean的insert*实现了事务管?AOP方式)。TransactionProxyFactoryBean自动创徏一个事务advisorQ?该advisor包括一个基于事务属性的pointcut,因此只有事务性的Ҏ(gu)被拦截?

2、业务类的修改,以OrderServiceZQ?

public class OrderService {

   /* Private Fields */
  private ItemDao itemDao;
  private OrderDao orderDao;
  private SequenceDao sequenceDao;

  /* Constructors */

  public OrderService() {
  }

/**
 * @param itemDao 要设|的 itemDao?
 */
public final void setItemDao(ItemDao itemDao) {
	this.itemDao = itemDao;
}
/**
 * @param orderDao 要设|的 orderDao?
 */
public final void setOrderDao(OrderDao orderDao) {
	this.orderDao = orderDao;
}
/**
 * @param sequenceDao 要设|的 sequenceDao?
 */
public final void setSequenceDao(SequenceDao sequenceDao) {
	this.sequenceDao = sequenceDao;
}
//剩下的部?
…?
}


U色部分Z攚w分。Spring采用的是Type2的设|依赖注入,所以我们只需要定义属性和相应的设值函数就可以了,ItemDaoQOrderDaoQSequenceDao的值由spring在运行期间注入。构造函数就可以为空了,另外也不需要自q写代码处理事务了Q事务在配置中声明)QdaoManager.startTransaction();{与事务相关的语句也可以L了。和原来的代码比较一下,是不是处理精了很多!可以更关注业务的实现?

4. l束?

ibatis是一个功能强大实用的SQL Map工具Q可以直接控制SQL,为系l设计提供了更大的自q间。其提供的最新示例程序JpetStore 4.0,设计优雅Q应用了q今为止很多最?jng)_践和设计模式Q非帔R于学习(fn)以及(qing)在此基础上创量的J2EE WEB应用E序。JpetStore 4.0是基于struts的,本文在此基础上,最大程度保持了原有设计的精华以?qing)最的代码改动量,在业务层和持久化层引入了Spring。在(zhn)阅M本文以及(qing)攚w后的源代码后,?x)深切的感受到Spring带来的种U好处:(x)自然的面向接口的~程Q业务对象的依赖注入Q一致的数据存取框架和声明式的事务处理,l一的配|文件…更重要的是Spring既是全面的又是模块化的,Spring有分层的体系l构Q这意味着(zhn)能选择仅仅使用它Q何一个独立的部分Q就像本文,而它的架构又是内部一致?


W笨 2005-04-10 11:40 发表评论
]]>J2EElg间共享对象技?/title><link>http://www.tkk7.com/kapok/archive/2005/04/08/2994.html</link><dc:creator>W笨</dc:creator><author>W笨</author><pubDate>Fri, 08 Apr 2005 06:41:00 GMT</pubDate><guid>http://www.tkk7.com/kapok/archive/2005/04/08/2994.html</guid><wfw:comment>http://www.tkk7.com/kapok/comments/2994.html</wfw:comment><comments>http://www.tkk7.com/kapok/archive/2005/04/08/2994.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/kapok/comments/commentRss/2994.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/kapok/services/trackbacks/2994.html</trackback:ping><description><![CDATA[<TABLE cellSpacing=0 cellPadding=0 width=760 align=center border=0> <TBODY> <TR> <TD class=title vAlign=center align=middle height=56><B><FONT color=#ff0000 size=3><span id="nswmypg" class=p16><A >http://www.uml.org.cn/j2ee/j2ee003.htm</A><BR><BR>J2EElg间共享对象技?/SPAN><!-- #EndEditable --></FONT></B></TD></TR> <TR> <TD class=formtitle align=middle height=40>作者:(x)龚永?   本文选自Q开攄l世界—赛q网  2003q?2?6?/TD></TR></TBODY></TABLE> <TABLE height=565 cellSpacing=0 cellPadding=0 width=760 align=center border=0> <TBODY> <TR> <TD class=content vAlign=top height=350><span id="sjhxctg" class=p11b>惌用好Struts应用框架Q必M解J2EE WebUJSP和Servlet技术存攑օ享对象的几种方式。同Ӟ要利用J2EE 开发Web应用E序也必L握组仉对象׃n的机制?BR><BR>像JavaE序有类U别变量、方法别变量一PJ2EE Web应用E序有四个对象存攑օ享对象。这些共享对象存攑֜那里Q以便存放者或者其它程序代码日后用。这四个对象分别是页面、请求、会(x)话和应用E序Q它们都是以数据l构?值对的Ş式保存的。同时这四个对象形成了四个别的׃n对象存放圎ͼ卛_用程序对象中的共享对象是全局性的Q在整个应用E序的生命周期内有效Q当然主动去掉除外)Q属于所有的上网用户Q会(x)话对象中的共享对象是在一个会(x)话期内有效,属于用户的当前会(x)话;h对象中的׃n对象在一个请求期内有效,属于用户发送的当前hQ页面对象中的共享对象只属于当前面的执行实例。本文主要分析共享对象的讄和访问方法,包括׃n对象的有效范围、具体访问方法、辅助显C手D和多线E下的实现策略?BR><BR> <CENTER><FONT color=#000099><STRONG>在JSP中访问共享对?/STRONG></FONT></CENTER><BR><BR>Servletq行时已l准备好了这些范围对象,如表1所C?BR><BR> <CENTER>? JSP中的׃n对象</CENTER><BR><BR> <CENTER><CCID_NOBR> <TABLE class=content width=502 border=1> <TBODY> <TR> <TD>  </TD> <TD>变量?/TD> <TD>变量cd</TD> <TD>对象可访问范?/TD></TR> <TR> <TD>面</TD> <TD>pageContext</TD> <TD>javax.servlet.jsp.PageContext</TD> <TD>在执行某一个JSPӞServletq行时会(x)为它初始化pageContext变量Q这个变量可以被整个JSP代码讉KQ包括INCLUDE指示W插q来的代码?/TD></TR> <TR> <TD>h</TD> <TD>ruquest</TD> <TD>javax.servlet.http.HttpServletRequest</TD> <TD>用户提交一个HTTPhlServlet定wQServletq行时会(x)把请求封装成HttpServletRequest的一个实例,在JSP中表Cؓ(f)request变量。能讉KpageContext的JSP代码也能讉KrequestQ另外被处理q个h的JSP代码FORWARD到的JSP代码也能讉K?/TD></TR> <TR> <TD>?x)?/TD> <TD>session</TD> <TD>javax.servlet.http.HttpSession</TD> <TD>一个HttpSession?x)话p创徏到关闭或失效期间的用戯求组成。处理这些请求的JSP可以讉K到这期间的session对象中的׃n对象。在?x)话关闭或失效时Q这些对象会(x)丢失?/TD></TR> <TR> <TD>应用E序</TD> <TD>application</TD> <TD>javax.servlet.ServletContext</TD> <TD>q个对象在应用程序的整个生命周期间都有效Q存攑֜q个对象内的数据MJSP都能讉K到?/TD></TR></TBODY></TABLE></CCID_NOBR></CENTER> <P><BR><BR> <CENTER><FONT color=#000099><STRONG>在Servlet中访问共享对?/STRONG></FONT></CENTER><BR><BR>Servlet中的׃n对象如表2?BR><BR> <CENTER>? Servlet中的׃n对象</CENTER><BR><BR> <CENTER><CCID_NOBR> <TABLE class=content width=502 border=1> <TBODY> <TR> <TD>h</TD> <TD>SERVLETcȝ一pd服务Ҏ(gu)的request参数?/TD> <TD>javax.servlet.http.HttpServletRequest</TD> <TD>用户提交一个HTTPhlServlet容器QServletq行时会(x)把请求封装成HttpServletRequest的一个实例,q作为Servlet服务Ҏ(gu)的request参数传递给Servlet。这个Servlet也可以把q个实例传递给其它Weblg?/TD></TR> <TR> <TD>?x)?/TD> <TD>request.getSession()或者request.getSession(boolesn)Ҏ(gu)获得?/TD> <TD>javax.servlet.http.HttpSession</TD> <TD>一个HttpSession?x)话p创徏到关闭或p|期间的用戯求组成。处理这些请求的Servlet可以讉K到这期间的session对象中的׃n对象。在?x)话关闭或失效时Q这些共享对象会(x)丢失?/TD></TR> <TR> <TD>应用E序</TD> <TD>SERVLET?getServletContext()</TD> <TD>javax.servlet.ServletContext</TD> <TD>q个对象在应用程序的整个生命周期间都有效Q存攑֜q个对象内的数据MWeblg都能讉K到?/TD></TR></TBODY></TABLE></CCID_NOBR></CENTER> <P><BR><BR> <CENTER><FONT color=#000099><STRONG>资源l合</STRONG></FONT></CENTER><BR><BR>在JSP技术中Q有两种把资源片断组合成一个资源的技术:(x)include 指示W和jsp:include元素。指C符的语法ؓ(f)Q?BR><BR><CCID_NOBR> <TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=550 align=center borderColorLight=black border=1> <TBODY> <TR> <TD class=code bgColor=#e6e6e6><PRE><CCID_CODE> <%@ include file="fragmentresource.jsp" %></CCID_CODE> </PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>当一个JSP被翻译成ServletӞ它会(x)被处理。jsp:include元素的语法是Q?BR><BR><CCID_NOBR> <TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=550 align=center borderColorLight=black border=1> <TBODY> <TR> <TD class=code bgColor=#e6e6e6><PRE><CCID_CODE> <jsp:include page="included.jsp"/></CCID_CODE> </PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>当这个JSP面被执行时Q它?x)被处理。指C符是代码的l合Q元素则是结果的l合。fragmentresource.jsp和主面h一L(fng)上下文,如页面对象;而included.jsp不具有和主页面一L(fng)面对象Q但h对象是同一个?BR><BR>在Servlet中,RequestDispatcher.include(request,response)实现l果的整?CZ代码如下Q?BR><BR><CCID_NOBR> <TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=550 align=center borderColorLight=black border=1> <TBODY> <TR> <TD class=code bgColor=#e6e6e6><PRE><CCID_CODE> RequestDispatcher dispatcher =request.getRequestDispatcher("/template.jsp"); if (dispatcher !=null) dispatcher.include(request,response);</CCID_CODE> </PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR> <CENTER><FONT color=#000099><STRONG>控制传?/STRONG></FONT></CENTER><BR><BR>在利用RequestDispatcher.forware(request,response)把控制传l另一个Weblg讄形成一个控制管道时Q要严格遵@“前面的lg处理requestQ最后的lg处理response”的准则。前面的lg甚至不能企图获取response输出的引用。这个控制管道中的组件具有同一个request对象Q不h相同的pageContext对象Q针对JSPQ。Servlet中传递控制的例子如下Q?BR><BR><CCID_NOBR> <TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=550 align=center borderColorLight=black border=1> <TBODY> <TR> <TD class=code bgColor=#e6e6e6><PRE><CCID_CODE> RequestDispatcher dispatcher =request.getRequestDispatcher("/template.jsp"); if (dispatcher !=null) dispatcher.forward(request,response);</CCID_CODE> </PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>JSP中传递控制的例子如下Q?BR><BR><CCID_NOBR> <TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=550 align=center borderColorLight=black border=1> <TBODY> <TR> <TD class=code bgColor=#e6e6e6><PRE><CCID_CODE> <jsp:forward page="/main.jsp"/></CCID_CODE> </PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>在JSP中,可以通过jsp:param元素来增加请求对象的参数Q适用于jsp:include和jsp:forward两元素?CZ如下Q?BR><BR><CCID_NOBR> <TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=550 align=center borderColorLight=black border=1> <TBODY> <TR> <TD class=code bgColor=#e6e6e6><PRE><CCID_CODE> <jsp: forward page="included.jsp"> <jsp:param name="param1 " value=="value1"/> </jsp:include></CCID_CODE> </PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR> <CENTER><FONT color=#000099><STRONG>昄׃n对象</STRONG></FONT></CENTER><BR><BR> <CENTER><IMG height=437 src="http://www.uml.org.cn/j2ee/images/97842.jpg" width=400></CENTER><BR><BR> <CENTER>? 昄׃n信息cd</CENTER><BR><BR>在网上昄各个U别的共享对象是一个非怸错的调试手段。下面的昄׃n对象cdQ如?Q实Cq个功能。它以类org.i18.struts.AttributeUtils为核心,q个c负责把四个对象的共享对象保存ؓ(f)?值对的HashMap对象。通过它可以得到请求对象中的共享对象及(qing)参数信息Q以?qing)页面对象、会(x)话对象、应用对象这些共享对象的函数接口。在JSP面中,通过下面的代码可以给q个cȝ对象提供输入Q?BR><BR><CCID_NOBR> <TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=550 align=center borderColorLight=black border=1> <TBODY> <TR> <TD class=code bgColor=#e6e6e6><PRE><CCID_CODE> <jsp:useBean id="bean3" scope="application" class="org.i18.struts.AttributeUtils" /> <jsp:setProperty name="bean0" property="object" value="<%= pageContext %>" /></CCID_CODE> </PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>JSP面可以利用Struts提供的logic标签库显C些共享对象:(x)<BR><BR><CCID_NOBR> <TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=550 align=center borderColorLight=black border=1> <TBODY> <TR> <TD class=code bgColor=#e6e6e6><PRE><CCID_CODE> <li> q是对象内的共享对?</li> <table align="center" cellpadding="5" border="0"> <tbody valign="center"> <tr> <td class="header"> ׃n对象?</td> <td class="header"> ׃n对象相关内容 </td> </tr> <logic:iterate id="element" name="bean0" property="pageProp" > <tr><td> <bean:write name="element" property="key"/> </td> <td> <bean:write name="element" property="value"/> </tr> </logic:iterate> </tbody> </table></CCID_CODE> </PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>在Servlet中,AttributeDisplayHelper帮助者类完成JSP中logic标签库相应的功能。帮助者类完成工作后,AttrServlet把帮助者类完成的结果放在request中的一个共享属性Attr中,接着把控制传lservletAttr.jspQ再由它讉KAttrServlet在request中设|的׃n属性AttrQƈ昄l果? <P>使用者可以通过attr.jsp、AttrServlet的url映射和index.jsp的提交按钮来查看当前上下文所有别的׃n对象?BR><BR> <CENTER><FONT color=#000099><STRONG>关于多线E问?/STRONG></FONT></CENTER><BR><BR>J2EEpd规范中,EJB规范保证了组件开发者在单线E的环境下编E,但Servlet规范没有规定Servlet的系列服务方法在单线E模式下q作Q所以开发者在使用׃n对象时要注意U程同步问题。一个比较通用的原则是Q在一个专职的lg中设|共享对象,当设|和讉K破坏数据的一致性时Q用Java的同步控制?BR><BR>一个Servlet(包括JSP)的生命周期由其所在的容器控制。当有一个ServlethӞ容器执行如下步骤Q?BR><BR>1.如果此Servlet的实例不存在Q容器先装蝲Servlet的类代码Q创Z个Servlet实例Q接着调用q个实例的initҎ(gu)?BR><BR>2.如果此Servlet的实例存在,容器分配一个处理用戯求的工作U程。如果Servler实现了SingleThreadModel接口Q工作线E会(x)在这个实例上同步Qƈ且在取得讉K权限后调用实例的serviceҎ(gu)Q否则直接调用实例的serviceҎ(gu)。javax.servlet.http.HttpServlet的serviceҎ(gu)?x)根据用L(fng)HTTPhcd调用相应的doxxxҎ(gu)?BR><BR>3.只有当所有的U程从这个实例中退出,容器在回收这个实例时才会(x)调用q个实例的destroyҎ(gu)?BR><BR>Servlet实例U程图(如图2Q体Cq个生命周期模型?BR><BR> <CENTER><IMG height=215 src="http://www.uml.org.cn/j2ee/images/97843.jpg" width=334></CENTER><BR><BR> <CENTER>? Servlet实例U程?/CENTER><BR><BR>虽然可以让所有的Servlet实现SingleThreadModel接口Q但q会(x)严重影响E序的性能。要解决多线E的同步问题Q我们首先要分析׃n对象的访问模式。在一个WebE序中,׃n对象按访问模式可以分Z下两c:(x)一ơ设|、多ơ读取的׃n对象和多ơ设|、多ơ读取的׃n对象?BR><BR><B>一ơ设|、多ơ读?/B><BR><BR>对于q种׃n对象Q可以开发一个事件监听器Q监听程序启动和停止事gQ代码如下:(x)<BR><BR><CCID_NOBR> <TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=550 align=center borderColorLight=black border=1> <TBODY> <TR> <TD class=code bgColor=#e6e6e6><PRE><CCID_CODE> package org.i18.listen import org.i18.utils.*; import javax.servlet.*; import util.Counter; public final class ContextListener implements ServletContextListener { private ServletContext context =null; public void contextInitialized(ServletContextEvent event){ context =event.getServletContext(); SynObject synObject = new SynObject(); //在这里把׃n对象攑֜应用对象? context.setAttribute("SYNOBJECT",synObject); } public void contextDestroyed(ServletContextEvent event){ context =event.getServletContext(); //清除保存在应用对象中的共享属? context.removeAttribute("SYNOBJECT "); } }</CCID_CODE> </PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>q样Q当Web应用E序启动Ӟ容器?x)调用监听器Q从而设|共享对象。共享对象的讉K只需直接调用getAttributeҎ(gu)卛_?BR><BR><B>多次讄、多ơ读?/B><BR><BR>对于多次讄、多ơ读取的׃n对象Q必d用Java的同步机Ӟ讉K步骤如下Q?BR><BR>W一步,首先设计一个同步类。这个同步类的代码非常简单:(x)<BR><BR><CCID_NOBR> <TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=550 align=center borderColorLight=black border=1> <TBODY> <TR> <TD class=code bgColor=#e6e6e6><PRE><CCID_CODE> package org.i18.utils public final class SynObject{ }</CCID_CODE> </PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>以上代码可以看出Q它其实什么都没做Q但l承了Object关于同步的方法和机制?BR><BR>W二步,把这个类的实例放到应用对象中作ؓ(f)一个共享对象。可以看个同步对象属于一ơ设|、多ơ用的׃n对象。在应用E序启动事g监听器中讄它,请参见前面的代码?BR><BR>W三步,讄׃n对象。如果有对象需要整个应用程序共享,可以在Servlet的service中利用同步机制来讄Q?BR><BR><CCID_NOBR> <TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=550 align=center borderColorLight=black border=1> <TBODY> <TR> <TD class=code bgColor=#e6e6e6><PRE><CCID_CODE> public void doGet (HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { ServletContext context= getServletContext(); //得到同步对象 Object obj = context.getAttribute("SYNOBJECT"); //获取同步钥匙 synchronized(obj){ //先检查是否存在这个共享对? Object obj2 = context.getAttribute("Attr"); if (obj2 == null) { //不存在,讄׃n对象 obj2 = new TestBean(); context.setAttribute("Attr",obj2); } } }</CCID_CODE> </PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>W四步,d׃n对象。当需要访问多ơ设|、多ơ访问的׃n对象Ӟ同样需要利用同步机Ӟ代码如下Q?BR><BR><CCID_NOBR> <TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=550 align=center borderColorLight=black border=1> <TBODY> <TR> <TD class=code bgColor=#e6e6e6><PRE><CCID_CODE> public void doGet (HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { ServletContext context= getServletContext(); //得到同步对象Q因个对象是一ơ设|、多ơ读取型的,不需要同? Object obj = context.getAttribute("SYNOBJECT"); //获取同步钥匙 synchronized(obj){ //得到需要的׃n对象 Object obj2 = context.getAttribute("Attr"); } }</CCID_CODE> </PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>?x)话对象内共享对象的多线E问题可以和应用对象一样处理。请求对象和面对象正常情况下是U程安全的,除非开发者自己引入了额外的线E?BR><BR>本文分析了开发好的J2EE应用必须掌握的、组仉对象׃n的技术,q种分析技术同样适用于EJB中。在EJB容器中,信息存放的位|变成了实现JNDI的服务提供者,大家通过JNDI的接口方法查询、绑定共享对象。需要注意的是,同步对象不能在JNDI中实玎ͼ因ؓ(f)大家搜烦出来的不是同一个内存对象?BR><BR></SPAN></TD></TR></TBODY></TABLE><img src ="http://www.tkk7.com/kapok/aggbug/2994.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/kapok/" target="_blank">W笨</a> 2005-04-08 14:41 <a href="http://www.tkk7.com/kapok/archive/2005/04/08/2994.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入Struts 1.1http://www.tkk7.com/kapok/archive/2005/04/08/2990.htmlW笨W笨Fri, 08 Apr 2005 03:57:00 GMThttp://www.tkk7.com/kapok/archive/2005/04/08/2990.htmlhttp://www.tkk7.com/kapok/comments/2990.htmlhttp://www.tkk7.com/kapok/archive/2005/04/08/2990.html#Feedback0http://www.tkk7.com/kapok/comments/commentRss/2990.htmlhttp://www.tkk7.com/kapok/services/trackbacks/2990.htmlhttp://www-900.ibm.com/developerWorks/cn/java/l-struts1-1/

王和全(ok_winnerboy@sina.comQ?BR>2003q?8 ?/P>

作ؓ(f)ZMVC模式的Web应用最l典框架QStruts已经正式推出?.1版本Q该版本在以往版本的基上,提供了许多激动h心的新功能。本文就带你走qStruts 1.1L入地了解q些功能?/BLOCKQUOTE>

说明Q?/B>希望本文的读者能有一定的Struts使用基础?/P>

1、Model 2

Struts是基于Model 2之上的,而Model 2是经典的MVCQ模型-视图Q控制器Q模型的Web应用变体Q这个改变主要是׃|络应用的特?-HTTP协议的无状态性引L(fng)。Model 2的目的和MVC一P也是利用控制器来分离模型和视图,辑ֈ一U层间松散耦合的效果,提高pȝ灉|性、复用性和可维护性。在多数情况下,你可以将Model 2与MVC{同h?/P>

下图表示一个基于Java技术的典型|络应用Q从中可以看出Model 2中的各个部分是如何对应于Java中各U现有技术的?/P>

在利用Model 2之前Q我们是把所有的表示逻辑和业务逻辑都集中在一P比如大杂烩似的JSPQ,有时也称q种应用模式为Model 1QModel 1的主要缺点就是紧耦合Q复用性差以及(qing)l护成本高?

2、Struts 1.1 和Model 2

既然Struts 1.1是基于Model 2之上Q那它的底层机制也就是MVCQ下面是Struts 1.1中的MVC实现C意图:(x)


图解说明Q其中不同颜色代表MVC的不同部分:(x)U色Q控制器Q、Ԍ模型Q和l色Q视图)

首先Q控制器QActionServletQ进行初始化工作Q读取配|文Ӟstruts-config.xmlQ,Z同的Struts模块初始化相应的ModuleConfig对象。比如配|文件中的Action映射定义都保存在ActionConfig集合中。相应地有ControlConfig集合、FormBeanConfig集合、ForwardConfig集合和MessageResourcesConfig集合{?/P>

提示Q?/B>模块是在Struts 1.1中新提出的概念,在稍后的内容中我们将详细介绍Q你现在可以单地把模块看作是一个子pȝQ它们共同组成整个应用,同时又各自独立。Struts 1.1中所有的处理都是在特定模块环境中q行的。模块的提出主要是ؓ(f)了解决Struts 1.0中单配置文g的问题?/I>

控制器接收HTTPhQƈ从ActionConfig中找出对应于该请求的Action子类Q如果没有对应的ActionQ控制器直接请求{发给JSP或者静态页面。否则控制器请求分发至具体Actionc进行处理?/P>

在控制器调用具体Action的executeҎ(gu)之前QActionForm对象利用HTTPh中的参数来填充自己(可选步骤,需要在配置文g中指定)。具体的ActionForm对象应该是ActionForm的子cd象,它其实就是一个JavaBean。此外,q可以在ActionFormcM调用validateҎ(gu)来检查请求参数的合法性,q且可以q回一个包含所有错误信息的ActionErrors对象。如果执行成功,ActionForm自动这些参C息以JavaBeanQ一般称之ؓ(f)form beanQ的方式保存在Servlet Context中,q样它们可以被其它Action对象或者JSP调用?/P>

Struts这些ActionForm的配|信息都攑֜FormBeanConfig集合中,通过它们Struts能够知道针对某个客户h是否需要创建相应的ActionForm实例?/P>

Action很简单,一般只包含一个executeҎ(gu)Q它负责执行相应的业务逻辑Q如果需要,它也q行相应的数据检查。执行完成之后,q回一个ActionForward对象Q控制器通过该ActionForward对象来进行{发工作。我们主张将获取数据和执行业务逻辑的功能放到具体的JavaBean当中Q而Action只负责完成与控制有关的功能。遵循该原则Q所以在上图中我Action对象归ؓ(f)控制器部分?/P>

提示Q?/B>其实在Struts 1.1中,ActionMapping的作用完全可以由ActionConfig来替代,只不q由于它是公共API的一部分以及(qing)兼容性的问题得以保留。ActionMapping通过l承ActionConfig来获得与其一致的功能Q你可以{同地看待它们。同理,其它例如ActionForward与ForwardConfig的关pM是如此?/I>

下图l出了客L(fng)从发求到获得响应整个q程的图解说明?/P>

下面我们来详细地讨Z下其中的每个部分Q在q之前,先来了解一下模块的概念?/P>

3、模?/SPAN>

我们知道Q在Struts 1.0中,我们只能在web.xml中ؓ(f)ActionServlet指定一个配|文Ӟq对于我们这些网上的教学例子来说当然没什么问题,但是在实际的应用开发过E中Q可能会(x)有些ȝ。因多开发h员都可能同时需要修攚w|文Ӟ但是配置文g只能同时被一个h修改Q这栯定会(x)造成一定程度上的资源争夺,势必?x)?jing)响开发效率和引v开发h员的抱怨?/P>

在Struts 1.1中,Z解决q个q行开发的问题Q提Z两种解决Ҏ(gu)Q?BR>

  1. 多个配置文g的支?
  2. 模块的支?

支持多个配置文gQ是指你能够为ActionServlet同时指定多个xml配置文gQ文件之间以逗号分隔Q比如Struts提供的MailReader演示例子中就采用该种Ҏ(gu)?/P>

  <!-- Action Servlet Configuration -->
  <servlet>
	<servlet-name>action</servlet-name>
	<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
	<init-param>
		<param-name>config</param-name>
		<param-value>/WEB-INF/struts-config.xml, /WEB-INF/struts-config-registration.xml</param-value>
	</init-param> 
	<load-on-startup>1</load-on-startup>
  </servlet>

q种Ҏ(gu)可以很好地解决修改冲H的问题Q不同的开发h员可以在不同的配|文件中讄自己的Action、ActionForm{等Q当然不是说每个开发h员都需要自q配置文gQ可以按照系l的功能模块q行划分Q。但是,q里q是存在一个潜在的问题Q就是可能不同的配置文g之间?x)生冲H,因ؓ(f)在ActionServlet初始化的时候这几个文g最l还是需要合q到一L(fng)。比如,在struts-config.xml中配|了一个名为success?lt;forward>Q而在struts-config-registration.xml中也配置了一个同L(fng)<forward>Q那么执行v来就?x)生冲H?/P>

Zd解决q种冲突QStruts 1.1中引q了模块QModuleQ的概念。一个模块就是一个独立的子系l,你可以在其中q行L所需的配|,同时又不必担心和其它的配|文件生冲H。因为前面我们讲q,ActionServlet是将不同的模块信息保存在不同的ModuleConfig对象中的。要使用模块的功能,需要进行以下的准备工作Q?/P>

1、ؓ(f)每个模块准备一个配|文?/P>

2、配|web.xml文gQ通知控制?/P>

军_采用多个模块以后Q你需要将q些信息告诉控制器,q需要在web.xml文gq行配置。下面是一个典型的多模块配|:(x)


<init-param>
	<param-name>config</param-name>
	<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param>
	<param-name>config/customer</param-name> 
	<param-value>/WEB-INF/struts-config-customer.xml</param-value>
</init-param>
<init-param> 
	<param-name>config/order</param-name>
	<param-value>/WEB-INF/struts-config-order.xml</param-value>
</init-param>

要配|多个模块,你需要在原有的一?lt;init-param>Q在Struts 1.1中将其对应的模块UCؓ(f)~省模块Q的基础之上Q增加模块对应的<init-param>。其?lt;param-name>表示为config/XXX的Ş式,其中XXX为对应的模块名,<param-value>中还是指定模块对应的配置文g。上面这个例子说明该应用有三个模块,分别是缺省模块、customer和orderQ它们分别对应不同的配置文g?/P>

3、准备各个模块所需的ActionForm、Action和JSP{资?/P>

但是要注意的是,模块的出C同时带来了一个问题,卛_何在不同模块间进行{发?有两U方法可以实现模块间的{发,一U就是在<forward>Q全局或者本圎ͼ中定义,另外一U就是利用org.apache.struts.actions.SwitchAction?/P>

下面是一个全局的例子:(x)


    ... 
    <struts-config>
	... 
	<global-forwards>
		<forward name="toModuleB"
			contextRelative="true"  
			path="/moduleB/index.do" 
		redirect="true"/>   
	... 
	</global-forwards>  
	...   
    </struts-config>

可以看出Q只需要在原有的path属性前加上模块名,同时contextRelative属性置为true卛_。此外,你也可以?lt;action>中定义一个类似的本地<forward>?/P>

  <action-mappings>
	<!-- Action mapping for profile form -->
	<action path="/login" 
	type="com.ncu.test.LoginAction"  
	name="loginForm"     
	scope="request"      
	input="tile.userLogin"
	validate="true">     
	<forward name="success" contextRelative="true" path="/moduleA/login.do"/> 
	</action> 
  </action-mappings>

如果你已l处在其他模块,需要{回到~省模块Q那应该cM下面q样定义Q即模块名ؓ(f)I?/P>

<forward name="success" contextRelative="true" path="/login.do"/>

此外Q你也可以用org.apache.struts.actions.SwitchActionQ例如:(x)


    ...
    <action-mappings> 
	<action path="/toModule" 
	type="org.apache.struts.actions.SwitchAction"/>  
	...    
    </action-mappings>  
    ...

4、ActionServlet

我们首先来了解MVC中的控制器。在Struts 1.1中缺省采用ActionServletcL充当控制器。当然如果ActionServlet不能满你的需求,你也可以通过l承它来实现自己的类。这可以?WEB-INF/web.xml中来具体指定?/P>

要掌握ActionServletQ就必须了解它所扮演的角艌Ӏ首先,ActionServlet表示MVCl构中的控制器部分,它需要完成控制器所需的前端控制及(qing)转发h{职责。其ơ,ActionServlet被实Cؓ(f)一个专门处理HTTPh的ServletQ它同时hservlet的特炏V在Struts 1.1中它主要完成以下功能Q?BR>

  • 接收客户端请?
  • Ҏ(gu)客户端的URI请求映到一个相应的Actionc?
  • 从请求中获取数据填充Form BeanQ如果需要)
  • 调用Actioncȝexecute()Ҏ(gu)获取数据或者执行业务逻辑
  • 选择正确的视囑֓应客?

此外QActionServletq负责初始化和清除应用配|信息的d。ActionServlet的初始化工作在initҎ(gu)中完成,它可以分Z个部分:(x)初始化ActionServlet自n的一些信息以?qing)每个模块的配置信息。前者主要通过initInternal、initOther和initServlet三个Ҏ(gu)来完成?/P>

我们可以?WEB-INF/web.xml中指定具体的控制器以?qing)初始参敎ͼ׃版本的变化以及(qing)Struts 1.1中模块概늚引进Q一些初始参数被废弃或者移入到/WEB-INF/struts-config.xml中定义。下面列出所有被废弃的参敎ͼ相应地在web.xml文g中也不鼓励再使用?/P>

  • application
  • bufferSize
  • content
  • debug
  • factory
  • formBean
  • forward
  • locale
  • mapping
  • maxFileSize
  • multipartClass
  • nocache
  • null
  • tempDir

ActionServletҎ(gu)不同的模块来初始化ModuleConfigc,q在其中以XXXconfig集合的方式保存该模块的各U配|信息,比如ActionConfigQFormBeanConfig{?/P>

初始化工作完成之后,ActionServlet准备接收客户h。针Ҏ(gu)个请求,Ҏ(gu)process(HttpServletRequest request, HttpServletResponse response)被调用。该Ҏ(gu)指定具体的模块,然后调用该模块的RequestProcessor的processҎ(gu)?/P>

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

	RequestUtils.selectModule(request, getServletContext());        
	getRequestProcessor(getModuleConfig(request)).process(request, response);
}

RequestProcessor包含了Struts控制器的所有处理逻辑Q它调用不同的processXXXҎ(gu)来完成不同的处理。下表列出其中几个主要的Ҏ(gu)Q?/P>

Ҏ(gu) 功能
processPath 获取客户端的h路径
processMapping 利用路径来获得相应的ActionMapping
processActionForm 初始化ActionFormQ如果需要)q存入正的scope?/TD>
processActionCreate 初始化Action
processActionPerform 调用Action的executeҎ(gu)
processForwardConfig 处理Actionq回的ActionForward

5、ActionForm

对于ActionForm你可以从以下几个斚w来理解它Q?BR>

  1. ActionForm表示HTTPH体中的数据Q可以将其看作是模型和视囄中介Q它负责保存视图中的数据供模型或者视图用。Struts 1.1文档中把它比作HTTP和Action之间的防火墙Q这体现了ActionFormh的过滤保护的作用Q只有通过ActionForm验证的数据才能够发送到Action处理?
  2. ActionForm是与一个或多个ActionConfig兌的JavaBeanQ在相应的action的executeҎ(gu)被调用之前,ActionForm?x)自动利用请求参数来填充自己Q初始化属性)?
  3. ActionForm是一个抽象类Q你必须通过l承来实现自qcR?

ActionForm首先利用属性的getter和setterҎ(gu)来实现初始化Q初始化完毕后,ActionForm的validateҎ(gu)被调用,你可以在其中来检查请求参数的正确性和有效性,q且可以错误信息以ActionErrors的Ş式返回到输入H体。否则,ActionForm被作ؓ(f)参数传给action的executeҎ(gu)以供使用?/P>

ActionForm bean的生命周期可以设|ؓ(f)sessionQ缺省)和requestQ当讄为sessionӞ记得在resetҎ(gu)中将所有的属性重新设|ؓ(f)初始倹{?/P>

׃ActionForm对应于HTTPH体Q所以随着面的增多,你的ActionForm会(x)急速增加。而且可能同一cd面字段会(x)在不同的ActionForm中出玎ͼq且在每个ActionForm中都存在相同的验证代码。ؓ(f)了解册个问题,你可以ؓ(f)整个应用实现一个ActionForm或者至一个模块对应于一个ActionForm?/P>

但是Q聚合的代h(hun)是复用性很差,而且隄护。针对这个问题,在Struts 1.1中提ZDynaActionForm的概c(din)?/P>

DynaActionFormc?/B>

DynaActionForm的目的就是减ActionForm的数目,利用它你不必创徏一个个具体的ActionFormc,而是在配|文件中配置出所需的虚拟ActionForm。例如,在下表中通过指定<form-bean>的type?org.apache.struts.action.DynaActionForm"来创Z个动态的ActionForm--loginForm?/P>

<form-beans>
	<form-bean name="loginForm" type="org.apache.struts.action.DynaActionForm">  
		<form-property name="actionClass" type="java.lang.String"/>
		<form-property name="username" type="java.lang.String"/>
		<form-property name="password" type="java.lang.String"/> 
	</form-bean> 
</form-beans>

动态的ActionForm的用方法跟普通的ActionForm相同Q但是要注意一炏V普通的ActionForm对象需要ؓ(f)每个属性提供getter和setterҎ(gu)Q以上面的例子而言Q我们需要提供getUsername() ?setUsername()Ҏ(gu)取得和设|username属性,同样地有一Ҏ(gu)法用于取得和讄password属性和actionClass属性?/P>

如果使用DynaActionFormQ它?yu)属性保存在一个HashMapcd象中Q同时提供相应的get(name) ?set(name)Ҏ(gu)Q其中参数name是要讉K的属性名。例如要讉KDynaActionForm中username的|可以采用cM的代码:(x)


String username = (String)form.get("username")Q?

׃值存放于一个HashMap对象Q所以要记得对get()Ҏ(gu)q回的Object对象做强制性类型{换。正是由于这点区别,如果你在Action中非帔RJ地使用ActionForm对象Q徏议还是用普通的ActionForm对象?/P>

在Struts 1.1中,除了DynaActionForm以外Q还提供了表单输入自动验证的功能Q在包org.apache.struts.validator中提供了许多有用的类Q其中最常见的就是DynaValidatorFormcR?/P>

DynaValidatorFormc?/B>

DynaValidatorForm是DynaActionForm的子c,它能够提供动态ActionForm和自动表单输入验证的功能。和使用DynaActionFormcMQ你必须首先在配|文件中q行配置Q?/P>

<form-beans>
	<form-bean name="loginForm" type="org.apache.struts.validator.DynaValidatorForm"> 
		<form-property name="actionClass" type="java.lang.String"/>     
		<form-property name="username" type="java.lang.String"/> 
		<form-property name="password" type="java.lang.String"/>  
	</form-bean>
</form-beans>

同时要定义验证的插gQ?/P>

  <plug-in className="org.apache.struts.validator.ValidatorPlugIn">
	<set-property property="pathnames"  
	value="/WEB-INF/validator-rules.xml,  
	/WEB-INF/validation.xml"/>
  </plug-in>

其中的validator.xml和validator-rules.xml分别表示验证定义和验证规则的内容Q可以合q在一PQ比如针对上例中的DynaValidatorFormQ我们有如下验证定义Qvalidator.xmlQ:(x)


<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE form-validation PUBLIC  
"-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.0//EN"  
"http://jakarta.apache.org/commons/dtds/validator_1_0.dtd">
<!--    Validation Rules    $Id: validation.xml-->

<form-validation>  
<!-- ========== Default Language Form Definitions ===================== -->
<formset>  
	<form name="loginForm">     
		<field property="username" depends="required, minlength,maxlength"> 
			<arg0   key="prompt.username"/>          
			<arg1   key="${var:minlength}" name="minlength" resource="false"/>       
			<arg2   key="${var:maxlength}" name="maxlength" resource="false"/>              
			<var>                
				<var-name>maxlength</var-name>    
				<var-value>16</var-value>         
			</var>          
			<var>      
				<var-name>minlength</var-name>     
				<var-value>3</var-value>         
			</var>       
		</field>     
		<field property="password" depends="required, minlength,maxlength" bundle="alternate">          
			<arg0   key="prompt.password"/>   
			<arg1   key="${var:minlength}" name="minlength" resource="false"/>          
			<arg2   key="${var:maxlength}" name="maxlength" resource="false"/>  
			<var>              
				<var-name>maxlength</var-name>     
				<var-value>16</var-value>        
			</var>          
			<var>      
				<var-name>minlength</var-name> 
				<var-value>3</var-value>       
			</var>        
		</field>    
	</form>   
</formset>
</form-validation>

从上q定义中Q我们可以看到对于字Dusername有三w证:(x)required, minlength, maxlengthQ意思是该字D不能ؓ(f)I,而且长度??6之间。而validator-rules.xml文g则可以采用Struts提供的缺省文件。注意在<form-bean>中定义的form是如何与validation.xml中的form兌h的。最后,要启动自动验证功能,q需要将Action配置的validate属性设|ؓ(f)true?/P>


<action path="/login"  
type="com.ncu.test.LoginAction"
name="loginForm"          
scope="request"         
input="tile.userLogin"validate="true">

此时QStruts根据xml配置文g中的定义来检验表单输入,q将不符合要求的错误信息输出到页面。但是你可能?x)想Q这个功能虽然好Q可是什么检验都跑到服务器端执行Q效率方面和用户易用性方面是不是有些问题Q你可能?x)怀念v那简单的JavaScript客户端验证?/P>

不用担心Q在Struts 1.1中也支持JavaScript客户端验证。如果你选择了客L(fng)验证Q当某个表单被提交以后,Struts 1.1启动客户端验证,如果览器不支持JavaScript验证Q则服务器端验证被启动,q种双重验证机制能够最大限度地满各种开发者的需要。JavaScript验证代码也是在validator-rules.xml文g中定义的。要启动客户端验证,你必d相应的JSP文g中做如下讄Q?BR>

  1. ?lt;html:form>增加onsubmit属?
  2. 讄Javascript支持

下表中列Z一JSP文g的示例代码,U字部分为Javascript验证所需代码?/P>

<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<table bgcolor="#9AFF9A" cellspacing="0" cellpadding="10" border="1" width="100%">
	<tr>
	<td> 
	<table cellspacing="0" cellpadding="0" border="0" width="100%"> 
	<tr bgcolor="#696969"> 
		<td align="center">     
		<font color="#FFFFFF">Panel 3: Profile</font>  
		</td>
		</tr> 
	<tr>  
		<td><br> 
		<html:errors/>  
		<html:form action="/login.do" focus="username"  onsubmit="return validateLoginForm(this);">  
		<html:hidden property="actionClass"/>   
		<center>      
		<table>      
			<tr>        
			<td>UserName:</td>   
			<td><html:text property="username" size="20"/></td> 
			</tr> 
			<tr>  
			<td>Password:</td>   
			<td><html:password property="password" size="20"/></td>    
			</tr>  
			<tr>  
			<td colspan=2><html:submit property="submitProperty" value="Submit"/></td>     
		</table>   
		</center>  
		</html:form> 
		<html:javascript formName="loginForm" dynamicJavascript="true" staticJavascript="false"/>  
	
	<script language="Javascript1.1" src="staticJavascript.jsp"></script>  
	</td> 
	</tr> 
	</table>
	</td>
	</tr>
</table>

其中onsubmit的gؓ(f)"return validateLoginForm(this);"Q它的语法ؓ(f)Q?/P>

return validate + struts-config.xml中定义的form-bean名称 + (this);

staticJavascript.jsp的内容ؓ(f)Q?/P>

<%@ page language="java" %>
<%-- set document type to Javascript (addresses a bug in Netscape according to a web resource --%>
<%@ page contentType="application/x-javascript" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<html:javascript dynamicJavascript="false" staticJavascript="true"/>


如果validator-rules.xml中定义的基本验证功能不能满你的需求,你可以自己添加所需的验证类型?/P>

6、Action

我们通过l承ActioncL实现具体的执行类。具体Actioncȝ功能一般都在executeQ以前是performҎ(gu)Q方法中完成Q其中主要涉?qing)到以下几个斚wQ?BR>

  1. 辅助ActionFormq行一些表单数据的(g)查?
  2. 执行必要的业务逻辑Q比如存取数据库Q调用实体bean{?
  3. 更新服务器端的bean数据Q后l对象中可能?x)用到这些数据,比如在JSP中利用bean:write来获得这些数据?
  4. Ҏ(gu)处理l果军_E序的去处,q以ActionForward对象的Ş式返回给ActionServlet?

提示Q?/B>׃在Action和ActionForm中都可以实现验证Ҏ(gu)Q那么如何来安排它们之间的分工呢Q一般来_(d)我们U着MVC分离的原则,也就是视囄的验证工作放在ActionForm来完成,比如输入不能为空Qemail格式是否正确Q利用ValidatorForm可以很轻村֜完成q些工作。而与具体业务相关的验证则攑օAction中,q样可以获得最大ActionForm重用性的可能?/I>

前面我们提到q,我们d业务逻辑执行分离到单独的JavaBean中,而Action只负责错误处理和程控制。而且考虑到重用性的原因Q在执行业务逻辑的JavaBean中不要引用Q何与Web应用相关的对象,比如HttpServletRequestQHttpServletResponse{对象,而应该将其{化ؓ(f)普通的Java对象。关于这一点,可以参考Petstore中WAF框架的实现思\?/P>

此外Q你可能q注意到execute与perform的一个区别:(x)executeҎ(gu)单地掷出Exception异常Q而performҎ(gu)则掷出ServletException和IOException异常。这不是说Struts 1.1在异常处理功能方面弱化了Q而是Z配合Struts 1.1中一个很好的功能--宣称式异常处理机制?/P>

7、宣U式异常处理

和EJB中的宣称式事务处理概늱|宣称式异常处理其实就是可配置的异常处理,你可以在配置文g中指定由谁来处理ActioncM掷出的某U异常。你可以按照以下步骤来完成该功能Q?BR>

  1. 实现org.apache.struts.action.ExceptionHandler的子c,覆盖executeҎ(gu)Q在该方法中处理异常q且q回一个ActionForward对象
  2. 在配|文件中配置异常处理对象Q你可以配置一个全局的处理类或者单独ؓ(f)每个Action配置处理c?

下表定义了一个全局的处理类CustomizedExceptionHandlerQ它被用来处理所有的异常?/P>

<global-exceptions> 
<exception 
	handler="com.yourcorp.CustomizedExceptionHandler" 
	key="global.error.message" 
	path="/error.jsp"    
	scope="request"    
	type="java.lang.Exception"/>
</global-exceptions>

其中具体的参数含义,可以参考ExceptionHandler.java源文件?/P>

8、taglib

讲完了模型和控制器,接下来我们要涉及(qing)的是视图。视囄角色主要是由JSP来完成,从JSP的规范中可以看出Q在视图层可?折腾"的技术不是很多,主要的就是自定义标记库的应用。Struts 1.1在原有的四个标记库的基础上新增了两个标记?-Tiles和Nested?/P>

其中Tiles除了替代Template的基本模板功能外Q还增加了布局定义、虚拟页面定义和动态页面生成等功能。Tiles强大的模板功能能够ə面获得最大的重用性和灉|性,此外可以l合Tiles配置文g中的面定义和Action的{发逻辑Q即你可以将一个Action转发C个在Tiles配置文g中定义的虚拟面Q从而减页面的数量。比如,下表中的Action定义了一个{发\径,它的l点是tile.userMainQ而后者是你在Tiles配置文g中定义的一个页面?/P>

<!-- ========== Action Mapping Definitions ============================== -->
<action-mappings>  
<!-- Action mapping for profile form --> 
	<action path="/login"   
		type="com.ncu.test.LoginAction"      
		name="loginForm"    
		scope="request"     
		input="tile.userLogin"
		validate="true">     
		<forward name="success" path="tile.userMain"/>   
	</action> 
</action-mappings>

Tiles配置文gQtiles-defs.xml


<!DOCTYPE tiles-definitions PUBLIC 
"-//Apache Software Foundation//DTD Tiles Configuration//EN"       "http://jakarta.apache.org/struts/dtds/tiles-config.dtd">
<tiles-definitions>  
<!-- =======================================================  --> 
<!-- Master definitions                                       -->
<!-- =======================================================  --> 
<!-- Page layout used as root for all pages. --> 

<definition name="rootLayout" path="/tiles-layouts/rootLayout.jsp"> 
	<put name="titleString" value="CHANGE-ME"/>   
	<put name="topMenu" value="/tiles-components/topMenu.jsp"/> 
	<put name="leftMenu" value="/tiles-components/panel1.jsp"/>  
	<put name="body" value="CHANGE-ME"/>   
	<put name="footer" value="/tiles-components/footer.jsp"/> 
</definition> 

<!-- =======================================================  --> 
<!-- Page definitions 					-->  
<!-- =======================================================  --> 

<!-- User Login page --> 
<definition name="tile.userLogin" extends="rootLayout"> 
	<put name="titleString" value="User Login"/>  
	<put name="body" value="/src/userLogin.jsp"/> 
</definition>  
<!-- User Main page --> 
<definition name="tile.userMain" extends="rootLayout"> 
	<put name="titleString" value="User Main"/>  
	<put name="body" value="/src/userMain.jsp"/> 
</definition>
</tiles-definitions>

而Nested标记库的作用是让以上q些基本标记库能够嵌套用,发挥更大的作用?/P>

9、Commons Logging 接口

所谓的Commons Logging接口Q是指将日志功能的用与日志具体实现分开Q通过配置文g来指定具体用的日志实现。这样你可以在Struts 1.1中通过l一的接口来使用日志功能Q而不ȝ具体是利用的哪种日志实现Q有点于cMJDBC的功能。Struts 1.1中支持的日志实现包括QLog4JQJDK Logging APIQ?LogKitQNoOpLog和SimpleLog?/P>

你可以按照如下的方式来用Commons Logging接口Q可以参照Struts源文中的许多cd玎ͼQ?/P>

package com.foo;
// ...
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
//...
	public class Foo {    
	// ...    
	private static Log log = LogFactory.getLog(Foo.class);
	// ...    
	public void setBar(Bar bar) {       
		if (log.isTraceEnabled()) {         
			log.trace("Setting bar to " + bar);   
		}      
	this.bar = bar;   
	}
// ...
}

而开启日志功能最单的办法是在WEB-INF/classes目录下添加以下两个文Ӟ(x)

commons-logging.properties文gQ?/P>

# Note: The Tiles framework now uses the commons-logging package to output different information or debug statements. 
Please refer to this package documentation to enable it. The simplest way to enable logging is to create two files in 
WEB-INF/classes:
# commons-logging.properties
# org.apache.commons.logging.Log=org.apache.commons.logging.impl.SimpleLog
# simplelog.properties
# # Logging detail level,
# # Must be one of ("trace", "debug", "info", "warn", "error", or "fatal").
#org.apache.commons.logging.simplelog.defaultlog=trace
org.apache.commons.logging.Log=org.apache.commons.logging.impl.SimpleLog

simplelog.properties文gQ?/P>

# Logging detail level,
# Must be one of ("trace", "debug", "info", "warn", "error", or "fatal").
org.apache.commons.logging.simplelog.defaultlog=fatal

q里我们采用的日志实现是SimpleLogQ你可以在simplelog.properties文g指定日志明细的别:(x)traceQdebugQinfoQwarnQerror和fatalQ从trace到fatal错误U别来高Q同时输出的日志信息也越来越。而这些别是和org.apache.commons.logging.log接口中的Ҏ(gu)一一对应的。这些别是向后包含的,也就是前面的U别包含后面U别的信息?/P>



W笨 2005-04-08 11:57 发表评论
]]>
վ֩ģ壺 ëƬȫѹۿ| ƷƵһ| Ƶ| ޹ƷSSS߹ۿAV| AVר| avӰѿ| wwwƵѿ| Ƶ߾Ʒ| ޾ƷƷԲվ| avվ߿| Ʒһʽֱ| ˳þõӰվ| Ƶ߹ۿ| þþWWW˳ɾƷ| Ƶվ| ޾Ʒ˳| ɫͼƬС˵| 91Ƶ| aëƬ18ϹۿƷ| һ| 99þþþþѿ| õĻ2019| þþƷۺɫ| Ʒɫͼ| žžƷƵ| ޹˸߹ۿ| AVһ| ÿµavƬ߹ۿ| ޹ۺ߾Ʒ| ѧһ| þþþùƷѿ| ޸Ļַ| 77777ҹþö| ˳Ƶ69| һһëƬȫ| ɫ͵͵Ůùۿŷ| ҹѸСӰ| ޾ҹþþþþ| պӾƷ| ëƬѹۿƵ| ޳ַ|