随笔:45 文章:5 评论:25 引用:0
BlogJava 首页 发新随笔
发新文章 联系 聚合管理

2009年1月6日

由于是别的人文章,在此就记下个LINK吧 http://blog.csdn.net/ferry_passion/article/details/7607489
posted @ 2012-07-25 21:42 Hill 阅读(244) | 评论 (0)编辑 收藏
 
快过年了,但在2011年还有不到2个月的时候,发篇随笔吧~
今年的计划到目前看,都基本实现了。但是,我知道,挑战和未来的规划也已经开始。
以后一段日子本人将专注分布式应用的学习和研究,希望能与大家分享一下心得体会吧。本论坛的高手很多,也希望能得到你们的指导。
分布式计算、分布式存储、虚拟化技术---->新的系统分析、设计方式;OSGI/Hadoop/Hbase....

今年过年也有很重要的事要做~期待中~
posted @ 2011-11-19 11:32 Hill 阅读(343) | 评论 (1)编辑 收藏
 
     摘要: ORA-01461: can bind a LONG value only for insert into a LONG column(点睛)  阅读全文
posted @ 2009-10-13 15:11 Hill 阅读(2250) | 评论 (0)编辑 收藏
 
     摘要: set_context与set_identifier  阅读全文
posted @ 2009-09-04 17:32 Hill 阅读(339) | 评论 (0)编辑 收藏
 
     摘要: Ibatis中#和$两个符号的使用区别  阅读全文
posted @ 2009-09-01 17:45 Hill 阅读(1873) | 评论 (0)编辑 收藏
 
     摘要: java这样也能写,我还真没用过。学习学习  阅读全文
posted @ 2009-07-15 16:16 Hill 阅读(311) | 评论 (0)编辑 收藏
 
     摘要: 在很多大型应用中都会对数据进行切分,并且采用多个数据库实例进行管理,这样可以有效提高系统的水平伸缩性。而这样的方案就会不同于常见的单一数据实例的方案,这就要程序在运行时根据当时的请求及系统状态来动态的决定将数据存储在哪个数据库实例中,以及从哪个数据库提取数据。  阅读全文
posted @ 2009-07-11 19:24 Hill 阅读(303) | 评论 (0)编辑 收藏
 
     摘要: 很多人在进行数据迁移时,希望把数据导入不同于原系统的表空间,在导入之后却往往发现,数据被导入了原表空间。
本例举例说明解决这个问题:  阅读全文
posted @ 2009-07-11 11:44 Hill 阅读(249) | 评论 (0)编辑 收藏
 
     摘要: 很多人在进行数据迁移时,希望把数据导入不同于原系统的表空间,在导入之后却往往发现,数据被导入了原表空间。
本例举例说明解决这个问题:  阅读全文
posted @ 2009-07-11 11:44 Hill 阅读(223) | 评论 (0)编辑 收藏
 
     摘要: 根据项目需要,最近在做一个关于owc透视表的功能(PivotTable),这个东西啊让我可是郁闷了将近10天,网上很多资源都是直接连接数据源的方式,但对于实现系统开发来说,不实用,因为b/s系统多数是分层架构,并且部署实施时,很难由客户端直接向数据库服务器发出请求。原因就不多说了。
  阅读全文
posted @ 2009-07-02 12:02 Hill 阅读(864) | 评论 (2)编辑 收藏
 
     摘要: Struts2上传文件  阅读全文
posted @ 2009-06-15 17:41 Hill 阅读(255) | 评论 (0)编辑 收藏
 
     摘要: 注意:在jdk1.4下运行时,不要多加jar包。struts2-core,xwork这两个转换成jdk1.4下的版本后,别的不会再转了。如果使用别的包时,转一个加一个试试,struts2在加入jar包时,可能有问题,使用时小心。  阅读全文
posted @ 2009-06-12 15:40 Hill 阅读(1257) | 评论 (0)编辑 收藏
 
     摘要: 在jsp中操作FCKeditor  阅读全文
posted @ 2009-06-09 14:34 Hill 阅读(445) | 评论 (0)编辑 收藏
 
     摘要: row_number() /rank()/dense_rank()学习之二  阅读全文
posted @ 2009-06-08 14:27 Hill 阅读(136) | 评论 (0)编辑 收藏
 
     摘要: ORACLE分析函数学习  阅读全文
posted @ 2009-06-08 14:04 Hill 阅读(202) | 评论 (0)编辑 收藏
 
     摘要: Selecting the first n rows with Oracle  阅读全文
posted @ 2009-06-08 11:56 Hill 阅读(178) | 评论 (0)编辑 收藏
 
     摘要: document.selection.createRange方法  阅读全文
posted @ 2009-05-31 13:51 Hill 阅读(11236) | 评论 (3)编辑 收藏
 
     摘要: 处理中文问题  阅读全文
posted @ 2009-05-27 17:16 Hill 阅读(1964) | 评论 (0)编辑 收藏
 
     摘要: 向原作者学习一下,从感性上  阅读全文
posted @ 2009-05-27 14:59 Hill 阅读(105) | 评论 (0)编辑 收藏
 
     摘要: 在以前的一篇文中介绍到,当用parameterMap作为ibatis映射输入参数时,要在ibatis的配置文件中
作相应的声明。但我们也可以不在ibatis映射文件中作声明,应用方法如下:   阅读全文
posted @ 2009-05-27 14:06 Hill 阅读(3436) | 评论 (0)编辑 收藏
 
     摘要: 关于自动完成的别人的例子,学习一下。  阅读全文
posted @ 2009-05-08 15:27 Hill 阅读(177) | 评论 (0)编辑 收藏
 
     摘要: 今天遇到要用replaceAll(),看到很多人对其也有困惑.我在此记录下来  阅读全文
posted @ 2009-04-29 10:59 Hill 阅读(116727) | 评论 (5)编辑 收藏
 

今天打算启用一下Oracle Database 11g的Database Control,发现初始并未安装:

[oracle@test126 ~]$ emctl start
EM Configuration issue. /opt/oracle/product/11.1.0/test126.hurray.com.cn_dodd not found.

 

遇到这类问题,可以通过重新配置,来创建EM的配置文件:

[oracle@test126 11.1.0]$ emca -config dbcontrol db -repos recreate

 

