分享
 
 
 

使用Java技术在Cocoon中实现商业逻辑

王朝java/jsp·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

来源:http://www.linuxforum.net/forum/printthread.php?Cat=&Board=java&main=387301&type=post

如果你对Apache Cocoon有了解或者以涉足,你可能对在Cocoon中如何使用Java更好

的实现特定的逻辑有所疑惑。这篇文章将给您介绍如何使用XSP(Extensible Server Page)

和Action。同时还有示例和设计原则。

你可能听到了一些来自Apache的关于Cocoon的声音。现在,在经历了三年的发展后,

Cocoon已经逐渐的从一个简单的实现XSL (Extensible Stylesheet Language) 转换

的servlet成长为一个饱满的Web应用框架。

Cocoon是用Java开发的,一般做为servlet运行在象Tomcat这样的servlet容器中。

在这篇文章里,我们将介绍到两种办法来在基于Cocoon的应用中使用Java实现商业逻辑。

首先,让我们来总的了解一下Cocoon。

Cocoon正式的定义是一个XML发布引擎,我们可以理解Cocoon为一个用来产生、转换、处理

和输出数据的框架。也可以理解Cocoon是一个从多种数据源接收数据再应用不同的处理,最

后将数据以想要的格式输出的机器。

我们也可以定义Cocoon是一个数据流机器。也就是说,当你使用Cocoon时,你定义数据的路

径或者流程来产生Web应用的页面。

下面是Cocoon主要的一些基本的原理:

1、Cocoon把所有的数据做为SAX (Simple API for XML) 事件来处理,任何非XML的数据都要

转变成XML描述。

2、生成器(能生成SAX事件)的组件负责处理输入数据

3、序列化器负责处理输出数据,将数据输出到客户端(浏览器、文件等)。

4、开发人员组合生成器、序列化器和其它组件构成管道。所有的管道都在一个叫做站点地图的

文件中定义。

5、通过URI (Uniform Resource Identifier)来匹配管道,但URI是与物理资源脱离的。

第5点需要说明一下:

对于传统的Web server,URI一般映射到物理资源。

例如,这个URI http://localhost/index.html 在Apache server将映射到一个叫index.html的

HTML文件。

在Cocoon中,URIs 和 物理资源可以是没有任何绝对的相互关系的。你可以自由的设计URI来帮

助用户更好的浏览你的站点。最后,你可以更好的组织你的文件让其容易管理和维护。

为了更好的了解Cocoon的处理模型,可以看一个简单的管道。

下面这个例子定义了一个叫index.html的页面。这个管道位于叫sitemap.xmap站点地图中:

<map:match pattern="index.html">

<map:generate type="file" src="content/mainfile.xml"/>

<map:transform type="xslt" src="content/stylesheets/mainstyle.xsl"/>

<map:serialize type="html"/>

</map:match>

这个管道有三步:

首先是一个生成器组件FileGenerator从XML文件"content/mainfile.xml "读取数据。

(FileGenerator实际上已经提前在地图中定义,可以通过"type"属性来引用。Cocoon中所有的

管道组件都是通过它们的type属性来引用的。)

接着进行转换,转换器TraxTransformer将XSL stylesheet应用到引入的数据。

最后,序列化器HTMLSerializer将数据写到客户端的浏览器。

你可能疑惑,上面所说的和Java开发有什么联系呢?

我们把Cocoon的应用分成三个部分:

1、数据的收集层 Data Collection (Generation)

2、数据的处理和转换层 Data Processing&Transforming

3、数据的输出层 Data Output(Serialization)

那么,Java开发在Cocoon的处理转换层是非常重要的。Cocoon的转换和处理层是基于Cocoon的

应用的核心,通过这一层对输入数据的处理,逻辑的应用,你就可以得到所期望的输出。

在Cocoon中,你可以有下面四种实现逻辑的方法:

1、使用转换器(Transformer)组件:他们按照你给定的规则转换传入的数据。典型的例子

便是TraxTransformer。

2、通过不同的 request、session、URI来选择不同的组件做出正确的处理。

3、使用已有的或者自己实现的Action。

4、使用混合了Java代码和内容的XSP。

这篇文章介绍最后两种办法:XSP 和 Action。XSP 和 Action的开发都是在servlet context内。

确切的说,两种组件(实际上是所有的组件)都要存取request, response, session, 和

