2000年7月26日星期三

Hibernate实战全解

  对象关系映射(Object Relative Mapping)简称ORM,是面向对象开发的一个热点,用来解决JDBC开发中手动进行OR映射的繁杂与不便。EJB中的实体Bean在这个领域是很著名的——既因为它的先进而著名,也因为它的低效而著名。有过实体Bean开发经验的人可能都会为实现远程接口造成的效率低下而头痛,在很多不大不小的项目中,使用实体Bean是否得不偿失,争论很大。一个轻量级的持久化方案也许能够解决一些问题,Hibernate应此而生。

  Hibernate是一个中间层,它的目的是把数据库中的关系通过一定的规则映射成为对象,让Java开发人员不用太多的考虑底层数据库的问题,只需要像通常情况下管理对象一样的管理数据。在关系数据库仍将持续占据市场的情况下,它很可观。在数据持久化领域,即便是轻量级的方案也会是复杂饶舌的,也许如同周杰伦的音乐一样不知所云。在学习它之前,最好先回想一下以前进行数据库开发中遇到的问题和不便,想想为什么需要一个持久化层,才能知道很多操作的目的是什么,以及为什么要这么干,在这个问题上我不想做更多的叙述,因为“长久以来……”这样的句式通常long(不好意思,打不出来)长,会对我的键盘和热情造成很大的磨损。如果让我写一本书,那么我会乐意去叙述什么是数据持久化,它有什么好处等等。废话少说,来了。

  首先需要配置环境,下载Hibernate 2.1(www.hibernate.org),把lib下的*.jar添加到classpath,你的数据库JDBC驱动程序也应该在classpath中。打开hibernate.properties,针对你使用的数据库,配置相应的信息,比如我使用的是MS SQL Server,配置如下:

## MS SQL Server
hibernate.dialect net.sf.hibernate.dialect.SQLServerDialect
hibernate.connection.driver_class com.microsoft.jdbc.sqlserver.SQLServerDriver
hibernate.connection.url jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=zizz
hibernate.connection.username sa
hibernate.connection.password

  其中很大部分是已经写好的,只需要取掉注释即可,我自己只是修改了数据库名称、帐号、密码。建立一个名为zizz的数据库备用。

  然后把这个文件拷贝到你的应用的根目录下。

  我们谈论了很多次映射,应该首先来看看这个映射是如何完成的。假设一个最简单的应用,写一个功能最单一的留言板,设计的数据有留言的编号、留言者名称、留言内容,还有留言时间。足够简单吧,换做是你打算怎么干?我猜你要首先建立一个数据库表格,名字也许叫做guestbook。No,这不是面向对象的方式,不妨首先从对象的角度来考虑。我们当然希望每一条留言都以对象的方式呈现,每个对象应该具有的属性有:id、author、content、time。偷个懒,没有画UML。下面这个类应该是很容易理解的:

//GuestBook.java
package org.bromon.zizz;

import java.util.*;

public class GuestBook
{
private int id;
private String author;
private String content;
private Calendar time;

private void setId(int id)
{
this.id=id;
}
public int getId()
{
return(id);
}

public void setAuthor(String author)
{
this.author=author;
}
public String getAuthro()
{
return(author);
}

public void setContent(String content)
{
this.content=content;
}
public String getContent()
{
return(content);
}

public void setTime(Calendar time)
{
this.time=time;
}
public Calendar getTime()
{
return(time);
}
}

  基本上是最简单的Bean了,如果觉得困难的话,请你先回火星等我。

  需要注意的是setId方法被指定为private,这是因为我希望用这个字段做主键,它最好由系统自动生成,所以不应该由用户来指定,这个方法专为Hibernate准备,所以是私有的。

  如何把这个类与数据库映射起来?看看Hibernate的魔法,使用一个XML文件来描述,它应该被命名为GuestBook.hbm.xml:

< ?xml version="1.0"? >
< !DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"' target=_blank >http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >

< hibernate-mapping package="org.bromon.zizz" >
< class table=”guestbook" lazy="true" >
< id type="integer" unsaved-value="null" >
< column sql-type="int" not-null="true"/ >
< generator / >
< /id >

< property column="author" not-null="true" unique="false"/ >
< property column="content" not-null="true"/ >
< property column="time" not-null="true"/ >
< /class >
< /hibernate-mapping >

虽然有点陌生,但是很易读,仔细琢磨一下。
下面来编写我们的应用,它的功能是插入数据:

//Operate.java
package org.bromon.zizz;
import net.sf.hibernate.*;
import net.sf.hibernate.cfg.*;
import net.sf.hibernate.tool.hbm2ddl.*;
import java.util.*;

public class Operate
{
public static void main(String args[])
{
try
{
Configuration cfg=new Configuration().addClass(GuestBook.class);
SessionFactory sessions=cfg.buildSessionFactory();
new SchemaExport(cfg).create(true,true);
Session session=sessions.openSession();

GuestBook gb=new GuestBook();
gb.setAuthor(“Bromon”);
gb.setContent(“留言的内容”);
gb.setTime(Calendar.getInstance());

Transaction ts=session.beginTransaction();
session.save(gb);
ts.commit();
session.close();
}catch(Exception e)
{
System.out.println(e);
}
}
}
编译:javac –d . *.java
执行一下:java org.bromon.zizz.Operate

到数据库里面看看,表格已经建立好了,并且数据也已经保存。如果把

new SchemaExport().create(true,true);

注释掉,那么系统不会创建表格,而只是在已有的表格中添加新的记录,当然,如果表格不存在的话,会产生异常。

你已经看到了Hibernate神奇魔法的5%,它足够的复杂强大,可以让你应付复杂的应用。

one-to-one关系
在绝大多数系统当中不可能只存在一个数据表格,否则就违背了关系数据库的初衷。表与表的关系比较复杂,可以分为几种情况:

● 一对一关联(one to one)
● 一对多关联(one to many)
● 多对一关联(many to one)
● 多对多关联(many to many)

按顺序来讲。假设一个一对一关联的例子是:
表格:person
id 编号(主键)
name 姓名
email email地址

表格:spouse
id 编号(外键)
name 姓名

person这个表保存用户信息,而spouse保存用户配偶的信息。在一般情况下一个人只有一个配偶,这很适合我们一对一的情况。如果你对婚外恋感兴趣的话,我们可以在一对多和多对一的关联中讨论这个问题。

OK,下面设计POJO:
Person这个类非常简单:

/*
* Created on 2004-4-19
*/
package org.bromon.zizz;

/**
* @author Bromon
*/
public class Person
{
private int id;
private String name;
private String email;

public void setId(int id)
{
this.id=id;
}
public int getId()
{
return(id);
}

public void setName(String name)
{
this.name=name;
}
public String getName()
{
return(name);
}

public void setEmail(String email)
{
this.email=email;
}
public String getEmail()
{
return(email);
}
}

然后编写它的映射规则,这个应该能够理解了:
< ?xml version="1.0"? >
< !DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"' target=_blank >http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >

< hibernate-mapping package="org.bromon.zizz" >
< class table="person" lazy="true" >
< id type="integer" unsaved-value="null" >
< column sql-type="int" not-null="true"/ >
< generator / >
< /id >

< property column="name" not-null="true" unique="false"/ >
< property column="email" not-null="false"/ >
< /class >
< /hibernate-mapping >

so easy是不是?一切都按部就班。下面是Souse类:

/*
* Created on 2004-4-20
*/
package org.bromon.zizz;

/**
* @author Bromon
*/
public class Spouse
{
private int id;
private String name;
private Person person;

public void setId(int id)
{
this.id=id;
}
public int getId()
{
return(id);
}

public void setName(String name)
{
this.name=name;
}
public String getName()
{
return(name);
}

public void setPerson(Person person)
{
this.person=person;
}
public Person getPerson()
{
return(person);
}
}

注意里面的域person。它的映射文件:

< ?xml version="1.0"? >
< !DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"' target=_blank >http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >

< hibernate-mapping package="org.bromon.zizz" >
< class table="spouse" lazy="true" >
< id type="integer" unsaved-value="null" >
< column sql-type="int" not-null="true"/ >
< generator >
< param >person< /param >
< /generator >
< /id >

< property column="name" not-null="true" unique="false"/ >
< one-to-one cascade="all" constrained="true" / >
< /class >
< /hibernate-mapping >

这里指明了id的generator是一个外键,和person相关联。然后指定一个one-to-one关系,不难理解是不是?Hibernate的确很符合我们的思维习惯。需要提醒的是,这种关联关系是单向的,Person并不需要去指定Spouse。

下面来操作这两个类:
/*
* Created on 2004-4-20
*/
package org.bromon.zizz;
import net.sf.hibernate.*;
import net.sf.hibernate.cfg.*;
import net.sf.hibernate.tool.hbm2ddl.*;
/**
* @author Bromon
*/
public class OperateSpouse
{
public static void main(String args[])
{
try
{
Configuration cfg=new Configuration().addClass(Spouse.class);
cfg.addClass(Person.class);
SessionFactory factory=cfg.buildSessionFactory();
new SchemaExport(cfg).create(true,true);
Session session=factory.openSession();

Person person=new Person();
person.setName("bromon");
person.setEmail("bromon@163.com");

Spouse spouse=new Spouse();
spouse.setName("who");
spouse.setPerson(person);

Transaction ts=session.beginTransaction();
session.save(person);
session.save(spouse);
ts.commit();
session.close();
}catch(Exception e)
{
System.out.println(e);
}
}
}

这个例子和第一篇中的例子非常相似。OK,执行一下,然后看看zizz数据库,搞掂。

Many-to-One关系

很明显一对多或者多对一关系是关系数据库中非常常见的现象,下面通过父亲-儿子的例子来演示一对多关系,多对一关系是类似的,不过在我们的这个例子中不宜采用,否则会带来伦理学上的问题。

首先定义Child类:
/*
* Created on 2004-5-8
*/
package org.bromon.zizz;

/**
* @author Bromon
*/
public class Child
{
private int id;
private String name;
private int fatherId;
private Person father;

public Child(){}

/**
* @return
*/
public Person getFather()
{
return father;
}

/**
* @return
*/
public int getFatherId()
{
return fatherId;
}

/**
* @return
*/
public int getId()
{
return id;
}

/**
* @return
*/
public String getName()
{
return name;
}

/**
* @param person
*/
public void setFather(Person p)
{
father = p;
}

/**
* @param i
*/
public void setFatherId(int i)
{
fatherId = i;
}

/**
* @param i
*/
public void setId(int i)
{
}

/**
* @param string
*/
public void setName(String string)
{
}

}

这里的fatherId是外键,关联person表的id字段。

下面是映射文件Child.hbm.xml:
< ?xml version="1.0"? >
< !DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"' target=_blank >http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >

< hibernate-mapping package="org.bromon.zizz" >
< class table="child" lazy="true" >
< id type="integer" unsaved-value="null" >
< column sql-type="int" not-null="true"/ >
< generator / >
< /id >

< property column="name" not-null="true" unique="false"/ >
< many-to-one column="fatherid" / >

< /class >
< /hibernate-mapping >
需要注意的是fatherId并没有做为一个property被映射,而是在many-to-one声明中使用。

需要对Person..java做修改,添加以下代码:

import java.util.*;

private Set children=new HashSet();
/**
* @return
*/
public Set getChildren()
{
return children;
}
/**
* @param set
*/
public void setChildren(Set set)
{
children = set;
}

然后修改Person.hbm.xml,对添加的代码做映射:

< set lazy="true" inverse="true" cascade="all" >
< key column="fatherid"/ >
< one-to-many / >
< /set >

这里的key column是child表的外键,inverse需要指定为true。

下面做操作一下,功能是查询person表中id=1的记录,作为小孩的父亲,然后给child表添加一条新记录。

/*
* Created on 2004-5-8
*/
package org.bromon.zizz;
import net.sf.hibernate.*;
import net.sf.hibernate.cfg.*;
import net.sf.hibernate.tool.hbm2ddl.*;
/**
* @author Bromon
*/
public class OperateChild
{
/**
* @param args
*/
public static void main(String args[])
{
try
{
Configuration cfg = new Configuration().addClass(Person.class);
cfg.addClass(Child.class);
SessionFactory sessions = cfg.buildSessionFactory();
new SchemaExport(cfg).create(true, true);
Session session = sessions.openSession();

Child c=new Child();

/*Query q=session.createQuery("from org.bromon.zizz.Person as p where p.id=1");
Person p=(Person)q.list().get(0);*/

Person p=(Person)session.find("from org.bromon.zizz.Person as p where p.id=?",new Integer(1),Hibernate.INTEGER).get(0);
System.out.println(p.getName());
c.setName("andy");
c.setFather(p);

Transaction ts = session.beginTransaction();
session.save(c);
ts.commit();
session.close();
} catch (Exception e)
{
System.out.println(e);
}
}
}

被注释掉的部分是HQL的另外一种查询方法。在这个例子中可以看出对象的查询非常容易,不需要自己再去封装数据,修改和删除对象也很容易:

//得到一个对象
Query q=session.createQuery("from org.bromon.zizz.Person as p where p.id=1");
Person p=(Person)q.list().get(0);

//修改数据
p.setName(“Mr Smith”);

//保存数据
session.save(p);
session.flush();

//删除数据
session.delete(p);
session.flush();

快速上手Spring

      “好的开始是成功的一半”,但凡一种新的技术,开始阶段一般是很苦恼的。为了不让更多的人走同样的弯路,我这里就把学习Spring的经验与大家分享, 一起快乐一下。 
 
  Spring是什么?反正这里我不会谈论春天的。我要说的是Spring Framework ,它又是用来做什么的呢?一会半会我也说不清楚。还好现在是互联网的时代,google一下,会发现不少有用的东西。 
 
     先来到Spring的大本营:http://www.springframework.org/  可以看看 About Spring,最重要的是下载一个
Spring版本。
 
     Spring的下载包有两种,一种是带有“with-dependencies”名称的,这种包含了Spring常用到的第三方包,比如Hibernate、aopalliance、jakarta-commons等。这里就使用了这种包,可以省却查找第三方包的麻烦。
 
     当前版本1.2.1 :spring-framework-1.2.1-with-dependencies.zip
 
     解压这个包,可以在 docs\reference目录下找到Spring 的参考手册,有html和pdf两种格式。如果你的英文不太好,可以看看Spring中文论坛翻译的1.1版本的中文版(pdf格式)。这个参考手册就是入门的不二法宝。
 
 
●     新手刚学Spring时,可能对一些概念似懂非懂,比如IoC(这里可不是国际奥委会)、DI等。这里向你推荐几篇好文章,有助于理解Spring。
 
· Rod Johnson:Introduction to Spring Framework (Spring 1.2版本)
 
· Robert C Martin, Bob: The Dependency Inversion Principle
 
     透明对此文的翻译: IoC 容器和Dependency Injection 模式
 
