﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>语源科技BlogJava-Blog空间</title><link>http://www.blogjava.net/yanrui312/</link><description>JAVA</description><language>zh-cn</language><lastBuildDate>Fri, 19 Jun 2026 10:17:36 GMT</lastBuildDate><pubDate>Fri, 19 Jun 2026 10:17:36 GMT</pubDate><ttl>60</ttl><item><title>Hibernate 能够满足我们的验证需求 </title><link>http://www.blogjava.net/yanrui312/articles/144729.html</link><dc:creator>颜颜</dc:creator><author>颜颜</author><pubDate>Thu, 13 Sep 2007 02:11:00 GMT</pubDate><guid>http://www.blogjava.net/yanrui312/articles/144729.html</guid><wfw:comment>http://www.blogjava.net/yanrui312/comments/144729.html</wfw:comment><comments>http://www.blogjava.net/yanrui312/articles/144729.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yanrui312/comments/commentRss/144729.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yanrui312/services/trackbacks/144729.html</trackback:ping><description><![CDATA[<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr valign="top">
            <td width="10"><img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="10" /></td>
            <td width="100%">
            <table class="no-print" cellspacing="0" cellpadding="0" width="160" align="right" border="0">
                <tbody>
                    <tr>
                        <td width="10"><img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="10" /></td>
                        <td>
                        <table cellspacing="0" cellpadding="0" width="150" border="0">
                            <tbody>
                                <tr>
                                    <td class="v14-header-1-small"></td>
                                </tr>
                            </tbody>
                        </table>
                        <!--start RESERVED FOR FUTURE USE INCLUDE FILES--><!-- this content will be automatically generated across all content areas --><br />
                        <!--end RESERVED FOR FUTURE USE INCLUDE FILES--><br />
                        </td>
                    </tr>
                </tbody>
            </table>
            <p>尽管在 Web 应用程序中尽可能多的层次中构建数据验证非常重要，但是这样做却非常耗时，以至于很多开发人员都会干脆忽略这个步骤 —— 这可能会导致今后大量问题的产生。但是随着最新版本的 Java&#8482; 平台中引入了注释，验证变得简单得多了。在本文中，Ted Bergeron 将向您介绍如何使用 Hibernate Annotations 的 Validator 组件在 Web 应用程序中轻松构建并维护验证逻辑。</p>
            <!--start RESERVED FOR FUTURE USE INCLUDE FILES--><!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters --><!--end RESERVED FOR FUTURE USE INCLUDE FILES-->
            <p>有时会有一种工具，它可以真正满足开发人员和架构师的需求。开发人员在第一次下载这种工具当天就可以在自己的应用程序中开始使用这种工具。理论上来说，这种工具在开发人员花费大量时间来掌握其用法之前就可以从中获益。架构师也很喜欢这种工具，因为它可以将开发人员导向更高理论层次的实现。Hibernate Annotations 的 Validator 组件就是一种这样的工具。</p>
            <table cellspacing="0" cellpadding="0" width="40%" align="right" border="0">
                <tbody>
                    <tr>
                        <td width="10"><img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="10" /></td>
                        <td>
                        <table cellspacing="0" cellpadding="5" width="100%" border="1">
                            <tbody>
                                <tr>
                                    <td bgcolor="#eeeeee"><a name="sidebar1"><strong>开始之前需要了解的内容</strong></a><br />
                                    <p>在阅读本文之前，应该对 Java 平台版本 5（尤其是注释）、JSP 2.0（因为本文中创建了一些标签文件，并在 TLD 中定义了一些函数，它们都是 JSP 2.0 的新特性）和 Hibernate 及 Spring 框架有一个基本的了解。请注意即使不使用 Hibernate 来实现持久性，也可以在自己的应用程序中使用 Hibernate Validator。</p>
                                    </td>
                                </tr>
                            </tbody>
                        </table>
                        </td>
                    </tr>
                </tbody>
            </table>
            <p>Java SE 5 为 Java 语言提供了很多需要的增强功能，不过其他增强功能可能都不如 <em>注释</em> 这样潜力巨大。使用 注释，我们就终于具有了一个标准、一级的元数据框架为 Java 类使用。Hibernate 用户手工编写 *.hbm.xml 文件已经很多年了（或者使用 XDoclet 来自动实现这个任务）。如果手工创建了 XML 文件，那就必须对每个所需要的持久属性都更新这两个文件（类定义和 XML 映射文档）。使用 HibernateDoclet 可以简化这个过程（请参看清单 1 给出的例子），但是这需要我们确认自己的 HibernateDoclet 版本支持要使用的 Hibernate 的版本。doclet 信息在运行时也是不可用的，因为它被编写到了 Javadoc 风格的注释中了。Hibernate Annotations，如图 2 所示，通过提供一个标准、简明的映射类的方法和所添加的运行时可用性来对这些方式进行改进。</p>
            <br />
            <a name="listing1"><strong>清单 1. 使用 HibernateDoclet 的 Hibernate 映射代码</strong></a><br />
            <table cellspacing="0" cellpadding="0" width="100%" border="0">
                <tbody>
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode">/**
                        * @hibernate.property column="NAME" length="60" not-null="true"
                        */
                        public String getName() {
                        return this.name;
                        }
                        /**
                        * @hibernate.many-to-one column="AGENT_ID" not-null="true" cascade="none"
                        *                        outer-join="false" lazy="true"
                        */
                        public Agent getAgent() {
                        return agent;
                        }
                        /**
                        * @hibernate.set lazy="true" inverse="true" cascade="all" table="DEPARTMENT"
                        * @hibernate.collection-one-to-many class="com.triview.model.Department"
                        * @hibernate.collection-key column="DEPARTMENT_ID" not-null="true"
                        */
                        public List&lt;Department&gt; getDepartment() {
                        return department;
                        }
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <br />
            <a name="listing2"><strong>清单 2. 使用 Hibernate Annotations 的 Hibernate 映射代码</strong></a><br />
            <table cellspacing="0" cellpadding="0" width="100%" border="0">
                <tbody>
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode">@NotNull
                        @Column(name = "name")
                        @Length(min = 1, max = NAME_LENGTH) // NAME_LENGTH is a constant declared elsewhere
                        public String getName() {
                        return name;
                        }
                        @NotNull
                        @ManyToOne(cascade = {CascadeType.MERGE }, fetch = FetchType.LAZY)
                        @JoinColumn(name = "agent_id")
                        public Agent getAgent() {
                        return agent;
                        }
                        @OneToMany(mappedBy = "customer", fetch = FetchType.LAZY)
                        public List&lt;Department&gt; getDepartment() {
                        return department;
                        }
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p>如果使用 HibernateDoclet，那么直到生成 XML 文件或运行时才能捕获错误。使用 注释，在编译时就可以检测出很多错误；或者如果在编辑时使用了很好的 IDE，那么在编辑时就可以检测出部分错误。在从头创建应用程序时，可以利用 hbm2ddl 工具为自己的数据库从 hbm.xml 文件中生成 DDL。一些重要的信息 —— 比如<code>name</code> 属性的最大长度必须是 60 个字符，或者 DDL 应该添加非空约束 —— 都被从 HibernateDoclet 项添加到 DDL 中。当使用注释时，我们可以以类似的方式自动生成 DDL。</p>
            <p>尽管这两种代码映射方式都可以使用，不过注释的优势更为明显。使用注释，可以用一些常量来指定长度或其他值。编译循环的速度更快，并且不需要生成 XML 文件。其中最大的优势是可以访问一些有用信息，例如运行时的非空注释或长度。除了清单 2 给出的注释之外，还可以指定一些验证的约束。所包含的部分约束如下：</p>
            <ul>
                <li><code>@Max(value = 100)</code>
                <li><code>@Min(value = 0)</code>
                <li><code>@Past</code>
                <li><code>@Future</code>
                <li><code>@Email</code> </li>
            </ul>
            <p>在适当条件下，这些注释会引起由 DDL 生成检查约束。（显然，<code>@Future</code> 并不是一个适当的条件。）还可以根据需要创建定制约束注释。</p>
            <p><a name="1"><span class="atitle">验证和应用程序层</span></a></p>
            <p>编写验证代码是一个烦人且耗时的过程。通常，很多开发人员都会放弃在特定的层进行有效性验证，从而可以节省一些时间；但是所节省的时间是否能够弥补在这个地方因忽略部分功能所引起的缺陷却非常值得探讨。如果在所有应用程序层中创建并维护验证所需要的时间可以极大地减少，那么争论的焦点就会转向是否要在多个层次中进行有效性验证。假设有一个应用程序，它让用户使用一个用户名、密码和信用卡号来创建一个帐号。在这个应用程序中所希望进行验证的组件如下：</p>
            <ul>
                <li><strong>视图：</strong> 通过 JavaScript 进行验证可以避免与服务器反复进行交互，这样可以提供更好的用户体验。用户可以禁用 JavaScript，因此这个层次的验证最好要有，但是却并不可靠。对所需要的域进行简单的验证是必须的。<br />
                <br />
                <li><strong>控制器：</strong> 验证必须在服务器端的逻辑中进行处理。这个层次中的代码可以以适合某个特定用途的方式处理验证。例如，在添加新用户时，控制器可以在进行处理之前检查指定的用户名是否已经存在。<br />
                <br />
                <li><strong>服务：</strong> 相对复杂的业务逻辑验证通常都最适合放到服务层中。例如，一旦有一个信用卡对象看起来有效，就应该使用信用卡处理服务对这个信用卡的信息进行确认。<br />
                <br />
                <li><strong>DAO：</strong> 在数据到达这个层次时，应该已经是有效的了。尽管如此，执行一次快速检查从而确保所需要的域都非空并且值也都在特定的范围或遵循特定的格式（例如 e-mail 地址域就应该包含一个有效的 e-mail 地址）也是非常有益的。在此处捕获错误总比产生可以避免的 <code>SQLException</code> 错误要好。<br />
                <br />
                <li><strong>DBMS：</strong> 这是通常可以忽略验证的地方。即使当前正在构建的应用程序是数据库的惟一客户机，将来还可能会添加其他客户机。如果应用程序有一些 bug（大部分应用程序都可能会有 bug），那么无效的数据也可能会被发送给数据库。在这种情况中，如果走运，就可以找到无效的数据，并且需要分析这些数据是否可以清除，以及如何清除。<br />
                <br />
                <li><strong>模型：</strong> 这是进行验证的一个理想地方，它不需要访问外部服务，也不需要了解持久性数据。例如，某业务逻辑可能会要求用户至少提供一个联系信息，这可以是一个电话号码也可以是一个 e-mail 地址；可以使用模型层的验证来确保用户的确提供了这种信息。 </li>
            </ul>
            <p>进行验证的一种典型方法是对简单的验证使用 Commons Validator，并在控制器中编写其他一些验证逻辑。Commons Validator 可以生成 JavaScript 来对视图中的验证进行处理。但是 Commons Validator 也有自己的缺陷：它只能处理简单的验证问题，并且将验证的信息都保存到了 XML 文件中。Commons Validator 被设计用来与 Struts 一起使用，而且没有提供一种简单的方法在应用程序层间重用验证的声明。</p>
            <p>在规划有效性验证策略时，选择在错误发生时简单地处理这些错误是远远不够的。一种良好的设计同时还要通过生成一个友好的用户界面来防止出现错误。采用预先进行的方法进行验证可以极大地增强用户对于应用程序的理解。不幸的是，Commons Validator 并没有对此提供支持。假设希望 HTML 文件设置文本域的 <code>maxlength</code> 属性来与验证匹配，或者在文本域之后放上一个百分号（%）来表示要输入百分比的值。通常，这些信息都被硬编写到 HTML 文档中了。如果决定修改 <code>name</code> 属性来支持 75 个字符，而不是 60 个字符，那么需要改动多少地方呢？在很多应用程序中，通常都需要：</p>
            <ul>
                <li>更新 DDL 来增大数据库列的长度（通过 HibernateDoclet、 hbm.xml 或 Hibernate Annotations）。
                <li>更新 Commons Validator XML 文件将最大值增加到 75。
                <li>更新所有与这个域有关的 HTML 表单，以修改 <code>maxlength</code> 属性。 </li>
            </ul>
            <p>更好的方法是使用 Hibernate Validator。验证的定义都被通过注释 添加到了模型层中，同时还有对所包含的验证处理的支持。如果选择充分利用所有的 Hibernate，这个 Validator 就可以在 DAO 和 DBMS 层也提供验证。在下面给出的样例代码中，将使用 reflection 和 JSP 2.0 标签文件多执行一个步骤，从而充分利用注释 为视图层动态生成代码。这可以清除在视图中使用的硬编写的业务逻辑。</p>
            <p>在清单 3 中，<code>dateOfBirth</code> 被注释为 <code>NotNull</code> 和过去的日期。 Hibernate 的 DDL 生成代码对这个列添加了一个非空约束，以及一个要求日期必须是之前日期的检查约束。e-mail 地址也是非空的，必须匹配 e-mail 地址的格式。这会生成一个非空约束，但是不会生成匹配这种格式的检查约束。</p>
            <br />
            <a name="listing3"><strong>清单 3. 通过 Hibernate Annotations 进行映射的简单联系方式</strong></a><br />
            <table cellspacing="0" cellpadding="0" width="100%" border="0">
                <tbody>
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode">/**
                        * A Simplified object that stores contact information.
                        *
                        * @author Ted Bergeron
                        * @version $Id: Contact.java,v 1.1 2006/04/24 03:39:34 ted Exp $
                        */
                        @MappedSuperclass
                        @Embeddable
                        public class Contact implements Serializable {
                        public static final int MAX_FIRST_NAME = 30;
                        public static final int MAX_MIDDLE_NAME = 1;
                        public static final int MAX_LAST_NAME = 30;
                        private String fname;
                        private String mi;
                        private String lname;
                        private Date dateOfBirth;
                        private String emailAddress;
                        private Address address;
                        public Contact() {
                        this.address = new Address();
                        }
                        @Valid
                        @Embedded
                        public Address getAddress() {
                        return address;
                        }
                        public void setAddress(Address a) {
                        if (a == null) {
                        address = new Address();
                        } else {
                        address = a;
                        }
                        }
                        @NotNull
                        @Length(min = 1, max = MAX_FIRST_NAME)
                        @Column(name = "fname")
                        public String getFirstname() {
                        return fname;
                        }
                        public void setFirstname(String fname) {
                        this.fname = fname;
                        }
                        @Length(min = 1, max = MAX_MIDDLE_NAME)
                        @Column(name = "mi")
                        public String getMi() {
                        return mi;
                        }
                        public void setMi(String mi) {
                        this.mi = mi;
                        }
                        @NotNull
                        @Length(min = 1, max = MAX_LAST_NAME)
                        @Column(name = "lname")
                        public String getLastname() {
                        return lname;
                        }
                        public void setLastname(String lname) {
                        this.lname = lname;
                        }
                        @NotNull
                        @Past
                        @Column(name = "dob")
                        public Date getDateOfBirth() {
                        return dateOfBirth;
                        }
                        public void setDateOfBirth(Date dateOfBirth) {
                        this.dateOfBirth = dateOfBirth;
                        }
                        @NotNull
                        @Email
                        @Column(name = "email")
                        public String getEmailAddress() {
                        return emailAddress;
                        }
                        public void setEmailAddress(String emailAddress) {
                        this.emailAddress = emailAddress;
                        }
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <table cellspacing="0" cellpadding="0" width="40%" align="right" border="0">
                <tbody>
                    <tr>
                        <td width="10"><img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="10" /></td>
                        <td>
                        <table cellspacing="0" cellpadding="5" width="100%" border="1">
                            <tbody>
                                <tr>
                                    <td bgcolor="#eeeeee"><a name="sidebar2"><strong>样例应用程序</strong></a><br />
                                    在 <a href="http://www.ibm.com/developerworks/cn/java/j-hibval.html#download">下载</a> 一节，您可以下载一个样例应用程序，它展示了本文中采用的设计思想和代码。由于这是一个可以工作的应用程序，因此代码比本文中讨论的的内容更为复杂。例如，清单 9 就节选于标签文件 text.tag；这个样例应用程序具有标签文件使用的所有代码，以及其他三个类似的标签文件使用的代码（用于选择、隐藏和检查框的 HTML 元素）。由于这是一个可以工作的应用程序，它包含了一个在这种类型的应用程序中都可以找到的架构。还有一个 Ant 构建文件、Spring 和 Hibernate XML 封装代码，以及 log4j 配置。虽然这些都不是本文介绍的重点，但是您会发现仔细研究一下这个样例应用程序的源代码是非常有用的。 </td>
                                </tr>
                            </tbody>
                        </table>
                        </td>
                    </tr>
                </tbody>
            </table>
            <p>如果需要，Hibernate DAO 实现也可以使用 Validation Annotations。所需做的是在 hibernate.cfg.xml 文件中指定基于 Hibernate 事件的验证规则。（更多信息请参考 Hibernate Validator 的文档；可以在 <a href="http://www.ibm.com/developerworks/cn/java/j-hibval.html#resources">参考资料</a> 一节中找到相关的链接）。如果真地希望抄近路，您可以只捕获服务或控制器中的 <code>InvalidStateException</code> 异常，并循环遍历 <code>InvalidValue</code> 数组。</p>
            <p><a name="2"><span class="atitle">对控制器添加验证</span></a></p>
            <p>要执行验证，需要创建一个 Hibernate 的 <code>ClassValidator</code> 实例。这个类进行实例化的代价可能会很高，因此最好只对希望进行验证的每个类来进行实例化。一种方法是创建一个实用工具类，对每个模型对象存储一个 <code>ClassValidator</code> 实例，如清单 4 所示：</p>
            <br />
            <a name="listing4"><strong>清单 4. 处理验证的实用工具类</strong></a><br />
            <table cellspacing="0" cellpadding="0" width="100%" border="0">
                <tbody>
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode">/**
                        * Handles validations based on the Hibernate Annotations Validator framework.
                        * @author Ted Bergeron
                        * @version $Id: AnnotationValidator.java,v 1.5 2006/01/20 17:34:09 ted Exp $
                        */
                        public class AnnotationValidator {
                        private static Log log = LogFactory.getLog(AnnotationValidator.class);
                        // It is considered a good practice to execute these lines once and
                        // cache the validator instances.
                        public static final ClassValidator&lt;Customer&gt; CUSTOMER_VALIDATOR =
                        new ClassValidator&lt;Customer&gt;(Customer.class);
                        public static final ClassValidator&lt;CreditCard&gt; CREDIT_CARD_VALIDATOR =
                        new ClassValidator&lt;CreditCard&gt;(CreditCard.class);
                        private static ClassValidator&lt;? extends BaseObject&gt; getValidator(Class&lt;?
                        extends BaseObject&gt; clazz) {
                        if (Customer.class.equals(clazz)) {
                        return CUSTOMER_VALIDATOR;
                        } else if (CreditCard.class.equals(clazz)) {
                        return CREDIT_CARD_VALIDATOR;
                        } else {
                        throw new IllegalArgumentException("Unsupported class was passed.");
                        }
                        }
                        public static InvalidValue[] getInvalidValues(BaseObject modelObject) {
                        String nullProperty = null;
                        return getInvalidValues(modelObject, nullProperty);
                        }
                        public static InvalidValue[] getInvalidValues(BaseObject modelObject,
                        String property) {
                        Class&lt;? extends BaseObject&gt;clazz = modelObject.getClass();
                        ClassValidator validator = getValidator(clazz);
                        InvalidValue[] validationMessages;
                        if (property == null) {
                        validationMessages = validator.getInvalidValues(modelObject);
                        } else {
                        // only get invalid values for specified property.
                        // For example, "city" applies to getCity() method.
                        validationMessages = validator.getInvalidValues(modelObject, property);
                        }
                        return validationMessages;
                        }
                        }
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p>在清单 4 中，创建了两个 <code>ClassValidator</code>，一个用于 <code>Customer</code>，另外一个用于 <code>CreditCard</code>。这两个希望进行验证的类可以调用 <code>getInvalidValues(BaseObject modelObject)</code>，会返回 <code>InvalidValue[]</code>。这则会返回一个包含模型对象实例错误的数组。另外，这个方法也可以通过提供一个特定的属性名来调用，这样做会只返回与该域有关的错误。</p>
            <p>在使用 Spring MVC 和 Hibernate Validator 时，为信用卡创建一个验证过程变得非常简单，如清单 5 所示：</p>
            <br />
            <a name="listing5"><strong>清单 5. Spring MVC 控制器使用的 CreditCardValidator</strong></a><br />
            <table cellspacing="0" cellpadding="0" width="100%" border="0">
                <tbody>
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode">/**
                        * Performs validation of a CreditCard in Spring MVC.
                        *
                        * @author Ted Bergeron
                        * @version $Id: CreditCardValidator.java,v 1.2 2006/02/10 21:53:50 ted Exp $
                        */
                        public class CreditCardValidator implements Validator {
                        private CreditCardService creditCardService;
                        public void setCreditCardService(CreditCardService service) {
                        this.creditCardService = service;
                        }
                        public boolean supports(Class clazz) {
                        return CreditCard.class.isAssignableFrom(clazz);
                        }
                        public void validate(Object object, Errors errors) {
                        CreditCard creditCard = (CreditCard) object;
                        InvalidValue[] invalids = AnnotationValidator.getInvalidValues(creditCard);
                        // Perform "expensive" validation only if no simple errors found above.
                        if (invalids == null || invalids.length == 0) {
                        boolean validCard = creditCardService.validateCreditCard(creditCard);
                        if (!validCard) {
                        errors.reject("error.creditcard.invalid");
                        }
                        } else {
                        for (InvalidValue invalidValue : invalids) {
                        errors.rejectValue(invalidValue.getPropertyPath(),
                        null, invalidValue.getMessage());
                        }
                        }
                        }
                        }
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p><code>validate()</code> 方法只需要将 <code>creditCard</code> 实例传递给这个验证过程，从而返回 <code>InvalidValue</code> 数组。如果发现了一个或多个这种简单错误，那么就可以将 Hibernate 的 <code>InvalidValue</code> 数组转换成 Spring 的 <code>Errors</code> 对象。如果用户已经创建了这个信用卡并且没有出现任何简单错误，就可以将更加彻底的验证委托给服务层进行。这一层可以与商业服务提供者一起对信用卡进行验证。</p>
            <p>现在我们已经看到这个简单的模型层注释是如何平衡到控制器、DAO 和 DBMS 层的验证的。在 HibernateDoclet 和 Commons Validator 中发现的验证逻辑的重合现在都已经统一到模型中了。尽管这是一个非常受欢迎的改进，但是视图层传统上来说一直是最需要进行详细验证的地方。</p>
            <br />
            <table cellspacing="0" cellpadding="0" width="100%" border="0">
                <tbody>
                    <tr>
                        <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
                        <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
                    </tr>
                </tbody>
            </table>
            <table class="no-print" cellspacing="0" cellpadding="0" align="right">
                <tbody>
                    <tr align="right">
                        <td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
                        <table cellspacing="0" cellpadding="0" border="0">
                            <tbody>
                                <tr>
                                    <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                                    </td>
                                    <td valign="top" align="right"><a class="fbox" href="http://www.ibm.com/developerworks/cn/java/j-hibval.html#main"><strong>回页首</strong></a></td>
                                </tr>
                            </tbody>
                        </table>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <br />
            <p><a name="3"><span class="atitle">为视图添加验证</span></a></p>
            <p>在下面的例子中，使用了 Spring MVC 和 JSP 2.0 标签文件。JSP 2.0 允许在 TLD 文件中对定制函数进行注册，并在一个标签文件中进行调用。标签文件类似于 taglibs，但是它们是使用 JSP 代码编写的，而不是使用 Java 代码编写的。采用这种方法，使用 Java 语言写好的代码就可以封装成函数，而使用 JSP 写好的代码则可以放入标签文件中。在这种情况中，对注释的处理需要使用映像，这会由几个函数来执行。绑定 Spring 或呈现 XHTML 的代码也是标签文件的一部分。</p>
            <p>清单 6 中节选的 TLD 代码定义 text.tag 文件可以使用，并定义了一个名为 <code>required</code> 的函数。</p>
            <br />
            <a name="listing6"><strong>清单 6. 创建表单 TLD</strong></a><br />
            <table cellspacing="0" cellpadding="0" width="100%" border="0">
                <tbody>
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode">
                        &lt;?xml version="1.0" encoding="ISO-8859-1" ?&gt;
                        &lt;taglib xmlns="http://java.sun.com/xml/ns/j2ee"
                        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                        xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
                        http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
                        version="2.0"&gt;
                        &lt;tlib-version&gt;1.0&lt;/tlib-version&gt;
                        &lt;short-name&gt;form&lt;/short-name&gt;
                        &lt;uri&gt;formtags&lt;/uri&gt;
                        &lt;tag-file&gt;
                        &lt;name&gt;text&lt;/name&gt;
                        &lt;path&gt;/WEB-INF/tags/form/text.tag&lt;/path&gt;
                        &lt;/tag-file&gt;
                        &lt;function&gt;
                        &lt;description&gt;determine if field is required from Annotations&lt;/description&gt;
                        &lt;name&gt;required&lt;/name&gt;
                        &lt;function-class&gt;com.triview.web.Utilities&lt;/function-class&gt;
                        &lt;function-signature&gt;Boolean required(java.lang.Object,java.lang.String)
                        &lt;/function-signature&gt;
                        &lt;/function&gt;
                        &lt;/taglib&gt;
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p>清单 7 节选自 <code>Utilities</code> 类，其中包含了标签文件使用的所有函数。在前文中我们曾经说过，最适合使用 Java 代码编写的代码都被放到了几个 TLD 可以映射的函数中，这样标签文件就可以使用它们了；这些函数都是在 <code>Utilities</code> 类中进行编码的。因此，我们需要三样东西：定义这些类的 TLD 文件、<code>Utilities</code> 中的函数，以及标签文件本身，后者要使用这些函数。（第四样应该是使用这个标签文件的 JSP 页面。）</p>
            <p>在清单 7 中，给出了在 TLD 中引用的函数和另外一个表示给定属性是否是 <code>Date</code> 的方法。在这个类中要涉及到比较多的代码，但是本文限于篇幅，不会给出所有的代码；不过需要注意 <code>findGetterMethod()</code> 除了将表达式语言（Expression Language，EL）方法表示（<code>customer.contact</code>）转换成 Java 表示（<code>customer.getContact()</code>）之外，还执行了基本的映像操作。</p>
            <br />
            <a name="listing7"><strong>清单 7. Utilities 节选</strong></a><br />
            <table cellspacing="0" cellpadding="0" width="100%" border="0">
                <tbody>
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode">public static Boolean required(Object object, String propertyPath) {
                        Method getMethod = findGetterMethod(object, propertyPath);
                        if (getMethod == null) {
                        return null;
                        } else {
                        return getMethod.isAnnotationPresent(NotNull.class);
                        }
                        }
                        public static Boolean isDate(Object object, String propertyPath) {
                        return java.util.Date.class.equals(getReturnType(object, propertyPath));
                        }
                        public static Class getReturnType(Object object, String propertyPath) {
                        Method getMethod = findGetterMethod(object, propertyPath);
                        if (getMethod == null) {
                        return null;
                        } else {
                        return getMethod.getReturnType();
                        }
                        }
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p>此处可以清楚地看到在运行时使用 Validation annotations 是多么容易。可以简单地引用对象的 getter 方法，并检查这个方法是否有相关的给定的注释 。</p>
            <p>清单 8 中给出的 JSP 例子进行了简化，这样就可以着重查看相关的部分了。此处，这里有一个表单，它有一个选择框和两个输入域。所有这些域都是通过在 form.tld 文件中声明的标签文件进行呈现的。标签文件被设计成使用智能缺省值，这样就可以根据需要允许简单编码的 JSP 可以有定义更多信息的选项。关键的属性是 <code>propertyPath</code>，它使用 EL 符号将这个域映射为模型层属性，就像是使用 Spring MVC 的 <code>bind</code> 标签一样。</p>
            <br />
            <a name="listing8"><strong>清单 8. 一个包含表单的简单 JSP 页面</strong></a><br />
            <table cellspacing="0" cellpadding="0" width="100%" border="0">
                <tbody>
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode">&lt;%@ taglib tagdir="/WEB-INF/tags/form" prefix="form" %&gt;
                        &lt;form method="post" action="&lt;c:url value="/signup/customer.edit"/&gt;"&gt;
                        &lt;form:select propertyPath="creditCard.type" collection="${creditCardTypeCollection}"
                        required="true" labelKey="prompt.creditcard.type"/&gt;
                        &lt;form:text propertyPath="creditCard.number" labelKey="prompt.creditcard.number"&gt;
                        &lt;img src="&lt;c:url value="/images/icons/help.png"/&gt;" alt="Help"
                        onclick="new Effect.SlideDown('creditCardHelp')"/&gt;
                        &lt;/form:text&gt;
                        &lt;form:text propertyPath="creditCard.expirationDate"/&gt;
                        &lt;/form&gt;
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p>text.tag 文件的完整源代码太大了，不好放在这儿，因此清单 9 给出了其中关键的部分：</p>
            <br />
            <a name="listing9"><strong>清单 9. 标签文件 text.tag 节选</strong></a><br />
            <table cellspacing="0" cellpadding="0" width="100%" border="0">
                <tbody>
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode">
                        &lt;%@ attribute name="propertyPath" required="true" %&gt;
                        &lt;%@ attribute name="size" required="false" type="java.lang.Integer" %&gt;
                        &lt;%@ attribute name="maxlength" required="false" type="java.lang.Integer" %&gt;
                        &lt;%@ attribute name="required" required="false" type="java.lang.Boolean" %&gt;
                        &lt;%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %&gt;
                        &lt;%@ taglib uri="formtags" prefix="form" %&gt;
                        &lt;c:set var="objectPath" value="${form:getObjectPath(propertyPath)}"/&gt;
                        &lt;spring:bind path="${objectPath}"&gt;
                        &lt;c:set var="object" value="${status.value}"/&gt;
                        &lt;c:if test="${object == null}"&gt;
                        &lt;%-- Bind ignores the command object prefix, so simple properties of the command object
                        return null above. --%&gt;
                        &lt;c:set var="object" value="${commandObject}"/&gt;
                        &lt;%-- We depend on the controller adding this to request. --%&gt;
                        &lt;/c:if&gt;
                        &lt;/spring:bind&gt;
                        &lt;%-- If user did not specify whether this field is required,
                        query the object for this info. --%&gt;
                        &lt;c:if test="${required == null}"&gt;
                        &lt;c:set var="required" value="${form:required(object,propertyPath)}"/&gt;
                        &lt;/c:if&gt;
                        &lt;c:choose&gt;
                        &lt;c:when test="${required == null || required == false}"&gt;
                        &lt;c:set var="labelClass" value="optional"/&gt;
                        &lt;/c:when&gt;
                        &lt;c:otherwise&gt;
                        &lt;c:set var="labelClass" value="required"/&gt;
                        &lt;/c:otherwise&gt;
                        &lt;/c:choose&gt;
                        &lt;c:if test="${maxlength == null}"&gt;
                        &lt;c:set var="maxlength" value="${form:maxLength(object,propertyPath)}"/&gt;
                        &lt;/c:if&gt;
                        &lt;c:set var="isDate" value="${form:isDate(object,propertyPath)}"/&gt;
                        &lt;c:set var="cssClass" value="input_text"/&gt;
                        &lt;c:if test="${isDate}"&gt;
                        &lt;c:set var="cssClass" value="input_date"/&gt;
                        &lt;/c:if&gt;
                        &lt;div class="field"&gt;
                        &lt;spring:bind path="${propertyPath}"&gt;
                        &lt;label for="${status.expression}" class="${labelClass}"&gt;&lt;fmt:message
                        key="prompt.${propertyPath}"/&gt;&lt;/label&gt;
                        &lt;input type="text" name="${status.expression}" value="${status.value}"
                        id="${status.expression}"&lt;c:if test="${size != null}"&gt; size="${size}"&lt;/c:if&gt;
                        &lt;c:if test="${maxlength != null}"&gt; maxlength="${maxlength}"&lt;/c:if&gt;
                        class="${cssClass}"/&gt;
                        &lt;c:if test="${isDate}"&gt;
                        &lt;img id="${status.expression}_button"
                        src="&lt;c:url value="/images/icons/calendar.png"/&gt;" alt="calendar"
                        style="cursor: pointer;"/&gt;
                        &lt;script type="text/javascript"&gt;
                        Calendar.setup(
                        {
                        inputField : "${status.expression}", // ID of the input field
                        ifFormat : "%m/%d/%Y", // the date format
                        button : "${status.expression}_button" // ID of the button
                        }
                        );
                        &lt;/script&gt;
                        &lt;/c:if&gt;
                        &lt;span class="icons"&gt;&lt;jsp:doBody/&gt;&lt;/span&gt;
                        &lt;c:if test="${status.errorMessage != null &amp;&amp; status.errorMessage != ''}"&gt;
                        &lt;p class="fieldError"&gt;&lt;img id="${status.expression}_error"
                        src="&lt;c:url value="/images/icons/error.png"/&gt;"
                        alt="error"/&gt;${status.errorMessage}&lt;/p&gt;
                        &lt;/c:if&gt;
                        &lt;/spring:bind&gt;
                        &lt;/div&gt;
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p>我们马上就可以看出 <code>propertyPath</code> 是惟一需要的属性。<code>size</code>、 <code>maxlength</code> 和 <code>required</code> 都可以忽略。<code>objectPath var</code> 被设置为在 <code>propertyPath</code> 中引用的属性的父对象。因此，如果 <code>propertyPath</code> 是 <code>customer.contact.fax.number</code>， 那么 <code>objectPath</code> 就应该被设置为 <code>customer.contact.fax</code>。我们现在就使用 Spring 的 <code>bind</code> 标签绑定到了包含属性的对象上。这会将对象变量设置成对包含属性的实例的引用。接下来，检查这个标签的用户是否已经指定他/她们是否希望属性是必须的。允许表单开发人员覆盖从注释中返回的值是非常重要的，因为有时他/她们希望让控制器为所需要的域设置缺省值，而用户可能并不希望为这个域提供值。如果表单开发人员没有为 <code>required</code> 指定值，那么就可以调用这个表单 TLD 的 <code>required</code> 函数。这个函数调用了在 TLD 文件中映射的方法。这个方法简单地检查 <code>@NotNull</code> 注释；如果它发现某个属性具有这个注释，就将 <code>labelClass</code> 变量设置为必须的。可以类似地确定正确的 <code>maxlength</code> 以及这个域是否是一个 <code>Date</code>。</p>
            <p>接下来使用 Spring 来绑定到 <code>propertyPath</code> 上，而不是像前面一样只绑定到包含这个属性的对象上。这允许在生成 <code>label</code> 和 <code>input</code> HTML 标签时使用 <code>status.expression</code> 和 <code>status.value</code>。 <code>input</code> 标签也可以使用一个大小 <code>maxlength</code> 以及适当的类来生成。如果前面已经确定属性是一个 <code>Date</code>，现在就可以添加 JavaScript 日历了。（可以在 <a href="http://www.ibm.com/developerworks/cn/java/j-hibval.html#resources">参考资料</a> 一节找到一个很好的日历组件的链接）。注意根据需要链接属性、输入 ID 和图像 ID 的标签是多么简单。）这个 JavaScript 日历需要一个图像 ID 来匹配输入域，其后缀是 <code>_button</code>。</p>
            <p>最后，可以将 <code>&lt;jsp:doBody/&gt;</code> 封装到一个 <code>span</code> 标签中，这样允许表单开发人员在页面中添加其他图标，例如用来寻求帮助的图标。（清单 8 给出了一个为信用卡号域添加的帮助图标。）最后的部分是检查 Spring 是否为这个属性报告和显示了一个错误，并和一个错误图标一起显示。</p>
            <p>使用 CSS，就可以对必须的域进行一下装饰 —— 例如，让它们以红色显示、在文本边上显示一个星号，或者使用一个背景图像来装饰它。在清单 10 中，将必须的域的标签设置成黑色，而且后面显示一个红色的星号（在 Firefox 以及其他标准兼容的浏览器中），如果是在 IE 中则还会在左边加上一个小旗子的背景图像：</p>
            <br />
            <a name="listing10"><strong>清单 10. 对必须域进行装饰的 CSS 代码</strong></a><br />
            <table cellspacing="0" cellpadding="0" width="100%" border="0">
                <tbody>
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode">label.required {
                        color: black;
                        background-image: url( /images/icons/flag_red.png );
                        background-position: left;
                        background-repeat: no-repeat;
                        }
                        label.required:after {
                        content: '*';
                        }
                        label.optional {
                        color: black;
                        }
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p>日期输入域自动会在右边放上一个 JavaScript 日历图标。对所有的文本域设置正确的 <code>maxlength</code> 属性可以防止用户输入太多文本所引起的错误。可以扩展 <code>text</code> 标签来为输入域类设置其他的数据类型。可以修改 <code>text</code> 标签使用 HTML，而不是 XHTML（如果希望这样）。可以不太费力地获得具有正确语义的 HTML 表单，而且不需学习基于组件的框架知识，就可以利用基于组件的 Web 框架的优点。</p>
            <p>尽管标签文件生成的 HTML 文件可以帮助防止一些错误的产生，但是在视图层并没有任何代码来真正进行错误检查。由于可以使用类属性，现在就可以添加一些简单的 JavaScript 来实现这种功能了，如清单 11 所示。这里的 JavaScript 也可以是通用的，在任一表单中都可以重用。</p>
            <br />
            <a name="listing11"><strong>清单 11. 简单的 JavaScript 验证程序</strong></a><br />
            <table cellspacing="0" cellpadding="0" width="100%" border="0">
                <tbody>
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode">&lt;script type="text/javascript"&gt;
                        function checkRequired(form) {
                        var requiredLabels = document.getElementsByClassName("required", form);
                        for (i = 0; i &lt; requiredLabels.length; i++) {
                        var labelText = requiredLabels[i].firstChild.nodeValue; // Get the label's text
                        var labelFor = requiredLabels[i].getAttribute("for"); // Grab the for attribute
                        var inputTag = document.getElementById(labelFor); // Get the input tag
                        if (inputTag.value == null || inputTag.value == "") {
                        alert("Please make sure all required fields have been entered.");
                        return false; // Abort Submit
                        }
                        }
                        return true;
                        }
                        &lt;/script&gt;
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p>这个 JavaScript 是通过为表单声明添加 <code>onsubmit="return checkRequired(this);"</code> 被调用的。这个脚本简单地获取具有所需要的类的表单中的所有元素。由于我们的习惯是在标签标记中使用这个类，因此代码会通过 <code>for</code> 属性来查找与这个标签连接在一起的输入域。如果任何输入域为空，就会生成一条简单的警告消息，表单提交就会取消。可以简单地对这个脚本进行扩充，使其扫描多个类，并相应地进行验证。</p>
            <p>对于基于 JavaScript 的综合的验证集合来说，最好是使用开源实现，而不是自行开发。在清单 8 中您可能已经注意到下面的代码：</p>
            <table cellspacing="0" cellpadding="0" width="100%" border="0">
                <tbody>
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode"> onclick="new Effect.SlideDown('creditCardHelp')"
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p>这个函数是 Script.aculo.us 库的一部分，这个库提供了很多高级的效果。如果正在使用 Script.aculo.us，就需要对所构建的内容使用 Prototype 库。 JavaScript 验证库的一个例子是由 Andrew Tetlaw 在 Prototype 基础上构建的。（请参看 <a href="http://www.ibm.com/developerworks/cn/java/j-hibval.html#resources">参考资料</a> 一节中的链接。）他的框架依赖于被添加到输入域的类：</p>
            <table cellspacing="0" cellpadding="0" width="100%" border="0">
                <tbody>
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode">&lt;input class="required validate-number" id="field1" name="field1" /&gt;
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p>可以简单地修改 text.tag 的逻辑在 <code>input</code> 标签中插入几个类。将 <code>class="required"</code> 添加到输入标签和 <code>label</code> 标签中不会影响 CSS 规则，但会破坏清单 10 中给出的简单 JavaScript 验证程序。如果要混合使用框架中的代码和简单的 JavaScript 代码，最好使用不同的类名，或在使用类名搜索元素时确保类名有效并检查标签类型。</p>
            <br />
            <table cellspacing="0" cellpadding="0" width="100%" border="0">
                <tbody>
                    <tr>
                        <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
                        <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
                    </tr>
                </tbody>
            </table>
            <table class="no-print" cellspacing="0" cellpadding="0" align="right">
                <tbody>
                    <tr align="right">
                        <td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
                        <table cellspacing="0" cellpadding="0" border="0">
                            <tbody>
                                <tr>
                                    <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                                    </td>
                                    <td valign="top" align="right"><a class="fbox" href="http://www.ibm.com/developerworks/cn/java/j-hibval.html#main"><strong>回页首</strong></a></td>
                                </tr>
                            </tbody>
                        </table>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <br />
            <p><a name="4"><span class="atitle">最后的考虑</span></a></p>
            <p>本文已经介绍了模型层的注释如何充分用来在视图、控制器、DAO 和 DBMS 层中创建验证。必须手工创建服务层的验证，例如信用卡验证。其他模型层的验证，例如强制属性 C 是必须的，而属性 A 和 B 都处于指定的状态，这也是一个手工任务。然而，使用 Hibernate Annotations 的 Validator 组件，就可以轻松地声明并应用大多数验证。</p>
            <p><a name="4.1"><span class="smalltitle">展望</span></a></p>
            <p>不论是简单例子还是所引用框架的 JavaScript 验证都可以对简单的条件进行检查，例如域必须要填写，或者客户机端代码中的数据类型必须要匹配预期的类型。需要用到服务器端逻辑的验证可以使用 Ajax 添加到 JavaScript 验证程序中。您可以使用一个用户注册界面来让用户可以选择用户名。文本标签可以进行增强来检查 <code>@Column(unique = true)</code> 注释。在找到这个注释时，标签可以添加一个用来触发 Ajax 调用的类。</p>
            <p>现在您不需要在应用程序层间维护重复的验证逻辑了，这样就可以节省出大量的开发时间。想像一下您最终可以为应用程序所能添加的增强功能！</p>
            </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.blogjava.net/yanrui312/aggbug/144729.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yanrui312/" target="_blank">颜颜</a> 2007-09-13 10:11 <a href="http://www.blogjava.net/yanrui312/articles/144729.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>hibernate对象定义</title><link>http://www.blogjava.net/yanrui312/articles/136746.html</link><dc:creator>颜颜</dc:creator><author>颜颜</author><pubDate>Tue, 14 Aug 2007 09:44:00 GMT</pubDate><guid>http://www.blogjava.net/yanrui312/articles/136746.html</guid><wfw:comment>http://www.blogjava.net/yanrui312/comments/136746.html</wfw:comment><comments>http://www.blogjava.net/yanrui312/articles/136746.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yanrui312/comments/commentRss/136746.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yanrui312/services/trackbacks/136746.html</trackback:ping><description><![CDATA[<dl>