context对象。在某些方面,你要实现的大量的逻辑都会与这些对象相互作用。

XSP

XSP是Cocoon项目的创新。你可以把它和JSP相比较,因为它们都是混合逻辑和内容而且JSP的

taglib和XSP的logicsheet也很相似。

XSP位于管道的起点,实际上它被Cocoon转换成生成器(Generator)来给管道中其余的组件提

供数据。

让我们看下面这个叫 sample1.xsp 简单的示例:

<?xml version="1.0"?>

<xsp:page language="java" xmlns:xsp="http://apache.org/xsp">

<xsp:logic>

Date now = new Date();

String msg = "Boo!";

</xsp:logic>

<content>

<title>Welcome to Cocoon</title>

<paragraph>

This is an XSP. You can see how we it contain both logic

(inside the <xsp:logic> tags) and content. In the logic block

above, we created a Date object whose value is <xsp:expr>now</xsp:expr>.

Oh, we also had a special message for you: <xsp:expr>msg</xsp:expr>

</paragraph>

</content>

</xsp:page>

首先注意这个文档的根标记是<xsp:page>。

<xsp:page language="java" xmlns:xsp="http://apache.org/xsp">

这个标记定义XSP的language(可以是Java或者JavaScript)和用到的逻辑单的namespace。

接着是我们定义了两个Java变量的<xsp:logic>块。

这些<xsp:logic>块可以有多个,可以出现在你希望的任何地方,而且可以包含各种Java代码。

最后,是我们自己的内容,从用户自己的跟标签开始,在上面的示例中是<content>。在内容部分里,

我们可以用<xsp:expr>这个标签得到在前面定义的变量。

记住,一个XSP实际上就是一个生成器Generator。Cocoon将其转换成Java源文件然后编译、执行它。

(如果想看XSP转换成的Java源文件,到你的servlet容器的工作路径下去找。例如,

如果你使用Tomcat 4.0.4,那么路径就是下面这样:

$CATALINA_HOME/work/Standalone/localhost/cocoon/cocoon-files/org/apache/cocoon/www.)

XSP执行后产生的XML数据被传递给管道其余的组件。

看下面这个管道实例:

<map:pipeline match="*.xsp">

<map:generate type="serverpages" src="examples/{1}.xsp"/>

<map:serialize type="xml"/>

</map:pipeline>

这里,我们使用一个指定的生成器 ServerPagesGenerator,来处理我们简单的XSP。返回给客户

端未加修饰的XML。

注意例子中使用了特别的 {1} 变量引用:它代替值在管道开始处的通配符指示的值。也就是说,

如果我们在浏览器中打开我们的Web应用中的sample1.xsp,那么 {1}的值便是sample1。

记住,同多数Cocoon组件一样,XSP访问request, response, session, 和 context 对象。这些

对象实际上是HttpServletRequest, HttpServletResponse, HttpSession, 和

HttpServletContext的封装,Cocoon正式版本提供了大量的存取这些对象的方法。

XSP在从数据库读取数据的时候特别有用。

数据库数据自然地以行和列组织,所以数据库数据很容易转换到XML。然而,JDBC

(Java Database Connectivity)没有适合地代码完成向XML的转换。

XSP可以让我们在读取数据时很容易,这要感谢ESQL 逻辑单。ESQL 逻辑单除了隐藏了详细

的JDBC代码,还允许将行和列放入到特定的标签中。同时ESQL 逻辑单也可以执行嵌套查询

和执行更新命令。

下面,我们举个XSP应用的例子:

假如我们想将一些Cocoon的资源(名称和URL)存储到数据库。

首先,我们定义存放资源的数据表,然后当用户通过关键字搜索时,我们使用XSP来找到相应

的行,将数据显示给用户。

随后,我们构建一个表单来增加新的列。

表的定义和插入的数据如下面所示。我们这里使用的数据库是MySQL,如果您使用的是其它的

数据库,要注意做相应的改动。这个例子中,必须要有配置好数据库连接池。

表结构如下:

use test;

create table Resources (

ResourceURL varchar(255) not null,

ResourceName varchar(64) not null

);

插入一些资源数据:

insert into Resources values

('http://xml.apache.org/cocoon', 'Cocoon Home Page');

insert into Resources values

('http://www.galatea.com/flashguides/cocoon-tips-2.xml', 'Cocoon 2.0 Tips and Tricks');

表建好后并且Cocoon也正确的配置过后,我们就可以写下面这个XSP例子:

