Posts Tagged ‘aB’

在IE中为abbr标签加样式

星期三, 06月 4th, 2008

作者:JunChen 2005-5-24 9:56:57
原文:http://www.sovavsiti.cz/css/abbr.html
翻译:JunChen

版权:译者JunChen所有,转载请联系译者.
简介

<abbr>是用来为web页面上de简称(译者注:这里把简称和缩写分开而论,简称范围比缩写大,取首字母de缩写用<acronym>标签)添加适当标注deXHTML标签,WindowsdeIE浏览器暂不支持<abbr>标签. 在IE里,您可以应用CSS给<acronym>但是不能应用给<abbr>标签,IE会为<acronym>标签detitle属性显示提示,但是会忽略<abbr>标签.

这个IEdebug(或者特色)使得一些网站人员认为<abbr>标签一点用都没有,而显然这么认为是不对de.在Mozilla和Opera里还是很正确de处理了这个标签,并且它对于web内容de可读性和语义化来说非常重要.这也是我为什么一直在寻找解决方法,最终我找到了.

解决方法

本方法基于一个简单de事实:即使IE会忽略<abbr>标签,但是其他嵌套在<abbr>标签里de标签还是正常de.所以我在嵌了一个<span>标签在<abbr>里,设置<span>detitle和class属性,然后<abbr>开始变得和<acronym>标签一样了.

相关代码例子

看一下下面de相关代码,是一个简单de缩写词de例子:

<abbr title=”Cascading Style Sheets”>CSS</abbr>
现在,对比一下修改后de相关代码:

<abbr title=”Cascading Style Sheets”><span class=”abbr” title=”Cascading Style Sheets”>CSS</span></abbr>
自动操作

手动de给每一个<abbr>标签嵌入<span>显然不可能——既无聊又对Mozilla和Opera没必要.幸运de是,现在有一个自动de、基于客户端脚本de解决方法.

您可能注意到了,这个页面(译者注:原作者de页面)上de简写词语即使在IE里都会有提示,并且加了CSS样式(虚下划线和一个问号状de鼠标光标).然而您如果看一下源相关代码,您将找不到在上文提到de<span>标签.这得益于本页加载de一个简单deJavaScript:

function styleAbbr() {
var oldBodyText, newBodyText, reg
if (isIE) {
oldBodyText = document.body.innerHTML;
reg = /<ABBR([^>]*)>([^<]*)<\/ABBR>/g;
newBodyText = oldBodyText.replace(reg, ‘<ABBR $1><SPAN class=\”abbr\” $1>$2</SPAN></ABBR>’);
document.body.innerHTML = newBodyText;
}
}
window.onload = function(){
styleAbbr()
};

isIE = (document.all) ? true:false;

这段脚本会检查客户端浏览器,如果是IE,那么则替换所有de<abbr>标签为修改过de版本(嵌入了<span>).注意de是我必须使用正则表达式和innerHTML属性来取代标准deDOM方法,因为IE不能通过DOM来获取<abbr>属性.

样式化

最后看一下这个页面上使用deCSS.相当简单:

abbr, acronym, span.abbr {
cursor: help;
border-bottom: 1px dashed #000;
}
Mozilla和Opera使用abbr和acronym属性选择器,IE则使用acronym和span.abbr.无论如何,<abbr>和<acronym>都被样式化了——一个问号状de鼠标光标(当鼠标指上后)和虚下划线.

其他

1.感谢Michael Kusyn提供了JavaScript解决方法.
2.更多关于<abbr>,<acronym>标签和两者de区别,参考Craig SailadeHTML is not an acronym… (Evolt.org)

欢迎交流意见评论,可以发邮件至marek@sovavsiti.cz.

认识CSS中absolute与relative

星期三, 06月 4th, 2008

很多朋友问过我absolute与relative怎么区分,怎么用?我都知道absolute是绝对定位,relative是相对定位,但是这个绝对与相对是什么意思呢?绝对是什么地方de绝对,相对又是相对于什么地方而言de呢?那他们又有什么样de特性,可以做出什么样de效果呢?关于两者之间又有什么样de技巧呢?下面我就来一一解读.

Absolute,CSS中de写法是:position:absolute; TOP、RIGHT、BOTTOM、LEFT(下面简称TRBL)进行定位,在没有设定TRBL,默认依据父级de做标原始点为原始点.如果设定TRBL并且父级没有设定position属性,那么当前deabsolute则以浏览器左上角为原始点进行定位,位置将由TRBL决定.

一般来讲,网页居中de话用Absolute就容易出错,因为网页一直是随着分辨率de大小自动适应de,而Absolute则会以浏览器de左上角为原始点,不会应为分辨率de变化而变化位置.很多人出错就在于这点上出错.而网页居左其特性与Relative很相似,但是还是有本质de区别de.

Relative,CSS中de写法是:position:relative; 他de意思是绝对相对定位,他是参照父级de原始点为原始点,无父级则以BODYde原始点为原始点,配合TRBL进行定位,当父级内有padding等CSS属性时,当前级de原始点则参照父级内容区de原始点进行定位.

有时我还需要依靠z-index来设定容器de上下关系,数值越大越在最上面,数值范围是自然数.当然有一点要注意,父子关系是无法用z-index来设定上下关系de,一定是子级在上父级在下.

JavaBean(EJB) 3.0 全新体验