<dt><span class=term>SessionFactory (<tt class=literal><font face=新宋体>net.sf.hibernate.SessionFactory</font></tt>)</span>
<dd>
<p>对属于单一数据库的编译过的映射文件的一个线程安全的，不可变的缓存快照。它是<tt class=literal><font face=新宋体>Session</font></tt>的工厂，是<tt class=literal><font face=新宋体>ConnectionProvider</font></tt>的客户。可能持有一个可选的（第二级）数据缓存，可以在进程级别或集群级别保存可以在事物中重用的数据。 </p>
<p>可能持有事务之间重用的数据的缓存。 </p>
<dt><span class=term>会话，Session (<tt class=literal><font face=新宋体>net.sf.hibernate.Session</font></tt>) </span>
<dd>
<p>单线程，生命期短促的对象，代表应用程序和持久化层之间的一次对话。封装了一个JDBC连接。也是<tt class=literal><font face=新宋体>Transaction</font></tt>的工厂。保存有必需的（第一级）持久化对象的缓存，用于遍历对象图，或者通过标识符查找对象。 </p>
<p>持有持久化对象的缓存。 </p>
<dt><span class=term>持久化对象（Persistent Object)及其集合(Collection)</span>
<dd>
<p>生命期短促的单线程的对象，包含了持久化状态和商业功能。它们可能是普通的JavaBeans/POJOs，唯一特别的是他们现在从属于且仅从属于一个<tt class=literal><font face=新宋体>Session</font></tt>。一旦<tt class=literal><font face=新宋体>Session</font></tt>被关闭，他们都将从Session中取消联系，可以在任何程序层自由使用（比如，直接作为传送到表现层的DTO,数据传输对象）。 </p>
<dt><span class=term>临时对象(Transient Object)及其集合（Collection）</span>
<dd>
<p>目前没有从属于一个<tt class=literal><font face=新宋体>Session</font></tt>的持久化类的实例。他们可能是刚刚被程序实例化，还没有来得及被持久化，或者是被一个已经关闭的<tt class=literal><font face=新宋体>Session</font></tt>所实例化的。 </p>
<dt><span class=term>事务，Transaction (<tt class=literal><font face=新宋体>net.sf.hibernate.Transaction</font></tt>)</span>
<dd>
<p>（可选） 单线程，生命期短促的对象，应用程序用它来表示一批工作的原子操作。是底层的JDBC,JTA或者CORBA事务的抽象。一个<tt class=literal><font face=新宋体>Session</font></tt>某些情况下可能跨越多个<tt class=literal><font face=新宋体>Transaction 事务</font></tt>。 </p>
<dt><span class=term>ConnectionProvider (<tt class=literal><font face=新宋体>net.sf.hibernate.connection.ConnectionProvider</font></tt>)</span>
<dd>
<p>（可选）JDBC连接的工厂和池。从底层的<tt class=literal><font face=新宋体>Datasource</font></tt>或者 <tt class=literal><font face=新宋体>DriverManager</font></tt>抽象而来。对应用程序不可见，但可以被开发者扩展/实现。 </p>
<dt><span class=term>TransactionFactory (<tt class=literal><font face=新宋体>net.sf.hibernate.TransactionFactory</font></tt>)</span>
<dd>
<p>（可选）<tt class=literal><font face=新宋体>事务</font></tt>实例的工厂。对应用程序不可见，但可以被开发者扩展/实现。 </p>
</dd></dl>
<img src ="http://www.blogjava.net/yanrui312/aggbug/136746.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yanrui312/" target="_blank">颜颜</a> 2007-08-14 17:44 <a href="http://www.blogjava.net/yanrui312/articles/136746.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>学XSL（二）</title><link>http://www.blogjava.net/yanrui312/articles/130127.html</link><dc:creator>颜颜</dc:creator><author>颜颜</author><pubDate>Fri, 13 Jul 2007 08:59:00 GMT</pubDate><guid>http://www.blogjava.net/yanrui312/articles/130127.html</guid><wfw:comment>http://www.blogjava.net/yanrui312/comments/130127.html</wfw:comment><comments>http://www.blogjava.net/yanrui312/articles/130127.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yanrui312/comments/commentRss/130127.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yanrui312/services/trackbacks/130127.html</trackback:ping><description><![CDATA[表一、运算符与特殊字符
<table style="FONT-SIZE: 12px" cellSpacing=0 cellPadding=0 width=500 align=center border=0>
    <tbody>
        <tr>
            <td width=67 bgColor=#666666 height=25>&nbsp;<font color=#ffffff>运算符</font></td>
            <td width=10 bgColor=#000000 height=25></td>
            <td width=427 bgColor=#000000><font color=#ffffff>描述</font></td>
        </tr>
        <tr>
            <td width=67 bgColor=#eeeeee height=35>
            <div align=center>/</div>
            </td>
            <td width=10 bgColor=#cccccc height=35></td>
            <td width=427 bgColor=#cccccc>选择子元素，返回左侧元素的直接子元素；如果"/"位于最左侧表示选择根结点的直接子元素</td>
        </tr>
        <tr bgColor=#f0f0f0>
            <td width=67 height=35>
            <div align=center>//</div>
            </td>
            <td width=10 bgColor=#eeeeee height=25></td>
            <td width=427 bgColor=#eeeeee>递归下降，不论深度，搜索指定的元素；如果位于最左侧表示从根结点出发递归下降搜索指定元素</td>
        </tr>
        <tr>
            <td width=67 bgColor=#eeeeee height=35>
            <div align=center><strong><font face="Verdana, Arial, Helvetica, sans-serif">.</font></strong></div>
            </td>
            <td width=10 bgColor=#cccccc height=25></td>
            <td width=427 bgColor=#cccccc>表示当前元素</td>
        </tr>
        <tr bgColor=#f0f0f0>
            <td width=67 height=35>
            <div align=center>*</div>
            </td>
            <td width=10 bgColor=#eeeeee height=25></td>
            <td width=427 bgColor=#eeeeee>通配符，选择任意元素，不考虑名字</td>
        </tr>
        <tr>
            <td width=67 bgColor=#eeeeee height=35>
            <div align=center>@</div>
            </td>
            <td width=10 bgColor=#cccccc height=25></td>
            <td width=427 bgColor=#cccccc>
            <p>&#160;</p>
            <p>取得属性值，作为属性名的前缀</p>
            </td>
        </tr>
        <tr bgColor=#f0f0f0>
            <td width=67 height=35>
            <div align=center>@*</div>
            </td>
            <td width=10 bgColor=#eeeeee height=25></td>
            <td width=427 bgColor=#eeeeee>
            <p>&#160;</p>
            <p>通配符，选择任意属性，不考虑名字</p>
            </td>
        </tr>
        <tr>
            <td width=67 bgColor=#eeeeee height=35>
            <div align=center><font face="Verdana, Arial, Helvetica, sans-serif"><strong>:</strong></font></div>
            </td>
            <td width=10 bgColor=#cccccc height=25></td>
            <td width=427 bgColor=#cccccc>名字作用范围分隔符，将名字作用范围前缀与元素或属性名分隔开来</td>
        </tr>
        <tr bgColor=#f0f0f0>
            <td width=67 height=35>
            <div align=center><font face="Verdana, Arial, Helvetica, sans-serif"><strong>!*</strong></font></div>
            </td>
            <td width=10 bgColor=#eeeeee height=25></td>
            <td width=427 bgColor=#eeeeee>在相关节点上应用指定方法</td>
        </tr>
        <tr>
            <td width=67 bgColor=#eeeeee height=35>
            <div align=center>()*</div>
            </td>
            <td width=10 bgColor=#cccccc height=25></td>
            <td width=427 bgColor=#cccccc>分组，明确指定优先顺序</td>
        </tr>
        <tr bgColor=#f0f0f0>
            <td width=67 height=35>
            <div align=center>[]</div>
            </td>
            <td width=10 bgColor=#eeeeee height=25></td>
            <td width=427 bgColor=#eeeeee>应用过滤样式</td>
        </tr>
        <tr>
            <td width=67 bgColor=#eeeeee height=35>
            <div align=center>[]*</div>
            </td>
            <td width=10 bgColor=#cccccc height=25></td>
            <td width=427 bgColor=#cccccc>下标运算符，用于在集合中指示元素</td>
        </tr>
    </tbody>