<?xml version="1.0"?>

<xsp:page language="java"

xmlns:xsp="http://apache.org/xsp"

xmlns:esql="http://apache.org/cocoon/SQL/v2">

<xsp:logic>

String keyword = request.getParameter("value");

</xsp:logic>

<content>

<title>Search results</title>

<esql:connection>

<esql:pool>resources</esql:pool>

<esql:execute-query>

<esql:query>

select * from Resources

where ResourceName like '%<xsp:expr>keyword</xsp:expr>%'

</esql:query>

<esql:results>

<resources>

<esql:row-results>

<resource>

<esql:get-columns/>

</resource>

</esql:row-results>

</ resources >

</esql:results>

</esql:execute-query>

</esql:connection>

</content>

</xsp:page>

注意在<xsp:page>标签中声明的名称空间(namespace)。任何时候,在XSP中使用逻辑单的时候,

必须要声明其名称空间(namespace)。你可以在Cocoon webapp路径下的WEB-INF/cocoon.xconf找

到逻辑单的定义。XSP 名称空间的声明就是要说明这是个XSP逻辑单。

实际上,所有的XSP至少要实现XSP逻辑单。在XSP被转换成Java源文件之前,其中的逻辑单

(实际上仅是XSL文件)会先做XSLT处理转换成Java代码。因此,在上面的例子中的所有的

ESQL标签都会转换成了我们所熟悉的JDBC代码。但是并不是所有的标签都可以变成JDBC代码,

注意上面示例中的<esql:pool>块,它涉及到了在WEB-INF/cocoon.xconf文件中定义的数据库

连接池。上面程序中使用的连接池叫做"resources" ,当然你可以使用你所喜欢的定义。

注意,我们这里使用<resources> 这个标签将结果集包了起来而且每行的数据都放到<resource>标

签里。这样我们就可以很容易的编写样式表来将XML转换成其它浏览器可以理解的格式。我们没有

为表的列定义任何标签,通过使用<esql:get-columns/>,Cocoon会将每一列的值放到自动以相应

的列名定义的标签里面。

现在,让我注意一下例子中的SQL查询语句,正如你所看到的,这条SQL是动态生成的。当用户

通过GETs 或者 POSTs提交数据到这个XSP后,在XSP的顶部,我们将request参数的值赋给

了keyword变量,然后根据keyword组成SQL语句。

既然这个例子很简单,让我们把它变复杂一点,加入Email功能,可以在用户提供Email地址后,

将查询结果发送给用户。

XSP示例如下:

<?xml version="1.0"?>

<xsp:page language="java"

xmlns:xsp="http://apache.org/xsp"

xmlns:esql="http://apache.org/cocoon/SQL/v2"

xmlns:sendmail="http://apache.org/cocoon/sendmail/1.0"

xmlns:xsp-request="http://apache.org/xsp/request/2.0"

>

<content>

<xsp:logic>

String keyword = <xsp-request:get-parameter name="value"/>;

String emailAddr = <xsp-request:get-parameter name="email"/>;

String emailBody = "";

</xsp:logic>

<title>Search results</title>

<esql:connection>

<esql:pool>resources</esql:pool>

<esql:execute-query>

<esql:query>

select * from Resources where ResourceName like

'%<xsp:expr>keyword</xsp:expr>%' order by ResourceName

</esql:query>

<esql:results>

<resources>

<esql:row-results>

<resource>

<xsp:logic>

emailBody += <esql:get-string column="ResourceName"/>;

emailBody += ", " + <esql:get-string column="ResourceURL"/> + "\n";

</xsp:logic>

<esql:get-columns/>

</resource>

</esql:row-results>

</resources>

</esql:results>

</esql:execute-query>

</esql:connection>

<xsp:logic>

if (emailAddr != null) {

<sendmail:send-mail>

<sendmail:charset>ISO-8859-1</sendmail:charset>

<sendmail:smtphost>MY_SMTP_HOST</sendmail:smtphost>

<sendmail:from>MY_FROM_ADDRESS</sendmail:from>

<sendmail:to><xsp:expr>emailAddr</xsp:expr></sendmail:to>

<sendmail:subject>Cocoon Search Results</sendmail:subject>

<sendmail:body><xsp:expr>emailBody</xsp:expr></sendmail:body>

</sendmail:send-mail>

}

</xsp:logic>

</content>