星期一, 06月 2nd, 2008

  引言

  期待以久deEJB3.0规范在最近发布了它de初稿.在本文中将对新de规范进行一个概要性de介绍,包括新增de元数据支持,EJBQLde修改,实体Bean模型访问bean上下文de新方法和运行时环境等等.作者还讨论了EJB在未来要作出de调整以及EJB3.0与其他开发规范之间de关系.
  开始

  无论如何由于EJBde复杂性使之在J2EE架构中de表现一直不是很好.EJB大概是J2EE架构中唯一一个没有兑现其能够简单开发并提高生产力de组建.EJB3.0规范正尝试在这方面作出努力以减轻其开发de复杂性.EJB3.0减轻了开发人员进行底层开发de工作量,它取消或最小化了很多(以前这些是必须实现)回调方法de实现,并且降低了实体Bean及O/R映射模型de复杂性.
  在本文中,我首先会介绍EJB3.0中几个主要de改变.它对进一步深入了解EJB3.0是非常重要de.随后,我会从更高de层面来描述已经被提交到EJB3.0规范中de细节,并一个个de讲解新de规范中de改变:实体Bean,O/R映射模型,实体关系模型和EJB QL(EJB查询语言)等等.
  背景
  EJB3.0中两个重要de变更分别是:使用了Java5中de程序注释工具和基于HibernatedeO/R映射模型.
  Java5中de元数据工具
  Java5(以前叫J2SE1.5或Tiger)中加入了一种新de程序注释工具.通过这个工具您可以自定义注释标记,通过这些自定义标记来注释字段、方法、类等等.这些注释并不会影响程序de语义,但是可以通过工具(编译时或运行时)来解释这些标记并产生附加de内容(比如部署描述文件),或者强制某些必须de运行时行为(比如EJB组件de状态特性).注释de解析可以通过源文件de解析(比如编译器或这IDE工具)或者使用Java5中deAPIs反射机制.注释只能被定义在源相关代码层.由于所有被提交到EJB3.0草案中de注释标记都有一个运行时deRetentionPolicy,因此会增加类文件占用de存储空间,但这却给容器制造商和工具制造商带来了方便.
  Hibernate
  目前Hibernate非常受欢迎,它是开发源相关代码deJava O/R映射框架,目de是把开发人员从繁琐de数据持久化编程中解脱出来.它也有一个标准deHQL(Hibernate 查询语言)语言,您可以在新deEJB QL中看到它de影子.Hibernate在处理如数据查询、更新、连接池、事务处理、实体关系处理等方面非常简单.
  概览
  
  在已经提交deEJB3.0规范中主要涉及两个方面de改变:
  1. 一套以注释为基础deEJB编程模型,再加上EJB2.1中定义de通过部署描述符和几个接口定义de应用程序行为.
  2. 新de实体Bean持久化模型,EJBQL也有许多重要de改变.
  还有一些有关上述de提议,比如:一个新de客户端编程模型,业务接口de使用以及实体Beande生命周期.请注意EJB2.1编程模型(包括部署描述符和home/remote接口)仍然是有效de.新de简化模型并没有完全取代EJB2.1模型.
  EJB注释
  EJB规范组织一个重要de目标是减轻原始相关代码de数量,并且他们为此给出了一个完美而简介de办法.在EJB3.0de里,任何类型de企业级Bean只是一个加了适当注释de简单Java对象(POJO).注释可以用于定义beande业务接口、O/R映射信息、资源引用信息,效果与在EJB2.1中定义部署描述符和接口是一样de.在EJB3.0中部署描述符不再是必须de了;home接口也没有了,您也不必实现业务接口(容器可以为您完成这些事情).
  比如,您可以使用@Stateless注释标记类把Java类声明为一个无状态回话bean.对于有状态回话bean来说,@Remove注释可以用来标记一个特定de方法,通过这个注释来说明在调用这个方法之后beande实例将被清除掉.
  为了减少描述组件de说明信息,规范组织还采纳了由异常进行配置(configuration-by-exception)de手段,意思是您可以为所有de注释提供一个明确de缺省值,这样多数常规信息就可以据此推断得出.
  新de持久化模型
  新de实体bean也是一个加了注释de简单Java对象(POJO).一旦它被EntityManager访问它就成为了一个持久化对象,并且成为了持久化上下文(context)de一部分.一个持久化上下文与一个事务上下文是松耦合de;严格de讲,它隐含de与一个事务会话共存.
  实体关系也是通过注释来定义de,O/R映射也是,并提供几种不同de数据库规范操作,在EJB2.1中这些要通过开发人员自己de设计模式或者其它技术来完成de(比如,自增长主键策略).
  深入研究
  现在是时候详细了解EJB3.0草案了.让我开始探讨所有EJB中四种企业级bean,并看看他们在新de规范中是什么样子.
  无状态回话bean
  在EJB3.0规范中,写一个无状态回话bean(SLSB)只需要一个简单deJava文件并在类层加上@Stateless注释就可以了.这个bean可以扩展javax.ejb.SessionBean接口,但这些不是必须de.
  一个SLSB不再需要home接口,没有哪类EJB再需要它了.Bean类可以实现业务接口也可以不实现它.如果没有实现任何业务接口,业务接口会由任意publicde方法产生.如果只有几个业务方法会被暴露在业务接口中,这些方法可以使用@BusinessMethod注释.缺省情况下所有产生de接口都是local(本地)接口,您也可以使用@Remote注释来声明这个接口为remote(远程)接口.
  下面de几行相关代码就可以定义一个HelloWorldbean了.而在EJB2.1中同样debean至少需要两个接口,一个实现类和几个空de实现方法,再加上部署描述符.
  import javax.ejb.*;
  /**
  * A stateless session bean requesting that a remote business
  * interface be generated for it.
  */
  @Stateless
  @Remote
  public class HelloWorldBean {
  public String sayHello() {
  return “Hello World!!!”;
  }
  }
   有状态回话bean
  除了几个SFSBde特别说明之外,有状态回话bean(SFSB)和SLSB一样精简:
   一个SFSB应该有一个方法来初始化自己(在EJB2.1中是通过ejbCreate()来实现de).在EJB3.0de规范中建议这些初始化操作可以通过自定义方法完成,并把他们暴露在业务接口中.在使用这个bean之前由客户端来调用相应de初始化方法.目前规范组织就是否提供一个注释来标记某个方法用于初始化还存在争议.
   Beande提供者可以用@Remove注释来标记任何SFSBde方法,以说明这个方法被调用之后beande实例将被移除.同样,规范组织仍然在讨论是否要有一种机制来处理这种特殊de情况,即当这个方法出现异常de情况下beande实例是否被移除.
  下面是对以上问题我个人de观点:
  1) 是否应该有一个注释来标明一个方法进行初始化呢?我de观点是――应该有,这样容器就可以在调用其他方法之前至少调用一个方法来进行初始化.这不仅可以避免不必要de错误(由于没有调用初始化方法)而且可以使容器更明确de判断是否可以重用SFSB实例.我暂且把这个问题放一放,规范组织只考虑为一个方法提供一个注释来声明它是一个初始化方法.
  2) 对于第二个问题我de观点也是肯定de.这有利于Beande提供者合客户端程序对其进行控制.只有一个遗留de问题:那就是一旦调用这个方法失败,是否能移除这个bean de实例?答案是不能,但是它将会在回话结束de时候被移除.
  消息驱动Bean
  消息驱动Bean是唯一一种必须实现一个业务接口deBean.这个接口指出bean支持de是哪一种消息系统.对于以JMS为基础deMDB来说,这个接口是javax.jms.MessageListener.注意MDB业务接口不是一个真正意义上de业务接口,它只是一个消息接口.
  实体Bean
   实体Bean使用@Entity注释来标记,所有实体bean中de属性/字段不必使用@Transient注释来标记.实体beande持久化字段可以通过JavaBean-style机制或者声明为public/protected字段来实现.
   实体bean可以使用助手类来描述其状态,但是这些类de实例并没有持久化唯一性(persistent identity)de特性(即,唯一标识这个beande字段等),实际上这些助手类与他们de实体bean实例是紧密结合de;并且这些对象还是以非共享方式来访问实体对象de.
  实体关联
  EJB3.0同时支持Bean之间双向de合单向de关联,它们可以是一对一、一对多、多对一或者是多对多de关联.然而双向关联de两端还要分为自身端(owning side)和对方端(inverse side)不同de端.自身端负责向数据库通告关联de变更.对于多对多de关联自身端必须明确de声明.实际上对方端通过isInverse=true进行注释(由此自身端就不必说明了而是由另一段推断出).看来上面de描述,规范组织还能说让EJB变de简单了吗?
  O/R映射
  EJB3.0中deO/R映射模型也有了重要de改变,它从原来deabstract-persistence-schema-based变成了现在deHibernate-inspired模式.尽管目前规范组织还在就此进行讨论但是一个明确de模型将会出现在下一个版本de草案中.
  举例来说,O/R映射模型将通过bean类中de注释来声明.而且此方法还会指出对应de具体表和字段.O/R映射模型提供了一套自有deSQL;而且除了提供一些基本deSQL外还支持某些高层开发de功能.比如,有一个通过@Column注释声明de字段columnDefinition,那么可以写这样deSQL:columnDefinition=”BLOB NOT NULL”
  客户端程序模型

  一个EJB客户端可以通过@Inject注释以一种“注入”de方式获得一个beande业务接口引用.您也可以使用另一个注释@javax.ejb.EJBContext.lookup()来完成上面de操作,但是规范中没有告诉我一个普通deJava客户端怎样获得一个Beande实例,因为这个普通deJava客户端是运行在一个客户端容器中,它无法访问@javax.ejb.EJBContex对象.现在还有另外一种机制来完成上面de工作那就是使用一个超级上下文环境对象:@javax.ejb.Context().但是规范中没有指出该如何在客户端中使用这个对象.
  EJB QL

  EJB QL可以通过@NamedQuery来注释.这个注释有两个成员属性分别是name和queryString.一旦定义了这些属性,就可以通过EntityManager.createNamedQuery(name)来指向这个查询.您也可以创建一个标准deJDBC风格de查询并使用EntityManager.createQuery(ejbqlString)或EntityManager.createNativeQuery(nativeSqlString)(这个方法用于执行一个本地查询)来执行查询.
  EJB QL有两个地方可以定义其参数.javax.ejb.Query接口提供了定义参数、指向查询、更新数据等等方法.下面是一个EJBQL指向查询de例子:

  .. ..
  @NamedQuery(
  name=”findAllCustomersWithName”,
  queryString=”SELECT c FROM Customer c WHERE c.name LIKE :custName”
  )
  .. ..
  @Inject public EntityManager em;
  customers = em.createNamedQuery(”findAllCustomersWithName”)
  .setParameter(”custName”, “Smith”)
  .listResults();

  下面列出了一些EJB QLde增强特性:
  1) 支持批量更新和删除.
  2) 直接支持内连接和外连接.FETCH JOIN运行您指出关联de实体,Order可以指定只查询某个字段.
  3) 查询语句可以返回一个以上de结果值.实际上,您可以返回一个依赖de类比如下面这样:
  SELECT new CustomerDetails(c.id, c.status, o.count)
  FROM Customer c JOIN c.orders o
  WHERE o.count > 100
  4) 支持group by 和having.
  5) 支持where子句de嵌套子查询.
  在提交deEJB3.0草案中,EJB QL与标准SQL非常de接近.实际上规范中甚至直接支持本地deSQL(就像我上面提到de那样).这一点对某些程序员来说也许有些不是很清楚,我将在下面进行更详细de讲解.

    多样性

  方法许可(Method permissions)可以通过@MethodPermissions或@Unchecked注释来声明;同样de,事务属性也可以通过@TransactionAttribute注释来声明.规范中仍然保留资源引用和资源环境引用.这些一样可以通过注释来声明,但是有一些细微de差别.比如,上下文(context)环境要通过注入工具控制.容器根据bean对外部环境引用自动初始化一个适当de已经声明de实例变量.比如,您可以象下面这样获得一个数据源(DataSource):
  @Resource(name=”myDataSource”) //Type is inferred from variable
  public DataSource customerDB;
  在上面de例子中如果您不指定引用资源de名称(name)那么其中decustomerDB会被认为是默认值.当所有de引用属性都可得到时,@Injec注释就可以这样写:
  @Inject public DataSource customerDB;
  容器负责在运行时初始化customerDB数据源实例.部署人员必须在此之前在容器中定义好这些资源属性.
  更好de消息是:那些以前必须检测de异常将一去不复返.您可以声明任意de应用程序异常,而不必在再抛出或捕获其他类似CreateException和FinderException这样de异常.容器会抛出封装在javax.ejb.EJBException中de系统级异常或者只在必要时候抛出IllegalArgumentException或IllegalStateException异常.
  EJB文件处理模式

  在我结束本节之前,让我de快速de浏览一下容器提供商在EJB处理模式方面可能de变更.规范中对此并没有明确de表态,但我可以想到至少两种模式.
   一种办法是首先利用EJB文件生成类似于EJB2.1部署模式de文件(包括必要de接口和部署描述符)然后再用类似于EJB2.1de方式来部署这个EJB组件.当然,这样产生de部署描述符可能并不标准但是它可以解决同一个容器对EJB2.1和EJB3.0兼容de问题.下面这幅图描述了这一过程.
   另一种方法是一种类似于JSP托放de部署模式.您可以把一个EJB文件放到一个预先定义de目录下,然后容器会识别这个EJB并处理它,然后部署并使之可以使用.这种方法可以建立于上面那种方法之上,在支持反复部署时有很大de帮助.考虑到部署de简单性也是EJB3.0规范de目de之一,我真诚de希望在下一个草案出来时能够确定一个模式(至少能有一个非正式de).
  您有什么想法?
  EJB3.0规范de制定正在有序de进行,为了使EJBde开发变得更加容易,EJB规范组织作出de努力是有目共睹de.就像他们说de那样,一切对会变得简单,但做到这一点并不容易.目前已经定义了50个注释标记(还有几个将在下一个草案中发布),每一个都有自己de缺省规则和其他de操作.当然,我真de不希望EJB3.0变成EJB2.1de一个翻版”EJB 3.0 = EJB 2.1 for dummies”(希望这个等式不要成立).最后,我还是忍不住要提一些我自己de观点:
  1) 首先,规范确实使反复部署变得容易了,并且有一个简单de模式来访问运行时环境.我还是觉得home接口应该放弃.
  2) 在早期deEJB规范中,实体bean用于映射一个持久化存储.理论上(也许只是理论上)可能需要把实体bean映射到一个遗留deEIS(enterprise information system)系统中.出于将来扩展de考虑这样作是有好处de,并且可以使更多de业务数据模型采用实体bean.也因此其伴随de复杂性使得实体bean不被看好.在本次提交de草案中,一个实体bean只是一个数据库de映射.并且是基于非抽象持久化模式和简单de数据访问模式de更加简单开发.
  3) 我对模型变更持保留态度,我认为在EJB中包含SQL脚本片断并不是个好注意.一些开发人员完全反对包含某些“SQL片段(SQLness)”(比如@Table 和 @Column注释).我de观点是这些SQLness是好de,据此我可以清楚de知道我到底要数据库作些什么.但是某些SQL段我看来并不是很好,比如columnDefinition=”BLOB NOT NULL”,这使得EJB相关代码和SQL之间de耦合太过紧密了.
  4) 尽管对于本地SQLde支持看似很诱人,其实在EJB相关代码中嵌入SQL是一个非常糟糕de主意.当然,有些办法可以避免在EJB中硬编码SQL,但是这应该在规范中说明,而不能是某些开发人员自己定义de模式.
  5) 假设@Table注释只用于类.在运行时通过@Table注释dename属性定义de表名称将必须对应一个实际de数据库表.规范对此应该给予清楚de说明和一致de模式.
  6) 规范还需要更清楚de说明客户端编程模型,尤其是普通java客户端.规范中所有de参考都假设或者隐含de使用EJB客户端.而且规范中对客户端de向后兼容方面也没有给出明确de说法.
  7) Transient注释应该重新命名以避免和已有detransient关键字发生冲突.事实上,在这一点上我更乐于稍微de背离一下configuration-by-exception原则并且定义一个@Persistent注释来明确de定义持久化字段.@Persistent注释可以仅仅是一个标记注释或者它可以有几个属性来关联O/R映射注释.
  与其他规范de关联
  目前可能影响到EJB3.0deJSR有JSR175(java语言元数据工具)和JSR181(Java Web服务元数据)
  JSR175已经初步完成并且不会和EJB3.0有太大de冲突;但是JSR181与EJB3.0有两个关联de地方:
  1) Web service接口:EJB规范将采用一种机制适应JSR181以便可以把一个bean实现为一个Web service并告诉Web service如何被客户端调用.
  2) JSR 181计划采用不同de机制来处理安全问题.在早期de规范中EJB建议使用一个一致de机制(MethodPermissions),但是JSR 181计划使用一个稍微不同de方式(SecurityRoles和SecurityIdentity注释).同样deRunAs注释de定义也存在这些许差别.这一问题还在解决中最终会在J2EE层de规范中维持其一致性.
  在J2EE 1.5中de一些开发规范可能与EJB3.0有关联.除了上面说到de几个关联之外现在没有其他de开发规范与EJB3.0有冲突.
  结束语
  在使EJBde开发变得简单高效之前,我还有很长一段路要走.规范组织在降低EJBde开发难度方面起了个好头.O/R映射模型de提议还处在早期阶段,规范组织正在完善它.我希望它不要太复杂也不要与SQL过分de耦合.让我不要只是停留在期望、希望、思考和请求中:提出您de想法并把您de建议发送给规范组织ejb3-feedback@sun.com.JCP并不是很民主de组织,但是您de建议一定是有价值de.
  本文de观点是作者de个人主张与作者所在de公司没有任何关系.作者非常感谢Hemant Khandelwal对发表此文de帮助.
  提供下载de相关代码是EJB3.0草案de示例相关代码.由于没有工具和环境de支持这个例子是没有经过验证de.这些相关代码只是示例了未来deEJB3.0大概de样子.
  Anil Sharma是BEA System公司开发Weblogic Integration产品de资深程序员.在加入BEA之前曾就职于Oracle 和 Pramati,并一直致力于研究J2EE技术.在业余时间它喜欢听音乐看电影;他还参加一些开源项目de开发工作.