STARTED EMCA at Aug 28, 2007 11:54:40 AM
EM Configuration Assistant, Version 11.1.0.5.0 Production
Copyright (c) 2003, 2005, Oracle. All rights reserved.

Enter the following information:
Database SID: dodd
Listener port number: 1521
Password for SYS user:
Password for DBSNMP user:
Password for SYSMAN user:
Password for SYSMAN user: Email address for notifications (optional): eygle@eygle.com
Outgoing Mail (SMTP) server for notifications (optional):
-----------------------------------------------------------------

You have specified the following settings

Database ORACLE_HOME ................ /opt/oracle/product/11.1.0

Local hostname ................ test126.hurray.com.cn
Listener port number ................ 1521
Database SID ................ dodd
Email address for notifications ............... eygle@eygle.com
Outgoing Mail (SMTP) server for notifications ...............

-----------------------------------------------------------------
Do you wish to continue? [yes(Y)/no(N)]: Y
Aug 28, 2007 11:56:58 AM oracle.sysman.emcp.EMConfig perform
INFO: This operation is being logged at /opt/oracle/cfgtoollogs/emca/dodd/emca_2007_08_28_11_54_40.log.
Aug 28, 2007 11:57:01 AM oracle.sysman.emcp.EMReposConfig invoke
INFO: Dropping the EM repository (this may take a while) ...
Aug 28, 2007 11:57:16 AM oracle.sysman.emcp.EMReposConfig invoke
INFO: Repository successfully dropped
Aug 28, 2007 11:57:16 AM oracle.sysman.emcp.EMReposConfig createRepository
INFO: Creating the EM repository (this may take a while) ...


Aug 28, 2007 12:08:35 PM oracle.sysman.emcp.EMReposConfig invoke
INFO: Repository successfully created
Aug 28, 2007 12:08:47 PM oracle.sysman.emcp.EMReposConfig uploadConfigDataToRepository
INFO: Uploading configuration data to EM repository (this may take a while) ...
Aug 28, 2007 12:11:45 PM oracle.sysman.emcp.EMReposConfig invoke
INFO: Uploaded configuration data successfully
Aug 28, 2007 12:11:51 PM oracle.sysman.emcp.util.DBControlUtil configureSoftwareLib
INFO: Software library configured successfully.
Aug 28, 2007 12:11:51 PM oracle.sysman.emcp.EMDBPostConfig configureSoftwareLibrary
INFO: Deploying Provisioning archives ...
Aug 28, 2007 12:12:13 PM oracle.sysman.emcp.EMDBPostConfig configureSoftwareLibrary
INFO: Provisioning archives deployed successfully.
Aug 28, 2007 12:12:13 PM oracle.sysman.emcp.util.DBControlUtil secureDBConsole
INFO: Securing Database Control (this may take a while) ...
Aug 28, 2007 12:12:39 PM oracle.sysman.emcp.util.DBControlUtil secureDBConsole
INFO: Database Control secured successfully.
Aug 28, 2007 12:12:39 PM oracle.sysman.emcp.util.DBControlUtil startOMS
INFO: Starting Database Control (this may take a while) ...
Aug 28, 2007 12:14:04 PM oracle.sysman.emcp.EMDBPostConfig performConfiguration
INFO: Database Control started successfully
Aug 28, 2007 12:14:04 PM oracle.sysman.emcp.EMDBPostConfig performConfiguration
INFO: >>>>>>>>>>> The Database Control URL is https://test126.hurray.com.cn:1158/em <<<<<<<<<<<
Aug 28, 2007 12:14:16 PM oracle.sysman.emcp.EMDBPostConfig invoke
WARNING:
************************ WARNING ************************

Management Repository has been placed in secure mode wherein Enterprise Manager data will be encrypted. The encryption key has been placed in the file: /opt/oracle/product/11.1.0/test126.hurray.com.cn_dodd/sysman/config/emkey.ora. Please ensure this file is backed up as the encrypted data will become unusable if this file is lost.

***********************************************************
Enterprise Manager configuration completed successfully
FINISHED EMCA at Aug 28, 2007 12:14:16 PM

 

Oracle 10g中,配置方法与此相同。
配置完成之后就可以启动EM了:

[oracle@test126 11.1.0]$ emctl start dbconsole
Oracle Enterprise Manager 11g Database Control Release 11.1.0.6.0
Copyright (c) 1996, 2007 Oracle Corporation. All rights reserved.
https://test126.hurray.com.cn:1158/em/console/aboutApplication
Starting Oracle Enterprise Manager 11g Database Control ........ started.
------------------------------------------------------------------
Logs are generated in directory /opt/oracle/product/11.1.0/test126.hurray.com.cn_dodd/sysman/log

 

如果是Linux环境,你可能还需要在iptables中开放1158端口,增加如下一行:
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 1158 -j ACCEPT

重新启动防火墙之后就可以通过IE在远端连接EM的Database Control了:

[root@test126 sysconfig]# service iptables restart
Flushing firewall rules: [ OK ]
Setting chains to policy ACCEPT: filter [ OK ]
Unloading iptables modules: [ OK ]
Applying iptables firewall rules: [ OK ]
Loading additional iptables modules: ip_conntrack_netbios_ns [ OK ]

 


-The End-

posted @ 2009-04-23 15:13 Hill 阅读(864) | 评论 (0)编辑 收藏
 
     摘要: 简单来说序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化,流的概念这里不用多说(就是I/O),我们可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间(注:要想将对象传输于网络必须进行流化)!在对对象流进行读写操作时会引发一些问题,而序列化机制正是用来解决这些问题的!
  阅读全文
posted @ 2009-03-12 15:11 Hill 阅读(555) | 评论 (1)编辑 收藏
 
     摘要: 是Logic 标签库中最复杂的标签,也是用途最广的一个标签,它能够在一个循环中遍历数组、Collection、Enumeration、Iterator 或 Map 中的所有元素。   阅读全文
posted @ 2009-03-09 10:34 Hill 阅读(1515) | 评论 (0)编辑 收藏
 
     摘要: Tiles框架为创建Web页面提供了一种模板机制,它能将网页的布局和内容分离。它允许先创建模板,然后在运行时动态地将内容插入到模板中。Tiles 框架建立在JSP的include指令的基础上,但它提供了比JSP的 include指令更强大的功能  阅读全文