</xsp:page>

来自sendmail逻辑单的几个标签让我们拥有了发送EMAIL的能力。在这个例子中,我们将查询结果

的每一行相加赋值给emailBody变量做为邮件的正文。当用户通过request参数提供一个EMAIL地址,

我们就可以发送EMAIL了。当然这需要您提前设定好SMTP服务器和FROM地址。

Cocoon知道根据sendmail逻辑单来处理在sendmail名称空间里的标签,因为这个名称空间已经

在<xsp:page>标签中已经声明。查看示例中的声明,你会看到xsp-request这个名称空间。

xsp-request逻辑单提供了Request常用方法的封装。虽然在XSP中直接访问request对象和

使用xsp-request逻辑单没有什么功能上的区别,但是,理论上使用logicsheet的标签比直

接的JAVA代码更优美。

在运行这个例子之前,你必须要先在cocoon.xconf文件中设置sendmail逻辑单,Cocoon的配置

文件都在Web application 的 WEB-INF目录下。用你熟悉的编辑器打开cocoon.xconf文件,

找到<target-language name="java">标签。在这个块内,你会发现所有其它逻辑单的定义。

在最后一个逻辑单(SOAP逻辑单)后加入下面的内容:

<builtin-logicsheet>

<parameter name="prefix" value="mail"/>

<parameter name="uri" value="http://apache.org/cocoon/sendmail/1.0"/>

<parameter name="href"

value="resource://org/apache/cocoon/components/language/markup/xsp/java/sendmail.xsl"/>

</builtin-logicsheet>

这个定义将http://apache.org.cocoon/sendmail/1.0名称空间和已经包括在Cocoon JAR中

的sendmail.xsl样式表联合起来。

要使用sendmail逻辑单的功能,Cocoon必须要 mail.jar 和 activation.jar这两个JAR。

如果您使用的Server是Tomcat4.x的话,那么它们位于$CATALINA_HOME/common/lib。

Actions

Action功能很强大,你可以将它放到管道的任何地方。Action可以认为是小的自包含的机器,

它获取某些输入数据,做一些处理,然后返回HashMap对象。不同于Cocoon中的Generators,

Transformers, Serializers组件,Action不对实际的XML内容做任何事情,它主要在管道中

实现一些逻辑。

学习Action包括要对管道参数做些了解,有时管道的组件必须交流数据。当然,XML内容会通

过SAX事件传递;但是,我们所说的是管道组件本身的功能需要的值。

管道参数有两种:input 和 output。Input参数是由紧跟在组件声明后面的一个或者多个

<map:parameter>标签来定义的。它们为组件提供一个或者多个值来影响其操作。

Matcher和Action这两个组件可以为它们后面的组件提供能存取的Output变量。

这些output参数放在HashMap对象里,可以通过key名称(如:{1})来引用。

所有的管道都至少有一个由管道开始处的Matcher提供的HashMap。我们在管道中使用这个

HashMap对象,使用{1}可以取得HashMap中Key为1的值。

Cocoon本身包含一些内置的Action。其中有一个是依靠数据库来鉴别用户的Action。当我们

想保护Cocoon中的某些页面,只允许授权的用户访问时,可以将用户的ID和密码存储到数据

库里,然后使用DatabaseAuthenticationAction来做登录确认。

这个DatabaseAuthenticationAction要求我们提供一个XML描述文件来说明要使用哪个表和哪

些列。下面是这个描述文件的示例:

<?xml version="1.0" encoding="UTF-8"?>

<auth-descriptor>

<connection>resources</connection>

<table name="Users">

<select dbcol="USER_NAME" request-param="userid" to-session="userid"/>

<select dbcol="USER_PASSWORD" request-param="userpwd"/>

</table>

</auth-descriptor>

上面这个文件说明用户认证Action将使用resources连接池和User表,request提交的userid参数

和userpwd参数将与User表中的USER_NAME和USER_PASSWORD列来比较,如果成功确认,将参

数userid写到session里。

当你在管道中使用一个Action时,它必须先在站点地图中的<map:components>块中的

<map:actions>块中定义。如下:

<map:components>

<!-- all other component definitions go here -->

<map:actions>

<map:action name="authenticator"

src="org.apache.cocoon.acting.DatabaseAuthenticatorAction"/>

<!-- other actions definitions go here -->

</map:actions>

</map:components>