· 冰云: 介绍 IOC    
 
· 夏昕:Spring开发指南
 
· 良葛格:Spring入门(非常适合新手入门)
 
· javaboutique网站上有篇教程(入门): The Spring Framework
 
 
●   如果要系统的学习Spring,还是需要Book的。
 
看看作者是谁,就知道这本书的质量如何了。毫无疑问,这本书吹响了向Spring进军的号角。前半部分主要阐述了J2EE开发的方方面面的经验和设计,后半部分通过对J2EE的改善设计而引申出了SpringFramework。这本书的中译本也出来了,不过翻译的一般。
          
 ·  Rod Johnson, Juergen Hoeller:   Expert One-on-One J2EE Development without EJB(7/2004)
看看书名就会吓一跳,不过Rod Johnson完全有资格说出来。前半本介绍EJB的不足,后半本讲Spring的原理。这本书的中译本也出来了。
 
 ·  Bruce A. Tate, Justin Gehtland:Better, Faster, Lighter Java(5/2004)
这本书主要阐述了如何利用Java进行轻量级开发,有关Spring的内容比较少,只有一章,不过此书获得了Jolt 大奖,质量肯定没的说了。
 
·   Craig Walls, Ryan Breidenbach:  Spring in Action(1/2005)
Manning出版的“in action”系列图书,质量一直非常好。此书系统介绍了Spring的方方面面,解析相当清晰。 
 
·  Matt Raible: Spring Live 
这本是source beat的电子书(只出电子版)。如果要深入这本书,需要有相当的基础。这本书非常适合有实践项目经验的人。采用TDD的方式使用Spring,而且提供Spring整合其他framework的各种作法 (相当多)。同时作者也维护着一个开源的项目appfuse。 
 
·  Rob Harrop,Jan Machacek:Pro Spring(1/2005)
我目前只看过了样章,还很不错,很系统,也很细致,尤其书中提供的源代码很适合新手练习。
 
预计2005年7月出版,作者阵容挺庞大的,几乎都是Spring开发团队的,值得期待。 
 
·  罗时飞  精通Spring
这好像是目前为止系统介绍Spring的唯一简体中文书籍。
 
以上书籍都在网上提供了书中的源代码下载和部分样章,如果拿不到书,看看源代码也是很不错。
 
 
●  说完书籍,再介绍一些好的论坛。
 
 
 
 
 
最后给大家推荐一个不错的资源收集站点  http://www.java201.com/
在其右上方的搜索栏中填入“spring”,可以获得不少整理好的有关spring的资料。
 
好了,先写到这里了,以后会陆续添加一些好的资料和站点。

快速上手Spring-HelloWorld

这篇文章主要谈谈Spring的入门开发,例子很简单,就是输出一条语句。有关各种软件的配置这里就不在多说了,如有不明白的,可以参考我以前的文章。

一、下载和配置

1. 所需软件

· JDK 5.0
· eclipse-SDK-3.1RC3-win32.zip
· xmlbuddy_2.0.62.zip
· spring-framework-1.2.1-with-dependencies.zip

如果安装后在Eclipse中看不到XmlBuddy,那么在eclipse命令后加上“-clean”参数。


2. 配置

● 配置Eclipse:
· Window->Perferences->Java->Compiler:将"Compiler compliance level"设为“5.0”
· Window->Perferences->Java->Build Path:勾选“Folders”

● 建立库文件夹

具体做法参考《Eclipse快速上手Hibernate--1. 入门实例》一文中的“建立库文件夹”的相关部分。

这里先将spring-framework-1.2.1-with-dependencies.zip解压,将其中的spring.jar(dist目录中)、commons-logging.jar(lib\jakarta-commons目录)、log4j-1.2.9.jar(lib\log4j目录)这三个文件复制到的”D:\java\Spring\lib" 目录中,然后在Eclipse中建立一个“Spring”库,将那三个文件添加进“Spring”库中。


二、项目实践

1. 创建项目

· 新建一个Java Project:SpringHello,注意要导入用户库Spring。

· 这是完成后整个项目的结构(预览一下):


2. 简单的HelloWorld

·下面开始创建一个新类:HelloWorld ;包名:javamxj.spring.beginning1,代码如下:
HelloWorld.java

package javamxj.spring.beginning1;

public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}

OK!非常简单,我就是要它打印出一条语句“Hello World!”。

现在我不想输出“Hello World!”,我想随心所欲的输出任何语句,很简单啊!将“Hello World!”替换成所希望输出的语句即可。不过这样有一个缺点,每次修改好了,都需要重新编译一下程序。像这样的小程序固然无所谓,可是如果是一个大项目,这样做就有些讨厌了。
还好,只要稍微修改一下程序,通过参数输入即可。

· 继续在这个包下建立一个新类:HelloWorldWithCommandLine,同样非常简单:
HelloWorldWithCommandLine.java

package javamxj.spring.beginning1;

public class HelloWorldWithCommandLine {

public static void main(String[] args) {
if (args.length > 0) {
System.out.println(args[0]);
} else {
System.out.println("Hello World!");
}
}
}

· 选中HelloWorldWithCommandLine,右击->Run As->Run...,在弹出窗口切换到“Arguments”栏,在“program arguments”中填入“Hello,javamxj!”,如下图:


这样,就输出了我希望的语句。

Spring之IOC

  Spring的功能是很强大的,在其“绝不发明自己认为好的轮子,而只发明自己认为不好的轮子”的指导思想下,通过充分实践了“一切实事求是、‘循证架构’的工作方式”的理论,基本上把轻量级的J2EE应用框架(如ORM、MVC等)进行了整合,并构架了一些常用的功能(如DAO),形成了一个功能强大的J2EE轻量级企业应用框架。
  然而,或许是大家对Spring掌握得还不透彻的缘故吧,看到很多软件企业中用到的Spring功能,基本上大多数都只是用其IOC功能,有时候附带用了其中的AOP事务管理功能。
  IOC及AOP虽然不是Spring首创,然而其在这两块都是做得很不错的,应该说整个Spring框架就是围绕着其IOC实现及AOP实现架设起来的。我想,深入挖掘IOC、AOP以及Spring中的实现,使用等,对于初学者帮助会非常大,因此,从本期开始,大峡的《玩玩Spring系列》将伴随大家一起走进IOC及AOP的世界。
  由于本人水平有限,文中难免有很多不足甚至错误之处,还请各位朋友不吝批评指教。

一、IOC简介
  IOC-全称Inversion of Control,中文解释:控制反转。另外,IOC又称DI(全称)Dependency Injection,中文解释:依赖注入。
  呵呵,这些名词搞得有点像学古文的味道哈。很大师还都说IOC中有一个著名的好莱坞理论:你呆着别动,到时我会找你。由于本人未到过好莱坞参加过社会实践,因此,这句话理解有点困难。
  IOC是一种新的设计模式,即IOC模式,系统中通过引入实现了IOC模式的IOC容器,即可由IOC容器来管理对象的生命周期、依赖关系等,从而使得应用程序的配置和依赖性规范与实际的应用程序代码分开。其中一个特点就是通过文本的配件文件进行应用程序组件间相互关系的配置,而不用重新修改并编译具体的Java代码。
  当前比较知名的IOC容器有:Pico Container、Avalon 、Spring、JBoss、HiveMind、EJB等,国内由板桥里人负责的国产开源项目Jdon框架,也是具有IOC容器功能(由于没来得及认真研读其源码,似乎jdon中IOC部份是调用Pico的IOC容器功能来实现的)。
  在上面的几个IOC容器中,轻量级的有Pico Container、Avalon、Spring、HiveMind等,超重量级的有EJB,而半轻半重的有容器有JBoss,Jdon等。
  
  IOC究竟是什么?IOC是如何产生的?用在什么场合?为什么我们以前不用IOC,而现在要用IOC?“物有本末,事有终始”,为了更加透彻的理解这一问题,大峡打算从自己所理解的面向对象(OO)设计及编程发展历程来进行分析,也许这样能让IOC的初学者更加了解IOC的发展的前因后果,争取做到“知其然,知其所以然,使其然!”。
  若大家等不急了,就直接百度一下有关IOC的其它文章,这方面国内很多先驱们已经作了很多介绍。如冰云的《IOC详解》、板桥里人的设计模式及IOC理论等。

二、 最老的OO编程
  记得曾经看《Think in Java》最早版本的时候,里面有这么一句让人振奋话:一切都是对象。这时我们OO编程的核心是围绕着面向对象编程的三个特性即“继承”、“封装”、“多态”来展开的。
2.1 封装
  那时我们学会了对现实就的事物及软件模型进行了抽象。比如要描述一只猫,那么这支猫应该有“颜色”、“重量”、“公母”、“脾气”、“出生日期”等属性,另外还有“跑”、“吃”、“叫”、“猫捉老鼠”等方法。如Java代码来表示,大致就是如下:
public Class Cat
{
private String color;//颜色
private String weight;//重量
private String sex;//公母
private String temper;//脾气
private String birthday;//出生日期
private void run ();//跑
private void eat (Food food); //吃(食物)
private void shout(int type);//叫(类别)
private boolean chase(Mice mice);//猫捉老鼠
}

2.2 继承
  最早的OO编程时期,我们还会引入继承,还经常鼓励大家多用继续,认为继承就是OO编程思想的核心。继承的核心就是围绕着如何把类与类之间具有共同特性的部份抽象到基类中。认为这样不但能使用了OO的特性,还减少了很多子类的代码。
  我们通过日常生活的常识知道,猫是一种动物,因此动物有的特性他基本上都有。于是,如果我们的系统中不但有猫,还会有很多其它的动物出现。我们就会设计一个动物类,把所有动物的共性抽象到一个基类中。这里,猫及动物基类的代码大致如下:
public abstract Class Animal{
private String color;//颜色
private String weight;//重量
private String sex;//公母
private String temper;//脾气
private String birthday;//出生日期
private void run ();//跑
private void eat (Food food); //吃(食物)
private void shout(int type);//叫(类别)
}
public Class Cat extends Animal
private int power;//能力
private int agility;//敏捷度
// 猫捉老鼠是特有的方法
private boolean chase(Mice mice) {
return true;
};
}


2.3 多态
  这时我们还会不时使用到OO的另外一个特性多态。多态是很重要的一门技术,然而很多时候却没有很好的理解并使用,回头看以前的代码,我们看到有很多地方属于故弄玄虚的嫌疑。
  接上面的例子,假如我们要写一个喂养宠物(有猫、狗、猪、豹、老鼠等)的程序。利用Java的多态特性,我们的大致代码如下:
public class PetManage {
private Animal pet;
public PetManage(Animal pet)
{
this.pet=pet;
}
//喂食我的宠物
public void feeding(Animal a)
{
}
/**
* @param args
*/
public static void main(String[] args) {
Animal myPet=new Cat();
PetManage mp=new PetManage(myPet);
}
}
  通过使用多态特性,哪一天若我们的不喜欢猫,而是喜欢养猪的时候,只要把new Cat()变成new Pig(),即可。

2.4 对象生命周期
  这一阶段的OO程序中,我们知道要用一个对象的时候,就要使用Java中的关键字new来生成一个来用即可。OO对于我们来说,一切都是那么简单,很多时候甚至感觉OO跟OP的编程方法也没太大区别。代码如下:
Cat myCat=new Cat();//创建一支具体的猫
myCat.shout();//叫一声
  此时,我们对Java虚拟是非常信任的,我们的思想也很单纯,我们知道Java对象的生命开始于new关键词。我们不太关心对象生命的结束,我们知道Java有一个比C语言历害、智能化的垃圾收集器,他会帮我们自己的清理内存中不用的对象。
  当然,也有的人由于对垃圾收集器忠诚度的怀疑,不放心垃圾收集器的能力,于是在程序中经常要加一句类似“myPet=null”的代码来结束对象的生命。
  当然,我们也知道有一些外部资源如数据库连接等,需要手动熟悉资源。于是知道在使用类似资源的时候必须都加上一句:conn.close(),有时候还要在close()后面再加一句:conn=null。呵呵,非常有意思。

2.5小结
  现在看来,其实那时确实犯了很多幼稚的错误,也走了不少的弯路,做了很多画蛇添足的工作,写了很多难与维护的代码。
  对比今天的IOC模式,若要从早的OO方法中硬要找一个类似Spring的容器的话,那就是:“程序员+JVM本身”。是程序员以及JVM一起我们管理对象的生命周期、对象之间的关系等。那时候若有任何变动都需要改代码,(虽然好的设计代码修改会非常少,但也得改!),然后编译,然后拿到测试环境及用户环境中执行。如此反复,年日复一日、年复一年。
  那时我们的代码复用用得最多的就是OO的继承功能,另外还有很多OP方法中带过来的函数。
  本文中涉及到的几个简单源码,请到EasyJF开源团队官网下载,地址:
  http://www.easyjf.com/html/bbs/20060602/12718636-1843943.htm?ejid=1287314863003738

ASP的六大对象说明

Application对象

  Application对象是个应用程序级的对象,用来在所有用户间共享信息,并可以在Web应用程序运行期间持久地保持数据。
Application的属性:
  方法如下:
  Application对象没有内置的属性,但是我们可以自行创建其属性。
  <% Application("属性名")=值 %>
  其实大部分Application变量都 存放在Contents集合中,当你创建一个新的Application变量时,其实就是在Contents集合中添加了一项。下面两个脚本是等效的:
  <% Application("greeting")="hello!" %> 或 <% Application.contents("greeting")="hello!"由于Application变量存在集合里,所以如果想要全部显示,其方法我们已经多次使用,例如For Each循环。  
<%
For Each item IN Application.Contents
Response.write("<br>"&item&Application.Contents(item))
next
%>
Application的方法:
  Application的方法只有两个方法:一个是Lock,另一个是Unlock。其中Lock方法用于保证同一时刻只能一个用户对Application操作。Unlock则用于取消Lock方法的限制。如:
<%
Application.Lock
Application("visitor_num")=Application("visitor_num")+1
Application.Unlock
%>
Application的事件:
1、Application_OnStart()
  当事件应用程序启动时触发。
2、Application_OnEnd()
  此事件应用程序结束时触发。
  这两个事件过程都是必须定义在Global.asp文件中,一般把连接数据的函数定义在这两个事件,然后放在Global.asp中。例如:
Sub Application_OnStart
Application("tt18_ConnectionString") = "driver={SQL
Server};server=jeff;uid=sa;pwd=;database=test"
End Sub
  一个数组能够被定义成Application对象,不过这个数组只能作为一个对象保存,而不能用Application(0)取出其值。可以定义一个临时数组实现这种功能。如:
<%
dim Array()
Array=Application("array")
for i = 0 to ubound(array)
Response.write Array(i)
next i
%>
  同样要修改这个Application对象也可以定义一个临时数组,把Application对象赋给数组,然后修改这个数组的元素,最后把数组赋回Application对象。如:
<%
dim Array()
Array=Application("array")
Array(0)="jeff"
Array(1)="zhu"
Application.lock
Application("array")=Array
Application.unlock
%>
ObjectContext对象

该对象用于控制Active Server Pages的事务处理。事务处理由Microsoft Transaction Server (MTS)管理。

事件

ObjectContext.OnTransactionAbort

由放弃的事务处理事件激发,在脚本完成处理后发生。

ObjectContext.OnTransactionCommit

由成功的事务处理事件激发,在脚本完成处理后发生。

方法

ObjectContext.SetAbort

显式的放弃一次事务处理。

ObjectContext.SetComplete

覆盖前面任何调用ObjectContext.SetAbort方法的调用。
Request对象

Request对象用于接受所有从浏览器发往你的服务器的请求内的所有信息。

集合

Request.ClientCertificate(key[SubField])

所有客户证书的信息的集合。对于Key,该集合具有如下的关键字:

Subject
证书的主题。包含所有关于证书收据的信息。能和所有的子域后缀一起使用。

Issuer
证书的发行人。包含所有关于证书验证的信息。除了CN外,能和所有的子域后缀一起使用。

VadidFrom
证书发行的日期。使用VBScript格式。

ValidUntil
该证书不在有效的时间。

SerialNumber
包含该证书的序列号。

Certificate
包含整个证书内容的二进制流,使用ASN.1格式。


对于SubField,Subject和Issuer关键字可以具有如下的子域后缀:(比如:SubjectOU或IssuerL)

C
起源国家。

O
公司或组织名称。

OU
组织单元。

CN
用户的常规名称。

L
局部。

S
州(或省)。

T
个人或公司的标题。

GN
给定名称。

I
初始。


当文件cervbs.inc(VBScript使用)或cerjavas.inc(Jscript使用)通过使用#INCLUDE导向包含在你的Active Server Page里时,下面两个标志可以使用:

ceCertPresent
指明客户证书是否存在,其值为TRUE或FALSE。

ceUnrecongnizedIssure
指明在该链表中的最后的证书的发行者是否未知,其值为TRUE或FALSE。


Request.Cookies(Cookie[(key).Attribute])

Cookie的集合。允许获得浏览器的Cookie。Cookie指明返回那一个Cookie。Key用于从Cookie字典中返回具有某一关键字的Cookie值。对于Attribute,你能使用属性HasKeys来确定某一Cookie是否具有子关键字。HasKeys的值为TRUE或FALSE。

Request.Form(Parameter)[(Index).Count]

填写在HTML的表单中所有的数据的集合。Parameter是在HTML表单中某一元素的名称。当某一参数具有不止一个值(比如,当在<SELECT>中使用MULTIPLE属性时)时,使用Index。当某一参数具有多值时,Count指明多值个数。

Request.QueryString(Varible)[(Index).Count]

查询字符串的所有值的集合。Varible是在查询字符串某一变量的名称。当某一变量具有多于一个值时,使用Index。当某一参数具有多值时,Count指明值的个数。

Request.ServerVaribles(Server Environment Variable)

环境变量的集合。允许读取HTTP头。你可以通过使用HTTP_前缀来读取任何头信息。比如,HTTP_USER_AGENT接受客户代理HTTP头(浏览器类型)。除此外,你可以使用下表所示的变量获得任何环境信息。

ALL_HTTP
客户端发送的所有HTTP标头,他的结果都有前缀HTTP_。

ALL_RAW
客户端发送的所有HTTP标头,其结果和客户端发送时一样,没有前缀HTTP_

APPL_MD_PATH
应用程序的元数据库路径。

APPL_PHYSICAL_PATH
与应用程序元数据库路径相应的物理路径。

AUTH_PASSWORD
当使用基本验证模式时,客户在密码对话框中输入的密码。

AUTH_TYPE
这是用户访问受保护的脚本时,服务器用于检验用户的验证方法。

AUTH_USER
代验证的用户名。

CERT_COOKIE
唯一的客户证书ID号。

CERT_FLAG
客户证书标志,如有客户端证书,则bit0为0。如果客户端证书验证无效,bit1被设置为1。

CERT_ISSUER
用户证书中的发行者字段。

CERT_KEYSIZE
安全套接字层连接关键字的位数,如128。

CERT_SECRETKEYSIZE
服务器验证私人关键字的位数。如1024。

CERT_SERIALNUMBER
客户证书的序列号字段。

CERT_SERVER_ISSUER
服务器证书的发行者字段

CERT_SERVER_SUBJECT
服务器证书的主题字段。

CERT_SUBJECT
客户端证书的主题字段。

CONTENT_LENGTH
客户端发出内容的长度。

CONTENT_TYPE
客户发送的form内容或HTTP PUT的数据类型。

GATEWAY_INTERFACE
服务器使用的网关界面。

HTTPS
如果请求穿过安全通道(SSL),则返回ON。如果请求来自非安全通道,则返回OFF。

HTTPS_KEYSIZE
安全套接字层连接关键字的位数,如128。

HTTPS_SECRETKEYSIZE
服务器验证私人关键字的位数。如1024。

HTTPS_SERVER_ISSUER
服务器证书的发行者字段。

HTTPS_SERVER_SUBJECT
服务器证书的主题字段。

INSTANCE_ID
IIS实例的ID号。

INSTANCE_META_PATH
响应请求的IIS实例的元数据库路径。

LOCAL_ADDR
返回接受请求的服务器地址。

LOGON_USER
用户登录Windows NT的帐号

PATH_INFO
客户端提供的路径信息。

PATH_TRANSLATED
通过由虚拟至物理的映射后得到的路径。

QUERY_STRING
查询字符串内容。

REMOTE_ADDR
发出请求的远程主机的IP地址。

REMOTE_HOST
发出请求的远程主机名称。

REQUEST_METHOD
提出请求的方法。比如GET、HEAD、POST等等。

SCRIPT_NAME
执行脚本的名称。

SERVER_NAME
服务器的主机名、DNS地址或IP地址。

SERVER_PORT
接受请求的服务器端口号。

SERVER_PORT_SECURE
如果接受请求的服务器端口为安全端口时,则为1,否则为0。

SERVER_PROTOCOL
服务器使用的协议的名称和版本。

SERVER_SOFTWARE
应答请求并运行网关的服务器软件的名称和版本。

URL
提供URL的基本部分。

方法

Request.BinaryRead(Count)

接收一个HTML表单的未经过处理的内容。当调用此方法时,Count指明要接收多少字节。在调用此方法后,Count指明实际上接收到多少个字节。

属性

Request.TotalBytes

查询体的长度,以字节为单位
Response对象

  Response对象用于向客户端浏览器发送数据,用户可以使用该对象将服务器的数据以HTML的格式发送到用户端的浏览器,它与Request组成了一对接收、发送数据的对象,这也是实现动态的基础。下面介绍它常用的属性和方法。

1、Buffer属性  
  该属性用于指定页面输出时是否要用到缓冲区,默认值为False。当它为True时,直到整个Active Server Page执行结束后才会将结果输出到浏览器上。如:
<%Response.Buffer=True%>
<html>
<Head>
<title>Buffer示例</title>
</head>
<body>
<%
for i=1 to 500
response.write(i & "<br>")
next
%>
</body>
</html>
  这页执行时,整个主页的所有内容会同时显示在浏览器上,这个主页会存在缓存区中直到脚本执行结束。

2、Expires属性
  该属性用于设置浏览器缓存页面的时间长度(单位为分),必须在服务器端刷新。通过如下设置:
<%Response.Expires=0%>
  通过在ASP文件中加入这一行代码,要求每次请求是刷新页面,因为Response一收到页面就会过期。

3、Write方法
  该方法把数据发送到客户端浏览器,如:  
<%Response.write "Hello,world!"%>

4、Redirect方法
  该方法使浏览器可以重新定位到另一个URL上,这样,当客户发出Web请求时,客户端的浏览器类型已经确定,客户被重新定位到相应的页面。如:
<html>
<head>
<title>Redirect示例</title>
</head>
<body>
<form aciton="formjump.asp" method="post">
<select >
<option selected value="fun">Fun</option>
<option value="news">News</option>
<option value="sample">Sample</option>
</select>
<input type=submit value="Jump">
</form>
</body>
</html>
  以上是提交的表单,下面是处理表单的文件formjump.asp:
<%response.buff=true%>
<html>
<head>
<title>Redirect示例</title>
</head>
<body>
<%
thisurl="http://www.tinyu.com/";
where=Request.form("wheretogo")
Select Case where
case "fun"
response.redirect thisurl & "/fun/default.asp"
case "news"
response.redirect thisurl & "/news/default.asp"
case "sample"
response.redirect thisurl & "/sample/default.asp"
End Select
%>
</body>
<html>
  这个例子当用户选择了以后,按"Jump"按钮提交表单,服务器接到申请后调用formjump.asp判断后定位到相应的URL。不过这里有一点要注意,HTTP标题已经写入到客户浏览器,任何HTTP标题的修改必须在写入页内容之前,遇到这种问题时,可以如下做:
在文件的开始<@ Language=..>后写:
Response.Buffer=True
在结尾定:
Response.Flush
  这里Flush是Response的一个方法,它必须是Buffer属性设置为True时才能使用,否则会产生一个运行模式错误。另外一个Clear方法也是用于清除被缓存的页面,同样要Buffer属性设置为True时才能使用。

5、End方法
  该方法用于告知Active Server当遇到该方法时停止处理ASP文件。如果Response对象的Buffer属性设置为True,这时End方法即把缓存中的内容发送到客户并清除冲区。所以要取消所有向客户的输出民,可以先清除缓冲区,然后利用End方法。如:
<%
Response.buffer=true
On error resume next
Err.clear
if Err.number<>0 then
Response.Clear
Response.End
end if
%>
Server 对象:

Server 对象提供对服务器上的方法和属性的访问。其中大多数方法和属性是作为实用程序的功能服务的。

语法

Server.property|method

属性

ScriptTimeout:
ScriptTimeout 属性指定脚本在结束前最大可运行多长时间。 当处理服务器组件时,超时限制将不再生效。

语法 Server.ScriptTimeout = NumSeconds

参数 NumSeconds
指定脚本在被服务器结束前最大可运行的秒数。默认值为 90 秒。

注释
通过使用元数据库中的AspScriptTimeout属性可以为 Web 服务或 Web 服务器设置缺省的ScriptTimeout值。ScriptTimeout属性不能设置为小于在元数据库中指定的值。例如,如果NumSeconds设置为10,而元数据库设置包含了默认值90秒,则脚本在90秒后超时。但如果NumSeconds设置为100,则脚本在100秒后超时。

关于使用元数据库的详细信息,参阅 关于元数据库。

示例 以下示例中,如果服务器处理脚本超过 100 秒,将使之超时。
<% Server.ScriptTimeout = 100 %>
以下示例获取 ScriptTimeout 属性当前值,并将其存储在变量 TimeOut 中。
<% TimeOut = Server.ScriptTimeout %>

方法

CreateObject
CreateObject 方法创建服务器组件的实例。如果该组件执行了 OnStartPage 和 OnEndPage 方法,则此时就会调用 OnStartPage 方法。有关服务器组件的详细信息,请参阅 可安装的 ASP 组件 。

语法 Server.CreateObject( progID )

参数 progID 指定要创建的对象的类型。progID 的格式为 [Vendor.] component[.Version]。

注释 默认情况下,由 Server.CreateObject 方法创建的对象具有页作用域。这就是说,再当前 ASP 页处理完成之后,服务器将自动破坏这些对象。要创建有会话或应用程序作用域的对象,可以使用 <OBJECT> 标记并设置 SESSION 或 APPLICATION 的 SCOPE 属性,也可以在对话及应用程序变量中存储该对象。
例如,在如下所示的脚本中,当 Session 对象被破坏,即当对话超时时或 Abandon 方法被调用时,存储在会话变量中的对象也将被破坏。
<% Set Session("ad") = Server.CreateObject("MSWC.AdRotator")%>
可以通过将变量设置为 Nothing 或新的值来破坏对象,如下所示。第一个例子释放 ad 对象,第二个例子用字串代替 ad 。
<% Session ("ad") = Nothing %>
<% Session ("ad") = " Other Valum " %>
不能创建与内建对象同名的对象实例。 例如,下列脚本将返回错误。
<% Set Response = Server.CreateObject("Response") %>

示例 <% Set MyAd = Server.CreateObject("MSWC.AdRotator") %>
上面的例子创建一个名为 MyAd 的 MSWC.AdRotator 服务器组件,MSWC.AdRotator 组件可用于在 Web 页上的自动轮换广告。
关于服务器组件的详细信息, 请参阅 Creating Components for ASP.

HTMLEncode HTMLEncode方法对指定的字符串应用 HTML 编码。

语法 Server.HTMLEncode( string )

参数 string 指定要编码的字符串。

示例脚本 <%= Server.HTMLEncode("The paragraph tag: <P>") %>

输出 The paragraph tag: <P>

注意 以上输出将被 Web 浏览器显示为The paragraph tag: <P>如果查看一下源文件或以文本方式打开一个 Web 页,您就可以看到已编码的 HTML。

MapPath
MapPath 方法将指定的相对或虚拟路径映射到服务器上相应的物理目录上。

语法
Server.MapPath( Path )

参数
Path
指定要映射物理目录的相对或虚拟路径。若 Path 以一个正斜杠 (/) 或反斜杠 (\) 开始,则 MapPath 方法返回路径时将 Path 视为完整的虚拟路径。若 Path 不是以斜杠开始,则 MapPath 方法返回同 .asp 文件中已有的路径相对的路径。
注释
MapPath 方法不支持相对路径语法 (.) 或 (..)。例如,下列相对路径 ../MyDir/MyFile.txt 返回一个错误。

MapPath 方法不检查返回的路径是否正确或在服务器上是否存在。

因为 MapPath 方法只映射路径而不管指定的目录是否存在,所以,您可以先用 MapPath 方法映射物理目录结构的路径,然后将其传递给在服务器上创建指定目录或文件的组件。

示例
对于下列示例,文件data.txt和包含下列脚本的test.asp文件都位于目录C:\Inetpub\Wwwroot\Script下。C:\Inetpub\Wwwroot目录被设置为服务器的宿主目录。

下列示例使用服务器变量 PATH_INFO 映射当前文件的物理路径。脚本
<%= server.mappath(Request.ServerVariables("PATH_INFO"))%><BR>

输出
c:\inetpub\wwwroot\script\test.asp<BR>

由于下列示例中的路径参数不是以斜杠字符开始的,所以它们被相对映射到当前目录,此处是 C:\Inetpub\Wwwroot\Script。脚本
<%= server.mappath("data.txt")%><BR>
<%= server.mappath("script/data.txt")%><BR>