posted @ 2009-03-06 11:20 Hill 阅读(321) | 评论 (0)编辑 收藏
 
     摘要: 大家都清楚在用PowerDesigner的时候,当你输入Name的时候Code是会自动帮你按照Name的内容填上的.

这个功能虽然好用,但是我需要在Name这一项加上一个中文的注释,这个时候怎么办呢?

下面两个例子,相信对你相当有用.  阅读全文
posted @ 2009-03-02 17:45 Hill 阅读(1495) | 评论 (0)编辑 收藏
 
     摘要: 近来读了一篇《怎样成为优秀的软件模型设计者》的文章,感触颇深。仔细对比分析,发现原来我自己和周围的软件开发人员平常的一些自认为对的做法,有很多是有问题的。  阅读全文
posted @ 2009-02-25 18:20 Hill 阅读(180) | 评论 (0)编辑 收藏
 
     摘要: 第一次感觉到“分享”的重要性,过去,只会想办法去获得,却没有意识到“分享”是更加值得去“获得”的能力与心态  阅读全文
posted @ 2009-02-25 18:06 Hill 阅读(128) | 评论 (0)编辑 收藏
 
<a href="javascript:self.close()">close</a>
<!--IE7 -->
<a href="javascript:window.open('','_self','');window.close();">direct close</a>
<!--IE6-->
<input type="button" value="click" onclick="javascript:window.opener=null;window.close();return false;">
posted @ 2009-02-24 15:11 Hill 阅读(118) | 评论 (0)编辑 收藏
 
     摘要: 学习JavaMail可以借鉴一下。用到时,可以参考  阅读全文
posted @ 2009-02-23 17:50 Hill 阅读(177) | 评论 (0)编辑 收藏
 
     摘要: 介绍了oracle9i的xmltype数据类型的基本使用
包括:建立含有xmltype数据类型的表
插入(insert)数据
查询(select)数据
更新(update)数据
添加超过4k字节的xml文档到xmltype型字段
适合初学者。
  阅读全文
posted @ 2009-02-13 11:27 Hill 阅读(2084) | 评论 (0)编辑 收藏
 
     摘要: RAC就是real application clusters的缩写,Oracle Real Application Clusters (RAC)可以支持24 x 7 有效的数据库应用 系统,您可以在由低成本的服务器构成的高可用性系统上自由部署您的应用,而无需修改您的应用程序。已经有超过4200个用户从中受益。现在Oracle在 10g RAC中更提供免费的集群软件和存储管理软件,为您降低应用成本。拥有RAC您无需再为成本而牺牲性能和 可靠性。
  阅读全文
posted @ 2009-02-04 10:39 Hill 阅读(820) | 评论 (0)编辑 收藏
 
     摘要: 新人新问题,感觉问问自己这些问题,也算是指明一个学习方向吧,感觉也不容易啊,努力学习中  阅读全文
posted @ 2009-02-03 11:13 Hill 阅读(406) | 评论 (8)编辑 收藏
 
     摘要: 本人是新人,要从头学习起,希望老手们能介绍经验啊,谢谢  阅读全文
posted @ 2009-02-03 11:11 Hill 阅读(225) | 评论 (0)编辑 收藏
 
     摘要: 删 除 数 据
删除表中数据的方法有两种,在指定的表或视图中删除满足给定条件的数据可以使用DELETE语句;如果要清除表中全部数据,则还可以使用TRUNCATE TABLE语句  阅读全文
posted @ 2009-02-03 11:04 Hill 阅读(1561) | 评论 (4)编辑 收藏
 
1Z0-042
安装和配置 Oracle Database 10g
监控和维护 Oracle Database 10g,并排除 Oracle Database 10g 中的故障
为 Oracle Database 10g 配置 Oracle Net Services
Oracle Database 10g 的备份和恢复
在 Oracle Database 10g 中创建和管理用户账户
1Z0-043
使用 RMAN 创建和管理备份集和映像副本
将数据库恢复到过去某一时刻的状态
使用 Oracle 闪回技术恢复数据库
检测块损坏情况并采取相应的措施修复损坏的块
使用各种数据库 Advisor 来监控和提高数据库的性能
使用资源管理器来控制数据库资源的使用情况
使用调度程序来简化管理任务
提高监听程序的安全性
复查数据库日志文件以便进行诊断
为数据库和单独的会话自定义基于语言的行为

专题的研究如:RMAN、RAS、STATSPACT、 DATAGUARD、TUNING、BACKUP&RECOVER等等
posted @ 2009-02-03 10:51 Hill 阅读(172) | 评论 (0)编辑 收藏
 
     摘要: 我是刚参加工作不久的新人,想通过参加一些相关的认证提高自己的工作能力,希望大家给点意见,谢谢


为了进一步提高Oracle认证的可选性,Oracle公司现在可以提供4种不同的认证,这些都是专为从事Oracle领域工作的人员包括DBA、开发人员以及管理人员所设计的  阅读全文
posted @ 2009-02-03 10:14 Hill 阅读(146) | 评论 (0)编辑 收藏
 
     摘要: JdbcTemplate是core包的核心类。它替我们完成了资源的创建以及释放工作,从而简化了我们对JDBC的使用。它还可以帮助我们避免一些常见的错误,比如忘记关闭数据库连接。JdbcTemplate将完成JDBC核心处理流程,比如SQL语句的创建、执行,而把SQL语句的生成以及查询结果的提取工作留给我们的应用代码。它可以完成SQL查询、更新以及调用存储过程,可以对ResultSet进行遍历并加以提取。它还可以捕获JDBC异常并将其转换成org.springframework.dao包中定义的,通用的,信息更丰富的异常。

  阅读全文
posted @ 2009-01-13 18:07 Hill 阅读(776) | 评论 (0)编辑 收藏
 
     摘要: 年关到了,商家忙着促销,网站忙着推广,阿里软件的服务集成平台也面临第一次多方大规模的压力考验。根据该平台5.3版本的压力测试结果,我们估算了一下现有的推广会带来的压力,基本上确定了服务集成平台年底不需要扩容。SA(System Administrator,系统管理员)为了保险起见还是通过请求方式来做定时的心跳检测,保证服务集成平台的可靠性。结果阿里旺旺推广开始的第一天,SA的报警短信就在几个忙时段不停地发告警,但是查看生产环境的服务器状况以及应用状况后看不出有什么问题,于是开始怀疑是否告警机制不是很合理。几日的访问记录统计报告看过以后,发现了几个问题,首先由于推广是在IM登录时段集中式的推广,因此高峰期比较集中,压力也很大,而告警发生的时刻也是那些时候;另外发现那些推广使用的API的处理时间比较长,同时还有些出现了问题,这几天除了服务集成平台告警以外,那些API服务器也在告警;因此可以看出问题应该是由于API提供商响应速度慢而拖累了服务集成平台的处理能力,监控机制在高峰情况下没有得到及时的响应,就认为是服务器已经处于无效状态。  阅读全文