JavaBeans程序开发

星期一, 06月 2nd, 2008

  JavaBeansde属性
  JavaBeansde属性与一般Java程序中所指de属性,或者说与所有面向对象de程序设计语言中对象de属性是一个概念,在程序中de具体体现就是类中de变量.在JavaBeans设计中,按照属性de不同作用又细分为四类:Simple, Index, Bound与Constrained属性.
  1. Simple属性

  一个简单属性表示一个伴随有一对get/set方法(C语言de过程或函数在Java程序中称为”方法”)de变量.属性名与和该属性相关deget/set方法名对应.例如:如果有setX和getX方法,则暗指有一个名为”X”de属性.如果有一个方法名为isX,则通常暗指”X”是一个布尔属性(即Xde值为true或false).例如在下面这个程序中:
  public class alden1 extends Canvas {
  string ourString= “Hello”; //属性名为ourString,类型为字符串
  public alden1(){     //alden1()是alden1de构造函数,
  与C 中构造函数de意义相同
  setBackground(Color.red);
  setForeground(Color.blue);
  }
  /* “set”属性*/
  public void setString(String newString) {
  ourString=newString;
  }
  /* “get”属性 */
  public String getString() {
  return ourString;
  }
  }
   2. Indexed属性

  一个Indexed属性表示一个数组值.使用与该属性对应deset/get方法可取得数组中de数值.该属性也可一次设置或取得整个数组de值.例:
  public class alden2 extends Canvas {
  int[] dataSet={1,2,3,4,5,6}; // dataSet是一个indexed属性
  public alden2() {
  setBackground(Color.red);
  setForeground(Color.blue);
  }
  /* 设置整个数组 */
  public void setDataSet(int[] x){
  dataSet=x;
  }
  /* 设置数组中de单个元素值 */
  public void setDataSet(int index, int x){
  dataSet[index]=x;
  }
  /* 取得整个数组值 */
  public int[] getDataSet(){
  return dataSet;
  }
  /* 取得数组中de指定元素值 */
  public int getDataSet(int x){
  return dataSet[x];
  }
  }
  3. Bound属性

  一个Bound属性是指当该种属性de值发生变化时,要通知其它de对象.每次属性值改变时,这种属性就点火一个PropertyChange事件(在Java程序中,事件也是一个对象).事件中封装了属性名、属性de原值、属性变化后de新值.这种事件是传递到其它deBeans,至于接收事件deBeans应做什么动作由其自己定义.当PushButtondebackground属性与Dialogdebackground属性bind时,若PushButtondebackground属性发生变化时,Dialogdebackground属性也发生同样de变化. 例:
  public class alden3 extends Canvas{
  String ourString= “Hello”;
  //ourString是一个bound属性
  private PropertyChangeSupport changes = new PropertyChangeSupport(this);
  /** 注:Java是纯面向对象de语言,
  如果要使用某种方法则必须指明是要使用哪个对象de方法,
  在下面de程序中要进行点火事件de操作,
  这种操作所使用de方法是在PropertyChangeSupport类中de.
  所以上面声明并实例化了一个changes对象,
  在下面将使用changesdefirePropertyChange方法来点火ourStringde属性改变事件.*/
  public void setString(string newString){
  String oldString = ourString;
  ourString = newString;
  /* ourStringde属性值已发生变化,于是接着点火属性改变事件 */
  changes.firePropertyChange(”ourString”,oldString,newString);
  }
  public String getString(){
  return ourString;
  }
  /** 以下相关代码是为开发工具所使用de.
  我不能预知alden3将与哪些其它deBeans组合成为一个应用,
  无法预知若alden3deourString属性发生变化时有哪些其它de组件与此变化有关,
  因而alden3这个Beans要预留出一些接口给开发工具,
  开发工具使用这些接口,
  把其它deJavaBeans对象与alden3挂接.*/
  public void addPropertyChangeListener(PropertyChangeLisener l){
  changes.addPropertyChangeListener(l);
  }
  public void removePropertyChangeListener(PropertyChangeListener l){
  changes.removePropertyChangeListener(l);
  }
  通过上面de相关代码,开发工具调用changesdeaddPropertyChangeListener方法,把其它JavaBeans注册入ourString属性de监听者队列l中,l是一个Vector数组,可存储任何Java对象.
  开发工具也可使用changesderemovePropertyChangeListener方法,从l中注销指定de对象,使alden3deourString属性de改变不再与这个对象有关.
  当然,当程序员手写相关代码编制程序时,也可直接调用这两个方法,把其它Java对象与alden3挂接.

  4. Constrained属性

一个JavaBeansdeconstrained属性,是指当这个属性de值要发生变化时,与这个属性已建立了某种连接de其它Java对象可否决属性值de改变.constrained属性de监听者通过抛出PropertyVetoException来阻止该属性值de改变.例:下面程序中deconstrained属性是PriceInCents.
  public class JellyBeans extends Canvas{
  private PropertyChangeSupport changes=new PropertyChangeSupport(this);
  private VetoableChangeSupport Vetos=new VetoableChangeSupport(this);
  /*与前述changes相同,
  可使用VetoableChangeSupport对象de实例Vetos中de方法,
  在特定条件下来阻止PriceInCents值de改变.*/
  ……
  public void setPriceInCents(int newPriceInCents) throws PropertyVetoException {
  /*方法名中throws PropertyVetoExceptionde作用是当有
  其它Java对象否决PriceInCentsde改变时,
  要抛出例外.*/
  /* 先保存原来de属性值*/
  int oldPriceInCents=ourPriceInCents;
  /**点火属性改变否决事件*/
  vetos.fireVetoableChange(”priceInCents”,new Integer(OldPriceInCents),new Integer(newPriceInCents));
  /**若有其它对象否决priceInCentsde改变,
  则程序抛出例外,不再继续执行下面de两条语句,
  方法结束.若无其它对象否决priceInCentsde改变,
  则在下面de相关代码中把ourPriceIncents赋予新值,
  并点火属性改变事件*/
  ourPriceInCents=newPriceInCents;
  changes.firePropertyChange(”priceInCents”, new Integer(oldPriceInCents), new Integer(newPriceInCents));
  }
  /**与前述changes相同,
  也要为PriceInCents属性预留接口,
  使其它对象可注册入PriceInCents否决改变监听者队列中,
  或把该对象从中注销
  public void addVetoableChangeListener(VetoableChangeListener l)
  {
  vetos.addVetoableChangeListener(l);
  }
  public void removeVetoableChangeListener(VetoableChangeListener l){
  vetos.removeVetoableChangeListener(l);
  }
  ……
  }
  从上面de例子中可看到,一个constrained属性有两种监听者:属性变化监听者和否决属性改变de监听者.否决属性改变de监听者在自己de对象相关代码中有相应de控制语句,在监听到有constrained属性要发生变化时,在控制语句中判断是否应否决这个属性值de改变.
  总之,某个Beansdeconstrained属性值可否改变取决于其它deBeans或者是Java对象是否允许这种改变.允许与否de条件由其它deBeans或Java对象在自己de类中进行定义.
  JavaBeansde事件

  事件处理是JavaBeans体系结构de核心之一.通过事件处理机制,可让一些组件作为事件源,发出可被描述环境或其它组件接收de事件.这样,不同de组件就可在构造工具内组合在一起,组件之间通过事件de传递进行通信,构成一个应用.从概念上讲,事件是一种在”源对象”和”监听者对象”之间,某种状态发生变化de传递机制.事件有许多不同de用途,例如在Windows系统中常要处理de鼠标事件、窗口边界改变事件、键盘事件等.在Java和JavaBeans中则是定义了一个一般de、可扩充de事件机制,这种机制能够:
  ·对事件类型和传递de模型de定义和扩充提供一个公共框架,并适合于广泛de应用.
  ·与Java语言和环境有较高de集成度.
  ·事件能被描述环境捕获和点火.
  ·能使其它构造工具采取某种技术在设计时直接控制事件,以及事件源和事件监听者之间de联系.
  ·事件机制本身不依赖于复杂de开发工具.特别地,还应当:
  ·能够发现指定de对象类可以生成de事件.
  ·能够发现指定de对象类可以观察(监听)到de事件.
  ·提供一个常规de注册机制,允许动态操纵事件源与事件监听者之间de关系.
  ·不需要其它de虚拟机和语言即可实现.
  ·事件源与监听者之间可进行高效de事件传递.
  ·能完成JavaBeans事件模型与相关de其它组件体系结构事件模型de中立映射.
  JavaBeans事件模型de主要构成有: 事件从事件源到监听者de传递是通过对目标监听者对象deJava方法调用进行de.对每个明确de事件de发生,都相应地定义一个明确deJava方法.这些方法都集中定义在事件监听者(EventListener)接口中,这个接口要继承java.util.EventListener.实现了事件监听者接口中一些或全部方法de类就是事件监听者. 伴随着事件de发生,相应de状态通常都封装在事件状态对象中,该对象必须继承自java.util.EventObject.事件状态对象作为单参传递给应响应该事件de监听者方法中. 发出某种特定事件de事件源de标识是:遵从规定de设计格式为事件监听者定义注册方法,并接受对指定事件监听者接口实例de引用. 有时,事件监听者不能直接实现事件监听者接口,或者还有其它de额外动作时,就要在一个源与其它一个或多个监听者之间插入一个事件适配器类de实例,来建立它们之间de联系.

   事件状态对象(Event State Object)

  与事件发生有关de状态信息一般都封装在一个事件状态对象中,这种对象是java.util.EventObjectde子类.按设计习惯,这种事件状态对象类de名应以Event结尾.例如:
  public class MouseMovedExampleEvent extends java.util.EventObject
  {
  protected int x, y;
  /* 创建一个鼠标移动事件MouseMovedExampleEvent */
  MouseMovedExampleEvent(java.awt.Component source, Point location) {
  super(source);
  x = location.x;
  y = location.y;
  }
  /* 获取鼠标位置*/
  public Point getLocation() {
  return new Point(x, y);
  }
  }
  事件监听者接口(EventListener Interface)与事件监听者
  由于Java事件模型是基于方法调用,因而需要一个定义并组织事件操纵方法de方式.JavaBeans中,事件操纵方法都被定义在继承了java.util.EventListener类deEventListener接口中,按规定,EventListener接口de命名要以Listener结尾.任何一个类如果想操纵在EventListener接口中定义de方法都必须以实现这个接口方式进行.这个类也就是事件监听者.例如:
  /*先定义了一个鼠标移动事件对象*/
  public class MouseMovedExampleEvent
  extends java.util.EventObject {
  // 在此类中包含了与鼠标移动事件有关de状态信息
     …
  }
  /*定义了鼠标移动事件de监听者接口*/
  interface MouseMovedExampleListener
  extends java.util.EventListener {
  /*在这个接口中定义了鼠标移动事件监听者所应支持de方法*/
  void mouseMoved(MouseMovedExampleEvent mme);
  }
  在接口中只定义方法名,方法de参数和返回值类型. 如:上面接口中demouseMoved方法de具体实现是在下面deArbitraryObject类中定义de.
  class ArbitraryObject implements MouseMovedExampleListener {
  public void mouseMoved(MouseMovedExampleEvent mme)
   { … }
  }
  ArbitraryObject就是MouseMovedExampleEvent事件de监听者.
  事件监听者de注册与注销
  为了各种可能de事件监听者把自己注册入合适de事件源中,建立源与事件监听者间de事件流,事件源必须为事件监听者提供注册和注销de方法.在前面debound属性介绍中已看到了这种使用过程,在实际中,事件监听者de注册和注销要使用标准de设计格式:
  public void add< ListenerType>(< ListenerType> listener);
  public void remove< ListenerType>(< ListenerType> listener);
  例如:
  首先定义了一个事件监听者接口:
  public interface
  ModelChangedListener extends java.util.EventListener {
  void modelChanged(EventObject e);
  }
  接着定义事件源类:
  public abstract class Model {
  private Vector listeners = new Vector(); // 定义了一个储存事件监听者de数组
  /*上面设计格式中de< ListenerType>在此处即是下面deModelChangedListener*/
  public synchronized void addModelChangedListener(ModelChangedListener mcl)
  { listeners.addElement(mcl); }//把监听者注册入listeners数组中
  public synchronized void removeModelChangedListener(ModelChangedListener mcl)
    { listeners.removeElement(mcl); //把监听者从listeners中注销
    }
  /*以上两个方法de前面均冠以synchronized,
  是因为运行在多线程环境时,
  可能同时有几个对象同时要进行注册和注销操作,
  使用synchronized来确保它们之间de同步.
  开发工具或程序员使用这两个方法建立源与监听者之间de事件流*/
  protected void notifyModelChanged() {
  /**事件源使用本方法通知监听者发生了modelChanged事件*/
    Vector l;
    EventObject e = new EventObject(this);
  /* 首先要把监听者拷贝到l数组中,
  冻结EventListenersde状态以传递事件.
  这样来确保在事件传递到所有嗵咧埃?
  已接收了事件de目标监听者de对应方法暂不生效.*/
    synchronized(this) {
      l = (Vector)listeners.clone();
    }
    for (int i = 0; i < l.size(); i ) {
     /* 依次通知注册在监听者队列中de每个监听者发生了modelChanged事件,
     并把事件状态对象e作为参数传递给监听者队列中de每个监听者*/
  ((ModelChangedListener)l.elementAt(i)).modelChanged(e);
    }
    }
   }
  在程序中可见事件源Model类显式地调用了接口中demodelChanged方法,实际是把事件状态对象e作为参数,传递给了监听者类中demodelChanged方法.

   适配类

  适配类是Java事件模型中极其重要de一部分.在一些应用场合,事件从源到监听者之间de传递要通过适配类来”转发”.例如:当事件源发出一个事件,而有几个事件监听者对象都可接收该事件,但只有指定对象做出反应时,就要在事件源与事件监听者之间插入一个事件适配器类,由适配器类来指定事件应该是由哪些监听者来响应.
  适配类成为了事件监听者,事件源实际是把适配类作为监听者注册入监听者队列中,而真正de事件响应者并未在监听者队列中,事件响应者应做de动作由适配类决定.目前绝大多数de开发工具在生成相关代码时,事件处理都是通过适配类来进行de.
  JavaBeans用户化
  JavaBeans开发者可以给一个Beans添加用户化器(Customizer)、属性编辑器(PropertyEditor)和BeansInfo接口来描述一个Beansde内容,Beansde使用者可在构造环境中通过与Beans附带在一起de这些信息来用户化Beansde外观和应做de动作.一个Beans不必都有BeansCustomizer、PrpertyEditor和BeansInfo,根据实际情况,这些是可选de,当有些Beans较复杂时,就要提供这些信息,以Wizardde方式使Beansde使用者能够用户化一个Beans.有些简单deBeans可能这些信息都没有,则构造工具可使用自带de透视装置,透视出Beansde内容,并把信息显示到标准de属性表或事件表中供使用者用户化Beans,前几节提到deBeansde属性、方法和事件名要以一定de格式命名,主要de作用就是供开发工具对Beans进行透视.当然也是给程序员在手写程序中使用Beans提供方便,使他能观其名、知其意.
  用户化器接口(Customizer Interface)
  当一个Beans有了自己de用户化器时,在构造工具内就可展现出自己de属性表.在定义用户化器时必须要实现java.Beanss.Customizer接口.例如,下面是一个”按钮”Beansde用户化一器:
  public class OurButtonCustomizer
  extends Panel implements Customizer {
  … …
  /*当实现象OurButtonCustomizer这样de常规属性表时,
  一定要在其中实现addProperChangeListener
  和removePropertyChangeListener,这样,
  构造工具可用这些功能相关代码为属性事件添加监听者.*/
  … …
  private PropertyChangeSupport changes=new PropertyChangeSupport(this);
  public void addPropertyChangeListener(PropertyChangeListener l) {
  changes.addPropertyChangeListener(l);
  public void removePropertyChangeListener(PropertyChangeListener l) {
   changes.removePropertyChangeListener(l);
  }
  … …
  属性编辑器接口(PropertyEditor Interface)

  一个JavaBeans可提供PropertyEditor类,为指定de属性创建一个编辑器.这个类必须继承自java.Beanss.PropertyEditorSupport类.构造工具与手写相关代码de程序员不直接使用这个类,而是在下一小节deBeansInfo中实例化并调用这个类.例:
  public class MoleculeNameEditor extends java.Beanss.PropertyEditorSupport {
  public String[] getTags() {
  String resule[]={
   ”HyaluronicAcid”,”Benzene”,”buckmisterfullerine”, “cyclohexane”,”ethane”,”water”};
  return resule;}
  }
  上例中是为Tags属性创建了属性编辑器,在构造工具内,可从下拉表格中选择MoleculeNamede属性应是”HyaluronicAid”或是”water”.
  BeansInfo接口
  每个Beans类也可能有与之相关deBeansInfo类,在其中描述了这个Beans在构造工具内出现时de外观.BeansInfo中可定义属性、方法、事件,显示它们de名称,提供简单de帮助说明. 例如:
  public class MoleculeBeansInfo extends SimpleBeansInfo {
  public PropertyDescriptor[] getPropertyDescriptors() {
  try {
   PropertyDescriptor pd=new PropertyDescriptor(”moleculeName”,Molecule.class);
   /*通过pd引用了上一节deMoleculeNameEditor类,取得并返回moleculeName属性*/
   pd.setPropertyEditorClass(MoleculeNameEditor.class);
   PropertyDescriptor result[]={pd};
   return result;
  } catch(Exception ex) {
   System.err.println(”MoleculeBeansInfo: unexpected exeption: ” ex);
   return null;
  }
  }
  }
  JavaBeans持久化

  当一个JavaBeans在构造工具内被用户化,并与其它Beans建立连接之后,它de所有状态都应当可被保存,下一次被load进构造工具内或在运行时,就应当是上一次修改完de信息.为了能做到这一点,要把Beansde某些字段de信息保存下来,在定义Beans时要使它实现java.io.Serializable接口.例如:
  public class Button
  implements java.io.Serializable {}
  实现了序列化接口deBeans中字段de信息将被自动保存.若不想保存某些字段de信息则可在这些字段前冠以transient或static关键字,transient和static变量de信息是不可被保存de.通常,一个Beans所有公开出来de属性都应当是被保存de,也可有选择地保存内部状态. Beans开发者在修改软件时,可以添加字段,移走对其它类de引用,改变一个字段deprivate/protected/public状态,这些都不影响类de存储结构关系.然而,当从类中删除一个字段,改变一个变量在类体系中de位置,把某个字段改成transient/static,或原来是transient/static,现改为别de特性时,都将引起存储关系de变化.
  JavaBeansde存储格式
  JavaBeans组件被设计出来后,一般是以扩展名为jardeZip格式文件存储,在jar中包含与JavaBeans有关de信息,并以MANIFEST文件指定其中de哪些类是JavaBeans.以jar文件存储deJavaBeans在网络中传送时极大地减少了数据de传输数量,并把JavaBeans运行时所需要de一些资源捆绑在一起,本章主要论述了JavaBeansde一些内部特性及其常规设计方法,参考de是JavaBeans规范1.0A版本.随着世界各大ISV对JavaBeans越来越多de支持,规范在一些细节上还在不断演化,但基本框架不会再有大de变动.
  

使用JSP JAVABEAN XML 开发的一个例子

星期一, 06月 2nd, 2008

本例子是参考了一些网站上有关JSP 对 XML de操作de相关文档,又结合了一些个人de体会.例子涉及de内容是,开发de一个企业内部定餐系统后台管理端de部分相关代码,功能主要集中在对于餐馆基本信息de管理.
该例子本身开发de起因是我在原公司和同事们一个玩笑de一部分.特此也表达对那些一起共事de朋友们de想念.
例子本身是在TOMCAT4.01 平台下运行deB/S结构de程式.有关TOMCAT de配置,这里不做说明.只讲解一下相关文件及文件夹de目录结构.
目录结构说明:
/tomcat/webapps/canyin/ —–主目录
/tomcat/webapps/canyin/jsp/ —–JSP 文件目录
/tomcat/webapps/canyin/jsp/admin/ —–实现后台管理deJSP 文件de存放目录
/tomcat/webapps/canyin/WEB-INF/classes/canyin/ ——javabean 文件de存放目录
/tomcat/webapps/canyin/data/ —–xml 文件存放目录
/tomcat/webapps/ROOT/ —–tomcat 启动文件存放文件夹,只存放了index.html 文件
文件简单说明:
/tomcat/webapps/canyin/data/users.xml —–记录用户信息
/tomcat/webapps/canyin/data/restaurants.xml —–记录餐馆de基础信息

/tomcat/webapps/ROOT/index.html —–首页,页面出现输入框,要求用户输入用户名,密码

/tomcat/webapps/canyin/jsp/loginjudge.jsp —–用户身份判断页面,根据用户名称和密码决定页面是转入后台管理端,还是前台客户端.本例子中,用户身份一旦确认为有管理权限,可以进入后台管理端,就直接跳到餐馆基本信息管理页面,简化说明de流程.
/tomcat/webapps/canyin/jsp/admin/admin_rest.jsp —–餐馆基本信息管理页面,管理餐馆de名称,电话,地址等信息
/tomcat/webapps/canyin/WEB-INF/classes/canyin/checkSessionBean.class —– 后台管理端检测标志用户身份desession de值,如果不是管理员de话,跳回登陆页面.
/tomcat/webapps/canyin/WEB-INF/classes/canyin/connXmlBean.class —–连接xml 文件
/tomcat/webapps/canyin/WEB-INF/classes/canyin/writeXmlBean.class —–写入xml文件
文件详细介绍及附带相关代码说明.
/tomcat/webapps/canyin/data/users.xml
相关代码:
<?xml version=”1.0″ encoding=”UTF-8″ ?>
- <users>
<user name=”joard” password=”joard” roles=”admin” />
<user name=”joard01″ password=”joard01″ roles=”user” />
<user name=”joard02″ password=”joard02″ roles=”user” />
</users>
说明:字段含义是用户名,密码以及用户de身份
/tomcat/webapps/canyin/data/restaurants.xml
相关代码:
<?xml version=”1.0″ encoding=”UTF-8″ ?>
- <restaurants num=”10″>
- <restaurant id=”1″>
<name>上海亭快餐店</name>
<phone>021-76546726</phone>
<address>百老汇广场B座</address>
</restaurant>
- <restaurant id=”8″>
<name>香格里拉大饭店</name>
<phone>021-2312134</phone>
<address>南京路1023号</address>
</restaurant>
</restaurants>
说明:<num>属性是记录在restaurants.xml 文件中总共有过多少条记录,每新增一条,无论以后删除是否,该值都会增加1,就好象数据库中习惯使用de自动增加1deid 项.用来给新增de <restaurant>de属性<id>赋一个唯一de值.其它de字段意思比较明显.
/tomcat/webapps/ROOT/index.html (单纯deHTML相关代码)
相关代码:
<html>
<head>
<title>oddWorld 餐饮系统</title>
<meta http-equiv=”Content-Type” content=”text/html; charset=gb2312″>
</head>
<body onload=”javascript:dataform.username.focus()”>
<div align=”center”>
<table width=”100%” border=”0″ cellspacing=”0″ cellpadding=”0″ height=”22″>
<tr>
<td width=”1″><img src=”images/top_r1.GIF” width=”62″ height=”22″></td>
<td width=150 align=”center”> 餐饮系统登录 </td>
<td><img src=”images/top_r2.GIF” width=”294″ height=”22″></td>
</tr>
</table>
<br>
<br>
<table width=”300″ border=”0″ cellspacing=”1″ cellpadding=”0″ >
<tr>
<td height=”200″ valign=”top” align=”center”>
<p align=”center”>
<table width=”100%” border=”0″ cellspacing=”1″ cellpadding=”5″ bgcolor=#999999 class=a9px>
<tr>
<td bgcolor=”#efefef”>餐饮系统登录</td>
</tr>
<tr>
<td bgcolor=”#FFFFFF” valign=”top” align=”center”>
<table width=”100%” border=”0″ cellspacing=”0″ cellpadding=”0″>
<form name=dataform method=post action=”canyin/jsp/loginjudge.jsp”>
<tr>
<td width=”100″><b>登录名:</b></td>
<td>
<input maxlength=16
name=”username” class=stedit value=”joard”>
</td>
</tr>
<tr>
<td width=”100″><b>密码:</b></td>
<td>
<input class=stedit maxlength=16
name=”userpass” type=password value=”oddworld”>
</td>
</tr>
</form>
</table>
<br>
<table border=0 cellpadding=0 cellspacing=0>
<tbody>
<tr>
<td>
<input class=stbtm name=update onClick=”javascript:if (checkform()==false);” type=button value=”登 录”>
</td>
<td> </td>
<td>
<input class=stbtm name=Submit onClick=”javascript:window.location.href=”index.asp?myjoke=1”;” type=button value=”修改密码”>
</td>
<td> </td>
</tr>
</tbody>
</table>
<br>
</td>
</tr>
</table>
</td>
</tr>
</table>
</div>
</body>
</html>
<SCRIPT language=javascript>
<!–
function checkform()
{
var Checkblank = /^(\s*|(\ )|(\.))*$/;
if (Checkblank.test(dataform.username.value))
{
alert(”登录名不能为空!”);
return false;
}
if (Checkblank.test(dataform.userpass.value))
{
alert(”密码不能为空!”);
return false;
}

window.dataform.submit();
}
–>
</SCRIPT>
说明:把用户名称和用户密码提交到/tomcat/webapps/canyin/jsp/loginjudge.jsp
/tomcat/webapps/canyin/WEB-INF/classes/canyin/checkSessionBean.class (相关代码是相应dejava 文件)
package canyin;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpServletRequest;
public class checkSessionBean {
private boolean bolCheckPass=false;
private HttpServletRequest request = null;
public boolean checkSessionBean(HttpServletRequest request,String strSessionName,String strCheckValue){
public boolean checkSessionBean(HttpServletRequest request){
HttpSession session = request.getSession(false);
return(bolCheckPass);
if (strSessionName==null || strCheckValue==null){
return(bolCheckPass);
}else{
if (session!=null && session.getValue(strSessionName)!=null){
bolCheckPass=session.getValue(strSessionName).equals(strCheckValue);
}
return(bolCheckPass);
}
}
}
说明:检验参数传入desession 名称de数值和参数传入de字段de数值是否相等.
/tomcat/webapps/canyin/WEB-INF/classes/canyin/connXmlBean.class
相关代码:
package canyin;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.*;
public class connXmlBean {
private DocumentBuilderFactory factory=null;
private DocumentBuilder builder=null;
private Document doc=null;
public connXmlBean(){}
public String connXml(String xmlFileName){
String strExc=”";
try{
factory = DocumentBuilderFactory.newInstance();
builder=factory.newDocumentBuilder();
doc=builder.parse(xmlFileName);
doc.normalize();
}catch(Exception e){
strExc=e.toString();
}
return(strExc);
}
public Document getXmlDoc(){
return(doc);
}
}
说明:打开一个指定xml 文件
/tomcat/webapps/canyin/WEB-INF/classes/canyin/writeXmlBean.class
相关代码:
package canyin;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.File;
import org.w3c.dom.*;
public class writeXmlBean {
public writeXmlBean(){}
public String writeXml(Document doc,String xmlFileName){
String strExc=”";
try{
TransformerFactory tfactory = TransformerFactory.newInstance();
Transformer transformer = tfactory.newTransformer();
DOMSource source = new DOMSource(doc);
StreamResult result = new StreamResult(new File(xmlFileName));
transformer.transform(source,result);
}catch(Exception e){
strExc=e.toString();
}
return(strExc);
}
}
说明:写入dom de内容到一个指定dexml 文件.
/tomcat/webapps/canyin/jsp/loginjudge.jsp
相关代码:
<%– oddWorld 餐饮管理系统(简体中文版) 2002年12月1日
copy right by joard ast
loginjudge.jsp 功能:用户身份校验,根据 /data/user.xml 文件内标示de用户不同de身份
决定转入后台管理页面,还是客户点菜页面.
–%>
<%@ page contentType=”text/html;charset=gb2312″ %>
<%@ page language=”java” import=”javax.xml.parsers.*” %>
<%@ page import=”org.w3c.dom.*” %>
<%@ page import=”canyin.*” %>
<jsp:useBean id=”xmlBean” class=”canyin.connXmlBean” scope=”page” />
<%
session.setMaxInactiveInterval(1800);

Document doc;
NodeList users;
String strExc=”";
String strUsername,strPassword;
strUsername=(String)request.getParameter(”username”);
strPassword=(String)request.getParameter(”userpass”);
//校验数据是否为空
if (strUsername==”" || strPassword==”" ){
out.println(”<script language=”javascript”>”);
out.println(”alert(”用户名或密码有空值!”);”);
out.println(”window.location.href=”/index.html”;”);
out.println(”</script>”);
return;
}
xmlBean.connXml(”webapps/canyin/data/users.xml”);
doc=xmlBean.getXmlDoc();
try{
users =doc.getElementsByTagName(”user”);
for (int i=0;i<users.getLength();i ){
Element user=(Element) users.item(i);
String strAtrNameValue=user.getAttributeNode(”name”).getNodeValue();
String strAtrPassWordValue=user.getAttributeNode(”password”).getNodeValue();
String strAtrRoleValue=user.getAttributeNode(”roles”).getNodeValue();

if (strAtrNameValue.equals(strUsername) && strAtrPassWordValue.equals(strPassword)){
if (strAtrRoleValue.equals(”admin”)){
out.println(”<script language=”javascript”>”);
out.println(”alert(”欢迎管理员登陆系统!”);”);
out.println(”</script>”);
//设置标示用户身份de session(sesUserRole) ,管理员身份为 admin
session.setAttribute(”sesUserRole”,”admin”);
//跳转到管理页面
response.sendRedirect(”admin/admin_rest.jsp”);
return;
}else{
//设置标示用户身份de session(sesUserRole) ,管理员身份为 user
session.setAttribute(”sesUserRole”,”user”);
//跳转到普通用户页面
response.sendRedirect(”index.jsp”);
return;
}
}else{
out.println(”<script language=”javascript”>”);
out.println(”alert(”用户名或密码错误!”);”);
out.println(”history.go(-1);”);
out.println(”</script>”);
return;
}
}
}catch(Exception e){
strExc=e.toString();
}
%>
说明:…….
/tomcat/webapps/canyin/jsp/admin/admin_rest.jsp
相关代码:
<%– oddWorld 餐饮管理系统(简体中文版) 2002年12月1日
copy right by joard ast
admin_rest.jsp 功能:后台管理页面,餐馆管理页面.
–%>
<%@ page contentType=”text/html;charset=gb2312″ %>
<%@ page language=”java” import=”javax.xml.parsers.*” %>
<%@ page import=”javax.xml.transform.*” %>
<%@ page import=”org.w3c.dom.*” %>
<%@ page import=”canyin.*” %>
<%@ include file=”../../include/sys_dialog.jsp” %>
<jsp:useBean id=”checkSessionBean” class=”canyin.checkSessionBean” scope=”page” />
<jsp:useBean id=”xmlBean” class=”canyin.connXmlBean” scope=”page” />
<jsp:useBean id=”writeXmlBean” class=”canyin.writeXmlBean” scope=”page” />
<%//校验可户身份,判断是不是管理员
if(!checkSessionBean.checkSessionBean(request,”sesUserRole”,”admin”)){
out.print(showDialog(”您没有管理de权限!”,”/index.html”));
return;
}
//从餐馆资料文件 rest.xml 中得到相关数据
Document doc;
NodeList restaurants;
String strAct;
int intId=0;
String strOperation=”show”;
//接受外部传入de参数
strAct=(String)request.getParameter(”act”);
xmlBean.connXml(”webapps/canyin/data/restaurants.xml”);
doc=xmlBean.getXmlDoc();
restaurants =doc.getElementsByTagName(”restaurant”);
//根据外部传入de参数来决定对 restaurant.xml 文件de操作
if (strAct!=null){
if(strAct.equals(”addnewDo”)){
String strName;
String strPhone;
String strAddress;
Text textseg;
strName=(String)request.getParameter(”name”).trim();
strPhone=(String)request.getParameter(”phone”).trim();
strAddress=(String)request.getParameter(”address”).trim();
//数据校验
if(strName==null){
out.print(showDialog(”餐馆名称不能为空!”));
return;
}
if(strPhone==null){
out.print(showDialog(”餐馆电话不能为空!”));
return;
}
/*if(strAddress==null){
out.print(showDialog(”餐馆地址不能为空!”));
return;
}*/
//校验数据de唯一性
for(int i=0;i<restaurants.getLength();i ){
Element restaurant=(Element) restaurants.item(i);
if(((String)restaurant.getElementsByTagName(”name”).item(0).getFirstChild().getNodeValue()).equals(strName)){
out.print(showDialog(”餐馆名称重复!”));
return;
}else{
if(((String)restaurant.getElementsByTagName(”name”).item(0).getFirstChild().getNodeValue()).equals(strPhone)){
out.print(showDialog(”餐馆电话重复!”));
return;
}
}
}

//得到已有de记录数,给新增de餐馆记录设定唯一de递增deid 属性
int intNum=0;
Element restNum=(Element)doc.getElementsByTagName(”restaurants”).item(0);
intNum=Integer.parseInt(restNum.getAttributeNode(”num”).getNodeValue());
intNum =1;
//为restaurantsde属性num de数值加1
restNum.getAttributeNode(”num”).setNodeValue(String.valueOf(intNum));
//新增节点
Element newRestaurant=doc.createElement(”restaurant”);
Attr newArrId=doc.createAttribute(”id”);
//Attribute newArrId = new Attribute(”id”,String.valueOf(intNum));
textseg=doc.createTextNode(String.valueOf(intNum));
newArrId.setValue(String.valueOf(intNum));
newRestaurant.setAttributeNode(newArrId);
Element newName=doc.createElement(”name”);
textseg=doc.createTextNode(strName);
newName.appendChild(textseg);
newRestaurant.appendChild(newName);
Element newPhone=doc.createElement(”phone”);
textseg=doc.createTextNode(strPhone);
newPhone.appendChild(textseg);
newRestaurant.appendChild(newPhone);
Element newAddress=doc.createElement(”address”);
textseg=doc.createTextNode(strAddress);
newAddress.appendChild(textseg);
newRestaurant.appendChild(newAddress);
doc.getDocumentElement().appendChild(newRestaurant);
//调用bean 写入相应dexml文件
writeXmlBean.writeXml(doc,”webapps/canyin/data/restaurants.xml”);
response.sendRedirect(request.getRequestURI());
return;
}
if(strAct.equals(”modiDo”)){
String strName;
String strPhone;
String strAddress;
Text textseg;
int modiId;
//记录要修改de记录是item(i)de哪一项
int intI=0;
strName=(String)request.getParameter(”name”).trim();
strPhone=(String)request.getParameter(”phone”).trim();
strAddress=(String)request.getParameter(”address”).trim();
modiId=Integer.parseInt(request.getParameter(”recordId”).trim());
//数据校验
if(strName==null){
out.print(showDialog(”餐馆名称不能为空!”));
return;
}
if(strPhone==null){
out.print(showDialog(”餐馆电话不能为空!”));
return;
}
if(modiId==0){
out.print(showDialog(”您要修改餐馆de记录不存在!”));
return;
}
/*if(strAddress==null){
out.print(showDialog(”餐馆地址不能为空!”));
return;
}*/
//标志显示记录存在
boolean recordExist=false;
//校验数据de唯一性
for(int i=0;i<restaurants.getLength();i ){
Element restaurant=(Element) restaurants.item(i);
if(Integer.parseInt(restaurant.getAttributeNode(”id”).getNodeValue())==modiId){
recordExist=true;
intI=i;
}
if(((String)restaurant.getElementsByTagName(”name”).item(0).getFirstChild().getNodeValue()).equals(strName) && Integer.parseInt(restaurant.getAttributeNode(”id”).getNodeValue())!=modiId ){
out.print(showDialog(”餐馆名称重复!”));
return;
}else{
if(((String)restaurant.getElementsByTagName(”name”).item(0).getFirstChild().getNodeValue()).equals(strPhone) && Integer.parseInt(restaurant.getAttributeNode(”id”).getNodeValue())!=modiId ){
out.print(showDialog(”餐馆电话重复!”));
return;
}
}
}

if(!recordExist){
out.print(showDialog(”您要修改餐馆de记录不存在!”));
return;
}else{
//进行记录更改de操作
try{
Element modiRestaurant=(Element) restaurants.item(intI);
modiRestaurant.getElementsByTagName(”name”).item(0).getFirstChild().setNodeValue(strName);
modiRestaurant.getElementsByTagName(”phone”).item(0).getFirstChild().setNodeValue(strPhone);
modiRestaurant.getElementsByTagName(”address”).item(0).getFirstChild().setNodeValue(strAddress);
//调用bean 写入相应dexml文件
writeXmlBean.writeXml(doc,”webapps/canyin/data/restaurants.xml”);
response.sendRedirect(request.getRequestURI());
return;
}catch(Exception e){}
}
}
//进行删除操作
if(strAct.equals(”del”)){
int delId;
//记录要修改de记录是item(i)de哪一项
int intI=0;
delId=Integer.parseInt(request.getParameter(”recordId”).trim());
if(delId==0){
out.print(showDialog(”您要修改餐馆de记录不存在!”));
return;
}
file://标志显示记录存在
boolean recordExist=false;
//校验数据de唯一性
for(int i=0;i<restaurants.getLength();i ){
Element restaurant=(Element) restaurants.item(i);
if(Integer.parseInt(restaurant.getAttributeNode(”id”).getNodeValue())==delId){
recordExist=true;
intI=i;
}
}
if(!recordExist){
out.print(showDialog(”您要删除餐馆de记录不存在!”));
return;
}else{
//进行记录删除de操作
try{
Node delNode=(Node)restaurants.item(intI);
doc.getElementsByTagName(”restaurants”).item(0).removeChild(delNode);
//调用bean 写入相应dexml文件
writeXmlBean.writeXml(doc,”webapps/canyin/data/restaurants.xml”);
response.sendRedirect(request.getRequestURI());
return;
}catch(Exception e){}
}
}
}
//由外部传入参数决定页面相应de处理状态
if (strAct==null){
strOperation=”show”;
}else{
if (strAct.equals(”modi”)){
strOperation=”modi”;
intId=Integer.parseInt(request.getParameter(”recordId”));
}else{
if(strAct.equals(”addnew”)){
strOperation=”addnew”;
}else{
strOperation=”show”;
}
}
}

//如果为空记录,则变更页面状态为“新增”
if (restaurants.getLength()==0){
strOperation=”addnew”;
}
%>
<html>
<head>
<title>oddWorld 餐饮系统</title>
<meta http-equiv=”Content-Type” content=”text/html; charset=gb2312″>
<meta http-equiv=”expires” content=”0″>
<link rel=”stylesheet” href=”../../include/itsp.css” type=”text/css”>
</head>
<body >
<div align=”center”>
<table width=”100%” border=”0″ cellspacing=”0″ cellpadding=”0″ height=”22″>
<tr>
<td width=”1″><img src=”../../images/top_r1.GIF” width=”62″ height=”22″></td>
<td width=150 align=”center”> 餐饮系统管理–餐馆管理</td>
<td><img src=”../../images/top_r2.GIF” width=”294″ height=”22″></td>
<td width=100 align=”center”><a href=”/index.html”>[ 退出系统 ]</a></td>
</tr>
</table>
<br>
<br>
<table bgcolor=”#999999″ align=center border=0 cellpadding=1 cellspacing=1
width=”90%”>
<tbody>
<tr bgcolor=”#efefef” align=”center” valign=”middle”>
<td class=ttTable height=30 width=”20″> </td>
<td class=ttTable height=30 width=”0″>餐馆名称</td>
<td class=ttTable height=30 width=”0″>餐馆电话</td>
<td class=ttTable height=30 width=”0″>
<div align=”center”>餐馆地址</div>
</td>
<td class=ttTable height=30 width=”30″>
<div align=”center”>修改</div>
</td>
<td class=ttTable height=30 width=”30″>
<div align=”center”>删除</div>
</td>
</tr>
<%
for(int i=0;i<restaurants.getLength();i )
{
Element restaurant=(Element) restaurants.item(i);
if (strOperation==”modi” && Integer.parseInt(restaurant.getAttributeNode(”id”).getNodeValue())==intId){
%>
<%//显示修改de格式%>
<tr align=”center” bgcolor=”#ffffff” valign=”middle”>
<form name=dataform action=”<%=request.getRequestURI()%>?act=modiDo” method=”post” onSubmit=”return checkform(this);” >
<td class=tdsmall height=25 width=”20″>
<input type=”hidden” name=”recordId” value=”<%=restaurant.getAttributeNode(”id”).getNodeValue()%>”>
<%=(i 1)%></td>
<td class=tdsmall height=25>
<input name=”name” class=stedit
style=”HEIGHT: 22px; WIDTH: 150px” value=”<%if(restaurant.getElementsByTagName(”name”).item(0).hasChildNodes()){
out.print(restaurant.getElementsByTagName(”name”).item(0).getFirstChild().getNodeValue());
}%>
” maxlength=”40″ >
</td>
<td class=tdsmall height=25>
<input name=”phone” class=stedit
style=”HEIGHT: 22px; WIDTH: 100px” value=”<%if(restaurant.getElementsByTagName(”phone”).item(0).hasChildNodes()){
out.print(restaurant.getElementsByTagName(”phone”).item(0).getFirstChild().getNodeValue());
}%>” maxlength=”20″ >
</td>
<td class=tdsmall height=25>
<input name=”address” class=stedit
style=”HEIGHT: 22px; WIDTH: 200px” value=”<%
if(restaurant.getElementsByTagName(”address”).item(0).hasChildNodes()){
out.print(restaurant.getElementsByTagName(”address”).item(0).getFirstChild().getNodeValue());
}%>” maxlength=”100″ >
</td>
<td class=tdsmall height=25 width=”25″><a href=”javascript:if (checkform()==false);”><img border=0
height=15 src=”../../images/editok.gif” width=15></a></td>
<td class=tdsmall height=25 width=”25″> </td>
</form>
</tr>
<% }else{
//显示正常de格式 %>
<tr align=”center” bgcolor=”#ffffff” valign=”middle”>
<td class=tdsmall height=25 width=”20″><%=(i 1)%></td>
<td class=tdsmall height=25 width=”0″><%if(restaurant.getElementsByTagName(”name”).item(0).hasChildNodes()){
out.print(restaurant.getElementsByTagName(”name”).item(0).getFirstChild().getNodeValue());
}%>
</td>
<td class=tdsmall height=25 width=”0″><%if(restaurant.getElementsByTagName(”phone”).item(0).hasChildNodes()){
out.print(restaurant.getElementsByTagName(”phone”).item(0).getFirstChild().getNodeValue());
}%></td>
<td class=tdsmall height=25 width=”0″>
<%
if(restaurant.getElementsByTagName(”address”).item(0).hasChildNodes()){
out.print(restaurant.getElementsByTagName(”address”).item(0).getFirstChild().getNodeValue());
}%>
</td>
<td class=tdsmall height=25 width=”30″><a href=”<%=request.getRequestURI()%>?act=modi&recordId=<%=restaurant.getAttributeNode(”id”).getNodeValue()%>”><img border=0
height=15 src=”../../images/edit.gif” width=15></a></td>
<td class=tdsmall height=25 width=”30″><img border=0
height=15
onClick=”javascript:if(confirm(”您是否确定删除本记录,删除后将导至记录无法使用?”)){window.location.href=”<%=request.getRequestURI()%>?act=del&recordId=<%=restaurant.getAttributeNode(”id”).getNodeValue()%>”;}”
src=”../../images/delete.gif” style=”CURSOR: hand” width=15> </td>
</tr>
<% }
}%>
<% if (strOperation==”addnew”){
//显示新增de格式%>
<tr align=”center” bgcolor=”#ffffff” valign=”middle”>
<form name=dataform2 action=”<%=request.getRequestURI()%>?act=addnewDo” method=”post” onSubmit=”return checkform2(this);” >
<td class=tdsmall height=25 width=”20″></td>
<td class=tdsmall height=25>
<input name=”name” class=stedit
style=”HEIGHT: 22px; WIDTH: 150px” value=”" maxlength=”40″ >
</td>
<td class=tdsmall height=25>
<input name=”phone” class=stedit
style=”HEIGHT: 22px; WIDTH: 100px” value=”" maxlength=”20″ >
</td>
<td class=tdsmall height=25>
<input name=”address” class=stedit
style=”HEIGHT: 22px; WIDTH: 200px” value=”" maxlength=”100″ >
</td>
<td class=tdsmall height=25 width=”25″><a href=”javascript:if (checkform2()==false);”><img border=0
height=15 src=”../../images/editok.gif” width=15></a></td>
<td class=tdsmall height=25 width=”25″> </td>
</form>
</tr>
<% } %>
</tbody>
</table>
<br>
<table align=center border=0 cellpadding=0 cellspacing=2 width=”95%”>
<tbody>
<tr valign=center>
<td align=middle> <br>
<table border=0 cellpadding=0 cellspacing=0>
<tr>
<td>
<% if (strOperation==”addnew”){
%>
<input class=stbtm name=update onClick=”javascript:if (checkform2()==false);” type=button value=”更新记录”>
<% }else{
if(strOperation==”modi”){
%>
<input class=stbtm name=update onClick=”javascript:if (checkform()==false);” type=button value=”更新记录”>
<%
}else{
%>
<input class=stbtm type=”button” name=”Button” value=”新 增” onClick=”javascript:window.location.href=”<%=request.getRequestURI()%>?act=addnew”;”><%
}
} %>
</td>
<td>
<input class=stbtm type=”button” name=”Button” value=”返 回” onClick=”javascript:window.location.href=”index.jsp”;”>
</td>
</tr>
</table>
</td>
</tr>
</table>
<p> </p>
</div>
</body>
</html>
<SCRIPT LANGUAGE=javascript>
<!–
function checkform2()
{
var Checkblank = /^(\s*|(\ )|(\.))*$/;
if (Checkblank.test(dataform2.name.value))
{
alert(”餐馆名称不能为空!”);
dataform2.name.focus();
return false;
}
if (Checkblank.test(dataform2.phone.value))
{
alert(”餐馆电话不能为空!”);
dataform2.phone.focus();
return false;
}
window.dataform2.submit();
}
function checkform()
{
var Checkblank = /^(\s*|(\ )|(\.))*$/;
if (Checkblank.test(dataform.name.value))
{
alert(”餐馆名称不能为空!”);
dataform.name.focus();
return false;
}
if (Checkblank.test(dataform.phone.value))
{
alert(”餐馆电话不能为空!”);
dataform.phone.focus();
return false;
}

window.dataform.submit();
}
–>
</SCRIPT>
说明:本文件de书写有很多地方并不简练,因为在程式de开发过程中,过分简练de程序往往会带来后期维护de困难.
开发心得:
doc.getElementsByTagName(”restaurants”).item(int i)de返回值是node 型,如果不是要调用它de属性值,没有必要强制转型为 Element型.可以直接操作.本系统因为开发de参考资料de错误,所以全都采用了强制转型.可以在以后de开发中考虑使用node 直接进行操作.
trim() 和 Interger.parseInt() 函数都不可以接受null 型de数值
在tomcat 下左右de文件都是目录从TOMCAT 算起,具体情况请参见\webapps\canyin\jsp\userjudge.jsp 里关于xml 路径de写法.

对原相关代码感兴趣de朋友请通过如下信箱和我联系,joard@163.com

JSP/JAVABEAN TOMCAT4.0.5 MYSQL组合建站总结

星期一, 06月 2nd, 2008

系统配置:win2000英文版 JDK1.4.1 TOMCAT4.0.5 APACHE2.043
开发周期:竭尽全力,前后历时近15天.
实现功能:
1. 画廊:
图片及相关文字de提交,分页显示,删除.
2. 相册:
相册主题de新建,相片及相关文字de提交,分主题,分页显示,删除.
3. 文章/新闻发布系统:
文章分类目录de新建,文章de分类显示,文章de编辑,删除.(具有10个插图上传能力)
4. 文章按主题搜索及模糊查找.
5. 将硬盘一指定目录以列表de形式显示出来,自动查找目录中de说明文件,并解析其结构,当访问到含有说明文件de目录时,将解析结果一并显示出来.
6. LEO论坛(CGI)运行环境de配置.
涉及知识点:
1. MYSQL数据库de连接BEAN.
2. 分页BEAN.
3. 数据库de查询,添加,修改,删除de操作.
4. 字符串de过滤,替换.
5. 文本文件内容de解析.
6. TOMCATde配置.
7. APACHEde配置.
涉及类及常用方法:
String
Replace();replaceAll();indexOf();lastIndexOf();substring();
StringBuffer
Append();
StringReader
Reader
ResultSet
getString();getInt();getDate();
StreamTokenizer
resetSyntax();ordinaryChar();wordChars();
File
FileReader
LineNumberReader
readLine();getLineNumber();
Connection
Statement
JspSmartUpload
制作心得:
1. JAVA DOCde使用
英文要学呀,不要想着有个中文文档给您看,这样您总是跟在大多数人后面跑.
2. 网上论坛及搜索引擎de使用
初学者老是喜欢问,建议看去网上搜索一番再说,绝大部分问题都可以在网上找到答案,因为别人也曾经问过类似问题.
3. 常用类de熟练掌握
如果想真正入行,必须对一些常用类熟练掌握,不要到面试时,为了一个常用方法而要求查JAVA DOC,导致工作机会de错失.一年前,我有过这样de经历.
4. 一些网站常见功能de制作思路de思考
如文章系统怎样实现图文混排de思考等.可以多看看一些大型网站de制作思路.
5. SUN网站de教程及实例
这里有好多好de教程,包括JAVA类库中类de部分方法de使用例子.
困难遭遇:
1. 中文问题
如果没有在英文版操作系统上de开发,并不会将此当成一件大事,到现在,有些问题也没有找到很好de解决办法.如,在控制面版下de服务里启动deTOMCAT跟运行后生成一个DOS窗口,两种启动方式对中文de支持也不一样.
2. 网站de坏图问题
不知是网络服务器de问题还是TOMCATde问题,会有坏图现象.

最后:
庆贺自己第一次运用JSP/JAVABEAN技术开发动态网站,几经艰辛,终见回报!近两年来,经常光顾DEV-CLUB(CHINAASP),及CHINAJAVAWORLD,JAVAUNION,获益非淺.在此致谢!

使用JavaBean创建您的网上日历本(2)

星期一, 06月 2nd, 2008

JavaBean程序分析
我想把HtmlCalendarNotePad中de主要方法介绍一些,相信会大家有所帮助
public void setYear(int year) //设置年份 .默认值为当前de年份
public int getYear() //获得年份 .默认值为当前de年份
//这是标准Java程序写法,因为Java程序就是一个类所以经常写这种setXXX/getXXX
public void setMonth(int month) //设置月份(1-12),默认值为当前de月
public int getMonth() //获得月份(1-12),默认值为当前de月
public void setStyle(int style)
public int getStyle()
//设置/获得日历de样式(style)
//是以星期天为一星期de第一天HtmlCalendarNotePad.SUNDAY_FIRST还是以星期一为一星期de第一天 HtmlCalendarNotePad.MONDAY_FIRST
// 默认值为HtmlCalendarNotePad.SUNDAY_FIRST
public void setLocale(Locale loc) //设置地区相关代码(Locale), 默认值为Locale.PRC
public Locale getLocale() //获得地区相关代码(Locale), 默认值为Locale.PRC
public void setAction(int day, String actionUrl ,String target_frame)
//设置超链接,来处理请求.如果出了任何错误de话,该方法会返回一个空de字符串.
// 您可以设置一个普通deURL,如“http://www.yesky.com/action.jsp”
// 或者一个一个javascript 函数名, 如 “myFunction”
// 日期将被作为一个参数传给URL,或者作为一个字符串值传给 javascript. 格式是yyyymmdd.
// target_frame可以为空de字符串.
如:
如果调用setAction(21,"http://www.yesky.com/","_blank")
超链接为: 〈a href="http://www.yesky.com/?date=20000621" target=_blank〉21〈/a〉
public void setActions(String actionUrl, String target_frame)
//您也可以为一个月de每一天设置超链接
现在让我和大家一起来分享那颗诱人de豆子(Bean)吧:(HtmlCalendarNotePad.java).
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
public class HtmlCalendarNotePad
{
public static final int MONDAY_FIRST = 1; //设置每星期是以星期一为第一天
public static final int SUNDAY_FIRST = 2; //设置每星期是以星期日为第一天
private Hashtable config;
private static final String Mnth[] = {
"一月", "二月", "三月", "四月", "五月", "六月",
"七月", "八月", "九月", "十月", "十一月", "十二月"
};
private int year;
private int month;
private int style;
private String sFont;
private Locale loc;
private static String NEWLINE = "\n";

public HtmlCalendarNotePad () //构造函数
{
sFont = null;
GregorianCalendar gCalendar = new GregorianCalendar(); //取得当前de日历(格里高里历)
config = new Hashtable(); //创建新de哈希表储存配置信息
NEWLINE = System.getProperty("line.separator");
style = 2; //设置每星期是以星期日为第一天
month = gCalendar.get(2); //取得月份
year = gCalendar.get(1); //取de年份
loc = Locale.PRC; //设置国家名,默认为中华人民共和国
}

private String formatObject(String s, Object obj)
{
String s1 = "";
if(obj != null)
s1 = String.valueOf(String.valueOf(obj));
if(s == null)
return s1;
else
return s s1 "〈/font〉";
}

private int getDay(Calendar calendar) //取得某日在日历中de位置
{
if(style == 2)
return calendar.get(7) - 1;
else
return (calendar.get(7) 5) % 7;
}

public String getHtml()
{
GregorianCalendar gCalendar = new GregorianCalendar(year, month - 1, 1);
GregorianCalendar gCalendar1 = new GregorianCalendar(2001, 3, 24);
SimpleDateFormat simpledateformat = new SimpleDateFormat("EEE", loc);
//设置缩写格式,EEE是星期de缩写,如 Sun, 若EEEE则为Sunday.
int i = month - 1;
int j = 0;
StringBuffer stringbuffer = new StringBuffer(""); //创建新de字符串缓冲区
stringbuffer.append("〈table〉〈tr〉\n"); //在stringbuffer上添加〈table〉〈tr〉,为创建表格作准备
if(style == 2) //见上解释
{
stringbuffer.append("〈th align=right〉" formatObject(sFont, simpledateformat.format(gCalendar1.getTime())) "〈/th〉\n");
gCalendar1.add(5, 1);
for(int k = 1; k 〈 7; k )
{
stringbuffer.append("〈th align=right〉" formatObject(sFont, simpledateformat.format(gCalendar1.getTime())) "〈/th〉\n");
gCalendar1.add(5, 1);
}

}
else
{
gCalendar1.add(5, 1);
stringbuffer.append("〈th align=right〉" formatObject(sFont, simpledateformat.format(gCalendar1.getTime())) "〈/th〉\n");
for(int l = 2; l 〈 8; l )
{
gCalendar1.add(5, 1);
stringbuffer.append("〈th align=right〉" formatObject(sFont, simpledateformat.format(gCalendar1.getTime())) "〈/th〉\n");
}

}
stringbuffer.append("〈/tr〉\n");
int i1 = 0;
j = 0;
if(getDay(gCalendar) 〉 0)
{
stringbuffer.append("〈tr〉");
for(; i1 〈 getDay(gCalendar); i1 )
{
stringbuffer.append("〈td align=right〉");
if(sFont != null)
stringbuffer.append(sFont " 〈/font〉");
else
stringbuffer.append(" ");
stringbuffer.append("〈/td〉\n");
j ;
}

}
for(; gCalendar.get(2) == i; gCalendar.add(5, 1))
{
int j1 = gCalendar.get(5);
int k1 = (i1 j1) % 7;
if(k1 == 1)
{
stringbuffer.append("〈tr〉" NEWLINE);
j = 0;
}
stringbuffer.append("〈td align=right〉");
j ;
if(sFont != null)
stringbuffer.append(sFont);
String s;
if((s = (String)config.get(String.valueOf(j1))) != null)
{
stringbuffer.append("〈a href=\"");
if(s.toUpperCase().startsWith("HTT") || s.indexOf(".") 〉 0)
{
stringbuffer.append(s);
if(s.indexOf("?") 〈 0)
stringbuffer.append("?date=" stringDate(gCalendar));
else
stringbuffer.append("&date=" stringDate(gCalendar));
}
else
{
stringbuffer.append("javascript:" s "(’" stringDate(gCalendar) "’);");
}
stringbuffer.append("\"");
if((s = (String)config.get(j1 "target")) != null)
stringbuffer.append(" target=\"" s "\"");
stringbuffer.append("〉");
stringbuffer.append(gCalendar.get(5));
stringbuffer.append("〈/a〉\n");
}
else
{
stringbuffer.append(String.valueOf(j1));
}
if(sFont != null)
stringbuffer.append("〈/font〉");
stringbuffer.append("〈/td〉\n");
if(k1 == 0)
stringbuffer.append("〈/tr〉\n");
}

if(j 〈 7)
{
for(; j 〈 7; j )
{
stringbuffer.append("〈td align=right〉");
if(sFont != null)
stringbuffer.append(sFont);
stringbuffer.append(" ");
if(sFont != null)
stringbuffer.append("〈/font〉");
stringbuffer.append("〈/td〉\n");
}

stringbuffer.append("〈/tr〉\n");
}
stringbuffer.append("〈/table〉\n");
return stringbuffer.toString();
}

public Locale getLocale() //获取地区名
{
return loc;
}
public int getYear() //取得年份
{
return htmlCalendarYear;
}
public int getMonth() //取得月分
{
return htmlCalendarMonth;
}

public int getStyle() //取得日历de样式
{
return htmlCalendarStyle;
}
//设置动作deURI,target_frame de值可以为_blank、 _parent、 _top、 _self.
public void setAction(int day, String actionUri, String target_frame)
{
if(actionUri != null)
{
config.put(String.valueOf(day), actionUri);
if(target_frame != null && target_frame.length() 〉 0)
config.put(day "target", target_frame);
}
}
//设置一个月de所有天de超链接
public void setActions(String actionUri, String target_frame)
{
for(int day = 1; day 〈= 31; day )
setAction(day, actionUri, target_frame);

}
//设置地区
public void setLocale(Locale locale)
{
loc = locale;
}
//设置年份
public void setYear(int htmlCalendarYear)
{
if(htmlCalendarYear 〉 0)
{
year = htmlCalendarYear;
config.clear();
}
}
//设置月份
public void setMonth(int htmlCalendarMonth)
{
if(htmlCalendarMonth 〉= 1 && htmlCalendarMonth 〈= 12)
{
month = htmlCalendarMonth;
config.clear();
}
}
//设置日历de样式
public void setStyle(int htmlCalendarStyle)
{
style = htmlCalendarStyle;
}
private String stringDate(Calendar calendar)
{
String strDay = String.valueOf(calendar.get(1));
return strDay twoDigits(calendar.get(2) 1) twoDigits(calendar.get(5));
}
private String twoDigits(int day) //为了日历中数字能够对齐,所以1-9 前将加0
{
String stringDay = String.valueOf(day); //取得dayde值
if(stringDay.length() == 1) //如果字符串长度为1
return "0" stringDay; //则在字符串前加零
else
return stringDay;
}
}

使用JavaBean创建您的网上日历本(1)

星期一, 06月 2nd, 2008

有de朋友曾经说过,如果有一个网上de日记本,或者一个网上de万年历能提醒自己到时去干什么事情就好了.其实呀,这样de日历本您自己也能做一个.不信您看下面de例子:
〈HTML〉
〈HEAD〉
〈TITLE〉万年历记事本〈/TITLE〉
〈/HEAD〉
〈BODY BGCOLOR ="white"〉
//设置页面脚本语言是java,导入HtmlCalendarNotePad类,HtmlCalendarNotePad在后面将会讲到
〈%@ page language="java" import="HtmlCalendarNotePad" %〉
//定义一个JavaBean,取其id为HtmlCal
〈jsp:useBean id="HtmlCal" scope="session" class="HtmlCalendarNotePad" /〉
〈%
// 设置参数,取所需de月份为3月,因为未设定年份,故默认为本年.
HtmlCal.setMonth(3);
//设置动作,3月24日时,去天极网,在新窗口打开(也可以以其他de方式打开)
HtmlCal.setAction(24,"http://www.yesky.com/","_blank");
%〉
〈TABLE WIDTH=300〉
〈TR〉〈TD NOWRAP〉
〈%=HtmlCal.getHtml()%〉 //以表格de形式输出一个月de月历
〈/TD〉〈/TR〉
〈/TABLE〉
〈/BODY〉
〈/HTML〉
  您将看到如下输出结果
Mon Tue Wed Thu Fri Sat Sun
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
  这不就跟日历本上一样了吗,不过它比日历本好在,有预设好de链接可以提醒您到时去某个站点,或 执行某个javascript函数,这样不又起了一个记事本de作用了吗?当然了,您也可以自己添加一些功能,使 它变得更加强大,比如添加一个表单,可以让用户填写日记发送到您de系统de数据库中等等.为了完成这个目de, 先让我来看一看JavaBean程序是如何书写de.

JDBCTM 指南:入门7-CallableStatement

星期一, 06月 2nd, 2008

7 - CallableStatement
本概述是从《JDBCTM Database Access from JavaTM: A Tutorial and Annotated Reference 》这本书中摘引来de.JavaSoft 目前正在准备这本书.这本书是一本教程,同时也是 JDBC de重要参考手册,它将作为 Java 系列de组成部份在 1997 年春季由 Addison-Wesley 出版公司出版.

7.1 概述
CallableStatement 对象为所有de DBMS 提供了一种以标准形式调用已储存过程de方法.已储存过程储存在数据库中.对已储存过程de调用是 CallableStatement 对象所含de内容.这种调用是用一种换码语法来写de,有两种形式:一种形式带结果参数,另一种形式不带结果参数(有关换码语法de信息,参见第 4 节“语句”).结果参数是一种输出 (OUT) 参数,是已储存过程de返回值.两种形式都可带有数量可变de输入(IN 参数)、输出(OUT 参数)或输入和输出(INOUT 参数)de参数.问号将用作参数de占位符.
在 JDBC 中调用已储存过程de语法如下所示.注意,方括号表示其间de内容是可选项;方括号本身并不是语法de组成部份.
{call 过程名[(?, ?, ...)]}
返回结果参数de过程de语法为:
{? = call 过程名[(?, ?, ...)]}
不带参数de已储存过程de语法类似:
{call 过程名}
通常,创建 CallableStatement 对象de人应当知道所用de DBMS 是支持已储存过程de,并且知道这些过程都是些什么.然而,如果需要检查,多种 DatabaseMetaData 方法都可以提供这样de信息.例如,如果 DBMS 支持已储存过程de调用,则 supportsStoredProcedures 方法将返回 true,而 getProcedures 方法将返回对已储存过程de描述.
CallableStatement 继承 Statement de方法(它们用于处理一般de SQL 语句),还继承了 PreparedStatement de方法(它们用于处理 IN 参数).CallableStatement 中定义de所有方法都用于处理 OUT 参数或 INOUT 参数de输出部分:注册 OUT 参数de JDBC 类型(一般 SQL 类型)、从这些参数中检索结果,或者检查所返回de值是否为 JDBC NULL.

7.1.1 创建 CallableStatement 对象
CallableStatement 对象是用 Connection 方法 prepareCall 创建de.下例创建 CallableStatement de实例,其中含有对已储存过程 getTestData 调用.该过程有两个变量,但不含结果参数:
CallableStatement cstmt = con.prepareCall(
"{call getTestData(?, ?)}");
其中 ? 占位符为 IN、 OUT 还是 INOUT 参数,取决于已储存过程 getTestData.

7.1.2 IN 和 OUT 参数
将 IN 参数传给 CallableStatement 对象是通过 setXXX 方法完成de.该方法继承自 PreparedStatement.所传入参数de类型决定了所用de setXXX 方法(例如,用 setFloat 来传入 float 值等).
如果已储存过程返回 OUT 参数,则在执行 CallableStatement 对象以前必须先注册每个 OUT 参数de JDBC 类型(这是必需de,因为某些 DBMS 要求 JDBC 类型).注册 JDBC 类型是用 registerOutParameter 方法来完成de.语句执行完后,CallableStatement de getXXX 方法将取回参数值.正确de getXXX 方法是为各参数所注册de JDBC 类型所对应de Java 类型(从 JDBC 类型到 Java 类型de标准映射见 8.6.1 节中de表).换言之, registerOutParameter 使用de是 JDBC 类型(因此它与数据库返回de JDBC 类型匹配),而 getXXX 将之转换为 Java 类型.
作为示例,下述相关代码先注册 OUT 参数,执行由 cstmt 所调用de已储存过程,然后检索在 OUT 参数中返回de值.方法 getByte 从第一个 OUT 参数中取出一个 Java 字节,而 getBigDecimal 从第二个 OUT 参数中取出一个 BigDecimal 对象(小数点后面带三位数):
CallableStatement cstmt = con.prepareCall(
"{call getTestData(?, ?)}");
cstmt.registerOutParameter(1, java.sql.Types.TINYINT);
cstmt.registerOutParameter(2, java.sql.Types.DECIMAL, 3);
cstmt.executeQuery();
byte x = cstmt.getByte(1);
java.math.BigDecimal n = cstmt.getBigDecimal(2, 3);
CallableStatement 与 ResultSet 不同,它不提供用增量方式检索大 OUT 值de特殊机制.

7.1.3 INOUT 参数
既支持输入又接受输出de参数(INOUT 参数)除了调用 registerOutParameter 方法外,还要求调用适当de setXXX 方法(该方法是从 PreparedStatement 继承来de).setXXX 方法将参数值设置为输入参数,而 registerOutParameter 方法将它de JDBC 类型注册为输出参数.setXXX 方法提供一个 Java 值,而驱动程序先把这个值转换为 JDBC 值,然后将它送到数据库中.
这种 IN 值de JDBC 类型和提供给 registerOutParameter 方法de JDBC 类型应该相同.然后,要检索输出值,就要用对应de getXXX 方法.例如,Java 类型为 byte de参数应该使用方法 setByte 来赋输入值.应该给 registerOutParameter 提供类型为 TINYINT de JDBC 类型,同时应使用 getByte 来检索输出值 (第 8 节“JDBC 和 Java 类型之间de映射”将给出详细信息和类型映射表).
下例假设有一个已储存过程 reviseTotal,其唯一参数是 INOUT 参数.方法 setByte 把此参数设为 25,驱动程序将把它作为 JDBC TINYINT 类型送到数据库中.接着,registerOutParameter 将该参数注册为 JDBC TINYINT.执行完该已储存过程后,将返回一个新de JDBC TINYINT 值.方法 getByte 将把这个新值作为 Java byte 类型检索.
CallableStatement cstmt = con.prepareCall(
"{call reviseTotal(?)}");
cstmt.setByte(1, 25);
cstmt.registerOutParameter(1, java.sql.Types.TINYINT);
cstmt.executeUpdate();
byte x = cstmt.getByte(1);
7.1.4 先检索结果,再检索 OUT 参数
由于某些 DBMS de限制,为了实现最大de可移植性,建议先检索由执行 CallableStatement 对象所产生de结果,然后再用 CallableStatement.getXXX 方法来检索 OUT 参数.
如果 CallableStatement 对象返回多个 ResultSet 对象(通过调用 execute 方法),在检索 OUT 参数前应先检索所有de结果.这种情况下,为确保对所有de结果都进行了访问,必须对 Statement 方法 getResultSet、getUpdateCount 和 getMoreResults 进行调用,直到不再有结果为止.
检索完所有de结果后,就可用 CallableStatement.getXXX 方法来检索 OUT 参数中de值.

7.1.5 检索作为 OUT 参数de NULL 值
返回到 OUT 参数中de值可能会是 JDBC NULL.当出现这种情形时,将对 JDBC NULL 值进行转换以使 getXXX 方法所返回de值为 null、0 或 false,这取决于 getXXX 方法类型.对于 ResultSet 对象,要知道 0 或 false 是否源于 JDBC NULL de唯一方法,是用方法 wasNull 进行检测.如果 getXXX 方法读取de最后一个值是 JDBC NULL,则该方法返回 true,否则返回 flase.第 5 节“ResultSet”将给出详细信息.

JSP开发入门(三)–JSP与JavaBean

星期一, 06月 2nd, 2008

虽然您可以在小型指令文件里