输出
c:\inetpub\wwwroot\script\data.txt<BR>
c:\inetpub\wwwroot\script\script\data.txt<BR>

接下来的两个示例使用斜杠字符指定返回的路径应被视为在服务器的完整虚拟路径。脚本
<%= server.mappath("/script/data.txt")%><BR>
<%= server.mappath("\script")%><BR>

输出
c:\inetpub\script\data.txt<BR>
c:\inetpub\script<BR>

下列示例演示如何使用正斜杠 (/) 或反斜杠 (\) 返回宿主目录的物理路径。脚本
<%= server.mappath("/")%><BR>
<%= server.mappath("\")%><BR>

输出
c:\inetpub\wwwroot<BR>
c:\inetpub\wwwroot<BR>

URLEncode
URLEncode 方法将 URL 编码规则,包括转义字符,应用到指定的字符串。

语法
Server.URLEncode( string )
参数
String 指定要编码的字符串。
示例
脚本 <%Response.Write(Server.URLEncode("http://www.tinyu.com";)) %>

输出 http%3A%2F%2Fwww%2Etinyu%2Ecom
Session对象

  Session其实指的就是访问者从到达某个特定主页到离开为止的那段时间。每一访问者都会单独获得一个Session。在Web应用程序中,当一个用户访问该应用时,Session类型的变量可以供这个用户在该Web应用的所有页面中共享数据;如果另一个用户也同时访问该Web应用,他也拥有自己的Session变量,但两个用户之间无法通过Session变量共享信息,而Application类型的变更则可以实现站点多个用户之间在所有页面中共享信息。

1、SessionID属性
  该属性返回当前会话的唯一标志,为每一个Session分配不同的编号。
  我曾在开发过程中就遇到对用户的控制问题。它要实现的功能就是,针对某个网站的一个模块,当一个会员登录后正在看此模块时,另一个人用同样的会员名登录,就不能浏览这个模块。也就是说一个会员名同时只能一个人浏览此模块。我通过用会员名(假设为UserID,唯一)和SessionID来实现了控制。当会员登录时,给这个会员一个Session记录登录状态如:Session("Status")="Logged",同时把这个会员的Session.SessionID写入数据库。当他要浏览此模块时,先判断其是否登录,若已经登录再判断它的SessionID是否与数据库记录的相同,如果不同则不能访问。这样,当另一个用户用相同的会员名登录时,那么数据库中记录的就是新的SessionID,前者访问此模块时就不能通过检查。这就实现了一个会员名同时只能一个人浏览某个模块。这个功能在一些收费网站有很有特别作用,它防止了一个会员名给多个人浏览的问题,为公司保障了利益。

2、TimeOut属性
  该属性用来定义用户Session对象的时限。如果用户在规定的时间内没有刷新网页,则Session对象就会终止。一般默认为20分钟。  

3、Abandon方法
  该方法是Session对象的唯一方法,可以清除Session对象,用来消除用户的Session对象并释放其所占的资源。如: <% Session.Abandon %>
4、Session_OnStart和Session_OnEnd事件
  和Application一样,当对象的例程每一次启动时触发Session_OnStart事件,然后运行Session_Onstart事件的处理过程。也就是说,当服务器接收到应用程序中的URL的HTTP请求时,触发此事件,并建立一个Session对象。同理,这个事件也必须定在Global.asa文件中。
  当调用Session.Abandon方法时或者在TimeOut的时间内没有刷新,这会触发Session_OnEnd事件,然后执行里面的脚本。Session变量与特定的用户相联系,针对某一个用户赋值的Session变量是和其他用户的Session变量完全独立的,不会存在相互影响。
Session应用一列:
  与Application一样,一个被定义为Session类型的数组只能将整个数组作为一个对象,用户不能直接改变Session数组中某个元素的值。为了创建一个Session数组,需先定义一个普通的数组,并对它的每一个元素赋初值,最后把它定义为一个Session数组。如:
<%
dim array()
array=array("李","明","男")
Session("info")=array
Response.write Session("info")(0) &"-"
Response.write Session("info")(1) &"-"
Response.write Session("info")(2) &"<br>"
%>
<hr>
<%
array(0)="天"
array(1)="宇"
array(2)="男"
Session("info")=array
Response.write Session("info")(0) & "-"
Response.write Session("info")(1) & "-"
Response.write Session("info")(2) & "<br>"
%>

2000年7月25日星期二

Tomcat ユーザ認証

■BASIC 認証

実行環境
JDK 5.0
eclipse 3.2
Tomcat 5.5
Windows XP

J2EE Webアプリケーションでは、安全なアプリケーション設計を行うため、任意のページにセキュリティ制約条件をつけて安全性を高めることができます。
セキュリティ制約条件がつけられたページにアクセスがあった場合、パスワードによるログイン認証を行うことができます。
ログイン認証には、BASIC 認証、DIGEST 認証、FORM 認証、CLIENT-CERT 認証があります。

◆BASIC 認証 : 最も一般的な認証方式。ユーザー名とパスワードをクリアテキストでサーバーに送信する。(パスワードは Base64 エンコード)
◆DIGEST 認証 : パスワードを暗号化して送信する。
◆FORM 認証 : ログインページなどをHTML形式でカスタマイズできる。ユーザー名とパスワードをクリアテキストでサーバーに送信する。
◆CLIENT-CERT 認証 : HTTPS(HTTP over SSL)を使用した認証システム。

ここでは、BASIC 認証を用いて、「secure」フォルダ以下のファイルにセキュリティ制約条件をつけた例をレポート。コンテナは Tomcat を使用し、eclipse + tomcatPlugin を使用して作成します。

ディレクトリ構成は下記のようになります。


web.xml に security-constraint タグと login-config タグを下記のように追加記述します。


security-constraint の子要素



Tomcat 5.5\conf にある tomcat-users.xml を編集します。ここでは、新しいロール名「test」、ユーザー名「test」、パスワード「test」を下記のように定義します。パスワードは仮のものですので、適切に変更してください。


http://localhost:8080/SrvSample/secure/menu.html にアクセスしてみます。


ユーザー名とパスワードを入力して「OK」を押すと menu.html にアクセスできます。


■FORM 認証

実行環境
JDK 5.0
eclipse 3.2
Tomcat 5.5
Windows XP

FORM 認証のは BASIC 認証とよく似ています。FORM 認証では、ログインフォームをHTML形式でカスタマイズできます。また、ログインに失敗した場合に転送するエラーページもカスタマイズできます。

ここでは、ログインページを login.html、エラーページを error.html とし、前の BASIC 認証を FORM 認証に書き換えてみます。

ディレクトリ構成は下記のようになります。


BASIC 認証で記述した web.xml の login-config タグの部分を下記のように書き換えます。


login.html を記述します。送信先は「j_security_check」、ユーザー名は「j_username」、パスワードは「j_password」と設定します。これはユーザー名とパスワードが「j_security_check」という予約された場所に送られることを意味しています。変更することはできません。


Tomcat を再起動して、http://localhost:8080/SrvSample/secure/menu.html にアクセス。


■JDBCRealm にる認証

実行環境
JDK 5.0
Tomcat 5.5
MySQL 5.0
Windows XP

BASIC 認証や FORM 認証は、Tomcat のデフォルトの UserDatabaseRealm で行います。UserDatabaseRealm では、ユーザー名、パスワード、ロール名を tomcat-users.xml から読み込んで使用しますが、ユーザーが多数になると管理が困難になるでしょう。
JDBCRealm を使うとデータベースにユーザー名、パスワード、ロール名を登録して管理することができます。ユーザー数の拡大に対応しやすくなるでしょう。

☆server.xml の設定

JDBCRealm 方式を使用するには Tomcat の server.xml Engine タグ内の150行目あたりを下記のように書き換えます。ここではデータベースに MySQL を使用し、データベース名「authority」、データベースユーザー名「authority」、パスワード「authority」と設定します。


☆データベースの設定

MySQL にデータベース名「authority」、ユーザー名「authority」、パスワード「authority」を設定します。

SQL文
grant all privileges on authority.* to authority@localhost identified by 'authority';
flush privileges;
create database authority;

「users」テーブルと「user_roles」テーブルを設定します。
ユーザー名「test」、パスワード「test」、ロール名「test」を登録します。
JDBCRealm 方式に切り替えると、tomcat-users.xml にデフォルトで記述してある「tomcat」、「admin」などのユーザー名とロール名もデータベースに登録し直す必要があります。


設定はこれで完了です。

2000年7月23日星期日

C/S和B/S

一、什么是C/S和B/S

要想对“C/S”和“B/S”技术发展变化有所了解,首先必须搞清楚三个问题。

第一、什么是C/S结构。

C/S (Client/Server)结构,即大家熟知的客户机和服务器结构。它是软件系统体系结构,通过它可以充分利用两端硬件环境的优势,将任务合理分配到Client端和Server端来实现,降低了系统的通讯开销。目前大多数应用软件系统都是Client/Server形式的两层结构,由于现在的软件应用系统正在向分布式的Web应用发展,Web和Client/Server 应用都可以进行同样的业务处理,应用不同的模块共享逻辑组件;因此,内部的和外部的用户都可以访问新的和现有的应用系统,通过现有应用系统中的逻辑可以扩展出新的应用系统。这也就是目前应用系统的发展方向。

传统的C/S体系结构虽然采用的是开放模式,但这只是系统开发一级的开放性,在特定的应用中无论是Client端还是Server端都还需要特定的软件支持。由于没能提供用户真正期望的开放环境,C/S结构的软件需要针对不同的操作系统系统开发不同版本的软件,加之产品的更新换代十分快,已经很难适应百台电脑以上局域网用户同时使用。而且代价高, 效率低。

第二、什么是B/S结构。

B/S(Browser/Server)结构即浏览器和服务器结构。它是随着Internet技术的兴起,对C/S结构的一种变化或者改进的结构。在这种结构下,用户工作界面是通过WWW浏览器来实现,极少部分事务逻辑在前端(Browser)实现,但是主要事务逻辑在服务器端(Server)实现,形成所谓三层3-tier结构。这样就大大简化了客户端电脑载荷,减轻了系统维护与升级的成本和工作量,降低了用户的总体成本(TCO)。

以目前的技术看,局域网建立B/S结构的网络应用,并通过Internet/Intranet模式下数据库应用,相对易于把握、成本也是较低的。它是一次性到位的开发,能实现不同的人员,从不同的地点,以不同的接入方式(比如LAN, WAN, Internet/Intranet等)访问和操作共同的数据库;它能有效地保护数据平台和管理访问权限,服务器数据库也很安全 。特别是在JAVA这样的跨平台语言出现之后,B/S架构管理软件更是方便、快捷、高效。

第三、管理软件主流技术。

管理软件技术的主流技术与管理思想一样,也经历了三个发展时期。首先,界面技术从上世纪DOS字符界面到Windows图形界面(或图形用户界面GUI),直至Browser浏览器界面三个不同的发展时期。其次,今天所有电脑的浏览器界面,不仅直观和易于使用,更主要的是基于浏览器平台的任何应用软件其风格都是一样的,使用人对操作培训的要求不高,而且软件可操作性强,易于识别;再者,平台体系结构也从过去单用户发展到今天的文件/服务器(F/S)体系、客户机/服务器(C/S)体系和浏览器/服务器(B/S)体系。

二、C/S和B/S 之比较

C/S和B/S是当今世界开发模式技术架构的两大主流技术。C/S是美国 Borland公司最早研发,B/S是美国微软公司研发。目前,这两项技术以被世界各国所掌握,国内公司以C/S和B/S技术开发出产品也很多。这两种技术都有自己一定的市场份额和客户群,各家企业都说自己的管理软件架构技术功能强大、先进、方便,都能举出各自的客户群体,都有一大群文人墨客为自己摇旗呐喊,广告满天飞,可谓仁者见仁,智者见智。

1、C/S架构软件的优势与劣势

(1)、应用服务器运行数据负荷较轻。

最简单的C/S体系结构的数据库应用由两部分组成,即客户应用程序和数据库服务器程序。二者可分别称为前台程序与后台程序。运行数据库服务器程序的机器,也称为应用服务器。一旦服务器程序被启动,就随时等待响应客户程序发来的请求;客户应用程序运行在用户自己的电脑上,对应于数据库服务器,可称为客户电脑,当需要对数据库中的数据进行任何操作时,客户程序就自动地寻找服务器程序,并向其发出请求,服务器程序根据预定的规则作出应答,送回结果,应用服务器运行数据负荷较轻。

(2)、数据的储存管理功能较为透明。

在数据库应用中,数据的储存管理功能,是由服务器程序和客户应用程序分别独立进行的,前台应用可以违反的规则,并且通常把那些不同的(不管是已知还是未知的)运行数据,在服务器程序中不集中实现,例如访问者的权限,编号可以重复、必须有客户才能建立定单这样的规则。所有这些,对于工作在前台程序上的最终用户,是“透明”的,他们无须过问(通常也无法干涉)背后的过程,就可以完成自己的一切工作。在客户服务器架构的应用中,前台程序不是非常“瘦小”,麻烦的事情都交给了服务器和网络。在C/S体系的下,数据库不能真正成为公共、专业化的仓库,它受到独立的专门管理。

(3)、C/S架构的劣势是高昂的维护成本且投资大。

首先,采用C/S架构,要选择适当的数据库平台来实现数据库数据的真正“统一”,使分布于两地的数据同步完全交由数据库系统去管理,但逻辑上两地的操作者要直接访问同一个数据库才能有效实现,有这样一些问题,如果需要建立“实时”的数据同步,就必须在两地间建立实时的通讯连接,保持两地的数据库服务器在线运行,网络管理工作人员既要对服务器维护管理,又要对客户端维护和管理,这需要高昂的投资和复杂的技术支持,维护成本很高,维护任务量大。

其次,传统的C/S结构的软件需要针对不同的操作系统系统开发不同版本的软件,由于产品的更新换代十分快,代价高和低效率已经不适应工作需要。在JAVA这样的跨平台语言出现之后,B/S架构更是猛烈冲击C/S,并对其形成威胁和挑战。

2、B/S架构软件的优势与劣势

(1)、维护和升级方式简单。

目前,软件系统的改进和升级越来越频繁,B/S架构的产品明显体现着更为方便的特性。对一个稍微大一点单位来说,系统管理人员如果需要在几百甚至上千部电脑之间来回奔跑,效率和工作量是可想而知的,但B/S架构的软件只需要管理服务器就行了,所有的客户端只是浏览器,根本不需要做任何的维护。无论用户的规模有多大,有多少分支机构都不会增加任何维护升级的工作量,所有的操作只需要针对服务器进行;如果是异地,只需要把服务器连接专网即可,实现远程维护、升级和共享。所以客户机越来越“瘦”,而服务器越来越“胖”是将来信息化发展的主流方向。今后,软件升级和维护会越来越容易,而使用起来会越来越简单,这对用户人力、物力、时间、费用的节省是显而易见的,惊人的。因此,维护和升级革命的方式是“瘦”客户机,“胖”服务器。

(2)、成本降低,选择更多。

大家都知道windows在桌面电脑上几乎一统天下,浏览器成为了标准配置,但在服务器操作系统上windows并不是处于绝对的统治地位。现在的趋势是凡使用B/S架构的应用管理软件,只需安装在Linux服务器上即可,而且安全性高。所以服务器操作系统的选择是很多的,不管选用那种操作系统都可以让大部分人使用windows作为桌面操作系统电脑不受影响,这就使的最流行免费的Linux操作系统快速发展起来,Linux除了操作系统是免费的以外,连数据库也是免费的,这种选择非常盛行。

比如说很多人每天上“网易”(原文为新浪)网,只要安装了浏览器就可以了,并不需要了解“网易”的服务器用的是什么操作系统,而事实上大部分网站确实没有使用windows操作系统,但用户的电脑本身安装的大部分是windows操作系统。

(3)、应用服务器运行数据负荷较重。

由于B/S架构管理软件只安装在服务器端(Server)上,网络管理人员只需要管理服务器就行了,用户界面主要事务逻辑在服务器(Server)端完全通过WWW浏览器实现,极少部分事务逻辑在前端(Browser)实现,所有的客户端只有浏览器,网络管理人员只需要做硬件维护。但是,应用服务器运行数据负荷较重,一旦发生服务器“崩溃”等问题,后果不堪设想。因此,许多单位都备有数据库存储服务器,以防万一。

3,C/S 与 B/S 区别

Client/Server是建立在局域网的基础上的,Browser/Server是建立在广域网的基础上的。

(1)、硬件环境不同:

C/S 一般建立在专用的网络上, 小范围里的网络环境, 局域网之间再通过专门服务器提供连接和数据交换服务。

B/S 建立在广域网之上的, 不必是专门的网络硬件环境,例如电话上网,租用设备, 信息自己管理, 有比C/S更强的适应范围, 一般只要有操作系统和浏览器就行。

(2)、对安全要求不同

C/S 一般面向相对固定的用户群, 对信息安全的控制能力很强。一般高度机密的信息系统采用C/S 结构适宜,可以通过B/S发布部分可公开信息。

B/S 建立在广域网之上, 对安全的控制能力相对弱, 面向是不可知的用户群。

(3)、对程序架构不同

C/S 程序可以更加注重流程,可以对权限多层次校验,对系统运行速度可以较少考虑。

B/S 对安全以及访问速度的多重的考虑, 建立在需要更加优化的基础之上。 比C/S有更高的要求,B/S结构的程序架构是发展的趋势,从MS的.Net系列的BizTalk 2000 Exchange 2000等,全面支持网络的构件搭建的系统。SUN和IBM推的JavaBean构件技术等,使B/S更加成熟。

(4)、软件重用不同

C/S 程序可以不可避免的整体性考虑, 构件的重用性不如在B/S要求下的构件的重用性好。

B/S 对的多重结构,要求构件相对独立的功能。 能够相对较好的重用。就如买来的餐桌可以再利用,而不是做在墙上的石头桌子。

(5)、系统维护不同

系统维护是软件生存周期中,开销大,相当重要

C/S 程序由于整体性,必须整体考察,处理出现的问题以及系统升级难,可能是再做一个全新的系统。

B/S 构件组成方面构件个别的更换,实现系统的无缝升级。系统维护开销减到最小,用户从网上自己下载安装就可以实现升级。

(6)、处理问题不同

C/S 程序可以处理用户面固定,并且在相同区域, 安全要求高的需求,与操作系统相关,应该都是相同的系统。

B/S 建立在广域网上, 面向不同的用户群,分散地域, 这是C/S无法作到的,与操作系统平台关系最小。

(7)、用户接口不同

C/S 多是建立在Window平台上,表现方法有限,对程序员普遍要求较高。

B/S 建立在浏览器上, 有更加丰富和生动的表现方式与用户交流, 并且大部分难度减低,降低开发成本。

(8)、信息流不同

C/S 程序一般是典型的中央集权的机械式处理,交互性相对低。

B/S 信息流向可变化, B-B、 B-C、 B-G等信息流向的变化, 更象交易中心

一、C/S结构与B/S结构的特点分析

随着计算机技术的不断发展与应用,计算模式从集中式转向了分布式,尤为典型的是C/S结构(Client/Server的简称,客户机/服务器模式)。两层结构C/S模式,在上个世纪八十年代及九十年代初得到了大量应用,最直接的原因是可视化开发工具的推广。之后,它开始向三层结构发展。近年来,随着网络技术不断发展,尤其是基于Web的信息发布和检索技术、Java计算技术以及网络分布式对象技术的飞速发展,导致了很多应用系统的体系结构从C/S结构向更加灵活的多级分布结构演变,使得软件系统的网络体系结构跨入一个新阶段,即B/S体系结构(Browser/Server的简称,浏览器/服务器模式)。基于Web的B/S方式其实也是一种客户机/服务器方式,只不过它的客户端是浏览器。为了区别于传统的C/S模式,才特意将其称为B/S模式。认识到这些结构的特征,对于系统的选型而言是很关键的。

1、系统的性能

在系统的性能方面,B/S占有优势的是其异地浏览和信息采集的灵活性。任何时间、任何地点、任何系统,只要可以使用浏览器上网,就可以使用B/S系统的终端。

不过,采用B/S结构,客户端只能完成浏览、查询、数据输入等简单功能,绝大部分工作由服务器承担,这使得服务器的负担很重。采用C/S结构时,客户端和服务器端都能够处理任务,这虽然对客户机的要求较高,但因此可以减轻服务器的压力。而且,由于客户端使用浏览器,使得网上发布的信息必须是以HTML格式为主,其它格式文件多半是以附件的形式存放。而HTML格式文件(也就是Web页面)不便于编辑修改,给文件管理带来了许多不便。

2、系统的开发

C/S结构是建立在中间件产品基础之上的,要求应用开发者自己去处理事务管理、消息队列、数据的复制和同步、通信安全等系统级的问题。这对应用开发者提出了较高的要求,而且迫使应用开发者投入很多精力来解决应用程序以外的问题。这使得应用程序的维护、移植和互操作变得复杂。如果客户端是在不同的操作系统上,C/S结构的软件需要开发不同版本的客户端软件。

但是,与B/S结构相比,C/S技术发展历史更为“悠久”。从技术成熟度及软件设计、开发人员的掌握水平来看,C/S技术应是更成熟、更可靠的。

3、系统的升级维护

C/S系统的各部分模块中有一部分改变,就要关联到其它模块的变动,使系统升级成本比较大。B/S与C/S处理模式相比,则大大简化了客户端,只要客户端机器能上网就可以。对于B/S而言,开发、维护等几乎所有工作也都集中在服务器端,当企业对网络应用进行升级时,只需更新服务器端的软件就可以,这减轻了异地用户系统维护与升级的成本。如果客户端的软件系统升级比较频繁,那么B/S架构的产品优势明显——所有的升级操作只需要针对服务器进行,这对那些点多面广的应用是很有价值的,例如一些招聘网站就需要采用B/S模式,客户端分散,且应用简单,只需要进行简单的浏览和少量信息的录入。

在系统安全维护上,B/S则略显不足,B/S结构尤其得考虑数据的安全性和服务器的安全性,毕竟现在的网络安全系数并不高。以OA软件为例,B/S结构要实现办公协作过程中复杂的工作流控制与安全性控制,还有很多技术上的难点。因此,当前虽然出现了B/S结构的OA系统产品,但尚未大范围推广。

设计模式的实际应用

通常,概念和这些概念在现实世界中的应用是有区别的,设计模式也不例外。

  设计模式无处不在。在阅读技术方面的出版物或者浏览技术方面的网站时,很容易发现对设计模式的引用。到目前为止,您很可能已经阅读过(至少翻阅过)一些设计模式方面的书籍,如《Core J2EE Design Patterns》或者Gang of Four编写的《Design Patterns》。此时,您可能会对设计模式有一些疑问。设计模式如何帮助我?他们是银弹吗?使用设计模式有什么问题吗?为什么我不能从集成开发环境(integrated development environment,IDE)中获得设计模式?
  上述的几个问题是采用设计模式进行处理过程中遇到的一些经典问题。通常,概念和这些概念在显示世界中的应用是有区别的,设计模式也不例外。本文将讨论设计模式在现实世界中的应用。这些信息可以帮助您成功地在项目中采用设计模式来作出正确的决定。

快速概述
  设计模式提供了一种共享经验的方式,可以使团体受益和避免不断的重复发明。设计模式通常捕捉问题的描述、问题的语境、推荐的问题解决方案以及使用解决方案后可以预见到的结果。为了具有最广泛的适用性(从而对更多的读者有用),设计模式通常从取决于环境的精确细节中抽象而来。这种抽象性产生了一些把设计模式应用到现有的案例中所必需的译码。这是一个重要细节:尽管设计模式是共享专业知识的好方法,但通常它对正确应用专业知识是非常重要的。
  设计模式这个概念最初产生于建筑行业。设计师(设计建筑物而不是计算机系统)意识到他们需要共享有关正确设计技术的想法。这些想法是在可以使设计师团体从分享经验和教训中获益的设计模式中形成的。设计模式在80年代后期从建筑业进入计算机系统领域。面向对象(Object-oriented,OO)原则逐渐得到普及,而设计模式成为培育新的OO追随者的最佳实践。
  Richard Gamma等(人们通常把他们称作 Gang of Four [GoF] )编著的《Design Patterns: Elements of Reusable Object-Oriented Software》一书使设计模式成为万众瞩目的焦点。随着设计模式逐渐普及,他们所涉及的领域就像“Ben and Jerry”效应那样也逐渐广泛起来。对那些不熟悉著名冰淇淋品牌的人来说,Ben and Jerry是一家冰淇淋产品的供应商,其冰淇淋产品拥有各种可以想象得到的配料组合(还包括一些您永远想象不到的)。因此,它就是设计模式,和普通的OO设计模式一样来源于GoF的著作,但是现在包括了专为开发语言、应用服务器、行业合成等提供的设计模式。

设计模式分类
  设计模式通常根据一些公共特性而组合在一起。GoF的著作把设计模式划分为三类:Creational、Behavioral和Structural。用于J2EE的设计模式通常划分为表现层(Presentation Tier)、业务逻辑层(Business Logic Tier)和集成层(Integration Tier)。这种分组方式可以使描述所有设计模式共享的公共细节更加轻松,或者使设计模式的分类和发现更加轻松。
  在对设计模式实际应用的讨论中,需要把设计模式划分为两类:broad exposure和isolated use。这种划分基于设计模式对应用程序设计人员和开发人员的可见性和应用程序的多个部分对设计模式的相依性。
  Broad exposure 设计模式因为可以影响多个团队成员或者应用程序的多个方面的设计和开发而闻名。这类设计模式的品质包括:
采用它会对很多根据设计模式创建的类产生负面影响。
应用程序的不同部分知道设计模式的使用。
使用这种设计模式的决定不能轻易取消。
  这类设计模式的例子有Model-View-Controller(MVC)模式、Value Object J2EE模式和Data Access Object(DAO)J2EE模式
  Isolated use是指设计模式的使用是隐藏细节的设计模式。这类设计模式的品质包括:

设计模式不影响其他团队成员或者应用程序其他部分的工作。
可以轻松地更改使用设计模式的决定,而且产生的影响极小。
  这类设计模式的例子有Singleton GoF模式或者Intercepting Filter J2EE模式。
  将设计模式划分为几类为了解设计模式的范围提供了一种快速的方法。了解范围使评估设计模式的影响更加轻松。可以使用或者抛弃这种设计模式吗?一旦采用这种设计模式就会影响应用程序的设计吗?这种设计模式影响了应用程序的多个部分和其他的应用程序了吗?预先了解这些影响为采用设计模式提供了指导。

设计模式应用AntiPatterns
  随着设计模式逐渐普及,出现了另一种叫做AntiPatterns的模式类型。尽管设计模式提供了关于可重复的最佳方法的专业知识,但是AntiPatterns通常描述应当避免的重复行为。AntiPatterns 验证了这样的事实:做错事情和办对事情的人一样多。
  本节将探讨设计模式采用中的AntiPatterns。了解这些AntiPatterns可以帮助您避免设计模式采用中的缺陷。如同设计模式一样,在他们提供了一些远见或者他们是一些非常熟悉的环境时,在他们可以为您的经验添加色彩和使您不再感到孤独时,此处的AntiPatterns是一个全新的概念。如果您想阅读更多有关AntiPatterns的资料,请参见本文结尾处的资源列表。

AntiPattern清单
设计模式?是的,我们全部拥有
  问题:决定在项目中使用哪一种设计模式。
  应用: 既有broad exposure又有isolated use设计模式。
  环境:一位开发人员通过介绍希望在一项工程中使用设计模式。
  动力:AntiPattern的动力通常有两种来源。一种是开发人员通过包括设计模式的最佳实践来改进项目的渴望。另一种是开发人员天生的好奇心驱使他利用这个项目来研究设计模式。
  推荐的解决方案:项目中应用了所有知名的设计模式。设计模式手册生成一份清单,而目标是可以核对所有的设计模式。
  产生的语境:项目团队和交付的应用程序由于不自然地引入太多设计模式而遭受损失。这就导致设计和开发非常复杂。这种不必要的复杂性会从已经完成的工作量、开发团队了解发生事情的能力、应用程序的实际性能和功能的正确性等方面影响开发成果。
  设计基本原理:设计模式是专业知识的主要来源。尽管使用他们的效果很好,但是全部使用他们就未必也是好的。
实际解决方案:设计模式的描述包含了使用模式的目标语境。必须考虑如何确保设计模式匹配项目。第二,设计模式不是来源于当某人阅读了一本设计模式的著作后,问:“我可以把这个设计模式使用在什么地方?”而是来源于某人寻找已发现问题的解决方案。

Developer/Project AntiPattern的实现
  (也称为:Design pattern xyz? Yeah,我们有10个)
  问题:在项目中或者项目之间控制设计模式的实现。
  应用:broad exposure和isolated use设计模式都从解决这种环境中受益。但是,broad exposure设计模式无疑控制了实现。
  语境:开发团队将设计模式结合到项目中。团队由许多经验丰富的开发人员组成,他们知道应该什么时候使用设计模式。所以会正确的设计模式。如果涉及到多个项目,项目之间没有设计模式实现共享。
  动力:最终期限日益临近,团队成员工作效率很高。重新使用实现会影响团队效率。假设他们都是专家,他们的实现都非常优秀。在多项目情况中,跨团队通信和代码共享要么没有被考虑,要么被作为进度表中的潜在影响被排除。
  推荐的解决方案:团队可以根据需要单独包含和实现设计模式。
  产生的语境:即使使用了正确的设计模式,但是他们是以很多不同的方式实现的。在限制集成和重新使用成果的实现之间存在不兼容。很多不必要的时间和工作被花费在维护、调试和扩展各种实现上。最终,各种实现都将被统一。
  设计基本原理:应当允许专家成员独立工作。只要所包含的设计模式足够好,就不需要共享实现。
  实际解决方案:开发团队应当协调设计模式的使用。共享设计模式的公共实现可在将来降低成本,但是更重要的是,它使开发人员之间互相兼容。如果需要,这种共享可以被限制到划归先前讨论的broad exposure设计模式内。重用实现在项目间也极有价值,尤其在未来将要集成的时候。

设计模式采用中IDE的角色
  IDE在继续发展和提供更多的功能。最初的IDE组成了一种编辑环境和一些调试工具。现在,他们通常包含设计环境、审计工具、配置管理系统集成等等。随着IDE不断扩展范围,需要确认他们在设计模式实现中的角色。诚然,设计模式在开发语言中实现,而IDE可以用于编辑源代码。但是,IDE可以扮演其他的角色吗?
  一些IDE具有下拉菜单,使您能够选择应用程序中包括的设计模式。虽然这可以加快设计模式的使用,但是它只会导致更快地编写出极差的代码。评估这个特性需要记住几个因素。
  第一,设计模式在抽象中描述问题,并需要一些译码来达到正确的实现。但是,他们常常包含“示例实现(sample implementation)”,并且IDE正是将这种示例类结构插入到应用程序中。这很可能不是所需要的实现,并且把他们放到应用程序中将带来更多的困惑,以及需要更多的编辑和重构工作而不是思考最初的实现。
  第二,和IDE拖放设计模式方法有关的另一个问题是前面讨论的两种AntiPatterns。加快设计模式的实现很可能会产生大量的设计模式应用,以及同一设计模式的多种版本,而不是解决任意问题的版本。
  设计模式面临的挑战不仅仅是得到一次快速实现,而是确定使用了正确的实现,以及机构中已有的一个完美的实现。

BEA WebLogic Workshop 8.1和设计模式
  您可能是一位BEA的客户,如果您正在阅读本文,您可能想知道新的BEA WebLogic Workshop 8.1是如何影响您的设计模式考虑的。首先,WebLogic Workshop是IDE,因此前面有关IDE的章节同样适用。对这些讨论感兴趣的Workshop的两个额外方面是控件和预实现的设计模式。
  WebLogic Workshop Controls是打包功能的一种方法,可以轻松将其包含到使用Workshop IDE的应用程序中。打包包括IDE必需的可视元素、运行时行为、要求的配置等等。控件是如何影响设计模式应用的呢?还记得设计模式在前面划分为isolated use和broad exposure吗?划分到isolated use类的设计模式可能被打包成 Workshop Controls。把设计模式作为控件打包可使 Workshop IDE的其他用户共享实现,从而避免了每一个Developer/Project AntiPattern中的实现。
  您可能想知道为什么broad exposure设计模式为什么不可以作为控件实现。原因是broad exposure设计模式导致创建了许多其他类或者独立于其他应用程序。这种情况就不适合控件的即插即用方面。broad exposure设计模式的采用应当三思而后行,一旦采用就不能轻易取消。这些要求不符合WebLogic Workshop Control的目标。
  WebLogic Workshop还具有很多预实现设计模式,如Pageflow和用户接口结构。在Workshop 中,您可以创建JSP和定义Pageflow来控制Web应用程序页面之间的定位。在这种情况下,WebLogic Workshop使用流行的Apache Struts 表现层框架。Workshop的这个方面(使用 Struts)提供了一种Model-View-Controller(MVC)设计模式实现,意味着不用创建自己的MVC实现。Workshop包含的其他功能很可能替代您自己的设计模式实现。尽管一些设计模式实现的开盒即用很好,但是应当验证不仅实现而且实现创建的WebLogic任何依从性也非常合适。

成功采用设计模式的三个步骤
  如何把设计模式的采用和日益临近的最后期限、紧缩的预算和很多公司现有的有限团队资源相结合?以下是成功制订设计模式的三个步骤。

强大的通信和培训
  许多机构拥有领先技术,可能正式通过了设计师论坛的论证或者非正式的公认专家。这些领先厂商将推广设计模式采用中的开放通信,并将培训开发具体设计模式的团队。通信应当跨开发团队和项目以便预先防止采用竖井和多种惟一的实现(谨记每个Developer/Project AntiPattern的实现)。培训可以采用正式的internal lunch-and-learns、正式的internal class或者派一些员工参加外部培训。这些培训方式将促进正确的设计模式应用程序。如果仅有极少的观众能够参加培训,最佳的候选人是那些感觉适合在回来后能够培训其同事的人。

设计模式采用指导
  设计模式可用于使项目受益,但是他们也可能因为误用而对应用程序造成损害。应当鼓励采用他们,但是对其的采用应当受到审阅和验证。设计模式可以包含在设计和开发过程中。在任何一种情况中,设计模式的使用应当由审阅者确认和验证。在审阅过程中还可能会遇到这样的情况,额外的设计模式不适用于最初包括的地方。即使环境中没有进行正式的审阅,这一步骤也可以通过同事审阅或者团队讨论来完成。这一步骤中的审阅者要么是主要团队的成员,要么与他们建立开放通信。
  指导采用对于broad exposure类别的设计模式非常关键。这些设计模式具有很多相关的风险,因为他们将创建依赖性。这些依赖性可能在一些对象类中,例如,只工作在更加广泛的DAO设计模式实现范围中的数据访问对象(DAO)、或者跨应用程序边界(如使用Value Object设计模式在应用程序和应用程序层之间传输数据)。这些设计模式也可以由项目中的其他人或者不同项目的人实现,而且实现应当重新使用,不同于创建另一种独特的实现。

重用实现,不只是设计模式
  只要在创建自己的设计模式实现中有一定的满足,团队和公司就可以在重用发生在代码层时,而不是设计创意层时获得更多益处。使企业获益的最初设计模式是改进的实现。但是,真正的目标是重用实现。重用实现将导致:a)其他可重用的类(取决于公共实现);b)缩短开发时间和降低成本;c)缩短维护时间和降低成本;d)在应用程序之间和内部轻松集成。
  这种重用对broad exposure设计模式非常重要(有时是基本的)。这些设计模式创建了外部依赖性(集成将从公共实现中受益)或者产生全部的自定义类库(如果有公共基础将可重用)。isolated use设计模式也可以从重用中获益,但是如果他们是根据具体情况定制的,他们就非常难以重用。
  有时您可能会问自己:“如果重用比较好,为什么设计模式和可以重用的实现不可以一同应用呢?”在我们讨论设计模式如何使更多读者获益的时候才会讨论这个问题。如果可能,如果已经预定义了实现,那么达到广泛适用性这个目标就会非常困难。然而,一旦设计模式被应用到特殊的问题域或者技术基础设施中,那么就可以重用在该环境中产生的实现。