posted @ 2009-01-12 18:38 Hill 阅读(197) | 评论 (0)编辑 收藏
 
     摘要: 原因:首先,JFreeChart和中文验证码的乱码问题和jsp的编码无关,是由于Java虚拟机找不到字体文件造成的,所以同类的Swing或者AWT的中文乱码问题也适用

  阅读全文
posted @ 2009-01-12 17:18 Hill 阅读(4045) | 评论 (0)编辑 收藏
 
     摘要: JFreeChart真是个好东西,今天刚刚学习了一下,以后要是用时,可以深入,大家要是不知道怎么在Web中画出图表,请参考,谢谢  阅读全文
posted @ 2009-01-12 16:41 Hill 阅读(134) | 评论 (0)编辑 收藏
 

Struts Validator验证器使用指南


验证器:

从0.5版,验证器在一些form中就已经实现了,他最初包含在开发人员包中,后来核心代码挪到Jakarta Commons包中和Struts特别扩展中作为 Struts 1.1的一部分。许多开发者为方便一直使用struts验证器,这篇文档首先概述验证器的核心功能性,然后大概介绍在 struts1.1中的变化和新增功能。

如果你配置好验证器插件,你应该扩展ValidatorForm而不是ActionForm,以便它能加载你的Validator资源。他根据struts-config.xml文件中的action的name属性为当前form的调用相应的验证器,因此在validator-rules.xml中的form元素的名称属性应该与action的name属性值相匹配。

另外一种选择是扩展ValidatorActionForm 而不是ValidatorForm,ValidatorActionForm使用struts-config.xml中action的path属性,所以path属性的值相应的应该与validator-rules.xml中的Form的name属性匹配。

一个分离的action可以定义给多页form的每个页面,而且验证规则可以与action关联而不是与页码,就像验证范例中的多页form范例那样。
国际化

在validator-rules.xml 文件中form的验证规则可以组织为FormSet。FormSet 有与java.util.Locale 类相应的属性:如语言, 国家以及变量型属性,如果他们未定义,FormSet 将把它设置为默认值。一个FormSet 也可以有关联的常量。另外还可以定义与FormSet 同一级别的全局global元素,他与FormSet同样也有常量。

注意:你必须在国际化的FormSet前声明一个没有国际化的默认FormSet。这样如果Validator没有找到locale时可以有一个默认版本。

        可插入验证器的默认错误信息值可以被msg元素覆盖。所以为mask验证器生成错误信息的替代方法就是使用msg属性,如果字段的name属性与验证器的name属性匹配,那末将使用字段的msg属性。

        error messages的可以设置arg0-arg3 等参数元素。如果没有设置arg0-arg3的name属性, error messages将使用他们作为默认的构建参数值。如果设置了name属性,你就可以把参数指定给一特定的可插入验证器,然后这些参数将在构造错误信息时被使用。

<field

property="lastName"

depends="required,mask">

<msg

name="mask"

key="registrationForm.lastname.maskmsg"/>

<arg0 key="registrationForm.lastname.displayname"/>

<var>

<var-name>mask</var-name>

<var-value>^[a-zA-Z]*$</var-value>

</var>

</field>

默认的arg0-arg3元素将在消息资源中查找相应的key,如果资源属性设为false,她将把值直接传进去,而不从消息资源中查找。注意1.1版本中,你必须为每个模块中明确地定义在验证中用到的消息资源,否则将使用top-level资源。

<field

property="integer"

depends="required,integer,intRange">

<arg0 key="typeForm.integer.displayname"/>

<arg1

name="range"

key="${var:min}"

resource="false"/>

<arg2

name="range"

key="${var:max}"

resource="false"/>

<var>

<var-name>min</var-name>

<var-value>10</var-value>

</var>

<var>

<var-name>max</var-name>

<var-value>20</var-value>

</var>

</field>
常量/变量

全局的常量可以在全局标签中定义,FormSet/本地常量能在formset 标签中创建。常量当前仅仅是代替字段的property属性,字段的var 元素的 value属性,字段的msg 元素的 key属性,字段的arg0-arg3 元素的 key属性。字段的变量也可以在arg0-arg3 元素中被代替(例如:${var:min}))。替换的顺序是FormSet/Locale常量第一,全局的常量第二,

arg elements 变量最后。

<global>

<constant>

<constant-name>zip</constant-name>

<constant-value>^\d{5}(-\d{4})?$</constant-value>

</constant>

</global>

 

<field

property="zip"

depends="required,mask">

<arg0 key="registrationForm.zippostal.displayname"/>

<var>

<var-name>mask</var-name>

<var-value>${zip}</var-value>

</var>

</field>

