首 页 网络编程
网页制作 图形图象 操作系统 冲浪宝典
软件教学 认证考试

网络安全 网络办公 行业资讯 评测对比
您当前位置:站长天空 -> 操作系统-> Solaris教程
使用Java技术在Cocoon中实现商业逻辑-JSP教程,Java技巧及代码
作者:网友供稿 点击:164
推荐
西部数码-全国虚拟主机10强!20余项虚拟主机管理功能,全国领先!第6代双线路虚拟主机,南北访问畅通无阻!可在线rar解压,自动数据恢复设置虚拟目录等.免费赠送访问统计,企业邮局.Cn域名注册10元/年,自助建站480元起,免费试用7天,满意再付款!P4主机租用799元/月.月付免压金
站内搜索
文章页数:[1] 
如果你对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>小,而且不要
把它们散布到各种你的标签中。
文章整理:站长天空 网址:http://www.z6688.com/
以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!

文章页数:[1] 


放大字体显示 缩小字体显示 打印文章 推荐给朋友
热门文章
·使用JSP对文件下载的控制-JSP教程,Jsp/Servlet
·[ASP.net(C#)]XML操作类(一)-.NET教程,C#语言
·java网络五子棋的源代码-JSP教程,Java技巧及代码
·用C#给程序加启动画面并只允许一个应用程序实例运行-.NET教程,C#语言
·.Net平台下开发中文语音应用程序-.NET教程,VB.Net语言
·用java取得本机的ip和机器名-JSP教程,Java技巧及代码
·动力设备环境及图像集中监控管理系统若干新问题——王观坤、刘崇海
·Java下XML编程接口比较:DOM SAX JDOM JAXP-.NET教程,XML应用
·使用Java技术在Cocoon中实现商业逻辑-JSP教程,Java技巧及代码
·[SQL]启动SQLServer的死锁检查-数据库专栏,SQL Server
最新文章
·日一万ip网站 gg月入万刀_网赚技巧
·浅谈cpm、cpc、cpa、pfp网站赚钱说明_网赚技巧
·王通:网站赚钱靠服务、产品和投资三种方式_网赚技巧
·网站的首页最应该放什么?_站长心得
·简洁而不简单:google成功十条真理_google推广
·google排名 如何用关键字优化网站_google推广
·用google adsense渠道跟踪你的广告(新手读)_google推广
·全面了解google 网页目录_google推广
·做完网站别忘调试----网站调试全攻略2_站长心得
·网上开店快乐赚钱-发现潜在商机_站长心得
相关主题
  • 使用javascript制作声音按钮_javascript教程
  • 使用javascript检测浏览器的相关特性_javascript教程
  • 使用javascript修改客户端注册表_javascript教程
  • 使用javascript创建快捷方式_javascript教程
  • 使用JavaBean创建您的网上日历本-JSP教程,Java技巧及代码
  • 西部数码虚拟主机

    友情链接
    CNNIC 西部数码
    万网 自助建站
    虚拟主机 asp空间
    域名注册 域名
    域名申请 主页空间
    论坛空间 网站空间
    国际域名 虚拟空间
    空间租用 DDOS防火墙
    成都主机托管 四川主机托管
    主机租用 服务器租用
    网站目录 自助建站
    虚拟主机 网址大全
    软件下载
    自助链接
    虚拟主机资讯 特价虚拟主机
    版权申明:本站文章均来自网络,如有侵权,请联系我们,我们收到后立即删除,谢谢!
    关于我们:站长天空:专业提供最新的站长资讯、在线教程、虚拟主机权威评测、虚拟主机性能对比、网站制作教程,开发教程,站长工具。包括网页制作教程、冲浪宝典、编程参考、操作系统、软件教学、行业动态等。
    特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有。
    发表评论 打印  刷新     关闭