架构中的设计模式
  这看起来像是一件可怕的任务,需要掌握设计模式如何应用在实际情况中,如何构建优质的实现,以及如何促进重用实现。完成该任务的方法之一就是在环境中引入应用程序架构。应用程序架构提供了应用程序需要的结构,从而使开发团队可以关注应用程序的域逻辑。这包含了已实现的设计模式。除了重用设计模式概念或者单个实现之外,可以在多个项目和应用程序之间重用架构。这种共享的公共实现确保了兼容性,并为开发和维护多种不同的实现提供了一种低成本替代方案。兼容性提供了重新使用需要的技术基础。没有足够的篇幅在这里深入讨论架构的其他重要品质,如运行时监测和管理、可配置应用程序逻辑和适应性行为等。您可以从Carnegie Mellon Software Engineering Institute (www.sei.cmu.edu/ata/ata_init.html) 中学习到更多有关架构的知识。

结束语
  设计模式是一种令人惊异的资源,应该使用他以增加您的优势。虽然设计模式提供了可重用的概念,但是面临的挑战是决定使用哪一种设计模式和致力于可以重用的实现。通过了解采用设计模式中会产生的风险,就可以在继续学习和实现更多设计模式时避免风险。
  按照本文概述的步骤会产生一个流程,用于在团队和机构中推广成功的设计模式采用。