验证器可以使用字段下面的变量部分来存储变量,这些变量通过字段的getVar((String key)方法取得。

<field

property="integer"

depends="required,integer,intRange">

<arg0 key="typeForm.integer.displayname"/>

<arg1

name="range"

key="${var:min}" resource="false"/>

<arg2

name="range"

key="${var:max}" resource="false"/>

<var>

<var-name>min</var-name>

<var-value>10</var-value>

</var>

<var>

<var-name>max</var-name>

<var-value>20</var-value>

</var>

</field>
使用validwhen设计复杂的验证

使用validwhen来设计复杂验证的一个经常的要求就是根据一个字段验证另外一个字段(比如, 如果你要用户两次输入口令来确认值口令一致),另外一个就是表单中的一个字段只有另外一个字段有确定值的时候才是必须输入的。新的validwhen验证规则将很快被包含在1.1后的STRUTS版本中,她就是用来处理这种情况的。

        validwhen 规则处理单个的变量字段,叫测试。这变量的值是一个布尔的表达式,如果验证有效则它必须为真。可以包含这种变量的表达式有:

u        单引号或双引号字符串literals,

u        十进制、十六进制、八进制的Integer literals,

u        null与null和空字符串匹配,

u        其它可以用属性名引用的form字段,例如customerAge,

u        可以在外部因用得索引字段, 例如childLastName[2],

u        可以默认implicit因用得索引字段, 例如childLastName[], 她将作为被索引的字段使用同样的索引到数组中,

The literal *这里指它包含当前测试字段的值,

作为例子,考虑一个包含通讯地址和邮箱字段的form。如果通讯地址不为空则邮箱字段是必须的required。你能这样定义validwhen 规则:

<field property="emailAddress" depends="validwhen">

<arg0 key="userinfo.emailAddress.label"/>

<var>

<var-name>test</var-name>

<var-value>((sendNewsletter == null) or (*this* != null))</var-value>

</var>

</field>

上面定义的意思是:如果通讯地址是空或不空时这个字段时有效的。

这里有个稍微复杂的例子,它使用了索引字段。假定有一个表单,允许用户输入他们希望定购的部件号和数量。类orderLine 的bean的一数组被用来在称为orderLines 的一属性保持输入项。

If you wished to verify that every line with part number also had a quantity entered, you could do it with:

如果你希望校验订单中有数量输入得每一行,你可以这样:

<field

property="quantity"

indexedListProperty="orderLines"

depends="validwhen">

<arg0 key="orderform.quantity.label"/>

<var>

<var-name>test</var-name>

<var-value>((orderLines[].partNumber == null) or (*this* != null))</var-value>

</var>

</field>

这里的意思是:如果相应的partNumber 字段是空, 或这字段是不空的,则这字段是有效的。

最后一个例子,想象一表单,用户必须输入他们的以英寸为单位的高度,如果他们在高度在60英寸以下,则出一错误。(it is an error to have checked off nbaPointGuard as a career.)

<field property="nbaPointGuard" depends="validwhen">

<arg0 key="careers.nbaPointGuard.label"/>

<var>

<var-name>test</var-name>

<var-value>((heightInInches >= 60) or (*this* == null))</var-value>

</var>

</field>

 

给程序员的简单说明:

所有的比较关系必须在parens 封装。All comparisons must be enclosed in parens.

只有两个itme时可以and或or链接。

如果比较的两item都可以转为整数,则使用numeric比较,否则使用字符串比较。
可插入验证器

验证是从validation.xml 文件中加载的,默认的验证规则定义在validation.xml 文件中,默认定义了required, mask ,byte, short, int, long, float, double, date (没有本地支持), and a numeric range。

" mask "方式依赖于默认值安装要求,那意味着"required "可以完成,在"'mask "将运行以前"required "和" mask "方式被默认包含进框架中了。任何字段如果不是"required "而且是空或有零长度将跳过其他验证。

如果使用了Javascript 标签,客户端javascript在validator's javascript 属性中查找值而且产生一个有验证form方法的对象,要得到更多的关于Javascript Validator 标签工作细节的详细的解释,参阅html标签API参考。

"'mask' "方式让你用一正则表达式掩码验证字段,它使用jakarta的正规表达式包,所有的有效性规则存储在validator-rules.xml 文件,使用的主类是org.apache.regexp.RE。

validation.xml文件中的验证器配置范例:

<validator name="required"

classname="org.apache.struts.validator.FieldChecks"

method="validateRequired"

methodParams="java.lang.Object,

org.apache.commons.validator.ValidatorAction,

org.apache.commons.validator.Field,

org.apache.struts.action.ActionErrors,

javax.servlet.http.HttpServletRequest"

msg="errors.required">

<validator name="mask"

classname="org.apache.struts.validator.FieldChecks"

method="validateMask"

methodParams="java.lang.Object,

org.apache.commons.validator.ValidatorAction,

org.apache.commons.validator.Field,

org.apache.struts.action.ActionErrors,

javax.servlet.http.HttpServletRequest"

msg="errors.invalid">


定义可插入验证器

方法的参数是用逗号分隔的一些类名称列表,方法属性需要有一个符合上面的列表的签名。列表由以下组合而成:

java.lang.Object – 要验证的Bean。

org.apache.commons.validator.ValidatorAction – 当前ValidatorAction。

org.apache.commons.validator.Field – 要验证的字段

org.apache.struts.action.ActionErrors – 如果验证错误将加入ActionError的错误对象javax.servlet.http.HttpServletRequest –当前request 对象。

javax.servlet.ServletContext – 应用的ServletContext。

org.apache.commons.validator.Validator–当前的org.apache.commons.validator.Validator实例。

java.util.Locale – 当前用户的Locale。
多页面form

字段部分有一可选的页面属性,它可以被设为整数,页上字段的所有验证小于或等于服务器端验证的当前页,页上字段的所有验证小于或等于客户端页上所有字段的验证小于或等于服务器端验证的当前页验证的当前页。一个mutli-part表单需要定义页面属性:

<html:hidden property="page" value="1"/>。
比较两个字段

这是一个展示你怎样才能比较两个字段是否有一样的值的例子。比如“用户改变他们的口令“一般会有口令字段和一确认字段。

<validator name="twofields"

classname="com.mysite.StrutsValidator"

method="validateTwoFields"

msg="errors.twofields"/>

<field property="password" depends="required,twofields">

<arg0 key="typeForm.password.displayname"/>

<var>

<var-name>secondProperty</var-name>

<var-value>password2</var-value>

</var>

</field>

 

public static boolean validateTwoFields(

Object bean, ValidatorAction va,  

Field field, ActionErrors errors, HttpServletRequest request,  

ServletContext application) {

String value = ValidatorUtils.getValueAsString( bean,   field.getProperty());

String sProperty2 = field.getVarValue("secondProperty");

String value2 = ValidatorUtils.getValueAsString( bean,   sProperty2);

 

        if (!GenericValidator.isBlankOrNull(value)) {

try {

if (!value.equals(value2)) {

errors.add(field.getKey(),

Resources.getActionError( application, request, va, field));

                             return false;

}

} catch (Exception e) {

errors.add(field.getKey(), Resources.getActionError( application, request, va, field));

return false;

}

}

}
已知的bug

Struts Validator依赖于Commons Validator包,所以问题报告和增强需求可能在两个产品中列出。

·    Struts Validator Bugzilla Reports

·    Commons Validator Bugzilla Reports
变更和deprecations

新建的标记属性。

<html:javascript>标记有新的属性定义.

使用commons-validator.jar中的DTD验证。

当前使用的验证XML文件是根据commons-validator.jar中的DTD。Struts不在为validator-rules.xml and validator.xml.单独维护一个分离的DTD,另外,commons-validator 现在维护一个统一的validator.dtd。修改所有validator.xml文件的DTD引用为

<!DOCTYPE form-validation PUBLIC

"-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.0//EN"

"http://jakarta.apache.org/commons/dtds/validator_1_0.dtd">

空字段。

当前默认在所有得基础验证类型中忽略空白的字段,如果你要求一个字段必须输入,那末在你的应用的validator.xml 文件相应的字段定义的depends属性中添加 " required "。

新建的范围RANGE方法.

JavaScript 和JAVA中都添加了intRange & floatRange 方法。

有条件地REQUIRED字段.

最大的修改是添加了基于其她字段的值的有条件地require验证的能力。它允许你定义逻辑如:“只有X字段非空的时候Y字段为’male’才有效”,这是实现上述逻辑的推荐方法,这种方法在1.1版后的第一版将实现。在1.1版中添加的Requiredif验证规则,将在新版中去掉。不过,如果你正准备使用requiredif,这里有一个简短的教程。

        让我们假定你有一个有3个字段的医药的信息表单,性别sex,怀孕测试pregnancyTest,测试结果testResult,如果性别为'f' or 'F',则怀孕测试pregnancyTest是required,如果pregnancyTest不是空,测试结果testResult是required。

你的validation.xml 文件的输入项应该是这样的:

<form name="medicalStatusForm">

<field property="pregnancyTest" depends="requiredif">

<arg0 key="medicalStatusForm.pregnancyTest.label"/>

<var>

<var-name>field[0]</var-name>

<var-value>sex</var-value>

</var>

<var>

<var-name>fieldTest[0]</var-name>

<var-value>EQUAL</var-value>

</var>

<var>

<var-name>fieldValue[0]</var-name>

<var-value>F</var-value>

</var>

<var>

<var-name>field[1]</var-name>

<var-value>sex</var-value>

</var>

<var>

<var-name>fieldTest[1]</var-name>

<var-value>EQUAL</var-value>

</var>

<var>

<var-name>fieldValue[1]</var-name>

<var-value>f</var-value>

</var>

<var>

<var-name>fieldJoin</var-name>

<var-value>OR</var-value>

</var>

</field>

<field property="testResult" depends="requiredif">

<arg0 key="medicalStatusForm.testResult.label"/>

<var>

<var-name>field[0]</var-name>

<var-value>pregnancyTest</var-value>

</var>

<var>

<var-name>fieldTest[0]</var-name>

<var-value>NOTNULL</var-value>

</var>

</field>

</form>

 

这里有一个使用索引的属性更复杂的例子,如果你的struts-config.xml 有这下面:

<form-bean name="dependentlistForm"

type="org.apache.struts.webapp.validator.forms.ValidatorForm">

<form-property

name="dependents"

type="org.apache.struts.webapp.validator.Dependent[]" size="10"/>

<form-property name="insureDependents" type="java.lang.Boolean" initial="false"/>

</form-bean>

这里dependentlistForm bean有lastName,firstName,dob,coverageType四个属性,你可以这样定义一验证规则:

<form name="dependentlistForm">

<field

property="firstName" indexedListProperty="dependents" depends="requiredif">

<arg0 key="dependentlistForm.firstName.label"/>

<var>

<var-name>field[0]</var-name>

<var-value>lastName</var-value>

</var>

<var>

<var-name>fieldIndexed[0]</var-name>

<var-value>true</var-value>

</var>

<var>

<var-name>fieldTest[0]</var-name>

<var-value>NOTNULL</var-value>

</var>

</field>

 

<field

property="dob" indexedListProperty="dependents" depends="requiredif,date">

<arg0 key="dependentlistForm.dob.label"/>

<var>

<var-name>field[0]</var-name>

<var-value>lastName</var-value>

</var>

<var>

<var-name>fieldIndexed[0]</var-name>

<var-value>true</var-value>

</var>

<var>

<var-name>fieldTest[0]</var-name>

<var-value>NOTNULL</var-value>

</var>

</field>

 

<field

property="coverageType" indexedListProperty="dependents" depends="requiredif">

<arg0 key="dependentlistForm.coverageType.label"/>

<var>

<var-name>field[0]</var-name>

<var-value>lastName</var-value>

</var>

<var>

<var-name>fieldIndexed[0]</var-name>

<var-value>true</var-value>

</var>

<var>

<var-name>fieldTest[0]</var-name>

<var-value>NOTNULL</var-value>

</var>

<var>

<var-name>field[1]</var-name>

<var-value>insureDependents</var-value>

</var>

<var>

<var-name>fieldTest[1]</var-name>

<var-value>EQUAL</var-value>

</var>

<var>

<var-name>fieldValue[1]</var-name>

<var-value>true</var-value>

</var>

<var>

<var-name>fieldJoin</var-name>

<var-value>AND</var-value>

</var>

</field>

</form>

这里的意思是:

如果lastName 字段是非空的,firstName 字段required。因为字段Indexed 为真,这它意味着lastName的indexed 必须与firstName 的索引的一样,dob同理,除非date不为空。

如果lastName 用样索引时的值不空, 而且非索引字段insureDependents为真,则coverageType 是only require。

你可以对字段在[n]中使用任意数字,唯一的限制是他们必须都是AND或OR,你无法混合使用。

Deprecation:

u        JavaScript 和Java的range方法.

u        StrutsValidator &StrutsValidatorUtil 类中的Deprecation方法
验证器api指南

一个简明的Struts验证器API指南 可以帮助你开始。
验证器资源

Struts Validator: Validating Two Fields Match 作者Matt Raible。(两个字段匹配验证)关于使用方法的文章。(范例部分为翻译此文内容)

DynaForms and the Validator 作者James Turner and Kevin Bedell。Struts Kickstart的其中一章(动态form和验证器),可以自由下载PDF).