一旦定义过后,就可以使用这个Action来负责我们要保护的区域。下面为要保护的区域定

义了三个管道:

<map:match pattern="protected/login.html">

<map:read mime-type="text/html" src="secret/login.html"/>

</map:match>

<map:match pattern="protected/login">

<map:act type="authenticator">

<map:parameter name="descriptor" value=" secret/auth-info.xml"/>

<map:redirect-to uri="index.html"/>

</map:act>

<map:redirect-to uri="login.html"/>

</map:match>

<map:match pattern="protected/*">

<map:match type="sessionstate" pattern="*">

<map:parameter name="attribute-name" value="userid"/>

<map:match pattern="protected/*.html">

<map:read mime-type="text/html" src=" secret/*.html"/>

</map:match>

<map:match pattern="protected/*.xsp">

<map:generate type="serverpages" src=" secret/{1}.xsp"/>

<map:serialize type="xml"/>

</map:match>

</map:match>

<map:redirect-to uri="login.html"/>

</map:match>

第一个管道简单的提供了一个登录的表单,是个HTML文件,不需要转换。第二个管道处理

从login.html提交的实际的登录动作。第三个来处理我们要保护的内容。

下面我们做详细的论述:

DatabaseAuthenticationAction依靠描述文件来验证登录。但是,我们如何知道验证是否

成功呢?对于Action,如果它们返回了有效的HashMap,那么在<map:act>块里的部分将执

行。如果返回null值,那么块下面的部分将执行。也就是说,按照上面管道的定义,我们

有两种可能的结果,即:如果认证通过,我们就可以到达受保护的区域,如果失败将返回

到Login页面。