2000年7月22日星期六

Struts編

サンプル参照:http://www.hellohiro.com/struts.htm

XML編

XML(SAX)編
◆SAXとは?
SAXはSimple API for XMLの略です。DOM(Document Object Model)と並んでXMLを解析するために使用する標準APIです。SAXはXML文書を先頭から読んでいきそこで発生したイベントをアプリケーションでとらえて処理するAPIです。DOMに比べて、メモリ消費量が少ない、解析速度が速いなどのメリットがありますが、ノードの追加、削除など、XML文書を更新する機能はありません。
XML(SAX)編 参照⇒ http://www.hellohiro.com/xmlsax.htm

XML(DOM)編
◆DOMとは?
DOMはDocument Object Modelの略です。XMLにアクセスするためのAPI(Application Programming Interface)です。W3Cによって仕様が公開されています。DOMは、XML文書をツリー構造として扱います。これを「DOMツリー」といいます。XML文書全体を解析してDOMツリーとしてメモリ上に保存するのでデータの多いXML文書を解析する場合は多くのメモリを消費します。

◆DOMツリーとは?
DOMツリーとはDOMがメモリ上に作成したXML文書全体の階層構造のことです。XML文書は階層構造で表現することができます。
XML(DOM)編 参照⇒ http://www.hellohiro.com/xmldom.htm

2000年7月19日星期三

システム設計概念

【基本設計】
【①】業務フロー:業務の流れを理解し、機能を洗い出す。
【②】機能一覧表:開発範囲となる機能(画面・帳票・バッチ)の一覧。
【③】ネットワーク構成図:システム構成を把握。
【④】テーブル定義:SIObject Browser使用。
【⑤】ER 図:SIObject Browser使用。
【⑥】画面遷移図:画面遷移を図示。
【⑦】機能設計書:機能別の設計書。
【⑧】表紙。
【⑨】I/O関連図:データと機能の関係を図示。
【⑩】画面レイアウト:画面イメージ。
【⑪】帳票レイアウト:帳票イメージ。
【⑫】設計書記述様式:設計書の記述方法の説明。

【詳細設計】
【①】機能設計書:機能別の設計書。
【②】表紙。
【③】目次・概要。
【④】フローチャート:バッチ処理のフローチャート。
【⑤】項目説明書:画面・帳票の項目説明。
【⑥】イベント一覧:画面操作で発生するイベントの一覧。
【⑦】BL一覧:機能に含まれるビジネスロジックの一覧。
【⑧】更新仕様書:機能またはBL単位の更新・処理内容を記述。
【⑨】補足説明書:上記の記述に対する補足説明。

2000年7月18日星期二

Web开发 用于操作数据库


package test;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;