</table>
<p>　　表二、逻辑运算符</p>
<table style="FONT-SIZE: 12px" cellSpacing=0 cellPadding=0 width=500 align=center border=0>
    <tbody>
        <tr>
            <td width=114 bgColor=#666666 height=25>&nbsp;<font color=#ffffff>可选方式</font></td>
            <td width=10 bgColor=#000000 height=25></td>
            <td width=368 bgColor=#000000><font color=#ffffff>描述</font></td>
        </tr>
        <tr>
            <td width=114 bgColor=#eeeeee height=25>
            <div align=center>and $and$ 或 &amp;&amp;</div>
            </td>
            <td width=10 bgColor=#cccccc height=25></td>
            <td width=368 bgColor=#cccccc height=25>逻辑与</td>
        </tr>
        <tr>
            <td width=114 bgColor=#f0f0f0 height=25>
            <div align=center>or $or$ 或 ||</div>
            </td>
            <td width=10 bgColor=#eeeeee height=25></td>
            <td width=368 bgColor=#eeeeee height=25>逻辑或</td>
        </tr>
        <tr>
            <td width=114 bgColor=#eeeeee height=25>
            <div align=center>not() $not$</div>
            </td>
            <td width=10 bgColor=#cccccc height=25></td>
            <td width=368 bgColor=#cccccc height=25>逻辑非</td>
        </tr>
    </tbody>
</table>
<p>　　表三、关系运算符<br></p>
<table style="FONT-SIZE: 12px" cellSpacing=0 cellPadding=0 width=500 align=center border=0>
    <tbody>
        <tr>
            <td width=114 bgColor=#666666 height=25>&nbsp;<font color=#ffffff>可选方式</font></td>
            <td width=10 bgColor=#000000 height=25></td>
            <td width=368 bgColor=#000000><font color=#ffffff>描述</font></td>
        </tr>
        <tr>
            <td width=114 bgColor=#eeeeee height=25>
            <div align=center>= 或 $eq$</div>
            </td>
            <td width=10 bgColor=#cccccc height=25></td>
            <td width=368 bgColor=#cccccc height=25>相等</td>
        </tr>
        <tr>
            <td width=114 bgColor=#f0f0f0 height=25>
            <div align=center>= 或 $ieq$</div>
            </td>
            <td width=10 bgColor=#eeeeee height=25></td>
            <td width=368 bgColor=#eeeeee height=25>相等（不区分大小写）</td>
        </tr>
        <tr>
            <td width=114 bgColor=#eeeeee height=25>
            <div align=center>!= 或 $ne$</div>
            </td>
            <td width=10 bgColor=#cccccc height=25></td>
            <td width=368 bgColor=#cccccc height=25>不等</td>
        </tr>
        <tr>
            <td width=114 bgColor=#f0f0f0 height=25>
            <div align=center>$ine$</div>
            </td>
            <td width=10 bgColor=#eeeeee height=25></td>
            <td width=368 bgColor=#eeeeee height=25>不等（不区分大小写）</td>
        </tr>
        <tr>
            <td width=114 bgColor=#eeeeee height=25>
            <div align=center>&lt; 或 $lt$ </div>
            </td>
            <td width=10 bgColor=#cccccc height=25></td>
            <td width=368 bgColor=#cccccc height=25>小于</td>
        </tr>
        <tr>
            <td width=114 bgColor=#f0f0f0 height=25>
            <div align=center>$ilt$</div>
            </td>
            <td width=10 bgColor=#eeeeee height=25></td>
            <td width=368 bgColor=#eeeeee height=25>小于（不区分大小写）</td>
        </tr>
        <tr>
            <td width=114 bgColor=#eeeeee height=25>
            <div align=center>&lt;= 或 $le$</div>
            </td>
            <td width=10 bgColor=#cccccc height=25></td>
            <td width=368 bgColor=#cccccc height=25>小于等于</td>
        </tr>
        <tr>
            <td width=114 bgColor=#f0f0f0 height=25>
            <div align=center>$ile$</div>
            </td>
            <td width=10 bgColor=#eeeeee height=25></td>
            <td width=368 bgColor=#eeeeee height=25>小于等于（不区分大小写）</td>
        </tr>
        <tr>
            <td width=114 bgColor=#eeeeee height=25>
            <div align=center>&gt; 或 $gt$ </div>
            </td>
            <td width=10 bgColor=#cccccc height=25></td>
            <td width=368 bgColor=#cccccc height=25>大于</td>
        </tr>
        <tr>
            <td width=114 bgColor=#f0f0f0 height=25>
            <div align=center>$igt$</div>
            </td>
            <td width=10 bgColor=#eeeeee height=25></td>
            <td width=368 bgColor=#eeeeee height=25>大于（不区分大小写）</td>
        </tr>
        <tr>
            <td width=114 bgColor=#eeeeee height=25>
            <div align=center>&gt;= 或 $ge$</div>
            </td>
            <td width=10 bgColor=#cccccc height=25></td>
            <td width=368 bgColor=#cccccc height=25>大于等于</td>
        </tr>
        <tr>
            <td width=114 bgColor=#f0f0f0 height=25>
            <div align=center>$ige$</div>
            </td>
            <td width=10 bgColor=#eeeeee height=25></td>
            <td width=368 bgColor=#eeeeee height=25>大于等于（不区分大小写） </td>
        </tr>
        <tr>
            <td width=114 bgColor=#eeeeee height=25>
            <div align=center>$all$ </div>
            </td>
            <td width=10 bgColor=#cccccc height=25></td>
            <td width=368 bgColor=#cccccc height=25>集合运算符，如果集合中所有项目均满足条件则返回"真" </td>
        </tr>
        <tr>
            <td width=114 bgColor=#f0f0f0 height=25>
            <div align=center>$any$</div>
            </td>
            <td width=10 bgColor=#eeeeee height=25></td>
            <td width=368 bgColor=#eeeeee height=25>集合运算符，如果集合中任意项目满足条件则返回"真" </td>
        </tr>
        <tr>
            <td width=114 bgColor=#eeeeee height=25>
            <div align=center>|</div>
            </td>
            <td width=10 bgColor=#cccccc height=25></td>
            <td width=368 bgColor=#cccccc height=25>集合运算符，返回两个集合的联合</td>
        </tr>
    </tbody>
</table>
<p>　　示例一：</p>
<p>　　从个人简历中寻找具有具有"WEB开发"技能的人的姓名与E-Mail。假设文档结构如下所示：</p>
<p>&lt;document&gt;<br>&lt;resume&gt;<br>&lt;name&gt;name&lt;/name&gt;<br>&lt;sex&gt;sex&lt;/sex&gt;<br>&lt;birthday&gt;birthday&lt;/birthday&gt;<br>&lt;skill&gt;skill1&lt;/skill&gt;<br>&lt;skill&gt;skill2&lt;/skill2&gt;<br>&#8230;<br>&lt;skill&gt;skilln&lt;/skill&gt;<br>&lt;/resume&gt;<br>&lt;resume&gt;<br>&#8230;<br>&lt;/resume&gt;<br>&#8230;<br>&lt;/document&gt;</p>
<p>　　为从以上结构的个人简历中寻找出所有具有WEB开发"技能的人的姓名与E-Mail的XSL文档结构如下：</p>
<p>&lt;TABLE border="1" cellspacing="0"&gt;<br>&lt;TH&gt;姓名&lt;/TH&gt;&lt;TH&gt;E-Mail&lt;/TH&gt;<br>&lt;xsl:for-each select="resume [$any$skill="WEB开发"]"&gt;<br>&lt;TR&gt;&lt;TD&gt;&lt;xsl:value-of select="name"/&gt;&lt;/TD&gt;<br>&lt;TD&gt;&lt;xsl:value-of select="E-Mail"/&gt;&lt;/TD&gt;<br>&lt;/TR&gt;<br>&lt;/xsl:for-each&gt;<br>&lt;/TABLE&gt;</p>
<p>　　说明：</p>
<p>　　1.[ ] ── 表示选择条件，只有满足条件的个人简历才被显示。</p>
<p>　　2.$any$ ── 由于每个人有多种技能，故加$any$作为前缀，以使每个人所有技能都能被比较。</p>
<p>　　3.skill='WEB开发' ── 筛选条件。</p>
<p>　　示例二、</p>
<p>　　仍上面的XML文档为例，如果欲选择1977/1/1之前出生的人的姓名、技能与E-Mail，相应的XSL文档结构如下（假定生日格式为yyyy/mm/dd）：</p>
<p>&lt;TABLE border="1" cellspacing="0"&gt;<br>&lt;TH&gt;姓名&lt;/TH&gt;&lt;TH&gt;技能&lt;/TH&gt;&lt;TH&gt;E-Mail&lt;/TH&gt;<br>&lt;xsl:for-each select="resume[birthday$lt$"1977/1/1"]"&gt;<br>&lt;TR&gt;<br>&lt;TD&gt;&lt;xsl:value-of select="name"/&gt;&lt;/TD&gt;<br>&lt;TD&gt;<br>&lt;xsl:value-of select="skill[0]"/&gt;<br>&lt;xsl:for-each select="skill[index()&gt;0]"&gt;、<br>&lt;xsl:value-of select="."/&gt;<br>&lt;/xsl:for-each&gt;<br>&lt;/TD&gt;<br>&lt;TD&gt;&lt;xsl:value-of select="E-Mail"/&gt;&lt;/TD&gt;<br>&lt;/TR&gt;<br>&lt;/xsl:for-each&gt;<br>&lt;/TABLE&gt;</p>
<p>　　说明：</p>
<p>　　1.birthday $lt$ '1977/1/1' ── 搜索条件，在此处使用"&lt; "会错误，故使用"$lt$"表示小于。</p>
<p>　　2.skill [0] ── 表示选择skill的第一项。</p>
<p>　　3.skill [index()&gt;0] ── 表示选择skill的第二项以后（包括第二项）的项目。</p>
<p>　　4.xsl:value-of select="." ── 表示选择当前标记的值。</p>
<p>　　相信大家应该注意到，在前面以及本次的例子中出现了一些函数，如index()、formatIndex()、childNumber()，也许大家还不完全明了其中的含义吧？敬请关注下一课。[page]</p>
<p>本期学习XSL样式方法，即可用于XSL元素&lt;xsl:for-each&gt;、&lt;xsl:value-of&gt;、&lt;xsl:template&gt;的select属性、&lt;xsl:apply-templates&gt;的match属性、&lt;xsl:if&gt;、&lt;xsl:when&gt;的test属性中，对元素的范围进行筛选，从而提供更大的灵活性。 </p>
<p><br>　　XML与DHTML（动态HTML）一样，这些节点都是一个个对象，而且这些对象都是有层次的，从根节点开始构成一颗层次清淅的树状结构，这就形成了文档对象模型DOM(Document Object Model)，通过对象的属性、方法来达到访问控制XML节点的目的。</p>
<p><br>　　我们这里不打算就XML的DOM逐一详细阐述，因为这完全可以写成一个篇幅较多的教程，我们先就一些常见的方法作一些讨论，以期对DOM的对象方法有一个大致的了解。</p>
<p>　　注：从本期开始，所有示例不再提供完整源代码，如有不明白之处，请仔细阅读前面七期、并动手练手。</p>
<p>　　一、end()</p>
<p>　　含义：返回集合中最后一个元素。</p>
<p>　　示例：输出最后一份简历</p>
<p>　　假定XML文件格式为：</p>
<p>&#8230;&#8230;&lt;resume&gt;&#8230;&lt;/resume&gt;&#8230;&#8230;&lt;resume&gt;&#8230;&lt;/resume&gt;&#8230;&#8230;</p>
<p>　　相应XSL文件内容为：</p>
<p>&lt;xsl:for-each select="resume[end()]"&gt;&#8230;&#8230;&lt;/xsl:for-each&gt;</p>
<p>　　或：</p>
<p>&lt;xsl:templates match="resume[end()]"&gt;&#8230;&#8230;&lt;/xsl:templates&gt;</p>
<p>　　或：</p>
<p>&lt;xsl:apply-template select="resume[end()]"&gt;&#8230;&#8230;&lt;/xsl:apply-template&gt;</p>
<p>　　二、index()</p>
<p>　　含义：返回该元素在集合中的位置，返回值是一整数，其中第一个元素返回0。</p>
<p>　　示例：返回前面三份简历。</p>
<p>resume[index()$le$3]</p>
<p>　　注意：index()是与父元素相关的，请看下例：</p>
<p>&lt;x&gt;<br>&lt;y/&gt;<br>&lt;y/&gt;<br>&lt;/x&gt;<br>&lt;x&gt;<br>&lt;y/&gt;<br>&lt;y/&gt;<br>&lt;/x&gt;</p>
<p>　　返回所有&lt;x&gt;中的第一个&lt;y&gt;</p>
<p>x/y[index()=0] 或x/y[0]</p>
<p>　　三、nodeName()</p>
<p>　　含义：返回元素的名字，即标记名。</p>
<p>　　示例：选择任意元素，假如其名字（即标记名）等于"name"：</p>
<p>*[nodeName()='name'] 或 *[name]</p>
<p>　　四、number()</p>
<p>　　含义：将值转换为数值形式，如果不是数值则返回空，要求参数。</p>
<p>　　示例：年龄(age)小于30岁的人的简历(resume)：</p>
<p>resume[number(age)$lt$30] 或 resume[age$lt$30]</p>
<p>　　五、nodeType()</p>
<p>　　含义：返回结点类型，结果为是数值。以下是返回值列表：<br><br></p>
<p>
<table style="FONT-SIZE: 12px" cellSpacing=1 cellPadding=0 width=500 align=center border=0>
    <tbody>
        <tr>
            <td width=209 bgColor=#000000 height=25><font color=#ffffff>&nbsp;结点类型</font></td>
            <td width=85 bgColor=#000000><font color=#ffffff>&nbsp;结点类型值</font></td>
            <td width=202 bgColor=#000000><font color=#ffffff>&nbsp;结点的字符形式描述</font></td>
        </tr>
        <tr>
            <td width=209 bgColor=#cccccc height=25>&nbsp;Element</td>
            <td width=85 bgColor=#cccccc>&nbsp;1</td>
            <td width=202 bgColor=#cccccc>&nbsp;'element'</td>
        </tr>
        <tr>
            <td width=209 bgColor=#eeeeee height=25>&nbsp;Element Attribute</td>
            <td width=85 bgColor=#eeeeee>&nbsp;2</td>
            <td width=202 bgColor=#eeeeee>&nbsp;'attribute'</td>
        </tr>
        <tr bgColor=#cccccc>
            <td width=209 height=25>&nbsp;Markup-Delimited Region of Text</td>
            <td width=85>&nbsp;3</td>
            <td width=202>&nbsp;'text'</td>
        </tr>
        <tr>
            <td width=209 bgColor=#eeeeee height=25>&nbsp;Processing Instruction</td>
            <td width=85 bgColor=#eeeeee>&nbsp;7</td>
            <td width=202 bgColor=#eeeeee>&nbsp;'processing_instruction'</td>
        </tr>
        <tr bgColor=#cccccc>
            <td width=209 height=25>&nbsp;Comment</td>
            <td width=85>&nbsp;8</td>
            <td width=202>&nbsp;'comment'</td>
        </tr>
        <tr>
            <td width=209 bgColor=#eeeeee height=25>&nbsp;Document Entity</td>
            <td width=85 bgColor=#eeeeee>&nbsp;9</td>
            <td width=202 bgColor=#eeeeee>&nbsp;'document'</td>
        </tr>
    </tbody>