在 protected/* 管道有几个嵌套的Matcher,第二个的type是sessionstate,这个Matcher

实际上是WildcardSessionAttributeMatcher,在这里用来读取session里的userid的值。

在这个例子中,我们知道DatabaseAuthenticationAction设置了一个叫userid的session属

性,我们通过检测userid属性来判断用户是否登录成功,如果它不存在,则转向到login页面。

在Cocoon已经有一个DatabaseAddAction可用来插入数据,但为了更好的理解Action,我们

将写一个自己的Action用来将新的Resource记录插入到Resources表中。

我们假想你已经编写了一个HTML页面,可用来POST两个变量name和url到管道。我们的

Action将从Request对象中找回name和url参数,将其插入到表中,最后返回一个HashMap对象。

下面是程序代码:

package test;

import org.apache.avalon.excalibur.datasource.DataSourceComponent;

import org.apache.avalon.framework.component.ComponentException;

import org.apache.avalon.framework.component.ComponentManager;

import org.apache.avalon.framework.component.ComponentSelector;

import org.apache.avalon.framework.parameters.Parameters;

import org.apache.avalon.framework.thread.ThreadSafe;

import org.apache.avalon.framework.component.Composable;

import org.apache.avalon.framework.activity.Disposable;

import org.apache.cocoon.environment.ObjectModelHelper;

import org.apache.cocoon.environment.Redirector;

import org.apache.cocoon.environment.Request;

import org.apache.cocoon.environment.Session;

import org.apache.cocoon.environment.SourceResolver;

import org.apache.cocoon.acting.AbstractAction;

import java.sql.Connection;

import java.sql.Statement;

import java.sql.SQLException;

import java.util.HashMap;

import java.util.Map;

public class AddResourceAction extends AbstractAction

implements ThreadSafe, Composable, Disposable

{

protected ComponentSelector dbselector;

protected ComponentManager manager;

public void compose(ComponentManager manager) throws ComponentException {

this.dbselector =

(ComponentSelector) manager.lookup(DataSourceComponent.ROLE + "Selector");

}

protected final DataSourceComponent getDataSource(String pool)

throws ComponentException {

return (DataSourceComponent) this.dbselector.select(pool);

}

public Map act( Redirector redirector, SourceResolver resolver,

Map objectModel, String

source, Parameters param )

throws Exception

{

Request req = ObjectModelHelper.getRequest(objectModel);

Session ses = req.getSession(true);

String poolName = param.getParameter("pool");

String resourceName = req.getParameter("name");

String resourceUrl = req.getParameter("url");

if (poolName == null) {

getLogger().error("Missing a pool name");

return null;

}

if (resourceName == null || resourceUrl == null) {

getLogger().error("Missing input parameters");

return null;

}

Map map = new HashMap();

DataSourceComponent dataSource = getDataSource(poolName);

Connection conn = null;

boolean status = false;

try {

conn = dataSource.getConnection();

Statement stmt = conn.createStatement();

String cmd = "insert into Resources values ('" +

resourceName + "', '" +

resourceUrl + "')";

stmt.executeUpdate(cmd);

map.put("resource-name", resourceName);

map.put("resource-url", resourceUrl);

getLogger().debug("Resources insert completed by user " +

ses.getId());

status = true;

stmt.close();

} catch (Exception e) {

getLogger().error("Stmt failed: ", e);

} finally {

try {

if (conn != null) conn.close();

} catch (SQLException sqe) {

getLogger().warn("Error closing the datasource", sqe);

}

}

if (!status) return null;

return(map);

}

public void dispose() {

this.manager.release(dbselector);

}

}

这儿有大量的东西需要消化,特别是如果你不熟悉Cocoon的结构。我们一步步的来说明。

首先,Cocoon action的主方法是 act(),当在管道中使用Action时Cocoon将调用这个方法。

在这个示例中,act()得到Request参数、从连接池中得到数据库连接,执行插入,然后填充

HashMap对象,并将其返回。

在Act方法的开始是从ObjectModelHelper组件中取得Request对象,然后得到两个参数。这个

Action需要另外一个参数,pool;它将告诉我们使用哪个连接池。如果这个参数没有,那么

Action将返回null而且将错误写到日志里。有了pool的名称,我们就可以从连接池得到数据

库的连接。Avalon的Excalibur组件用来负责Cocoon的连接池。如果你不熟悉Avalon,可以

访问这里http://jakarta.apache.org/avalon/ 。

代码中的insert statement是直接的JDBC语法。在插入成功后,会将成功的信息写到日志里。

对于日志,如果按上面Action的写法,所有的日志信息都写到你的Web Application的

WEB-INF/logs/sitemap.log文件。

最后,我们将两个输入参数写到了Map对象,虽然它们都在Request对象中,这样做是多余的,

但我们这样做是为了示例Map对象的用法。

看一下这个Action在站点地图中的定义。我们必须首先在站点地图的<map:components>区定

义这个Action。

<map:components>

<!-- all other component definitions go here -->

<map:actions>

<map:action name="authenticator"

src="org.apache.cocoon.acting.DatabaseAuthenticatorAction"/>

<map:action name="add-resource" src="test.AddResourceAction"

logger="sitemap.action.AddResourceAction"/>

<!-- other actions definitions go here -->

</map:actions>

</map:components>

在管道中使用这个Action:

<map:match pattern="addresource">

<map:act type="add-resource">

<map:parameter name="pool" value="resources"/>

<map:read mime-type="text/html" src="examples/confirmation.html"/>

</map:act>

<map:read mime-type="text/html" src="examples/addresource.html"/>

</map:match>

可以看到,在<map:act> 行的下面,紧跟着的<map:parameter>标签为Action提供"pool"参数。

一切顺利的话,Action将返回一个Map对象,confirmation页面将被显示。

在浏览器中打开http://localhost:8080/cocoon/addresource,你会看到一个输入表单。

表单提交后,如果插入成功,将显示confirmation页面,如果出现错误,将再次返回到表单

页面。查看WEB-INF/logs/sitemap.log,错误信息会告诉你出现了什么错误。

如何有效的使用XSP和Action?

XSP和Action是在Cocoon中实现逻辑的两种不同的办法。选择哪一种更适合呢?

XSP在取数据或者创建结构化的数据方面是很有用的。Action被证明在控制数据流程

(并不产生或者影响数据)的逻辑实现上很有用。我们上面看到的用户验证和数据库

操作便是这样的两个例子。

然而,有一点需要说明的问题:XSP会将逻辑和内容混合。而Cocoon的一个基本的原则

就是逻辑、内容、表示的分离。

在使用XSP的时候,我们提出以下几点建议:

首先,尽可能的使用逻辑单,逻辑单会很好的将Java代码隐藏。

第二,尽量使用Cocoon的提供的功能,如:在做数据库的Select的时候,我们也可以用

SqlTransformer来实现。

第三,在决策方面的逻辑尽可能的使用Selector, Matcher或Action组件。

最后,当无法避免在你的XSP中插入Java逻辑的话,尽可能的让<xsp:logic>小,而且不要

把它们散布到各种你的标签中。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有