public class TestDAO {
private Connection conObj = null;

public TestDAO() {
if(conObj==null){
try {
conObj=TestDBAccess.getInstance().getConnection();
} catch (SQLException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}

public ArrayList select(String keyStr) {
String sql = "";
PreparedStatement pstmt = null;
ResultSet rset = null;
ArrayList dataList = new ArrayList();

// SQL作成
sql = " SELECT * FROM dbo.TableName ";

// エリアコード
if (keyStr.equals("") || keyStr == null) {
sql += " WHERE COL1 LIKE ? ";
keyStr = "%";
} else {
sql += " WHERE COL1 = ? ";
}

try {

pstmt = this.conObj.prepareStatement(sql);
pstmt.setString(1, keyStr);

rset = pstmt.executeQuery();

while (rset.next()) {
ArrayList rowList = new ArrayList();

// データ格納
rowList.add(this.checkString(rset.getString("COL1")));
rowList.add(this.checkString(rset.getString("COL2")));
rowList.add(this.checkString(rset.getString("COL3")));
rowList.add(this.checkString(rset.getString("COL4")));
rowList.add(this.checkString(rset.getString("COL5")));

dataList.add(rowList);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (pstmt != null)
pstmt.close();
if (rset != null)
rset.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return dataList;
}

public boolean delete(String keyStr) {
boolean ret = true;
PreparedStatement pstmt = null;
String sql = "";

// SQL作成
sql = " DELETE FROM dbo.TableName ";
sql += " WHERE ";
sql += " ID = ? ";

try {
// データセット
pstmt = this.conObj.prepareStatement(sql);
// エリアコード
pstmt.setString(1, keyStr);

// SQL実行
pstmt.execute();
} catch (Exception e) {
e.printStackTrace();
ret = false;
} finally {
try {
if (pstmt != null)
pstmt.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return ret;
}

public boolean insert(ArrayList dataList) {
boolean ret = true;
String sql = "";
PreparedStatement pstmt = null;

sql = " INSERT INTO dbo.TableName ( ";
sql += " COL1, ";
sql += " COL2, ";
sql += " COL3, ";
sql += " COL4, ";
sql += " COL5 ";
sql += " ) values ( ";
sql += "?, ";
sql += "?, ";
sql += "?, ";
sql += "?, ";
sql += "? ";
sql += ")";

try {
pstmt = this.conObj.prepareStatement(sql);

pstmt.setString(1, this.checkString(dataList.get(0)));// COL1
pstmt.setString(2, this.checkString(dataList.get(1)));// COL2
pstmt.setString(3, this.checkString(dataList.get(2)));// COL3
pstmt.setString(4, this.checkString(dataList.get(3)));// COL4
pstmt.setString(5, this.checkString(dataList.get(4)));// COL5

// SQL実行
pstmt.execute();

} catch (Exception e) {
e.printStackTrace();
ret = false;
} finally {
try {
if (pstmt != null)
pstmt.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return ret;
}

public boolean update(ArrayList dataList) {
boolean ret = true;
String sql = "";
PreparedStatement pstmt = null;

// SQL作成
sql = " UPDATE dbo.TableName SET ";
sql += " COL1 = ?, ";
sql += " COL2 = ?, ";
sql += " COL3 = ?, ";
sql += " COL4 = ?, ";
sql += " COL5 = ? ";
sql += " WHERE ";
sql += " ID = ? ";

try {
pstmt = this.conObj.prepareStatement(sql);

pstmt.setString(1, this.checkString(dataList.get(0)));// COL1
pstmt.setString(2, this.checkString(dataList.get(1)));// COL2
pstmt.setString(3, this.checkString(dataList.get(2)));// COL3
pstmt.setString(4, this.checkString(dataList.get(3)));// COL4
pstmt.setString(5, this.checkString(dataList.get(4)));// COL5

// SQL実行
pstmt.execute();
} catch (Exception e) {
e.printStackTrace();
ret = false;
} finally {
try {
if (pstmt != null)
pstmt.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return ret;
}

private String checkString(Object obj) {
return obj == null ? "" : obj.toString().trim();
}
}

Web开发 用于获得数据库连接


package test;

import java.sql.*;
import javax.naming.*;
import javax.sql.*;

public class TestDBAccess {
private static TestDBAccess _instance = null; // DB接続管理オブジェクト
private DataSource ds = null;

private TestDBAccess() throws SQLException, Exception {
try {
// 初期コンテキストを取得
InitialContext ic = new InitialContext();
// ルックアップしてデータソースを取得
this.ds = (DataSource) ic.lookup("java:comp/env/jdbc/viper");
} catch (Exception e) {
e.printStackTrace();
}
}

public synchronized static TestDBAccess getInstance() throws SQLException, Exception {
return _instance == null ? new TestDBAccess() : _instance;
}

public Connection getConnection() throws SQLException, Exception {
Connection con = null;

try {
// コネクション取得
con = this.ds.getConnection();
} catch (Exception e) {
e.printStackTrace();
}
return con;
}
}

Web开发 用于输出日志


package test;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;

public class TestLog {
private static final long serialVersionUID = 1L;
private FileWriter filewrt;
private String logName = "";
private String DirPATH = System.getProperty("catalina.home") + "\\logs\\";

public TestLog() {
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyyMM");
Date date = new Date();
logName = DirPATH + "veriserve" + sdf1.format(date) + ".log";
}

public TestLog(String name) {
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyyMM");
Date date = new Date();
logName = DirPATH + name + sdf1.format(date) + ".log";
}

private void open() {
try {
// ログファイル名
File FileName = new File(logName);

if (CheckBeforeWriteFile(FileName)) {
// ファイル追加モード
filewrt = new FileWriter(FileName, true);
} else {
// 新規作成
if (FileName.createNewFile()) {
filewrt = new FileWriter(FileName, true);
}
}

} catch (IOException e) {

// エラー発生
e.printStackTrace();
}
}

private void close(){
try {
filewrt.close();
} catch( IOException e){
// エラー発生
e.printStackTrace();
}
}

private static boolean CheckBeforeWriteFile(File FileName) {
if (FileName.exists()) {
if (FileName.isFile() && FileName.canWrite()) {
return true;
}
}
return false;
}

public void Write(String PGName, String Msg) {
String LogMsg;
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy'/'MM'/'dd HH':'mm':'ss");
Date date = new Date();

LogMsg = sdf1.format(date) + "\t" + "[" + PGName + "]\t" + Msg + "\n";

try {
this.open();
filewrt.write(LogMsg);
this.close();
} catch (IOException e) {
// エラー発生
e.printStackTrace();
}
}
}

2000年7月8日星期六

2000年7月7日星期五

tomcat+apache+mod_jk整合

◆ApacheとTomcatの連携 ここではTomcatをApacheに組み込んで動かしてみたいと思います。こうすることでApacheが動いている環境でもポート80でTomcatを利用することがきるようになります。ApacheとTomcatの連携にはJK2コネクタを使用します。JK2コネクタはTomcatとApacheを連携するためのモジュールです。JK2コネクタはJKコネクタの改良版として開発され設定もシンプルになっています。

◆ApacheとTomcatを連携するメリット
①.ApacheのHTTPサーバとしての様々な機能を利用できるようになる。
②.一台のWebサーバに対して複数のTomcatのインスタンスを割り当てることによって負荷分散を実現できる。
③.静的なファイル(HTMLファイルや画像ファイル)をより高速なApacheが処理し、動的なページ(Servlet、Jsp)をTomcatが処理することによって効率のよいレスポンスが実現できる。

mod_jkのダウンロード:
http://tomcat.apache.org/connectors-doc/

ApacheとTomcatをmod_jkでの連携方法:
http://www.develop-memo.com/server/apache/modjk.html
http://www.wings.msn.to/contents/env/jsp/renkei_win.html
http://www.jajakarta.org/tomcat/tomcat3.2-4.0/tomcat-3.2.3/doc-ja/mod_jk-howto.html

2000年7月2日星期日

IT関連用語

問題:J2SE
→Java 2 Platform Standard Editionの略です。Java 2プログラミング言語の機能セットの一つで、基礎となる標準的な機能をまとめた、デスクトップ向けエディションです。

問題:J2EE
→Java 2 Platform Enterprise Editionの略です。Java 2プログラミング言語の機能セットの一つで、標準機能セットのJ2SEに、サーバ用のAPIや諸機能を付加した、企業向け、サーバ向けエディションです。

問題:J2ME
→Java 2 Platform Micro Editionの略です。Java 2プログラミング言語の機能セットの一つで、家電製品や携帯情報端末、携帯電話などの組み込み機器向けの機能をまとめた、小規模組み込みシステム向けエディションです。

問題:Servlet
→Webサーバ上で動作するJavaアプリケーションの一種です。WebサーバがServletを起動し、Servletからデータベースなどにアクセスし、その結果Webページとして返す際などに利用される。Servletを追加することにより、Webサーバの機能を拡張することができる。

問題:JavaBeans
→再利用しやすくするため、決められた作法に基づいて開発されたJavaのソフトウェアコンポーネントです。JavaBeansを部品として利用しながらソフトウェアの開発効率を高めることができる。

問題:Apache
→最も人気の高いWebサーバソフトウェアの一つです。Apacheはフリーソフトウェアとして無償で公開され、世界中のボランティアプログラマたちの手によって長年に渡って開発が続けられている。

問題:Tomcat
→オープンソースのソフトウェアで、Java Servlet、JSPを処理するアプリケーションサーバです。Tomcatは単独でウェブサーバとして動作することも可能だが、ApacheやIISのプラグインと動作できるようになっており、実際にはプラグインとしての利用が主流である。

問題:JBoss
→Java言語で書かれたJ2EEアプリケーションサーバです。JBossはTomcatなどのアプリケーションサーバを含むため、JBossのみでウェブアプリケーションを実行できる。EJBを実装したものでもあるため、様々なEJBアプリケーションを実行することも可能である。

問題:Cookie
→ウェブサーバとウェブブラウザとの間で、ユーザに関する情報やアクセス情報などをやり取りするための仕組みです。

問題:JVM
→Java Virtual Machineの略です。Javaプログラムを実行する仮想的なマシンです。Javaプログラムは通常CPUに依存しない中間コードにコンパイルされて配布される。VMがこの中間コードをCPUに依存したコードに変換して実行する。

問題:JDK
→Java Development Kitの略で、Javaプログラミングを行う際に必要、Javaのコンプイラ等が含まれる。

問題:JRE
Java Runtime Engineの略です。Javaの実行環境です。Javaアプリケーションを実行する際に必要。実行するだけならJRE、開発もするならJDKが必要。

問題:JSP
→HTMLファイル中に埋め込まれるJavaプログラムです。ユーザからそのページへのアクセス要求があった時にコンパイルされ、その結果がWebページとしてユーザに返される。

問題:PL・SQL:
→PL・SQLとは、SQL言語にプログラムに必要な繰り返し命令、エラー処理などを追加した言語であり、Programming Language for SQLの略です。

問題:SQL・Plus:
→SQL・Plusとは、Oracleデータベース用の対話ツールです。 主にデータベースに対してこのツールを通してSQLの実行などを行います。更にSQL・Plusには、各種のコマンドが用意されています。

問題:MySqlの使うことがありますか
→はい、MySqlの業務経験がないですが、Programming Levelで他のdatabaseとあまり変わらないと思います。対応できると思います。

問題:Hibernateでの開発経験がありますか:
→はい、HibernateはJava開発でよく使うOpenSourceのframeworkです。これは業務経験がないですが、別のframeworkでの開発経験がありますので、対応できると思います。

問題:Linuxでの経験がありますか。
→はい、直接Linuxでの開発経験がないですが、開発環境はWindowsで、システムの実行環境はLinuxでした。

問題:インデックスを使えますか。
→はい、使えます。インデックスを正しい使えばシステムの性能をアップできます。でも、使い方は正しくない時システムに悪い影響もあります。例えば:よく使えないコラム或いは状態が少ないのコラム(性別)でインデックスを作るのはシステムにとって性能をあまりよくないと思います。

問題:PL・SQLの経験がありますか。
→はい、経験があります。私はOracle9Iデータベースでの開発経験があります。その時PL・SQLでテスト用データの作成、操作結果のオンラインチェック、データベースバックアップなど仕事を担当しました。

問題:PL/SQLを使ったことありますか?
→はい、使ったことあります。PL/SQL-プロシージャを作ったことがあります。また、PL・SQLでテスト用データの作成、操作結果のオンラインチェック、データベースバックアップなどを担当しました。

問題:Linux(或いはUnix 或いはAIX)はどの程度使えますか?
→はい、システム管理者とする経験はありませんが、一般ユーザとする経験はあります。例えば、Linux環境でプログラムソースの作成、コンバイル、アプリ実行などができます。そして、ローカル作業環境の管理ができます。

問題:Ajaxの業務経験はありますか?
→はい、直接にAjaxを使う経験はありませんが、その関連技術のJavaScript、XML、CSSの業務経験があります。チャンスがあれば、是非挑戦したいと思います。

問題:UMLの経験がありますか?
→はい、私は基本設計と詳細設計の経験があります。設計する時、UMLを使って、ユースケース図、パッケージ図、クラス図、シーケンス図などを作成して、分析を行いました。

2000年6月23日星期五

VB中的字符串处理

1、处理机制

在VB4.0以后,字符串采用统一编码方式,即“UniCode”。
早期英文字符占用1个字节,汉字占用2个字节,即ANSI方式;UniCode方式下,所有字符都占用2个字节。
在字符串函数中,考虑的是字符个数,而不是字节数。
为了兼容早期的软件,可使用转换函数StrConv把字符串转换为指定格式。
新字符串=StrConv(待转字符串,转换常数)
常数
值
说明

vbUpperCase
1
将字符串文字转成大写。

vbLowerCase
2
将字符串文字转成小写。

vbProperCase
3
将字符串中每个字的开头字母转成大写。

vbUnicode
64
根据系统的缺省码页将字符串转成 Unicode。

vbFromUnicode
128
将字符串由 Unicode 转成系统的缺省码页。


当在Unicode方式下,必须用加了“B”的字符串函数来处理ANSI的字符串才能得到正确的值,函数如下:
处理Unicode的字符串的函数名
处理ANSI的字符串的函数名

len
lenB

left
leftB

right
rightB

mid
midB

instr
instrB

函数说明见下面:


例:运行下面程序,查看两种编码的使用区别。

Private Sub Command1_Click()
Dim tempStr, newStr As String
tempStr = "VB程序设计"
Print "tempStr="; tempStr
newStr = StrConv(tempStr, vbFromUnicode)
Print "******"
Print "执行newStr = StrConv(tempStr, vbFromUnicode)后:"
Print "newStr="; newStr
Print "LenB(newStr)="; LenB(newStr)
Print "注:每个长度单位为1个字节,字符占1个,汉字占2个"
Print "Len(newstr)="; Len(newStr)
Print "注:每个长度单位为2个字节,字符占半个,汉字占1个"
Print "******"
newStr = StrConv(newStr, vbUnicode)
Print "执行newStr = StrConv(tempStr, vbUnicode)后:"
Print "newStr="; newStr
Print "LenB(newStr)="; LenB(newStr)
Print "注:每个长度单位为1个字节,字符占2个,汉字占2个"
Print "Len(newStr)="; Len(newStr)
Print "注:每个长度单位为2个字节,字符占1个,汉字占1个"
End Sub


2、字符串函数

删除空格字符串
去掉字符串左部连续空格:LTrim$(字符串)
去掉字符串左部连续空格:RTrim$(字符串)
去掉字符串两边连续空格:Trim$(字符串)
例:使用函数。查看运行结果。
Private Sub Command1_Click()
aaa1 = "aaaaa "
bbb1 = " bbbbb"
ccc1 = " ccccc "
Print "|" + LTrim(aaa1) + "|" + LTrim(bbb1) + "|" + LTrim(ccc1) + "|"
Print "|" + RTrim(aaa1) + "|" + RTrim(bbb1) + "|" + RTrim(ccc1) + "|"
Print "|" + Trim(aaa1) + "|" + Trim(bbb1) + "|" + Trim(ccc1) + "|"
End Sub


字符串截取函数
左部截取:Left$(字符串,n) 从左部开始取n个字符
右部截取:Right$(字符串,n) 从右部取n个字符
中部截取:Mid$(字符串,p,n) 从左部开始的第p个字符,取连续n个字符
例:使用函数。查看运行结果。
Private Sub Command1_Click()
MyString = "Mid Function Demo"
FirstWord = Left(MyString, 3) ' 返回 "Mid"。
LastWord = Right(MyString, 4) ' 返回 "Demo"。
MidWords = Mid(MyString, 5) ' 返回 "Funcion Demo"。
Print FirstWord, LastWord, MidWords
End Sub


字符串长度测试函数
Len(字符串):字符串的长度。
Len(变量名):变量的存储空间。
例:使用函数。查看运行结果。
Private Type CustomerRecord ' 定义用户自定义的数据类型。
ID As Integer ' 将此定义放在常规模块中。
Name As String * 10
Address As String * 30
End Type

Private Sub Command1_Click()
Dim Customer As CustomerRecord ' 声明变量。
Dim MyInt As Integer, MyCur As Currency
Dim MyString, MyLen
MyString = "Hello World" ' 设置变量初值。
MyLen = Len(MyInt) ' 返回 2。
Print MyLen
MyLen = Len(Customer) ' 返回 42。
Print MyLen
MyLen = Len(MyString) ' 返回 11。
Print MyLen
MyLen = Len(MyCur) ' 返回 8。
Print MyLen
End Sub


String$函数
String$(n,ASCII码值):返回由该ASCII码指定的字符组成的n个字符的字符串
String$(n,字符串):返回由该字符串的第一个字符组成n个字符的字符串
例:使用函数。查看运行结果。
Private Sub Command1_Click()
Print String$(6, 65) '返回字符串"AAAAAA
Print String$(3, "c") '返回字符串"ccc"
End Sub


空格函数
Space$(n):返回由n个空格组成的字符串
例:使用函数。查看运行结果。
Private Sub Command1_Click()
Print "start" + Space(3) + "END" '返回字符串"start END"
Print "start" + "123" + "END" '返回字符串"start123END"
End Sub


字符串匹配函数
Instr$([开始位置,]string1,string2[,n]) 查找某字符串在另一个字符串中首次出现的位置。
n为0,二进制比较(区分大小写);n为1,文本方式比较(不区分大小写);n为2,数据库信息比较。
返回的结果:

如果
InStr返回

string1 为零长度
0

string1 为 Null
Null

string2 为零长度
开始位置

string2 为 Null
Null

string2 找不到
0

在 string1 中找到string2
找到的位置

开始位置 > string2
0

例:使用函数。查看运行结果。
Private Sub Command1_Click()
Dim SearchString, SearchChar, MyPos
SearchString = "XXpXXpXXPXXP" ' 被搜索的字符串。
SearchChar = "P" ' 要查找字符串 "P"。

' 从第四个字符开始,以文本比较的方式找起。返回值为 6(小写 p)。
' 小写 p 和大写 P 在文本比较下是一样的。
MyPos = InStr(4, SearchString, SearchChar, 1)
Print MyPos

' 从第一个字符开使,以二进制比较的方式找起。返回值为 9(大写 P)。
' 小写 p 和大写 P 在二进制比较下是不一样的。
MyPos = InStr(1, SearchString, SearchChar, 0)
Print MyPos

' 缺省的比对方式为二进制比较(最后一个参数可省略)。
MyPos1 = InStr(SearchString, SearchChar) ' 返回 9。
mypos2 = InStr(1, SearchString, "W") ' 返回 0。
Print MyPos1, mypos2
End Sub


字母大小写转换函数
Ucase$(字符串):将字符串转换为大写
Lcase$(字符串):将字符串转换为小写
例:使用函数。查看运行结果。
Private Sub Command1_Click()
Print UCase$("Rose") '返回字符串"ROSE"
Print LCase$("Rose") '返回字符串"rose"
End Sub

替换字符串命令
Mid$(字符串,位置[,L])=子字符串
说明:在字符串中,从指定位置开始L个字符被子字符串替换,如果子字符串长度大于L,子字符串被截取;如果L没有,则替换后的字符串长度和被替换前的字符串长度一样。
例:使用函数。查看运行结果。
Private Sub Command1_Click()
Dim MyString
MyString = "The dog jumps" ' 设置字符串初值。
Mid(MyString, 5, 3) = "fox"
Print MyString ' MyString = "The fox jumps"
Mid(MyString, 5) = "cow"
Print MyString ' MyString = "The cow jumps"
Mid(MyString, 5) = "cow jumped over"
Print MyString ' MyString = "The cow jumpe"
Mid(MyString, 5, 3) = "duck"
Print MyString ' MyString = "The duc jumpe"
End Sub

VB文件操作

在VB中,对于顺序文件、随机文件、二进制文件的操作通常都有3个步骤:

(1)打开文件

文件操作的第一步是打开文件。在创建新文件或使用旧文件之前,必须先打开文件。打开文件的操作,会为这个文件在内存中准备一个读写时使用的缓冲区,并且声明文件在什么地方,叫什么名字,文件的处理方式。

(2)访问文件

访问文件是文件操作的第二步。所谓访问文件,即对文件进行读/写操作。从磁盘将数据送到内存称为“读”,从内存将数据存到磁盘称为“写”。

(3)关闭文件

打开的文件使用(读/写)完后,必须关闭,否则会造成数据丢失。关闭文件会把文件缓冲区中的数据全部写入磁盘,释放掉该文件缓冲区占用的内存。

A 顺序文件

顺序文件用于处理一般的文本文件,它是标准的ASCII文件。顺序文件中各数据的写入顺序、在文件中的存放顺序、和从文件中的读出顺序3者是一致的。

顺序文件按行组织信息。每行由若干项组成,行的长度不固定,每行由回车换行符号结束。

a 顺序文件的打开与关闭

在对顺序文件进行操作之前,必须用Open语句打开要操作的文件。在对一个文件操作完成后,要用Close语句将它关闭。

1.Open语句的一般格式

Open 文件名 [For打开方式 ] As [#]文件号

(1)文件名。

(2)打开方式包括以下3种。

Input Output Append

(3)文件号。

是一个1~511之间的整数。它用来代表所打开的文件,文件号可以是整数或数值型变量。

例如:

① Open "d:\shu1.dat" For Input As #1

该语句以输入方式打开文件shu1.dat,并指定文件号为1。

② Open "d:\shu2.dat" For  Output As #5

该语句以输出方式打开文件shu2.dat,即向文件shu2.dat进行写操作,并指定文件号为5。

③ Open "d:\shu3.dat" For  Append As #7

该语句以添加方式打开文件shu3.dat,即向文件shu3.dat添加数据,并指定文件号为7。

2.Close语句的一般格式

Close [文件号表列]

例如:

① Close #1

关闭文件号为1的文件。

② Close #2,#7,#8

关闭文件号为2,7,8的文件。

③ Close

关闭所有已打开的文件。

b 顺序文件的写操作

VB用Print语句或Write语句向顺序文件写入数据。创建一个新的顺序文件或向一个已存在的顺序文件中添加数据,都是通过写操作实现的。另外,顺序文件也可由文本编辑器(记事本、Word等)创建。

1.Print语句

Print语句的一般格式:

Print #文件号 [,输出表列]

例如:

Open "d:\shu2.dat" For  Output As #2

Print # 2, "zhang";"wang";"li"

Print # 2, 78;99;67

Close #2

执行上面的程序段后,写入到文件中的数据如下:

zhangwangli

78 99 67

2.Write语句

用Write语句项文件写入数据时,与Print语句不同的是,Write语句能自动在各数据项之间插入逗号,并给各字符串加上双引号。

Write语句的一般格式:

Write #文件号 [,输出表列]

例如:

Open "d:\shua.dat" For  Output As #6

Write # 6, "zhang";"wang";"li"

Write # 6, 78;99;67

Close #6

执行上面的程序段后,写入到文件中的数据如下:

"zhang","wang","li"

78,99,67

c 顺序文件的读操作

顺序文件的读操作,就是从已存在的顺序文件中读取数据。在读一个顺序文件时,首先要用Input方式将准备读的文件打开。VB提供了Input、Line Input语句和Input函数将顺序文件的内容读入。Input函数将在9.4节中介绍。

1.Input语句

Input语句一般格式:

Input #文件号,变量表列

例如:

Private Sub form_Click()

Dim x$,y$,z$,a%,b%,c%

Open "c:\_vb\shua.dat" For Input As #1

Input #1,x,y,z

Input #1,a,b,c

Print x,y,z

Print a,b,c

Print a + b + c

Close #1

End Sub

如果顺序文件shua.dat的内容如下:

"zhang","wang","li"

78,99,67

执行Form_Click过程,在窗体上显示的内容为:

zhang wang li

78 99 67

244

2.Line Input语句

Line Input语句是从打开的顺序文件中读取一行。

Line Input语句的一般格式:

Line Input #文件号,字符串变量

例如:

如果顺序文件shua.dat的内容如下:

"zhang","wang","li"

78,99,67

我们用Line Input语句将数据读出并且把它显示在文本框中。

Private Sub Command1_Click()

Dim a$,b$

Open "c:\_vb\shua.dat" For Input As #2

Line Input #2,a

Line Input #2,b

Text1.Text = a & b

End Sub

执行以上过程,文本框中显示的内容为:

"zhang","wang","li"78,99,67



B 随机文件

使用顺序文件有一个很大的缺点,就是它必须顺序访问,即使明知所要的数据是在文件的末端,也要把前面的数据全部读完才能取得该数据。而随机文件则可直接快速访问文件中的任意一条记录,它的缺点是占用空间较大。

随机文件由固定长度的记录组成,一条记录包含一个或多个字段。具有一个字段的记录对应于任一标准类型,比如整数或者定长字符串。具有多个字段的记录对应于用户定义类型。随机文件中每个记录都有一个记录号,只要指出记录号,就可以对该文件进行读写。

1 随机文件的打开与关闭

在对一个随机文件操作之前,也必须用Open语句打开文件,随机文件的打开方式必须是Random方式,同时要指明记录的长度。与顺序文件不同的是,随机文件打开后,可同时进行写入与读出操作。

Open语句的一般格式:

Open 文件名 For Random As #文件号 Len=记录长度

说明:

记录长度是一条记录所占的字节数。可以用Len函数获得。

例如,定义以下记录:

Type student

Name As String*10

Age As Integer

End Type

就可以用下面的语句打开:

Open "d:\Test.dat" For Random As #9 Len=Len(student)

随机文件的关闭同顺序文件一样,用Close 语句。

2 随机文件的写操作

用Put语句进行随机文件的写操作。

Put语句一般格式为:

Put # 文件号,记录号,变量

说明:

Put语句把变量的内容写入文件中指定的记录位置。记录号是一个大于或等于1的整数。

例如,Put # 1,9,t

表示将变量t的内容送到1号文件中的第9号记录去。

3 随机文件的读操作

用Get语句进行随机文件的读操作。

Get语句格式一般为:

Get # 文件号,记录号,变量

说明:

Get语句把文件中由记录号指定的记录内容读入到指定的变量中。

例如,Get # 2,3,u

表示将2号文件中的第3条记录读出后存放到变量u中。

D 二进制文件

二进制文件被看作是字节顺序排列的。由于对二进制文件的读写是以字节为单位进行的,所以能对文件进行完全的控制。如果知道文件中数据的组织结构,则任何文件都可以当作二进制文件来处理使用。

1 二进制文件的打开与关闭

二进制文件的打开用Open语句。

其格式为:

Open 文件名 For Binary As #文件号

使用Close语句关闭二进制文件。

2 二进制文件的读/写操作

对二进制文件的读/写同随机文件一样用Put和Get语句。

它们的格式如下:

Put # 文件号,位置,变量

Get # 文件号,位置,变量



文件操作常用函数

1.LOF函数

格式:LOF(文件号)

功能:返回一个已打开文件的大小,类型为Long,单位是字节。

2.FileLen函数

格式:FileLen(文件名)

功能:返回一个未打开文件的大小,类型为long,单位是字节。文件名可以包含驱动器以及目录。

3.EOF函数

格式:EOF(文件号)

功能:用于判断读取的位置是否已到达文件尾。当读到文件尾时,返回True,否则返回False。对于顺序文件,用EOF 函数测试是否到达文件尾;对于随机文件和二进制文件,如果读不到最后一个记录的全部数据,返回True,否则返回False。对于以Output 方式打开的文件,EOF 函数总是返回True。

4.LOC函数

格式:LOC(文件号)

功能:返回文件当前读/写的位置,类型为Long。 对于随机文件,返回最近读/写的记录号;对于二进制文件,返回最近读/写的字节的位置。对于顺序文件,返回文件中当前字节位置除以 128 的值。对于顺序文件而言,Loc 的返回值无实际意义。

5.Input函数

格式:Input(字符数,#文件号)

功能:从打开的顺序文件读取指定数量的字符。Input函数返回从文件中读出的所有字符,包括逗号、回车符、换行符、引号和空格等。

例如:Text1.Text=Input(Lof(2),#2)

该语句是将2号文件的内容全部复制到文本框中。

VB基础知识

一:变量定义
Dim a as integer,b as long,c as single , d as String
等价于
Dim a%,b&,c! , d$

二:注意“+”和“&”的区别
"ABC"+"DEF" → "ABCDEF"
"姓名:" & "张三" → "姓名:张三"
23 & "7" → "237"
23+"7" → 30

三:输入框
Dim r!
r = InputBox("请输入半径","输入框")

四:消息框
dim inta
inta=MsgBox(“密码错”,21,“密码核对” )

五:if 语句
Dim weight as single,pay as single
weight= Text1.Text

If weight > 50 Then
pay =(weight - 50)* 0.5 + 50 * 0.2
Else
pay = weight * 0.2
End If

六:if elseif 语句
Dim score!
score = Text1.Text

If score >= 90 Then
Text2.Text = "优秀"
ElseIf score >= 80 Then
Text2.Text = "良好"
ElseIf score >= 70 Then
Text2.Text = "中"
ElseIf score >= 60 Then
Text2.Text = "及格"
Else
Text2.Text = "不及格"
End If

七:行If语句
Dim b As Single,max As Single

If b > max Then max = b

八:Select Case语句
Dim score!
score = Text1.Text

Select Case score
Case Is >= 90
Text2.Text = "优秀"
Case Is >= 80
Text2.Text = "良好"
Case Is >= 70
Text2.Text = "中"
Case Is >= 60
Text2.Text = "及格"
Case Else
Text2.Text = "不及格"
End Select

九:for循环
For n=1 To 10 Step 3
Print n,
Next n

十:While循环
x=1
While x<5 Print x, x=x+1 Wend 十一:Do while循环 r = m Mod n Do While r > 0
m = n
n = r
r = m Mod n
Loop
r = m Mod n

十二:Do until循环
Do Until r = 0
m = n
n = r
r = m Mod n
Loop

十三:循环的结束
1.Exit For
该语句用于For…Next循环,在循环体中可以出现一次或多次。当系统执行到该语句时,就强制退出当前循环。常见的使用格式是:If 条件 Then Exit For 即当循环执行过程中满足某个条件时,就执行循环退出语句结束循环。

2.Exit Do 该语句用于Do…Loop循环,具体用法同Exit For一样。

十四:一维数组
Dim A(5)As Integer 等价于 Dim A%(5)
定义了一个一维数组,名字为y,类型为Integer,占据6个(0~5)整型变量的空间。

十五:二维数组
Dim T(2,3)As Integer
定义了一个二维数组,名字为T,类型为Integer,该数组有3行(0~2)4列(0~3),占据12(3×4)个整型变量的空间

十六:子过程
Sub jieCheng(n%,p&)
On Error GoTo Click_Err
Dim i%
p = 1

For i = 1 To n
p = p * i
Next i

Click_Err:
Resume Next
End Sub

子过程的调用
Dim a&,b&,c&,d&
Call jieCheng(7,a)
Call jieCheng(11,b)
Call jieCheng(10,c)

d = a + b-c

Print "7!+11!-10!=";d

十七:函数过程
例子一:
Function jieCheng &(n%)
On Error GoTo Click_Err
Dim i%
jch = 1

For i = 1 To n
jch = jch * i
Next i

Click_Err:
Resume Next
End Function

函数过程的调用
Dim d&
d = jieCheng(7)+ jieCheng(11)-jieCheng(10)
Print "7!+11!-10!=";d

例子二:
Function sum%(b%())
On Error GoTo Click_Err
Dim i%

For i = LBound(b)To UBound(b)
If b(i)< 0 Then
sum = sum + b(i)
End If
Next i
Click_Err:
Resume Next
End Function

函数过程的调用
Dim a%(10),s%,i%

For i = 1 To 10
a(i)= Int(Rnd * 100)- 50
Print a(i);
Next i
Print
s = sum(a())
Print "数组中的负元素之和为:";s

技巧一:格式转换
CStr(value)
将value转换成string型

CInt(value)
将value转换成int形

CLng(value)
将value转换成long形

注:CBool,CByte,CDate,CVar,CVDate,CVErr,用法类似

技巧二:格式化
Format$( value, "0000")
将value格式化成4位

Format(Now, "yyyymmdd")
将当前日期格式化成yyyymmdd格式

Format(Now - 1, "yyyy-mm-dd")
将当前日期减一天后格式化成yyyy-mm-dd格式