</table>
</p>
<p>　　六、value()</p>
<p>　　含义：返回元素或属性的值。</p>
<p>　　示例：value()是元素或属性的缺省方法，以下表示是等价：</p>
<p>name!value()="NAME" 与 name="NAME"<br><br>@attr="attribute_value" 与 @attr="attribute_value"</p>
<p>　　注：@是属性前缀，@attr表示是属性attr</p>
<p>　　七、attribute()</p>
<p>　　含义：返回所有属性结点的集合，等价于"@*"。</p>
<p>　　示例：寻找所有的resume元素，满足条件至少有一个属性的值为"ABC"：</p>
<p>resume[$any$attribute()='ABC'] 或 resume[$any$@*='ABC']</p>
<p>　　寻找所有的resume元素，满足条件至少有一个子元素有一个属性的值为"ABC"：</p>
<p>resume[$any$*/attribute()='ABC'] 或 resume[$any$*/@*='ABC']</p>
<p>　　八、comment()</p>
<p>　　含义：返回所有注释结点。</p>
<p>　　示例：</p>
<p>resume[$any$comment()='禹希初的简历']</p>
<p>　　表示寻找含有注释语句：&lt;!--禹希初的简历--&gt;的&lt;resume&gt;元素。</p>
<p>　　九、cdata()</p>
<p>　　含义：返回所有CDATA类型的结点的集合。</p>
<p>　　示例：</p>
<p>resume[$any$cdata()='禹希初的简历']</p>
<p>　　表示寻找含有下述语句（必须是直接子结点）&lt;![CDATA[禹希初的简历]]&gt;的&lt;resume&gt;元素。</p>
<p>　　十、node()</p>
<p>　　含义：返回当前上下文环境中除根结点和属性结点以外的所有结点的集合，等价于：</p>
<p>"*|pi()|comment()|text()"</p>
<p>　　示例：寻找所有元素resume，其最后一个结点的名字为"skill"：</p>
<p>resume[node()[end()]!nodeName()='skill']</p>
<p>　　寻找所有resume元素的第一个结点：resume/node()[0]。</p>
<p>　　十一、textnode()</p>
<p>　　含义：返回所有文本类型的结点的集合。</p>
<p>　　示例：寻找每一个p元素的第二个文本结点：</p>
<p>p/textnode(1) 或 p!textnode(1)</p>
<p>　　十二、text()</p>
<p>　　含义：返回所有表示文本字符串的结点的集合，等价于"cdata()|textnode()"。</p>
<p>　　本期的内容就介绍至此，另有一个函数date()在本人的机器上一试就发生错误使浏览器自动关闭，还有一个函数pi()本人尚未找到适当的应用方法，就不介绍了，下期将讲述如何XSL中使用脚本。[page]</p>
<p>有时，我们可能会希望XML文档输出时能对其中内容加上一些统计信息或者如编号什么的，利用前面的知识就不太容易实现了。今天将介绍两个新元素&lt;xsl:eval&gt;与&lt;xsl:script&gt;，使我们能轻松处理这个难题。</p>
<p>&lt;xsl:eval&gt;</p>
<p>　　含义：计算脚本表达式，输出一个文本字符串。</p>
<p>　　语法：</p>
<p>&lt;xsl:eval language="language-name"&gt;</p>
<p>　　属性：</p>
<p>　　language ── 规定所用脚本语言的名字，可用的属性有"JavaScript"、"JScript"、"VBScript"、"VBS"等，缺省为"JScript"。</p>
<p>&lt;xsl:script&gt;</p>
<p>　　含义：声明全局变量或定义函数。</p>
<p>　　语法：</p>
<p>&lt;xsl:script language="language-name"&gt;</p>
<p>　　属性：同&lt;xsl:eval&gt;</p>
<p><br>　　示例：</p>
<p>　　不知大家对于第四期《跟我学XML》中的例子是否还有印象？其中的XML文档并没有对简历编号，但输出中却加上了大写的罗马数字序号。今天将再举一稍为复杂一些的例子：</p>
<p>　　假如我们编写一份年终生产统计表，其中需要小计一项，常规的作法是事先将其算出来，现在不必了，我们可以只给出单项统计，显示时再统计小计一项。请找出《跟我学XML》的第四期，XML文件不必修改，对XSL文件的修改如下：</p>
<p>&lt;?xml version="1.0" encoding="GB2312"?&gt;<br>&lt;xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl"&gt;</p>
<p>&lt;xsl:template match="/"&gt;<br>&lt;HTML&gt;&lt;HEAD&gt;&lt;TITLE&gt;1999年生产统计&lt;/TITLE&gt;&lt;/HEAD&gt; <br>&lt;BODY&gt;&lt;xsl:apply-templates select="document"/&gt;&lt;/BODY&gt;<br>&lt;/HTML&gt;<br>&lt;/xsl:template&gt;</p>
<p>&lt;xsl:template match="document"&gt;<br>&lt;H3&gt;1999年生产统计&lt;/H3&gt; <br>&lt;TABLE border="1" cellspacing="0"&gt; <br>&lt;TH&gt;班组&lt;/TH&gt;<br>&lt;TH&gt;一季度&lt;/TH&gt;<br>&lt;TH&gt;二季度&lt;/TH&gt;<br>&lt;TH&gt;三季度&lt;/TH&gt;<br>&lt;TH&gt;四季度&lt;/TH&gt;<br>&lt;xsl:apply-templates select="report"/&gt;<br><strong><br>&lt;TR&gt;&lt;TD&gt;小计&lt;/TD&gt;<br>&lt;TD&gt;&lt;xsl:eval&gt;total(this,"q1")&lt;/xsl:eval&gt;&lt;/TD&gt;<br>&lt;TD&gt;&lt;xsl:eval&gt;total(this,"q2")&lt;/xsl:eval&gt;&lt;/TD&gt;<br>&lt;TD&gt;&lt;xsl:eval&gt;total(this,"q3")&lt;/xsl:eval&gt;&lt;/TD&gt;<br>&lt;TD&gt;&lt;xsl:eval&gt;total(this,"q4")&lt;/xsl:eval&gt;&lt;/TD&gt;<br>&lt;/TR&gt;</strong><br><br>&lt;/TABLE&gt;<br><strong><br>&lt;xsl:script&gt;<br>function total(node,q)<br>{<br>　　temp=0; <br>　　mark='/document/report/'+q;<br>　　v=node.selectNodes(mark);<br>　　for(t=v.nextNode();t;t=v.nextNode())<br>　　{<br>　　　　temp+=Number(t.text);<br>　　}<br>　　return temp; //小计值<br>}<br>&lt;/xsl:script&gt;<br></strong><br>&lt;/xsl:template&gt;<br><br>&lt;xsl:template match="report"&gt;<br>&lt;TR&gt;<br>&lt;TD&gt;&lt;xsl:value-of select="class"/&gt;&lt;/TD&gt;<br>&lt;TD&gt;&lt;xsl:apply-templates select="q1"/&gt;&lt;/TD&gt;<br>&lt;TD&gt;&lt;xsl:apply-templates select="q2"/&gt;&lt;/TD&gt;<br>&lt;TD&gt;&lt;xsl:apply-templates select="q3"/&gt;&lt;/TD&gt;<br>&lt;TD&gt;&lt;xsl:apply-templates select="q4"/&gt;&lt;/TD&gt;<br>&lt;/TR&gt;<br>&lt;/xsl:template&gt;</p>
<p>&lt;xsl:template match="q1|q2|q3|q4"&gt;<br>&lt;!--此处测试产量，如小于等于20则添加一STYLE属性color，其值为red（红色）--&gt; <br>&lt;xsl:if test=".[value() $le$ 20]"&gt; <br>&lt;xsl:attribute name="style"&gt;color:red&lt;/xsl:attribute&gt;<br>&lt;/xsl:if&gt;<br>&lt;xsl:value-of/&gt;<br>&lt;/xsl:template&gt;</p>
<p>&lt;/xsl:stylesheet&gt;<br><br>
<div align=center><img height=180 alt="" src="http://www.7880.com/Upload/2004_Pack/0007.gif" width=262 border=0><br><span class=pt9>上例在浏览器中的样子(IE5.0或更新版本)</span><br><br></div>
<p>　　说明：</p>
<p>　　注意到执行结果的变化了吗？黑体部分为添加部分，注意添加部分分为两部分，&lt;xsl:script&gt;&lt;/xsl:script&gt;必须置于&lt;/TABLE&gt;之后，切记。</p>
<p>　　selectNodes() ── 是XMLDOMObject的一个方法，返回文档中所有满足条件的结点的集合，条件与&lt; xsl:for-each &gt;和select属性的取值采用同样的写法，可以加筛选、下标等限制，如寻找一季度产量大于等于50的班组：</p>
<p>/document/report/q1[value() $ge$ 50]</p>
<p>　　以上写法还有一个更简单的写法：</p>
<p>//q1[value()$ge$50]</p>
<p>　　//表示从根结点出发遍历所有结点，寻找满足条件的结点，如果文档内有同名但意义不同的结点则不能用此种方法，非不得已不建议使用。以此为例，如果希望统计年总产量，则可以下述字符串寻找结点（建议使用最后一种，此种描述将精确找到需要汇总的数据）：</p>
<p>//*[value()$gt$0] 或 //(q1|q2|q3|q4) 或 /document/report/(q1|q2|q3|q4)</p>
<p>　　nextNode() ── 返回结点集中的下一个结点</p>
<p>　　Number() ── 将提供的参数转换为数值</p>
<p><br>　　下期介绍XSL函数2，用于&lt;xsl:script&gt;及&lt;xsl:eval&gt;中，以及&lt;xsl:if&gt;和&lt;xsl:when&gt;的expr属性。建议读者熟悉JavaScript、JScript、VBScript中至少一种，否你能用XSL完成的工作将十分有限。由于篇幅关系，此处不作详细介绍。[page]</p>
<p>本期介绍多个XSL对于VBScript、JScript增加的方法、属性，以充分发挥XML的优势，用于&lt;xsl:script&gt;、&lt;xsl:eval&gt;标记内表达式的编写或&lt;xsl:if&gt;、&lt;xsl:when&gt;的expr属性。</p>
<p>&#160;</p>
<p>　　一、absoluteChildNumber </p>
<p>　　含义：返回结点相对于它所有的兄弟（不论名字是否相同）的序号。</p>
<p>　　语法：absoluteChildNumber(node)</p>
<p>　　参数：node ── 对象，欲返回编号的结点。</p>
<p>　　示例：</p>
<p>　　1、假定文档结构为：&lt;document&gt;&lt;head/&gt;&lt;body/&gt;&lt;/document&gt;，其中document为顶层结点，下述表达式将输出：</p>
<p>&lt;xsl:eval&gt;<br>absoluteChildNumber(this.selectNodes('/document/body').item(0))<br>&lt;/xsl:eval&gt;</p>
<p>　　2、确定当前结点相对于其所有兄弟的序号：</p>
<p>&lt;xsl:eval&gt;<br>absoluteChildNumber(this)<br>&lt;/xsl:eval&gt;</p>
<p>　　二、ancestorChildNumber</p>
<p>　　含义：从给定结点出发根据给定祖先结点名返回最近的祖先结点的序号（相对于同名结点）。如果找不祖先，则返回0。</p>
<p>　　语法：ancestorChildNumber(bstrNodeName, pNode)</p>
<p>　　参数：</p>
<p>　　bstrNodeName ── 字符串。被搜索的祖先结点的名字。</p>
<p>　　pNode ── 对象。搜索开始位置的结点。</p>
<p>　　示例查找当前结点最近的名为report祖先结点：</p>
<p>ancestorChildNumber('report',this)</p>
<p>　　三、attributes</p>
<p>　　含义：返回结点属性的集合。</p>
<p>　　语法：object.attributes</p>
<p>　　参数：object ── 结点对象。</p>
<p>　　示例：当前结点属性的个数</p>
<p>this.attributes.length</p>
<p>　　当前结点第三个属性的值</p>
<p>this.attributs.item(2).value</p>
<p>或<br><br>this.attributes.item(2).text</p>
<p>或<br><br>this.attributes(2).text</p>
<p>　　注意：如果给定的下标大于属性总和减1将出错，第一个属性的下标是0。</p>
<p>　　四、baseName</p>
<p>　　含义：返回有名字空间限制的基本名，即不包括名字前缀。</p>
<p>　　语法：object.baseName</p>
<p>　　参数：object ── 结点对象</p>
<p>　　示例，当前结点的基本名：<br><br>this.baseName</p>
<p>　　五、childNumber</p>
<p>　　含义：返回结点相对于同名同胞的序号。</p>
<p>　　语法：childNumber(object)</p>
<p>　　参数：object ── 结点对象</p>
<p>　　示例，假定XML文档结构如下：</p>
<p>&lt;x&gt;&lt;y&gt;&lt;z&gt;&lt;/z&gt;&lt;/y&gt;&lt;/x&gt;</p>
<p>　　如果当前结点是z，则childNumber(this)返回1，而absoluteChildNumber(this)返回3。 </p>
<p>　　六、dataType</p>
<p>　　含义：设定或读取结点的数据类型。</p>
<p>　　语法：设定结点的数据类型 object.dataType=objValue<br>　　　　　读取结点的数据类型 objValue=object.dataType</p>
<p>　　参数：object ── 结点对象。<br><br>　　示例，读取当前结点的数据类型：</p>
<p>dtType=this.dataType</p>
<p>　　七、depth</p>
<p>　　含义：指定结点出现在文档树上的深度，即该结点位于文档第几层，顶层结点位于第一层，根结点（即用"/"表示的结点）位于第0层。</p>
<p>　　语法：depth(pNode)</p>
<p>　　参数：pNode ── 结点对象</p>
<p>　　示例，当前结点的深度：</p>
<p>depth(this)</p>
<p>　　八、firstChild、lastChild</p>
<p>　　含义：返回结点的第一个子结点（或最后一个子结点）。</p>
<p>　　语法：pNode.firstChild<br>　　　　　pNode.lastChild</p>
<p>　　参数：pNode ── 结点对象</p>
<p>　　示例，当前结点的第一个结点的名字：</p>
<p>this.firstChild.nodeName</p>
<p>　　九、formatIndex</p>
<p>　　含义：用指定的计数系统格式化提供的整数。</p>
<p>　　语法：formatIndex(lIndex, bstrFormat)</p>
<p>　　参数：</p>
<p>　　lIndex ── 整型数值或变量</p>
<p>　　bstrFormat ── 数据格式，可选值有a、A、i、I、1、01（以0打头的数值形式，如果要求固定长度的编号如0001、0002则非常有用）。</p>
<p>　　示例，当前结点的大写罗马数字编号：</p>
<p>formatIndex(childNumber(this),'I')</p>
<p>　　十、formatNumber</p>
<p>　　含义：以指定格式输出数值。</p>
<p>　　语法：formatNumber(dblNumber, bstrFormat)</p>
<p>　　参数：说明同formatNumber，不同之处在于格式化的可以是小数。</p>
<p>　　示例，对变量a的值格式化为两位小数：<br><br>formatNumber(a,'#.00')：</p>
<p>　　十一、hasChildNodes</p>
<p>　　含义：如果结点有子结点则返回true（-1），否则为false（0）。</p>
<p>　　语法：pNode.hasChildNodes()</p>
<p>　　注意：与此前介绍的函数不同，此函数后必须带一个空括号。</p>
<p>　　示例，判断当前结点是否有子结点：</p>
<p>this.hasChildNodes</p>
<p>　　十二、namespaceURI、prefix</p>
<p>　　含义：返回结点名字空间的全局资源标识符（或前缀）。</p>
<p>　　语法：pNode.namespaceURI<br>　　　　　pNode.prifix</p>
<p>　　十三、nextSibling、previousSibling、parentNode</p>
<p>　　含义：返回结点的下一个兄弟（或前一个兄弟、或结点的父结点）。</p>
<p>　　语法：pNode.nextSibling<br>　　　　　pNode.previousSibling<br>　　　　　pNode.parentNode</p>
<p>　　注意：对根结点（即"/"）应用parentNode方法、对第一个孩子结点应用previousSibling方法、对最后一个孩子结点应用nextSibling方法均会导致错误，可通过此过关系运算符==（等于）和!=（不等于）来判断一个结点是否某一指定结点，格式为pNode1 = pNode2或pNode2 != pNode2。</p>
<p>　　十四、nodeName</p>
<p>　　含义：返回元素、属性、入口的名字或其他类型结点的一个特定字符串。</p>
<p>　　语法：pNode.nodeName</p>
<p>　　示例，当前结点的名字：</p>
<p>this.nodeName</p>
<p>　　十五、nodeType、NodeTypeString</p>
<p>　　含义：返回结点的类型的数值形式（或字符串形式）。<br><br>　　语法：pNode.nodeType 或 pNode.nodeTypeString</p>
<p>　　返回值：</p>
<p>
<table style="FONT-SIZE: 9pt" cellSpacing=1 cellPadding=0 width=500 align=center border=0>
    <tbody>
        <tr>
            <td width=209 bgColor=#000000 height=25><font color=#ffffff>&nbsp;结点类型</font></td>
            <td width=85 bgColor=#000000><font color=#ffffff>&nbsp;结点类型值</font></td>
            <td width=202 bgColor=#000000><font color=#ffffff>&nbsp;结点的字符形式描述</font></td>
        </tr>
        <tr>
            <td width=209 bgColor=#cccccc height=25>&nbsp;Element</td>
            <td width=85 bgColor=#cccccc>&nbsp;1</td>
            <td width=202 bgColor=#cccccc>&nbsp;'element'</td>
        </tr>
        <tr>
            <td width=209 bgColor=#eeeeee height=25>&nbsp;Element Attribute</td>
            <td width=85 bgColor=#eeeeee>&nbsp;2</td>
            <td width=202 bgColor=#eeeeee>&nbsp;'attribute'</td>
        </tr>
        <tr bgColor=#cccccc>
            <td width=209 height=25>&nbsp;Markup-Delimited Region of Text</td>
            <td width=85>&nbsp;3</td>
            <td width=202>&nbsp;'text'</td>
        </tr>
        <tr>
            <td width=209 bgColor=#eeeeee height=25>&nbsp;Processing Instruction</td>
            <td width=85 bgColor=#eeeeee>&nbsp;7</td>
            <td width=202 bgColor=#eeeeee>&nbsp;'processing_instruction'</td>
        </tr>
        <tr bgColor=#cccccc>
            <td width=209 height=25>&nbsp;Comment</td>
            <td width=85>&nbsp;8</td>
            <td width=202>&nbsp;'comment'</td>
        </tr>
        <tr>
            <td width=209 bgColor=#eeeeee height=25>&nbsp;Document Entity</td>
            <td width=85 bgColor=#eeeeee>&nbsp;9</td>
            <td width=202 bgColor=#eeeeee>&nbsp;'document'</td>
        </tr>
    </tbody>
</table>
</p>
<p>　　十六、nodeTypedValue</p>
<p>　　含义：以结点预定义的数据类型返回结点的值。</p>
<p>　　语法：pNode.nodeTypedValue</p>
<p>　　示例，假定当前结点的数据类型是fixed.14.4，下例将以数值返回结点的值，而不是文本一个字符串：</p>
<p>this.nodeTypedValue</p>
<p>　　十七、nodeValue</p>
<p>　　含义：返回结点的文本。</p>
<p>　　语法：pNode.nodeValue</p>
<p>　　注意：该方法不用于元素类结点，可用于属性、CDATA、注释、文本等结点。</p>
<p>　　示例，当前元素第一个属性的值：</p>
<p>this.attributes(0).nodeValue</p>
<p>　　当前元素内的文本（假定该元素内只有文本，无其它元素，即&lt;mark&gt;text&lt;/mark&gt;，建议多尝几次掌握其确切的用法）。</p>
<p>this.firstChild.nodeValue</p>
<p>　　十八、ownerDocument</p>
<p>　　含义：返回包含该结点的文档的根。</p>
<p>　　语法：pNode.ownerDocument</p>
<p>　　注意：该方法用于文档的根结点将出错。</p>
<p>　　十九、selectNodes</p>
<p>　　含义：给定的样式匹配应用于当前结点并返回匹配的结点集合。</p>
<p>　　语法：pNode.selectNodes('pattern')</p>
<p>　　提示：pattern的编写与&lt;xsl:for-each&gt;的select属性的值类似，其中以"/"开头表示从文档的根出发搜索；以"//"开头表遍历文档的所有结点；以".."开头表示从当前结点的父结点开始；如果欲从当前结点向下搜索则不能有以上特殊字符打头。</p>
<p>　　示例，与当前结点同名的元素在其父元素内的个数：</p>
<p>childNumber(this.selectNodes("../"+this.nodeName+"[end()]").item(0))</p>
<p>　　当前元素内名字为"skill"的元素的个数：</p>
<p>childNumber(this.selectNodes("skill[end()]").item(0))</p>
<p>　　二十、selectSingleNode</p>
<p>　　含义：与selectNodes类似，不同的只返回匹配的第一个结点、而不是结点集合。</p>
<p>　　语法：pNode.selectSingleNode('pattern')</p>
<p>　　示例，与当前结点同名的元素在其父元素内的个数：</p>
<p>childNumber(this.selectSingleNode("../"+this.nodeName+"[end()]"))</p>
<p>　　当前元素内名字为"skill"的元素的个数：</p>
<p>childNumber(this.selectSingleNode("skill[end()]"))</p>
<p>　　二十一、text</p>
<p>　　含义：返回结点与它的子树内的文字内容。</p>
<p>　　语法：pNode.text</p>
<p>　　示例，整个文档内的文字内容：</p>
<p>this.ownerDocument.text</p>
<p>　　当前元素及其子树的文字内容：</p>
<p>this.text</p>
<p>　　二十二、xml</p>
<p>　　含义：返回结点及其后代的XML表示。</p>
<p>　　语法：pNode.xml</p>
<p>　　示例，当前文档的XML内容：</p>
<p>this.ownerDocument.xml</p>
<p>　　另有几个函数不作介绍，列于其下以供参考，如感兴趣，请访问http://msdn.microsoft.com获取详细说明。</p>
<p>formatTime(varTime, bstrFormat,varDestLocale) <br>formatDate(varDate, bstrFormat,varDestLocale)<br>apendChild(newChild)<br>definition<br>CloneNode<br>insertBefore(newChild, refChild)<br>parsed<br>removeChild(oldChild)<br>replaceChild(newChild, oldChild)<br>specified<br>transformNode(stylesheet)<br>transformNodeToObject(stylesheet,outputObject) <br>uniqueID(pNode)</p>
<img src ="http://www.blogjava.net/yanrui312/aggbug/130127.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yanrui312/" target="_blank">颜颜</a> 2007-07-13 16:59 <a href="http://www.blogjava.net/yanrui312/articles/130127.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>学XSL（一）</title><link>http://www.blogjava.net/yanrui312/articles/130125.html</link><dc:creator>颜颜</dc:creator><author>颜颜</author><pubDate>Fri, 13 Jul 2007 08:57:00 GMT</pubDate><guid>http://www.blogjava.net/yanrui312/articles/130125.html</guid><wfw:comment>http://www.blogjava.net/yanrui312/comments/130125.html</wfw:comment><comments>http://www.blogjava.net/yanrui312/articles/130125.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yanrui312/comments/commentRss/130125.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yanrui312/services/trackbacks/130125.html</trackback:ping><description><![CDATA[随着Internet的发展，越来越多的信息进入互联网，信息的交换、检索、保存及再利用等迫切的需求使HTML这种最常用的标记语言已越来越捉襟见肘。HTML将数据内容与表现融为一体，可修改性、数据可检索性差，而XML借鉴了HTML与数据库、程序语言的优点，将内容与表现分开，不仅使检索更为方便，更主要的是用户之间数据的交换更加方便，可重用性更强。
<p>　　XML是一种元标记语言，没有许多固定的标记，为WEB开发人员提供了更大的灵活性。当我们使用HTML时，标记只是简单的表示内容的显示形式，而与表示的内容没有任何关联，为文档的进一步处理带来极大的不便。比如要表示个人简历，用HTML的表示方式如下：</p>
<p>&lt;HTML&gt;<br>&lt;BODY&gt;<br>&lt;TABLE border=1 cellspacing=0&gt;<br>&lt;TH&gt;姓名&lt;TD&gt;禹希初&lt;TH&gt;性别&lt;TD&gt;男&lt;TH&gt;生日&lt;TD&gt;1977.5<br>&lt;TR&gt;<br>&lt;TH&gt;技能&lt;TD colspan=5&gt;数据库设计与维护、WEB开发<br>&lt;/TABLE&gt;<br>&lt;/BODY&gt;<br>&lt;/HTML&gt;<br><br>
<table class=pt11 cellSpacing=0 align=center border=1>
    <tbody>
        <tr>
            <th>姓名
            <td>禹希初
            <th>性别
            <td>男
            <th>生日
            <td>1977.5
            <tr>
                <th>技能
                <td colSpan=5>数据库设计与维护、WEB开发 </td>
            </tr>
        </tbody>
    </table>
    <div align=center><br><span class=pt9>上例在浏览器中的样子</span><br><br></div>
    <p>　　在这里，我们无法从标记TH、TD得知其内容表示什么，如果用XML，相应的文档（文件名：个人简历.xml）就可写成如下形式：</p>
    <p>&lt;?xml version="1.0" encoding="GB2312"?&gt;<br>&lt;resume&gt;<br>&lt;name&gt;禹希初&lt;/name&gt;<br>&lt;sex&gt;男&lt;/sex&gt;<br>&lt;birthday&gt;1977.5&lt;/birthday&gt;<br>&lt;skill&gt;数据库设计与维护、WEB开发&lt;/skill&gt;<br>&lt;/resume&gt;<br><br></p>
    <div align=center><img height=142 alt="" src="http://www.7880.com/Upload/2004_Pack/0001.gif" width=324 border=0><br><span class=pt9>上例在浏览器中的样子(IE5.0或更新版本)</span><br></div>
    <p>说明：</p>
    <p>　　version──规定了XML文档的版本，此处只能是1.0；</p>
    <p>　　encoding── 此处规定了XML文档的编码类型，此处取值为"GB2312"，也就是"简体中文"。</p>
    <p>　　对比两例，使用XML我们可以做到自定义标记，用标记表明内容的含义。这样在Internet上交流资料时，为用计算机处理文档提供了极大的方便，同时我们阅读源文件时也不会被一大堆格式弄得晕头转向。</p>
    <p>　　然而，由于XML并没有为标记规定显示方式，如果我们在游览器中查看以上两个文档（建议使用IE5.0或更新版本），我们将看到xml文档并没有以诸如表格的方式来显示。难道我们就不能像HTML一样显示文档吗？回答是否定的。以个人简历为例，需要另建一个格式文件说明各个标记的显示方式，其内容如下（假设文件名为resume.css）：</p>
    <p>resume{ display: block;}<br>name{ display: block; font-size:120%;}<br>sex{ display:block; text-indent:2em}<br>birthday{ display:block; text-indent:2em}<br>skill{ display:block; text-indent:2em}</p>
    <p>说明：</p>
    <p>　　以上均为CSS样式，建议读者参考有关资料熟悉CSS，在以后学习中必须用到，此处由于篇幅关系不作介绍。建立文件resume.css后，在个人简历.xml文件的第一行后添加以下文字：</p>
    <p>&lt;?xml:stylesheet type="text/css" href="resume.css"?&gt;</p>
    <div align=center><img height=96 alt="" src="http://www.7880.com/Upload/2004_Pack/0002.gif" width=256 border=0><br><span class=pt9>上例在浏览器中的样子(IE5.0或更新版本)</span><br><br></div>
    <p>说明：</p>
    <p>　　此处表示引用一个外部CSS样式文件，其中type规定样式类型（可取值为text/css或text/xsl），href规定文件路径。</p>
    <p>　　保存文件，再以IE5.0打开文件。怎么样？格式有些不一样吧。好象还不令人满意，文档内容是清晰了，但显示效果比HTML编写的文档就差得多了，XML编写的文档就只能以这种方式显示吗？！</p>
    <p>提示：</p>
    <p>　　1. 为了更好的理解与掌握XML，建议大家熟悉HTML 4.0与CSS 2.0语法；掌握JavaScript、VBscript中至少一种；编程经验、对数据库理论与SQL的了解均能使大家在学习XML时获益。</p>
    <p>　　2. XML文档中标记必须成对出现，如果是空标记也必须有前加"/"的同名标记结束，或使用此种文式&lt;xml_mark/&gt;表示空标记。</p>
    <p>　　3. XML以及下面将要介绍的XSL文档，属性值必须用双引号（"）或单引号（&#8217;）括起来。</p>
    <p>　　4. XML文档必须是良构的（XSL文档也是XML文档中一种），也就是说标记必须有结束标记、标记可以嵌套但不可交叉，如：</p>
    <p>&lt;outer&gt;&lt;inner&gt;&lt;/inner&gt;&lt;inner/&gt;&lt;/outer&gt;</p>
    <p>是合法的，而下面的形式</p>
    <p>&lt;outer&gt;&lt;inner&gt;&lt;/outer&gt;&lt;/inner&gt;</p>
    <p>则是错误的。如果XML文档在浏览时出错，多半是违反了上面提到的规则。[page]</p>
    <p>上期我们讲到用CSS（层叠样式表）来格式化XML文档，其效果并不很令人满意。实际上CSS用来格式化HTML标记比较合适些，只是因为它简单才在上例中采用。<br><br>　　XML在更多的时候只是一种数据文件，怎样将它变为我们日常所看到的HTML格式那样的文件呢？如果我们将XML文件比作结构化的原料的话，那么XSL就好像"筛子"与"模子"，筛子选取自己需要的原料，这些原料再通过模子形成最终的产品：HTML。<br><br>　　这个模子大致是这样：我们先设计好表现的页面，再将其中需要从XML中获取数据来填充内容的部分"挖掉"，然后用XSL语句从XML中筛出相关的数据来填充。一言以譬之：这XSL实际上就是HTML的一个"壳子"，XML数据利用这个"壳"来生成"传统"的HTML。<br><br>　　XML在展开时是一个树形结构，我们将树形结构中自定义标记称为节点，节点之间存在父子、兄弟关系，我们要访问其中的结点从根结点就要以"／"来层层进入。<br><br>　　在XSL这个壳中，我们要从原料库XML里提取相关的数据，就要用到XSL提供的模式化查询语言。所谓模式化查询语言，就是通过相关的模式匹配规则表达式从XML里提取数据的特定语句，即我们上面所说的"筛子"。</p>
    <p>　　参考微软的"XSL开发者指南"，我们大致可将模式语言分为三种：</p>
    <p>　　选择模式：<br><br>&lt;xsl:for-each&gt;、&lt;xsl:value-of&gt;和 &lt;xsl:apply-templates&gt;</p>
    <p>　　测试模式：<br><br>&lt;xsl:if&gt; 和&lt;xsl:when&gt;</p>
    <p>　　匹配模式：<br><br>&lt;xsl:template&gt;<br><br>　　我们现在就分别对之进行介绍。<br><br>　　一、 选择模式<br><br>　　选择模式语句将数据从XML中提取出来，是一种简单获得数据的方法，这几个标记都有一个select属性，选取XML中特定的结点名的数据。<br><br>　　<strong>1、&lt;xsl:for-each&gt;</strong><br><br>　　如在XML中有这样的数据:<br><br>&lt;author&gt;<br>&lt;name&gt;小禹&lt;/name&gt;<br>&lt;name&gt;春华&lt;/name&gt;<br>&lt;name&gt;秋实&lt;/name&gt;<br>&lt;/author&gt;<br><br>　　我们要读取这三个作者名字，是一个一个地按"author/name"方法来读取吗，可有多个这样的name呀？如果有一种程序性的语句来循环读取有多好啊！<br><br>　　想得很对，XSL提供了这样的具有程序语言性质的语句：&lt;xsl:for-each&gt;<br><br>　　用它读取这三个作者名字的方法如下:<br><br>&lt;xsl:for-each select="author/name"&gt;<br>&#8230;&#8230;<br>&lt;/xsl:for-each&gt;<br><br>　　select，顾名思义，选取，它可以选定XML中特定唯一的标记，也可以选择某一类相同的标记，我们称之为结点集。<br><br>　　语法：<br><br>&lt;xsl:for-each select="pattern" order-by="sort-criteria-list"&gt;<br><br>　　属性：<br><br>　　1.select<br><br>　　根据XSL样式查询考察上下文以决定哪类结点集（满足select条件）使用此样式描述。作为一种简化的表示就是，如果你想对文档中的某一种标记的内容的显示方式进行格式化，就可以将让select等于此元素的标记名。例如欲对标记xml_mark进行格式化，即可用如下方式表示：<br><br>&lt;xsl:for-each select="xml_mark"&gt;<br>&lt;!--样式定义--&gt;<br>&lt;/xsl:for-each&gt;<br><br>　　2.order-by<br><br>　　以分号（;）分隔、作为排序标准的列表。在列表元素前添加加号（+）表示按此标记的内容以升序排序，添加减号（-）表示逆序排序。作为一种简化的表示就是，排序标准列表就是由select规定的标记的子标记的序列，每个标记之间以（;）分隔。</p>
    <p>　　<strong>2、&lt;xsl:value-of&gt;</strong><br><br>　　&lt;xsl:for-each&gt;模式只是选取节点，并没有取出节点的值，好比猴子只是爬到了树的某个枝干上，那么就用&lt; xsl:value-of &gt;来摘"胜利果实"吧！<br><br>　　语法：<br><br>&lt;xsl:value-of select="pattern"&gt;提取节点的值<br><br>　　属性：<br><br>　　select用来与当前上下文匹配的XSL式样。简单的讲，如果要在XSL文档某处插入某个XML标记（假定是xml_mark标记）的内容，可用如下方式表示：<br><br>&lt;xsl:value-of select="xml_mark"&gt;&lt;/xsl:value-of&gt;<br><br>　　或<br><br>&lt;xsl:value-of select="xml_mark"/&gt;<br><br>示例：<br><br>　　此处仍以上期的个人简历的作为例子，我们需要对文件（个人简历.xml）作一定修改，确切的说是将其中的第二行：<br><br>&lt;?xml:stylesheet type="text/css" href="resume.css"?&gt;<br><br>　　修改为：<br><br>&lt;?xml:stylesheet type="text/xsl" href="resume.xsl"?&gt;<br><br>　　然后建立一个新文件：resume.xsl，其内容如下：<br><br>&lt;?xml version="1.0" encoding="GB2312"?&gt;<br>&lt;HTML xmlns:xsl="http://www.w3.org/TR/WD-xsl"&gt;<br>&lt;HEAD&gt;<br>&lt;TITLE&gt;个人简历&lt;/TITLE&gt;<br>&lt;/HEAD&gt;&lt;BODY&gt; <br>&lt;xsl:for-each select="resume"&gt;<br>&lt;P/&gt;<br>&lt;TABLE border="1" cellspacing="0"&gt;<br>&lt;CAPTION style="font-size: 150%; font-weight: bold"&gt;<br>个人简历<br>&lt;/CAPTION&gt;<br>&lt;TR&gt;<br>&lt;TH&gt;姓名&lt;/TH&gt;&lt;TD&gt;&lt;xsl:value-of select="name"/&gt;&lt;/TD&gt;<br>&lt;TH&gt;性别&lt;/TH&gt;&lt;TD&gt;&lt;xsl:value-of select="sex"/&gt;&lt;/TD&gt;<br>&lt;TH&gt;生日&lt;/TH&gt;&lt;TD&gt;&lt;xsl:value-of select="birthday"/&gt;&lt;/TD&gt;<br>&lt;/TR&gt;<br>&lt;TR&gt;<br>&lt;TH&gt;技能&lt;/TH&gt;&lt;TD colspan="5"&gt;&lt;xsl:value-of select="skill"/&gt;&lt;/TD&gt;<br>&lt;/TR&gt;<br>&lt;/TABLE&gt;<br>&lt;/xsl:for-each&gt;<br>&lt;/BODY&gt;<br>&lt;/HTML&gt;<br><br>
    <div align=center><img height=98 alt="" src="http://www.7880.com/Upload/2004_Pack/0003.gif" width=270 border=0><br><span class=pt9>上例在浏览器中的样子(IE5.0或更新版本)</span><br><br></div>
    <p>　　完成这些以后再来让我们看一下辛勤劳动的成果，怎么样？效果不错吧。更酷还在后头呢。现在我们对文件（个人简历.xml）作进一步的修改：<br><br>　　1.在标记&lt;resume&gt;前添加一个新标记&lt;document&gt;；<br><br>　　2.将标记对&lt;resume&gt;&lt;/resume&gt;之间的内容（包括这一对标记）复制并粘贴在其后，并在最后用&lt;document&gt;结束。<br><br>　　3.以Notepad.exe打开文件resume.xsl，在标记&lt;HTML&gt;之后添加文字：&lt;xsl:for-each select="document"&gt;；在标记&lt;/HTML&gt;之前添加文字：&lt;/xsl:for-each&gt;，保存文件。<br><br>　　4.在浏览器中打开文件（个人简历.xml）。看到了什么？两份个人简历！<br><br></p>
    <div align=center><img height=98 alt="" src="http://www.7880.com/Upload/2004_Pack/0003.gif" width=270 border=0><br><img height=98 alt="" src="http://www.7880.com/Upload/2004_Pack/0003.gif" width=270 border=0><br><span class=pt9>上例在浏览器中的样子(IE5.0或更新版本)</span><br><br></div>
    <p><br>　　就这样，利用XML我们可以编写内容与样式完成分离的文档！当然，XSL文件比一般的HTML文件要复杂一些，然而一旦完成则可用于格式化所有同类的XML文档。[page]</p>
    <p>经过前几日的学习，我们学习了XHTML文档的编写和以及三个XSL元素，已能编写相当灵活的XSL文档，今天将学习的是XSL模板的编写。我们都知道，短的文档、程序十分好读，但当规模增大后，其复杂性也以更快的速度增加。</p>
    <p>　　前面我们学了&lt;xsl:for-each&gt;、&lt;xsl:value-of&gt;等，可以用它们对XML数据实现简单的格式化输出，但如果遇到比较复杂的XML格式输出，将XSL按照要求依次写下来的话，一是设计困难，可扩展性差，不利于人员之间的分工协作；另则，可修改性很差，可能会出现牵一发而动全军的情况，不利于维护。程序中模块化设计逐步细化的方法在这里得到了应用！ </p>
    <p>　　XSL模板将XSL的设计细化成一个个模板（块），最后再将这些模板（块）组合成一个完整的XSL；好比船与集装箱，我们不是将所有的货物一件件地堆起来，而是装在各自的集装箱中，然后再在船上将这些集装箱堆放起来。这种方法可以使你先从整体上考虑整个XSL的设计，然后将一些表现形式细化成不同的模块，再具体设计这些模块，最后将它们整合在一起，这样，将宏观与微观结合起来，符合人们条理化、规范化要求。</p>
    <p>&nbsp;</p>
    <p>　　装集装箱－－书写模板（块）：&lt;xsl:template&gt;</p>
    <p>　　语法：<br><br>&lt;xsl:template match="node-context" language="language-name"&gt;</p>
    <p>　　属性：</p>
    <p>　　match ── 确定什么样的情况下执行此模板。作为一种简化的说明，在此处使用标记的名字；其中最上层模板必须将match设为"/"。</p>
    <p>　　language ── 确定在此模板中执行什么脚本语言，其取值与HTML中的SCRIPT标记的LANGUAGE属性的取值相同，缺省值是Jscript。</p>
    <p>　　&lt;xsl:template&gt;用match属性从XML选取满足条件的节点，征对这些特定的节点形成一个特定输出形式的模板。</p>
    <p>&nbsp;</p>
    <p>　　吊集装箱上船－－调用模板（块）：&lt;xsl:apply-templates&gt;</p>
    <p>　　语法：</p>
    <p>&lt;xsl:apply-templates select="pattern" order-by="sort-criteria-list"&gt;</p>
    <p>属性：</p>
    <p>　　select ── 确定在此上下文环境中应执行什么模板，即选取用&lt; xsl:template &gt;标记建立的模板（块）。</p>
    <p>　　order-by ── 以分号（;）分隔的排序标准，通常是子标记的序列。</p>
    <p>　　示例：</p>
    <p>　　以个人简历为例，为便于处理我们希望"技能"中每一项都用标记对&lt;skill&gt;&lt;/skill&gt;括起来，有多少项技能就有多少个这种标记对，经过修改后的个人简历XML文档内容如下：</p>
    <p>&lt;?xml version="1.0" encoding="GB2312"?&gt;<br>&lt;?xml:stylesheet type="text/xsl" href="resume_template.xsl"?&gt;<br>&lt;document&gt;<br>&lt;resume&gt;<br>&lt;name&gt;禹希初&lt;/name&gt;<br>&lt;sex&gt;男&lt;/sex&gt;<br>&lt;birthday&gt;1977.5&lt;/birthday&gt;<br>&lt;skill&gt;数据库设计与维护&lt;/skill&gt;<br>&lt;skill&gt;WEB开发&lt;/skill&gt;<br>&lt;/resume&gt;<br>&lt;/document&gt;<br><br>　　然后，建立一个新XSL文件resume_template.xsl，采用模板的形式，其内容如下：<br><br>&lt;?xml version="1.0" encoding="GB2312"?&gt;<br>&lt;xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl"&gt;<br>&lt;!--根模板--&gt;<br>&lt;xsl:template match="/"&gt;<br>&lt;HTML&gt;&lt;HEAD&gt;&lt;TITLE&gt;个人简历&lt;/TITLE&gt;&lt;/HEAD&gt;<br>&lt;BODY&gt;<br>&lt;xsl:apply-templates select="document/resume"/&gt;<br>&lt;/BODY&gt;<br>&lt;/HTML&gt;<br>&lt;/xsl:template&gt;<br>&lt;!--简历模板--&gt;<br>&lt;xsl:template match="resume"&gt;<br>&lt;TABLE border="1" cellspacing="0"&gt;<br>&lt;CAPTION&gt;个人简历（<br>&lt;xsl:eval&gt;formatIndex(childNumber(this),"I")&lt;/xsl:eval&gt;<br>）&lt;/CAPTION&gt;<br>&lt;xsl:apply-templates select="name"/&gt;<br>&lt;xsl:apply-templates select="sex"/&gt;<br>&lt;xsl:apply-templates select="birthday"/&gt;<br>&lt;TR/&gt;<br>&lt;TD&gt;技能&lt;/TD&gt;&lt;TD COLSPAN="5"&gt;<br>&lt;TABLE cellspacing="0"&gt;<br>&lt;xsl:apply-templates select="skill"/&gt;<br>&lt;/TABLE&gt;<br>&lt;/TD&gt;<br>&lt;/TABLE&gt;<br>&lt;BR/&gt;<br>&lt;/xsl:template&gt;<br>&lt;!--姓名模板--&gt;<br>&lt;xsl:template match="name"&gt;&lt;TD&gt;姓名&lt;/TD&gt;<br>&lt;TD&gt;&lt;xsl:value-of/&gt;&lt;/TD&gt;<br>&lt;/xsl:template&gt;<br>&lt;!--性别模板--&gt;<br>&lt;xsl:template match="sex"&gt;&lt;TD&gt;性别&lt;/TD&gt;<br>&lt;TD&gt;&lt;xsl:value-of/&gt;&lt;/TD&gt;<br>&lt;/xsl:template&gt;<br>&lt;!--生日模板--&gt;<br>&lt;xsl:template match="birthday"&gt;&lt;TD&gt;生日&lt;/TD&gt;<br>&lt;TD&gt;&lt;xsl:value-of/&gt;&lt;/TD&gt;<br>&lt;/xsl:template&gt;<br>&lt;!--技能模板--&gt;<br>&lt;xsl:template match="skill"&gt;<br>&lt;TR&gt;&lt;TD&gt;&lt;xsl:value-of/&gt;&lt;/TD&gt;&lt;/TR&gt;<br>&lt;/xsl:template&gt;<br>&lt;/xsl:stylesheet&gt;<br><br>
    <div align=center><img height=120 alt="" src="http://www.7880.com/Upload/2004_Pack/0004.gif" width=237 border=0><br><span class=pt9>上例在浏览器中的样子(IE5.0或更新版本)</span><br><br></div>
    <p><br><br>　　保存文件，打开文件（个人简历.xml），效果令人满意吧。其实要做到同样的效果，用前面三周介绍的方法也可做，但你得把它作为一整体考虑。<br><br>　　在上面的XSL文件中，我们将性别、生日、技能等数据项分别用模板来单独写，再用&lt;xsl:apply-template&gt;来调用，这样，即使你日后要对这些模板作相应的修改与扩充也很方便，不致于出现互相干扰、混杂不清的情况。这种从上至下、逐层细化的设计方法，极大地减少工作复杂程度，也大大减少了差错的产生，可以实现多人的协作设计。 </p>
    <p>　　注意：如果XML文档中不同标记有同名的子标记，在为其编写模板时，应把父标记作为其前缀，格式为（parent_mark/child_mark）。模板文件必须有一个根模板，其属性match是"/"。[page]</p>
    <p>XML技术的优势之一就在于数据输出的可选择性，即选择需要的数据输出。前面我们所讲到的选择模式语句:&lt;xsl:for-each&gt;、&lt;xsl:value-of&gt;及&lt;xsl:apply-template&gt;只是简单的选取通过"/"符号层层到达的节点，如果我们对XML数据不需要全部输出，而只需要其中的满足某条件的部分数据，"萝卜青菜、各取所需"，那么条件判断&lt;xsl:if&gt;与多条件判断&lt;xsl:choose&gt;及&lt;xsl:when&gt;则迎合了这种需要，如果你对程序设计熟悉的话，会觉得它们似曾相识。 </p>
    <p>&nbsp;</p>
    <p>　　XSL中的IF，首先，介绍XSL元素&lt;xsl:if&gt;的语法结构： </p>
    <p>&nbsp;</p>
    <p>　　语法：</p>
    <p>&nbsp;</p>
    <p>&lt;xsl:if expr="script-expression" language="language-name" test="pattern"&gt;</p>
    <p>&nbsp;</p>
    <p>　　属性：</p>
    <p>　　expr ── 脚本语言表达式，计算结果为"真"或"假"；如果结果为"真"，且通过test，则在输出中显示其中内容（可省略此项属性）。 </p>
    <p>　　language ── expr属性中表达式的脚本语言类型，其取值与HTML标记SCRIPT的LANGUAGE属性的取值相同，缺省为"JScript"。<br><br>　　test ──源数据测试条件。 </p>
    <p>　　示例： </p>
    <p>　　此处以一份报表为例，文件名为report.xml，其内容如下： </p>
    <p>&lt;?xml version="1.0" encoding="GB2312"?&gt;<br>&lt;?xml:stylesheet type="text/xsl" href="report.xsl"?&gt;<br>&lt;document&gt;<br><br>&lt;report&gt;<br>&lt;class&gt;<br>甲班<br>&lt;/class&gt;<br>&lt;q1&gt;50&lt;/q1&gt;<br>&lt;q2&gt;70&lt;/q2&gt;<br>&lt;q3&gt;30&lt;/q3&gt;<br>&lt;q4&gt;10&lt;/q4&gt;<br>&lt;/report&gt;<br><br>&lt;report&gt;<br>&lt;class&gt;<br>乙班<br>&lt;/class&gt;<br>&lt;q1&gt;20&lt;/q1&gt;<br>&lt;q2&gt;30&lt;/q2&gt;<br>&lt;q3&gt;40&lt;/q3&gt;<br>&lt;q4&gt;50&lt;/q4&gt;<br>&lt;/report&gt;<br><br>&lt;report&gt;<br>&lt;class&gt;<br>丙班<br>&lt;/class&gt;<br>&lt;q1&gt;70&lt;/q1&gt;<br>&lt;q2&gt;40&lt;/q2&gt;<br>&lt;q3&gt;20&lt;/q3&gt;<br>&lt;q4&gt;10&lt;/q4&gt;<br>&lt;/report&gt;<br><br>&lt;/document&gt; </p>
    <p>&nbsp;</p>
    <p>　　我们采用XSL模板结合今天所学的&lt;xsl:if&gt;，为其编写一个XSL文档，要求季度产量小于等于20的用红色表示，文件名为report.xsl，内容如下： </p>
    <p><br>&lt;?xml version="1.0" encoding="GB2312"?&gt;<br>&lt;xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl"&gt;</p>
    <p>&lt;xsl:template match="/"&gt;<br>&lt;HTML&gt;&lt;HEAD&gt;&lt;TITLE&gt;1999年生产统计&lt;/TITLE&gt;&lt;/HEAD&gt; <br>&lt;BODY&gt;&lt;xsl:apply-templates select="document"/&gt;&lt;/BODY&gt;<br>&lt;/HTML&gt;<br>&lt;/xsl:template&gt;</p>
    <p>&lt;xsl:template match="document"&gt;<br>&lt;H3&gt;1999年生产统计&lt;/H3&gt; <br>&lt;TABLE border="1" cellspacing="0"&gt; <br>&lt;TH&gt;班组&lt;/TH&gt;<br>&lt;TH&gt;一季度&lt;/TH&gt;<br>&lt;TH&gt;二季度&lt;/TH&gt;<br>&lt;TH&gt;三季度&lt;/TH&gt;<br>&lt;TH&gt;四季度&lt;/TH&gt;<br>&lt;xsl:apply-templates select="report"/&gt;<br>&lt;/TABLE&gt;<br>&lt;/xsl:template&gt;</p>
    <p>&lt;xsl:template match="report"&gt;<br>&lt;TR&gt;<br>&lt;TD&gt;&lt;xsl:value-of select="class"/&gt;&lt;/TD&gt;<br>&lt;TD&gt;&lt;xsl:apply-templates select="q1"/&gt;&lt;/TD&gt;<br>&lt;TD&gt;&lt;xsl:apply-templates select="q2"/&gt;&lt;/TD&gt;<br>&lt;TD&gt;&lt;xsl:apply-templates select="q3"/&gt;&lt;/TD&gt;<br>&lt;TD&gt;&lt;xsl:apply-templates select="q4"/&gt;&lt;/TD&gt;<br>&lt;/TR&gt;<br>&lt;/xsl:template&gt;</p>
    <p>&lt;xsl:template match="q1|q2|q3|q4"&gt;<br>&lt;!--此处测试产量，如小于等于20则添加一STYLE属性color，其值为red（红色）--&gt; <br>&lt;xsl:if test=".[value()$le$20]"&gt; <br>&lt;xsl:attribute name="style"&gt;color:red&lt;/xsl:attribute&gt;<br>&lt;/xsl:if&gt;<br>&lt;xsl:value-of/&gt;<br>&lt;/xsl:template&gt;</p>
    <p>&lt;/xsl:stylesheet&gt;<br>
    <div align=center><img height=164 alt="" src="http://www.7880.com/Upload/2004_Pack/0005.gif" width=268 border=0><br><span class=pt9>上例在浏览器中的样子(IE5.0或更新版本)</span><br><br></div>
    <p>&nbsp;</p>
    <p>&nbsp;</p>
    <p>　　说明：</p>
    <p>　　q1|q2|q3|q4 ──　标记q1、q2、q3、q3均用此模板确定输出</p>
    <p>　　$le$ ──　是关系运算符中的"小于等于"，其它关系有小于（$lt$）、大于（$gt$）、大于等于（$ge$）、等于（$eq$）、不等于（$ne$）等。</p>
    <p>　　. ──　表示引用当前标记。</p>
    <p>　　[ ] ──　表示筛选，只有满足筛选条件的标记才能被选取。</p>
    <p>&nbsp;</p>
    <p>　　value() ──XSL函数，其他常用XSL函数有text()、end()、index()等。 </p>
    <p>&nbsp;</p>
    <p>&nbsp;</p>
    <p>　　下期，我们将学习XSL的另外三个元素，可对同一数据进行多次测试，根据不同条件产生相应输出。[page]</p>
    <p>上期我们学习了XSL元素&lt;xsl:if&gt;，已能通过测试XML数据的值来决定不同的输出形式，不知你尝试过没有，实际上&lt;xsl:for-each&gt;也可部分实现&lt;xsl:if&gt;的功能，但有时，我们希望对同一数据同时测试多个条件，根据不同条件输出相应结果。当然，我们可以用if，假如我们只有if可用的话。幸好我们有一个更好的选择，那就是用&lt;xsl:choose&gt;。下面介绍相关元素的语法：<br><br>　　&lt;xsl:choose&gt;<br><br>　　语法：&lt;xsl:choose&gt;<br><br>　　属性：无，表示一个多选测试的开始</p>
    <p>　　&lt;xsl:when&gt;<br><br>　　语法：<br><br>&lt;xsl:when expr="script-expression" language="language-name" test="pattern"&gt;<br><br>　　属性：<br><br>　　expr ── 脚本语言表达式，计算结果为"真"或"假"；如果结果为"真"，且通过test，则在输出中显示其中内容（可省略此项属性）。<br><br>　　language ── expr属性中表达式的脚本语言类型，其取值与HTML标记SCRIPT的LANGUAGE属性的取值相同，缺省为"JScript"。<br><br>　　test ── 源数据测试条件。</p>
    <p>　　&lt;xsl:otherwise&gt;<br><br>　　语法：&lt;xsl:otherwise&gt;<br><br>　　属性：无，在一个多选测试中，如果没有不满足&lt;xsl:when&gt;规定的条件，如果在最后有此标记，则输出此标记中的内容。</p>
    <p>　　示例：</p>
    <p>　　此处以学生成绩单为例，要求按成绩的高低给出优秀（ &gt;85）、一般（70~85）、及格（60~69）、不及格（&lt; 60），而不是显示分数。其中成绩单的XML文档(文件名：grade.xml)如下：<br><br>&lt;?xml version="1.0" encoding="GB2312"?&gt;<br>&lt;?xml:stylesheet type="text/xsl" href="grade.xsl"?&gt;<br>&lt;document&gt;<br>&lt;grade&gt;<br>&lt;name&gt;大胖&lt;/name&gt;<br>&lt;english&gt;80&lt;/english&gt;<br>&lt;math&gt;90&lt;/math&gt;<br>&lt;chymest&gt;90&lt;/chymest&gt;<br>&lt;/grade&gt;<br>&lt;grade&gt;<br>&lt;name&gt;小花&lt;/name&gt;<br>&lt;english&gt;98&lt;/english&gt;<br>&lt;math&gt;70&lt;/math&gt;<br>&lt;chymest&gt;85&lt;/chymest&gt;<br>&lt;/grade&gt;<br>&lt;/document&gt;</p>
    <p>　　为实现按分数分等级显示，其XSL文档（文件名：grade.xsl）内容如下：</p>
    <p>&lt;?xml version="1.0" encoding="GB2312"?&gt;<br>&lt;xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl"&gt;<br>&lt;xsl:template match="/"&gt;<br>&lt;HTML&gt;<br>&lt;HEAD&gt;&lt;TITLE&gt;成绩单&lt;/TITLE&gt;&lt;/HEAD&gt;<br>&lt;BODY&gt;<br>&lt;xsl:apply-templates select="document"/&gt;<br>&lt;/BODY&gt;<br>&lt;/HTML&gt;<br>&lt;/xsl:template&gt;</p>
    <p>&lt;xsl:template match="document"&gt;<br>&lt;TABLE border="1" cellspacing="0"&gt;<br>&lt;TH&gt;姓名&lt;/TH&gt;&lt;TH&gt;英语&lt;/TH&gt;&lt;TH&gt;数学&lt;/TH&gt;&lt;TH&gt;化学&lt;/TH&gt;<br>&lt;xsl:apply-templates select="grade"/&gt;<br>&lt;/TABLE&gt;<br>&lt;/xsl:template&gt;</p>
    <p>&lt;xsl:template match="grade"&gt;<br>&lt;TR&gt;<br>&lt;TD&gt;&lt;xsl:apply-templates select="name"/&gt;&lt;/TD&gt;<br>&lt;TD&gt;&lt;xsl:apply-templates select="english"/&gt;&lt;/TD&gt;<br>&lt;TD&gt;&lt;xsl:apply-templates select="math"/&gt;&lt;/TD&gt;<br>&lt;TD&gt;&lt;xsl:apply-templates select="chymest"/&gt;&lt;/TD&gt;<br>&lt;/TR&gt;<br>&lt;/xsl:template&gt;</p>
    <p>&lt;xsl:template match="name"&gt;<br>&lt;xsl:value-of/&gt;<br>&lt;/xsl:template&gt;</p>
    <p>&lt;xsl:template match="english|math|chymest"&gt;<br>&lt;xsl:choose&gt;<br>&lt;xsl:when test=".[value()$gt$85]"&gt;优秀&lt;/xsl:when&gt;<br>&lt;xsl:when test=".[value()$gt$70]"&gt;一般&lt;/xsl:when&gt;<br>&lt;xsl:when test=".[value()$gt$60]"&gt;起格&lt;/xsl:when&gt;<br>&lt;xsl:otherwise&gt;不起格&lt;/xsl:otherwise&gt;<br>&lt;/xsl:choose&gt;<br>&lt;/xsl:template&gt;</p>
    <p>&lt;/xsl:stylesheet&gt;<br><br>
    <div align=center><img height=100 alt="" src="http://www.7880.com/Upload/2004_Pack/0006.gif" width=170 border=0><br><span class=pt9>上例在浏览器中的样子(IE5.0或更新版本)</span><br><br></div>
    <p>　　说明：在&lt;xsl:choose&gt;选择中，从第一个&lt;xsl:when&gt;开始，逐个测试，直到满足一个测试条件就将其中的内容输出，不再测试后面的条件；如果不满足任何一个条件，则输出&lt;xsl:otherwise&gt;中的内容。<br><br>　　标记对&lt;xsl:when&gt;&lt;/xsl:when&gt;与&lt;xsl:otherwise&gt;&lt;/xsl:otherwise&gt;中可嵌套&lt;xsl:if&gt;或&lt;xsl:choose&gt;。</p>
<img src ="http://www.blogjava.net/yanrui312/aggbug/130125.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yanrui312/" target="_blank">颜颜</a> 2007-07-13 16:57 <a href="http://www.blogjava.net/yanrui312/articles/130125.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Webservice 的设计和模式 </title><link>http://www.blogjava.net/yanrui312/articles/129003.html</link><dc:creator>颜颜</dc:creator><author>颜颜</author><pubDate>Mon, 09 Jul 2007 03:12:00 GMT</pubDate><guid>http://www.blogjava.net/yanrui312/articles/129003.html</guid><wfw:comment>http://www.blogjava.net/yanrui312/comments/129003.html</wfw:comment><comments>http://www.blogjava.net/yanrui312/articles/129003.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yanrui312/comments/commentRss/129003.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yanrui312/services/trackbacks/129003.html</trackback:ping><description><![CDATA[Webservice 作为一项新的技术出现在我们面前，它的出世是用于解决在不同的平台下的应用的协同的。目前几乎每家厂商都要去开发Webservice 应用，然而如果缺乏对Webservice更深的了解，不能很好的在设计阶段处理好一些重要的问题，那么最终完成的系统必然是效率低下，没有可靠性的产品。
<div>&nbsp;</div>
<div>在设计Webservice 应用时，以下几点务必要考虑到：</div>
<div>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 管理好与外系统的协同关系</div>
<div>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 掌握底层的传输模型</div>
<div>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 提供与应用相适应的安全策略</div>
<div>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 计划好部署的相关事项</div>
<div>&nbsp;</div>
<div>以下，将就这几条相关的设计需求和一些常用模式是如何应用于Webservice模型展开详细讨论。在讨论中，你会发现Webservice这项新的技术是如何与我们在以往的软件开发相结合的。</div>
<div>&nbsp;</div>
<div>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 标准提供了协同的能力</div>
<div>&nbsp;</div>
<div>Webservice的一个最基本的目的就是提供在各个不同平台的不同应用系统的协同工作能力。</div>
<div>为了使得一个公司的网络应用达到最高的效率，存在它自己和它的合作伙伴，供应商以及客户之间的Webservice，应该能够实现无缝的交互。如果在众多的Webservice之间不能轻松的实现交互，那么该应用的效率将大打折扣。但是，在现实中这种情况是极有可能出现的。由于各个公司对业务的理解各不相同，就是理解相同的情况下，对于相同的概念也可能用不同的形式加以表现，具体而言就是对于同一数据可能采取不同的xml表示。由于以上的原因，对于协同性的问题应该在设计应用架构时就加以考虑，而不是留待以后去改变。</div>
<div>&nbsp;</div>
<div>Webservice 主要由以下几块技术所构成，SOAP (Simple Object Access Protocol), WSDL (Web service Description Language), 以及UDDI (Universal Description, Discovery and Integration)。</div>
<div>&nbsp;</div>
<div></div>
<div>在这里我们不会去详细研究这些技术，而是揭示他们的一些重要特性，这些特性需要在Webservice的设计时详加考虑。</div>
<div>&nbsp;</div>
<div>WSDL是实现协同能力的关键，它提供了一份契约用于与新老的应用之间交互。这项技术使得各个组织可以将标准的制定集中在Service的外部接口，而不用考虑各组织的具体实现。简而言之，它实现了Webservice的接口与实现的分离。从而使得标准的制定，更加容易。并且，基于这份接口描述，很多工具可以从中自动生成客户端代码，减少了开发者的工作量，并使得大部分开发者摆脱了编写SOAP消息传递代码过程。</div>
<div>&nbsp;</div>
<div>SOAP是实现在各个Webservice组件之间传递消息的传输层。因此，SOAP应该是一项透明的协同技术。但是，由于很多的SOAP实现方法却与标准背道而驰，要么添加了新的扩展功能要么删减了一些标准功能。由于对SOAP标准的支持程度不同，使得Webservice的协同能力大打折扣，实现协同的困难加大了。基于这种情况，当开发者需要Webservice运行在不同平台上时，就要对具体情况加以了解并相应的编码以解决这种不一致性。如果所有的SOAP实现组织都能够遵循标准的话，那么Webservice的开发者就不需要考虑使用该Webservice的底层平台了。</div>
<div>&nbsp;</div>
<div>尽管如此，不同SOAP实现的协同还是相当困难，因为协同标准的制定存在大量的分歧，目前一些组织正致力于标准的制定，比如<a href="http://www.soapbuilders.org/"><u><font color=#0000ff>SOAP Builders</font></u></a> 和 <a href="http://www.ws-i.org/"><u><font color=#0000ff>WS-I</font></u></a>。然而，现在Webservice开发者只有针对不同平台，给予不同的实现，使得开发的成本和负担加大了。 </div>
<div>&nbsp;</div>
<div>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 理解传输模型</div>
<div>&nbsp;</div>
<div>SOAP并不是完全透明的解决方案，它把一些复杂的实现细节隐藏起来。Webservice的开发者必须深入的了解SOAP，了解底层的传输机制以及模型,从而知道SOAP是如何实现的。在一些简单的应用中，某些工具可以帮助Webservice的开发者生成SOAP消息传递的代码，但是这只在最简单的应用中有效。真正的情况不可能那么简单，可能在某些方面你需要有特殊的处理（这种情况在实际开发中是很常见的），这个时候，你就需要直接操纵SOAP的消息传递代码，以及一些底层的XML内容。因此，Webservice的开发者需要深入了解SOAP和XML层的内容。</div>
<div>&nbsp;</div>
<div>在开发Webservice的接口的时候，不要以为使用XML技术，协作性的问题就迎刃而解了，XML并不是解决集成问题的灵丹妙药。这里同样需要标准的制定，需要一个在业界公认的词汇表。仅仅在你的设计框架中引入XML技术并不能保证系统具有协同性，XML仅仅是用来描述数据的语言，XML自己并不提供语义去理解数据。就如同英语和德语都使用拉丁字母，但是他们的语义却并不相同。</div>
<div>&nbsp;</div>
<div>即使你使用相同的语言，也不能保证具有良好的协作性。比如你的公司可能使用Order描述一个订单，但你的合作伙伴可能使用Purchase_Order，而另一个伙伴可能又不相同。你不可能强迫你所有的合作伙伴都采用和你相同的词汇。因此需要有一项技术可以在众多的描述之间充当翻译的角色。XSLT就是这么一种技术，它用于不同语言的转换。和XSLT的配合使用XML才能解决协同性的问题。</div>
<div>&nbsp;</div>
<div>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DOM vs. SAX</div>
<div>许多的Webservice开发环境，将开发者从底层的XML文档的解析和处理中解放出来，他们提供了自动化或者很方便的工具，使得这一过程变得很简单。但是对于一些有特殊要求的Webservice应用，比如需要更好的柔性或者对速度要求特别高的应用，就需要手工处理XML文档。这时候两种XML解析的模型－DOM 和SAX的选择，将成为重要的问题。</div>
<div>&nbsp;</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp; </div>
<div>DOM使用树状图的方式解析XML文档，而SAX则更多的采用事件驱动的模型。</div>
<div>&nbsp;</div>
<div>DOM先将XML文档映射成一颗树，然后通过采用一系列与树相关的操作去处理这份文档。这种方法有很多的好处，首先开发者很容易理解，使用一颗树这对于开发者来说是最常见不过的了。DOM最常用于XML在Service中需要频繁修改的场合。当然DOM也有它的缺点，在处理XML文档的时候，它需要载入整个文档，而不管你需要修改的是否只是其中的一小部分。因此它的运行效率以及对内存的使用显然是不能接受的，尤其是面对很大的XML文档。</div>
<div>SAX使用事件驱动的模型来处理XML文档。通过一系列事件的触发，来完成对XML的解析，你可以只关心你所要处理的事件，当这些事件发生时，会调用到相应的回调函数来通知到你。采用这种方式就可以在很大程度上提高XML文档解析的效率。但是它的缺点在于难于使用，以及对同一文档的多次处理会存在一些问题。</div>
<div>总而言之，DOM更适合处理那种文档型的XML文件，而SAX则适于那种想直接将XML结构映射成在你系统中的一个对象的操作。（比如将一个XML结构直接映射成JAVA中的一个Class）或者那种针对XML文件中特殊Tag的操作。</div>
<div>&nbsp;</div>
<div>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 文档交换vs. RPC模型</div>
<div>这两种交互方式应该在应用架构的设计初始就应该详加考虑，因为它将在很大程度上决定系统的耦合程度。</div>
<div>RPC（Remote Procedure Call）本质上就是远程方法的调用。尽管Webservice是基于XML的但是你仍然可以使用远程方法调用这种模式来进行Webservice的实现，尤其是在那种简单的请求相应的模型中。在这个过程中，传输中的XML文件所描述的更多是有关远程方法的信息，比如方法名，方法参数等等。</div>
<div>而文档交换方式，与RPC相比较在XML文件中不是做远程方法的映射，而是一份完整的自包含的业务文档，当Service端收到这份文档后，先进行预处理（比如词汇的翻译和映射），然后再构造出返回消息。这个构造返回消息的过程中，往往不再是简简单单的一个方法调用，而是多个对象协同完成一个事务的处理，再将结果返回。</div>
<div></div>
<div>这两种方式的区别，类似与打电话和发邮件的不同处理方法。在目前，对于第一种方法提供了很多自动化的工具使得远程方法的调用能够很容易的完成，而后一种方法缺少一系列工具的支持，需要开发者手工完成。</div>
<div>尽管如此，在此还是推荐使用文档交换的方式。由于它在以下方面具有RPC所不具备的优点。</div>
<div>使用文档方式，你可以充分利用XML文件的功能去描述和验证一份业务文档，而在RPC模型中XML仅仅被用于描述方法的信息。</div>
<div>使用文档方式，在客户的Service的提供者之间不再需要紧密的约定，而RPC模型需要客户和Service的提供者紧密相连，一旦方法发生变化，客户端就需要做相应的改动。这不符合低耦合系统的要求，而在文档交换方式中则灵活的多。</div>
<div>由于业务数据是自包含的，显然文档模型更利于采用异步处理。</div>
<div>&nbsp;</div>
<div>&nbsp;</div>
<div>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 利用设计模式</div>
<div>设计模式在设计Webservice的时候显然可以起到相当大的作用。设计模式的主要目的就是为解决某些在类似环境下的相像问题提供已有的较为成熟的设计方案。在这里，只简单的提及一些很常用的模式，让我们了解到模式在Webservice中可以起到的作用。</div>
<div>Adapter ：为内部系统提供一个不同的接口</div>
<div>Fa&#231;ade： 封装复杂的内部实现，提供一系列简单的接口</div>
<div>Proxy： 作为其他对象的代理，代替它提供服务</div>
<div>&nbsp;</div>
<div>Adapter模式用于将一个组件的接口转化成客户所需要的样子，这里的客户就是Webservice。一个常见的情况就是将原有的老的系统包装成一个Webservice。比如现在使用的是J2EE的平台，而原来有一个C++的系统实现了某些功能，现在需要将它发布成Webservice，那么就需要利用JNI技术做一个Adapter，为原来的C++组件提供一个Java的接口，然后再转化为Webservice。</div>
<div>&nbsp;</div>
<div>Fa&#231;ade模式用于构建粗粒度的服务，它包装了细粒度的服务，从而为复杂的系统提供了一个简单的接口。在J2EE中，Session Bean就象是一个Fa&#231;ade，而Entity Bean则是细粒度的服务。在Webservice中也一样，使用Fa&#231;ade模式可以将已有的组件的功能发挥殆尽。</div>
<div></div>
<div>&nbsp;</div>
<div>Proxy 模式用于充当其他对象的代理，类似于中间人的作用，将处理工作从一个对象传递到另一个对象。在Webservice中，它主要用于隐藏Soap消息构造的过程。也可以用于模拟对象（Mock Object）的创建。</div>
<div>&nbsp;</div>
<div>以上仅仅是一些可以用于Webservice开发的模式，如果你熟练的将这些模式应用于Webservice开发，你将会发现开发Webservice应用，将好像做一种特殊的面向对象设计。</div>
<div>&nbsp;</div>
<div>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 安全</div>
<div>Webservice为作为方便的服务被用广大领域使用的同时，也成为了黑客们的美食。在这里，本文将就目前对Webservice安全所能做的改进做简单介绍。</div>
<div>在Webservice中的安全主要分为以下三个方面。</div>
<div>传输&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SSL/HTTPS 对连接加密，而不是传输数据</div>
<div>消息&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 数据加密(XML Encryption) &nbsp;&nbsp;数字签名(XML-DSIG)</div>
<div>底层架构&nbsp; 利用应用服务安全机制</div>
<div>&nbsp;</div>
<div>传输时的安全是最容易被加入到你的Webservice应用中的，利用现有的SSL 和HTTPS协议，就可以很容易的获得连接过程中的安全。</div>
<div>&nbsp;</div>
<div>然而这种安全实现方法有两个弱点。一是它只能保证数据传输的安全，而不是数据本身的安全，数据一旦到达某地，那么就可以被任何人所查看。而在Webservice中，一份数据可能到达多个地方，而这份数据却不该被所有的接受者所查看。二是它提供的是要么全有要么全无的保护，你不能选择哪部分数据要被保护，而这种可选择性也是在Webservice中所常要用到的。</div>
<div>&nbsp;</div>
<div>第二层的保护是对于消息本身的保护。你可以使用已有的XML安全扩展标准，实现数字签名的功能，从而保证你的消息是来自特定方并没有被修改过。XML文件的加密技术从更大程度上加强了Webservice的安全，它能够定制数据传输到后，能否被接受者所查看，进一步完善了传输后的安全，业界也在不断的制定Webservice的安全标准，比如SAML 和 WS-Security。</div>
<div>&nbsp;</div>
<div>最后一层保护就是依靠底层架构的安全，这更多的来自于操作系统和某些中间件的保护。比如在J2EE中，主持Webservice的应用服务器。目前很多的J2EE应用服务器都支持Java Authentication and Authorization Service (JAAS)，这是最近被加入到J2SE 1.4当中的。利用主持Webservice的服务器，实现一些安全机制这是很自然的做法。另一种利用底层架构的安全方法就是，做一个独立的负责安全的服务器，Webservice的使用者和创建者都需要与之取得安全信任。<br></div>
<img src ="http://www.blogjava.net/yanrui312/aggbug/129003.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yanrui312/" target="_blank">颜颜</a> 2007-07-09 11:12 <a href="http://www.blogjava.net/yanrui312/articles/129003.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Fckeditor 资料整理</title><link>http://www.blogjava.net/yanrui312/articles/123019.html</link><dc:creator>颜颜</dc:creator><author>颜颜</author><pubDate>Sat, 09 Jun 2007 06:31:00 GMT</pubDate><guid>http://www.blogjava.net/yanrui312/articles/123019.html</guid><wfw:comment>http://www.blogjava.net/yanrui312/comments/123019.html</wfw:comment><comments>http://www.blogjava.net/yanrui312/articles/123019.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yanrui312/comments/commentRss/123019.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yanrui312/services/trackbacks/123019.html</trackback:ping><description><![CDATA[<table style="TABLE-LAYOUT: fixed">
    <tbody>
        <tr>
            <td>
            <div class=cnt>
            <div class=navigation>
            <div class=alignleft><span class=content>以下内容为收集整理：＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝</span></div>
            </div>
            <div class=post>
            <div class=entry>
            <p>javascript调用方式：<br>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－<br>&lt;script type=&#8221;text/javascript&#8221; src=&#8221;FCKeditor/fckeditor.js&#8221;&gt;&lt;/scrīpt&gt;</p>
            <p>&lt;textarea name=&#8221;content&#8221; cols=&#8221;80&#8243; rows=&#8221;4&#8243;&gt;<br>&lt;/textarea&gt;<br>&lt;script type=&#8221;text/javascript&#8221;&gt;<br>var oFCKeditor = new FCKeditor(&#8221;content&#8221;);<br>oFCKeditor.BasePath = &#8220;FCKeditor/&#8221;;<br>oFCKeditor.Height = 400;<br>oFCKeditor.ToolbarSet = &#8220;Default&#8221;;<br>oFCKeditor.ReplaceTextarea();<br>&lt;/script&gt;<br>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－</p>
            <p>如果想要使用从数据库读来的文本数据或者是后台来自文件的txt/html文本数据。<br>只要在<br>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－<br>&lt;textarea name=&#8221;content&#8221; cols=&#8221;80&#8243; rows=&#8221;4&#8243;&gt;<br>&lt;/textarea&gt;<br>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－</p>
            <p>中加入自己的显示内容的formbean对应字段即可<br>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－<br>&lt;textarea name=&#8221;content&#8221; cols=&#8221;80&#8243; rows=&#8221;4&#8243;&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;c:out value=&#8221;${contentData}&#8221; /&gt;<br>&lt;/textarea&gt;<br>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－</p>
            <p>这样内容就会被显示在FCKeditor编辑框中了，点击提交按钮以后就可以在后台的相应java action中得到content参数中的内容就是页面上FCKeditor中的内容数据了。可以在struts/jsf中使用。</p>
            <p>＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝</p>
            <p>由于给FCKeditor瘦身，所以常会报缺少对象支持等错误，只要在FCKeditor/editor/lang中加上相应的js语言文件即可。如果加载页面失败（FCKeditor未定义）还有一个可能就是引用FCKeditor/fckeditor.js文件路径不对！</p>
            <p>关于FCKeditor瘦身要点如下：<br>1.将FCKeditor目录下及子目录下所有以&#8220;_&#8221;下划线开头的文件夹删除<br>2.FCKeditor根目录下只保留fckconfig.js, fckeditor.js, fckstyles.xml, fcktemplates.xml，其余全部删除<br>3.将editor/filemanager/upload目录下文件及文件夹清空<br>4.将/editor/filemanager/browser/default/connectors/下的所有文件删除<br>5.还可以将editor/skins目录下的皮肤文件删除，只留下default一套皮肤（如果你不需要换皮肤的话）<br>6.还可以将editor/lang目录下文件删除，只保留en.js, fcklanguagemanager.js, zh-cn.js, zh.js文件<br>7.如果你是使用javascrīpt来调用加载FCKeditor，那么就不需要在web.xml中配置fckeditor的tag文件。<br>8.还有一个问题刚开始使用FCKeditor的人常会遇到就怎么控制它的toolbar的大小和元素排列，其实很简单。<br>在fckconfig.js中用这样的标签[]来划分每行的元素的多少，这样就可以控制toolbar的长短和大小了，具体示例参看：fckconfig.js中的toolbarset[&#8221;Default&#8221;]</p>
            <p>＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝</p>
            <p>用fckconfig.js中的一些选项来控制toolbarset中控件的功能，实现功能裁剪：<br>1)：取消超链接中的浏览服务器和上传功能，方法如下：<br>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－<br>FCKConfig.LinkBrowser = true;<br>FCKConfig.LinkUpload = true;<br>改为：<br>FCKConfig.LinkBrowser = false;<br>FCKConfig.LinkUpload = false;<br>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－</p>
            <p>2)：取消图片链接中的浏览服务器和上传功能，方法如下：<br>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－<br>FCKConfig.ImageUpload = true;<br>FCKConfig.ImageBrowser = true;<br>改为：<br>FCKConfig.ImageUpload = false;<br>FCKConfig.ImageBrowser = false;<br>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－</p>
            <p>3)：Dlg Button中取消高级功能，方法如下：<br>FCKConfig.LinkDlgHideAdvanced = false ;<br>FCKConfig.ImageDlgHideAdvanced = false ;<br>改为：<br>FCKConfig.ImageDlgHideAdvanced = true ;<br>FCKConfig.LinkDlgHideTarget = true ;<br>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－</p>
            <p>下一篇介绍FCKeditor的上传和浏览服务器功能，以及如何在里面实现动态</p>
            <p>超连接，转发到servlet经过filter以后去调用服务器的action</p>
            <p>如何实现对应用户浏览自己的图片的列表实现！</p>
            <p>＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝</p>
            <p>FCKeditor集成java servlet可以实现文件的上传和服务器端列表读取功能FCKeditor自己提供了两个servlet来分别实现上传文件功能，和读取服务器端文件列表功能，这两个servlet分别为：<br>com.fredck.FCKeditor.connector.ConnectorServlet（读取文件列表）<br>com.fredck.FCKeditor.uploader.SimpleUploaderServlet（实现文件上传）</p>
            <p>1.浏览服务器端文件列表<br>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－<br>web.xml文件中，比如：ConnectorServlet中的配置选项：<br>&lt;init-param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;param-name&gt;baseDir&lt;/param-name&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;param-value&gt;/UserFiles/&lt;/param-value&gt;<br>&lt;/init-param&gt;</p>
            <p>意思是在浏览服务器上的baseDir配置指定里面的所有文件及其目录结构列表。<br>如果你的baseDir没有配置，Connector将会自动创建一个默认的文件夹<br>UserFiles，对应的ConnectorServlet中init()方法中代码如下：<br>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－<br>baseDir = getInitParameter(&#8221;baseDir&#8221;);<br>if (baseDir == null)<br>&nbsp;&nbsp;&nbsp;&nbsp; baseDir = &#8220;/UserFiles/&#8221;;</p>
            <p>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－<br>还想说一下的是，FCKeditor的client调用server的servlet方法采用的是Ajax思想来实现。当你点击浏览服务器(browser server)的时候就会触发一个异步的javascrīpt + xmlhttp的调用响应，后台的servlet会去完成你要请求的事件，然后数据以xml方式返回给client来解析。很明显，你要实现去数据库或者其他的文件系统请求列表，你只要修改<br>ConnectorServlet中两个私有方法：getFolders 和 getFiles</p>
            <p>让它去你指定的地方得到文件列表即可，这样你的文件可以放在任何你指定目录下。多说一句，很多人都想知道个人blog系统中怎么实现上传文件以后对应用户浏览自己的列表的，我的做法很简单，建立你用户名的文件夹，你上传只能上传到你的目录夹，浏览可以通过程序指定浏览对应用户下的文件夹即可，这个时候你要修改Connectorservlet中的路径即可！<br>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－</p>
            <p>2.超连接重定位问题<br>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－<br>FCKeditor可以插入超连接，实现对文件的预览功能，只要我们稍微改变我们可以使FCKeditor编辑器支持对任意文件系统下的任意文件的客户端浏览和下载保存！FCKeditor本来提供的是相对URL超链接，只要我们修改ConnectorServlet中传递给客户端的地址的时候，把它改写成绝对URL然后再通过我们自己的filter的servlet实现重定向去一个下载/浏览文件的struts的action方法就可以实现在客户端对超连接文件的下载和浏览！说一下具体做法吧：</p>
            <p>1)：修改ConnectorServlet传递给客户端javascrīpt的路径，代码如下：<br>String currentUrl = &#8220;<a target=_blank><font color=#000000>http://&#8221;</font></a> + request.getserver +request.getServerPort + request.getContextPath + resourcePath;</p>
            <p>以上代码请在ConnectorServlet的doGet()里面拼装！在调用CreateCommonXml()私有方法的时候参数传入：<br>myEl.setAttribute(&#8221;path&#8221;,currentPath);<br>myEl.setAttribute(&#8221;url&#8221;,currentUrl);</p>
            <p>提醒一下resourcePath为在web.xml配置文件中ConnectorServlet中的一个初始化参数配置，等一下利用filter实现对超连接的重定位就提取URL中的这个配置参数来判断，配置如下：<br>&lt;init-param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;param-name&gt;resourcePath&lt;/param-name&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;param-value&gt;/fileSystem/&lt;/param-value&gt;<br>&lt;/init-param&gt;</p>
            <p>2)：建立你的filter servlet，实现对URL的截获，对符合要求的URL进行重定位到你的对应action中去即可<br>3)：实现你的对应action来实现文件的上传和下载功能即可！<br>4)：扩展功能－实现对URL的加密，对连接的URL中加上一串字符，最后几位作为算法校验，对不符合要求的URL连接,filter将会拒绝重定位到指定action。此外利用自己写的扩展类还可以实现对超连接的文件类型进行限制，比如你只能超连接JPG|GIF|DOC|TXT|HTML等几种后缀名的文件，对其他文件即使你指定超连接也让你浏览和下载，这些都可以在web.xml中通过修改对应servlet的配置文件的初始化参数实现。</p>
            <p>3.页面javascrīpt修改<br>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－<br>浏览服务器的功能对应的html/javascrīpt相关的文件为：browser.html和frmresourcelist.html对应你想传递的信息你可以append在文件名的字符串后面，在GetFileRowHtml（）的javascrīpt函数中实现对文件名的截取，这样client只会显示文件名，而你可以得到文件的数据库唯一标识，任何你想要的信息你都可以通过修改ConnectorServlet中的私有方法getFiles()来实现，只要修改页面frmresurcelist.html中的GetFileRowHtml（）中传入变量fileName即可。你还可以在点击选中文件的时候实现一个你自己的Ajax调用，一切取决你的项目需要！</p>
            <p>4.我不是一个javascrīpt高手，其实如果我对javascrīpt了解多一些也许对客户端的代码修改以后做出更眩的功能。可以更好的完成对FCKeditor裁剪。<br>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－</p>
            <p>5.注意点<br>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－<br>无论怎么修改别人的东西，请一定尊重开源精神！<br>很多人配置好了FCKeditor的上传功能以后常会遇到xmlhttp request 404 error,后面是一串路径，其实就是你的servlet-mapping中的路径不对，你只要把xmlhttp request errot 404 后面跟的路径，copy到你的web.xml中对应红色文字的位置，如下：<br>&lt;servlet-mapping&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;servlet-name&gt;Connector&lt;/servlet-name&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;url-pattern&gt;/FCKeditor/editor/filemanager/browser/default/connectors/jsp/connector&lt;/url-pattern&gt;<br>&lt;/servlet-mapping&gt;</p>
            <p>别忘了SimpleUploader的servlet－mapping也要做同样的修改！</p>
            <p>还有一个错误就是http 500错误，这个可能是你的URL请求不对，应该和FCKeditor没关系的!</p>
            <p>＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝</p>
            <p>fckconfig.js总配置文件，可用记录本打开，修改后将文件存为utf-8 编码格式。找到：<br>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－<br>FCKConfig.TabSpaces = 0;<br>改为：<br>FCKConfig.TabSpaces = 1;</p>
            <p>即在编辑器域内可以使用Tab键。</p>
            <p>如果你的编辑器还用在网站前台的话，比如说用于留言本或是日记回复时，那就不得不考虑安全了，<br>在前台千万不要使用Default的toolbar，要么自定义一下功能，要么就用系统已经定义好的Basic，<br>也就是基本的toolbar，找到：<br>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－<br>FCKConfig.ToolbarSets[&#8221;Basic&#8221;] = [<br>[&#8217;Bold&#8217;,'Italic&#8217;,'-&#8217;,'OrderedList&#8217;,'UnorderedList&#8217;,'-&#8217;,/*&#8217;Link&#8217;,*/&#8217;Unlink&#8217;,'-&#8217;,'Style&#8217;,'FontSize&#8217;,'TextColor&#8217;,'BGColor&#8217;,'-&#8217;,<br>&#8216;Smiley&#8217;,'SpecialChar&#8217;,'Replace&#8217;,'Preview&#8217;] ];</p>
            <p>这是改过的Basic，把图像功能去掉，把添加链接功能去掉，因为图像和链接和flash和图像按钮添加功能都能让前台页直接访问和上传文件， fckeditor还支持编辑域内的鼠标右键功能。</p>
            <p>FCKConfig.ContextMenu = [&#8217;Generic&#8217;,/*&#8217;Link&#8217;,*/&#8217;Anchor&#8217;,/*&#8217;Image&#8217;,*/&#8217;Flash&#8217;,'Select&#8217;,'Textarea&#8217;,'Checkbox&#8217;,'Radio&#8217;,'TextField&#8217;,'HiddenField&#8217;,<br>/*&#8217;ImageButton&#8217;,*/&#8217;Button&#8217;,'BulletedList&#8217;,'NumberedList&#8217;,'TableCell&#8217;,'Table&#8217;,'Form&#8217;];</p>
            <p>这也是改过的把鼠标右键的&#8220;链接、图像，FLASH，图像按钮&#8221;功能都去掉。</p>
            <p>找到：<br>FCKConfig.FontNames = &#8216;Arial;Comic Sans MS;Courier New;Tahoma;Times New Roman;Verdana&#8217;;</p>
            <p>加上几种我们常用的字体</p>
            <p>FCKConfig.FontNames = &#8216;宋体;黑体;隶书;楷体_GB2312;Arial;Comic Sans MS;Courier New;Tahoma;Times New Roman;Verdana&#8217;;</p>
            <p>添加文件 /TestFCKeditor/test.jsp:<br>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－<br>&lt;%@ page language=&#8221;java&#8221; import=&#8221;com.fredck.FCKeditor.*&#8221; %&gt;<br>&lt;%@ taglib uri=&#8221;/TestFCKeditor&#8221; prefix=&#8221;FCK&#8221; %&gt;<br>&lt;script type=&#8221;text/javascript&#8221; src=&#8221;/TestFCKeditor/FCKeditor/fckeditor.js&#8221;&gt;&lt;/script&gt;</p>
            <p>&lt;%&#8211;<br>三种方法调用FCKeditor<br>1.FCKeditor自定义标签 (必须加头文件 &lt;%@ taglib uri=&#8221;/TestFCKeditor&#8221; prefix=&#8221;FCK&#8221; %&gt; )<br>2.script脚本语言调用 (必须引用 脚本文件 &lt;script type=&#8221;text/javascript&#8221; src=&#8221;/TestFCKeditor/FCKeditor/fckeditor.js&#8221;&gt;&lt;/script&gt; )<br>3.FCKeditor API 调用 (必须加头文件 &lt;%@ page language=&#8221;java&#8221; import=&#8221;com.fredck.FCKeditor.*&#8221; %&gt; )<br>&#8211;%&gt;</p>
            <p>&lt;%&#8211;<br>&lt;form action=&#8221;show.jsp&#8221; method=&#8221;post&#8221; target=&#8221;_blank&#8221;&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;FCK:editor id=&#8221;content&#8221; basePath=&#8221;/TestFCKeditor/FCKeditor/&#8221;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; width=&#8221;700&#8243; height=&#8221;500&#8243; skinPath=&#8221;/TestFCKeditor/FCKeditor/editor/skins/silver/&#8221;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; toolbarSet = &#8220;Default&#8221;&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 内容<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;/FCK:editor&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;input type=&#8221;submit&#8221; value=&#8221;Submit&#8221;&gt;<br>&lt;/form&gt;<br>&#8211;%&gt;</p>
            <p>&lt;form action=&#8221;show.jsp&#8221; method=&#8221;post&#8221; target=&#8221;_blank&#8221;&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;table border=&#8221;0&#8243; width=&#8221;700&#8243;&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;tr&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;td&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;textarea id=&#8221;content&#8221; name=&#8221;content&#8221; style=&#8221;WIDTH: 100%; HEIGHT: 400px&#8221;&gt;input&lt;/textarea&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;script type=&#8221;text/javascript&#8221;&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var oFCKeditor = new FCKeditor(&#8217;content̵ <img class=wp-smiley alt=;) src="http://blog.olist.cn/wp-includes/images/smilies/icon_wink.gif"> ;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; oFCKeditor.BasePath = &#8220;/TestFCKeditor/FCKeditor/&#8221; ;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; oFCKeditor.Height = 400;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; oFCKeditor.ToolbarSet = &#8220;Default&#8221; ;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; oFCKeditor.ReplaceTextarea();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/script&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;input type=&#8221;submit&#8221; value=&#8221;Submit&#8221;&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/td&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/tr&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;/table&gt;<br>&lt;/form&gt;</p>
            <p>&lt;%&#8211;<br>&lt;form action=&#8221;show.jsp&#8221; method=&#8221;post&#8221; target=&#8221;_blank&#8221;&gt;<br>&lt;%<br>FCKeditor oFCKeditor ;<br>oFCKeditor = new FCKeditor( request, &#8220;content&#8221; ) ;<br>oFCKeditor.setBasePath( &#8220;/TestFCKeditor/FCKeditor/&#8221; ) ;<br>oFCKeditor.setValue( &#8220;input&#8221; );<br>out.println( oFCKeditor.create() ) ;<br>%&gt;<br>&lt;br&gt;<br>&lt;input type=&#8221;submit&#8221; value=&#8221;Submit&#8221;&gt;<br>&lt;/form&gt;<br>&#8211;%&gt;</p>
            <p>添加文件/TestFCKeditor/show.jsp:<br>&lt;%<br>String content = request.getParameter(&#8221;content&#8221;);<br>out.print(content);<br>%&gt;</p>
            <p>＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝</p>
            <p>1、适时打开编辑器<br>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－<br>很多时候，我们在打开页面的时候不需要直接打开编辑器，而在用到的时候才打开，这样一来有很好的用户体验，另一方面可以消除FCK在加载时对页面打开速度的影响，点击&#8220;Open Editor&#8221;按钮后才打开编辑器界面。</p>
            <p>实现原理：<br>使用JAVASCRIPT版的FCK，在页面加载时（未打开FCK），创建一个隐藏的TextArea域，这个TextArea<br>的name和ID要和创建的FCK实例名称一致，然后点击&#8221;Open Editor&#8221;按钮时，通过调用一段函数，使用<br>FCK的ReplaceTextarea()方法来创建FCKeditor，代码如下：<br>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－<br>&lt;script type=&#8221;text/javascript&#8221;&gt;<br>&lt;!&#8211;<br>function showFCK(){<br>&nbsp;&nbsp; var oFCKeditor = new FCKeditor( &#8216;fbContent&#8217; ) ;<br>&nbsp;&nbsp; oFCKeditor.BasePath = &#8216;/FCKeditor/&#8217; ;<br>&nbsp;&nbsp; oFCKeditor.ToolbarSet = &#8216;Basic&#8217; ;<br>&nbsp;&nbsp; oFCKeditor.Width = &#8216;100%&#8217; ;<br>&nbsp;&nbsp; oFCKeditor.Height = &#8216;200&#8242; ;<br>&nbsp;&nbsp; oFCKeditor.ReplaceTextarea() ;<br>}<br>//&#8211;&gt;<br>&lt;/script&gt;<br>&lt;textarea name=&#8221;fbContent&#8221; id=&#8221;fbContent&#8221;&gt;&lt;/textarea&gt;</p>
            <p>2、使用FCKeditor 的 API<br>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－<br>FCKeditor编辑器，提供了非常丰富的API，用于给End User实现很多想要定制的功能，比如最基本的数据验证，如何在提交的时候用JS判断当前编辑器区域内是否有内容，FCK的API提供了GetLength()方法；</p>
            <p>再比如如何通过脚本向FCK里插入内容，使用InsertHTML()等；</p>
            <p>还有，在用户定制功能时，中间步骤可能要执行FCK的一些内嵌操作，那就用ExecuteCommand()方法。</p>
            <p>详细的API列表，请查看FCKeditor的Wiki。而常用的API，请查看FCK压缩包里的_samples/html/sample08.html。此处就不贴代码了。</p>
            <p>3、外联编辑条（多个编辑域共用一个编辑条）<br>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－<br>这个功能是2.3版本才开始提供的，以前版本的FCKeditor要在同一个页面里用多个编辑器的话，得一个个创建，现在有了这个外联功能，就不用那么麻烦了，只需要把工具条放在一个适当的位置，后面就可以无限制的创建编辑域了。</p>
            <p>要实现这种功能呢，需要先在页面中定义一个工具条的容器：&lt;div id=&#8221;xToolbar&#8221;&gt;&lt;/div&gt;，然后再根据这个容器的id属性进行设置。</p>
            <p>JAVASCRIPT实现代码：<br>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－<br>&lt;div id=&#8221;xToolbar&#8221;&gt;&lt;/div&gt;<br>FCKeditor 1:<br>&lt;script type=&#8221;text/javascript&#8221;&gt;<br>&lt;!&#8211;<br>&nbsp;&nbsp;&nbsp;&nbsp; // Automatically calculates the editor base path based on the _samples directory.<br>&nbsp;&nbsp;&nbsp;&nbsp; // This is usefull only for these samples. A real application should use something like this:<br>&nbsp;&nbsp;&nbsp;&nbsp; // oFCKeditor.BasePath = &#8216;/fckeditor/&#8217;; // &#8216;/fckeditor/&#8217; is the default value.<br>&nbsp;&nbsp;&nbsp;&nbsp; var sBasePath = document.location.pathname.substring(0,document.location.pathname.lastIndexOf(&#8217;_samples&#8217;));</p>
            <p>&nbsp;&nbsp;&nbsp;&nbsp; var oFCKeditor = new FCKeditor( &#8216;FCKeditor_1&#8242; );<br>&nbsp;&nbsp;&nbsp;&nbsp; oFCKeditor.BasePath = sBasePath;<br>&nbsp;&nbsp;&nbsp;&nbsp; oFCKeditor.Height = 100;<br>&nbsp;&nbsp;&nbsp;&nbsp; oFCKeditor.Config[ &#8216;ToolbarLocation&#8217; ] = &#8216;Out:parent(xToolbar)&#8217;;<br>&nbsp;&nbsp;&nbsp;&nbsp; oFCKeditor.Value = &#8216;This is some &lt;strong&gt;sample text&lt;/strong&gt;. You are using FCKeditor.&#8217;;<br>&nbsp;&nbsp;&nbsp;&nbsp; oFCKeditor.Create();<br>//&#8211;&gt;<br>&lt;/script&gt;<br>&lt;br /&gt;<br>FCKeditor 2:<br>&lt;script type=&#8221;text/javascript&#8221;&gt;<br>&lt;!&#8211;<br>&nbsp;&nbsp;&nbsp;&nbsp; oFCKeditor = new FCKeditor( &#8216;FCKeditor_2&#8242; );<br>&nbsp;&nbsp;&nbsp;&nbsp; oFCKeditor.BasePath = sBasePath;<br>&nbsp;&nbsp;&nbsp;&nbsp; oFCKeditor.Height = 100;<br>&nbsp;&nbsp;&nbsp;&nbsp; oFCKeditor.Config[ &#8216;ToolbarLocation&#8217; ] = &#8216;Out:parent(xToolbar)&#8217;;<br>&nbsp;&nbsp;&nbsp;&nbsp; oFCKeditor.Value = &#8216;This is some &lt;strong&gt;sample text&lt;/strong&gt;. You are using FCKeditor.&#8217;;<br>&nbsp;&nbsp;&nbsp;&nbsp; oFCKeditor.Create();<br>//&#8211;&gt;<br>&lt;/script&gt;<br>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－<br>此部分的详细DEMO请参照_samples/html/sample11.html，_samples/html/sample11_frame.html</p>
            <p>4、文件管理功能、文件上传的权限问题<br>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－<br>一直以来FCKeditor的文件管理部分的安全是个值得注意，但很多人没注意到的地方，虽然FCKeditor在各个Release版本中一直存在的一个功能就是对上传文件类型进行过滤，但是她没考虑过另一个问题：到底允许谁能上传？到底谁能浏览服务器文件？</p>
            <p>之前刚开始用FCKeditor时，我就出现过这个问题，还好NetRube（FCKeditor中文化以及FCKeditor ASP版上传程序的作者）及时提醒了我，做法是去修改FCK上传程序，在里面进行权限判断，并且再在fckconfig.js里把相应的一些功能去掉。但随之FCK版本的不断升级，每升一次都要去改一次配置程序fckconfig.js，我发觉厌烦了，就没什么办法能更好的控制这种配置么？事实上，是有的。</p>
            <p>在fckconfig.js里面，有关于是否打开上传和浏览服务器的设置，在创建FCKeditor时，通过程序来判断是否创建有上传浏览功能的编辑器。首先，我先在fckconfig.js里面把所有的上传和浏览设置全设为false，接着我使用的代码如下：</p>
            <p>JAVASCRIPT版本：<br>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－<br>&lt;script type=&#8221;text/javascript&#8221;&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp; var oFCKeditor = new FCKeditor( &#8216;fbContent&#8217; );<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;% if power = powercode then %&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp; oFCKeditor.Config[&#8217;LinkBrowser&#8217;] = true;<br>&nbsp;&nbsp;&nbsp;&nbsp; oFCKeditor.Config[&#8217;ImageBrowser&#8217;] = true;<br>&nbsp;&nbsp;&nbsp;&nbsp; oFCKeditor.Config[&#8217;FlashBrowser&#8217;] = true;<br>&nbsp;&nbsp;&nbsp;&nbsp; oFCKeditor.Config[&#8217;LinkUpload&#8217;] = true;<br>&nbsp;&nbsp;&nbsp;&nbsp; oFCKeditor.Config[&#8217;ImageUpload&#8217;] = true;<br>&nbsp;&nbsp;&nbsp;&nbsp; oFCKeditor.Config[&#8217;FlashUpload&#8217;] = true;<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;% end if %&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp; oFCKeditor.ToolbarSet = &#8216;Basic&#8217;;<br>&nbsp;&nbsp;&nbsp;&nbsp; oFCKeditor.Width = &#8216;100%&#8217;;<br>&nbsp;&nbsp;&nbsp;&nbsp; oFCKeditor.Height = &#8216;200&#8242;;<br>&nbsp;&nbsp;&nbsp;&nbsp; oFCKeditor.Value = &#8216;&#8217;;<br>&nbsp;&nbsp;&nbsp;&nbsp; oFCKeditor.Create();<br>&lt;/script&gt;<br>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－</p>
            <p>在按钮旁边加文字<br>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－<br>打开 editor/js/ 两个js文件<br>fckeditorcode_gecko.js<br>fckeditorcode_ie.js</p>
            <p>第一个是支持非ie浏览器的<br>第二个文件是支持ie浏览器的</p>
            <p>搜索 FCKToolbarButton，可以看到许多类似这样的语句：<br>case &#8216;Save&#8217;:B = new FCKToolbarButton(&#8217;Save&#8217;, FCKLang.Save, null, null, true, null, 3); break;</p>
            <p>&#8216;Save&#8217;是按钮英文名字<br>FCKToolbarButton 的四个参数分别是：<br>按钮命令名称，按钮标签文字，按钮工具提示，按钮样式，按钮是否在源代码模式可见，按钮下拉菜单其中将第4项参数设置为 FCK_TOOLBARITEM_ICONTEXT 即可使按钮旁边出现文字，注意没有引号。<br>例如：<br>case &#8216;Preview&#8217;:B = new FCKToolbarButton(&#8217;Preview&#8217;, FCKLang.Preview, null, FCK_TOOLBARITEM_ICONTEXT, true, null, 5);<br>这样我们就可以将 我们经常用的3种模式源代码、预览、全屏编辑按钮都加上文字了。</p>
            <p>解释fck样式的工作原理<br>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－<br>fck的样式设置涉及到了两个文件，一个是你定义好的样式表文件.css，另一个是告诉fck样式表如何使用的xml文件，两个文件确一不可。<br>css文件的位置是不做要求的，但是需要你在应用的编辑器的页面上插入样式表文件的链接。这样才能显示出来样式。<br>fckstyles.xml 在与editor目录同级的目录下。该文件定义了那些样式可以使用在那些标签里面。</p>
            <p>这就是fck自带的样式xml定义：<br>&lt;?xml version=&#8221;1.0&#8243; encoding=&#8221;utf-8&#8243; ?&gt;<br>&lt;Styles&gt;<br>&nbsp;&nbsp; &lt;Style name=&#8221;Image on Left&#8221; element=&#8221;img&#8221;&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;Attribute name=&#8221;style&#8221; value=&#8221;padding: 5px; margin-right: 5px&#8221; /&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;Attribute name=&#8221;border&#8221; value=&#8221;2&#8243; /&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;Attribute name=&#8221;align&#8221; value=&#8221;left&#8221; /&gt;<br>&nbsp;&nbsp; &lt;/Style&gt;<br>&nbsp;&nbsp; &lt;Style name=&#8221;Image on Right&#8221; element=&#8221;img&#8221;&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;Attribute name=&#8221;style&#8221; value=&#8221;padding: 5px; margin-left: 5px&#8221; /&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;Attribute name=&#8221;border&#8221; value=&#8221;2&#8243; /&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;Attribute name=&#8221;align&#8221; value=&#8221;right&#8221; /&gt;<br>&nbsp;&nbsp; &lt;/Style&gt;<br>&nbsp;&nbsp; &lt;Style name=&#8221;Custom Bold&#8221; element=&#8221;span&#8221;&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;Attribute name=&#8221;style&#8221; value=&#8221;font-weight: bold;&#8221; /&gt;<br>&nbsp;&nbsp; &lt;/Style&gt;<br>&nbsp;&nbsp; &lt;Style name=&#8221;Custom Italic&#8221; element=&#8221;em&#8221; /&gt;<br>&nbsp;&nbsp; &lt;Style name=&#8221;Title&#8221; element=&#8221;span&#8221;&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;Attribute name=&#8221;class&#8221; value=&#8221;Title&#8221; /&gt;<br>&nbsp;&nbsp; &lt;/Style&gt;<br>&nbsp;&nbsp; &lt;Style name=&#8221;Code&#8221; element=&#8221;span&#8221;&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;Attribute name=&#8221;class&#8221; value=&#8221;Code&#8221; /&gt;<br>&nbsp;&nbsp; &lt;/Style&gt;<br>&nbsp;&nbsp; &lt;Style name=&#8221;Title H3&#8243; element=&#8221;h3&#8243; /&gt;<br>&nbsp;&nbsp; &lt;Style name=&#8221;Custom Ruler&#8221; element=&#8221;hr&#8221;&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;Attribute name=&#8221;size&#8221; value=&#8221;1&#8243; /&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;Attribute name=&#8221;color&#8221; value=&#8221;#ff0000&#8243; /&gt;<br>&nbsp;&nbsp; &lt;/Style&gt;<br>&lt;/Styles&gt;</p>
            <p>每一个&lt;style&gt;将来会生成一个样式的菜单项。name名称就是显示在菜单里的文字；element定义了该样式可以应用在那种html标签上，&lt;Attribute&gt;的 name 指定了将会修改标签的哪个属性来应用样式，value则是修改成的值。<br>看这个：<br>&lt;Style name=&#8221;Title&#8221; element=&#8221;span&#8221;&gt;<br>&nbsp;&nbsp; &lt;Attribute name=&#8221;class&#8221; value=&#8221;Title&#8221; /&gt;<br>&lt;/Style&gt;</p>
            <p>如果你在fck选定了文字 &#8220;经典论坛 》 前台制作与脚本专栏 》 FCKeditor 实战技巧 - 1 》 编辑帖子&#8221; 应用该样式 则原来文字就会变成&lt;span class=&#8221;Title&#8221;&gt;经典论坛 》 前台制作与脚本专栏 》 FCKeditor 实战技巧 - 1 》 编辑帖子&lt;/span&gt;</p>
            <p>注意：如果编辑器呈整页编辑状态，那么整页里面也需要插入样式表链接才能显示出来样式。</p>
            <p>＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝</p>
            <p>FCKeditor JavaScript API（翻译整理）<br>原文地址：<a href="http://wiki.fckeditor.net/Developer%20s_Guide/%20_API" target=_blank><u><font color=#0000ff>http://wiki.fckeditor.net/Developer%27s_Guide/Javascript_API</font></u></a><br>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－<br>FCK 编辑器加载后，将会注册一个全局的 FCKeditorAPI 对象。</p>
            <p>FCKeditorAPI 对象在页面加载期间是无效的，直到页面加载完成。如果需要交互式地知道 FCK 编辑器已经加载完成，可使用&#8220;FCKeditor_OnComplete&#8221;函数。<br>&lt;script type=&#8221;text/javascript&#8221;&gt;<br>function FCKeditor_OnComplete(editorInstance) {<br>&nbsp;&nbsp; FCKeditorAPI.GetInstance(&#8217;FCKeditor1&#8242;).Commands.GetCommand(&#8217;FitWindow&#8217;).Execute();<br>}<br>&lt;/script&gt;</p>
            <p>在当前页获得 FCK 编辑器实例：<br>var oEditor = FCKeditorAPI.GetInstance(&#8217;InstanceName&#8217;);</p>
            <p>从 FCK 编辑器的弹出窗口中获得 FCK 编辑器实例：<br>var oEditor = window.parent.InnerDialogLoaded().FCK;</p>
            <p>从框架页面的子框架中获得其它子框架的 FCK 编辑器实例：<br>var oEditor = window.FrameName.FCKeditorAPI.GetInstance(&#8217;InstanceName&#8217;);</p>
            <p>从页面弹出窗口中获得父窗口的 FCK 编辑器实例：<br>var oEditor = opener.FCKeditorAPI.GetInstance(&#8217;InstanceName&#8217;);</p>
            <p>获得 FCK 编辑器的内容：<br>oEditor.GetXHTML(formatted); // formatted 为：true|false，表示是否按HTML格式取出<br>也可用：<br>oEditor.GetXHTML();</p>
            <p>设置 FCK 编辑器的内容：<br>oEditor.SetHTML(&#8221;content&#8221;, false); // 第二个参数为：true|false，是否以所见即所得方式设置其内容。此方法常用于&#8220;设置初始值&#8221;或&#8220;表单重置&#8221;操作。</p>
            <p>插入内容到 FCK 编辑器：<br>oEditor.InsertHtml(&#8221;html&#8221;); // &#8220;html&#8221;为HTML文本</p>
            <p>检查 FCK 编辑器内容是否发生变化：<br>oEditor.IsDirty();</p>
            <p>在 FCK 编辑器之外调用 FCK 编辑器工具条命令：<br><font color=#ff0000>命令列表如下：</font><br>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－<br>DocProps, Templates, Link, Unlink, Anchor, BulletedList, NumberedList, About, Find, Replace, Image, Flash, SpecialChar, Smiley, Table, TableProp, TableCellProp, UniversalKey, Style, FontName, FontSize, FontFormat, Source, Preview, Save, NewPage, PageBreak, TextColor, BGColor, PasteText, PasteWord, TableInsertRow, TableDeleteRows, TableInsertColumn, TableDeleteColumns, TableInsertCell, TableDeleteCells, TableMergeCells, TableSplitCell, TableDelete, Form, Checkbox, Radio, TextField, Textarea, HiddenField, Button, Select, ImageButton, SpellCheck, FitWindow, Undo, Redo<br>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－<br>使用方法如下：<br>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－<br>oEditor.Commands.GetCommand(&#8217;FitWindow&#8217;).Execute();<br>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－</p>
            </div>
            </div>
            </div>
            </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.blogjava.net/yanrui312/aggbug/123019.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yanrui312/" target="_blank">颜颜</a> 2007-06-09 14:31 <a href="http://www.blogjava.net/yanrui312/articles/123019.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>appfuse简介</title><link>http://www.blogjava.net/yanrui312/articles/118889.html</link><dc:creator>颜颜</dc:creator><author>颜颜</author><pubDate>Mon, 21 May 2007 07:29:00 GMT</pubDate><guid>http://www.blogjava.net/yanrui312/articles/118889.html</guid><wfw:comment>http://www.blogjava.net/yanrui312/comments/118889.html</wfw:comment><comments>http://www.blogjava.net/yanrui312/articles/118889.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yanrui312/comments/commentRss/118889.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yanrui312/services/trackbacks/118889.html</trackback:ping><description><![CDATA[<p>一、Appfuse简介</p>
<p>Appfuse是Matt Raible 开发的一个指导性的入门级J2EE框架，它对如何集成流行的Spring、Hibernate、ibatis、struts、Xdcolet、junit 等基础框架给出了示范，最新的1.7版更是提供了对Taperstry和JSF的支持。在持久层，AppFuse采用了Hibernate O/R映射工具（http://www.hibernate.org）；在容器方面，它采用了Spring Framework（http://www.springframework.org）。用户可以自由选择Struts、Spring/MVC， Webwork，Taperstry、JSF这几个web框架。采用TDD的开发方式，使用JUnit测试各层，甚至测试 jsp 输出的 w/o 错误。为了简化开发，预定义好了一套目录结构、基类、用来创建数据库、配置Tomcat、测试部署应用的 Ant 任务，帮助快速自动生成源程序和自动维护部分配置文件。</p>
<p>参考资料： <br>在https://appfuse.dev.java.net/可以下载Appfuse，目前的版本是1.7。 <br>Appfuse的参考资料和文档可以在http://raibledesigns.com/wiki/Wiki.jsp?page=AppFuse查看。</p>
<a name=more></a>
<p>二、Appfuse框架快速入门</p>
<p>AppFuse项目的主要目的是帮助开发人员减少在开始一个项目时所要做的工作。以下是使用它新建一个项目的基本步骤：</p>
<p>1、下载或从CVS (cvs -d :pserver:guest@cvs.dev.java.net:/cvs co appfuse)检出appfuse最新版本之源码。</p>
<p>2、安装J2SE 1.4+, 正确设置JAVA_HOME环境变量, 安装Ant 1.6.2+, 设置ANT_HOME环境变量。</p>
<p>3、安装MySQL 3.23.x+ (建议使用 4.1.7版本)和Tomcat 4.1.x+ (建议使用 5.0.28版本)，设置CATALINA_HOME环境变量指向你Tomcat安装目录。</p>
<p>注意: 如果你准备使用MySQL 4.1.7，那么你必须将其默认的字符集设置为UTF-8字符集，并且将其默认的表类型设置为InnoDB类型。也就是说，你要在你的c:\Windows\my.ini 或者/etc/my.cnf 文件中添加以下几行：<br>[mysqld]<br>default-character-set=utf8<br>[mysqld]<br>default-table-type=innodb </p>
<p>4、 安装一个本地的SMTP服务器，或者如果你已经有一个可用的SMTP服务器的话，你可以去修改mail.properties (在web/WEB-INF/classes目录下) 和build.properties (在根目录下 -- 为 log4j信息) 以指向你的SMTP服务器 - 默认地它是指向你的本机的SMTP服务器的。</p>
<p>5、将lib/junit3.8.1/junit.jar文件拷贝到$ANT_HOME/lib目录下。</p>
<p>6、执行 ant new -Dapp.name=YOURAPPNAME -Ddb.name=YOURDBNAME 命令。这将创建一个名为&#8220;YOURAPPNAME&#8221;的目录。</p>
<p>警告: 该命令对于某些app.name值将不执行 - 不要使用 "test"，任何包含 "appfuse" 在其中的名你，或者任何以数字、两个存折号(-) 等等混合出来的名称。 </p>
<p>7、 转到新的目录，执行ant的setup任务创建数据库，同时将你的应用发布到Tomcat服务器上。只有当你的root用户没有口令建库的任务才会工作。 你也可以在需要的时候打开build.properties文件去更改这root用户的口令。如果你想进行测试并且希望了解是否所有方面均可以工作完好， 那么你可以执行ant的test-all任务进行全面的测试 -当然前提是当你做测试的时候先将Tomcat服务器停止。</p>
<p>8、执行ant的test-reports任务 - 当这个任务执行完后，会有一条消息告诉你如何查看那些产生的测试报告。</p>
<p>当你确定你通过以上步骤配置好你的AppFuse开发环境后 - 下面你需要做的事就是学习一下指南来了解如何使用 AppFuse 进行你的开发。 </p>
<p>可选择的安装</p>
<p>如果你愿意选择用iBATIS做为你的持久层框架，请专门去看一下extras/ibatis目录下的 README.txt 文件。 <br>如果你愿意选择用Spring做为你的WEB层框架，请专门去看一下extras/spring目录下的 README.txt 文件。 <br>如果你愿意选择用WebWork做为你的WEB层框架，请专门去看一下extras/webwork目录下的 README.txt 文件。 <br>如果你愿意选择Tapestry做为你的web层框架，请专门去看一下extras/tapestry目录下的 README.txt 文件。 <br>如果你愿意选择JSF做为你的web层框架，请专门去看一下extras/jsf目录下的 README.txt 文件。 <br><br>如果你希望你能够通过脚本来自动地完成创建和测试，那么可以参考以下的脚本： <br>rm -r ../appfuse-spring<br>ant new -Dapp.name=appfuse-spring -Ddb.name=ibatis<br>cd ../appfuse-spring<br>ant install-ibatis install-springmvc<br>cd extras/ibatis<br>ant uninstall-hibernate<br>cd ../..<br>ant setup <br>ant test-all test-reports </p>
<p>如果你并不想安装iBATIS, Spring MVC 或者 WebWork，在你将你的项目放入代码控制仓库前你应该删除掉它们在extras目录中的安装内容。 </p>
<p>--------------------------------------------------------------------------------<br></p>
<p>通 常当你完成了以上所有步骤并使它们可以工作后，最可能的事是你会希望把&#8220;org.appfuse&#8221;包名，改为类似&#8220;com.company&#8221;这样的包名。 现在做这件事已经非常简单了，所有你需要做的事就是下载一个改包名的工具，看看它的README文件，以了解它的安装和使用。</p>
<p>注意: 使用这个工具前你最好是将你的项目做一个备份，从而保证能够恢复它。 </p>
<p>如果你将org.appfuse.webapp.form包改为如test.web.form这样的包名，你得同时去修改一下src/service包中的ConverterUtil类，getOpposingObject方法是你的朋友，让我们来看一下： </p>
<p>name = StringUtils.replace(name, "model", "webapp.form");<br>name = StringUtils.replace(name, "webapp.form", "model"); </p>
<p>三、AppFuse 开发指南</p>
<p>如果你已经下载了AppFuse并且想在你的机器上安装它，你最好按照快速入门中的步骤进行安装。一旦你已经将所有的内容安装好后，以下的指南是你学习如何使用AppFuse进行开发的最好的教程。 </p>
<p>注意: 这个开发指南在AppFuse的发布版本中同样包含一份，如果你想更新在你的工程中的那份拷贝（它在docs目录中），可以通过执行 "ant wiki"来完成。 </p>
<p>对 于AppFuse 1.6.1, 你能够本指南中告诉你的方法生成大部分代码。如果你正在使用的是Struts+Hibernate这样一个组合，你甚至可以完全生成它们。而如果你的 web层框架选择了Spring或者WebWork就不那么幸运了, 对于它们来说要写一个自动化的安装脚本存在许多困难，所以你就不得不自己动手来配置那些Controllers和Actions了。这主要是因为我没有对 这些web层框架使用XDoclet，同时也是由于使用Ant工具作为安装工具的局限性所致。自动生成代码的工具我称之为 AppGen ，我在 Part I 中讲解如何使用它。</p>
<p>Part I: 在AppFuse中创建新的 DAOs 和对象 - 这是一个关于如何创建一个基于数据为中表的Java对象以及如何创建Java类从而持久化对象到数据库中的教程。</p>
<p>1、关于这个指南：</p>
<p>本指南将向你展示如何在数据库中创建一个新的表，以及如何创建访问这个表的Java代码。 </p>
<p>我们将创建一个对象和一些其他的类来将这个对象持久化（保存、装载、删除）到数据库中。用 Java 的语言来说，我们称这个对象是一个POJO对象（Plain Old Java Object ），这个对象基本上与数据库中的某张表是相对应的，其他的类将是： </p>
<p>一个数据访问对象（也称为是一个DAO），一个接口，一个 Hibernate 实现类。 <br>一个 JUnit 类，用来测试我们的 DAO 对象是否可以正确工作。 <br>注 意:如果你是在使用 MySQL 并且如果你想要使用事务 (一般说来你肯定会选择使用的)，那么你必须将 table-type设置为 InnoDB。你可以这样做，将下面的内容加到你的 mysql 配置文件 (/etc/my.cnf or c:\Windows\my.ini) 中。第二个设置（用来设置UTF-8字符集）是mysql 4.1.7+所需要的。 </p>
<p>[mysqld]<br>default-table-type=innodb<br>default-character-set=utf8</p>
<p>如果你使用 PostgreSQL 遇到批处理发生混淆的错误，可以试着在你的src/dao/**/hibernate/applicationContext-hibernate.xml文件中加入
<prop key="hibernate.jdbc.batch_size"></prop>
0 配置来关闭批处理。 <br></p>
<p>AppFuse 使用 Hibernate 作为它默认的持久层。 Hibernate 是一个对象关系映射框架，它使你将你的Java对象与数据库的表建立起一种映射。使你可以很容易地在你的对象上执行CRUD (Create, Retrieve, Update, Delete) 操作。</p>
<p>你也同样可以使用iBATIS 作为持久层的另一个可能的选择。如果要在 AppFuse安装iBATIS，请看一下extras/ibatis目录中的 README.txt 文件。如果你想用 iBATIS 替换 Hibernate，我希望你是有足够的理由并且你应该对它是熟悉的。我也希望你能够针对如何在AppFuse中使用iBATIS 为本指南提出好的建议。 ;-) </p>
<p>下面我将用文字来告诉你在实际的开发过程中我是如何做的。<br>让我们从在AppFuse项目结构中创建一个新的对象，一个DAO和一个测试用例来开始。</p>
<p>内容列表</p>
<p>[1] 创建一个新的对象并且加入 XDoclet 标签<br>[2] 使用Ant，基于我们新建的对象创建一个新的数据库表 <br>[3] 创建一个新的 DAOTest 以便对于DAO 进行JUnit测试 <br>[4] 创建一个新的 DAO 对于我们这个对象执行 CRUD 操作 <br>[5] 为Person对象和PersonDAO配置Spring配置文件<br>[6] 运行 DAOTest 进行测试</p>
<p>[1] 创建一个新的对象并且加入 XDoclet 标签</p>
<p>我们需要做的第一件事就是创建一个对象去持久化它。让我们创建一个简单 "Person" 对象 (创建到 src/dao/**/model 目录中) ，我们让它有一个 id, 一个 firstName 和一个 lastName (作为这个对象的属性)。</p>
<p>package org.appfuse.model;</p>
<p>public class Person extends BaseObject {<br>private Long id;<br>private String firstName;<br>private String lastName;</p>
<p>/*<br>Generate your getters and setters using your favorite IDE: <br>In Eclipse:<br>Right-click -&gt; Source -&gt; Generate Getters and Setters<br>*/<br>} </p>
<p>这 个类应该继承自 BaseObject,由于BaseObject有 3 个抽象方法: (equals(), hashCode() and toString()) ，所以你必须在这个Person类中实现它们。前两个方法是 Hibernate 要求的，最简单的方法是使用工具（如：Commonclipse）来完成它，如果你想知道关于使用这个工具的更多的信息你可以去Lee Grey的网站去找。另外一个你可以使用的工具是Commons4E，它是一个 Eclipse Plugin ，我没有用过，所以我无法告诉你它有什么功能。 </p>
<p><br>如果你使用的是 IntelliJ IDEA ，你可以生成 equals() 和 hashCode()，但是生成不了 toString()，当然有一个 ToStringPlugin，但我从来没有亲自用过。 </p>
<p>现在我们已经有了一个创建好的 POJO ，我们需要在里面加上 XDoclet 标签以便由它生成 Hibernate 映射文件。这个映射文件是让 Hibernate 来映射对象到表，映射属性到表的列的。</p>
<p>首先，我们加入一个 @hibernate.class 标签，这个标签告诉 Hibernate 这个对象将映射哪一张表： </p>
<p>/**<br>* @hibernate.class table="person"<br>*/<br>public class Person extends BaseObject { </p>
<p>我们也必须加一个主键映射，否则当生成映射文件的时候XDoclet将出现错误。注意所有这些@hibernate.* 标签应该放置在你的POJO对象的 getter方法的 Javadocs 位置。 </p>
<p>/**<br>* @return Returns the id.<br>* @hibernate.id column="id"<br>* generator-class="increment" unsaved-value="null"<br>*/</p>
<p>public Long getId() {<br>return this.id;<br>} </p>
<p>我 使用 generator-class="increment" 代替 generate-class="native" ，因为我发现当在其他一些数据库上使用"native"时存在一些问题。如果你只打算使用MySQL，我推荐你使用"native"，而我们的这个指南使 用&#8220;increment&#8221;。</p>
<p>[2] 使用Ant，基于我们新建的对象创建一个新的数据库表 </p>
<p>你可以通过运行"ant setup-db"来创建person表。这个任务创建一方面会创建Person.hbm.xml文件，另一方面可以在数据库中创建一张"person"表。从ant的控制台，你可以看到Hibernate为你创建的表模型：</p>
<p>[schemaexport] create table person (<br>[schemaexport] id bigint not null,<br>[schemaexport] primary key (id)<br>[schemaexport] );<br></p>
<p>如果你想看一下Hibernate为你生成的 Person.hbm.xml 文件的内容，你可以去 build/dao/gen/**/model 目录去看，下面我列出其内容：</p>
<p>&nbsp;</p>
<p>"-//Hibernate/Hibernate Mapping DTD 2.0//EN" <br>"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd"&gt;</p>
<p><hibernate-mapping></hibernate-mapping><br><class></class><br>name="org.appfuse.model.Person"<br>table="person"<br>dynamic-update="false"<br>dynamic-insert="false"<br>&gt;</p>
<p><br>name="id"<br>column="id"<br>type="java.lang.Long"<br>unsaved-value="null"<br>&gt;</p>
<img src ="http://www.blogjava.net/yanrui312/aggbug/118889.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yanrui312/" target="_blank">颜颜</a> 2007-05-21 15:29 <a href="http://www.blogjava.net/yanrui312/articles/118889.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>什么是Java EE 5</title><link>http://www.blogjava.net/yanrui312/articles/118342.html</link><dc:creator>颜颜</dc:creator><author>颜颜</author><pubDate>Fri, 18 May 2007 06:05:00 GMT</pubDate><guid>http://www.blogjava.net/yanrui312/articles/118342.html</guid><wfw:comment>http://www.blogjava.net/yanrui312/comments/118342.html</wfw:comment><comments>http://www.blogjava.net/yanrui312/articles/118342.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yanrui312/comments/commentRss/118342.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yanrui312/services/trackbacks/118342.html</trackback:ping><description><![CDATA[<p>　　最近，SUN的伙伴们(the folks at SUN)将J2EE 1.5改名为java EE 5，当然以前J2EE版本还是称为J2EE，之所以改名，目的还是让大家清楚J2EE只是Java企业应用，用我的话来说，J2EE就是Java在数据库服务器端的应用（见<a href="http://www.jdon.com/idea/j2eebasic.htm" target=_blank><u><font color=#0000ff>初学者如何开发出一个高质量的J2EE系统</font></u></a>）。</p>
<p>　　看来，现在大多数人倾向于更加突出Java了，J2EE容易让初学者误解是独立于一套Java的技术方案。</p>
<p>　　从更深层次来看，Java诞生十年来，很多近十年前的业务组件至今还可以使用，软件应用不再由于语言的更迭革命带来毁灭的打击了。</p>
<p>　　但是，随着WEB和EJB容器概念诞生，使得软件应用业开始担心SUN的伙伴们是否还在Java平台上不断推出翻新的标准框架，致使软件应用业的业务核心组件架构无所适从，从一直以来是否需要EJB的讨论声中说明了这种彷徨。</p>
<p>　　笔者曾经在2004年底中国软件技术大会Ioc微容器(也就是<a href="http://www.jdon.com/jdonframework/" target=_blank><u><font color=#0000ff>Jdon框架</font></u></a>的实现原理)演讲中指出：我们需要一个跨J2SE/WEB/EJB的微容器，保护我们的业务核心组件（中间件），以延续它的生命力，而不是依赖J2SE/J2EE版本，如下图：</p>
<p><img height=239 src="http://www.jdon.com/artichect/images/javaEE.jpg" width=300></p>
<p>　　此次J2EE改名为Java EE，实际也反映出业界这种共同心声。</p>
<p>　　让我们看看Java EE 5有哪些新的功能或规定，我们可以从SUN网站<a href="http://jcp.org/en/jsr/detail?id=244" target=_blank><u><font color=#0000ff>下载Java EE 5规范</font></u></a>。其架构图如下：</p>
<p><img height=479 src="http://www.jdon.com/artichect/images/javaEEArchitecture.jpg" width=546></p>
<p>　　图中灰色加黑部分是Java EE 5新的功能，我们看到，在WEB层主要加入了JSF这个新的表现层框架，和我们日常开发关系密切的是，引入了新的Java Persistence标准，这个标准正在由EJB 3.0专家组制定。</p>
<p>　　值得指出的是，这个Java持久化标准也可以嵌入在WEB层调用，所以，它肯定不会从属于EJB标准，这样，当前所有的Java持久层标准如：JDBC/JDO/Hibernate/Entity Bean将可能统一，减少用户的架构选择 痛苦。</p>
<p>　　从架构图可以看出，无论Web结构或EJB结构，提供实现的功能相差不多，这样，我们的业务核心组件就可以根据需要部署在Web或EJB中运行，而不依赖具体的Java EE容器了。前面一章图的目标在Java EE 5中可以实现了。 </p>
<p>　　当然，Java EE 5重要改变还是：Java EE不再象以前那样只注重大型商业系统的开发，而是更关注小到中型系统的开发，简化这部分系统开发步骤。</p>
<p>　　落实这一简化行动的最大特征是在Java 5.0(Java 1.5)中加入Annotations，通过Annotations引入，降低Java EE开发时，既要写code，又要写XML配置文件之苦，来回照顾，疲于奔命，Annotations既是得益于C++语言，也是从开源项目xDoclet实践中获得经验。</p>
<p>　　但是Annotations是一把双刃剑，初学者用得不好，将会使得原本在XML中的Hard code（硬编码） 写进入代码Annotations，破坏代码的简洁和灵活性，<a href="http://www.jdon.com/jive/thread.jsp?forum=91&amp;thread=22356" target=_blank><u><font color=#0000ff>Annotations讨论按这里</font></u></a>。</p>
<p>　　当然，Annotations的引入不只是解决XML配置，从大的概念说，是解决一个资源注射问题(XML属于其中一个资源)，在原理J2EE中，容器管理的资源都是由JNDI向应用程序提供的，现在通过Annotations可以方便实现注射。</p>
<p>　　资源注射（Resource injection）设计概念其实是来自Ioc模式(<a href="http://www.jdon.com/AOPdesign/Ioc.htm" target=_blank><u><font color=#0000ff>Dependency Injection</font></u></a> )，笔者设计开发的Jdon框架其实已经将Annotations这一宗旨的目的实现，在<a href="http://www.jdon.com/jdonframework/SimpleJdonFrameworkTest.rar" target=_blank><u><font color=#0000ff>Jdon框架应用演示源码</font></u></a><a href="http://www.jdon.com/artichect/SimpleJdonFrameworkTest.rar"><strong><u><font color=#0000ff>SimpleJdonFrameworkTest</font></u></strong></a>中，在jdonframework.xml中有一段如下配置：</p>
<p>&lt;pojoService class="com.jdon.framework.test.dao.JdbcDAO" name="jdbcDAO"&gt;<br>　　&lt;constructor value="java:/TestDS"/&gt;<br>&lt;/pojoService&gt; </p>
<p>　　这段配置是将数据库的JNDI名java:/TestDS注射到JdbcDAO中。Jdon Framework下一个版本将是基于Java 5.0，这行配置 将可通过 Annotations写在jdbcDAO代码中，方便程序员开发。</p>
<p>　　从这里，大家也可以知道EJB 3.0和EJB 2.0的区别了，有的人疑惑，是学习EJB 3.0还是学习EJB 2.0，其实EJB 3.0并没有在原理机制上对EJB有多大改动，只是做了编程方面的简化，另外EJB CMP参考了Hibernate新特点，EJB 3.0最大简化变动是CMP编程方法上。所以，无论学习EJb 3.0/2.0，EJB原理和运行机制都是一样，这部分才是学习EJB最大的困难处，而不是因为到了EJB 3.0，理解EJB就会容易。</p>
<p>　　初学者可从Eclipse+Xdoclet开发EJB 2.0开始学习， EJB 3.0 = EJB 2.0 + xDoclet，<a href="http://www.jdon.com/idea/eclipse-jbossIDE.htm" target=_blank><u><font color=#0000ff>这里有一篇Eclipse开发EJB教程</font></u></a>，使用JBossIDE非常简单，无需Lomboz等插件。</p>
<p>　　Security安全是Java EE的一个重要特点，也就是基于容器的安全访问，无需自己手工编码，具体实现可参考<a href="http://www.jdon.com/jdonframework/news.htm" target=_blank><u><font color=#0000ff>Jdon框架应用演示源码JdonNews</font></u></a>。这虽然是基于J2EE 1.3编写，但是和Java EE 5区别不是很大。</p>
<p>　　事务管理Transaction Management也是Java EE 5的一个重要部分，该标准文档 从几个方面阐述了事务管理的要点，标准中规定了在WEB层中使用事务和线程的处理关系，标准中规定：Web服务器如Tomcat无需在Web层提供事务支持，因为Web组件根本不支持事务繁衍/传递。</p>
<p>　　因为目前一些架构如Struts+Spring+Hibernate/Struts+Hibernate是标准中的Web结构，因此Java EE 5在J2EE 4.2.2规定了 Web组件事务的生命周期，如果Web组件直接调用JTA，事务就不可以跨一个客户端的多个请求，事务只能在一个请求(Servlet/Jsp)中完成，这个标准规定了我们在Web架构中（如上述架构）无法使用长事务(如<a href="http://www.uidesign.net/Articles/Papers/WebMVC-BrowsersTransactio.html" target=_blank><u><font color=#0000ff>工作流/状态图中跨页面请求事务</font></u></a>)，针对一个客户端跨请求的事务目前只有唯一解决方案：只有使用EJB的有态Session。<a href="http://www.jguru.com/faq/view.jsp?EID=121370" target=_blank><u><font color=#0000ff>参考文章：I manage long duration transactions?</font></u></a></p>
<p>　　以上是Java EE 5主要部分，Java EE包含更多其他技术部分如Jdbc JMS JCA JNDI 等等，需要用户在实践中摸索。</p>
<img src ="http://www.blogjava.net/yanrui312/aggbug/118342.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yanrui312/" target="_blank">颜颜</a> 2007-05-18 14:05 <a href="http://www.blogjava.net/yanrui312/articles/118342.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>