JavaBean(Java豆)
JavaBean 概述 JavaBean 是一种 JAVA 语言写成的可重用组件。为写成JavaBean,类必须是具体的和公共的,并且具有无参数的构造器。JavaBean 通过提供符合一致性设计模式的公共方法将内部域暴露称为属性。众所周知,属性名称符合这种模式,其他Java 类可以通过自省机制发现和操作这些JavaBean 属性。 用户可以使用JavaBean将功能、处理、值、数据库访问和其他任何可以用java代码创造的对象进行打包,并且其他的开发者可以通过内部的JSP页面、Servlet、其他JavaBean、applet程序或者应用来使用这些对象。用户可以认为JavaBean提供了一种随时随地的复制和粘贴的功能,而不用关心任何改变。 JavaBean 的发展 最初,JavaBean的目的是为了将可以重复使用的软件代码打包标准。特别是用与帮助厂家开发在综合开发环境(IDE)下使用的java软件部件。这些包括如Grid控件,用户可以将该部件拖放到开发环境中。从此,JavaBean就可以扩展为一个java web 应用的标准部件,并且JavaBean部件框架已经扩展为企业版的 Bean(EJB)。 JavaBean需求 JavaBean是java 类,属于某些特定的译码知道方针,并且扩展了适应性和范围,允许用户访问内部的属性和方法。通过这些,JavaBean类可以用于下列方法: 1. 在 IDE中,JavaBean的功能允许应用开发者浏览其中的方法,即使JavaBean是被编译的,并且无法利用原始的源文件。 2. 在 Remote Method Invocation(RMI) 的分布式风格中,这项功能听起来并不让人兴奋,但是在未来的包含企业版的JavaBean后,将改变这种情况。 3. 为了JavaBean的属性值和状态可以保存到磁盘上。 这里说的有些简单,如果想了解更多的东西,可以查阅一些资料。 JavaBean设计注意事项 实际应用中,在表现对象或者处理前扩展JavaBean的设计非常有用。通常认为如果提供信息,web开发者将在功能上满足拥护对JavaBean的需求。例如: HTML中显示敏感的数据时,用户可以屏蔽的条目,如 密码、 电话号码等。 良好规划设计的JavaBean是值得信赖的。 Java 中的 null 不同于 SQL 中的 null 看这样一道测试程序: public class NullTest { public static void main(String[] args) { int I = 0; // int I = null; // not a valid initialization String str = null; String strZeroOutValue = “”; StringBuffer sb = new StringBuffer(“Null test: String initialized as null = “); sb.append(str); sb.append(“\n String representing a Zero Out Value:”); sb.append(strZeroOutValue); System.out.println(sb.toString()); } } 这个程序将产生下面的结果: Null test: String initialized as null = null String representing a Zero Out Value: 在JavaBean中, 我们将 I 变量声明为一个 int, 我们必须处理与该变量相关的值,因为I 默认的没有值,int 类型无法初始化为 null, 如果int 值没有进行明确的初始化,编译器将提示错误。 执行 NullTest 程序,将验证在文本中空字符串是空值的替代,用于显示 SQL 的插入和更新。如果我们需要删除一些信息,通过删除表单区域的文本,需要将数据库发送长度为0的字符串。 尽管大多数数据库将空字符串作为空值进行操作,如果我们使用的 数据库不支持处理空字符串的话,我们必须写入附加的java代码,来创建自己的SQL插入声明。 例如: if(test.length==0) { sb.append(“null”); } else { sb.append(test); } JavaBean 的范围: JavaBean 的范围。 Scope 是一个具有生命时间的变量。JavaBean的范围在 <jsp:useBean scope=”….>标志中右边进行表示。将产生一个JavaBean的快捷参考。 说明:jsp服务器引擎将剥离<jsp。。。。标记。并且在最终用户的浏览器上无法显示实际代码。 存在下面四种范围: 页面、 请求、 对话、 应用。 对话范围: 对话范围的JavaBean 主要应用与跨多个页面和时间段: 例如填充 用户信息。 添加信息并且接受回馈,保存用户最近执行页面的轨迹。对话范围JavaBean保留一些和用户对话 ID 相关的信息。这些信息来自临时的对话cookie,并在当用户关闭浏览器时,这个cookie将从客户端和服务器删除。 页面/请求范围: 页面和请求范围的JavaBean有时类似表单 的bean , 这是因为 他们大豆用与处理表单。表单需要很长的时间来处理用户的输入,通常情况下用于页面接受HTTP/POST或者GET请求。另外页面和请求范围的bean可以用于减少大型站点服务器上的负载,如果使用对话bean,耽搁的处理就可能会消耗掉很多资源。 应用: 应用范围通常应用于服务器的部件,例如 JDBC 连接池、应用监视、拥护计数和其他参与用户行为的类。 在Bean中限制HTML的产生: 理论上,JavaBean 将不会产生任何HTML,因为这是jsp层负责的工作;然而,为了动态消息提供一些预先准备的格式是非常有用的。产生的HTML将被标注的 JavaBean方法返回。 这里有一些非常重要的事情: 1. 不要试图在JavaBean返回的HTML中放置任何字体尺寸。 并不是所有的浏览器都相同。很多浏览器无法处理完整的字体尺寸。 2. 不要试图在JavaBean返回的HTML中放置任何脚本或者DHTML。 向页面直接输出脚本或者DHTML相当于自我毁灭,因为某些浏览器版本在处理不正确的脚本时会崩溃(非常少但是有)。如果用户的JavaBean在运行时是动态的推出复杂的HTML语言,用户将陷入调试的噩梦。另外,复杂的HTML将限制JavaBean的寿命和灵活性。 3. 不要提供任何的选择。 如果用户使用不同的系统浏览页面,可以提供一种可以替换的方法。 按着Sun公司的定义,JavaBean是一个可重复使用的软件组件。实际上JavaBean是一种Java类,通过封装属性和方法成为具有某种功能或者处理某个业务的对象,简称bean。由于javabean是基于java语言的,因此javabean不依赖平台,具有以下特点: 1. 可以实现代码的重复利用 2. 易编写、易维护、易使用 3. 可以在任何安装了Java运行环境的平台上的使用,而不需要重新编译。 编写javabean就是编写一个java的类,所以你只要会写类就能编写一个bean,这个类创建的一个对象称做一个bean。为了能让使用这个bean的应用程序构建工具(比如JSP引擎)知道这个bean的属性和方法,只需在类的方法命名上遵守以下规则: 1. 如果类的成员变量的名字是xxx,那么为了更改或获取成员变量的值,即更改或获取属性,在类中可以使用两个方法: getXxx(),用来获取属性xxx。 setXxx(),用来修改属性xxx.。 2. 对于boolean类型的成员变量,即布尔逻辑类型的属性,允许使用"is"代替上面的"get"和"set"。 3. 类中方法的访问属性都必须是public的。 4. 类中如果有构造方法,那么这个构造方法也是public的并且是无参数的。 |
虽然你可以把大段的代码放在脚本片段(scriptlet)内,但是绝大多数的Java代码属于可重复使用的名为JavaBea的组件。JavaBean类似于ActiveX控件:他们都能提供常用功能并且可以重复使用。
JavaBean的值是通过一些属性获得的,你可通过这些属性访问JavaBean设置。以一个人来打比方,这个人就是一个JavaBean,这个人的名字、身份证号码以及住址就是其属性。在JSP网站,你基本上就是通过玩“Connect the beans)”来使你的网站动态化。
假设JavaBean先于网站被创建出来,你需要做的第一件事就是告诉JSP页面它将要用到一个“Bean”。你可以用“<jsp:useBean>”标记来做到这一点:
<jsp:useBean id="localName" class="com.jguru.Person" scope="application" />。
“<jsp:useBean>”标记要求你用“id”属性来识鉴别Bean。这里你提供一个名字来JSP页面其余部分的Bean。除了“id”属性,你还须告诉网页面从何处查找Bean,或者它的Java类别名是什么。这种类别属性提供确 认Bean的功能,其他一些方法也可以做到这一点。最后一个必需的元素是“scope”属性。有了“scope”属性的帮助,你就能告诉Bean为单一页面(默认情况)[scope="page"]、为[scope="request"]请求为会话[scope="session"],或者为整个应用程序[scope="application"]保持留信息。有了会话范围,你就能非常容易地在JSP网页面上维护购物车等项目。
一旦你声名了一个JavaBean,你就可以访问它的属性来定制它。要获得属性值,请用“<jsp:getProperty>”标记。有了这个标记,你就能指定将要用到的Bean名称(从useBean的"id"字段得到)以及你想得到其值的属性。实际的值被放在输出中:
<jsp:getProperty id="localName" property="name" />。
要改变JavaBean属性,你必须使用“<jsp:setProperty>”标记。对这个标记,你需要再次识别Bean和属性,以修改并额外提供新值。如果命名正确,这些值可以从一个已提交的表中直接获得:参数获得:
<jsp:setProperty id="localName" property="*" />;
可以从一个参数获得,但你必须直接命名属性和参数:
<jsp:setProperty id="localName" property="serialNumber" value="string" />
或者直接用一个名字称和值来设置: <jsp:setProperty id="localName" property="serialNumber" value= <%= expression %> />.
关于JavaBean 的最后一件事: 为了Web服务器能找到JavaBean,你需要将其类别文件放在一个特殊位置。用JSWDK,最 方便的位置是安装目录下的类别目录,如jswdk-1.0.1classes。
JavaBeans的属性
JavaBeans的属性与一般Java程序中所指的属性,或者说与所有面向对象的程序设计语言中对象的属性是一个概念,在程序中的具体体现就是类中的变量。在JavaBeans设计中,按照属性的不同作用又细分为四类:Simple, Index, Bound与Constrained属性。
1. Simple属性
一个简单属性表示一个伴随有一对get/set方法(C语言的过程或函数在Java程序中称为"方法")的变量。属性名与和该属性相关的get/set方法名对应。例如:如果有setX和getX方法,则暗指有一个名为"X"的属性。如果有一个方法名为isX,则通常暗指"X"是一个布尔属性(即X的值为true或false)。例如在下面这个程序中:
public class alden1 extends Canvas {
string ourString= "Hello"; //属性名为ourString,类型为字符串
public alden1(){ //alden1()是alden1的构造函数,
与C++中构造函数的意义相同
setBackground(Color.red);
setForeground(Color.blue);
}
/* "set"属性*/
public void setString(String newString) {
ourString=newString;
}
/* "get"属性 */
public String getString() {
return ourString;
}
}
2. Indexed属性
一个Indexed属性表示一个数组值。使用与该属性对应的set/get方法可取得数组中的数值。该属性也可一次设置或取得整个数组的值。例:
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;
}
/* 设置数组中的单个元素值 */
public void setDataSet(int index, int x){
dataSet[index]=x;
}
/* 取得整个数组值 */
public int[] getDataSet(){
return dataSet;
}
/* 取得数组中的指定元素值 */
public int getDataSet(int x){
return dataSet[x];
}
}
3. Bound属性
一个Bound属性是指当该种属性的值发生变化时,要通知其它的对象。每次属性值改变时,这种属性就点火一个PropertyChange事件(在Java程序中,事件也是一个对象)。事件中封装了属性名、属性的原值、属性变化后的新值。这种事件是传递到其它的Beans,至于接收事件的Beans应做什么动作由其自己定义。当PushButton的background属性与Dialog的background属性bind时,若PushButton的background属性发生变化时,Dialog的background属性也发生同样的变化。 例:
public class alden3 extends Canvas{
String ourString= "Hello";
//ourString是一个bound属性
private PropertyChangeSupport changes = new PropertyChangeSupport(this);
/** 注:Java是纯面向对象的语言,
如果要使用某种方法则必须指明是要使用哪个对象的方法,
在下面的程序中要进行点火事件的操作,
这种操作所使用的方法是在PropertyChangeSupport类中的。
所以上面声明并实例化了一个changes对象,
在下面将使用changes的firePropertyChange方法来点火ourString的属性改变事件。*/
public void setString(string newString){
String oldString = ourString;
ourString = newString;
/* ourString的属性值已发生变化,于是接着点火属性改变事件 */
changes.firePropertyChange("ourString",oldString,newString);
}
public String getString(){
return ourString;
}
/** 以下代码是为开发工具所使用的。
我们不能预知alden3将与哪些其它的Beans组合成为一个应用,
无法预知若alden3的ourString属性发生变化时有哪些其它的组件与此变化有关,
因而alden3这个Beans要预留出一些接口给开发工具,
开发工具使用这些接口,
把其它的JavaBeans对象与alden3挂接。*/
public void addPropertyChangeListener(PropertyChangeLisener l){
changes.addPropertyChangeListener(l);
}
public void removePropertyChangeListener(PropertyChangeListener l){
changes.removePropertyChangeListener(l);
}
通过上面的代码,
开发工具调用changes的addPropertyChangeListener方法
把其它JavaBeans注册入ourString属性的监听者队列l中,
l是一个Vector数组,可存储任何Java对象。
开发工具也可使用changes的removePropertyChangeListener方法,
从l中注销指定的对象,
使alden3的ourString属性的改变不再与这个对象有关。
当然,当程序员手写代码编制程序时,
也可直接调用这两个方法,
把其它Java对象与alden3挂接。
4. Constrained属性
一个JavaBeans的constrained属性,是指当这个属性的值要发生变化时,与这个属性已建立了某种连接的其它Java对象可否决属性值的改变。constrained属性的监听者通过抛出PropertyVetoException来阻止该属性值的改变。例:下面程序中的constrained属性是PriceInCents。
public class JellyBeans extends Canvas{
private PropertyChangeSupport changes=new PropertyChangeSupport(this);
private VetoableChangeSupport Vetos=new VetoableChangeSupport(this);
/*与前述changes相同,
可使用VetoableChangeSupport对象的实例Vetos中的方法,
在特定条件下来阻止PriceInCents值的改变。*/
......
public void setPriceInCents(int newPriceInCents) throws PropertyVetoException {
/*方法名中throws PropertyVetoException的作用是当有
其它Java对象否决PriceInCents的改变时,
要抛出例外。*/
/* 先保存原来的属性值*/
int oldPriceInCents=ourPriceInCents;
/**点火属性改变否决事件*/
vetos.fireVetoableChange("priceInCents",new Integer(OldPriceInCents),
new Integer(newPriceInCents));
/**若有其它对象否决priceInCents的改变,
则程序抛出例外,不再继续执行下面的两条语句,
方法结束。若无其它对象否决priceInCents的改变,
则在下面的代码中把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);
}
......
}
从上面的例子中可看到,一个constrained属性有两种监听者:属性变化监听者和否决属性改变的监听者。否决属性改变的监听者在自己的对象代码中有相应的控制语句,在监听到有constrained属性要发生变化时,在控制语句中判断是否应否决这个属性值的改变。
总之,某个Beans的constrained属性值可否改变取决于其它的Beans或者是Java对象是否允许这种改变。允许与否的条件由其它的Beans或Java对象在自己的类中进行定义。
JavaBeans的事件
事件处理是JavaBeans体系结构的核心之一。通过事件处理机制,可让一些组件作为事件源,发出可被描述环境或其它组件接收的事件。这样,不同的组件就可在构造工具内组合在一起,组件之间通过事件的传递进行通信,构成一个应用。从概念上讲,事件是一种在"源对象"和"监听者对象"之间,某种状态发生变化的传递机制。事件有许多不同的用途,例如在Windows系统中常要处理的鼠标事件、窗口边界改变事件、键盘事件等。在Java和JavaBeans中则是定义了一个一般的、可扩充的事件机制,这种机制能够:
对事件类型和传递的模型的定义和扩充提供一个公共框架,并适合于广泛的应用。
与Java语言和环境有较高的集成度。
事件能被描述环境捕获和点火。
能使其它构造工具采取某种技术在设计时直接控制事件,以及事件源和事件监听者之间的联系。
事件机制本身不依赖于复杂的开发工具。特别地,还应当:
能够发现指定的对象类可以生成的事件。
能够发现指定的对象类可以观察(监听)到的事件。
提供一个常规的注册机制,允许动态操纵事件源与事件监听者之间的关系。
不需要其它的虚拟机和语言即可实现。
事件源与监听者之间可进行高效的事件传递。
能完成JavaBeans事件模型与相关的其它组件体系结构事件模型的中立映射。
JavaBeans事件模型的主要构成有: 事件从事件源到监听者的传递是通过对目标监听者对象的Java方法调用进行的。对每个明确的事件的发生,都相应地定义一个明确的Java方法。这些方法都集中定义在事件监听者(EventListener)接口中,这个接口要继承java.util.EventListener。实现了事件监听者接口中一些或全部方法的类就是事件监听者。 伴随着事件的发生,相应的状态通常都封装在事件状态对象中,该对象必须继承自java.util.EventObject。事件状态对象作为单参传递给应响应该事件的监听者方法中。 发出某种特定事件的事件源的标识是:遵从规定的设计格式为事件监听者定义注册方法,并接受对指定事件监听者接口实例的引用。 有时,事件监听者不能直接实现事件监听者接口,或者还有其它的额外动作时,就要在一个源与其它一个或多个监听者之间插入一个事件适配器类的实例,来建立它们之间的联系。
事件状态对象(Event State Object)
与事件发生有关的状态信息一般都封装在一个事件状态对象中,这种对象是java.util.EventObject的子类。按设计习惯,这种事件状态对象类的名应以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事件模型是基于方法调用,因而需要一个定义并组织事件操纵方法的方式。JavaBeans中,事件操纵方法都被定义在继承了java.util.EventListener类的EventListener接口中,按规定,EventListener接口的命名要以Listener结尾。任何一个类如果想操纵在EventListener接口中定义的方法都必须以实现这个接口方式进行。这个类也就是事件监听者。例如:
/*先定义了一个鼠标移动事件对象*/
public class MouseMovedExampleEvent
extends java.util.EventObject {
// 在此类中包含了与鼠标移动事件有关的状态信息
...
}
/*定义了鼠标移动事件的监听者接口*/
interface MouseMovedExampleListener
extends java.util.EventListener {
/*在这个接口中定义了鼠标移动事件监听者所应支持的方法*/
void mouseMoved(MouseMovedExampleEvent mme);
}
在接口中只定义方法名,
方法的参数和返回值类型。
如:上面接口中的mouseMoved方法的
具体实现是在下面的ArbitraryObject类中定义的。
class ArbitraryObject implements MouseMovedExampleListener {
public void mouseMoved(MouseMovedExampleEvent mme)
{ ... }
}
ArbitraryObject就是MouseMovedExampleEvent事件的监听者。
事件监听者的注册与注销
为了各种可能的事件监听者把自己注册入合适的事件源中,建立源与事件监听者间的事件流,事件源必须为事件监听者提供注册和注销的方法。在前面的bound属性介绍中已看到了这种使用过程,在实际中,事件监听者的注册和注销要使用标准的设计格式:
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(); // 定义了一个储存事件监听者的数组
/*上面设计格式中的< ListenerType>在此处即是下面的ModelChangedListener*/
public synchronized void addModelChangedListener(ModelChangedListener mcl)
{ listeners.addElement(mcl); }//把监听者注册入listeners数组中
public synchronized void removeModelChangedListener(ModelChangedListener mcl)
{ listeners.removeElement(mcl); //把监听者从listeners中注销
}
/*以上两个方法的前面均冠以synchronized,
是因为运行在多线程环境时,
可能同时有几个对象同时要进行注册和注销操作,
使用synchronized来确保它们之间的同步。
开发工具或程序员使用这两个方法建立源与监听者之间的事件流*/
protected void notifyModelChanged() {
/**事件源使用本方法通知监听者发生了modelChanged事件*/
Vector l;
EventObject e = new EventObject(this);
/* 首先要把监听者拷贝到l数组中,
冻结EventListeners的状态以传递事件。
这样来确保在事件传递到所有监听者之前,
已接收了事件的目标监听者的对应方法暂不生效。*/
synchronized(this) {
l = (Vector)listeners.clone();
}
for (int i = 0; i < l.size(); i++) {
/* 依次通知注册在监听者队列中的每个监听者发生了modelChanged事件,
并把事件状态对象e作为参数传递给监听者队列中的每个监听者*/
((ModelChangedListener)l.elementAt(i)).modelChanged(e);
}
}
}
在程序中可见事件源Model类显式地调用了接口中的modelChanged方法,实际是把事件状态对象e作为参数,传递给了监听者类中的modelChanged方法。
适配类
适配类是Java事件模型中极其重要的一部分。在一些应用场合,事件从源到监听者之间的传递要通过适配类来"转发"。例如:当事件源发出一个事件,而有几个事件监听者对象都可接收该事件,但只有指定对象做出反应时,就要在事件源与事件监听者之间插入一个事件适配器类,由适配器类来指定事件应该是由哪些监听者来响应。
适配类成为了事件监听者,事件源实际是把适配类作为监听者注册入监听者队列中,而真正的事件响应者并未在监听者队列中,事件响应者应做的动作由适配类决定。目前绝大多数的开发工具在生成代码时,事件处理都是通过适配类来进行的。
JavaBeans用户化
JavaBeans开发者可以给一个Beans添加用户化器(Customizer)、属性编辑器(PropertyEditor)和BeansInfo接口来描述一个Beans的内容,Beans的使用者可在构造环境中通过与Beans附带在一起的这些信息来用户化Beans的外观和应做的动作。一个Beans不必都有BeansCustomizer、PrpertyEditor和BeansInfo,根据实际情况,这些是可选的,当有些Beans较复杂时,就要提供这些信息,以Wizard的方式使Beans的使用者能够用户化一个Beans。有些简单的Beans可能这些信息都没有,则构造工具可使用自带的透视装置,透视出Beans的内容,并把信息显示到标准的属性表或事件表中供使用者用户化Beans,前几节提到的Beans的属性、方法和事件名要以一定的格式命名,主要的作用就是供开发工具对Beans进行透视。当然也是给程序员在手写程序中使用Beans提供方便,使他能观其名、知其意。
用户化器接口(Customizer Interface)
当一个Beans有了自己的用户化器时,在构造工具内就可展现出自己的属性表。在定义用户化器时必须要实现java.Beanss.Customizer接口。例如,下面是一个"按钮"Beans的用户化一器:
public class OurButtonCustomizer
extends Panel implements Customizer {
... ...
/*当实现象OurButtonCustomizer这样的常规属性表时,
一定要在其中实现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类,为指定的属性创建一个编辑器。这个类必须继承自java.Beanss.PropertyEditorSupport类。构造工具与手写代码的程序员不直接使用这个类,而是在下一小节的BeansInfo中实例化并调用这个类。例:
public class MoleculeNameEditor extends java.Beanss.PropertyEditorSupport {
public String[] getTags() {
String resule[]={
"HyaluronicAcid","Benzene","buckmisterfullerine",
"cyclohexane","ethane","water"};
return resule;}
}
上例中是为Tags属性创建了属性编辑器,在构造工具内,可从下拉表格中选择MoleculeName的属性应是"HyaluronicAid"或是"water"。
BeansInfo接口
每个Beans类也可能有与之相关的BeansInfo类,在其中描述了这个Beans在构造工具内出现时的外观。BeansInfo中可定义属性、方法、事件,显示它们的名称,提供简单的帮助说明。 例如:
public class MoleculeBeansInfo extends SimpleBeansInfo {
public PropertyDescriptor[] getPropertyDescriptors() {
try {
PropertyDescriptor pd=new PropertyDescriptor("moleculeName",Molecule.class);
/*通过pd引用了上一节的MoleculeNameEditor类,取得并返回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建立连接之后,它的所有状态都应当可被保存,下一次被load进构造工具内或在运行时,就应当是上一次修改完的信息。为了能做到这一点,要把Beans的某些字段的信息保存下来,在定义Beans时要使它实现java.io.Serializable接口。例如:
public class Button
implements java.io.Serializable {
}
实现了序列化接口的Beans中字段的信息将被自动保存。若不想保存某些字段的信息则可在这些字段前冠以transient或static关键字,transient和static变量的信息是不可被保存的。通常,一个Beans所有公开出来的属性都应当是被保存的,也可有选择地保存内部状态。 Beans开发者在修改软件时,可以添加字段,移走对其它类的引用,改变一个字段的private/protected/public状态,这些都不影响类的存储结构关系。然而,当从类中删除一个字段,改变一个变量在类体系中的位置,把某个字段改成transient/static,或原来是transient/static,现改为别的特性时,都将引起存储关系的变化。
JavaBeans的存储格式
JavaBeans组件被设计出来后,一般是以扩展名为jar的Zip格式文件存储,在jar中包含与JavaBeans有关的信息,并以MANIFEST文件指定其中的哪些类是JavaBeans。以jar文件存储的JavaBeans在网络中传送时极大地减少了数据的传输数量,并把JavaBeans运行时所需要的一些资源捆绑在一起,本章主要论述了JavaBeans的一些内部特性及其常规设计方法,参考的是JavaBeans规范1.0A版本。随着世界各大ISV对JavaBeans越来越多的支持,规范在一些细节上还在不断演化,但基本框架不会再有大的变动。
一个donewuser.jsp文件用来实现记录添加操作,另一个文件listuser.jsp文件
用来显示所有的已经注册的用户信息。这两个页面都涉及到了JavaBean的具体调用,还是来看看文件吧,
对于文件中关键代码,都会添加上注释,以方便大家理解。
donewuser.jsp文件
说明:用户注册操作页面,并根据用户注册成功否显示相应的反馈信息,这个页面主要的特点就是使用了
lyf.adduser这个JavaBean的addNewUser()方法来进行记录的添加。
< %@ page contentType="text/html;charset=gb2312"%>
< % response.setHeader("Expires","0"); %>
< !--生成一个JavaBean:lyf.adduser的实例,id为adduser,生存范围为page-->
< jsp:useBean id="adduser" class="lyf.adduser" scope="page"/>
< !--设置JavaBean中各个属性的值,这会调用JavaBean中各个属性的set方法,以便JavaBean得到
正确的属性值,”*”代表进行所有属性的匹配-->
< jsp:setProperty name="adduser" property="*"/>
< html>
< head>
< meta http-equiv="Content-Language" content="zh-cn">
< meta http-equiv="Content-Type" content="text/html; charset=gb2312">
< meta name="GENERATOR" content="Microsoft FrontPage 3.0">
< meta name="ProgId" content="FrontPage.Editor.Document">
< title>用户添加< /title>
< /head>
< body bgcolor="#FFEBBD">
< div align="center">< center>
< %
//调用lyf.adduser的checkUser()方法检查是否有重复的用户名
//如果有重复就显示对应的信息
if(!adduser.checkUser())
{
//页面文字输出信息,使用jsp内置对象out的println方法,相当于asp中的response.write方法
out.println("对不起,这个用户名"+adduser.getUsername()+"已经被申请了,请重新选择!");
//return代表返回,运行时候碰到return就不会进行下面的处理了,功能相当于asp中的response.end
return;
}
%>
< %
//如果没有用户名重复的问题,调用lyf.adduser的addNewUser()方法来将用户数据添加到数据库中,并
根据数据添加成功否来显示对应的信息
if(adduser.addNewUser()){
%>
< H2>添加用户成功!
< H2>添加用户失败,请和管理员联系!
< /BODY>
< /HTML>
listuser.jsp文件
说明:用户信息列表页面,用于显示注册的所有用户信息,对数据进行了分页显示。
为了方便大家使用,采用了通用的分页代码,如果是JDBC2.0以上或者其他支持
TYPE_SCROLL_INSENSITIVE游标的数据库驱动程序,可以有更简洁的分页方法。
和前面jsp页面类似的语句就不讲解了,代表在
这个页面中导入java.sql.ResultSet的这个类库,因为Jsp页面中间要声明ResultSet;
oracle.jdbc.driver.*类库是Oracle专用的Jdbc驱动程序,让Jsp页面可以用来进行Oracle的数据库操作。
< %@ page contentType="text/html;charset=gb2312"%>
< % response.setHeader("Expires","0"); %>
< %@ page import="java.sql.ResultSet" %>
< %@ page import="oracle.jdbc.driver.*" %>
< !--生成一个JavaBean:lyf.db的实例-->
< jsp:useBean id="db" class="lyf.db" scope="request"/>
< jsp:setProperty name="db" property="*"/>
< %
java.lang.String strSQL; //SQL语句
int intPageSize; //一页显示的记录数
int intRowCount; //记录总数
int intPageCount; //总页数
int intPage; //待显示页码
java.lang.String strPage;
int i,j,k;
//设置一页显示的记录数
intPageSize = 15;
//取得待显示页码
strPage = request.getParameter("page");
if(strPage==null){//表明在QueryString中没有page这一个参数,此时显示第一页数据
intPage = 1;
}
else{//将字符串转换成整型
intPage = java.lang.Integer.parseInt(strPage);
if(intPage<1) intPage = 1;
}
//获取记录总数
strSQL = "select count(*) from user";
ResultSet result = db.executeQuery(strSQL); //执行SQL语句并取得结果集
result.next(); //记录集刚打开的时候,指针位于第一条记录之前
intRowCount = result.getInt(1);
result.close(); //关闭结果集
//记算总页数
intPageCount = (intRowCount+intPageSize-1) / intPageSize;
//调整待显示的页码
if(intPage>intPageCount) intPage = intPageCount;
strSQL="select * from user order by id desc";
//执行SQL语句并取得结果集
result = db.executeQuery(strSQL);
//将记录指针定位到待显示页的第一条记录上
i = (intPage-1) * intPageSize;
for(j=0;j
< html>
< head>
< meta http-equiv="Content-Language" content="zh-cn">
< meta http-equiv="Content-Type" content="text/html; charset=gb2312">
< meta name="GENERATOR" content="Microsoft FrontPage 3.0">
< meta name="ProgId" content="FrontPage.Editor.Document">
< title>用户列表< /title>
< /head>
< body bgcolor="#FFEBBD">
< div align="center">< center>
< table border="1"
borderColorDark="#FFFFFF" borderColorLight="#000000" cellspacing="0" height="22"
width="100%">
< tr bgcolor="#FFEBAD">
< td height="1" width="691" class="main">
第页 共页
< a href="listuser.jsp?page=0">首页
< a href="listuser.jsp?page=">上一页
上一页
< a href="listuser.jsp?page=">下一页
下一页
< a href="listuser.jsp?page=">尾页
第< input type="text" class="main" name="page" size="3" value=""
tabindex="1">页< input type="submit" class="main" value="go" name="B1" tabindex="2">
< table border="1" width="100%" cellspacing="0" bordercolorlight="#000000"
bordercolordark="#FFFFFF" class="main">
< tr bgcolor="#FFEBAD">
< td >
< div align="left">用户名
< td >
< p align="center">Email
< td >
< p align="center">主页
< td>
< p align="center">登记时间
< td>
< p align="center">说明
< tr bgcolor="#FFEBAD">
< td>
< div align="left">
< td>< div align="center">
< td>< div align="center">< font color="#0000CC">
< td>< div align="center">< font color="#FF6666">
< td>< div align="center">< font color="#0000FF">
< /table>
< /body>
< /html>
先运行程序newuser.jsp文件,进行用户注册操作,然后运行listuser.jsp文件,看看是否已经
添加到数据库中。对于具体jsp文件和class文件放在什么目录下的问题,请看具体jsp服务器软件的参考,
一个最简单的方法就是用Jbuilder4.0直接运行,因为它自带了Tomcat服务器软件。
好了,到这里Jsp+JavaBean的介绍就基本上结束了。
jsp文件:pagedata.jsp //显示数据
javabean:DataConn.java //连接数据库
javabean:GetCount.java //获得数据记录数
DataConn.java文件内容:
package aliang_org;
import java.sql.*;
public class DataConn{
public static Connection getDataConn() {
Connection conn = null;
try {
Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver").newInstance();
conn = DriverManager.getConnection("jdbc:microsoft:sqlserver://127.0.0.1:1433;DatabaseName=your","your","your");
}
catch(Exception ex) {
ex.printStackTrace();
}
return conn;
}
}
GetCount .java文件内容:
package aliang_org;
import java.sql.*;
import java.util.*;
import java.io.*;
public class GetCount {
private Connection conn;
public GetCount()throws Exception{
conn = DataConn.getDataConn();
}
public int getCounter()throws Exception{
int ret = 0;
Statement stmt = conn.createStatement();
ResultSet rset = stmt.executeQuery("select count(*) from UserInfos");
while(rset.next()){
ret = rset.getInt(1);
}
return ret;
}
}
pagedata.jsp 文件内容:
<%@ page contentType="text/html; charset=gb2312" language="java"%>
<%@ page import = "aliang_org.*"%>
<%@ page import = "java.util.*"%>
<%@ page import = "java.io.*"%>
<%@ page import = "java.sql.*"%>
<html>
<head>
<title>
jsp/JavaBean分页列出数据
</title>
</head>
<body bgcolor="#ffffff">
<h1>jsp/JavaBean分页列出数据</h1>
<hr />
<jsp:useBean id="dataConnection" class="aliang_org.DataConn" scope="page" />
<jsp:useBean id="getcounter" class="aliang_org.GetCount" scope="page" />
<table width="100%" border="1" cellpadding="0" cellspacing="0">
<tr align="center" bgcolor="#00FFFF">
<td>ID序号</td>
<td>用户名</td>
<td>密码</td>
<td>电子信箱</td>
<td colspan="2">操作选项</td>
</tr>
<%
int nowPages;//当前页
int pages;//请求页数
int countPerPage = 10 ; //每页显示条数
int pageCount;//总页数
int recordCount = getcounter.getCounter(); //recordCount为总记录数
pageCount = (int)Math.ceil((recordCount + countPerPage-1) / countPerPage);//算出总页数
//得到请求页
if(request.getParameter("pages") == null){
pages = 1;
}else{
pages = new Integer(request.getParameter("pages")).intValue();
}
//得到当前实际页面
if(pages > pageCount){
nowPages = 1;
}else{
nowPages = pages;
}
//获取记录集
Connection conn = dataConnection.getDataConn();
ResultSet rs = conn.createStatement().executeQuery("Select top "+ countPerPage +" * from UserInfos where ID not in (select top "+ (nowPages-1)*countPerPage +" ID from UserInfos order by id desc) order by ID desc");
while(rs.next()){
int ID = rs.getInt("ID");
String username = rs.getString("username");
String password = rs.getString("password");
String email = rs.getString("email");
%>
<tr>
<td align="center"><%= ID %></td>
<td><%= username %></td>
<td><%= password %></td>
<td><%= email %></td>
<td align="center"><a href="modify.jsp?ID=<%= ID %>">修改</a></td>
<td align="center"><a href="delete.jspID=<%= ID %>">删除</a></td>
</tr>
<%
}
//关闭连接
rs.close();
conn.close();
%>
<form method="get">
<tr align="right">
<td colspan="6">共有<font color=red><%= recordCount %></font>条记录 当前<font color=red><%= nowPages %>/<%= pageCount %></font>页
<% if(pageCount > 1){ %>
<% if(pages > 1){%>
<a href="">首页</a>
<%}if(pages < pageCount){%>
<a href="?pages=<%= nowPages+1 %>">下一页</a>
<%}if(pages != 1){%>
<a href="?pages=<%= nowPages - 1 %>">上一页</a>
<%}%>
<a href="?pages=<%= pageCount %>">尾页</a>
<% } %>跳转到
<select name="pages" onChange="javascript:this.form.submit();">
<% for(int i=1;i<=pageCount;i++){%>
<option value="<%= i %>" <% if(nowPages == i){%>selected<% } %>><%= i %></option>
<% } %>
</select>页</td>
</tr>
</form>
</table>
<hr />
© 2004 Aliang.org StartJAVA
</body>
</html>
还有不要忘了把sql-jdbc驱动安装了,并拷贝C:\Program Files\Microsoft SQL Server 2000 Driver for JDBC\lib文件夹到web目录下的WEB-INF下面。
public class OracleConnectionManager {
private DBConnectionManager connMgr;
private final String poolName="Oracle";
private Connection conn;
public OracleConnectionManager() {
String userID = "system";
String password = "manager";
String JDBCDriver = "oracle.jdbc.driver.OracleDriver";
String JDBCDriverType = "jdbc:oracle:thin";
String DBHost = "127.0.0.1";
String Port = "1521";
String SID="sid";
String url = JDBCDriverType+":@"+DBHost + ":" + Port + ":" +SID;
connMgr = DBConnectionManager.getInstance();
if (DBConnectionManager.clients == 1){
connMgr.init(poolName, JDBCDriver, url, userID, password);
connMgr.init("egongOracle", JDBCDriver, JDBCDriverType+":@110.7.6.22:1521:egong", "abcusername", "abcpasswd");
}
}
public Connection getConnection() {
return (conn=connMgr.getConnection(poolName));
}
public Connection getConnection(String newOracle) {
return (conn=connMgr.getConnection(newOracle));
}
public void freeConnection() {
connMgr.freeConnection(poolName,conn);
}
public void freeConnection(String newOracle) {
connMgr.freeConnection(newOracle,conn);
}
}
public class DBConnectionManager {
static private DBConnectionManager instance=null; // The single instance
static public int clients=0;
private Vector drivers = new Vector();
private PrintWriter log;
private Hashtable pools = new Hashtable();
private final int MAX_CONN = 1000;
/**
* Returns the single instance, creating one if it´s the
* first time this method is called.
*
* @return DBConnectionManager The single instance.
*/
static synchronized public DBConnectionManager getInstance() {
if (instance == null) {
instance = new DBConnectionManager();
}
clients++;
return instance;
}
/**
* A private constructor since this is a Singleton
*/
private DBConnectionManager() {
// init();
}
/**
* Returns a connection to the named pool.
*
* @param name The pool name as defined in the properties file
* @param con The Connection
*/
public void freeConnection(String name, Connection con) {
DBConnectionPool pool = (DBConnectionPool) pools.get(name);
if (pool != null) {
pool.freeConnection(con);
}
}
/**
* Returns an open connection. If no one is available, and the max
* number of connections has not been reached, a new connection is
* created.
*
* @param name The pool name as defined in the properties file
* @return Connection The connection or null
*/
public Connection getConnection(String name) {
DBConnectionPool pool = (DBConnectionPool) pools.get(name);
if (pool != null) {
return pool.getConnection();
}
return null;
}[Page]
/**
* Returns an open connection. If no one is available, and the max
* number of connections has not been reached, a new connection is
* created. If the max number has been reached, waits until one
* is available or the specified time has elapsed.
*
* @param name The pool name as defined in the properties file
* @param time The number of milliseconds to wait
* @return Connection The connection or null
*/
public Connection getConnection(String name, long time) {
DBConnectionPool pool = (DBConnectionPool) pools.get(name);
if (pool != null) {
return pool.getConnection(time);
}
return null;
}
/**
* Closes all open connections and deregisters all drivers.
*/
public synchronized void release() {
// Wait until called by the last client
if (--clients != 0) {
return;
}
Enumeration allPools = pools.elements();
while (allPools.hasMoreElements()) {
DBConnectionPool pool = (DBConnectionPool) allPools.nextElement();
pool.release();
}
Enumeration allDrivers = drivers.elements();
while (allDrivers.hasMoreElements()) {
Driver driver = (Driver) allDrivers.nextElement();
try {
DriverManager.deregisterDriver(driver);
log("Deregistered JDBC driver " + driver.getClass().getName());
}
catch (SQLException e) {
log(e, "Can´t deregister JDBC driver: " + driver.getClass().getName());
}
}
log.close();
}
/**
* Creates instances of DBConnectionPool based on the properties.
* A DBConnectionPool can be defined with the following properties:
* <PRE>
* <poolname>.url The JDBC URL for the database
* <poolname>.user A database user (optional)
* <poolname>.password A database user password (if user specified)
* <poolname>.maxconn The maximal number of connections (optional)
* </PRE>
*
* @param props The connection pool properties
*/
private void createPools(String poolName, String url, String user, String password, int max) {
/*
Enumeration propNames = props.propertyNames();
while (propNames.hasMoreElements()) {
String name = (String) propNames.nextElement();
if (name.endsWith(".url")) {
String poolName = name.substring(0, name.lastIndexOf("."));
String url = props.getProperty(poolName + ".url");
if (url == null) {
log("No URL specified for " + poolName);
continue;
}
String user = props.getProperty(poolName + ".user");
String password = props.getProperty(poolName + ".password");
String maxconn = props.getProperty(poolName + ".maxconn", "0");
int max;
try {
max = Integer.valueOf(maxconn).intValue();
}
catch (NumberFormatException e) {
log("Invalid maxconn value " + maxconn + " for " + poolName);
max = 0;
}
DBConnectionPool pool =
new DBConnectionPool(poolName, url, user, password, max);
pools.put(poolName, pool);
log("Initialized pool " + poolName);
}
}
*/
DBConnectionPool pool =
new DBConnectionPool(poolName, url, user, password, max);
pools.put(poolName, pool);
log("Initialized pool " + poolName);
}
public void init(String poolName, String driver, String url, String user, String passwd) {
init(poolName, driver, url, user, passwd, MAX_CONN);
}
* Loads properties and initializes the instance with its values.
*/
public void init(String poolName, String driver, String url, String user, String passwd, int maxconn) {
/*
InputStream is = getClass().getResourceAsStream("/db.properties");
Properties dbProps = new Properties();
try {
dbProps.load(is);
}
catch (Exception e) {
System.err.println("Can´t read the properties file. " +
"Make sure db.properties is in the CLASSPATH");
return;
}
String logFile = dbProps.getProperty("logfile", "DBConnectionManager.log");
String logFile = "." + File.separator + "logs" + File.separator + "dbpool";
try {
log = new PrintWriter(new FileWriter(logFile, true), true);
}
catch (IOException e) {
System.err.println("Can´t open the log file: " + logFile);
log = new PrintWriter(System.err);
}
*/
log = new PrintWriter(System.err);
loadDrivers(driver);
createPools(poolName, url, user, passwd, maxconn);
}
/**
* Loads and registers all JDBC drivers. This is done by the
* DBConnectionManager, as opposed to the DBConnectionPool,
* since many pools may share the same driver.
*
* @param props The connection pool properties
*/
private void loadDrivers(String driverClassName) {
/*
String driverClasses = props.getProperty("drivers");
StringTokenizer st = new StringTokenizer(driverClasses);
while (st.hasMoreElements()) {
String driverClassName = st.nextToken().trim();
try {
Driver driver = (Driver)
Class.forName(driverClassName).newInstance();
DriverManager.registerDriver(driver);
drivers.addElement(driver);
log("Registered JDBC driver " + driverClassName);
}
catch (Exception e) {
log("Can´t register JDBC driver: " +
driverClassName + ", Exception: " + e);
}
}