Validating user input 作者 David Winterfeldt and Ted Husted。Struts in Action的其中一章,可以自由下载(PDF)。

posted @ 2009-01-07 09:52 Hill 阅读(197) | 评论 (0)编辑 收藏
 

首先,我先大概介绍一下jpetstore的整体架构,spring的这个版本主要使用了struts+spring+ibatis的框架组合,
而在MVC层的框架,这个版本又同时提供了两个实现版本,一个是struts,一个是spring 自带的web框架,

而数据库持久层使用的是ibatis框架,这个框架是一个SQL映射框架,轻量级而且使用非常容易,基本上会

使用JDBC的朋友看一两个小时就会使用了。
下图是该应用的一个简略架构图,没有什么好的工具,就大概画了一个,虽然比较简单,不过也基本可以

概括应用的整体框架了,首先是JSP请求,通过struts-config.xml(这里只是根据struts来画的,spring其实也差不多),

请求转到相应的Action中,可以看到,这里有一个BaseAction,是所有Action实现的父类,这个类的作用稍后再讲,

然后就是每个Action通过PetStoreFacade的对象调用后台DAO对象,从而通过ibatis进行数据的持久操作,

这里使用了门面(Facade)模式,隔离了前台与后台耦合度,而PetStoreFacade就是这个门面。结合下图,

相信大家对整个jpetstore会有个大概的了解。

 

 

好了,大概的结构讲了下,接下来我们就从代码入手,在这里考虑到struts大家比较熟悉,因此,

本文是以struts版本来讲,同时声明,本文并不会一段段代码详细讲述,而只是提供一个学习的参考,

大概讲解一下流程,让大家不再茫然不知从哪开始,好了,废话也不多说了。

既然是WEB应用,那当然首先从配置文件看起,那就非web.xml莫属了,打开WEB-INF目录下的web.xml,

我们挑出目前我们应该关注的配置代码:
代码  
<servlet>    
   <servlet-name>petstore</servlet-name>    
   <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>    
   <load-on-startup>2</load-on-startup>    
</servlet>    
   
<servlet>    
   <servlet-name>action</servlet-name>    
   <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>    
   <load-on-startup>3</load-on-startup>    
</servlet>    
   
<servlet-mapping>    
   <servlet-name>petstore</servlet-name>    
   <!--   
   <servlet-name>action</servlet-name>   
   -->    
   <url-pattern>*.do</url-pattern>    
</servlet-mapping>
在这里可看到两个servlet设置,以及一个servlet mapping,第一个petstore的servlet是用于spring web框架的,

而第二个action的servlet就是用于struts的,而这个版本的mapping默认的是使用spring web,可以看到,<servlet-name>action</servlet-name>这一行是被注释掉了,我们要使用struts的话,那就把这个注释去掉,

改为注释掉<servlet-name>petstore</servlet-name>,好了,如此注释以后,整个应用就会以struts的框架运行。

(这些配置是什么意思?如果你还搞不懂的话,建议你先去学学基础再来研究应用吧)

接下来我们可以打开strutc-config.xml文件,这里就是struts的默认配置文件,在这里可以看到struts的相关配置信息。

好了,接下来我们就以一个请求来讲述基本的请求流程,以search为例,生成项目,再启动,

成功之后进入应用的首页,我们终于可以看到久违的鹦鹉界面了,激动吧,呵呵,在界面的右上角,

有一个Search文本框,我们就以这个Search为例子来讲解,输入一个关键字cat,然后点search,

结果出来了,这个过程的内部是如何运作的呢?

我们用鼠标右键点击页面,然后选择属性,我们看到显示的地址可能是:
http://localhost:8080/shop/searchProducts.do;jsessionid=E2D01E327B82D068FEE9D073CA33A2A3

这个地址就是我们刚才点击查询时提交的地址了,我们看到searchProducts.do这个字符串,

我们之前在web.xml里面的设置大家还记得吗?
<servlet-mapping>
   <!--
   <servlet-name>petstore</servlet-name>
   -->
   <servlet-name>action</servlet-name>
   <url-pattern>*.do</url-pattern>
</servlet-mapping>
代表着所有以.do为结尾的请求,都将被名叫action的servlet处理,也就是通过struts-config配置进行处理,那我们就去struts-config.xml里面看看,打开struts-config.xml文件,ctrl+F弹出查询界面,输入searchProducts,我们就可查到
<action path="/shop/searchProducts" type="org.springframework.samples.jpetstore.web.
struts.SearchProductsAction" name="emptyForm" scope="session" validate="false">    
<forward name="success" path="/WEB-INF/jsp/struts/SearchProducts.jsp"/>    
</action>
根据以上配置,我们可以得知,刚才我们的提交将会被SearchProductsAction处理,而该action的form是emptyForm,查找emptyForm这个别名,我们可以找到它指向一个BaseActionForm,打开这个form我们可以看到,里面只有两个validate方法和一个addErrorIfStringEmpty方法,没有任何的属性,那就是说search这个提交并没有把我们输入的关键字保存在form里面,打开SearchProductsAction,我们看到execute方法里的第一句就是

String keyword = request.getParameter("keyword");
也就是说我们输入的关键字是以参数的形式传入到request里面,参数名字为“keyword”,我们打开IncludeTop.jsp,这个文件在WEB-INF/jsp/struts目录下。

注意了,jsp目录下分别有spring以及struts两个目录,这两个目录就是分别对应两个web框架的,我们使用的是struts所以jsp代码就到struts目录里面,为什么打开IncludeTop.jsp呢,我们可以看到,无论我们进入哪个页面,search那个文本框都存在,也就是说,这个文本框是被当作一个模板插入到所有页面当中去的,我们随便打开一个页面,就可以看到页面开头都是:

<%@ include file="IncludeTop.jsp" %>
这句就是把IncludeTop.jsp当作页面头部包含进来,所以凡是包含这个头页面的页面,他们的头部都是一样的,这也是我们在开发中常用的一种方式,统一界面的一种方式,我们也可以看到在这些文件尾部也有类似的代码,如:

<%@ include file="IncludeBottom.jsp" %>

其作用也是一样。打开IncludeTop.jsp后,我们看到其中有一段代码:

<form action="<c:url value="/shop/searchProducts.do"/>" method="post">
<input type="hidden" name="search" value="true"/>
<input name="keyword" size="14"/> <input border="0" src="../images/search.gif"

type="image" name="search"/>
</form>
这段代码就是我们search文本框以及提交链接的代码,在这里就不做详细介绍了。

好了,接下来我们再看看这个action的后续代码

if (keyword != null) {  
if (!StringUtils.hasLength(keyword)) {  
   request.setAttribute("message", "Please enter a keyword to search for,

then press the search button.");  
   return mapping.findForward("failure");  
   }  
PagedListHolder productList = new PagedListHolder(getPetStore().

searchProductList(keyword.toLowerCase()));  
productList.setPageSize(4);  
request.getSession().setAttribute("SearchProductsAction_productList", productList);  
request.setAttribute("productList", productList);  
return mapping.findForward("success");  
}

这里第一句就是判断keyword是否为空,不为空就执行其中的代码,我们search的时候这个keyword肯定是不为空的,

那就是说这段if包含的代码就是我们search的处理代码了,有的人也许会说,如果我不输入关键字而直接点search呢,

这keyword不就是为空了吗?我想这个你在此处加个断点,再运行一下就知道了,虽然你没有输入,

但是keyword一样不是null,它将是一个空字符串,而不是空对象。我们看到if里面还包含有一个if,

这里就是判断keyword是否为空字符串了,StringUtils.hasLength()方式是一个工具类,

用来判断keyword是否为空字符串,如果是空字符串就返回false,而这句判断当返回false时,

因为前面有个感叹号,所以值为false就执行被if所包含的语句,里面的代码就是保存一个错误信息,然后return mapping.findForward("failure");,这句的意思就不再解释了。

现在假设我们正确输入keyword,那么程序将不会执行if语句中的代码,直接向下执行,我们看到


PagedListHolder productList = new PagedListHolder(getPetStore().

searchProductList(keyword.toLowerCase()));

这句代码,PagedListHolder是spring提供的一个用来保存查询结构类,通常用来进行分页处理,

因此我们可以知道

getPetStore().searchProductList(keyword.toLowerCase())
这一句就是用来查询,并返回查询结果的。getPetStore()这个方法是继承自BaseAction的,它将获得一个PetStoreFacade的实现,我们打开BaseAction的代码,可以看到如下代码

public void setServlet(ActionServlet actionServlet) {  
       super.setServlet(actionServlet);  
       if (actionServlet != null) {  
           ServletContext servletContext = actionServlet.getServletContext();  
       WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);  
       this.petStore = (PetStoreFacade) wac.getBean("petStore");  
       }  
}

这句代码里面最重要的一句就是

WebApplicationContext wac = WebApplicationContextUtils.

getRequiredWebApplicationContext(servletContext);
这句代码的作用就是获取一个WebApplicationContext对象wac,在这里我们只需要知道这个对象的作用,而不对其进行深入研究,通过这个对象,我们可以根据spring的配置文件,获得相应的Bean,下面这句就是用来获取相应bean的语句:


this.petStore = (PetStoreFacade) wac.getBean("petStore");
petStore这个就是bean的id,这个petStore的bean具体是哪个类呢?我们打开applicationContext.xml,可以找到以下配置代码


<bean id="petStore" class="org.springframework.samples.jpetstore.domain.logic.PetStoreImpl">
   <property name="accountDao" ref="accountDao"/>
   <property name="categoryDao" ref="categoryDao"/>
   <property name="productDao" ref="productDao"/>
   <property name="itemDao" ref="itemDao"/>
   <property name="orderDao" ref="orderDao"/>
</bean>
从这里可以看到,petStoreFacade的具体类就是PetStoreImpl,而这个类当中,分别通过spring IOC注入了几个DAO的bean,这几个DAO的配置可以在dataAccessContext-local.xml文件里面找到,我们打开PetStoreImpl这个实现类,我们看到类里有一个searchProductList方法,这个方法就是我们在Action当中调用的方法

return this.productDao.searchProductList(keywords);
从这句代码可以看出,这个方法是通过调用productDao的searchProductList方法来获得结果的,

productDao这个DAO从上面的配置文件可以看出,是通过IOC容器进行注入的,我们打开dataAccessContext-local.xml文件,可以看到

<bean id="productDao" class="org.springframework.samples.jpetstore.dao.ibatis

.SqlMapProductDao">
   <property name="sqlMapClient" ref="sqlMapClient"/>
</bean>
这句配置代表了productDao的实现类就是SqlMapProductDao,同时这个类包含有一个sqlMapClient的属性对象,这个对象也是通过ioc注入的,再在这个配置文件里,我们可以找到如下一段代码

<bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
   <property name="configLocation" value="WEB-INF/sql-map-config.xml"/>
   <property name="dataSource" ref="dataSource"/>
</bean>
这段代码就是sqlMapClient这个bean的配置,这里的实现是SqlMapClientFactoryBean,它是spring专门为ibatis框架提供的一个支持,通过这个对象就可以很好的集成ibatis框架,具体的介绍可以通过spring官方文档或者是其他一些教程获得,在这里就不多做介绍。

好了,接下来我们知道了,实际查询数据是通过DAO的实现类SqlMapProductDao进行的,而SqlMapProductDao当中就是通过了ibatis进行数据的查询,从而返回结果,这里也就不多做介绍了,大家可以通过ibatis的教程获得ibatis的使用方法,非常的简单,search操作从前台到后台的大概流程就介绍到这里了。

在这个参考文章中,我并没有对具体技术做过多的讲解,那是因为本文只是作为一个研究jpetstore的参考,提供一个可供参考的研究流程,主要是为了那些想开始研究jpetstore但是又不知道从哪开始或者是不知道如何进行研究的新人朋友们而准备的,如果具体的讲解每一部分,那我想将不仅仅是一篇文章就可以完成的事情,因为这里涉及到struts,spring,ibatis等具体的框架技术,每一个框架基本都可以写成一本书,用一篇文章来讲就不太实际了,而且我个人更倾向于遇到不理解的地方的时候,多使用google来搜索,这样能够进一步加深自己对问题的理解。

好了,关于jpetstore源码研究入门的文章就写到这里结束了,由于本人技术和文笔有限,有错漏或者表达不当的地方请不要介意,欢迎各位朋友来指正。

posted @ 2009-01-06 15:50 Hill 阅读(3777) | 评论 (0)编辑 收藏
CALENDER
<2009年1月>
28293031123
45678910
11121314151617
18192021222324
25262728293031
1234567

常用链接

留言簿(3)

随笔档案

文章档案

搜索

  •  

最新评论

阅读排行榜

评论排行榜


Powered By: 博客园
模板提供沪江博客