java基础 - 2

2020年08月06日 阅读数:142
这篇文章主要向大家介绍java基础 - 2,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。

JAVA编程语言和JDBCjavascript

编写正确且遵照规范的Java程序,能够无需从新编译就在任何启用Java技术的平台上运行。Java编程语言完全地进行了规定。根据定义,启用Java技术的平台必须支持已知的核心库。java.sql包或javax.sql包或者JDBC就是这样一个库,它们能够视为ODBC的可移植版本,且其自己就是重大的标准。JAVA编程语言和JDBC一块儿使用,能够给编写数据库应用程序提供正确的可移植性解决方案。注意:虽然可移植的应用程序和标准数据库界面都是重大的成果,但不要忘记,由于历史,竞争,有时是没有意思的缘由,各类数据库并无完全地进行标准化。这可能意味着,必须根据(甚至同一平台上的)特定数据库的性能或内在的调整来寻找"最低公分母"。不管采用标准的SQLODBCJDBC,或其余解决方案,都存在这个问题。最后须要指明的是JDBC驱动程序就是JAVA类,它实现JDBC驱动程序接口,并能够为特别的数据库转换程序(通常是SQL)请求。无疑,驱动程序在这里起了重要做用。大多数的数据库供应商如今都提供驱动程序,以实现特定系统的JDBC API。这些一般都是免费提供的。第三方驱动程序也能够得到,成本从免费到费用浩大的都有。css

JDBC编程的核心包为java.sql包,其结构以下图:html

JDBC编程的步骤java

第一:加载驱动程序node

为了与特定的数据库相连,JDBC必须加载相应的驱动程序。如:mysql

try {git

Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");程序员

} catch (ClassNotFoundException e) {web

e.printStackTrace();正则表达式

}

// 加载Oracle的驱动程序

Class.forName("oracle.jdbc.driver.OracleDriver");

// 加载Microsoft SQL Server的驱动程序 Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver");

第二:要将"驱动程序"传递到DriverManager,而后得到"链接"

DriverManager类的getConnection(String url,String user, String password)方法用于创建与某个数据库的链接。每一个JDBC驱动程序使用一个专门的JDBC URL做为自我标识的一种方法。

JDBC URL的格式为:jdbc : <子协议名> : <子名称>

子协议(sub-protocol)与JDBC驱动程序有关,能够是odbc,oracle,db2mysqlmicrosoft等等,根据实际的JDBC驱动程序厂商而不一样。数据库定位器(database locator)是与驱动程序有关的指示器,用于惟一指定应用程序要和哪一个数据库进行交互。根据驱动程序的类型,该定位器可能包括主机名,端口和数据库系统名。

try{

String url="jdbc:odbc:myodbc";

Connection con=DriverManager.getConnection(url);

// 或者

Connection con=

DriverManager.getConnection(url,user,password);

}catch(SQLException e){

e.printStackTrace();

}

// 1.Microsoft SQL ServerURL

url="jdbc:Microsoft:sqlserver://192.168.0.1:1433;databasename=mydb";

127.0.0.1 也能够用字符串 "localhost"代替

// 2.Oracle URL

url="jdbc:oracle:thin:@192.168.0.1:1521:goudan";

第三:建立语句,Statement ,PreparedStatement,CallableStatement,并将它们用于更新数据库或执行查询。

Statement 对象用于将 SQL 语句发送到数据库中。实际上有三种 Statement 对象,它们都做为在给定链接上执行 SQL语句的对象:StatementPreparedStatement( 继承Statement )和 CallableStatement(继承PreparedStatement)。它们都专用于发送特定类型的 SQL 语句: Statement 对象用于执行不带参数的简单 SQL语句;PreparedStatement 对象用于执行带或不带 IN 参数的预编译 SQL 语句;CallableStatement对象用于执行对数据库已存储过程的调用。

第四:查询返回包含有已请求数据的ResultSet,该ResultSet是按类型检索的。

ResultSet包含符合SQL语句中条件的全部行,而且它经过一套get方法(这些get方法能够访问当前行中的不一样列)提供了对这些行中数据的访问。

第五:DatabaseMetaDataResultSetMetaData接口能够用来提供有关数据库或ResultSet的信息。

实例分析

1:经过ODBC创建链接

? 配置WINDOWSODBC详细步骤参考 <<ODBC链接步骤.doc>>

? 代码以下:

import java.sql.*;

public class ODBCTest {

public static void main(String[] args) {

Connection con = null;

try {

// 加载ODBC驱动

1Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

// 经过驱动管理器(DriverManager)得到链接

2con = DriverManager.getConnection("jdbc:odbc:myodbc",

"pubuse","123");

// 若是链接不成功,就会出现异常,不会执行下面这个语句

System.out.println("connect success!");

} catch (Exception e) {// 若是出现异常,会打印堆栈里异常的信息

e.printStackTrace();

} finally {// 用完后,关闭链接,释放资源

try {

if (con != null) // 防止出现内存泄露

con.close();

} catch (Exception e) {

e.printStackTrace();

}

}

}

}

2:经过SQLSERVER 提供的驱动程序得到链接

? 下面经过SQLSERVER提供的三个驱动得到链接。三个jar文件的名字分别为:msbase.jar,mssqlserver.jar,msutil.jar 而后设置classpath环境变量指向这三个路径。假如这三个文件放在d:\mssqldriver 目录下,以下图所示:

? 而后在系统环境变量classpath里面设置这三个jar文件,这样的话就能够在任何的dos命令窗口里面使用。

也能够在一个dos窗口里面设置。这样的话是只在该窗口下有效。

? 部分代码以下:

………

try {

// 加载SQLSERVER的驱动程序Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver");

// 经过驱动来得到一个链接

con = DriverManager.getConnection(

"jdbc:microsoft:sqlserver://localhost:1433;"

+ "databasename=pubs", "sa", "");

System.out.println("connect success!");

} catch (Exception e) {

e.printStackTrace();

}

………

3:经过ORACLE提供的驱动程序得到链接

? 下面介绍经过ORACLE提供的驱动程序得到链接。只有一个jar文件:classes12.jar 同上设置环境变量classpath 指向该jar文件。

部分代码以下

………

try {

// 加载ORACLE9i的驱动程序

Class.forName("oracle.jdbc.driver.OracleDriver");

// 得到链接 oracle数据库的端口号:1521 数据服务器的名字叫itjob

// 登录的用户名为system,密码为:system (默认密码为manager)

con = DriverManager.getConnection(

"jdbc:oracle:thin:@127.0.0.1:1521:itjob",

"system","system");

System.out.println("con ok");

} catch (Exception e) {

e.printStackTrace();

}

………

4:经过数据源得到链接

使用JDBC API的一个主要好处就是独立于数据库的编程,由于大部分JDBC应用程序均可以被轻易地转换到不一样的数据库。然而,仍然有两个主要的内容与特定的数据库有关,即JDBC Driver类和JDBC URL

随着JDBC API的升级,数据源技术的引入,能够提升数据库访问的灵活性。本质上,DataSource对象表示一个特殊的数据源。除了将数据库和专门的JDBC驱动程序信息封装到一个单独的,标准化的对象中以外,数据源能够做为Connection工厂,并为设置和获取DataSource对象进行成功操做所须要的特定属性提供方法。DataSource对象可能须要的一些标准属性包括:

? databaseName

? serverName

? portNumber

? username

? password

使用数据源的另外一个好处就是和安全有关的敏感信息,如用户名,密码甚至数据库服务器只在一处编码,这能够由系统管理员完成。虽然和DataSource对象的交互能够用图形应用程序完成,但真正看到运行的示例是有指导性的。虽然DataSource对象的概念很简单,但为了在Java应用程序中使用,DataSource对象是使用Java名称和目录接口(JNDI)来引用的。

下面首先介绍下JNDI的相关概念。JNDI是个JAVA API,它包括名称和目录服务器的概念,道理和JDBC与数据库进行通信的概念差很少。如:硬盘经过与磁道和扇区打交道工做,但用户只关心文件名和目录,文件系统管理名称服务,该服务将给定的文件名和硬盘上特定的位置相关联。另外一个简单的示例就是Web,多数用户只关心Web站点的名称,如www.5itjob.com,并不关心底层的IP地址。然而,TCP/IP通信是经过使用IP地址,而不是人类能看懂的名称进行的。两个表示法之间的转换是经过DNS(域名系统,Domain Name System)完成的。虽然JNDI用本身的方式提供了一个丰富和有用的API,咱们的需求却简单得多。简短地说,咱们须要知道怎样作四件事:

? 建立名称并将其绑定到一个Java对象

? 查询名称以检索Java对象

? 删除一个名称

? 从新绑定名称到一个新的Java对象

首先要先加载数据源的驱动程序。让环境变量classpath指向下面几个jar文件

2,代码以下:

import java.util.Hashtable;

import javax.naming.*;

import javax.naming.directory.*;

import java.sql.*;

import javax.sql.*;

import com.microsoft.jdbcx.sqlserver.SQLServerDataSource;

public class JNDIServer {

// First we define the relevant parameters for this datasource

private String serverName = "192.168.0.1";

private int portNumber = 1433;

private String login = "student";

private String password = "student";

private String databaseName = "mydb";

private String filePath = "jdbc/mydatasource";

public JNDIServer() {

Hashtable env = new Hashtable();

env.put(Context.INITIAL_CONTEXT_FACTORY,

"com.sun.jndi.fscontext.RefFSContextFactory");

try {

// 建立初始化上下文环境

Context ctx = new InitialContext(env);

// 若是filePath已经绑定过了,那么先解除绑定

ctx.unbind(filePath);

// 建立SQLServerDataSource

SQLServerDataSource ds = new SQLServerDataSource();

// 设置数据源的参数

ds.setServerName(serverName);

ds.setPortNumber(portNumber);

ds.setDatabaseName(databaseName);

ds.setUser(login);

ds.setPassword(password);

ds.setDescription("JDBC DataSource Connection");

// 绑定 JDBC 数据源

ctx.bind(filePath, ds);

ctx.close();

System.out.println("DataSource Created Success!");

} catch (Exception e) {

e.printStackTrace();

}

}

public static void main(String args[]) {

new JNDIServer();

}

}

运行成功后,会在控制台打印:DataSource Created Success!

同时在运行该类所在的磁盘根目录下生成".bindings"文件,以下图:

数据源建立好以后,下面开始使用数据源。

代码以下:

import java.util.Hashtable;

import javax.naming.*;

import java.sql.*;

import javax.sql.*;

public class UseJNDI {

public static void main(String args[]) {

Connection con = null;

try {

Hashtable env = new Hashtable();

env.put(Context.INITIAL_CONTEXT_FACTORY,

"com.sun.jndi.fscontext.RefFSContextFactory");

Context ctx = new InitialContext(env);

// 得到数据源对象

DataSource ds =

(DataSource) ctx.lookup("jdbc/mydatasource");

// 经过数据源对象得到一个链接

con = ds.getConnection();

// 若是链接不成功,就会出现异常,不会执行下面这个语句

System.out.println("connect success!");

} catch (Exception e) {

e.printStackTrace();

} finally {

try {

if (con != null)// 用完链接后,要关闭释放

con.close();

} catch (Exception e) {

e.printStackTrace();

}

}

}

}

下面是删除JNDI的引用的代码:

import java.util.Hashtable;

import javax.naming.*;

import java.sql.*;

import javax.sql.*;

public class DeleteJNDI {

public static void main(String[] args) {

Hashtable env = new Hashtable();

env.put(Context.INITIAL_CONTEXT_FACTORY,

"com.sun.jndi.fscontext.RefFSContextFactory");

try {

Context ctx = new InitialContext(env);

ctx.unbind("jdbc/pubs");

ctx.close();

System.out.println("delete succeed!");

} catch (Exception ex) {

System.err.println("ERROR: " + ex.getMessage());

}

}

}

5:经过链接池得到链接

首先请根据咱们在上面建立数据库链接的经验,来思考下列问题:

? 为何须要链接池?

? 什么是PooledConnection?

? 初始化链接池

? 使用链接池

当使用DriverManager或者DataSource方法来获取数据库链接时,每一个对新数据库链接的请求都会致使很大的开销。若是频繁地获取新的链接,将会影响性能问题。这在Web服务器端编程的时候尤其明显。请求一个新的Connection对象会带来大量的开销和不少潜在的错误。为了最小化开销,为何在咱们使用完数据库链接后不是从新使用它们,而是删除它们呢?JDBC设计者在建立接口ConnectionPoolDataSource时使用这种流行的设计模式,这容许您建立数据库链接池,其中的链接在关闭后能够重用,而不是删除。

PooledConnection是一个特殊类型的数据库链接,在关闭时不会被删除,不象常规的Connection对象(当常规的链接再也不被使用时,垃圾收集器能删除它们)。相反,PooledConnection被缓存以备未来再次使用,从而可能带来大幅度的性能提高。使用数据库链接池几乎和使用DataSource对象同样。首先,不是建立一个实现DataSource接口的类的实例,而是建立了一个实现了ConnectionPoolDataSource接口的类的实例。能够像之前同样使用JNDI来绑定这个新的数据源到一个名称。要实际使用池化的数据源对象,调用ConnectionPooledDataSource(它接下来会创建数据库链接)上的getPooledConnection()获得一个PooledConnection对象。要建立将使用的Connection对象,调用PooledConnection对象(而不是之前的DriverManagerDataSource对象)上的getConnection().这种方法的一个额外的好处是,由于是自动处理,用ConnectionPool管理必定数量的数据库链接方便多了。若是你的客户机许可限制了可以同时链接到数据库的客户机的数目,这种自动化可能很是重要。

初始化链接池。下面的例子咱们将使用SQLServer2000数据库和i-net软件的第三方驱动程序来建立PooledDataSource.全部与数据库相关的代码都包含在初始化或绑定过程当中。

首先应该在classpath里面再指向名字为Merlia.jarjar文件。

2,代码以下:

import com.inet.tds.PDataSource;

import java.util.Hashtable;

import javax.naming.*;

import com.microsoft.jdbcx.sqlserver.SQLServerDataSource;

public class JNDIofPooledDataSourceTest {

public static void main(String[] args) {

String serverName = "192.168.0.1";

String databaseName = "mydb";

String userName = "student";

String password = "student";

// 经过下面的名字能够得到一个池化的链接

String filePath = "jdbcPool/mydatasource";

int portNumber = 1433;

int poolSize = 10; // We want to create a pool with 10 connections.

Hashtable env = new Hashtable();

env.put(Context.INITIAL_CONTEXT_FACTORY,

"com.sun.jndi.fscontext.RefFSContextFactory");

try {

Context ctx = new InitialContext(env);

// 建立一个池化的数据源对象

PDataSource ds = new PDataSource();

ds.setServerName(serverName);

ds.setPortNumber(portNumber);

ds.setDatabaseName(databaseName);

ds.setUser(userName);

ds.setPassword(password);

ds.setDescription("i-Net PDataSource");

ds.setMaxPoolSize(poolSize);

// 绑定池化的数据源

ctx.bind(filePath, ds);

ctx.close();

System.out.println("PooledDataSource Created Success!");

} catch (Exception ex) {

System.err.println("ERROR: " + ex.getMessage());

}

}

}

一旦初始化了PooledDataSource,就可以在Java应用程序中使用它来建立数据库链接池。请看下面的例子:

import java.util.Hashtable;

import javax.naming.*;

import java.sql.*;

import javax.sql.*;

import java.io.*;

public class UseJNDIOfPooledDataSource {

public static void main(String[] args) {

Connection con = null;

try {

String filePath = "jdbcPool/mydatasource";

Hashtable env = new Hashtable();

env.put(Context.INITIAL_CONTEXT_FACTORY,

"com.sun.jndi.fscontext.RefFSContextFactory");

Context ctx = new InitialContext(env);

// 经过JNDI得到池化的数据源

ConnectionPoolDataSource ds =

(ConnectionPoolDataSource) ctx .lookup(filePath);

// 经过池化的数据源得到池化的链接

PooledConnection pcon = ds.getPooledConnection();

// 经过池化的链接得到链接

con = pcon.getConnection();

// 若是链接不成功,就会出现异常,不会执行下面这个语句

System.out.println("connect success!");

} catch (Exception e) {

e.printStackTrace();

} finally {

try {

if (con != null)// 用完链接后,要关闭释放

con.close();

} catch (Exception e) {

e.printStackTrace();

}

}

}

}

6:总结数据库链接的各类方式

下面经过综合的例子来讲明各类数据库的链接。

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.SQLException;

import java.util.Hashtable;

import javax.naming.Context;

import javax.naming.InitialContext;

import javax.sql.ConnectionPoolDataSource;

import javax.sql.DataSource;

import javax.sql.PooledConnection;

public class DBCon {

// 经过JDBC-ODBC桥得到链接

public static Connection getOdbcCon(String datasourcename, String name,String password)

throws ClassNotFoundException, SQLException {

String url = "jdbc:odbc:";

Connection con = null;

con = DriverManager.getConnection(url + datasourcename, name, password);

return con;

}

// 经过SQLSERVER的三个驱动,链接SQLSERVER2000数据库

public static Connection getSQLServerCon(String name, String password)

throws ClassNotFoundException, SQLException {

String url = "jdbc:microsoft:sqlserver://127.0.0.1:1433";

Connection con = null;

Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver");

con = DriverManager.getConnection(url, name, password);

return con;

}

// 经过ORACLE的驱动,链接ORACLE数据库

public static Connection getOracleCon(String name, String password)

throws ClassNotFoundException, SQLException {

Connection con = null;

Class.forName("oracle.jdbc.driver.OracleDriver");

// 得到链接 oracle数据库的端口号为:1521 数据服务器的名字叫goudan(做者外号)

String url = "jdbc:oracle:thin:@127.0.0.1:1521:goudan";

con = DriverManager.getConnection(url, name, password);

return con;

}

// 经过数据源得到链接

public static Connection getConnectionFromDataSource(String filePath)

throws javax.naming.NamingException, SQLException {

Connection con = null;

Hashtable env = new Hashtable();

env.put(Context.INITIAL_CONTEXT_FACTORY,

"com.sun.jndi.fscontext.RefFSContextFactory");

Context ctx = new InitialContext(env);

DataSource ds = (DataSource) ctx.lookup(filePath);

con = ds.getConnection();

return con;

}

// 经过链接池得到链接

public static Connection getConnectionFromPooledDataSource(String filePath)

throws javax.naming.NamingException, SQLException {

Connection con = null;

Hashtable env = new Hashtable();

env.put(Context.INITIAL_CONTEXT_FACTORY,

"com.sun.jndi.fscontext.RefFSContextFactory");

Context ctx = new InitialContext(env);

ConnectionPoolDataSource ds = (ConnectionPoolDataSource) ctx

.lookup(filePath);

PooledConnection pc = ds.getPooledConnection();

con = pc.getConnection();

return con;

}

}

内容总结

? JDBCJava Database Connectivity)是Sun提供的一套数据库编程接口API,由Java语言编写的类、接口组成;经过这些类和接口JAVA能够方便的访问各类数据库中的数据。

? JDBC链接数据库的四种方式

? JDBC-ODBC桥,简单易用,只适用于WINDOW平台,适用于学习JDBC

? 部分Java驱动程序(native-API partly-Java Driver),少用到。

? 网络驱动程序(net-protocol all-java driver(JDBC Proxy)),应用比较广。

? 纯Java驱动程序(native-protocal all-Java driver,它直接与数据库进行通信,最好的一种链接。

? JDBC的编程步骤

? 加载驱动程序;

? 创建到数据库的链接对象Connection

? 建立语句,Statement ,PreparedStatement,

CallableStatement,并将它们用于更新数据库或执行查询;

? 查询返回包含有已请求数据的ResultSet

? 经过SqlServer,Oracle的驱动程序链接数据库;

? JDBC数据源,链接池技术的实现原理及其优势

? 数据源技术的引入,能够提升数据库访问的灵活性

? 使用数据源,和安全有关的敏感信息,如用户名,密码甚至数据库服务器地址等信息只在一处编码。

? 使用链接池能够提升链接数据库的效率。

独立实践

1 写一个SQLSERVER链接池的实现,访问pub数据库。

2 写一个链接ORACLE数据库的实现,用scott用户登录,操做emp

3 从控制台输入注册信息(用户名,密码,确认密码),写到数据库里面。

4 从控制台输入用户名和密码,在数据库中并验证其有效性。(提示:须要编写的类有:User, Validation,DbUtil)。

5 登录成功后,从控制台输入一个表名,并从控制台打印该表里的数据。

第二十章:高级JDBC

学习目标

? 使用DDL,DML语言对数据库进行基本操做

? 预编译语句

? 使用事务

? 事务的级别控制

? 使用存储过程

? 操做元数据

? 可滚动的和可更新的结果集

? 批处理更新

? 字符大对象CLOB

? 二进制大对象BLOB

? RowSet 新特性

使用DDL,DML语言对数据库进行基本操做。

? 建立表并插入数据及修改数据:

import java.sql.Connection;

import java.sql.Statement;

public class CreateTable {

public static void main(String[] args) {

Connection con = null;

try {

// 经过链接池来得到一个链接

con = DBCon .getConnectionFromPooledDataSource("jdbcPool/mydatasource");

// 建立语句对象

Statement st = con.createStatement();

// 建立表的SQL语句

String sql = "create table student(id int,name char(30),age int)";

// 执行完SQL语句的结果

boolean b = st.execute(sql);

if (b) {

System.out.println("create success");

} else {

System.out.println("create fail");

}

// 插入数据到student

sql = "insert into student values(1,'andy',47)"

+ "insert into student values(2,'jacky',53)"

+ "insert into student values(3,'周润发',51)"

+ "insert into student values(4,'谢贤',60)";

// 执行完SQL语句的结果

b = st.execute(sql);

if (b) {

System.out.println("insert success");

} else {

System.out.println("create fail");

}

// 更新表数据

sql = "update student set name='刘德华' where id=1";

int rows = st.executeUpdate(sql);

// 若是更新成功,rows确定是大于1的值

if (rows > 0)

System.out.println("update success");

else

System.out.println("update fail");

} catch (Exception e) {

e.printStackTrace();

} finally {

try {

if (con != null)

con.close();

} catch (Exception e) {

e.printStackTrace();

}

}

}

}

查询数据库里的数据

Statement对象的executeQuery()方法通常用于执行一个select语句,它只返回一个结果集,要想把查询结果最后显示给用户,必须对ResultSet对象进行处理。ResultSet对象包括一个由查询语句返回的一个表,这个表中包含全部的查询结果。对ResultSet对象的处理必须逐行进行。ResultSet对象维持一个指向当前行的指针(相似于Iterator的用法)。最初,这个指针指向第一行以前。ResultSetnext()方法使这个指针移向下一行。所以,第一次使用next()方法将指针指向结果集的第一行,这是能够对第一行的数据进行处理。处理完毕后,使用next()方法,将指针移向下一行,继续处理第二行数据。next()方法的返回值是一个boolean值,若为true,则说明指针成功地移向下一行,能够对该行进行处理。若返回值是false,则说明没有下一行,即结果集已经处理完毕。按从左至右的顺序对各列进行处理能够得到较高的执行效率。ResultSet接口的getXXX()方法能够从某列中得到结果,XXX表示JDBC的数据类型。

请看下例:

import java.sql.Connection;

import java.sql.ResultSet;

import java.sql.ResultSetMetaData;

import java.sql.Statement;

public class ResultSetTest {

public static void main(String[] args) {

Connection con = null;

try {

con = DBCon

.getConnectionFromPooledDataSource("jdbcPool/mydatasource");

Statement st = con.createStatement();

String query = "select id,name from student";

// 得到一个结果集

ResultSet rs = st.executeQuery(query);

// 得到结果集的元数据(表及相关的信息)

ResultSetMetaData rsmt = rs.getMetaData();

// 获得结果集有几列

int num = rsmt.getColumnCount();

String[] columns = new String[num];

// 列的序号是从1开始的

for (int i = 0; i < num; i++)

columns[i] = rsmt.getColumnName(i + 1);

// 先输出列名

for (int i = 0; i < num; i++)

System.out.print(columns[i] + " ");

// 输出列名以后换行

System.out.println();

// 取出结果

while (rs.next()) {

// 输出每一行的值

for (int i = 1; i <= num; i++) {

String temp = rs.getString(i);

System.out.print(temp + " ");

}

System.out.println();

}

} catch (Exception e) {

e.printStackTrace();

} finally {

try {

// 用完后要关闭链接,释放资源

if (con != null)

con.close();

} catch (Exception e) {

e.printStackTrace();

}

}

}

}

输出结果为:(Eclipse环境)

预编译语句(PreparedStatement

Statement对象在每次执行SQL语句时都将该语句传给数据库,在屡次执行同一语句时,这样作效率较低。这时能够使用PreparedStatement对象.若是数据库支持预编译,它能够将SQL语句传给数据库做预编译,之后每次执行这个SQL语句时,速度就能够提升不少。若是数据库不支持预编译,则在语句执行时,才将其传给数据库。这对用户来讲是透明的。PreparedStatement对象的SQL语句还能够接受参数。在语句中指出须要接受哪些参数,而后进行预编译。在每一次执行时,能够给SQL语句传输不一样的参数,这样就大大提升了灵活性。PreparedStatement接口是Statement接口派生的子接口,所以它能够使用Statement接口中的方法。

代码

import java.sql.Connection;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.ResultSetMetaData;

import java.sql.Statement;

public class PrepareStatementTest {

public static void main(String[] args) {

Connection con = null;

try {

con = DBCon

.getConnectionFromPooledDataSource("jdbcPool/mydatasource");

// 建立修改表的PrepareStatement SQL语句

String sql = "update student set name=? where id=?";

// 建立预编译语句对象

PreparedStatement st = con.prepareStatement(sql);

String[] names = new String[] { "梁朝伟", "贝壳汗母", "小罗", "霍元甲" };

for (int i = 0; i < names.length; i++) {

st.setString(1, names[i]);

st.setInt(2, i + 1);

st.executeUpdate();

}

st.close();

// 打印执行完SQL语句的结果

Statement stq = con.createStatement();

// 定义一个查询的SQL语句

String query = "select id,name from student";

// 得到一个结果集

ResultSet rs = stq.executeQuery(query);

// 得到结果集的元数据(表及的信息)

ResultSetMetaData rsmt = rs.getMetaData();

// 获得有几列,保存在num变量里

int num = rsmt.getColumnCount();

String[] columns = new String[num];

// 列的序号是从1开始的

for (int i = 0; i < num; i++)

columns[i] = rsmt.getColumnName(i + 1);

// 先输出列名

for (int i = 0; i < num; i++)

System.out.print(columns[i] + " ");

// 输出列名以后换行

System.out.println();

// 取出结果

while (rs.next()) {

// 输出每一行的值

for (int i = 1; i <= num; i++) {

String temp = rs.getString(i);

System.out.print(temp + " ");

}

System.out.println();

}

} catch (Exception e) {

e.printStackTrace();

} finally {

try {

if (con != null)

con.close();

} catch (Exception e) {

e.printStackTrace();

}

}

}

}

使用事务

下面介绍如何把一系列语句组织成一个事务?若是事务中全部命令都能正确执行,就能够提交这个事务;不然,若是事务中有一个命令出现错误,回滚这个事务,并返回到提交以前的状态,好像什么也没有发生。把命令组合成事务的主要缘由是保证数据库的完整性。对于一个事务而言,要么事务中语句所有获得正确执行,事务就可被提交了,要么它中间出现错误。后一种状况,能够调用rollback()方法,数据库将自动放弃上一次提交事务以来的所有变化。一个数据库链接的缺省模式是autocommit模式,每一个SQL命令一执行就会提交给数据库。一旦某个命令已提交,就不能把它回退。能够用Connection接口的getAutocommit()方法,检验数据库的目前自动提交模式设置。用命令con.setAutoCommit(false)方法关闭自动提交模式。用con.commit()命令提交事务。用con.rollback()回滚一个事务。

代码

import java.sql.Connection;

import java.sql.PreparedStatement;

import java.sql.SQLException;

import javax.naming.NamingException;

public class UseTran {

public static void main(String[] args) {

Connection con = null;// 方法里临时变量要手动初始化

PreparedStatement updateAge = null;

String updateString = "update student "

+ "set age = ? where name like ?";

String jndiname = "jdbcPool/mydatasource";

try {

con = DBCon.getConnectionFromPooledDataSource(jndiname);

updateAge = con.prepareStatement(updateString);

int[] age = { 45, 39, 25, 96 };

String[] names = { "梁朝伟%", "贝壳汗母%", "小罗%", "霍元甲%" };

int len = age.length;

// 设置事务提交模式为非自动提交

con.setAutoCommit(false);

for (int i = 0; i < len; i++) {

updateAge.setInt(1, age[i]);

updateAge.setString(2, names[i]);

updateAge.executeUpdate();

}

// 上面执行的语句,若是不出现异常则提交 SQL 语句

con.commit();

System.out.println("update success!");

} catch (NamingException ex) {

System.err.println("Name Not Bound : " + ex.getMessage());

} catch (SQLException ex) {

System.err.println("SQLException: " + ex.getMessage());

if (con != null) {

try {

System.err.print("Transaction is being ");

System.err.println("rolled back");

// 若是出现异常则事务回滚

con.rollback();

} catch (SQLException excep) {

System.err.print("SQLException: ");

System.err.println(excep.getMessage());

}

}

} finally {// 无论发生不发生异常,要关闭链接,释放资源

try {

if (con != null) {

con.close();

}

} catch (Exception e) {

e.printStackTrace();

}

}

}

}

事务的级别控制

SQL术语中,事务是逻辑工做单元(logical unit of work,LUW)构成的一个或多个语句。这在某种含义上意味着,一切都是事务。不过,一般而言,术语事务用来表示或全或无的系列操做;也就是说,要么一切十分红功,要么什么也没有发生。典型的事务是从银行账户提款,并存放到另外一个。只要提款完成,金额就消失了。另外一个范例是复式薄记记账法中的借方和贷方:借方和贷方都必须完成。

在单用户模式,事务很是容易理解-它们只是和保存或忘记应用程序的状态有关。在多用户模式中,事务变得复杂多了。多用户事务的经典说明是银行账户,其中一个应用程序试图在借记账户,同时另外一个应用程序试图贷记同一个账户。在前面已经介绍过多线程编程(并发编程)。相似的,事务的根本问题是保持两个事务相互隔离,不然一个应用程序就可能影响另外一个,从而致使错误的程序状态。当处理多个访问相同数据的用户时,一般可能出现三种问题:

? 脏读:当应用程序使用了被另外一个应用程序修改过的数据,而这个数据处于未提交状态时,就会发生脏读。第二个应用程序随后会请求回滚被其修改的数据。第一个事务使用的数据就会被损坏,或者"变脏"

? 不可重复的读:当一个事务得到了数据,而该数据随后被一个单独的事务所更改时,若第一个事务再次读取更改后的数据,就会发生不可重复的读。这样,第一个事务进行了一个不可重复的读。

? 虚读:当事务经过某种查询获取了数据,另外一个事务修改了部分该数据,原来的事务第二次获取该数据时,就会发生虚读。第一个事务如今会有不一样的结果集,它可能包含虚读。

为了解决与"多个线程请求相同数据"相关的问题,事务之间用锁相互隔开。多数主流的数据库支持不一样类型的锁;所以,JDBC API支持不一样类型的事务,它们由Connection对象指派或肯定。在JDBC API中能够得到下列事务级别:

? TRANSACTION_NONE 说明不支持事务

? TRANSACTION_READ_UNCOMMITTED说明在提交前一个事务能够看到另外一个事务的变化。这样脏读,不可重复的读和虚读都是容许的。

? TRANSACTION_READ_COMMITTED说明读取未提交的数据是不容许的。这个级别仍然容许不可重复的读和虚读产生。

? TRANSACTION_REPEATABLE_READ说明事务保证可以再次读取相同的数据而不会失败,但虚读仍然会出现。

? TRANSACTION_SERIALIZABLE是最高的事务级别,它防止脏读,不可重复读和虚读。

为何不是全部事务都运行在TRANSACTION_SERIALIZABLE模式以保证最高程度的数据完整性呢?问题在于,和处理多线程编程有关的问题类似,事务保护的级别越高,性能损失就越大。假定您的数据库和JDBC驱动程序支持这个特性,则给定一个Connection对象,您能够明确地设置想要的事务级别:con.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);

您还能够肯定当前事务的级别:

int transactionIsolation = con.getTransactionIsolation();

if (transactionIsolation == Connection.TRANSACTION_NONE)

System.out.println("当前事务级别为:TRANSACTION_NONE");

代码:

import java.sql.Connection;

import java.sql.SQLException;

import javax.naming.NamingException;

public class TranTest {

public static void main(String[] args) {

String jndiname = "jdbcPool/mydatasource";

Connection con = null;

try {

con = DBCon.getConnectionFromPooledDataSource(jndiname);

int transactionIsolation = con.getTransactionIsolation();

if (transactionIsolation == Connection.TRANSACTION_NONE) {

System.out.println("当前事务级别为:TRANSACTION_NONE");

} else if (transactionIsolation == Connection.TRANSACTION_READ_COMMITTED) {

System.out.println("当前事务级别为:TRANSACTION_READ_COMMITTED");

} else if (transactionIsolation == Connection.TRANSACTION_READ_UNCOMMITTED) {

System.out.println("当前事务级别为:TRANSACTION_READ_UNCOMMITTED");

} else if (transactionIsolation == Connection.TRANSACTION_REPEATABLE_READ) {

System.out.println("当前事务级别为:TRANSACTION_REPEATABLE_READ");

} else if (transactionIsolation == Connection.TRANSACTION_SERIALIZABLE) {

System.out.println("当前事务级别为:TRANSACTION_SERIALIZABLE");

}

con.setAutoCommit(false);

con.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);

transactionIsolation = con.getTransactionIsolation();

if (transactionIsolation == Connection.TRANSACTION_NONE) {

System.out.println("当前事务级别为:TRANSACTION_NONE");

} else if (transactionIsolation == Connection.TRANSACTION_READ_COMMITTED) {

System.out.println("当前事务级别为:TRANSACTION_READ_COMMITTED");

} else if (transactionIsolation == Connection.TRANSACTION_READ_UNCOMMITTED) {

System.out.println("当前事务级别为:TRANSACTION_READ_UNCOMMITTED");

} else if (transactionIsolation == Connection.TRANSACTION_REPEATABLE_READ) {

System.out.println("当前事务级别为:TRANSACTION_REPEATABLE_READ");

} else if (transactionIsolation == Connection.TRANSACTION_SERIALIZABLE) {

System.out.println("当前事务级别为:TRANSACTION_SERIALIZABLE");

}

} catch (NamingException ex) {

System.err.println("Name Not Bound : " + ex.getMessage());

} catch (SQLException ex) {

System.err.println("SQLException : " + ex.getMessage());

} finally {

try {

if (con != null)

con.close();

} catch (Exception e) {

e.printStackTrace();

}

}

System.out.println("程序执行结束!");

}

}

使用存储过程

不少数据库都支持在数据库内部执行的函数。这种方法有几个好处,包括更快的性能和改进的安全性。这些函数称为存储过程。存储过程是用来封装SQL语句来完成一个完整的业务功能,相似于面向对象里面方法的概念。虽然它们一般是用SQL编写的,但也能够用数据库支持的任何编程语言编写。随着Java语言日趋流行,几个数据库厂商---OracleOracle数据库)和IBMdb2数据库)都起用了Java语言建立存储过程,还能够在不一样数据库之间移动存储过程。

存储过程能够支持三种类型的参数:IN,OUTINOUT,这对于存储过程在数据库内部真正能作什么来讲,带来了很大的灵活性。无论存储过程是用什么语言编写的,它都能以一种标准的方式从Java应用程序调用。

首先,您须要建立一个CallableStatement对象。为了标识存储过程和过程须要的参数的类型和数量,还要容许使用三种类型的调用。下面的清单说明了这三种类型(假定咱们正在调用一个名为StudentList的存储过程):

? {call StudentList}若是过程不须要参数

? {call StudentList(?,?)}若是过程须要两个参数

? {?=call StudentList(?,?)}若是参数须要两个参数并返回一个

要想使用存储过程,首先应该建立一个存储过程!

代码的实现

import java.sql.Connection;

import java.sql.SQLException;

import java.sql.Statement;

import javax.naming.NamingException;

public class CreateStoredProceduresofSQLServer {

public static void main(String[] args) {

Connection con = null;

Statement stmt = null;

String jndiname = "jdbcPool/mydatasource";

try {

con = DBCon.getConnectionFromPooledDataSource(jndiname);

stmt = con.createStatement();

// 1.建立存储过程show_students

String createProcedure1 = "create procedure show_students " + "as " + "select id, name,age " + "from students " + "order by id";

// 删除数据库中存在的同名过程

stmt.executeUpdate("if exists(select name from sysobjects "

+ "where name='show_students'and type='p') "

+ "drop procedure show_students");

stmt.executeUpdate(createProcedure1);

// 2.建立储存过程onestudent

String createProcedure2 = "create procedure onestudent "

+ "@stu_id int = null, " + "@name varchar(20) output, "

+ "@age int output " + "as " + "if @stu_id = null "

+ "BEGIN "

+ " PRINT 'ERROR: You must specify a stu_id value.' "

+ " RETURN "

+ "END "

+

// Get the sales for the specified cof_name and " +

// assign it to the output parameter. " +

"SELECT @name = name, @age = age " + "FROM coffees "

+ "WHERE id = @stu_id " + "RETURN ";

stmt.executeUpdate("if exists(select name from sysobjects "

+ "where name='onestudent'and type='p') "

+ "drop procedure onestudent");

stmt.executeUpdate(createProcedure2);

// 3.建立函数

String createProcedure3 = "CREATE FUNCTION pubuse.ageofstu "

+

// Input cof_name

"(@stu_name varchar(20)) "

+ "RETURNS int "

+ // return sales

"AS " + "BEGIN " + " DECLARE @age int "

+ " SELECT @age = age " + " FROM student "

+ " WHERE name like @stu_name " + " RETURN @age "

+ "END ";

stmt.executeUpdate("if exists(select name from sysobjects "

+ "where name='ageofstu') "

+ "drop function pubuse.ageofstu");

stmt.executeUpdate(createProcedure3);

stmt.close();

con.close();

} catch (NamingException ex) {

System.err.println("Name Not Bound : " + ex.getMessage());

} catch (SQLException ex) {

System.err.println("SQLException : " + ex.getMessage());

}

System.out.println("程序执行结束!");

}

}

下面是使用存储过程的代码:

import java.sql.CallableStatement;

import java.sql.Connection;

import java.sql.ResultSet;

import java.sql.SQLException;

import java.sql.Types;

import javax.naming.NamingException;

public class InvokeStoreProcdureofSQLServer {

public static void main(String[] args) {

Connection con = null;

String jndiname = "jdbcPool/mydatasource";

// 定义调用存储过程和函数的 SQL 语句

String callSQL1 = "{call show_students}";

String callSQL2 = "{call onestudent(?,?,?)}";

String callSQL3 = "{? = call ageofstu(?)}";

try {

con = DBCon.getConnectionFromPooledDataSource(jndiname);

// 调用第 1 个存储过程

CallableStatement cs = con.prepareCall(callSQL1);

ResultSet rs = cs.executeQuery();

System.out.println("第一个存储过程调用结果");

while (rs.next()) {

String id = rs.getString(1);

String name = rs.getString(2);

String age = rs.getString(3);

System.out.println(id + " " + name + " " + age);

}

// 调用第 2 个存储过程

cs = con.prepareCall(callSQL2);

cs.setString(1, "2");

cs.registerOutParameter(2, Types.CHAR);

cs.registerOutParameter(3, Types.INTEGER);

cs.execute();

String name = cs.getString(2);

int age = cs.getInt(3);

System.out.println("第二个存储过程调用结果");

System.out.println("This student's name is " + name

+ " and age is " + age);

// 调用函数

cs = con.prepareCall(callSQL3);

cs.setString(2, "小罗");

cs.registerOutParameter(1, Types.INTEGER);

cs.execute();

age = cs.getInt(1);

System.out.println("函数调用结果");

System.out.println("This student's name is " + age + ".");

cs.close();

con.close();

} catch (NamingException ex) {

System.err.println("Name Not Bound : " + ex.getMessage());

} catch (SQLException ex) {

System.err.println("SQLException : " + ex.getMessage());

}

System.out.println("调用结束!");

}

}

操做元数据

JDBC Meta Data(元数据)----描述数据的数据。它的接口有:

? DatabaseMetaData(数据库元数据)

? ResultSetMetaData(结果集元数据)

DatabaseMetaData(数据库元数据)

在对数据源进行链接之后,获得一个Connection对象,能够从这个对象得到有关数据源的各类信息,包括关于数据库中的各个表,表中的各个列,数据类型和存储过程等各方面的信息。根据这些信息,JDBC程序能够访问一个事先并不了解的数据库。获取这些信息的方法都是在DatabaseMetaData的对象上实现的,而DatabaseMetaData对象是在Connection对象之上得到的。

下面的语句能够在一个链接的基础上建立一个DatabaseMetaData 对象:

DatabaseMetaData dm=con.getMetaData();

数据库的一些经常使用信息可经过DatabaseMetaData对象的下列方法得到。

? getURL()//返回一个String对象,表明数据库的URL.

? getUserName()//返回此链接使用的数据库的用户名

? isReadOnly()//返回一个boolean值,指示数据库是否只容许读操做。

? getDatabaseProduceName()//返回数据库的产品名称

? getDatabaseProduceVersion()//返回数据库的版本号

? getDriverName()//返回驱动程序的名称。

? getDriverVersion()//返回驱动程序的版本号

代码示例:

import java.sql.Connection;

import java.sql.DatabaseMetaData;

import java.sql.ResultSet;

import java.sql.SQLException;

import javax.naming.NamingException;

public class DataBaseMetaDataTest {

public static void main(String[] args) {

Connection con = null;

String jndiname = "jdbcPool/mydatasource";

try {

con = DBCon.getConnectionFromPooledDataSource(jndiname);

// 测试数据库信息

DatabaseMetaData dm = con.getMetaData();

System.out.println("1. 数据库的基本信息");

System.out.println("Database is " + dm.getDatabaseProductName());

System.out.println("Database version is "

+ dm.getDatabaseProductVersion());

System.out.println("JDBC Driver is " + dm.getDriverName());

System.out.println("JDBC driver version is "

+ dm.getDriverVersion());

// 获取数据库中目录(数据库)的信息

System.out.println("2. 数据库中目录(数据库)的信息");

ResultSet catalogs = dm.getCatalogs();

while (catalogs.next()) {

System.out.println(catalogs.getString(1));

}

// 获取数据库中模式的信息

System.out.println("3. 数据库中模式的信息");

ResultSet schemas = dm.getSchemas();

while (schemas.next()) {

System.out.println(schemas.getString(1));

}

// 获取数据库中各个表的状况

System.out.println("4. 数据库中各个表的信息");

ResultSet tables = dm.getTables("pubs", null, null, null);

while (tables.next()) {

for (int i = 0; i < 5; i++) {

System.out.print(tables.getString(i + 1));

System.out.print(" | ");

}

System.out.println();

}

// 获取数据库表中各个列的信息

System.out.println("5. 数据库表中各个列的信息");

ResultSet columns = dm.getColumns(null, null,

"student", null);

while (columns.next()) {

for (int i = 0; i < 18; i++) {

System.out.print(columns.getString(i + 1));

System.out.print(" | ");

}

System.out.println();

}

} catch (NamingException ex) {

System.err.println("Name Not Bound : " + ex.getMessage());

} catch (SQLException ex) {

System.err.println("SQLException : " + ex.getMessage());

} finally {

try {

if (con != null)

con.close();

} catch (Exception e) {

e.printStackTrace();

}

}

System.out.println("程序结束!!!");

}

}

ResultSetMetaData(结果集元数据)

根据结果集的元数据,能够获得一个查询结果集关于查询表中列的个数,各个列名,类型以及各个列的宽度等。ResultSetMetaData的对象能够由ResultSet对象的getMetaData()方法获得。

ResultSetMetaData对象的经常使用方法以下:

? ResultSet rs=stmt.executeQuery();

? ResultSetMetaData rsmd=rs.getMetaData();

? rsmd.getColumnCount()//返回ResultSet对象的列数。

? rsmd.getColumnDisplaySize(int column);//返回column指定的列的最大宽度。

? rsmd.getColumnLabel(int column)//返回column指定列的标签。

? rsmd.getColumnName(int column)//返回column指定列的列名。

请看下例:

import java.sql.Connection;

import java.sql.ResultSet;

import java.sql.ResultSetMetaData;

import java.sql.SQLException;

import java.sql.Statement;

import javax.naming.NamingException;

public class ResultSetMetaDataTest {

public static void main(String[] args) {

Statement stmt = null;

ResultSet rs = null;

Connection con = null;

String jndiname = "jdbcPool/mydatasource";

try {

con = DBCon.getConnectionFromPooledDataSource(jndiname);

stmt = con.createStatement();

String querySQL1 = "select * from student";

rs = stmt.executeQuery(querySQL1);

// 提取结果集的元数据:

ResultSetMetaData rsmd = rs.getMetaData();

int colCount = rsmd.getColumnCount();

String[] columnNames = new String[colCount];

String[] columnLabels = new String[colCount];

int[] columnTypes = new int[colCount];

for (int i = 0; i < colCount; i++) {

columnNames[i] = rsmd.getColumnName(i + 1);

columnLabels[i] = rsmd.getColumnLabel(i + 1);

columnTypes[i] = rsmd.getColumnType(i + 1);

}

System.out.println();

System.out.println("提取的数据以下:");

System.out.println();

System.out .println("----------------------");

for (int i = 0; i < colCount; i++) {

System.out.print(columnLabels[i] + "\t");

}

System.out.println();

for (int i = 0; i < colCount; i++) {

System.out.print(columnNames[i] + "\t");

}

} catch (NamingException ex) {

System.err.println("Name Not Bound : " + ex.getMessage());

} catch (SQLException ex) {

System.err.println("SQLException: " + ex.getMessage());

} finally {

try {

if (con != null)

con.close();

} catch (Exception e) {

}

}

System.out.println("程序结束!!!");

}

}

可滚动的和可更新的结果集

可滚动的结果集表示在结果集中先后滚动显示数据。可更新的结果集表示能够经过结果集来更新数据库里的数据。

可滚动的ResultSet能够使用如下方法:

? absolute()

? afterLast()

? beforeFirst()

? first()

? getRow()

? isAfterLast()

? isBeforeFirst()

? isFirst()

? isLast()

? last()

? moveToCurrentRow()//仅对可更新的ResultSet有效

? previous()

? relative(int rows)//将游标移动相对量的行,或者正方向或者负方向

为了让查询返回可滚动的结果集,你必须用

Statement stmt=con.createStatement(type,concurrency);

获得一个不一样的Statement对象。

对于PreparedStatement,调用

PreparedStatement stmt=con.preparedStatement(command,type,concurrency);

例如,若是你只想在一个结果总来回滚动,而不想编辑它的数据,用下列语句:

Statement stmt=con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);

全部调用方法:

ResultSet rs=stmt.executeQuery(query);

返回的结果集都是可滚动的,一个可滚动的结果集用一个光标指示目前的位置。

ResultSet类型值

? TYPE_FORWARD_ONLY 结果集不可滚动

? TYPE_SCROLL_INSENSITIVE 结果集可滚动,但不反映数据库的变化

? TYPE_SCROLL_SENSITIVE 结果集可滚动,并反映数据库的变化

ResultSet同步值

? CONCUR_READ_ONLY 不能用结果集更新数据库

? CONCUR_UPDATABLE 能够用结果集更新数据库

若是你想编辑ResultSet类型数据,并让这些变化在数据库中自动体现出来,就须要建立一个可更新的结果集。可更新的结果集不必定是可滚动的,但若是想让用户编辑数据,一般这个结果集也是可滚动的。为了获得可更新的结果集,构造语句以下:

Statement stmt=con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);

接着,调用executeQuery()就可返回可更新的结果集。

注意:不是全部的查询均可返回可更新的结果集。若是某个查询涉及多个表,那么它的返回结果多是不可更新的。但若是查询只涉及一个表或涉及用主关键字链接的多个表,那么返回结果是可更新的,这能够用ResultSetgetConcurrency()方法验证。

SQL类型相对应,JDBC 2.0为每一个数据类型提供了与它相应的updateXxx()方法,如updateDouble(),updateString()等等。

updateXxx()方法只能改变当前光标的记录值,而不能改变数据库中数据。调用updateRow()方法把修改后的数据保存到数据库中。若是移动到另外一行以前,没有调用updateRow()方法,本行中全部的更新将被撤消。也能够调用cancelRowUpdates()方法,撤消本行中的全部修改。

例如:

String query="select * from student";

ResultSet rs=stmt.executeQuery(query);

while(rs.next()){

double age=rs.getInt("age");

rs.updateInt("age",age+1);

rs.updateRow();

往数据库中添加一个新记录:

? 首先用moveToInsertRow()方法把光标移动到指定位置(它称为插入行)。

? 接着,调用 updateXxx()方法,在插入位置建立一个新行。

? 最后,调用insertRow()方法,把这个新行发送给数据库。插入完成后,调用

moveToCourrentRow()方法把光标移回调用moveToInsertRow()方法之前的位置。

例:

rs.moveToInsertRow();

rs.updateString("name","李雨春");

rs.updateInt("age",21);

rs.insertRow();

rs.moveToCurrentRow();

调用下面的方法,能够删除位于光标下面的一行:

rs.deleteRow();

deleteRow() 方法会当即把这行从结果集和数据库中删除。

代码的实现

import java.sql.Connection;

import java.sql.ResultSet;

import java.sql.SQLException;

import java.sql.Statement;

import javax.naming.NamingException;

public class ResultSetDemo {

public static void main(String[] args) {

Connection con = null;

Statement stmt = null;

ResultSet rs = null;

String querySQL = "select name,age from student";

String jndiname = "jdbcPool/mydatasource";

try {

con = DBCon.getConnectionFromPooledDataSource(jndiname);

stmt = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,

ResultSet.CONCUR_UPDATABLE);

rs = stmt.executeQuery(querySQL);

// 显示修改以前的数据

System.out.println("修改以前的数据:");

while (rs.next()) {

for (int i = 1; i <= 2; i++) {

System.out.print(rs.getObject(i));

System.out.print(" ");

}

System.out.println();

}

// 过年了,把记录里年龄都加1,虽然写一个SQL语句就能够实现

// update student set age=age+1

// 可是为了测试rs的用法咱们还使用JDBC

rs.beforeFirst();

while (rs.next()) {

int age = rs.getInt("age");

rs.updateInt("age", age + 1);

rs.updateRow();

}

// 显示修改后的数据

rs = stmt.executeQuery(querySQL);

System.out.println("修改以后的数据:");

while (rs.next()) {

for (int i = 1; i <= 2; i++) {

System.out.print(rs.getObject(i));

System.out.print(" ");

}

System.out.println();

}

// 增长一条数据

rs.moveToInsertRow();

rs.updateString("name", "李雨春");

rs.updateInt("age", 21);

rs.insertRow();

rs.moveToCurrentRow();

// 显示增长后的数据

System.out.println("增长以后的数据:");

rs = stmt.executeQuery("select name,age from student");

while (rs.next()) {

for (int i = 1; i <= 2; i++) {

System.out.print(rs.getObject(i));

System.out.print(" ");

}

System.out.println();

}

// 删除最后一行数据

if (rs.last())

rs.deleteRow();

System.out.println("删除以后的数据:");

rs = stmt.executeQuery(querySQL);

while (rs.next()) {

for (int i = 1; i <= 2; i++) {

System.out.print(rs.getObject(i));

System.out.print(" ");

}

System.out.println();

}

stmt.close();

} catch (NamingException ex) {

System.err.println("Name Not Bound : " + ex.getMessage());

} catch (SQLException ex) {

System.out.println("\n--- SQLException caught ---\n");

while (ex != null) {

System.out.println("Message: " + ex.getMessage());

System.out.println("SQLState: " + ex.getSQLState());

System.out.println("ErrorCode: " + ex.getErrorCode());

ex = ex.getNextException();

System.out.println();

}

} finally {

try {

if (con != null)

con.close();

} catch (Exception e) {

}

}

System.out.println();

System.out.println("End.");

}

}

打印结果如图

批处理更新

批处理更新功能能够一次向数据库提交多个更新操做,要求数据库进行处理。一块儿提交多个更新(而非一个一个单独地提交更新)在某些状况下将大大提升性能。

能够利用Statement,PreparedStatement,CallableStatement对象来提交批处理更新。

代码的实现

import java.sql.BatchUpdateException;

import java.sql.Connection;

import java.sql.ResultSet;

import java.sql.SQLException;

import java.sql.Statement;

import javax.naming.NamingException;

public class BatchUpdateTest {

public static void main(String[] args) {

Connection con = null;

Statement stmt = null;

String jndiname = "jdbcPool/mydatasource";

try {

con = DBCon.getConnectionFromPooledDataSource(jndiname);

stmt = con.createStatement();

con.setAutoCommit(false);

stmt.addBatch("insert into student values('拉登',56)");

stmt.addBatch("insert into student values('张学友',49)");

stmt.addBatch("insert into student values('刘德华',51)");

stmt.addBatch("insert into student values('猪八戒',128)");

int[] updateCounts = stmt.executeBatch();

con.commit();

for (int i = 0; i < updateCounts.length; i++) {

System.out.println("Line" + (i + 1) + " update counts is : "

+ updateCounts[i]);

}

con.setAutoCommit(true);

ResultSet uprs = stmt.executeQuery("select * from student");

System.out.println("table student after insertion:");

while (uprs.next()) {

String name = uprs.getString("name");

int age = uprs.getInt("age");

System.out.print(name + " " + age);

System.out.println();

}

uprs.close();

stmt.close();

} catch (BatchUpdateException b) {

System.err.println("SQLException: " + b.getMessage());

System.err.println("SQLState: " + b.getSQLState());

System.err.println("Message: " + b.getMessage());

System.err.println("Vendor: " + b.getErrorCode());

System.err.print("Update counts: ");

int[] updateCounts = b.getUpdateCounts();

for (int i = 0; i < updateCounts.length; i++) {

System.err.print(updateCounts[i] + " ");

}

} catch (SQLException ex) {

System.err.println("SQLException: " + ex.getMessage());

System.err.println("SQLState: " + ex.getSQLState());

System.err.println("Message: " + ex.getMessage());

System.err.println("Vendor: " + ex.getErrorCode());

} catch (NamingException ex) {

System.err.println("Name Not Bound : " + ex.getMessage());

} finally {

try {

if (con != null)

con.close();

} catch (Exception e) {

}

}

System.out.println("程序执行结束!");

}

}

打印结果如图

字符大对象CLOB

mssqlserver 2000里面并无字符大对象这个数据类型,可是能够经过nchar类型来代替字符大对象数据,可是只能存放3k字符如下的。在ORACLE数据库里面存在字符大对象CLOB数据类型。该数据类型用来存放文本类型的数据。

SQL代码:

ORACLE的实现,在运行该例子以前,首先把ORACLE的驱动文件classes12.jar加到classpath里面。而后打开ORACLE数据库,创建一张表。SQL语句以下:

CREATE TABLE MESSAGES(ID VARCHAR2(20),TITLE VARCHAR2(30),AUTHOR VARCHAR(20),MESSAGE CLOB);

JAVA代码:

package com.app;

import java.io.File;

import java.io.FileInputStream;

import java.io.OutputStream;

import java.sql.ResultSet;

import java.sql.Statement;

import java.sql.Connection;

import oracle.sql.CLOB;

public class OracleClob {

public static void main(String[] args) {

String jndiname = "jdbcPool/mydatasource";

Connection con = DBCon.getConnectionFromPooledDataSource(jndiname);

String insertSQL = "Insert INTO messages VALUES('1001','my message', 'xsw', empty_clob())";

try {

con.setAutoCommit(false);

Statement stmt = con.createStatement();

stmt.execute(insertSQL);

String cmd = "SELECT message FROM messages WHERE id='1001'";

ResultSet rset = stmt.executeQuery(cmd);

rset.next();

CLOB clob = (CLOB) rset.getClob(1);

File file = new File("myfile.txt");

FileInputStream fis = new FileInputStream(file);

OutputStream outstream = clob.getAsciiOutputStream();

int size = clob.getBufferSize();

byte[] buffer = new byte[size];

int length = -1;

while ((length = fis.read(buffer)) != -1)

outstream.write(buffer, 0, length);

fis.close();

outstream.close();

con.commit();

con.setAutoCommit(true);

} catch (Exception e) {

e.printStackTrace();

}

}

}

下面看下MSSQLSERVER数据库的实现(首先在查询分析器里面运行下列SQL语句:

CREATE TABLE messages (

id char(30),

title char(40) ,

author char(20) ,

message ntext

)

而后运行下面的代码:

package com.app;

import java.io.File;

import java.io.FileInputStream;

import java.sql.Connection;

import java.sql.PreparedStatement;

public class MSSQLClob {

public static void main(String[] args) {

String jndiname = "jdbcPool/mydatasource";

Connection con = DBCon.getConnectionFromPooledDataSource(jndiname);

String insertSQL = "Insert INTO messages VALUES(?,?,?,?)";

PreparedStatement stmt = con.prepareStatement(insertSQL);

File file = new File("myfile.txt");

FileInputStream fis = new FileInputStream(file);

stmt.setString(1, "1001");

stmt.setString(2, "my message");

stmt.setString(3, "xsw");

stmt.setAsciiStream(4, fis, (int) file.length());

stmt.executeUpdate();

fis.close();

}

}

二进制大对象BLOB

该数据类型是用来存放图片文件的。在MSSQLSERVER 2000没有该数据类型,那么能够用binary数据类型代替,可是该数据只能存放小7k的图片。在ORACLE数据库里,有BLOB数据类型,来直接存放图片文件,直接操做该数据类型。对不支持BLOB数据类型的MSSQLSERVER 2000数据库,咱们能够用二进制流进行读取图片文件。

ORACLE的实现:

SQL语句:

CREATE TABLE AUTHORS(AUTHOR VARCHAR2(40),PHOTO BLOB)

JAVA代码:

package com.app;

import java.io.File;

import java.io.FileInputStream;

import java.io.OutputStream;

import java.sql.Connection;

import java.sql.ResultSet;

import java.sql.Statement;

import oracle.sql.BLOB;

public class OracleBlob {

public static void main(String[] args) {

String jndiname = "jdbcPool/mydatasource";

Connection con = DBCon.getConnectionFromPooledDataSource(jndiname);

String insertSQL = "Insert INTO authors VALUES('1001', empty_blob())";

try {

con.setAutoCommit(false);

Statement stmt = con.createStatement();

stmt.execute(insertSQL);

String cmd = "SELECT photo FROM authors WHERE author='1001'";

ResultSet rset = stmt.executeQuery(cmd);

rset.next();

BLOB blob = (BLOB) rset.getBlob(1);

File file = new File("mypic.jpg");

FileInputStream fis = new FileInputStream(file);

OutputStream outstream = blob.getAsciiOutputStream();

int size = blob.getBufferSize();

byte[] buffer = new byte[size];

int length = -1;

while ((length = fis.read(buffer)) != -1)

outstream.write(buffer, 0, length);

fis.close();

outstream.close();

con.commit();

con.setAutoCommit(true);

} catch (Exception e) {

e.printStackTrace();

}

}

}

MSSQLSERVER 2000的实现

SQL语句:

CREATE TABLE authors(

author char(50) ,

photo image NOT NULL

)

代码:

package com.app;

import java.io.File;

import java.io.FileInputStream;

import java.sql.Connection;

import java.sql.PreparedStatement;

public class MSSQLBlob {

public static void main(String[] args) {

String jndiname = "jdbcPool/mydatasource";

Connection con = DBCon.getConnectionFromPooledDataSource(jndiname);

String insertSQL = "Insert INTO authors VALUES(?,?)";

PreparedStatement stmt = con.prepareStatement(insertSQL);

File file = new File("mypic.jpg");

FileInputStream fis = new FileInputStream(file);

stmt.setString(1, "1001");

stmt.setBinaryStream(2, fis, (int) file.length());

stmt.executeUpdate();

fis.close();

}

}

RowSet 新特性

Java 5Java Database Connectivity (JDBC)方面增强了支持,其中加入了新的包javax.sql.rowsetjavax.sql.rowset.serialjavax.sql.rowset.spi。从RowSet接口继承规定了五个新的接口:

? CachedRowSet:能够不用与数据源创建长期的链接,只有当从数据库读取数据或是往数据库写入数据的时候才会与数据库创建链接,它提供了一种轻量级的访问数据库的方式,其数据均存在内存中。

? JdbcRowSet:对ResultSet的对象进行包装,使得能够将ResultSet对象作为一个JavaBeans 组件。

? FilteredRowSet:继承自CachedRowSet,能够根据设置条件获得数据的子集。

? JoinRowSet:继承自CachedRowSet,能够将多个RowSet对象进行SQL Join语句的合并。

? WebRowSet:继承自CachedRowSet,能够将WebRowSet对象输出成XML格式。

CachedRowSet

CachedRowSet能够经过调用populate(ResuletSet rs)来生成数据,一旦得到数据,CachedRowSet就能够断开与数据库的链接,直到往数据库写入数据的时候才需创建链接。

下面的代码演示了如何根据ResultSet创建一个CachedRowSet对象:

package com.itjob;

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.ResultSet;

import java.sql.SQLException;

import java.sql.Statement;

import javax.sql.rowset.CachedRowSet;

import com.sun.rowset.CachedRowSetImpl;

public class CachedRSTest {

private final static String DRIVER = "sun.jdbc.odbc.JdbcOdbcDriver";

private final static String URL = "jdbc:odbc:myodbc";

private final static String USER = "student";

private final static String PASSWORD = "student";

public static void main(String[] args) throws InstantiationException,

IllegalAccessException, ClassNotFoundException, SQLException {

Class.forName(DRIVER);

Connection con = DriverManager.getConnection(URL, USER, PASSWORD);

Statement stmt = con.createStatement();

ResultSet rs = stmt.executeQuery("SELECT * FROM student");

CachedRowSet crset = new CachedRowSetImpl();

crset.populate(rs);

rs.close();

stmt.close();

con.close();

System.out.println("name");

while (crset.next()) {

System.out.println(crset.getString("name"));

}

}

}

JdbcRowSet

JdbcRowSet功能与ResultSet相似,与CachedRowSet不一样,JdbcRowSet在操做时保持与数据库的链接。能够将与数据库链接的URL,用户名,密码以及执行的SQL语句经过setXXX形式绑定。另外,JdbcRowSet返回的结果默认是能够上下滚动和可更新的,固然这须要数据库厂商提供的JDBC Driver支持。下面的代码演示了如何经过set方法设定数据库链接参数,以及如何操做JdbcRowSet对象。

package com.itjob;

import java.sql.SQLException;

import javax.sql.rowset.JdbcRowSet;

import com.sun.rowset.JdbcRowSetImpl;

public class JdbcRSDemo {

private final static String DRIVER = "sun.jdbc.odbc.JdbcOdbcDriver";

private final static String URL = "jdbc:odbc:myodbc";

private final static String USER = "student";

private final static String PASSWORD = "student";

public static void main(String[] args) throws SQLException, ClassNotFoundException {

Class.forName(DRIVER);

JdbcRowSet jrs = new JdbcRowSetImpl();

// 设置链接数据库的URL

jrs.setUrl(URL);

// 设置链接数据库的用户名

jrs.setUsername(USER);

// 设置链接数据库的密码

jrs.setPassword(PASSWORD);

// 设置执行数据库的SQL语句

jrs.setCommand("select * from student");

jrs.execute();

System.out.println("name");

while (jrs.next()) {

System.out.println(jrs.getString("name"));

}

jrs.close();

}

}

FilteredRowSet

FilteredRowSet接口中规定了能够设定过滤器,其过滤接口为Predicate接口,必须实现Predicate接口中的evaluate方法。具体的代码以下:

package com.itjob;

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.ResultSet;

import java.sql.SQLException;

import java.sql.Statement;

import javax.sql.rowset.CachedRowSet;

import javax.sql.rowset.FilteredRowSet;

import com.sun.rowset.CachedRowSetImpl;

import com.sun.rowset.FilteredRowSetImpl;

public class FilteredRSDemo {

private final static String DRIVER = "sun.jdbc.odbc.JdbcOdbcDriver";

private final static String URL = "jdbc:odbc:myodbc";

private final static String USER = "student";

private final static String PASSWORD = "student";

public static void main(String[] args) throws InstantiationException,

IllegalAccessException, ClassNotFoundException, SQLException {

Class.forName(DRIVER).newInstance();

Connection con = DriverManager.getConnection(URL, USER, PASSWORD);

Statement stmt = con.createStatement();

ResultSet rs = stmt.executeQuery("SELECT * FROM student");

FilteredRowSet frs = new FilteredRowSetImpl();

frs.populate(rs);

con.close();

MyDBFilter filter = new MyDBFilter(1, 4);

frs.setFilter(filter);

System.out.println("name");

while (frs.next()) {

System.out.println(frs.getString("name"));

}

frs.close();

}

}

package com.itjob;

import java.sql.SQLException;

import javax.sql.RowSet;

import javax.sql.rowset.CachedRowSet;

import javax.sql.rowset.Predicate;

class MyDBFilter implements Predicate {

private int low;

private int high;

public MyDBFilter(int low, int high) {

this.low = low;

this.high = high;

}

public boolean evaluate(RowSet rs) {

CachedRowSet crs=(CachedRowSet)rs;

//若是idlowhigh之间返回真

try {

int idValue = crs.getInt("id");

if (low < idValue && idValue < high) {

return true;

}

} catch (SQLException e) {

}

return false;

}

public boolean evaluate(Object arg0, int arg1) throws SQLException {

return false;

}

public boolean evaluate(Object arg0, String arg1) throws SQLException {

return false;

}

}

WebRowSet

WebRowSet继承自CachedRowSet,支持XML格式的查询,更新等操做,下面的代码将WebRowSet对象输出成XML格式到文件。

package com.itjob;

import java.io.FileNotFoundException;

import java.io.FileOutputStream;

import java.io.IOException;

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.ResultSet;

import java.sql.SQLException;

import java.sql.Statement;

import javax.sql.rowset.WebRowSet;

import com.sun.rowset.WebRowSetImpl;

public class WebRSDemo {

private final static String DRIVER = "sun.jdbc.odbc.JdbcOdbcDriver";

private final static String URL = "jdbc:odbc:myodbc";

private final static String USER = "student";

private final static String PASSWORD = "student";

public static void main(String[] args) throws InstantiationException,

IllegalAccessException, ClassNotFoundException, SQLException, IOException {

Class.forName(DRIVER).newInstance();

Connection con = DriverManager.getConnection(URL, USER, PASSWORD);

Statement stmt = con.createStatement();

ResultSet rs = stmt.executeQuery("SELECT * FROM student");

// 关闭与数据库的链接

con.close();

WebRowSet wrs = new WebRowSetImpl();

wrs.populate(rs);

FileOutputStream out = new FileOutputStream("data.xml");

wrs.writeXml(out);

wrs.close();

}

}

内容总结

? 对数据库进行SQL语句进行基本操做(包括create, insert, update,delete

? 使用预编译语句能够提升数据库访问效率及增强安全性

? 能够在应用程序级别使用事务

? 经过事务的级别控制,能够知足不一样的业务方面的需求

? 使用存储过程优化查询,提升查询效率

? 操做元数据

? 可滚动的和可更新的结果集

? 批处理更新

? 字符大对象CLOB的用法

? 二进制大对象BLOB用法

独立实践

1 链接oracle数据库,创建个可更新的结果集,对scott用户下的emp表进行批处理更新年龄,姓名等字段。

2 链接sql 2000数据库,使用pubs数据库,创建一个带参数的方法,该方法经过输入表名,能动态打印出该表的记录(包括列名)。

3 写一个保存人简历的程序(有图片)。不要求显示输入和输出,所有经过代码来操做,可是要求从数据库能够看到记录。

4 经过事务操做来实现两个帐户转帐的过程。(要求本身建表)

5 综合所学知识,对scott用户下的emp表进行增、删、改、查操做。

第二十一章:XML基础

学习目标

? XML的概念

? 定义XML文档

? 命名空间(Naming Space

? XML 文档规则

? DTD

? SCHEMA

? 样式表(XSL

XML的概念

XML,称为可扩展标记语言(Extensible Markup Language),用来建立自定义标记的标记语言。它由万维网协会(W3C)建立,用来克服 HTML(即超文本标记语言(Hypertext Markup Language),它是全部网页的基础)的局限。和 HTML 同样,XML 基于 SGML ― 标准通用标记语言(Standard Generalized Markup Language)。XML 是为 Web 设计的。

XML是以数据为中心的,用来标记数据,说明数据是什么的。

应用领域为:

? 信息存储和交换领域,相似于数据管理系统,来存储数据。这种数据处理与硬件,软件没有太大的关系,就是一个文本文件,具备可移植性。处理XML数据的时候,经过JAVA代码写的解析器就能够存取XML数据。

? 在J2EE服务器用的比较多,在服务器端经过XML文件来进行处理数据业务。配置文件几乎全是XML文件。在EJBHibernateJAVA热门技术领域,XML的应用更加剧要。

XMLHTML相同点:

? XMLHTML都是基于文本的标记语言。

XMLHTML差异:

? 在功能上最大的区别就是XML是用来存储、传输以及交换数据的,HTML是用来格式化和显示数据的。

? 在内容上的最大的区别是HTML里面的标记(markup)都是定义好的,XML里面的标记都是自定义的。好比:对于那些HTML中的标记中形如"用斜体显示数据"<i>…</i>)的地方,XML标签则表现为程序中的一个字段名。它为一个数据定义一个标记(label)来表示该数据。(如:<name>andy lau</name>)。注意:因为标识数据可让你看出其中的意义(如何解释它,应该如何处理它),XML有时也被描述为一种可以指明数据意义(语义)的机制。

HTML是最成功的标记语言。您几乎能够在任何设备(从掌上电脑到大型机)上查看最简单的HTML标记,甚至能够用合适的工具将HTML标记转换成语音和其它格式。既然HTML成功了,为何W3C(万维网协会)还要建立XML呢?为了回答这个问题,请查看下面这个文档:

<p>

<b>张三</b>

<br>

<br>西安电子科技大学:电子信息工程

</p>

HTML的问题在于它是为人设计的。即便不用浏览器查看上面的HTML文档,您和我也会知道那是某我的的信息。做为高级动物---人来说:您和我具备理解大多数文档的含义和意图的智慧。可是遗憾的是机器不能作到。尽管这个文档中的标记告诉浏览器如何显示该信息,但标记没有告诉浏览器信息是什么?!您和我都知道它是一我的的信息,但计算机并不知道。要显示HTML,浏览器只需遵照HTML文档中的指令便可。段标记告诉浏览器在新的一行显示,而且一般在前面有一个空行,而两个换行标记则告诉浏览器前进到下一行,而且行之间没有空行。尽管浏览器出色地将文档格式化,但计算机仍不知道这个表明某我的的信息。

以下图在浏览器里的显示:

为了完成对样本HTML文档的讨论,请考虑从该信息中提取出该人是学哪一个专业的?请看下面的算法:

从该文档中找到有两个<br>标记的段落,那么学校就是第二个换行标记的后面的第一个冒号后面的内容,尽管该算法对该文档起做用,可是不具有通用性。若是文档格式一改变,那么该算法就不生效了。

如今让咱们来看一个样本XML文档。使用XML,您能够给文档中的标记赋予某种涵义。更重要的是,计算机也容易处理这样的信息。

请看下例:

<person>

<name>张三</name>

<sex></sex>

<daxue>西安电子科技大学</daxue>

<zhuanye>电子信息工程</zhuanye>

</person>

如今算法编写又简单又具备通用性:

咱们只须要找到<zhuanye></zhuanye>标记之间的内容(技术上称为<zhuanye>元素),就能够从该文档抽取邮政编码。

定义XML文档

有三个通用术语用来描述XML文档的组成部分:标记,元素和属性。

? 标记是左尖括号(<)和右尖括号(>)之间的文本。有开始标记(例如:<zhuanye>)和结束标记(例如:</zhuanye>)。

? 元素是开始标记,结束标记以及位于两者之间的全部内容。在上面的样本中,<person>元素包含四个子元素<name>,<sex>,<daxue>,<zhuanye>.

? 属性是一个元素的开始标记中的名称-值对。如:

<tizhong danwei='kg'>80</tizhong> danwei <tizhong>元素的属性。

定义XML基本语法要求:

? XML文档必须包含在一个单一元素中。这个单一元素称为根元素,它包含文档中全部文本和全部其它元素。如:

<aihao>

play majiang

</aihao>

XML文档包含在一个单一元素<aihao>中。

下面是一个不包含单一根元素的文档:

<aihao>play pike</aihao>

<aihao>play majiang</aihao>

无论该文档可能包含什么信息,XML解析器都会拒绝它。

? XML元素不能重叠。下面是一些不合乎规则的标记:

<person>刘德华<age>28</person></age>

若是您在<person>元素中开始了<age>元素,则必须在<person>元素中结束<age>元素。

如:<person>刘德华<age>49</age></person>

? 不能省去任何结束标记。在下面的XML文档里面标记是不合乎规则的。由于没有任何结束标记。

<person>刘德华

<age>49

? 空元素:若是一个元素根本不包含标记,则称为空元素。如:

<tizhong danwei="kg"></tizhong>

<tizhong danwei="kg"/> 是等价的。

? 元素是区分大小写的 在HTML中,<b><B>是相同的;在XML中,它们是不一样的。若是您试图用</B>标记去结束<b>元素,那么将会出错。

<b>我爱深圳市计算机行业协会!</B> ---------错误

<b>我爱深圳市计算机行业协会!</b> ---------正确

属性的规则:

? 属性必须有值

? 属性值必须用引号括起

请看下例:

<tizhong danwei></tizhong> ---错误(属性无值)

<tizhong danwei=kg></tizhong> --错误(属性值没有用引号括起)

<tizhong danwei='kg'></tizhong>----正确

<tizhong danwei="kg"></tizhong>----正确

属性值能够用单引号括起,也能够用双引号括起,可是要始终保持一致。若是属性值包含单引号或双引号,则您能够使用另外一种引号来括起该值。如:

description="zhangsan's JAVA is very well",或者使用实体"表明双引号,使用实体'表明单引号。实体是一个符号(如:",XML解析器会用其它文本代替该符号(如: ")。

XML声明: 大多数XML文档以XML声明做为开始,它向解析器提供了关于文档的基本信息。XML 声明一般在XML 文档的第一行出现。XML 声明不是必选项,可是若是使用XML 声明,必须在文档的第一行,前面不得包含任何其余内容或空白。

? 下所示:

<?xml version="1.0" encoding="gb2312" standalone="no"?>

<?xml version="1.1" encoding="utf-8" ?>

声明最多能够包含三个名称-值对。

? version是使用的XML版本;目前最新版本为1.1

? encoding是该文档所使用的字符集。如没有指定encoding,XML解析器会假定字符在utf-8字符集中,这是一个几乎支持世界上全部语言的字符和象形文字的unicode标准。

? standalone(能够是yesno)定义了是否能够在不读取任何外部实体或外部DTD文档的状况下处理该文档。由于standalone="no"是缺省值,因此您不多会在XML声明中看到standalone

? 注释能够出如今文档的任何位置;它们甚至能够出如今根元素的前面或后面。注释以<!-- 开始,以 --> 结束。注释不能在结束部分之外包含双连字符 --;除此以外,注释能够包含任何内容。最重要的是,注释内的任何标记都忽略;若是您但愿除去XML文档的一块较大部分,只须要用注释括住那个部分便可。(要恢复这个注释掉的部分,只需除去注释标记便可。)下面是包含注释的标记:

<!-- 这里是注释 -->

? 处理指令: XML文件还能够包含处理指令(PI Processing Instruction),这些指令能够将命令或信息传给正在处理XML数据的应用。

一般它用来传递信息给解析器的下游程序。通常写法以下:

<?xml:stylesheet href="style.css" type="text/css"?>

命名空间(Naming Space

XML 命名空间提供了一种避免元素命名冲突的方法。

命名冲突

由于XML文档中使用的元素不是固定的,那么两个不一样的XML文档使用同一个名字来描述不一样类型的元素的状况就可能发生。而这种状况又每每会致使命名冲突。请看下面两个例子

这个 XML 文档在table元素中携带了水果的信息:

<table>

<tr>

<td>Apples</td>

<td>Bananas</td>

</tr>

</table>

这个 XML 文档在table元素中携带了桌子的信息(家具,不能吃的哦):

<table>

<name> African Coffee Table </name>

<width>80</width>

<length>120</length>

</table>

若是上面两个XML文档片段碰巧在一块儿使用的话,那么将会出现命名冲突的状况。由于这两个片段都包含了<table>元素,而这两个table元素的定义与所包含的内容又各不相同。

使用前缀解决命名冲突问题

下面的XML文档在table元素中携带了信息:

<h:table>

<h:tr>

<h:td>Apples</h:td>

<h:td>Bananas</h:td>

</h:tr>

</h:table>

下面的XML文档携带了家具table的信息:

<f:table>

<f:name>African Coffee Table</f:name>

<f:width>80</f:width>

<f:length>120</f:length>

</f:table>

如今已经没有元素命名冲突的问题了,由于这两个文档对各自的table元素使用了不一样的前缀,table元素在两个文档中分别是(<h:table> <f:table>)

经过使用前缀,咱们建立了两个不一样的table元素。

使用命名空间

下面的XML文档在table元素中携带了信息:

<h:table xmlns:h="http://www.w3.org/TR/html4/">

<h:tr>

<h:td>Apples</h:td>

<h:td>Bananas</h:td>

</h:tr>

</h:table>

下面的XML文档携带了家具table的信息:

<f:table xmlns:f="http://www.w3s.com/furniture">

<f:name>African Coffee Table</f:name>

<f:width>80</f:width>

<f:length>120</f:length>

</f:table>

在上面两个例子中除了使用前缀外,两个table元素都使用了xmlns属性,使元素和不一样的命名空间关联到一块儿。

命名空间属性

命名空间属性通常放置在元素的开始标记处,其使用语法以下所示:

xmlns:namespace-prefix="namespace"

在上面的例子中,命名空间定义了一个Internet 地址:

xmlns:f="http://www.w3s.com/furniture"

W3C 命名规范声明命名空间自己就是一个统一资源标示符,Uniform Resource Identifier (URI)

当咱们在元素的开始标记处使用命名空间时,该元素全部的子元素都将经过一个前缀与同一个命名空间相互关联。

注意:用来标识命名空间的网络地址并不被XML解析器调用,XML解析器不须要从这个网络地址中查找信息,该网络地址的做用仅仅是给命名空间一个惟一的名字,所以这个网络地址也能够是虚拟的,然而又不少公司常常把这个网络地址指向一个真实的Web页面,这个地址包含了关于当前命名空间更详细的信息。

能够访问http://www.w3.org/TR/html4/.

统一资源标识符

通用资源标识符Uniform Resource Identifier (URI)是一个标识网络资源的字符串。最普通的URI应该是统一资源定位符Uniform Resource Locator (URL)URL用于标识网络主机的地址。另外一方面,另外一个不经常使用的URI是通用资源名字Universal Resource Name (URN)。在咱们的例子中,通常使用的是URLs

既然前面的例子使用的URL地址来标识命名空间,咱们能够确信这个命名空间是惟一的。

默认的命名空间

定义一个默认的XML命名空间使得咱们在子元素的开始标记中不须要使用前缀。他的语法以下所示:

<element xmlns="namespace">

下面的XML文档在table元素中包含了水果的信息:

<table xmlns="http://www.w3.org/TR/html4/">

<tr>

<td>Apples</td>

<td>Bananas</td>

</tr>

</table>

下面的XML文档包含了家具table的信息:

<table xmlns="http://www.w3s.com/furniture">

<name>African Coffee Table</name>

<width>80</width>

<length>120</length>

</table>

使用命名空间

文档开始使用XSL的时候,就会发现命名空间使用的是如此频繁。XSL样式单主要用于将XML文档转换成相似于HTML文件的格式。

若是看一下下面的XSL文档,就会发现有不少标记都是HTML标记。那些标记并非HTML标记,是加了前缀的XSL,这个XSL前缀由命名空间"http://www.w3.org/TR/xsl"所标识:

<?xml version="1.0" encoding="ISO-8859-1"?>

<xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/xsl">

<xsl:template match="/">

<html>

<body>

<table border="2" bgcolor="yellow">

<tr>

<th>Title</th>

<th>Artist</th>

</tr>

<xsl:for-each select="CATALOG/CD">

<tr>

<td>

<xsl:value-of select="TITLE" />

</td>

<td>

<xsl:value-of select="ARTIST" />

</td>

</tr>

</xsl:for-each>

</table>

</body>

</html>

</xsl:template>

</xsl:stylesheet>

XML 文档规则

XML 文档规则是用来规定XML文档有那些标记组成,标记的多少,顺序,属性等方面的内容。也就是规定XML的格式及内容。目的是为了解析器解析该XML文档提供方便。XML的文档规则有两种:DTDXML Schema

DTDDocument Type Definition)文档类型定义

DTD定义能够在XML文档中出现的元素,这些元素出现的次序,它们能够如何相互嵌套以及XML文档结构的其它详细信息。

XML Schema

模式能够定义您能在DTD中使用的全部文档结构,还能够定义数据类型和比DTD更复杂的规则。

所以XML 文档能够分为三个类型:

? 格式良好的:知足XML的基本的语法特色,可是不遵照DTD或者XML Schema

? 有效的文档:即知足了XML的基本的语法特色,又遵照DTD或者 XML Schema

? 无效的文档:不知足XML的基本的语法特色。或者该文档不遵照DTD或者XML Schema

首先咱们介绍下DTDDocument Type Definition)文档类型定义

XML强大之处在于能够定制本身的标记来替代预先定义的标记集。这里的最大问题在于如何让处理器明白这些自定义的标记,同时咱们常说XML要求文档结构完整,这意味着咱们必须在提供XML文件时提供它的语法规范。为此,XML须要有一个文档来定义标记的含义,元素相关的属性等,这样解析器就可以检查输入的数据是否正确。

DTD能够包含在XML文档中,也能够独立为一个文件。一个符合DTD验证的XML文档是有效的文档。

请看下例:(student.dtd)

<?xml version="1.0" encoding="UTF-8"?>

<!ELEMENT students (student*)>

<!ELEMENT student (name,age,address,phone?)>

<!ELEMENT name (#PCDATA)>

<!ELEMENT age (#PCDATA)>

<!ELEMENT address (#PCDATA)>

<!ELEMENT phone (#PCDATA)>

<!ATTLIST age xusui CDATA #IMPLIED>

DTD定义了样本文档中使用的全部元素。它定义了三个元素

? <students>元素包含零个或者多个<student>元素。

? <student>元素包含一个<name>,一个<age>,一个<address>,和一个可选的<phone>元素。也就是说<name>,<age>,<address>元素必须出现,而〈phone〉元素无关紧要,可是这些元素必须按前后顺序出现。

? 包含文本的元素:(#PCDATA)代表该元素必须包含通过解析的字符数据,通过解析的字符数据实质上就是否是标记的任何文本,即该元素不能包含本身的子元素)

? 其中<age>元素 有个可选的属性xusui.

尽管DTD至关简单,但它清楚地说明了什么样的元素组合是合乎规则的。<age>元素出如今<name>元素以前就是不合乎规则的。另外,请注意,DTD语法不一样于普通的XML语法。(XML Schema 文档自己就是XML)。尽管DTD的语法不一样,但您仍能够将普通的注释放到DTD中。

下面的XML文档是知足上面DTD的文档:

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE students SYSTEM "D:\java\xml\student.dtd">

<students>

<student>

<name>andy</name>

<age>49</age>

<address>luohu luohukejidasha</address>

</student>

<student>

<name>goudan</name>

<age xusui="当年减出生年加1">28</age>

<address>shanxi xi an</address>

<phone>02988755660</phone>

</student>

</students>

固然也能够把dtd文档写到XML文档内部:

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE students [

<!ELEMENT students (student*)>

<!ELEMENT student (name,age,address,phone?)>

<!ELEMENT name (#PCDATA)>

<!ELEMENT age (#PCDATA)>

<!ELEMENT address (#PCDATA)>

<!ELEMENT phone (#PCDATA)>

<!ATTLIST age

xusui CDATA #IMPLIED>

]>

<students>

<student>

<name>andy</name>

<age>49</age>

<address>luohu luohukejidasha</address>

</student>

<student>

<name>goudan</name>

<age xusui="当年减出生年加1">28</age>

<address>shanxi xi an</address>

<phone>02988755660</phone>

</student>

</students>

下面分析把DTD文档写在XML文档内部和写在外部的对比:

若是采用外部DTD的话,就须要有两个文档,第一个文档就是关于DTD的文档,第二个文档就是遵照DTD格式的内容文档。实际上,咱们能够创建无穷多个遵照该DTD格式的文档。咱们在构造关系数据库中的表的时候,须要定义好表的结构(也就是表包含的字段集合),而后就能够往这个表中放入记录,记录的个数从理论上讲能够是无穷多个的。这里关于表的结构就相似于DTD文档,记录相似于遵照DTD格式的内容文档。

外部DTD的好处是:它能够方便高效地被多个XML文件所共享。你只要写一个DTD文件,就能够被多个XML文件所引用。事实上,当许多组织须要统一他们的数据交换格式时,他们就是经过外部DTD来完成的。这样作不只简化了输入工做,还保证当你须要对DTD作出改动时,只需改动一个公用的DTD文件,而不用一一去改动每一个应用了它的XML文件。不过须要注意,若是DTD的改动不是"向后兼容"的,这时原先根据该DTD编写的那些XML文件可能就会出现问题了。因此在改动DTD时,必定要尽可能兼容之前的文档,除非解析器变了。(相似于JAVAJDK的发展,JDK的每一个版本的发布都会兼容之前的版本,这样在换JDK时,之前的程序就不要改动了。目前的最新JDK的版本是1.6也叫6.0)

由以上的例子可知,创建合法XML文件的要点之一就是要定义好DTD,而后全部的内容就按照DTD格式进行编写。DTD实际上表现了一个层次关系,你也能够把它理解成一棵树的结构。树中的节点实际就是一个个元素(ELEMENT,一个元素包含其余的元素。

对于一个DTD的定义,最关键的在于它内部的元素和属性的定义。按上面例子的理解,一个DTD文档实际上就是元素定义的集合,而元素可能包含属性,也可能不包含属性。属性能够经过如下的语法进行定义:

<! ATTLIST ElementName

AttributeName Type Default

AttributeName Type Default

>

ATTLIST是一个XML语言的保留关键字,ElementName表示元素的名称,它至关于程序设计语言中变量的名称。一个元素能够包含多个属性,一个属性由三部分构成:AttributeName,TypeDefaultAttributeName表示该属性的名字,Type代表该属性的类型,Default代表该属性的特色。咱们先来看一下XML所定义的属性类型的种类,以下表:

类型 具体的含义说明

CDATA 这个类型代表该属性只能包含字符数据,好比刘德华“hibernate”,”小罗等等

ID 该属性的取值必须是惟一的,就像咱们每一个人都有的身份证号码是惟一的同样,在一个文档内两个ID属性的值不能同样

IDREFIDREFS 这个属性的值实际上就像C++中的指针同样,它是一个指向文档中其余地方声明的ID值,因此,若是在具体的文档中该属性的取值和它所指向的ID值不匹配的话,就会返回错误。IDREFSIDREF相似,可是能够具备由空格分隔的多个引用

ENTITY,ENTITIES ENTITY属性的值必须对应一个在DTD文档内声明的但尚未分析过的实体。ENTITIES属性和ENTITY相似,不一样的是它能够包含多个实体,外部实体,参数实体和外部参数实体。能够把实体理解为程序设计语言中的变量

NMTOKENNMTOKENS NMTOKENCDATA的一个子集,它所使用的字符必须是字母,数字,句点,破折号,下划线或冒号。NMTOKENSNMTOKEN相似,不一样之处在于它能够包含多个值,每一个值之间用空格进行分隔

NOTATION NOTATION的值必须引用已在DTD文档其余地方声明过的某注解名称

NOTATIONenumerated 该属性的值必须匹配NOTATION名称列表中的某个名称。好比,咱们已经存在两个NOTATION,一个为beauty,一个为beast;咱们能够定义一个属性类型为NOTATIONbeauty|beast

Enumerated 该属性几乎和C++中的枚举变量同样(JDK1.5里面也有枚举类型),咱们事先定义好一些值,该属性的值必须匹配所列出的这些值。好比如今有值为美丽,魅力,温柔,体贴,该属性的类型就能够表现为(美丽|魅力|温柔|体贴),实际内容文档必须从这些值中取一个,值之间用“|”进行分隔

Default属性特色能够有四种形式:#REQUIRED,#IMPLIED,#FIXED value,Defaultvalue.下表对这四种形式进行了详细介绍:

值 含义

#REQUIRED 用来告诉XML解析程序,该元素的全部实例都必须有该属性的值,就像数据表中某一个字段为NOT NULL同样

#IMPLIED 表示若是该元素的实例中没有指定该元素的值的话,就忽略该属性,就像在数据表中某一个字段的值能够为NULL同样

#FIXED value 表示包含该属性的元素实例必须在指定列出的值中,好比 一个属性名称为学员:学员 CDATA #FIXED “刘德华

表示属性值只能去刘德华

Defaultvalue 为属性提供一个默认的值好比一个属性名称为学员:学员 CDATA “刘德华若是在该属性的实例中没有包含这个属性的话,解析器就认为该属性的值就是刘德华,若是在该属性的实例中包含了这个属性并赋值了的话,就采用这个赋值

下面的DTD例子用来讲明属性类型IDREF/IDREFS的使用:

以下例:<second.dtd>

<?xml version="1.0" encoding="UTF-8"?>

<!ELEMENT family (person+)>

<!ELEMENT person EMPTY>

<!ATTLIST person

relID ID #REQUIRED

parentID IDREFS #IMPLIED

name CDATA #REQUIRED

>

请注意:这里的parentID的类型为IDREFS,这个代表该属性的值必须在XML文档中出现过。若是该属性的值没在XML文档中出现过的话,解析器就认为该文档是不合法的。好比,下面的文档就是一个不合法的文档:

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE family SYSTEM "D:\java\xml\second.dtd">

<family>

<person relID="p1" name="李雨春" />

<person relID="p2" name="狗蛋" />

<person relID="p3" name="张曼玉" />

<person relID="p4" parentID="p1 p6 p2" name="小罗" />

</family>

缘由是parentID中出现了值 "p6",而这个值并无在文档中出现过。

元素的定义语法为:

<!ELEMENT NAME CONTENT>

这里ELEMENTXML语言的关键字,NAME表示元素的名称,CONTENT表示元素的类型。它指定了元素起始标记和结束标记之间容许包含的东西。CONTENT能够如下几种形式出现,以下表:

内容 解释

EMPTY 若是一个元素的CONTENT被声明为EMPTY的话,表示该元素不能包含任何子元素和文本

ANY 表示该元素中能够包含任何在DTD中定义的元素内容

#PCDATA 代表该元素能够包含任何字符数据,可是不能在其中包含任何子元素。

假设咱们定义元素学员:

<! ELEMENT student #PCDATA>

则下面的实例是正确的:

<student>比耳盖茨</student>

而下面的实例就是错误的:

<student>比耳<B>盖茨</B></student>

由于在其中包含了子元素<B>

通常若是定义元素的CONTENT#PCDATA,最好在其中只加入纯文本字符数据

其余类型 最一般的状况是一个元素自己是由其余元素的集合构成的

在前面的一些例子中,你们必定注意到了"|","+","*"等这些符号,这些符号具体的含义及使用方法见下表:

符号 符号类型 描述 示例 示例说明

() 括号 用来给元素分组 (刘德华|张学友),(小罗|卡罗斯),盖茨 分红三组

| 竖条 代表在列出的对象中选折一个 (爱人|朋友) 表示爱人和朋友必须出现,且只能出现一个

+ 加号 表示该对象最少出现一次,能够出现屡次 (student+ 表示student必须出现,能够出现屡次

* 星号 表示该对象容许出现任意屡次,也能够是零次 (爱好* 爱好能够出现零次到屡次

? 问号 表示该对象能够出现,但只能出现一次 (爱人? 爱人能够出现一次或零次

, 逗号 表示对象必须按指定的顺序出现 (name,sex,age) 表示name,sex,age必须顺序出现

有时咱们还会在DTD中遇到一些实体的定义,通常分为如下两种状况:

内部实体:

<!ENTITY texing "jdakdjfaldlfldsafdfdslkfdsa"

外部实体:

<!ENTITY texing SYSTEM "http://www.5itjob.com/zhaosheng.xml">

它们在XML文档中的使用是统一的:

computer&texing; </computer>

最后,咱们在看个例子:〈shenzhen.dtd>

<?xml version="1.0" encoding="UTF-8"?>

<!ELEMENT shenzhen (nanshan,luohu)>

<!ELEMENT nanshan (#PCDATA)>

<!ATTLIST nanshan mianji CDATA #REQUIRED>

<!ELEMENT luohu (#PCDATA)>

<!ATTLIST luohu mianji CDATA #REQUIRED>

<!ENTITY miaoshu "shen zhen de zui hao de qu">

下面是知足上面DTDXML文档:<testshenzhen.xml>

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE shenzhen SYSTEM "D:\j2se质料整理\xml\shenzhen.dtd">

<shenzhen>

<nanshan mianji="50">

&miaoshu;

</nanshan>

<luohu mianji="60" />

</shenzhen>

XML Schema

上面咱们讨论了用DTD来验证XML文档,虽然DTD胜任对XML文档结构的验证,可是它有不少局限。为此,微软提出了Schema的方案来改进DTDSchema从字面意义上来讲,能够翻译成架构,它的基本意思是为XML文档制定一种模式。Schema相对于DTD的明显好处是XML Schema文档自己也是XML文档,而不是像DTD同样使用自成一体的语法。这就方便了用户和开发者,由于能够使用相同的工具来处理XML Schema和其余XML信息,而没必要专门为Schema使用特殊的工具。Schema简单易懂,懂得XML语法规则的人均可以马上理解它。Schema的概念提出已久,但W3C的标准最近才出来,相应的应用支持还没有完善,但采用Schema已成为XML发展的一个趋势。

请看下例:<movie.dtd>

<?xml version="1.0" encoding="UTF-8"?>

<!ELEMENT movie (daoyan,zhuyan)>

<!ELEMENT daoyan (#PCDATA)>

<!ELEMENT zhuyan (#PCDATA)>

<movie.xsd>

<?xml version="1.0" encoding="UTF-8"?>

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"

elementFormDefault="qualified" attributeFormDefault="unqualified">

<xs:element name="daoyan" type="xs:string" />

<xs:element name="zhuyan" type="xs:string" />

<xs:element name="movie">

<xs:complexType>

<xs:choice maxOccurs="2">

<xs:element ref="daoyan" />

<xs:element ref="zhuyan" />

</xs:choice>

</xs:complexType>

</xs:element>

</xs:schema>

<moviedtd.xml>

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE movie SYSTEM "D:\java\xml\movie.dtd">

<movie>

<daoyan>张艺谋</daoyan>

<zhuyan>刘德华</zhuyan>

</movie>

<moviexsd.xml>

<?xml version="1.0" encoding="UTF-8"?>

<movie xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:noNamespaceSchemaLocation="D:\java\xml\movie.xsd">

<daoyan>张艺谋</daoyan>

<zhuyan>刘德华</zhuyan>

</movie>

从上例可看出这两个XML文当格式彻底是同样的。只不过采用的模式文档不同,一个是DTD 一个是 XML Schema

XML Schema中,元素是经过它的名字和类型来肯定的。名称就是该元素的名字,类型就像JAVA中的一个变量,有基本类型(int double char等),有复杂类型(类类型)。在Schema中也是同样,类型(type)能够分为两种形式,一种是简单类型,一种是复合类型。简单类型不能包含元素和属性。复杂类型不只能够包含属性,并且能够在其中嵌套其余的元素,或者能够和其余元素中的属性相关联。

SchemaDTD的区别:

XMLSGML中继承了DTD,并用它来定义内容的模式,验证和组织元素。同时,它也有不少局限,以下所述:

? DTD不遵照XML语法;

? DTD不可扩展;

? DTD不支持命名空间的应用;

? DTD没有提供强大的数据类型支持,只能表示很简单的数据类型。

Schema彻底克服了这些弱点,使得基于Web的应用系统交换XML数据更为容易。下面是它所展示的一些新特性:

? Schema彻底基于XML语法,不须要再学习特殊的语法;

? Schema能用处理XML文档的工具处理,而不须要特殊的工具;

? Schema大大扩充了数据类型,支持boolean,number,date and times,URI,integer,decimal number 等等

? Schema支持原型,也就是元素的继承。如:咱们定义了一个""数据类型,而后能够根据它产生"狼狗""纯狗"两种数据类型;

? Schema支持属性组。咱们通常声明一些公共属性,而后能够应用于全部的元素,属性组容许把元素,属性关系放于外部定义,组合;

? 开放性。原来的DTD只能有一个DTD应用于一个XML文档,如今能够有多个Schema运用于一个XML文档。

Schema的数据类型

基本数据类型

下表列出的是最基本的数据类型,被W3C组织所认可:以下表:

数据类型 相关特性 描述

string length,pattern,maxLength,minLength,enumeration,

whiteSpace 表示字符串

boolean pattern,whiteSpace 布尔型

decimal enumeration,pattern,minInclusive,minExclusive,

maxInclusive,maxExclusive 表明特定精度的数字

float pattern,enumeration,minInclusive,minExclusive,

maxInclusive,maxExclusive 表示单精度32位浮点数

double pattern,enumeration,minInclusive,minExclusive,

maxInclusive,maxExclusive 表示双精度64位浮点数

duration pattern,enumeration,minInclusive,minExclusive,

maxInclusive,maxExclusive 表示持续时间

dateTime pattern,enumeration,minInclusive,minExclusive,

maxInclusive,maxExclusive 表明特定的时间

time pattern,enumeration,minInclusive,minExclusive,

maxInclusive,maxExclusive 表明特定的时间,可是是天天重复的

date pattern,enumeration,minInclusive,minExclusive,

maxInclusive,maxExclusive 表明日期

hexBinary length,pattern,maxLength,minLength,enumeration 表明十六进制数

anyURL length,pattern,maxLength,minLength,enumeration 表明一个URL,用来定位文件

NOTATION length,pattern,maxLength,minLength,enumeration 表明NOTATION类型

扩展数据类型

下面这些类型是微软扩充的数据类型,并无被W3C组织彻底采纳。

数据类型 相关特性 描述

ID length,enumeration,pattern,maxLength,

minLength 用于惟一标识元素

IDREF length,enumeration,pattern,maxLength,

minLength 参考ID类型的元素或属性

ENTITY length,enumeration,pattern,maxLength,

minLengthwhiteSpace 实体类型

NMTOKEN length,enumeration,pattern,maxLength,

minLength NMTOKEN类型

NMTOKENS length,enumeration ,maxLength,

minLengthwhiteSpace NMTOKEN类型集

long enumeration,pattern,fractionDigits,

minInclusive,minExclusive,maxInclusive,

maxExclusive,totalDigits,whiteSpace 表示整型数,大小介于-92233720368547758089223372036854775807之间

int enumeration,pattern,fractionDigits,

minInclusive,minExclusive,maxInclusive,

maxExclusive,totalDigits,whiteSpace 表示整型数,大小介于-21474836482147483647之间

short enumeration,pattern,fractionDigits,

minInclusive,minExclusive,maxInclusive,

maxExclusive,totalDigits,whiteSpace 表示整型数,大小介于-3276832767之间

byte enumeration,pattern,fractionDigits,

minInclusive,minExclusive,maxInclusive,

maxExclusive,totalDigits,whiteSpace 表示整型数,大小介于-128127之间

数据类型的相关特性

前面这些数据类型说明中都有一列相关特性,用于说明该列的类型的一些扩充应用。下面是对它们应用的描述:

enumeration

指定的数据集中选折,主要的目的是为了能限定用户的选值。

fractionDigits

限定最大的小数位,主要用于控制精度。

length

指定数据的长度单元

maxExclusive

指定数据的最大值。其余值必须小于它

maxInclusive

指定最大值,其余值能够小于等于它

maxLength

指定长度的最大值

minExclusive

指定最小值,其余值必须大于它。

minInclusive

指定最小值,其余值能够大于等于它。

minLength

指定长度的最小值

pattern

规范数据的显示规范,使得数据必须匹配它的显示方式。

Schema的元素类型

Schema DTD中的元素类型丰富得多,这可以使用户和程序都更容易肯定元素的种类,下面的表,只是列出了最经常使用的元素类型。

元素类型 描述

schema 包含一个已经定义的schema

element 声明一个元素

group 把一组元素声明组合在一块儿,以便它们可以一块儿被复合类型应用

attribute 声明一个属性

attributeGroup 把一组属性声明组合在一块儿,以便他们可以一块儿被复合类型应用

choice 容许惟一的一个元素从一个组中被选择

simpleType 定义一个简单类型,它决定了元素和属性值的约束和相关信息

simpleContent 应用于complexType,对它的内容进行约束和扩展等

complexType 定义一个复合类型,它决定了一组元素和属性值的约束和相关信息

list 从一个特定数据类型的集合中选择定义一个简单类型元素

union 从一个特定简单数据类型的集合中选择定义一个简单类型元素

unique 定义一个属性或元素值,它必须在一个特定范围内

squence 给一组元素一个特定的序列

restriction 定义一个约束条件

Schema的样例以下:

<?xml version="1.0" encoding="UTF-8"?>

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"

elementFormDefault="qualified" attributeFormDefault="unqualified">

<!--内容-->

</xs:schema>

其中xs命名空间指定的是W3C组织的定义,也就是遵循它的标记集规范。目标命名空间是全局的,指定目标命名元素符合本Schema的定义。elementFormDefault的取值有两个选择:

unqualifiedqualified,前者表示目标命名空间不必定遵循本Schema,后者则必须遵循。attributeFormDefault相似。

<?xml version="1.0" encoding="UTF-8"?>

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"

elementFormDefault="qualified" attributeFormDefault="unqualified">

<xs:element name="cat" type="xs:string" />

<xs:element name="dog" type="xs:string" />

<xs:element name="tel">

<xs:simpleType>

<xs:restriction base="xs:string">

<xs:pattern value="(([0-5]){2}|([a-z]))?" />

</xs:restriction>

</xs:simpleType>

</xs:element>

<xs:element name="langdog" type="xs:string" substitutionGroup="dog" />

<xs:element name="maodog" type="xs:string" substitutionGroup="dog" />

<xs:element name="pets">

<xs:complexType>

<xs:choice maxOccurs="unbounded">

<xs:element ref="cat" />

<xs:element ref="dog" />

<xs:element ref="tel" />

</xs:choice>

</xs:complexType>

</xs:element>

</xs:schema>

element中有些经常使用的属性:

name 表明该元素的名字,不能和ref同时使用;

type 表明该元素的类型,能够是已有的简单类型,也能够自定义复合类型,或者应用其余命名空间中的类型定义,不能和ref同时使用;

minOccurs 表示该元素出现的最少次数;

maxOccurs 表示该元素出现的最大次数;

ref 表示该元素参考已经定义的元素;

fixed 表示该元素的值是不能改变的,不能与default一块儿使用;

default 表示该元素的缺省值;

substitutionGroup 表示这个元素的父类,如狼狗也属于狗这一类。

样式表(XSL

XML是一种程序交换原始数据的简单而标准的方法,然而基于XML的数据自己并不包含数据显示的信息。为了使数据便于阅读理解,咱们须要将信息更好地显示。相似于MVC模式,XML文档是M的部分,样式表就是V的部分,也就是说,同一个XML文档能够有多个显示效果(样式表)。样式表就是这样一种为XML文档"化妆"的文档,它是一种专门描述结构文档表现方式的文档,便可以描述这些文档如何在屏幕上显示,也能够描述它们的打印效果,甚至声音效果。使用样式表定义XML文档数据的显示方式,将XML数据与其表达形式分离的作法符合业界提倡的数据和表示层分开的思想(MVC)。这样一来,XML格式文档不会重蹈某些HTML文档(以显示数据为主)结构混杂,内容繁乱的覆辙,XML的编写者也能够集中精力于数据自己,而不受显示方式的细枝末节的影响。不只如此,样式表还带来另外一个好处,即定义不一样的样式表能够使相同的数据呈现不一样的显示外观,从而适合于不一样应用,甚至可以在不一样的显示设备上显示。这样,XML数据就能够获得最大程度上的重用性,知足不一样的应用需求。迄今为止,W3CWWW标准化组织)已经给出了两种样式表语言的推荐标准:一种是级联样式表CSSCascading Style Sheets),另外一种是可扩展样式表语言XSLeXtensible Stylesheet Language)。下面,咱们就来说述这两种样式表。

CSS样式表

级联样式表CSS是一种样式描述规则,是一种针对HTML而提出的样式表语言,如今一样能够很好地应用与描述XML文档的显示。利用CSS,咱们能够定义HTMLXML文档中元素的显示效果,包括元素的位置,颜色,背景,边框,字体,排版格式等等。

请看下面的一个XML文档:

<?xml version="1.0" encoding="UTF-8"?>

<?xml-stylesheet type="text/css" href="test.css"?>

<person>

<name>andy</name>

<age>49</age>

<address>中国 香港 铜锣弯</address>

</person>

咱们定义显示上面XML文档的CSS样式表(test.css),以下:

person

{

margin-top:.7in;

margin-bottom:.7in;

margin-left:1.5in;

margin-right:1in;

background-color:blue;

display:block

}

name

{

display:block;

font-family:Arial,Helvetica,sans-serif;

font-size:32pt;

width:10em;

color:red

}

age

{

display:block;

font-size=x-large;

color:yellow

}

address

{

display:block;

font-size:20pt;

color:cyan

}

显示效果以下图:

XML中引用一个级联样式表,咱们使用以下的处理指令:

<?xml-stylesheet type="text/css" href="样式表路径"?>

咱们在示例XML的文档的第二句,引入了样式表。

XML解析器解析XML文档和CSS文件,并执行以下任务:

解析"person.xml",抽取其元素;

使用"test.css"文件,应用样式格式化XML文档中的元素。

XSL样式表

XSL是一种用来转换XML文档的样式表,它包含转换和格式化XML文档的规则。XSL在转换XML文档时分为明显的两个过程:首先转换文档结构,而后将文档格式化输出:以下图:

这两个步骤能够分离开来单独处理,所以在XSL发展过程当中逐渐分裂为两种分支:XSLT(结构转换)和XSL-FO(格式化输出)。其中XSL-FO的做用相似于CSSHTML中的做用,因为其发展缓慢而不成熟,W3C至今还未能出台一个获得多方承认的XSL-FO,并且目前主流的浏览器大都不支持XSL-FO,本书不对其作讲解。前面学习XML时,咱们已经知道XML是一个完整的树形结构文档。在转换XML文档时可能须要处理其中的一部分(节点)数据,那么如何查找和定位XML文档中的信息呢?XPath就是一种专门用来在XML文档中查找信息的语言,用来描述如何识别,选择,匹配XML文档中的各个构成元件,包括元素,属性,文字内容等。XPath隶属XSLT,所以,咱们一般会将XSLT语法和XPath语法混在一块儿说。用一种比较好理解的解释:若是将XML文档看做一个数据库,XPath就是SQL查询语言;若是将XML文档当作DOS目录结构,XPath就是cd,dir等目录操做命令的集合。XSL包含XSLTXPath的强大功能,从而能够把XML文档转换成任何一种其它格式的文档,例如XML文档,HTML文档,XHTML文档等等,限于目前浏览器的支持能力,在大多数状况下,XSLXML文档转换为一个HTML文档进行显示,本节中咱们主要讲述怎样使用XSL样式表将XML文档转换为HTML文档。

XSL的基本结构

为了让你们可以对XSL有一个感性认识,并很快地掌握它的精髓,咱们先看一个简单例子。经过剖析这个例子,来掌握XSL的基本结构和一些基本语法。

请看下面的XML文档:

<?xml version="1.0" encoding="UTF-8"?>

<?xml-stylesheet type="text/xsl" href="teststudents.xsl"?>

<students>

<student ID="102376">

<name>李明</name>

<sex></sex>

<java>97</java>

<oracle>69</oracle>

<mssql>76</mssql>

</student>

<student ID="102378">

<name>张一强</name>

<sex></sex>

<java>92</java>

<oracle>83</oracle>

<mssql>62</mssql>

</student>

<student ID="102379">

<name>王亮</name>

<sex></sex>

<java>74</java>

<oracle>91</oracle>

<mssql>71</mssql>

</student>

<student ID="102380">

<name>赵三金</name>

<sex></sex>

<java>89</java>

<oracle>93</oracle>

<mssql>67</mssql>

</student>

</students>

下面是上面的XML文档所引用的teststudents.xsl:

<?xml version="1.0" encoding="UTF-8"?>

<xsl:stylesheet version="1.0"

xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

xmlns:fo="http://www.w3.org/1999/XSL/Format">

<xsl:template match="/">

<html>

<head>

<title>第一期东北JAVA班部分学员</title>

</head>

<body>

<h2 align="center">成绩单</h2>

<xsl:apply-templates select="students" />

</body>

</html>

</xsl:template>

<xsl:template match="students">

<table border="1" cellpadding="0" align="center">

<tbody>

<tr>

<th>姓名</th>

<th>性别</th>

<th>java</th>

<th>oracle</th>

<th>mssql</th>

</tr>

<xsl:for-each select="student">

<tr>

<td>

<xsl:value-of select="name" />

</td>

<td>

<xsl:value-of select="sex" />

</td>

<td>

<xsl:value-of select="java" />

</td>

<td>

<xsl:value-of select="oracle" />

</td>

<td>

<xsl:value-of select="mssql" />

</td>

</tr>

</xsl:for-each>

</tbody>

</table>

</xsl:template>

</xsl:stylesheet>

显示结果以下图: 如今咱们来分析一下这段XML代码的执行过程:

在做为XML声明和XSL声明以后,样式表利用<xsl:template><xsl:apply-templates/></xsl:template>声明XSL模板,并调用该模板。

根据<xsl:apply-templates/>,系统最早匹配XML源树的根节点。根节点用"/"表示,它的匹配方法在一对<xsl:template match="/">括起的源码中声明。按照这段代码,首先生成带有样式信息的HTML文档的开头一段代码。

<html>

<head>

<title>第一期东北JAVA班部分学员</title>

</head>

<body>

<h2 align="center">

成绩单

</h2>

下面,系统看到了<xsl:apply-templates select="students"/>的指示,因而,它在XML源树中寻找标记为"students"的节点进行匹配。就像函数调用同样,如今系统跳到了用<xsl:template match="students">括起的"函数"中继续生成下面的HTML代码。

<table border="1" cellpadding="0" align="center">

<tbody>

<tr>

<th>姓名</th>

<th>性别</th>

<th>java</th>

<th>oracle</th>

<th>mssql</th>

</tr>

如今,系统又接到了新的指示<xsl:for-each select="student" >。这条指示要求系统寻找标记为"student"的子节点,对于每个"student"子树中的内容,系统为其生成表中一行的内容。每一行包含四列,分别把标记为"name","sex","java","oracle","mssql"的节点的内容填进去。对应到本例中的XML数据,生成的HTML代码:

<tr>

<td>李明</td>

<td></td>

< td >97</ td > < td >69</ td >< td >76</ td >

</tr>

<tr>

<td>张一强</td>

<td></td>

< td >92</ td > < td >83</ td >< td >62</ td >

</tr>

<tr>

<td>王亮</td>

<td></td>

< td >74</ td > < td >91</ td >< td >71</ td >

</tr>

<tr>

<td>赵三金</td>

<td></td>

< td >89</ td > < td >93</ td >< td >67</ td >

</tr>

处理完<xsl:for-each select="student" >中的内容,系统继续生成HTML代码:

</tbody>

</table>

至此,系统已经处理完<xsl:template match="students">中的全部内容,能够"函数返回"了。系统返回到<xsl:template match="/">括起的源码中,完成HTML最后两行代码的生成:

</body>

</html>

把上面的HTML代码串起来,就是生成的转换结果文件。

分析上面的例子,咱们能够看出一个XSL文件基本上由几部分组成:

由于XSL文档自己是XML文档,所以文档的第一句就是XML声明:

<?xml version="1.0" encoding="UTF-8"?>

接下来就是XSL声明:

<xsl:stylesheet version="1.0"

xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

xmlns:fo="http://www.w3.org/1999/XSL/Format">

<!--模板规则-->

<! -输出模板--

</xsl:stylesheet>

XSL声明中包含模板,模板中定义XML的输出格式。

XSL文档自己是结构完整的XML文档,因此在书写时要注意标签的匹配问题。</xsl:stylesheet>既是XSL的声明语句,也是根元素,必须位于文件的首部,一般也要利用xmlns属性指明XSL的命名空间。样式表中全部的模板都由标签<xsl:template>标明,模板能够说明处理的对象(元素,属性),处理的方式或是转换的结果。此时,咱们能够把该标签相似地理解为编程语言中函数的概念。

XSL的基本语法

经过对上面XSL转换XML文档为HTML文档的简单例子的分析,相信你们对XSL的基本语法有了一个初步的认识,下面咱们再来详细分析一下XSL的元素语法。

XSL模板:

模板是XSL中最重要的概念之一,XSL文件就是由一个一个的模板组成的,任何一个XSL文件至少包含一个模板。能够把XSL的设计细化成一个个模板,最后再将这些模板组合成一个完整的XSL。这种方法如同程序中模块化设计逐步细化同样,能够使你先从总体上考虑整个XSL的设计,而后将一些表现形式细化成不一样的模板,再具体设计这些模块,最后将它们整合在一块儿。模板中两个主要的元素为:<xsl:apply-templates><xsl:template>, 这两个元素相结合,就如同编程中的函数调用:前者是调用指令,然后者就是函数体。

定义一个模板的基本语法是:

<xsl:template match="pattern">

<!-输出模板-->

</xsl:template>

属性match则控制模板的匹配模式,肯定在什么状况下执行此模板。属性match的取值把模板规则与指定的元素或属性相比较,只有指定的节点才会被处理。其中最上层的模板即根节点必须将match取值设定为"/"

<xsl:template match="/">

<!-输出根节点的模板-->

</xsl:template>

若是属性match的取值为"*",那么表示该规则适用于全部的未单独指定处理的元素节点。好比下例中的第二个模板就表示要处理除<mynode>元素以外的全部节点。

<xsl:template match="mynode">

<!-输出模板-->

</xsl:template>

<xsl:template match="*">

<!-输出模板-->

</xsl:template>

此外,XSL中还能够使用路径指示符来指定一些特殊位置的元素与模板相匹配。"//"表明任意深度位置,如<xsl:template match="//node">用来匹配文档中任何位置的<node>元素;而若是是<xsl:template match="my//node">,则代表是匹配<my>元素的后继节点中全部<node>元素。另一个路径指示符是"/",表示直接的父子节点关系。将刚才例子中的"//"换为"/",就意味着匹配的是<my>元素子节点中的<node>元素。XSL在输出模板中描述输出格式,这些格式能够是各类字符串,标签符号,节点值或者是一些XSL语法结构,如条件判断,循环处理等。模板定义完毕后,就能够在XSL中调用该模板,调用一个模板的语法为:

<xsl:apply-templates select="node"/>

<xsl:apply-templates>用来讲明哪个节点被模板具体处理。你能够将它理解为程序中调用子函数。Select属性用来定义确切的节点名称。<xsl:apply-templates>老是包含在<xsl:template>元素中,像这样:

<xsl:template match="/">

<xsl:apply-templates select="students" />

</xsl:template>

这段代码说明模板匹配整个文档(根节点),具体执行时处理根节点下全部students元素。对于不一样的元素须要调用不一样的模板进行处理。为了激活样式表中的模板规则,要在根元素模板规则中使用<xsl:apply-templates>元素,这样就会层层做用使整个样式表文件生效。直接使用<xsl:apply-templates>表示不加区分地对当前节点的全部子节点应用模板,而在select属性中书写匹配式则可以限定做用对象:

<?xml version="1.0" encoding="UTF-8"?>

<xsl:stylesheet version="1.0"

xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

xmlns:fo="http://www.w3.org/1999/XSL/Format">

<!--根模板-->

<xsl:template match="/">

<xsl:apply-templates select="//student" />

</xsl:template>

<!--student模板-->

<xsl:template match="student">

<xsl:apply-templates select="name" />

<xsl:apply-templates select="java" />

</xsl:template>

<!--name模板-->

<xsl:template match="student">

<xsl:value-of />

</xsl:template>

<!--java模板-->

<xsl:template match="java">

<xsl:value-of />

</xsl:template>

</xsl:stylesheet>

上述第一个模板与XML文档中的根元素相匹配,并对根节点如下的全部<student>元素应用模板规则。而后,一旦遇到<student>标签,就对<name>元素和<java>元素分别应用模板规则。最后,通过转换显示的结果是学生姓名和java成绩。

节点选择语句<xsl:value-of >

在许多应用场合中,输出模板中须要使用节点的取值,此时能够根据须要使用<xsl:value-of select="pattern" >语句输出节点值,最直接的使用方式是使用空元素<xsl:value-of />,这样能够输出当前节点及其后继节点的取值。而若是仅仅是想输出指定节点的取值,能够利用select属性进行限定(select属性能够是任意合法的路径表达式)。

<xsl:value-of select="name" >

<xsl:value-of select="//student" >

上述第一个表达式匹配的对象是当前节点的全部子节点中名称为<name>的元素,第二个表达式匹配的对象则是当前节点中全部后继节点中名为<student>的元素。注意:在XSL样式表中必须有一个模板规则与根元素相匹配。例如想在输出文档中显示学生成绩单XML源文档中的name元素的值,能够这样写XSL代码:

<xsl:template match="student">

<xsl:value-of select="name" />

</xsl:template>

其中match="student"定义模板匹配student节点,xsl:value-of语法说明须要输出一个节点的值,而select="name"则定义须要被输出的元素为name。这个过程相似于在数据库里查询一我的的名字。

循环判断语句<xsl:for-each>

XML文档中,若是存在两条以上的数据,必须使用<xsl:for-each >元素指定上层的节点元素,再用<xsl:value-of >设定要输出的内容,才能显示整个XML文档数据。这种循环结构可以遍历整个结果集合,而没必要针对每一条结果都单独书写转换规则。它的标准语法格式为:

<xsl:for-each select="student" >

….

</xsl:for-each>

条件判断语句<xsl:if>

<xsl:if>语句是简单地对条件进行判断,结果为真就执行条件内部的规则,所以能够把if条件与简单的布尔表达式联合使用。

<xsl:if>有三种不一样的用法,若是要用元素的名称做为匹配条件,语法是:

<xsl:if test="元素名称" >

下面的例子对"name"元素的内容用红色显示:

<?xml version="1.0" encoding="UTF-8"?>

<xsl:stylesheet version="1.0"

xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

xmlns:fo="http://www.w3.org/1999/XSL/Format">

<xsl:template match="/">

<html>

<head>

<title>第一期东北JAVA班部分学员</title>

</head>

<body>

<h2 align="center">成绩单</h2>

<xsl:apply-templates select="students" />

</body>

</html>

</xsl:template>

<xsl:template match="students">

<table border="1" cellpadding="0" align="center">

<tbody>

<tr>

<th>姓名</th>

<th>性别</th>

<th>java</th>

<th>oracle</th>

<th>mssql</th>

</tr>

<xsl:for-each select="student">

<tr>

<td>

<xsl:if test="name">

<font color="red">

<xsl:value-of select="name" />

</font>

</xsl:if>

</td>

<td>

<xsl:value-of select="sex" />

</td>

<td>

<xsl:value-of select="java" />

</td>

<td>

<xsl:value-of select="oracle" />

</td>

<td>

<xsl:value-of select="mssql" />

</td>

</tr>

</xsl:for-each>

</tbody>

</table>

</xsl:template>

</xsl:stylesheet>

显示的XML文件为:

若是用元素内容做为匹配条件,语法是:

<xsl:if test="元素名称[.='元素内容']" >

注意:"."表示当前元素。请看下例,若是名字是叫李明的 显示红颜色。

<xsl:if test="name[.='李明']">

<font color="red">

<xsl:value-of select="name" />

</font>

</xsl:if>

内容总结

? 会写XML文档

? 理解XML命名空间概念

? 掌握DTDSCHEMA的用法

? 会经过使用XSL进行XML的显示

? 知道XML的做用

? 正确使用XML技术

独立实践

? 写一个关于学生课程信息的DTDSHEMA文档

? 根据上面的DTDSCHEMA文档写出XML文档,并定义XSL显示该XML

? 写一个数据库链接的DTDSHEMA文档

? 根据数据库链接的DTDSHEMA文档,写出XML文档,为下一章解析XML,建立数据库链接作准备

? 如下XML 文件不是一個有效的文件,為什麼?

<?xml version = “1.0”?>

<!—A DTD example -->

<!DOCTYPE library [

<!ELEMENT library (branch+, book+)>

<!ELEMENT branch (name, address)>

<!ATTLIST branch branchID ID #REQUIRED>

<!ELEMENT book (#PCDATA)>

<!ATTLIST book inBranch IDREF>

<!ELEMENT name (#PCDATA)>

<!ELEMENT address ( #PCDATA )>

]>

<library>

<branch branchID = “b1”>

<name>理工图书馆</name>

<address>理工大道一号</address>

</branch>

<branch branchID = “b2”>

<name>文法商图书馆</name>

<address>文管大道一号</address>

</branch>

<branch>

<name>总图书馆</name>

</branch>

<book inBranch = “b2”>

如何当个称职的职场新鲜人

</book>

<book inBranch = “b3”>

如何管理資料库

</book>

<book inBranch = “b1”>

JAVA 十天速成手冊

</book>

</library>

修改上面的代码并写出相应的Schema

第二十二章:使用Java解析XML

学习目标

? 解析器的介绍

? 文档对象模型(DOM)解析实例

? SAX解析实例

? DOM4J解析实例

? JDOM解析实例

? JAVA操纵XML 实例讲解

解析器的介绍

XML解析器是读取XML文档并分析其结构的代码。这一部分将介绍XML解析器是如何工做的。将讨论不一样类型的XML解析器及什么时候使用它。

通常而言使用解析器须要如下步骤:

? 建立一个解析器对象

? 使解析器指向您的XML文档

? 处理结果

显然第三步最为复杂。一旦知道了XML文档的内容,比方说,您可能但愿生成一个Web页面,建立一个订单或者作一个饼图。这里讨论的常见XML解析工具使这项工做大大简化。

解析器有不一样的分类方法:

? 验证和非验证解析器

? 支持一种或多种XML Schema语言的解析器

? 支持Document Object Model (DOM)的解析器

? 支持Simple API for XML (SAX)的解析器

咱们已知有三种不一样类型的XML文档:

? 结构良好的文档:这类文档符合XML基本规则(属性必须放在引号中,标签必须正确嵌套等)。

? 有效文档:这些结构良好的文档同时还符合文档类型定义(DTD)或XML Schema所定义的规则。

? 无效文档:除以上两种文档外的全部其余文档。

若是您有一个XML文档符合XML的基本规则,那么它就是一个结构良好的文档。若是该文档还知足您的公司所定义的支出账目文档规则,那么它也是有效的。若是XML解析器发现XML文档不是结构良好的,XML Specification要求解析器报告一个致命错误。

验证解析器:在解析时验证XML文档,而非验证解析器:不验证文档。换句话说,若是一个XML文档是结构良好的,那么非验证解析器并不关心文档是否符合DTD或模式中定义的规则,甚至不关心该文档是否符合DTD或模式中定义的规则,甚至不关心该文档是否有这样的规则。(多数验证解析器都默认关闭验证功能。)

那么为何要使用非验证解析器呢?有两个很好的理由:速度和效率。XML解析器读取DTD或者模式,创建规则引擎保证XML文档中的每一个元素和属性都遵循这些规则,须要作大量的工做。若是您确信一个XML文档是有效的,那么就能够彻底跳过验证。根据文档规则复杂程度的不一样,这样能够节约至关可观的时间和内存。若是您的代码不够健壮,它从XML文档中获得输入数据,而且该代码要求文档遵循特定的DTD或者模式,那么您可能就不得不验证全部的内容,不论代价多么高,多么浪费时间。

DOMW3C推荐的正式标准。它定义了一个接口,程序可以访问和更新XML文档的结构。若是一个XML解析器声称支持DOM,就意味着它实现了该标准中定义的接口。目前,有三个级别的DOM是正式的推荐标准,被命名为DOM Level 1,DOM Level 2 DOM Level 3。本章中所讨论的DOM功能都是DOM Level 2的一部分。

DOM解析器:当你使用DOM解析器解析一个XML文档时,您获得一棵结构树,它表示XML文档的内容。全部的文本,元素和属性,都在这个树结构中

解析的过程:

解析后的XML文档。 DOM还提供各类不一样的功能,可用于分析和操做树的内容和结构。DOM是处理XML数据的传统方法。使用DOM时,数据以树状结构的形式被加载到内存中,因此,DOM解析是一个比较耗费内存的操做。如上图所示,矩形框表示元素节点,椭圆表示文本节点。DOM使用父子关系。例如,在这个例子中,student是具备五个孩子的根元素:三个文本节点(空白),以及两个元素节点nameage。要认识到的一件重要事情是,name age节点实际上具备null值。相反,它们具备文本节点(goudan 28)做为孩子。

DOM以及广义的基于树的处理具备几个优势

首先,因为树在内存中是持久的,所以能够修改它以便应用程序能对数据和结构做出更改。它还能够在任什么时候候在树中上下导航,而不是像SAX那样是一次性的处理。DOM使用起来也要简单得多。

另外一方面,在内存中构造这样的树涉及大量的开销。大型文件彻底占用系统内存容量的状况并不鲜见。此外,建立一棵DOM树多是一个缓慢的过程。为了知足该缺点,咱们使用SAXSimple API for XMLAPI处理XML文档内容。它的设计目标是占用更少的内存,把更多的工做交给程序员。SAXDOM是互补的,有各自的适用环境。请看下图解析过程:

当使用SAX解析器解析一个XML文档时,解析器在读取文档的过程当中会生成一系列的事件。至于如何处理这些事件则取决于您的需求。下面列出了一小部分您在XML文档时可能遇到的事件:

? startDocument事件

? 对于每一个元素,在元素开始时有startElement事件,元素结束时有endElement事件

? 若是元素包含内容,对于文本将出现characters事件,对于子元素将出现startElementendElement事件,依此类推

? endDocument事件

以下代码及过程:

<?xml version="1.0" encoding="UTF-8"?>

<student>

<name>goudan</name>

<age>28</age>

</student>

分析这个代码片断的SAX处理器,通常状况下将产生如下事件:

startDocument

startElement (person)

characters (white space)

startElement (name)

characters (goudan)

endElement (name)

characters (white space)

startElement (age)

characters (28)

endElement (age)

characters (white space)

endElement (person)

endDocument

这种处理的优势很是相似于流媒体的优势。分析可以当即开始,而不是等待全部的数据被处理。并且,因为应用程序只是在读取数据时检查数据,所以不须要将数据存储在内存中。这对于大型文档来讲是个巨大的优势。事实上,应用程序甚至没必要解析整个文档;它能够在某个条件获得知足时中止解析。通常来讲,SAX还比它的替代者DOM快许多。

另外一方面,因为应用程序没有以任何方式存储数据,使用SAX来更改数据或在数据流中日后移是不可能的。尽管SAXDOM提供了许多有用的功能,对于程序员而言有些任务仍是太复杂了。延续开源社群有须要就建立工具的历史传统,Java技术专家Jason Hunter Brett McLaughlin缔造了JDOM,这个Java库极大简化了XML文档的处理。和DOM相似,JDOM也提供一个对象树表示XML文档,可是这些对象工做的方式对Java程序员更直观。要记住,JDOM在背后包含使用普通SAXDOM 解析器的适配器; JDOM对全部主要的(和几个次要的)Java XML解析器都提供了适配器,所以没必要担忧您的Java XML解析器是否支持JDOMJDOM在幕后使用的解析器不须要您的干涉。

通常来讲如下状况应使用DOM解析器:

? 须要详细了解文档的结构

? 须要改变文档的结构(也许您须要对元素排序,增长新的元素等等)

? 须要屡次引用解析的信息

进一步推广,在如下状况中应使用SAX解析器:

? 内存少

? 只须要XML文档中少许元素或属性

? 解析的信息只使用一次

JDOM的内存使用比DOM少,可是不如SAX好。此外,若是您但愿进行验证,JDOM要求您设置底层的解析器,JDOM自己不进行验证。就是说,若是JDOM可以完成您所须要的全部功能,并且速度知足您的须要,它能够简化您的编码工做。

文档对象模型(DOM)解析实例

DOM主要包含下列内容:

DOM文档是以层次结构组织的节点或信息片断的集合。这个层次结构容许开发人员从树中导航寻找特定信息。分析该结构一般须要加载整个文档和构造层次结构,而后才能作任何工做。因为它是基于信息层次的,于是DOM被认为是基于树或基于对象的。

DOM解析过程(下图):

利用JAXPxml文档的内容解析到一个个的Java对象中,只需几行代码就能作到这一点。首先,咱们须要创建一个解析器工厂,以利用这个工厂来得到一个具体的解析器对象:

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

咱们在这里使用DocumentBuilderFacotry的目的是为了建立与具体解析器无关的程序,当DocumentBuilderFactory类的静态方法newInstance()被调用时,它根据一个系统变量来决定具体使用哪个解析器。又由于全部的解析器都服从于JAXP所定义的接口,因此不管具体使用哪个解析器,代码都是同样的。因此当在不一样的解析器之间进行切换时,只须要更改系统变量的值,而不用更改任何代码。这就是工厂所带来的好处。这个工厂模式的具体实现,能够参看下面的类图

DocumentBuilder db = dbf.newDocumentBuilder();

当得到一个工厂对象后,使用它的静态方法newDocumentBuilder()方法能够得到一个DocumentBuilder对象,这个对象表明了具体的DOM解析器。但具体是哪种解析器,微软的或者IBM的,对于程序而言并不重要。 而后,咱们就能够利用这个解析器来对XML文档进行解析了:

Document doc = db.parse("d:/xml/message.xml");

DocumentBuilderparse()方法接受一个XML文档名做为输入参数,返回一个Document对象,这个Document对象就表明了一个XML文档的树模型。之后全部的对XML文档的操做,都与解析器无关,直接在这个Document对象上进行操做就能够了。而具体对Document操做的方法,就是由DOM所定义。

使用Document对象的getElementsByTagName()方法,咱们能够获得一个NodeList对象,一个Node对象表明了一个XML文档中的一个标签元素,而NodeList对象,所表明的是一个Node对象的列表:

NodeList nl = doc.getElementsByTagName("message");

咱们经过这样一条语句所获得的是XML文档中全部<message>标签对应的Node对象的一个列表。而后,咱们能够使用NodeList对象的item()方法来获得列表中的每个Node对象:

Node my_node = nl.item(0);

当一个Node对象被创建以后,保存在XML文档中的数据就被提取出来并封装在这个Node中了。在这个例子中,要提取Message标签内的内容,咱们一般会使用Node对象的getNodeValue()方法:

String message = my_node.getFirstChild().getNodeValue();

请注意,这里还使用了一个getFirstChild()方法来得到message下面的第一个子Node对象。虽然在message标签下面除了文本外并无其它子标签或者属性,可是咱们坚持在这里使用getFirseChild()方法,这主要和W3CDOM的定义有关。W3C把标签内的文本部分也定义成一个Node,因此先要获得表明文本的那个Node,咱们才可以使用getNodeValue()来获取文本的内容。

DOM对象

DOM的基本对象有5个:DocumentNodeNodeListElementAttr。下图表示了基本对象间的关系

Document对象表明了整个XML的文档,全部其它的Node,都以必定的顺序包含在Document对象以内,排列成一个树形的结构,程序员能够经过遍历这颗树来获得XML文档的全部的内容,这也是对XML文档操做的起点。咱们老是先经过解析XML源文件而获得一个Document对象,而后再来执行后续的操做。此外,Document还包含了建立其它节点的方法,好比createAttribute()用来建立一个Attr对象。它所包含的主要的方法有: createAttribute(String):用给定的属性名建立一个Attr对象,并可在其后使用setAttributeNode方法来放置在某一个Element对象上面。createElement(String):用给定的标签名建立一个Element对象,表明XML文档中的一个标签,而后就能够在这个Element对象上添加属性或进行其它的操做。

createTextNode(String):用给定的字符串建立一个Text对象,Text对象表明了标签或者属性中所包含的纯文本字符串。若是在一个标签内没有其它的标签,那么标签内的文本所表明的Text对象是这个Element对象的惟一子对象。

getElementsByTagName(String):返回一个NodeList对象,它包含了全部给定标签名字的标签。

getDocumentElement():返回一个表明这个DOM树的根节点的Element对象,也就是表明XML文档根元素的那个对象。

Node对象是DOM结构中最为基本的对象,表明了文档树中的一个抽象的节点。在实际使用的时候,不多会真正的用到Node这个对象,而是用到诸如ElementAttrTextNode对象的子对象来操做文档。Node对象为这些对象提供了一个抽象的、公共的根。虽然在Node对象中定义了对其子节点进行存取的方法,可是有一些Node子对象,好比Text对象,它并不存在子节点。Node对象所包含的主要的方法有:

appendChild(org.w3c.dom.Node):为这个节点添加一个子节点,并放在全部子节点的最后,若是这个子节点已经存在,则先把它删掉再添加进去。

getFirstChild():若是节点存在子节点,则返回第一个子节点,对等的,还有getLastChild()方法返回最后一个子节点。

getNextSibling():返回在DOM树中这个节点的下一个兄弟节点,对等的,还有getPreviousSibling()方法返回其前一个兄弟节点。

getNodeName():根据节点的类型返回节点的名称。

getNodeType():返回节点的类型。

getNodeValue():返回节点的值。

hasChildNodes():判断是否是存在有子节点。

hasAttributes():判断这个节点是否存在有属性。

getOwnerDocument():返回节点所处的Document对象。

insertBefore(org.w3c.dom.Node neworg.w3c.dom.Node ref):在给定的一个子对象前再插入一个子对象。

removeChild(org.w3c.dom.Node):删除给定的子节点对象。

replaceChild(org.w3c.dom.Node neworg.w3c.dom.Nodeold):用一个新的Node对象代替给定的子节点对象。

NodeList对象,表明了一个包含一个或者多个Node的列表。能够简单的把它当作一个Node的数组,咱们能够经过方法来得到列表中的元素:

getLength():返回列表的长度。

item(int):返回指定位置的Node对象。

Element对象表明的是XML文档中的标签元素,继承于Node,亦是Node的最主要的子对象。在标签中能够包含有属性,于是Element对象中有存取其属性的方法,而任何Node中定义的方法,也能够用在Element对象上面。

getElementsByTagName(String):返回一个NodeList对象,它包含了在这个标签中其下的子孙节点中具备给定标签名字的标签。

getTagName():返回一个表明这个标签名字的字符串。getAttribute(String):返回标签中给定属性名称的属性的值。在这儿须要主要的是,应为XML文档中容许有实体属性出现,而这个方法对这些实体属性并不适用。这时候须要用到getAttributeNodes()方法来获得一个Attr对象来进行进一步的操做。

getAttributeNode(String):返回一个表明给定属性名称的Attr对象。Attr对象表明了某个标签中的属性。Attr继承于Node,可是由于Attr其实是包含在Element中的,它并不能被看做是Element的子对象,于是在DOMAttr并非DOM树的一部分,因此Node中的getparentNode()getpreviousSibling()getnextSibling()返回的都将是null。也就是说,Attr实际上是被看做包含它的Element对象的一部分,它并不做为DOM树中单独的一个节点出现。这一点在使用的时候要同其它的Node子对象相区别。

请看下面例子:(message.xml

<?xml version="1.0" standalone="yes"?>

<links>

<link>

<text>sohu</text>

<url newWindow="no">http://www.sohu.com</url>

<author>zhangzhaoyang</author>

<date>

<day>2</day>

<month>1</month>

<year>2004</year>

</date>

<description>zhangzhaoyang's website</description>

</link>

<link>

<text>Java</text>

<url newWindow="no">http://java.sun.com</url>

<author>Sun Microsystems</author>

<date>

<day>3</day>

<month>1</month>

<year>2001</year>

</date>

<description>Sun Microsystem's website.</description>

</link>

<link>

<text>microsoft</text>

<url newWindow="no">http://www.microsoft.com</url>

<author>bill gates</author>

<date>

<day>4</day>

<month>1</month>

<year>2000</year>

</date>

<description>bill's website</description>

</link>

</links>

DOM解析的例子:

下面代码使用DOM解析XML文件

package com.app;

import javax.xml.parsers.DocumentBuilder;

import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;

import org.w3c.dom.Element;

import org.w3c.dom.NodeList;

public class DOMXMLReader {

public static void main(String args[]) {

try {

DocumentBuilderFactory factory = DocumentBuilderFactory

.newInstance();

DocumentBuilder builder = factory.newDocumentBuilder();

Document doc = builder.parse("url.xml");

doc.normalize();

NodeList links = doc.getElementsByTagName("link");

for (int i = 0; i < links.getLength(); i++) {

Element link = (Element) links.item(i);

System.out.print("Content: ");

System.out.println(link.getElementsByTagName("text").item(0)

.getFirstChild().getNodeValue());

System.out.print("URL: ");

System.out.println(link.getElementsByTagName("url").item(0)

.getFirstChild().getNodeValue());

System.out.print("Author: ");

System.out.println(link.getElementsByTagName("author").item(0)

.getFirstChild().getNodeValue());

System.out.print("Date: ");

Element linkdate = (Element) link.getElementsByTagName("date")

.item(0);

String day = linkdate.getElementsByTagName("day").item(0)

.getFirstChild().getNodeValue();

String month = linkdate.getElementsByTagName("month").item(0)

.getFirstChild().getNodeValue();

String year = linkdate.getElementsByTagName("year").item(0)

.getFirstChild().getNodeValue();

System.out.println(day + "-" + month + "-" + year);

System.out.print("Description: ");

System.out.println(link.getElementsByTagName("description")

.item(0).getFirstChild().getNodeValue());

System.out.println();

}

} catch (Exception e) {

e.printStackTrace();

}

}

}

下面代码修改XML文件:

package com.app;

import javax.xml.parsers.DocumentBuilder;

import javax.xml.parsers.DocumentBuilderFactory;

import javax.xml.transform.Transformer;

import javax.xml.transform.TransformerFactory;

import javax.xml.transform.dom.DOMSource;

import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;

import org.w3c.dom.Element;

import org.w3c.dom.Text;

public class DOMXMLWriter {

public static void main(String args[]) {

try {

DocumentBuilderFactory factory = DocumentBuilderFactory

.newInstance();

DocumentBuilder builder = factory.newDocumentBuilder();

Document doc = builder.parse("url.xml");

doc.normalize();

// ---取得变量----

String text = "itjob";

String url = "www.5itjob.com";

String author = "xsw";

String discription = "a good website for it job!";

// -------------

Text textseg;

Element link = doc.createElement("link");

Element linktext = doc.createElement("text");

textseg = doc.createTextNode(text);

linktext.appendChild(textseg);

link.appendChild(linktext);

Element linkurl = doc.createElement("url");

textseg = doc.createTextNode(url);

linkurl.appendChild(textseg);

link.appendChild(linkurl);

Element linkauthor = doc.createElement("author");

textseg = doc.createTextNode(author);

linkauthor.appendChild(textseg);

link.appendChild(linkauthor);

java.util.Calendar rightNow = java.util.Calendar.getInstance();

String day = Integer.toString(rightNow

.get(java.util.Calendar.DAY_OF_MONTH));

String month = Integer.toString(rightNow

.get(java.util.Calendar.MONTH));

String year = Integer.toString(rightNow

.get(java.util.Calendar.YEAR));

Element linkdate = doc.createElement("date");

Element linkdateday = doc.createElement("day");

textseg = doc.createTextNode(day);

linkdateday.appendChild(textseg);

Element linkdatemonth = doc.createElement("month");

textseg = doc.createTextNode(month);

linkdatemonth.appendChild(textseg);

Element linkdateyear = doc.createElement("year");

textseg = doc.createTextNode(year);

linkdateyear.appendChild(textseg);

linkdate.appendChild(linkdateday);

linkdate.appendChild(linkdatemonth);

linkdate.appendChild(linkdateyear);

link.appendChild(linkdate);

Element linkdiscription = doc.createElement("description");

textseg = doc.createTextNode(discription);

linkdiscription.appendChild(textseg);

link.appendChild(linkdiscription);

doc.getDocumentElement().appendChild(link);

TransformerFactory tFactory = TransformerFactory.newInstance();

Transformer transformer = tFactory.newTransformer();

DOMSource source = new DOMSource(doc);

StreamResult result = new StreamResult(new java.io.File("url.xml"));

transformer.transform(source, result);

System.out.println("write complete!");

} catch (Exception e) {

e.printStackTrace();

}

}

}

SAX解析实例

//如下代码统计url.xml文件中每一个标签出现的次数

package com.app;

import java.io.File;

import java.util.Enumeration;

import java.util.Hashtable;

import javax.xml.parsers.SAXParser;

import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;

import org.xml.sax.SAXException;

import org.xml.sax.helpers.DefaultHandler;

public class TagCounter extends DefaultHandler {

// Hashtable用来记录tag出现的次数

private Hashtable tags;

public void startDocument() throws SAXException {

tags = new Hashtable();

}

// 解析完成后的工做

public void endDocument() throws SAXException {

Enumeration e = tags.keys();

while (e.hasMoreElements()) {

String tag = (String) e.nextElement();

int count = ((Integer) tags.get(tag)).intValue();

System.out.println("Tag <" + tag + "> occurs " + count + " times");

}

}

// 对每个开始元属进行处理

public void startElement(String namespaceURI, String localName,

String rawName, Attributes atts) throws SAXException {

String key = rawName;

Object value = tags.get(key);

if (value == null) {

// 若是是新标签,把它添加在Hastable

tags.put(key, new Integer(1));

} else {

// 若是之前碰到过,获得其计数值,并加1

int count = ((Integer) value).intValue();

count++;

tags.put(key, new Integer(count));

}

}

static public void main(String[] args) {

String filename = null;

filename = "url.xml";

SAXParserFactory spf = SAXParserFactory.newInstance();

SAXParser saxParser = null;

try {

// 建立解析器SAXParser对象

saxParser = spf.newSAXParser();

saxParser.parse(new File(filename), new TagCounter());

} catch (Exception ex) {

ex.printStackTrace();

}

}

}

//如下程序解析url.xml

package com.app;

import java.io.File;

import java.io.IOException;

import java.util.Stack;

import javax.xml.parsers.SAXParser;

import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;

import org.xml.sax.SAXException;

import org.xml.sax.helpers.DefaultHandler;

public class SAXXMLReader extends DefaultHandler {

Stack tags = new Stack();

// 用于保存解析出来的信息

String text = null;

String url = null;

String author = null;

String description = null;

String day = null;

String year = null;

String month = null;

public void endDocument() throws SAXException {

System.out.println("------解析结束--------");

}

public void startDocument() throws SAXException {

System.out.println("------解析开始--------");

}

public void startElement(String p0, String p1, String p2, Attributes p3)

throws SAXException {

tags.push(p2);

}

public void endElement(String p0, String p1, String p2) throws SAXException {

tags.pop();

if (p2.equals("link"))

parser();

}

public void characters(char[] p0, int p1, int p2) throws SAXException {

// 察看栈顶元素,根据元素名称给对应的变量赋值

String tag = (String) tags.peek();

if (tag.equals("text"))

text = new String(p0, p1, p2);

else if (tag.equals("url"))

url = new String(p0, p1, p2);

else if (tag.equals("author"))

author = new String(p0, p1, p2);

else if (tag.equals("day"))

day = new String(p0, p1, p2);

else if (tag.equals("month"))

month = new String(p0, p1, p2);

else if (tag.equals("year"))

year = new String(p0, p1, p2);

else if (tag.equals("description"))

description = new String(p0, p1, p2);

}

private void parser() {

System.out.print("Content: ");

System.out.println(text);

System.out.print("URL: ");

System.out.println(url);

System.out.print("Author: ");

System.out.println(author);

System.out.print("Date: ");

System.out.println(day + "-" + month + "-" + year);

System.out.print("Description: ");

System.out.println(description);

System.out.println();

}

static public void main(String[] args) {

String filename = "url.xml";

SAXParserFactory spf = SAXParserFactory.newInstance();

SAXParser saxParser = null;

try {

saxParser = spf.newSAXParser();

saxParser.parse(new File(filename), new SAXXMLReader());

} catch (Exception ex) {

ex.printStackTrace();

}

}

}

DOM4J解析实例

D:\\wuhan\\test.xml

<?xml version="1.0" encoding="GB2312"?>

<RESULT>

<VALUE><NO>A1234</NO><NO>A1234aaaaaaaaaa</NO><ADDR>深圳罗湖</ADDR>

</VALUE>

<VALUE>

   <NO>B1234</NO>

   <ADDR>深圳南山</ADDR>

</VALUE>

<VALUE>

   <NO>B1234xxxx</NO>

   <ADDR>深圳xxxxx</ADDR>

</VALUE>

</RESULT>

Java 代码

package com.xml;

import java.io.*;

import java.util.*;

import org.dom4j.*;

import org.dom4j.io.*;

public class Dom4j {

public static void main(String arge[]) {

long lasting = System.currentTimeMillis();

try {

File f = new File("D:\\wuhan\\test.xml");

SAXReader reader = new SAXReader();

Document doc = reader.read(f);

Element root = doc.getRootElement();

Element foo;

for (Iterator i = root.elementIterator("VALUE"); i.hasNext();) {

foo = (Element) i.next();

System.out.print("车牌号码:" + foo.elementText("NO"));

System.out.println("车主地址:" + foo.elementText("ADDR"));

}

} catch (Exception e) {

e.printStackTrace();

}

}

}

JDOM解析实例

package com.xml;

import java.io.*;

import java.util.*;

import org.jdom.Document;

import org.jdom.Element;

import org.jdom.input.SAXBuilder;

public class JDom {

public static void main(String arge[]) {

long lasting = System.currentTimeMillis();

try {

SAXBuilder builder = new SAXBuilder();

Document doc = builder.build(new File("D:\\wuhan\\test.xml"));

Element foo = doc.getRootElement();

List allChildren = foo.getChildren();

for(int i = 0;i < allChildren.size();i++) {

System.out.print("车牌号码:" + ((Element)allChildren.get(i)).getChild("NO").getText());

System.out.println("车主地址:" + ((Element)allChildren.get(i)).getChild("ADDR").getText());

}

} catch (Exception e) {

e.printStackTrace();

}

}}

JAVA操纵XML 实例讲解

JDBC开发中须要链接到不一样的数据库,利用XML文件保存不一样类型数据库的链接参数,并使用统一的程序解析XML以取得相应的链接参数。

<<Db.xml>>

<?xml version="1.0" encoding="UTF-8"?>

<DBS>

<DB>

<type>Oracle</type>

<driver>oracle.jdbc.driver.OracleDriver</driver>

<url>jdbc:oracle:thin:@localhost:1521:goudan</url>

<user>pubuser1</user>

<password>11111111</password>

</DB>

<DB>

<type>mysql</type>

<driver>org.git.mm.mysql.Driver</driver>

<url>jdbc:mysql://localhost:3306/BookDB</url>

<user>pubuser2</user>

<password>11111111</password>

</DB>

<DB>

<type>sqlserver</type> <driver>com.microsoft.jdbc.sqlserver.SQLServerDriver</driver>

<url>jdbc:Microsoft:sqlserver://127.0.0.1:1433</url>

<user>pubuser3</user>

<password>11111111</password>

</DB>

</DBS>

<<DBTest.java>>

package test.xml;

import java.io.IOException;

import javax.xml.parsers.DocumentBuilder;

import javax.xml.parsers.DocumentBuilderFactory;

import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;

import org.w3c.dom.Element;

import org.w3c.dom.Node;

import org.w3c.dom.NodeList;

import org.xml.sax.SAXException;

public class DBTest {

public DBTest(String type) {

// this.type = type ;

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

try {

DocumentBuilder db = dbf.newDocumentBuilder();// 建立解析器

Document doc = db.parse("Db.xml");// 解析地址

NodeList n1 = doc.getElementsByTagName("DB");// 获得含有DB标签的一个列表

for (int i = 0; i < n1.getLength(); i++) {

Element myNode = (Element) n1.item(i);// 经过item方法得到每一个对象

if (myNode.getElementsByTagName("type").item(0).getFirstChild()

.getNodeValue().equals(type)) {

String driver = myNode.getElementsByTagName("driver").item(

0).getFirstChild().getNodeValue();

String url = myNode.getElementsByTagName("url").item(0)

.getFirstChild().getNodeValue();

String user = myNode.getElementsByTagName("user").item(0)

.getFirstChild().getNodeValue();

String password = myNode.getElementsByTagName("password")

.item(0).getFirstChild().getNodeValue();

System.out.println(driver + " " + url + " " + user + " "

+ password);

break;

}

}

} catch (ParserConfigurationException e) {

e.printStackTrace();

} catch (SAXException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

}

public static void main(String[] rag) {

DBTest dbt1 = new DBTest("Oracle");

DBTest dbt2 = new DBTest("mysql");

DBTest dbt3 = new DBTest("sqlserver");

}

}

经过JAVA写数据到XML里面

<<persons.xml>>

<?xml version="1.0" encoding="UTF-8"?>

<persons>

<person>

<name>andy</name>

<age>45</age>

<tel>13632940025</tel>

<sex>m</sex>

</person>

</persons>

<<WriteXmlTest.xml>>

package test.xml;

import java.io.File;

import java.io.IOException;

import javax.xml.parsers.DocumentBuilder;

import javax.xml.parsers.DocumentBuilderFactory;

import javax.xml.parsers.ParserConfigurationException;

import javax.xml.transform.Transformer;

import javax.xml.transform.TransformerConfigurationException;

import javax.xml.transform.TransformerException;

import javax.xml.transform.TransformerFactory;

import javax.xml.transform.dom.DOMSource;

import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;

import org.w3c.dom.Element;

import org.w3c.dom.NodeList;

import org.w3c.dom.Text;

import org.xml.sax.SAXException;

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.ResultSet;

import java.sql.Statement;

import java.util.ArrayList;

public class WriteXmlTest {

public static void main(String[] args) {

try {

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

DocumentBuilder db = dbf.newDocumentBuilder();

Document dc = db.parse("persons.xml");

fang(dc);

} catch (ParserConfigurationException e) {

e.printStackTrace();

} catch (SAXException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

}

public static void fang(Document doc) {

Element eleroots = doc.getDocumentElement();

Element eleroot = doc.createElement("person");// 建立gen元素

eleroots.appendChild(eleroot);

Element elename = doc.createElement("name");

Text text1 = doc.createTextNode("andy");

elename.appendChild(text1);

eleroot.appendChild(elename);

Element eleage = doc.createElement("age");

Text text2 = doc.createTextNode("45");

eleage.appendChild(text2);

eleroot.appendChild(eleage);

Element eletel = doc.createElement("tel");

Text text3 = doc.createTextNode("13632940025");

eletel.appendChild(text3);

eleroot.appendChild(eletel);

Element elesex = doc.createElement("sex");

Text text4 = doc.createTextNode("m");

elesex.appendChild(text4);

eleroot.appendChild(elesex);

TransformerFactory tff = TransformerFactory.newInstance();

Transformer tf = null;

try {

tf = tff.newTransformer();

} catch (TransformerConfigurationException e) {

e.printStackTrace();

}

DOMSource ds = new DOMSource(doc);

StreamResult sr = new StreamResult(new File("persons.xml"));

try {

tf.transform(ds, sr);

} catch (TransformerException e) {

e.printStackTrace();

}

}

}

内容总结

? 知道解析器的做用

? 理解DOMSAX解析XML原理

? 了解DOMSAX解析的优缺点

? 使用DOMSAX解析XML文档

? 使用DOM4J解析XML文档

? 使用JDOM 解析XML文档

? 可以使用各类解析方式来处理应用程序

? 可以写出XML方式的链接池技术

独立实践

? 写一个XML文档:包括JDBC链接所须要的驱动,url,用户名,密码等

? 使用SAX或者DOM解析上面的XML创建数据库的链接

? 使用DOM4J解析一个判断用户的登录的操做,用户信息存在XML文档里

? 使用JDOM解析上一章的一个关于学生课程信息的XML文档,并在控制台显示数据

? 使用任何一种解析方式对学生课程信息的XML文档进行写一行增长课程信息的数据。

第二十三章:HTML基础

学习目标

? HTML文档是由HTML元素组成的文本文件,是预约义的正在使用的HTML标签

? HTML中最重要的标签是定义标题元素,段落和换行的标签

? 掌握格式化元素好比加粗和倾斜文本

? 了解HTML中的特殊实体

? 掌握超连接、锚的用法

? 掌握表格的画法以及如何添加表的标题、表头等

? 掌握无序、有序及自定义列表

? 了解并掌握图像标签、alt属性、src属性,以及如何利用图像建立超连接

? 掌握如何在<body>标签中设置网页背景

知识要点

HTML元素

HTML文档是由HTML元素组成的文本文件。

HTML元素是预约义的正在使用的HTML标签。

________________________________________HTML标签

HTML标签用来组成HTML元素。

HTML标签两端有两个包括字符:“<”“>”,这两个包括字符被称为角括号。

HTML标签一般成对出现,好比<b></b>。一对标签的前面一个是开始标签,第二个是结束标签,在开始和结束标签之间的文本是元素内容。

HTML标签是大小写无关的,<b><B>表示的意思是同样的。________________________________________

回忆一下上一页的HTML例子:

<html>

<head>

<title>Title of page</title>

</head>

<body>

This is my first homepage.

<b>This text is bold</b>

</body>

</html>

下面是一个HTML元素:

<b>This text is bold</b>

HTML元素以开始标签<b>起始, 内容是:This text is bold,以结束标签</b>停止。<b>标签的目的是定义一个须要被显示成粗体的HTML元素。

下面也是一个HTML元素:

<body>

This is my first homepage.

<b>This text is bold</b>

</body>

HTML标签以开始标签<body>起始,终止于结束标签</body><body>标签的目的是定义一个HTML元素,使其包含HTML文档的主体。________________________________________为何使用小写标签?

HTML标签是大小写无关的:<B><b>含义相同。当你上网的时候,你会注意到多数教程在示例中使用大写的HTML标签,咱们老是使用小写标签。为何?

假如你想投入到下一代HTML中,你应该开始使用小写标签。W3C在他们的HTML4建议中提倡使用小写标签,XHTML(下一代HTML)也须要小写标签。________________________________________

标签属性

标签能够拥有属性。属性可以为页面上的HTML元素提供附加信息。

标签<body>定义了HTML页面的主体元素。使用一个附加的bgcolor属性,你能够告诉浏览器:你页面的背景色是红色的,就像这样:

<body bgcolor="red">

标签<table>定义了一个HTML表格。使用一个附加的border属性,你能够告诉浏览器:这个表格是没有边框的,代码是:

<table border="0">

属性一般由属性名和值成对出现,就像这样:name="value"。属性一般是附加给HTML元素的开始标签的。

引号样式

属性值应该被包含在引号中。双引号是最经常使用的,可是单引号也能够使用。

在不多状况下,好比说属性值自己包含引号,使用单引号就很必要了。

好比:name='John "ShotGun" Nelson'

注意:中文引号跟英文引号是不同的。上面所指的引号都是英文状态下的引号。

HTML基本标签

HTML中最重要的标签是定义标题元素,段落和换行的标签。

学习HTML的最好方法是编辑运行示例代码。

请本身尝试一下这个例子

一个很是简单的HTML文档:

<html>

<body>

The content of the body element is displayed in your browser.

</body>

</html>

这个例子是一个很是简单的HTML文档,拥有不多的几个HTML标签。它说明了一个主体元素中的文本是如何在浏览器中显示的。

简单的段落:

<html>

<body>

<p>

This is a paragraph.

</p>

<p>

This is a paragraph.

</p>

<p>

This is a paragraph.

</p>

<p>

Paragraph elements are defined by the p tag.

</p>

</body>

</html>

这个例子说明了段落元素中的文本是如何在浏览器中显示的。

标题元素

标题元素由标签<h1><h6>定义。<h1>定义了最大的标题元素,<h6>定义了最小的。

<h1>

This is a heading

</h1>

<h2>

This is a heading

</h2>

<h3>

This is a heading

</h3>

<h4>

This is a heading

</h4>

<h5>

This is a heading

</h5>

<h6>

This is a heading

</h6>

HTML自动在一个标题元素先后各添加一个空行。

段落

段落是用<p>标签订义的。

<p>This is another paragraph</p>

HTML自动在一个段落先后各添加一个空行。

换行

当须要结束一行,而且不想开始新段落时,使用<br>标签。<br>标签无论放在什么位置,都可以强制换行。

<p>

This

<br>

is a para

<br>

graph with line breaks

</p>

<br>标签是一个空标签,它没有结束标记。

注释

注释标签用来在HTML源文件中插入注释。注释会被浏览器忽略。你能够使用注释来解释你的代码,这能够在你之后编辑代码的时候帮助你。

<!-- This is a comment -->

注意:你须要在左括号“<”后面跟一个感叹号,右括号不用。

技巧

当你写下HTML文本的时候,你不能确知在另一个浏览器中,这些文本将被如何显示。有人用着大的显示器,有的人用的小一些。每次用户调整窗口大小的时候,文本都将被从新格式化。不要想在编辑器中写一些空行和空格来协助排版。

HTML将截掉你文本中的多余空格。无论多少个空格,处理起来只当一个。一点附加信息:在HTML里面,一个空行也只被看成一个空格来处理。

使用空段落<p>来插入空白行是一个坏习惯,请使用<br>标签来替代。(可是不要用<br>标签来建立列表,咱们后面会专门学习HTML列表的。)

你也许注意到了段落能够不写结束标记</p>。别依赖它,HTML的下一个版本将不许你漏掉任何一个结束标签。

HTML自动在某些元素先后增长额外的空行,就像在段落和标题元素的先后同样。

咱们使用了水平线(<hr>标签)来分隔咱们教程的章节。

例如:

多个段落

<html>

<body>

<p>

This paragraph contains a lot of lines in the source code, but the

browser ignores it.

</p>

<p>

This paragraph contains a lot of spaces in the source code, but the

browser ignores it.

</p>

<p>

The number of lines in a paragraph depends on the size of your

browser window. If you resize the browser window, the number of lines

in this paragraph will change.

</p>

</body>

</html>

这个例子说明了段落的一些默认行为。

换行

<html>

<body>

<p>

To break

<br>

lines

<br>

in a

<br>

paragraph,

<br>

use the br tag.

</p>

</body>

</html>

这个例子说明了在HTML文档中换行的使用。

格式

<html>

<body>

<p>

My Bonnie lies over the ocean. My Bonnie lies over the sea. My Bonnie

lies over the ocean. Oh, bring back my Bonnie to me.

</p>

<p>

Note that your browser simply ignores your formatting!

</p>

</body>

</html>

这个例子说明了HTML显示格式的一些问题。

标题元素

<html>

<body>

<h1>

This is heading 1

</h1>

<h2>

This is heading 2

</h2>

<h3>

This is heading 3

</h3>

<h4>

This is heading 4

</h4>

<h5>

This is heading 5

</h5>

<h6>

This is heading 6

</h6>

<p>

Use heading tags only for headings. Don't use them just to make

something bold. Use other tags for that.

</p>

</body>

</html>

这个例子说明了在HTML中显示标题元素的标签。

居中的标题元素

<html>

<body>

<h1 align="center">

This is heading 1

</h1>

<p>

The heading above is aligned to the center of this page. The heading

above is aligned to the center of this page. The heading above is

aligned to the center of this page.

</p>

</body>

</html>

这个例子显示了一个居中的标题元素。

水平线

<html>

<body>

<p>

The hr tag defines a horizontal rule:

</p>

<hr>

<p>

This is a paragraph

</p>

<hr>

<p>

This is a paragraph

</p>

<hr>

<p>

This is a paragraph

</p>

</body>

</html>

这个例子说明了如何插入水平线。

隐藏的注释

<html>

<body>

<!--This comment will not be displayed-->

<p>

This is a regular paragraph

</p>

</body>

</html>

这个例子说明了在HTML文档中如何插入隐藏的注释。

背景色

<html>

<body bgcolor="yellow">

<h2>

Look: Colored Background!

</h2>

</body>

</html>

这个例子说明了如何给页面设置背景色。

HTML格式

HTML定义了不少元素用来格式化输出,好比加粗和倾斜文本。

例如:

格式化文字

<html>

<body>

<b>This text is bold</b>

<br>

<strong> This text is strong </strong>

<br>

<big> This text is big </big>

<br>

<em> This text is emphasized </em>

<br>

<i> This text is italic </i>

<br>

<small> This text is small </small>

<br>

This text contains

<sub> subscript </sub>

<br>

This text contains

<sup>

superscript

</sup>

</body>

</html>

这个例子说明了在HTML里面能够怎样格式化文本。

预格式化文本

<html>

<body>

<pre>

This is

preformatted text.

It preserves both spaces

and line breaks.

</pre>

<p>

The pre tag is good for displaying computer code:

</p>

<pre>

for i = 1 to 10

print i

next i

</pre>

</body>

</html>

这个例子说明了能够怎样用pre标签来控制换行和空格。

计算机输出标签

<html>

<body>

<code>

Computer code

</code>

<br>

<kbd>

Keyboard input

</kbd>

<br>

<tt>Teletype text</tt>

<br>

<samp>

Sample text

</samp>

<br>

<var>

Computer variable

</var>

<br>

<p>

<b>Note:</b> These tags are often used to display

computer/programming code.

</p>

</body>

</html>

这个例子说明了计算机输出标签在显示上的不一样。

地址

<html>

<body>

<address>

Donald Duck

<br>

BOX 555

<br>

Disneyland

<br>

USA

</address>

</body>

</html>

这个例子说明了如何用HTML书写一个地址。

缩写和首字母缩略法

<html>

<body>

<abbr title="United Nations">UN</abbr>

<br>

<acronym title="World Wide Web">WWW</acronym>

<p>

The title attribute is used to show the spelled-out version when

holding the mouse pointer over the acronym or abbreviation.

</p>

<p>

This only works for the acronym element in IE 5.

</p>

<p>

This works for both the abbr and acronym element in Netscape 6.2.

</p>

</body>

</html>

这个例子说明了如何处理缩写和首字母缩略。

文字方向

<html>

<body>

<p>

If your browser supports bi-directional override (bdo), the next line

will be written from the right to the left (rtl):

</p>

<bdo dir="rtl">

Here is some Hebrew text

</bdo>

</body>

</html>

这个例子说明了如何改变文字方向。

块引用

<html>

<body>

Here comes a long quotation:

<blockquote>

This is a long quotation. This is a long quotation. This is a long

quotation. This is a long quotation. This is a long quotation.

</blockquote>

Here comes a short quotation:

<q> This is a short quotation </q>

<p>

With the block quote element, the browser inserts line breaks and

margins, but the q element does not render as anything special.

</p>

</body>

</html>

这个例子说明了如何处理大段引用和小块引用。

删除和插入文字

<html>

<body>

<p>

a dozen is

<del>

twenty

</del>

<ins>

twelve

</ins>

pieces

</p>

<p>

Most browsers will overstrike deleted text and underline inserted

text.

</p>

<p>

Some older browsers will display deleted or inserted text as plain

text.

</p>

</body>

</html>

这个例子说明了如何标记被删除或者插入的文本。

查看HTML源文件

在浏览器菜单中选择查看——源文件,将弹出一个窗口,这个页面的HTML代码就在其中。

HTML实体

有些字符,好比说“<”字符,在HTML中有特殊的含义,所以不能在文本中使用。

想要在HTML中显示一个小于号“<”,须要用到字符实体。

字符实体

HTML中,有些字符拥有特殊含义,好比小于号“<”定义为一个HTML标签的开始。假如咱们想要浏览器显示这些字符的话,必须在HTML代码中插入字符实体。

一个字符实体拥有三个部分:一个and符号(&),一个实体名或者一个实体号,最后是一个分号(;

想要在HTML文档中显示一个小于号,咱们必须这样写:<或者<

使用名字相对于使用数字的优势是容易记忆,缺点是并不是全部的浏览器都支持最新的实体名,可是几乎全部的浏览器都能很好地支持实体号。

注意:实体名是大小写敏感的。

下面这个例子可以让你针对HTML实体实践一下。

<html>

<body>

<p>

This is a character entity: {

</p>

<p>

</body>

</html>

不可拆分的空格

HTML中,最多见的字符实体就是不可拆分空格。

一般,HTML会合并你文档中的空格。假如在你的HTML文本中连续写了10个空格,其中9个会被去掉。想要在HTML中插入空格,能够使用实体:

经常使用的字符实体

显示结果 描述 实体名 实体号

不可拆分的空格

< 小于 < <

> 大于 > >

& and符号 & &

" 引号 " "

' 单引号 '

其它字符实体

显示结果 描述 实体名 实体号

¢ 分 ¢ ¢

£ 英镑 £ £

¥ 人民币元 ¥ ¥

§ 章节 § §

? 版权 © ©

? 注册 ® ®

× 乘号 × ×

÷ 除号 ÷ ÷

HTML连接

HTML使用超级连接来链接到网络上的其余页面。

例如

建立连接

<html>

<body>

<p>

<a href="lastpage.htm"> This text</a> is a link to a page on this Web

site.

</p>

<p>

<a href="http://www.microsoft.com/"> This text</a> is a link to a

page on the World Wide Web.

</p>

</body>

</html>

这个例子说明了在HTML文档中如何建立连接

图片做为连接

<html>

<body>

<p>

You can also use an image as a link:

<a href="lastpage.htm"> <img border="0" src=".\images\next.gif">

</a>

</p>

</body>

</html>

这个例子说明了在HTML文档中如何用图片做为连接。

锚标签和href属性

HTML使用锚标签(<a>)来建立一个链接到其余文件的连接。锚能够指向网络上的任何资源:HTML页面,图像,声音,影片等等。

建立一个锚的语法:

<a href="url">Text to be displayed</a>

锚能够指向网络上的任何资源:HTML页面,图像,声音,影片等等。

标签<a>被用来建立一个连接指向的锚,href属性用来指定链接到的地址,在锚的起始标签<a>和结束标签</a>中间的部分将被显示为超级连接。

这个锚定义了一个到W3Schools的连接:

<a href="http://www.w3schools.com/">Visit W3Schools!</a>

target属性

使用target属性,你能够定义从什么地方打开连接地址。

下面这段代码打开一个新的浏览器窗口来打开连接:

<a href="http://www.w3schools.com/" target="_blank">Visit W3Schools!</a>

锚标签和name属性

name属性用来建立一个命名的锚。使用命名锚之后,可让连接直接跳转到一个页面的某一章节,而不用用户打开那一页,再从上到下慢慢找。

下面是命名锚的语法:

<a name="label">Text to be displayed</a>

你能够为锚随意指定名字,只要你愿意。下面这行代码定义了一个命名锚:<a name="tips">Useful Tips Section</a>

你应该注意到了:命名锚的显示方式并无什么不同凡响的。

想要直接连接到“tips”章节的话,在URL地址的后面加一个“#”和这个锚的名字,就像这样:

<a name="http://www.w3schools.com/html_links.asp#tips">Jump to the Useful Tips Section</a>

一个连接到本页面中某章节的命名锚是这样写的:<a name="#tips">Jump to the Useful Tips Section</a>

技巧

尽可能在子目录路径后面加一个左斜杠。假如你像下面这样写:href="http://www.w3schools.com/html",将会产生向服务器产生两个HTTP请求,由于服务器会在后面追加一个左斜杠,产生一个新的请求,就像这样:href="http://www.w3schools.com/html/"

命名锚一般用来在大型文档的开头建立章节表。这个页面的每一个章节被加上一个命名锚,到这些锚的连接被放在页面的顶端。

若是浏览器没法找到指定的命名锚,它将转到这个页面的顶部,而不显示任何错误提示。

例如:

在新浏览器窗口中打开连接

<html>

<body>

<a href="lastpage.htm" target="_blank">Last Page</a>

<p>

If you set the target attribute of a link to "_blank", the link will

open in a new window.

</p>

</body>

</html>

这个例子说明了怎样用打开新窗口的方式来连接到其余页面,这样,访问者不用离开你的页面。

连接到本页面的某个位置

<html>

<body>

<p>

<a href="#C4"> See also Chapter 4. </a>

</p>

<p>

<h2>

Chapter 1

</h2>

<p>

This chapter explains ba bla bla

</p>

<h2>

Chapter 2

</h2>

<p>

This chapter explains ba bla bla

</p>

<h2>

Chapter 3

</h2>

<p>

This chapter explains ba bla bla

</p>

<a name="C4"><h2>

Chapter 4

</h2>

</a>

<p>

This chapter explains ba bla bla

</p>

<h2>

Chapter 5

</h2>

<p>

This chapter explains ba bla bla

</p>

<h2>

Chapter 6

</h2>

<p>

This chapter explains ba bla bla

</p>

<h2>

Chapter 7

</h2>

<p>

This chapter explains ba bla bla

</p>

<h2>

Chapter 8

</h2>

<p>

This chapter explains ba bla bla

</p>

<h2>

Chapter 9

</h2>

<p>

This chapter explains ba bla bla

</p>

<h2>

Chapter 10

</h2>

<p>

This chapter explains ba bla bla

</p>

<h2>

Chapter 11

</h2>

<p>

This chapter explains ba bla bla

</p>

<h2>

Chapter 12

</h2>

<p>

This chapter explains ba bla bla

</p>

<h2>

Chapter 13

</h2>

<p>

This chapter explains ba bla bla

</p>

<h2>

Chapter 14

</h2>

<p>

This chapter explains ba bla bla

</p>

<h2>

Chapter 15

</h2>

<p>

This chapter explains ba bla bla

</p>

<h2>

Chapter 16

</h2>

<p>

This chapter explains ba bla bla

</p>

<h2>

Chapter 17

</h2>

<p>

This chapter explains ba bla bla

</p>

</body>

</html>

这个例子说明了如何跳转到一个文档的某部分。

跳出框架

<html>

<body>

<p>

Locked in a frame?

</p>

<a href="http://www.w3schools.com/" target="_top">Click here!</a>

</body>

</html>

这个例子说明了假如你的页面在框架中,如何跳出框架。

建立一个邮件连接

<html>

<body>

<p>

This is a mail link:

<a href="mailto:someone@microsoft.com?subject=Hello%20again">

Send Mail</a>

</p>

<p>

<b>Note:</b> Spaces between words should be replaced by %20 to

<b>ensure</b> that the browser will display your text properly.

</p>

</body>

</html>

这个例子说明了如何连接到一个邮件信息(只有安装了邮件程序才有效)。

建立一个邮件连接2

<html>

<body>

<p>

This is another mailto link:

<a

href="mailto:someone@microsoft.com?cc=someoneelse@microsoft.com&bcc=andsomeoneelse2@microsoft.com&subject=Summer%20Party&body=You%20are%20invited%20to%20a%20big%20summer%20party!">Send

mail!</a>

</p>

<p>

<b>Note:</b> Spaces between words should be replaced by %20 to

<b>ensure</b> that the browser will display your text properly.

</p>

</body>

</html>

这个例子显示了一个完成度更高的邮件连接。

HTML表格

使用HTML能够建立表格。

例如:

<html>

<body>

<p>

Each table starts with a table tag. Each table row starts with a tr

tag. Each table data starts with a td tag.

</p>

<h4>

One column:

</h4>

<table border="1">

<tr>

<td>

100

</td>

</tr>

</table>

<h4>

One row and three columns:

</h4>

<table border="1">

<tr>

<td>

100

</td>

<td>

200

</td>

<td>

300

</td>

</tr>

</table>

<h4>

Two rows and three columns:

</h4>

<table border="1">

<tr>

<td>

100

</td>

<td>

200

</td>

<td>

300

</td>

</tr>

<tr>

<td>

400

</td>

<td>

500

</td>

<td>

600

</td>

</tr>

</table>

</body>

</html>

这个例子说明了如何在HTML页面中建立表格。

表格边框

<html>

<body>

<h4>

With a normal border:

</h4>

<table border="1">

<tr>

<td>

First

</td>

<td>

Row

</td>

</tr>

<tr>

<td>

Second

</td>

<td>

Row

</td>

</tr>

</table>

<h4>

With a thick border:

</h4>

<table border="8">

<tr>

<td>

First

</td>

<td>

Row

</td>

</tr>

<tr>

<td>

Second

</td>

<td>

Row

</td>

</tr>

</table>

<h4>

With a very thick border:

</h4>

<table border="15">

<tr>

<td>

First

</td>

<td>

Row

</td>

</tr>

<tr>

<td>

Second

</td>

<td>

Row

</td>

</tr>

</table>

</body>

</html>

这个例子显示了不一样的表格边框。

表格

表格是用<table>标签订义的。表格被划分为行(使用<tr>标签),每行又被划分为数据单元格(使用<td>标签)。td表示表格数据Table Data),即数据单元格的内容。数据单元格能够包含文本,图像,列表,段落,表单,水平线,表格等等。

<table border="1">

<tr>

<td>

row 1, cell 1

</td>

<td>

row 1, cell 2

</td>

</tr>

<tr>

<td>

row 2, cell 1

</td>

<td>

row 2, cell 2

</td>

</tr>

</table>

在浏览器中显示以下:

row 1, cell 1 row 1, cell 2

row 2, cell 1 row 2, cell 2

表格和border属性

若是不指定border属性,表格将不显示边框。有时候这颇有用,可是多数时候咱们但愿显示边框。

想要显示一个有边框的表格,须要使用border属性。

<table border="1">

<tr>

<td>

Row 1, cell 1

</td>

<td>

Row 1, cell 2

</td>

</tr>

</table>

表格头

表格头使用<th>标签指定。

<table border="1">

<tr>

<th>

Heading

</th>

<th>

Another Heading

</th>

</tr>

<tr>

<td>

row 1, cell 1

</td>

<td>

row 1, cell 2

</td>

</tr>

<tr>

<td>

row 2, cell 1

</td>

<td>

row 2, cell 2

</td>

</tr>

</table>

在浏览器中显示以下:

Heading Another Heading

row 1, cell 1 row 1, cell 2

row 2, cell 1 row 2, cell 2

表格中的空单元格

在多数浏览器中,没有内容的单元格显示得不太好。

<table border="1">

<tr>

<td>

row 1, cell 1

</td>

<td>

row 1, cell 2

</td>

</tr>

<tr>

<td>

row 2, cell 1

</td>

<td></td>

</tr>

</table>

在浏览器中显示以下:

row 1, cell 1 row 1, cell 2

row 2, cell 1

注意一下空单元格的边框没有显示出来。为了不这个,能够在空单元格里加入不可分空格来占位,这样边框能正常显示。

<table border="1">

<tr>

<td>

row 1, cell 1

</td>

<td>

row 1, cell 2

</td>

</tr>

<tr>

<td>

row 2, cell 1

</td>

<td>

</td>

</tr>

</table>

在浏览器中显示以下:

row 1, cell 1 row 1, cell 2

row 2, cell 1

技巧

一般不多使用<thead><tbody><tfoot>标签,由于浏览器对它们的支持很差。但愿这个在XHTML的将来版本中获得改变。

________________________________________

例如:

没有边框的表格

<html>

<body>

<h4>

This table has no borders:

</h4>

<table>

<tr>

<td>

100

</td>

<td>

200

</td>

<td>

300

</td>

</tr>

<tr>

<td>

400

</td>

<td>

500

</td>

<td>

600

</td>

</tr>

</table>

<h4>

And this table has no borders:

</h4>

<table border="0">

<tr>

<td>

100

</td>

<td>

200

</td>

<td>

300

</td>

</tr>

<tr>

<td>

400

</td>

<td>

500

</td>

<td>

600

</td>

</tr>

</table>

</body>

</html>

这个例子显示了一个没有边框的表格。

表格头

<html>

<body>

<h4>

Table headers:

</h4>

<table border="1">

<tr>

<th>

Name

</th>

<th>

Telephone

</th>

<th>

Telephone

</th>

</tr>

<tr>

<td>

Bill Gates

</td>

<td>

555 77 854

</td>

<td>

555 77 855

</td>

</tr>

</table>

<h4>

Vertical headers:

</h4>

<table border="1">

<tr>

<th>

First Name:

</th>

<td>

Bill Gates

</td>

</tr>

<tr>

<th>

Telephone:

</th>

<td>

555 77 854

</td>

</tr>

<tr>

<th>

Telephone:

</th>

<td>

555 77 855

</td>

</tr>

</table>

</body>

</html>

这个例子说明了如何显示表格头。

空单元格

<html>

<body>

<table border="1">

<tr>

<td>

Some text

</td>

<td>

Some text

</td>

</tr>

<tr>

<td></td>

<td>

Some text

</td>

</tr>

</table>

<p>

As you can see, one of the cells has no border. That is because it is

empty. Try to insert a space in the cell. Still it has no border.

</p>

<p>

The trick is to insert a no-breaking space in the cell.

</p>

<p>

No-breaking space is a character entity. If you don't know what a

character entity is, read the chapter about it.

</p>

<p>

The no-breaking space entity starts with an ampersand ("&"),then the

letters "nbsp", and ends with a semicolon (";")

</p>

<p>

</p>

</body>

</html>

这个例子说明了如何使用“ ”来支撑没有内容的单元格。

有标题的表格

<html>

<body>

<h4>

This table has a caption,and a thick border:

</h4>

<table border="6">

<caption>

My Caption

</caption>

<tr>

<td>

100

</td>

<td>

200

</td>

<td>

300

</td>

</tr>

<tr>

<td>

400

</td>

<td>

500

</td>

<td>

600

</td>

</tr>

</table>

</body>

</html>

这个例子说明了如何建立一个有标题的表格。

单元格跨行(列)的表格

<html>

<body>

<h4>

Cell that spans two columns:

</h4>

<table border="1">

<tr>

<th>

Name

</th>

<th colspan="2">

Telephone

</th>

</tr>

<tr>

<td>

Bill Gates

</td>

<td>

555 77 854

</td>

<td>

555 77 855

</td>

</tr>

</table>

<h4>

Cell that spans two rows:

</h4>

<table border="1">

<tr>

<th>

First Name:

</th>

<td>

Bill Gates

</td>

</tr>

<tr>

<th rowspan="2">

Telephone:

</th>

<td>

555 77 854

</td>

</tr>

<tr>

<td>

555 77 855

</td>

</tr>

</table>

</body>

</html>

这个例子显示了如何定义跨行或者跨列的单元格。

表格内的其余标签

<html>

<body>

<table border="1">

<tr>

<td>

<p>

This is a paragraph

</p>

<p>

This is another paragraph

</p>

</td>

<td>

This cell contains a table:

<table border="1">

<tr>

<td>

A

</td>

<td>

B

</td>

</tr>

<tr>

<td>

C

</td>

<td>

D

</td>

</tr>

</table>

</td>

</tr>

<tr>

<td>

This cell contains a list

<ul>

<li>

apples

</li>

<li>

bananas

</li>

<li>

pineapples

</li>

</ul>

</td>

<td>

HELLO

</td>

</tr>

</table>

</body>

</html>

这个例子说明了如何在元素中显示其余元素。

cellpadding属性

<html>

<body>

<h4>

Without cellpadding:

</h4>

<table border="1">

<tr>

<td>

First

</td>

<td>

Row

</td>

</tr>

<tr>

<td>

Second

</td>

<td>

Row

</td>

</tr>

</table>

<h4>

With cellpadding:

</h4>

<table border="1" cellpadding="10">

<tr>

<td>

First

</td>

<td>

Row

</td>

</tr>

<tr>

<td>

Second

</td>

<td>

Row

</td>

</tr>

</table>

</body>

</html>

这个例子说明了如何使用cellpadding属性在表格内容和边框之间留出更多空白。

cellspacing属性

<html>

<body>

<h4>

Without cellspacing:

</h4>

<table border="1">

<tr>

<td>

First

</td>

<td>

Row

</td>

</tr>

<tr>

<td>

Second

</td>

<td>

Row

</td>

</tr>

</table>

<h4>

With cellspacing:

</h4>

<table border="1" cellspacing="10">

<tr>

<td>

First

</td>

<td>

Row

</td>

</tr>

<tr>

<td>

Second

</td>

<td>

Row

</td>

</tr>

</table>

</body>

</html>

这个例子说明了如何使用cellspacing属性来增长单元格间距。

给表格增长背景色或者背景图像

<html>

<body>

<h4>

A background color:

</h4>

<table border="1" bgcolor="red">

<tr>

<td>

First

</td>

<td>

Row

</td>

</tr>

<tr>

<td>

Second

</td>

<td>

Row

</td>

</tr>

</table>

<h4>

A background image:

</h4>

<table border="1" background="/images/bgdesert.jpg">

<tr>

<td>

First

</td>

<td>

Row

</td>

</tr>

<tr>

<td>

Second

</td>

<td>

Row

</td>

</tr>

</table>

</body>

</html>

这个例子说明了如何给表格增长背景。

给表格增长背景色或者背景图像

<html>

<body>

<h4>

Cell backgrounds:

</h4>

<table border="1">

<tr>

<td bgcolor="red">

First

</td>

<td>

Row

</td>

</tr>

<tr>

<td background="/images/bgdesert.jpg">

Second

</td>

<td>

Row

</td>

</tr>

</table>

</body>

</html>

这个例子说明了如何给一个或多个单元格增长背景。

给单元格内容设置对齐方式

<html>

<body>

<table width="400" border="1">

<tr>

<th align="left">

Money spent on....

</th>

<th align="right">

January

</th>

<th align="right">

February

</th>

</tr>

<tr>

<td align="left">

Clothes

</td>

<td align="right">

$241.10

</td>

<td align="right">

$50.20

</td>

</tr>

<tr>

<td align="left">

Make-Up

</td>

<td align="right">

$30.00

</td>

<td align="right">

$44.45

</td>

</tr>

<tr>

<td align="left">

Food

</td>

<td align="right">

$730.40

</td>

<td align="right">

$650.00

</td>

</tr>

<tr>

<th align="left">

Sum

</th>

<th align="right">

$1001.50

</th>

<th align="right">

$744.65

</th>

</tr>

</table>

</body>

</html>

这个例子说明了如何使用“align”属性来设置单元格的对齐方式,让表格好看一些。

frame属性

<html>

<body>

<p>

If you see no frames around the tables in these examples, your

browser is too old, or does not support it.

</p>

<h4>

With frame="border":

</h4>

<table frame="border">

<tr>

<td>

First

</td>

<td>

Row

</td>

</tr>

<tr>

<td>

Second

</td>

<td>

Row

</td>

</tr>

</table>

<h4>

With frame="box":

</h4>

<table frame="box">

<tr>

<td>

First

</td>

<td>

Row

</td>

</tr>

<tr>

<td>

Second

</td>

<td>

Row

</td>

</tr>

</table>

<h4>

With frame="void":

</h4>

<table frame="void">

<tr>

<td>

First

</td>

<td>

Row

</td>

</tr>

<tr>

<td>

Second

</td>

<td>

Row

</td>

</tr>

</table>

<h4>

With frame="above":

</h4>

<table frame="above">

<tr>

<td>

First

</td>

<td>

Row

</td>

</tr>

<tr>

<td>

Second

</td>

<td>

Row

</td>

</tr>

</table>

<h4>

With frame="below":

</h4>

<table frame="below">

<tr>

<td>

First

</td>

<td>

Row

</td>

</tr>

<tr>

<td>

Second

</td>

<td>

Row

</td>

</tr>

</table>

<h4>

With frame="hsides":

</h4>

<table frame="hsides">

<tr>

<td>

First

</td>

<td>

Row

</td>

</tr>

<tr>

<td>

Second

</td>

<td>

Row

</td>

</tr>

</table>

<h4>

With frame="vsides":

</h4>

<table frame="vsides">

<tr>

<td>

First

</td>

<td>

Row

</td>

</tr>

<tr>

<td>

Second

</td>

<td>

Row

</td>

</tr>

</table>

<h4>

With frame="lhs":

</h4>

<table frame="lhs">

<tr>

<td>

First

</td>

<td>

Row

</td>

</tr>

<tr>

<td>

Second

</td>

<td>

Row

</td>

</tr>

</table>

<h4>

With frame="rhs":

</h4>

<table frame="rhs">

<tr>

<td>

First

</td>

<td>

Row

</td>

</tr>

<tr>

<td>

Second

</td>

<td>

Row

</td>

</tr>

</table>

</body>

</html>

这个例子说明了如何使用“frame”属性来控制表格周围的边框。

HTML列表

HTML支持有序、无序和自定义列表。

例如:

一个无序列表

<html>

<body>

<h4>

An Unordered List:

</h4>

<ul>

<li>

Coffee

</li>

<li>

Tea

</li>

<li>

Milk

</li>

</ul>

</body>

</html>

这个例子显示了一个无序列表。

有序列表

<html>

<body>

<h4>

An Ordered List:

</h4>

<ol>

<li>

Coffee

</li>

<li>

Tea

</li>

<li>

Milk

</li>

</ol>

</body>

</html>

这个例子显示了一个有序列表。

无序列表

无序列表是一个项目的序列。各项目前加有标记(一般是黑色的实心小圆圈)。

无序列表以<ul>标签开始。每一个列表项目以<li>开始。

<ul>

<li>

Coffee

</li>

<li>

Milk

</li>

</ul>

在浏览器中显示以下:

? Coffee

? Milk

无序列表的项目中能够加入段落、换行、图像,连接,其余的列表等等。

有序列表

有序列表也是一个项目的序列。各项目前加有数字做标记。

有序列表以<ol>标签开始。每一个列表项目以<li>开始。

<ol>

<li>

Coffee

</li>

<li>

Milk

</li>

</ol>

在浏览器中显示以下:

1. Coffee

2. Milk

有序列表的项目中能够加入段落、换行、图像,连接,其余的列表等等。

自定义列表

自定义列表不是一个项目的序列,它是一系列条目和它们的解释。

有序列表以<dl>标签开始,自定义列表条目以<dt>开始,自定义列表的定义以<dd>开始。

<dl>

<dt>

Coffee

</dt>

<dd>

Black hot drink

</dd>

<dt>

Milk

</dt>

<dd>

White cold drink

</dd>

</dl>

在浏览器中显示以下:

Coffee

Black hot drink

Milk

White cold drink

自定义列表的定义(标签<dd>)中能够加入段落、换行、图像,连接,其余的列表等等。

例如:

有序列表的不一样类型

<html>

<body>

<h4>

Numbered list:

</h4>

<ol>

<li>

Apples

</li>

<li>

Bananas

</li>

<li>

Lemons

</li>

<li>

Oranges

</li>

</ol>

<h4>

Letters list:

</h4>

<ol type="A">

<li>

Apples

</li>

<li>

Bananas

</li>

<li>

Lemons

</li>

<li>

Oranges

</li>

</ol>

<h4>

Lowercase letters list:

</h4>

<ol type="a">

<li>

Apples

</li>

<li>

Bananas

</li>

<li>

Lemons

</li>

<li>

Oranges

</li>

</ol>

<h4>

Roman numbers list:

</h4>

<ol type="I">

<li>

Apples

</li>

<li>

Bananas

</li>

<li>

Lemons

</li>

<li>

Oranges

</li>

</ol>

<h4>

Lowercase Roman numbers list:

</h4>

<ol type="i">

<li>

Apples

</li>

<li>

Bananas

</li>

<li>

Lemons

</li>

<li>

Oranges

</li>

</ol>

</body>

</html>

这个例子显示了有序列表的不一样类型。

无序列表的不一样类型

<html>

<body>

<h4>

Disc bullets list:

</h4>

<ul type="disc">

<li>

Apples

</li>

<li>

Bananas

</li>

<li>

Lemons

</li>

<li>

Oranges

</li>

</ul>

<h4>

Circle bullets list:

</h4>

<ul type="circle">

<li>

Apples

</li>

<li>

Bananas

</li>

<li>

Lemons

</li>

<li>

Oranges

</li>

</ul>

<h4>

Square bullets list:

</h4>

<ul type="square">

<li>

Apples

</li>

<li>

Bananas

</li>

<li>

Lemons

</li>

<li>

Oranges

</li>

</ul>

</body>

</html>

这个例子显示了无序列表的不一样类型。

嵌套列表

<html>

<body>

<h4>

A nested List:

</h4>

<ul>

<li>

Coffee

</li>

<li>

Tea

<ul>

<li>

Black tea

</li>

<li>

Green tea

</li>

</ul>

</li>

<li>

Milk

</li>

</ul>

</body>

</html>

这个例子说明了能够怎样嵌套列表。

嵌套列表2

<html>

<body>

<h4>

A nested List:

</h4>

<ul>

<li>

Coffee

</li>

<li>

Tea

<ul>

<li>

Black tea

</li>

<li>

Green tea

<ul>

<li>

China

</li>

<li>

Africa

</li>

</ul>

</li>

</ul>

</li>

<li>

Milk

</li>

</ul>

</body>

</html>

这个例子说明了如何建立一个更复杂化的嵌套列表。

自定义列表:

<html>

<body>

<h4>

A Definition List:

</h4>

<dl>

<dt>

Coffee

</dt>

<dd>

Black hot drink

</dd>

<dt>

Milk

</dt>

<dd>

White cold drink

</dd>

</dl>

</body>

</html>

这个例子说明了如何建立一个自定义列表。

HTML图像

使用HTML能够在文档中显示图像。

例如:

插入图像

<html>

<body>

<p>

An image:

<img src="./images/constr.gif" width="144" height="50">

</p>

<p>

A moving image:

<img src="./images/hackanm.gif" width="48" height="48">

</p>

<p>

Note that the syntax of inserting a moving image is no different from

that of a non-moving image.

</p>

</body>

</html>

这个例子说明了如何在网页中显示图像。

插入非本地图像

<html>

<body>

<p>

An image from W3Schools:

<img src="http://www.w3schools.com/images/ie.gif" width="73"

height="68">

</p>

</body>

</html>

这个例子说明了如何在网页中显示非本地图像。

Img标签和src属性

HTML里面,图像是由<img>标签订义的。

<img>是空标签,意思是说,它只拥有属性,而没有结束标签。

想要在页面上显示一个图像,须要使用src属性。“src”表示的意思。“src”属性的值是所要显示图像的URL

插入图像的语法:

<img src="url">

URL指向图像存储的地址。网站“www.w3schools.com”子目录“images”中的图像“boat.gif”URL以下: “http://www.w3schools.com/images/boat.gif”

当浏览器在文档中遇到img标签时,就放置一个图像。若是把img标签放在两个段落之间,就会先显示一个段落,而后是这个图像,最后是另一个段落。

alt属性

alt属性用来给图像显示一个交互文本alt属性的值是由用户定义的。

<img src="boat.gif" alt="Big Boat">

“alt”属性在浏览器装载图像失败的时候告诉用户所丢失的信息,此时,浏览器显示这个交互文本来代替图像。给页面上的图像都加上alt属性是一个好习惯,它有助于更好地显示信息,并且,对纯文本浏览器颇有用。

技巧

若是一个HTML文档包含10个图像,那么为了正确显示这个页面,须要加载11个文件。加载图像是须要时间的,因此请谨慎使用图像。

例如:

背景图像

<html>

<body background="./images/background.jpg">

<h3>

Look: A background image!

</h3>

<p>

Both gif and jpg files can be used as HTML backgrounds.

</p>

<p>

If the image is smaller than the page, the image will repeat itself.

</p>

</body>

</html>

这个例子说明了在HTML页面中如何添加背景图像。

对齐图像

<html>

<body>

<p>

An image

<img src="./images/xhtml.gif" align="bottom" width="100" height="50">

in the text

</p>

<p>

An image

<img src="./images/xhtml.gif" align="middle" width="100" height="50">

in the text

</p>

<p>

An image

<img src="./images/xhtml.gif" align="top" width="100" height="50">

in the text

</p>

<p>

Note that bottom alignment is the default alignment

</p>

<p>

An image

<img src="./images/xhtml.gif" width="100" height="50">

in the text

</p>

<p>

<img src="./images/xhtml.gif" width="100" height="50">

An image before the text

</p>

<p>

An image after the text

<img src="./images/xhtml.gif" width="100" height="50">

</p>

</body>

</html>

这个例子说明了在文字中如何对齐图像。

浮动图像

<html>

<body>

<p>

<img src="./images/xhtml.gif" align="left" width="100" height="50">

A paragraph with an image. The align attribute of the image is set to

"left". The image will float to the left of this text.

</p>

<p>

<img src="./images/xhtml.gif" align="right" width="100" height="50">

A paragraph with an image. The align attribute of the image is set to

"right". The image will float to the right of this text.

</p>

</body>

</html>

这个例子说明了如何让图像浮动在段落的旁边。

调整图像大小

<html>

<body>

<p>

<img src="./images/hackanm.gif" width="20" height="20">

</p>

<p>

<img src="./images/hackanm.gif" width="45" height="45">

</p>

<p>

<img src="./images/hackanm.gif" width="70" height="70">

</p>

<p>

You can make a picture larger or smaller changing the values in the

"height" and "width" attributes of the img tag.

</p>

</body>

</html>

这个例子说明了如何改变图像的大小。

图像的交互文本

<html>

<body>

<img src="./images/prev.gif" alt="Last Page">

<p>

Text-only browsers will only display the text in the "alt" attribute,

which is "Last Page".Note that if you hold the mouse pointer over the

image it will display the text.

</p>

</body>

</html>

这个例子说明了如何为图像建立交互文本。将鼠标放在图像上,将可以看到这个文本。

图像连接:

<html>

<body>

<p>

You can also use an image as a link:

<a href="back.htm"> <img border="0" src="./images/next.gif">

</a>

</p>

</body>

</html>

这个例子说明了如何使用图像做为连接。

图像地图

<html>

<body>

<p>

Click on one of the planets to watch it closer:

</p>

<img src="./images/planets.gif" width="145" height="126"

usemap="#planetmap">

<map id="planetmap" name="planetmap">

<area shape="rect" coords="0,0,82,126" alt="Sun" href="sun.htm">

<area shape="circle" coords="90,58,3" alt="Mercury"

href="mercury.htm">

<area shape="circle" coords="124,58,8" alt="Venus" href="venus.htm">

</map>

<p>

<b>Note:</b> We use both an

<b>id</b> and a

<b>name</b> attribute in the map tag because some versions of

Netscape don't understand the id attribute.

</p>

</body>

</html>

这个例子说明了如何建立带有可点击区域的图像地图。每一个可点击区域是一个超级连接。

将图像转化为图像地图

<html>

<body>

<p>

Move the mouse over the image, and look at the status bar to see how

the coordinates change.

</p>

<p>

<a href="ismap.htm"> <img src="./images/planets.gif" ismap

width="146" height="126"> </a>

</p>

</body>

</html>

这个例子说明了如何将图像转化为图像地图。将鼠标在图像上移动,状态栏将显示坐标。

HTML背景

好的背景可以让网站变得很美妙。

例如:

好的背景和文字颜色

<html>

<body bgcolor="#d0d0d0">

<p>

This is a paragraph. This is a paragraph. This is a paragraph. This

is a paragraph. This is a paragraph.

</p>

<p>

This is another paragraph. This is another paragraph. This is another

paragraph. This is another paragraph.

</p>

</body>

</html>

一个背景色和文字颜色让页面文本更易读的例子。

差的背景和文字颜色

<html>

<body bgcolor="#ffffff" text="yellow">

<p>

This is a paragraph. This is a paragraph. This is a paragraph. This

is a paragraph. This is a paragraph.

</p>

<p>

This is another paragraph. This is another paragraph. This is another

paragraph. This is another paragraph.

</p>

</body>

</html>

一个背景色和文字颜色让页面文本不便阅读的例子。

背景

<body>标签有两个属性能够指定背景。背景能够是颜色,也能够是图像。

bgcolor

bgcolor属性将背景设置为颜色,它的值能够是一个十六进制数、RGB值或者一种颜色名称。

<body bgcolor="#000000">

<body bgcolor="rgb(0,0,0)">

<body bgcolor="black">

上面三条语句均可以把页面背景设置成黑色。

background

background属性将背景设置为图像,它的值是你所要使用图像的URL地址。若是这个图像的大小不及浏览器窗口,它将平铺占满窗口。

<body background="clouds.gif">

<body background="http://www.w3schools.com/clouds.gif">

URL地址能够是相对地址(上面第一条语句),也能够是绝对地址(上面第二条语句)。

________________________________________注意:若是打算使用背景图像,应该牢记:

? 这个图像会使页面加载时间过长吗?小技巧:图像文件的容量尽可能不要超过10K

? 这个图像跟页面上其余图像协调吗?

? 这个图像跟页面上的文字颜色协调吗?

? 这个图像平铺了之后看起来还能够吗?

? 这个图像吸引了文字的注意力,喧宾夺主了吗?

________________________________________

技巧

<body>标签的bgcolorbackgroundtext属性在最新的HTML标准(HTML4XHTML)中已被废弃。W3C在他们的推荐中已删除这些属性。在HTML的将来版本中,层叠样式表(CSS)将被用来定义HTML元素的布局和显示属性。

通常的网站不多使用背景图像。

最经常使用的背景颜色是黑色、白色和灰色。

例如:

好的背景图像

<html>

<body background="./images/background.jpg">

<h3>

Image Background

</h3>

<p>

Both gif and jpg files can be used as HTML backgrounds.

</p>

<p>

If the image is smaller than the page, the image will repeat itself.

</p>

</body>

</html>

一个背景图像和文字颜色让页面文本易于阅读的例子。

好的背景图像2

<html>

<body background="./images/paper.gif">

<h3>

Image Background

</h3>

<p>

Both gif and jpg files can be used as HTML backgrounds.

</p>

<p>

If the image is smaller than the page, the image will repeat itself.

</p>

</body>

</html>

另外一个背景图像和文字颜色让页面文本易于阅读的例子。

差的背景图像

<html>

<body background="./images/rock.jpg">

<h3>

Image Background

</h3>

<p>

Both gif and jpg files can be used as HTML backgrounds.

</p>

<p>

If the image is smaller than the page, the image will repeat itself.

</p>

</body>

</html>

一个背景图像和文字颜色让页面文本不便阅读的例子。

内容总结

? 掌握HTML中最重要的标签是定义标题元素,段落和换行的标签,格式化元素好比加粗和倾斜文本,了解HTML中的特殊实体

? 掌握超连接、锚的用法,表格的画法以及如何添加表的标题、表头等。无序、有序及自定义列表。以及如何在<body>标签中设置网页背景。

? 了解并掌握图像标签、alt属性、src属性,以及如何利用图像建立超连接

独立实践

? 写一个本身简历的HTML文档。

? 写一个用表格显示本班学生的HTML文档。

? 经过点击简历的小头像,能够看到显示完整相片的HTML文档。

? 在上面三个HTML文档里用不一样的背景图片

? 定义一个我的主页的HTML文档

第二十四章:HTML进阶

学习目标

? 掌握表单的建立方法、提交方式以及表单所包含的文档对象

? 掌握框架的建立方法、框架大小的调整以及如何在不一样框架之间建立连接

? 掌握IFrame的使用,如何利用IFame引入其它页面,以及如何在IFrame和包含IFrame的页面之间通讯

知识要点

HTML表单

HTML表单用来选择不一样种类的用户输入。

例如:

文本框

<html>

<body>

<form>

First name:

<input type="text" name="firstname">

<br>

Last name:

<input type="text" name="lastname">

</form>

</body>

</html>

这个例子说明了在HTML页面中如何建立文本框。在文本框中,用户能够输入文本。

密码框

<html>

<body>

<form>

Username:

<input type="text" name="user">

<br>

Password:

<input type="password" name="password">

</form>

<p>

Note that when you type characters in a password field, the browser

displays asterisks or bullets instead of the characters.

</p>

</body>

</html>

这个例子说明了在HTML页面中如何建立密码框。

表单

表单是一个可以包含表单元素的区域。

表单元素是可以让用户在表单中输入信息的元素(好比文本框,密码框,下拉菜单,单选框,复选框等等)。

表单是用<form>元素定义的。

<form>

<input>

<input>

</form>

Input

最经常使用的表单标签是<input>标签。Input的类型用type属性指定。最经常使用的input类型解释以下:

文本框

在表单中,文本框用来让用户输入字母、数字等等。

<form>

First name:

<input type="text" name="firstname">

<br>

Last name:

<input type="text" name="lastname">

</form>

________________________________________注意,表单自己并不可见。另外,在多数浏览器中,文本框的宽度默认是20个字符。________________________________________

单选按钮

当须要用户从有限个选项中选择一个时,使用单选按钮。

<form>

<input type="radio" name="sex" value="male">

Male

<br>

<input type="radio" name="sex" value="female">

Female

</form>

在浏览器中显示以下:

Male

Female

________________________________________注意,各选项中只能选取一个。________________________________________

复选框

当须要用户从有限个选项中选择一个或多个时,使用复选框。

<form>

<input type="checkbox" name="bike">

I have a bike

<br>

<input type="checkbox" name="car">

I have a car

</form>

在浏览器中显示以下:

I have a bike

I have a car

表单的action属性和提交按钮

当用户点击提交按钮的时候,表单的内容会被提交到其余文件。表单的action属性定义了所要提交到的目的文件,该目的文件收到信息后一般进行相关的处理。

<form name="input" action="html_form_action.asp" method="get">

Username:

<input type="text" name="user">

<input type="submit" value="Submit">

</form>

若是在上面这个文本框中输入一些字符,按下提交按钮之后,输入的字符将被提交到页面“action.asp”

例如:

复选框

<html>

<body>

<form>

I have a bike:

<input type="checkbox" name="Bike">

<br>

I have a car:

<input type="checkbox" name="Car">

</form>

</body>

</html>

这个例子说明了在HTML页面中如何建立复选框。用户能够选中,也能够取消选择复选框。

单选按钮

<html>

<body>

<form>

Male:

<input type="radio" checked name="Sex" value="male">

<br>

Female:

<input type="radio" name="Sex" value="female">

</form>

<p>

When a user clicks on a radio-button, the button becomes checked, and

all other buttons with the same name become unchecked

</p>

</body>

</html>

这个例子说明了在HTML页面中如何建立单选按钮。

简单的下拉列表

<html>

<body>

<form>

<select name="cars">

<option value="volvo">

Volvo

<option value="saab">

Saab

<option value="fiat">

Fiat

<option value="audi">

Audi

</select>

</form>

</body>

</html>

这个例子说明了在HTML页面如何建立下拉列表。下拉列表是能够选择的列表。

预选的下拉列表

<html>

<body>

<form>

<select name="cars">

<option value="volvo">

Volvo

<option value="saab">

Saab

<option value="fiat" selected>

Fiat

<option value="audi">

Audi

</select>

</form>

</body>

</html>

这个例子说明了如何建立一个含有预先选定元素的下拉列表。

文本域

<html>

<body>

<p>

This example demonstrates a text-area.

</p>

<textarea rows="10" cols="30">

The cat was playing in the garden.

</textarea>

</body>

</html>

这个例子说明了如何建立文本域(多行文本),用户能够在其中输入文本。在文本域中,字符个数不受限制。

建立按钮

<html>

<body>

<form>

<input type="button" value="Hello world!">

</form>

</body>

</html>

这个例子说明了如何建立按钮。按钮上的文字能够本身定义。

数据周围的标题边框

<html>

<body>

<fieldset>

<legend>

Health information:

</legend>

<form>

Height

<input type="text" size="3">

Weight

<input type="text" size="3">

</form>

</fieldset>

<p>

If there is no border around the input form, your browser is too old.

</p>

</body>

</html>

这个例子说明了如何在数据周围画带有标题的边框。

含有文本框和提交按钮的表单:

<html>

<body>

<form name="input" action="action.asp" method="get">

Enter your first name:

<input type="text" name="FirstName" value="Mickey">

<br>

Enter your last name:

<input type="text" name="LastName" value="Mouse">

<br>

<input type="submit" value="Submit">

</form>

<p>

If you click the "Submit" button, you will send your input to a new

page called action.asp.

</p>

</body>

</html>

这个例子说明了在HTML页面中加入表单。这个表单包含了两个文本框和一个提交按钮。

含有复选框的表单

<html>

<body>

<form name="input" action="action.asp" method="get">

I have a bike:

<input type="checkbox" name="Bike" checked>

<br>

I have a car:

<input type="checkbox" name="Car">

<br>

<input type="submit" value="Submit">

</form>

<p>

If you click the "Submit" button, you send your input to a new page

called action.asp.

</p>

</body>

</html>

这个表单包含了两个复选框和一个提交按钮。

含有单选按钮的表单

<html>

<body>

<form name="input" action="action.asp" method="get">

Male:

<input type="radio" name="Sex" value="Male" checked>

<br>

Female:

<input type="radio" name="Sex" value="Female">

<br>

<input type="submit" value="Submit Now!">

</form>

<p>

If you click the "Submit" button, you will send your input to a new

page called action.asp.

</p>

</body>

</html>

这个表单包含了两个单选按钮和一个提交按钮。

从表单发送电子邮件

<html>

<body>

<form action="MAILTO:someone@w3schools.com" method="post"

enctype="text/plain">

<h3>

This form sends an e-mail to W3Schools.

</h3>

Name:

<br>

<input type="text" name="name" value="yourname" size="20">

<br>

Mail:

<br>

<input type="text" name="mail" value="yourmail" size="20">

<br>

Comment:

<br>

<input type="text" name="comment" value="yourcomment" size="40">

<br>

<br>

<input type="submit" value="Send">

<input type="reset" value="Reset">

</form>

</body>

</html>

这个例子说明了如何从一个表单发送电子邮件。

HTML框架

使用框架,能够在一个浏览器窗口中显示不止一个页面。

例如:

垂直分栏

<html>

<frameset cols="25%,50%,25%">

<frame src="frame_a.htm">

<frame src="frame_b.htm">

<frame src="frame_c.htm">

</frameset>

</html>

这个例子说明了如何建立一个有三个页面的垂直分栏。

水平分栏

<html>

<frameset rows="25%,50%,25%">

<frame src="frame_a.htm">

<frame src="frame_b.htm">

<frame src="frame_c.htm">

</frameset>

</html>

这个例子说明了如何建立一个有三个页面的水平分栏。

如何使用<noframes>标签

<html>

<frameset cols="25%,50%,25%">

<frame src="frame_a.htm">

<frame src="frame_b.htm">

<frame src="frame_c.htm">

<noframes>

<body>

Your browser does not handle frames!

</body>

</noframes>

</frameset>

</html>

这个例子说明了如何使用<noframes>标签。

框架

使用框架,能够在一个浏览器窗口中显示不止一个HTML文档。这样的HTML文档被称为框架页面,它们是相互独立的。

使用框架的不利因素有:

? 网站开发者须要关心更多HTML文档的状况。

? 打印整个页面变得困难。

frameset标签

? <frameset>标签订义了如何将窗口拆分红框架。

? 每一个frameset标签订义了一组行和列。

? 行/列的值指明了每一个行/列在屏幕上所占的大小。

frame标签

<frame>标签订义了每一个框架中放入什么文件。

下面这个例子中,有一个两列的分栏。第一个被设置成窗口宽度的25%,第二个被设置成窗口宽度的75%。页面“frame_a.htm”被放在第一个分栏中,“frame_b.htm”被放在第二个分栏中。

<frameset cols="25%,75%">

<frame src="frame_a.htm">

<frame src="frame_b.htm">

</frameset>

技巧

假如一个框架有可见边框,用户能够拖动边框来改变它的大小。若是不想让用户改变大小,能够在<frame>标签中加入:noresize="noresize"

给不支持框架的浏览器写上<noframes>标签。

例如:

混合框架

<html>

<frameset rows="50%,50%">

<frame src="frame_a.htm">

<frameset cols="25%,75%">

<frame src="frame_b.htm">

<frame src="frame_c.htm">

</frameset>

</frameset>

</html>

这个例子说明了怎样把三个页面以行列混合的方式放在框架中。

使用了noresize="noresize"的框架

<html>

<frameset rows="50%,50%">

<frame noresize="noresize" src="frame_a.htm">

<frameset cols="25%,75%">

<frame noresize="noresize" src="frame_b.htm">

<frame noresize="noresize" src="frame_c.htm">

</frameset>

</frameset>

</html>

这个例子说明了noresize属性。这个框架是不可改变大小的,把鼠标移动到框架边界上,你会发现没法调整大小。

导航框架

<html>

<frameset cols="120,*">

<frame src="frame_link.htm">

<frame src="frame_a.htm" name="showframe">

</frameset>

</html>

这个例子说明了如何建立一个导航框架。导航框架包含了一系列连接,它们的目标页面在第二个框架中。文件“frame_links.htm”包含了三个连接,连接的代码以下:

<a href ="frame_a.htm" target ="showframe">Frame a</a>

<a href ="frame_b.htm" target ="showframe">Frame b</a>

<a href ="frame_c.htm" target ="showframe">Frame c</a>

第二个框架将显示连接到的页面。

内联框架:

<html>

<body>

<iframe src="intro.htm"></iframe>

<p>

Some older browsers don't support iframes.

</p>

<p>

If they don't, the iframe will not be visible.

</p>

</body>

</html>

这个例子说明了如何建立一个内联框架(包含在HTML页面里的框架)。

在框架内跳转到指定章节

<html>

<frameset cols="30%,70%">

<frame src="frame_a.htm">

<frame src="frame_section.htm#C10">

</frameset>

</html>

这个例子显示了两个框架页。其中一个的源是一个文件的指定章节,该章节在文件“frame_section.htm”中使用代码<a name="C10">指定。

使用导航框架跳转到指定章节

<html>

<frameset cols="200,*">

<frame src="frame_linksection.htm">

<frame src="frame_section.htm" name="showframe">

</frameset>

</html>

这个例子显示了两个框架页。左边的导航框架包含了一系列以第二个框架为目标的连接(“frame_linksection.htm”),第二个框架显示连接文件(“frame_section.htm”)。导航框架中的一个连接指向目标文件中的指定章节。文件“frame_link”中的HTML代码是像这样的:

<a href ="frame_section" target ="showframe">没有命名锚的连接</a>

<a href ="frame_section#C10" target ="showframe">有命名锚的连接</a>

IFrame

纵观时下网站,几乎每页都要放Banner,栏目图片,版权等一大堆雷同的东西,这都是为了网站风格统1、广告效应的须要。有没有办法,让这些雷同的东西一次下载后就不用再下载,而只下载那些内容有变化区域的网页内容呢?

答案是:应用Iframe标记!

Iframe标记的使用

提起Iframe,可能早已被不少人忘记。不过,提及它的兄弟Frame你们就不会陌生了。Frame标记即帧标记,咱们所说的多帧结构就是在一个浏览器窗口中显示多个HTML文件。如今,咱们遇到一种很现实的状况:若有一个教程,是一节一节地上,每页末尾作一个上一节下一节的连接,除了每节教程内容不一样以外,页面其它部份内容都是相同的,若是一页一页地作页面,这彷佛太让人厌烦了。若是有一种方法让页面其它地方不变,只将教程作成一页一页的内容页,不含其它内容,在点击上下翻页连接时,只改变教程内容部分,其它保持不变。这样,一是省时,另则之后若是教程有个三长两短的变更,也很方便。更重要的是将那些广告Banner、栏目列表、导航等几乎每页的都有的东西只下载一次后就再也不下载了。

Iframe标记,又叫浮动帧标记,你能够用它将一个HTML文档嵌入在一个HTML中显示。它不一样于Frame标记,最大的区别是这个标记所引用的HTML文件不是与另外的HTML文件相互独立显示,而是能够直接嵌入在一个HTML文件中,与这个HTML文件内容相互融合,成为一个总体;另外,还能够屡次在一个页面内显示同一内容,而没必要重复写内容,一个形象的比喻即画中画

Iframe标记的使用格式

<Iframe src="URL" width="x" height="x" scrolling="[option]" frameborder="x"></iframe>

src:文件的路径,既但是HTML文件,也能够是文本、JSP等;

widthheight"画中画"区域的宽与高;

scrolling:src的指定的HTML文件在指定的区域不显不完时,滚动选项,若是设置为NO,则不出现滚动条;如为Auto:则自动出现滚动条;如为Yes,则显示;

frameborder:区域边框的宽度,为了让画中画与邻近的内容相融合,常设置为0

好比:

<Iframe src="http://netschool.cpcw.com/homepage" width="250" height="200" scrolling="no" frameborder="0"></iframe>

父窗体与浮动帧之间的相互控制

在脚本语言与对象层次中,包含Iframe的窗口咱们称之为父窗体,而浮动帧则称为子窗体,弄清这二者的关系很重要,由于要在父窗体中访问子窗体或相反都必须清楚对象层次,才能经过程序来访问并控制窗体。

在父窗体中访问并控制子窗体中的对象

在父窗体中,Iframe是子窗体,它是document对象的一个子对象,能够直接在脚本中访问子窗体中的对象。

如今就有一个问题:咱们怎样来控制这个Iframe。当咱们给这个Iframe设置了ID 属性后,就可经过文档对象模型DOMIframe所含的HTML进行一系列控制。

好比在example.htm里嵌入test.htm文件,并控制test.htm里一些标记对象:

<Iframe src="test.htm" id="test" width="250" height="200" scrolling="no" frameborder="0"></iframe>

test.htm文件代码为:

<html>

<body>

<h1 id="myH1">

hello,my boy

</h1>

</body>

</html>

如咱们要改变ID号为myH1H1标记里的文字为hello,my dear,则可用:

document.myH1.innerText="hello,my dear"(其中,document可省)

example.htm文件中,Iframe标记对象所指的子窗体与通常的DHTML对象模型一致,对象访问控制方式也是同样的。

在子窗体中访问并控制父窗体中对象

在子窗体中咱们能够经过其parent(双亲)对象来访问父窗口中的对象。

example.htm

<html>

<body οnclick="alert(tt.myH1.innerHTML)">

<Iframe name="tt" src="frame1.htm" width="250" height="200"

scrolling="no" frameborder="0"></iframe>

<h1 id="myH2">

hello,my wife

</h1>

</body>

</html>

若是要在frame1.htm中访问ID号为myH2中的标题文字并将之改成"hello,my friend",咱们就能够这样写:

parent.myH2.innerText="hello,my friend"

这里parent对象就表明当前窗体(example.htm所在窗体),要在子窗体中访问父窗体中的对象,无一例外都经过parent对象来进行。

Iframe虽然内嵌在另外一个HTML文件中,但它保持相对的独立,是一个独立王国,在单一HTML中的特性一样适用于浮动帧中。

试想一下,经过Iframe标记,咱们可将那些不变的内容以Iframe来表示,这样,没必要重复写相同的内容,这有点象程序设计中的过程或函数,减省了多少繁琐的手工劳动。另外,相当重要的是,它使页面的修改更为可行,由于,没必要由于版式的调整而修改每一个页面,你只需修改一个父窗体的版式便可了。

________________________________________有一点要注意,Nestscape浏览器不支持Iframe标记,但在时下IE的天下,这彷佛也无大碍,普遍采用Iframe标记,既是为本身着想,又为网友节省了网费,何乐而不为。

________________________________________

内容总结

? 熟练掌握表单的建立方法、提交方式以及表单所包含的文档对象

? 熟练掌握框架的建立方法、框架大小的调整以及如何在不一样框架之间建立连接

? 掌握IFrame的使用,如何利用IFame引入其它页面,以及如何在IFrame和包含IFrame的页面之间通讯

独立实践

? 写一个用户登陆的HTML文档

? 把上一章练习里的几个HTML文档经过框架组织起来

? 写一个注册用户信息的HTML文档。要求有普通用户和管理员用户之分

? 使用Iframe来优化上面结个HMTL文档

? 思考:如何改变浏览器的滚动条的样式,如何改变IFrame的滚动条的样式

第二十五章:JavaScript介绍

学习目标

? 了解JavaScript是什么以及它的特色

? 了解JavaScriptJava的区别

? 了解JavaScript基于对象编程的特性、编译方式

? 了解JavaScript的运行环境

? 会编写简单的JavaScript程序

JavaScript语言概况

JavaScript能够使信息和用户之间不只只是一种显示和浏览的关系,而是实现了一种实时的、动态的、可交式的表达能力。从而基于CGI静态的HTML页面将被可提供动态实时信息,并对客户操做进行反应的Web页面的取代。JavaScript脚本正是知足这种需求而产生的语言。它深受普遍用户的喜好的欢迎。它是众多脚本语言中较为优秀的一种,它与WWW的结合有效地实现了网络计算和网络计算机的蓝图。无疑Java家族将占领Internet网络的主导地位。所以,尽快掌握JavaScript脚本语言编程方法是我国广大用户日益关心的。

什么是JavaScript

JavaScript是一种基于对象(Object)和事件驱动(Event Driven)并具备安全性能的脚本语言。使用它的目的是与HTML超文本标记语言、Java 脚本语言(Java小程序)一块儿实如今一个Web页面中连接多个对象,与Web客户交互做用。从而能够开发客户端的应用程序等。它是经过嵌入或调入在标准的HTML语言中实现的。它的出现弥补了HTML语言的缺陷,它是JavaHTML折衷的选择,具备如下几个基本特色:

是一种脚本编写语言

JavaScript是一种脚本语言,它采用小程序段的方式实现编程。像其它脚本语言同样,JavaScript一样已经是一种解释性语言,它提供了一个容易的开发过程。

它的基本结构形式与CC++VBDelphi十分相似。但它不像这些语言同样,须要先编译,而是在程序运行过程当中被逐行地解释。它与HTML标识结合在一块儿,从而方便用户的使用操做。

基于对象的语言

JavaScript是一种基于对象的语言,同时以能够看做一种面向对象的。这意味着它能运用本身已经建立的对象。所以,许多功能能够来自于脚本环境中对象的方法与脚本的相互做用。

简单性

JavaScript的简单性主要体如今:首先它是一种基于Java基本语句和控制流之上的简单而紧凑的设计, 从而对于学习Java是一种很是好的过渡。其次它的变量类型是采用弱类型,并未使用严格的数据类型。

安全性

JavaScript是一种安全性语言,它不容许访问本地的硬盘,并不能将数据存入到服务器上,不容许对网络文档进行修改和删除,只能经过浏览器实现信息浏览或动态交互。从而有效地防止数据的丢失。

动态性

JavaScript是动态的,它能够直接对用户或客户输入作出响应,无须通过Web服务程序。它对用户的反映响应,是采用以事件驱动的方式进行的。所谓事件驱动,就是指在主页(Home Page)中执行了某种操做所产生的动做,就称为事件”(Event)。好比按下鼠标、移动窗口、选择菜单等均可以视为事件。当事件发生后,可能会引发相应的事件响应。

跨平台性

JavaScript是依赖于浏览器自己,与操做环境无关,只要能运行浏览器的计算机,并支持JavaScript的浏览器就可正确执行。从而实现了编写一次,走遍天下的梦想。

实际上JavaScript最杰出之处在于能够用小程序作大事。无须有高性能的电脑,软件仅需一个字处理软件和一个浏览器,无须WEB服务器便可在本身的电脑完成全部的事情。

综合所述JavaScript 是一种新的描述语言,它能够被嵌入到 HTML 的文件之中。 JavaScript语言能够作到回应使用者的需求事件 (如: form 的输入) ,而不用任何的网路来回传输资料,因此当一位使用者输入一项资料时,它不用通过传给伺服端 (server)处理,再传回来的过程,而直接能够被客户端 (client) 的应用程式所处理。

JavaScriptJava的区别

虽然JavaScriptJava有紧密的联系,但倒是两个公司开发的不一样的两个产品。JavaSUN公司推出的新一代面向对象的程序设计语言,特别适合于Internet应用程序开发;而JavaScriptNetscape公司的产品,其目的是为了扩展Netscape Navigator功能,而开发的一种能够嵌入Web页面中的基于对象和事件驱动的解释性语言, 它的前身是Live Script;而Java的前身是Oak语言。下面对两种语言间的异同做以下比较:

基于对象和面向对象

Java是一种真正的面向对象的语言,即便是开发简单的程序,必须设计对象。 JavaScript是种脚本语言,它能够用来制做与网络无关的,与用户交互做用的复杂软件。它是一种基于对象(Object Based)和事件驱动(Event Driver)的编程语言。于是它自己提供了很是丰富的内部对象供设计人员使用。

解释和编译

两种语言在其浏览器中所执行的方式不同。Java的源代码在传递到客户端执行以前,必须通过编译,于是客户端上必须具备相应平台上的仿真器或解释器,它能够经过编译器或解释器实现独立于某个特定的平台编译代码的束缚。

JavaScript是一种解释性编程语言,其源代码在发往客户端执行以前不需通过编译,而是将文本格式的字符代码发送给客户编由浏览器解释执行。

强变量和弱变量

两种语言所采起的变量是不同的。

Java采用强类型变量检查,即全部变量在编译以前必须声明。如:

Integer x;

String y;

x=1234;

x=4321;

其中X=1234说明是一个整数,Y=4321说明是一个字符串。

JavaScript中变量声明,采用其弱类型。即变量在使用前不需声明,而解释器在运行时检查其数据类型,如:

  x=1234;

  y"4321";

前者说明x为其数值型变量,然后者说明y为字符型变量。

代码格式

Java是一种与HTML无关的格式,必须经过像HTML中引用外部媒体那么进行装载,其代码以字节代码的形式保存在独立的文档中。

JavaScript的代码是一种文本字符格式,能够直接嵌入HTML文档中,而且可动态装载。编写HTML文档就像编辑文本文件同样方便。

嵌入方式

HTML文档中,两种编程语言的标识不一样,JavaScript使用<Script>...</Script>来标识,而Java使用<applet>...</applet>来标识。

静态联编和动态联编

Java采用静态联编,即Java的对象引用必须在编译时的进行,以使编译器可以实现强类型检查。

JavaScript采用动态联编,即JavaScript的对象引用在运行时进行检查,如不经编译则就没法实现对象引用的检查。

实例

下面咱们经过一个例子,编写第一个JavaScript程序。经过它可说明JavaScript的脚本是怎样被嵌入到HTML文档中的。

test1.html文档:

<html>

<head>

<Script Language="JavaScript">

  alert("这是第一个JavaScript例子!");

  alert("欢迎你进入JavaScript世界!");

  alert("从此咱们将共同窗习JavaScript知识!");

  </Script>

</head><

</html>

在浏览器中打开test1.html,怎么没有显示啊?

说明:

test.htmlHTML文档,其标识格式为标准的HTML格式;

JavaScript代码由 <Script Language ="JavaScript">...</Script>说明。在标识<Script Language ="JavaScript">...</Script>之间就可加入JavaScript脚本

alert()JavaScript的窗口对象方法,其功能是弹出一个具备OK对话框并显示()中的字符串

经过<!-- ...//-->标识说明:若不认识JavaScript代码的浏览器,则全部在其中的标识均被忽略;若认识,则执行其结果。使用注释这是一个好的编程习惯,它使其余人能够读懂你的语言。

JavaScript </Script> 标签结束。

从上面的实例分析中咱们能够看出,编写一个JavaScript程序确实很是容易的。

内容总结

? JavaScript是一种基于对象(Object)和事件驱动(Event Driven)并具备安全性能的脚本语言。

? JavaScriptJava的区别

Java 面向对象 编译 强变量 applet 静态联编

jscript 基于对象 解释 弱变量 直接嵌入 动态联编

独立实战

? 写一个简单的javasript程序,弹出警告窗口,显示欢迎来到javascript世界”;

? 把下列语句加到网页中试试

<Script Language="JavaScript">

document.write("<h1>Hello World!</h1>")

</Script>

? 上网搜索如何用javascript显示当前的日期和弹出一个对话框

? 简述JavaJavaScript之间的区别

? 什么是JavaScriptJavaScript有什么好处?

第二十六章:JavaScript基础

学习目标

? 了解并掌握JavaScript中经常使用的基本数据类型、变量、常量、操做运算符

? 掌握JavaScript中全部的程序流

? 了解并掌握JavaScript中的经常使用函数,包括字符串函数、日期函数等

? 了解JavaScript的事件机制,并针对事件编写事件处理程序

基本结构

JavaScript提供脚本语言的编程与Java很是类似,并提供了功能强大的类库。对于已经具有Java语言的人来讲,学习JavaScript脚本语言是一件很是轻松愉快的事。

JavaScript代码的加入

<HTML>

<Head>

<Script Language="JavaScript">

document. Write("这是ITJOB学校");

document. close();

</Script>

</Head>

</HTML>

说明:

Document. write()是文档对象的输出函数,其功能是将括号中的字符或变量值输出到窗口;document. close()是将输出关闭。

可将<Script>...</Script>标识放入<head>.. </Head><Body> ...</Body>之间。将JavaScript标识放置<Head>... </Head>在头部之间,使之在主页和其他部分代码以前装载,从而可以使代码的功能更强大;能够将JavaScript标识放置在<Body>... </Body>主体之间以实现某些部分动态地建立文档。

基本数据类型

JavaScript脚本语言同其它语言同样,有它自身的基本数据类型、表达式和算术运算符以及程序的基本框架结构。

JavaScript中四种基本的数据类型:数值(整数和实数)、字符串型(用“”号或‘’括起来的字符或数值)、布尔型(使TrueFalse表示)和空值。在JavaScript的基本类型中的数据能够是常量,也能够变量。因为JavaScript采用弱类型的形式,于是一个数据的变量或常量没必要首先声明,而是在使用或赋值时肯定其数据的类型的。固然也能够先声明该数据的类型,它是经过在赋值时自动说明其数据类型的。

常量

整型常量

JavaScript的常量一般又称字面常量,它是不能改变的数据。其整型常量能够使用十六进制、八进制和十进制表示其值。

实型常量

实型常量是由整数部分加小数部分表示,如12.32193.98 。能够使用科学或标准方法表示:5E74e5等。

布尔值

布尔常量只有两种状态:TrueFalse。它主要用来讲明或表明一种状态或标志,以说明操做流程。它与C++是不同的,C++能够用1或0表示其状态,JavaScript只能用TrueFalse表示其状态。

字符型常量

使用单引号()或双引号()括起来的一个或几个字符。如 "This is a book of JavaScript ""3245""ewrt234234" 等。

空值

JavaScript中有一个空值null,表示什么也没有。如试图引用没有定义的变量,则返回一个Null值。

特殊字符

同C语言同样,JavaScript中一样以有些以反斜杠(/)开头的不可显示的特殊字符。一般称为控制字符。

变量

变量的主要做用是存取数据、提供存放信息的容器。对于变量必须明确变量的命名、变量的类型、变量的声明及其变量的做用域。

变量的命名

JavaScript中的变量命名同其计算机语言很是类似,这里要注意如下两点:

? 必须是一个有效的变量,即变量以字母开头,中间能够出现数字如test1text2等。除下划线(_)做为连字符外,变量名称不能有空格、(+)、(-)、(,)或其它符号。

? 不能使用JavaScript中的关键字做为变量。

JavaScript中定义了40多个类键字,这些关键是JavaScript内部使用的,不能做为变量的名称。如varintdoubletrue不能做为变量的名称。

在对变量命名时,最好把变量的意义与其表明的意思对应起来,以避免出现错误。

变量的类型

JavaScript中,变量能够用命令Var做声明:

var mytest;

该例子定义了一个mytest变量。但没有赋予它的值。

Var mytest=”This is a book”

该例子定义了一个mytest变量, 并赋值。

JavaScript中,变量不声明,而在使用时再根据数据的类型来肯定其变量的类型。

如:

x=100

y="125"

xy= True

cost=19.5等。

其中x整数,y为字符串,xy为布尔型,cost为实型。

变量的声明及其做用域

JavaScript变量能够在使用前声明,并可赋值。经过使用var关键字对变量声明。对变量声明的最大好处就是能及时发现代码中的错误;由于JavaScript是采用动态编译的,而动态编译是不易发现代码中的错误,特别是变量命名的方面。

对于变量还有一个重要性──那就是变量的做用域。在JavaScript中一样有全局变量和局部变量。全局变量是定义在全部函数体以外,其做用范围是整个函数;而局部变量是定义在函数体以内,只对其该函数是可见的,而对其它函数则是不可见的。

表达式和运算符

表达式

在定义完变量后,就能够对它们进行赋值、改变、计算等一系列操做,这一过程用表达式来完成,表达式是变量、常量、布尔及运算符的集合,表达式能够分为算术表述式、字串表达式、赋值表达式以及布尔表达式等。

运算符

运算符是完成操做的一系列符号,在JavaScript中有算术运算符,如+、-*/等;

有比较运算符如!=、==等; 有逻辑布尔运算符如!(取反)、|||; 有字串运算如+ 、+=等。

JavaScript主要有双目运算符和单目运算符。其双目运算符由下列组成:

操做数1 运算符 操做数2

即由两个操做数和一个运算符组成。如5040"This"+"that"等。单目运算符,只需一个操做数,其运算符可在前或后。

算术运算符

JavaScript中的算术运算符有单目运算符和双目运算符。

双目运算符

+(加) 、-(减)、 *(乘)、 /(除)、 %(取模) 、|(按位或)、&(按位与)<<(左移)、 >>(右移)、 >>>(右移,零填充)。

单目运算符

-(取反)、~(取补)、++(递加1)、--(递减1)。

比较运算符

比较运算符它的基本操做过程是,首先对它的操做数进行比较,尔后再返回一个trueFalse值,有8个比较运算符:

<(小于)>(大于)<=(小于等于)>=(大于等于)==(等于)!=(不等于)

布尔逻辑运算符

JavaScript中增长了几个布尔逻辑运算符:

!(取反)&=(与以后赋值)、 &(逻辑与)、 |=(或以后赋值)、 |(逻辑或)、 ^=(异或以后赋值)、 ^(逻辑异或)、 ?:(三目操做符)、||(或)、==(等于)|=(不等于)

其中三目操做符主要格式以下:

操做数?结果1:结果2

若操做数的结果为真,则表述式的结果为结果1,不然为结果2。

实例

下面是一个跑马灯效果的JavaScript文档。

Test2_1.html

<html>

<head>

<script Language="JavaScript">

var msg="这是一个跑马灯效果的JavaScript文档";

var interval = 100;

var spacelen = 120;

var space10=" ";

var seq=0;

function Scroll() {

len = msg.length;

window.status = msg.substring(0, seq+1);

seq++;

if ( seq >= len ) {

seq = spacelen;

window.setTimeout("Scroll2();", interval );

}

else

window.setTimeout("Scroll();", interval );

}

function Scroll2() {

var out="";

for (i=1; i<=spacelen/space10.length; i++)

out += space10;

out = out + msg;

len=out.length;

window.status=out.substring(seq, len);

seq++;

if ( seq >= len ) { seq = 0; };

window.setTimeout("Scroll2();", interval );

}

Scroll();

</script>

<body>

</body>

</html>

JavaScript程序构成

JavaScript脚本语言的基本构成是由控制语句、函数、对象、方法、属性等,来实现编程的。

程序控制流

在任何一种语言中,程序控制流是必须的,它能使得整个程序减少混乱,使之顺利按其必定的方式执行。下面是JavaScript经常使用的程序控制流结构及语句:

if条件语句

基本格式

if(表述式)

语句段1;

......

else

语句段2;

.....

功能:若表达式为true,则执行语句段1;不然执行语句段2。

说明:

if -else 语句是JavaScript中最基本的控制语句,经过它能够改变语句的执行顺序。

表达式中必须使用关系语句,来实现判断,它是做为一个布尔值来估算的。

它将零和非零的数分别转化成falsetrue

if后的语句有多行,则必须使用花括号将其括起来。

另外,if语句能够嵌套使用。

for循环语句

基本格式

for(初始化;条件;增量)

语句集;

功能:实现条件循环,当条件成立时,执行语句集,不然跳出循环体。

说明:

初始化参数告诉循环的开始位置,必须赋予变量的初值;

条件:是用于判别循环中止时的条件。若条件知足,则执行循环体,不然跳出。

增量:主要定义循环控制变量在每次循环时按什么方式变化。

三个主要语句之间,必须使用逗号分隔。

while循环

基本格式

while(条件)

语句集;

该语句与For语句同样,当条件为真时,重复循环,不然退出循环。

breakcontinue语句

Java语言相同,使用break语句使得循环从Forwhile中跳出,continue使得跳过循环内剩余的语句而进入下一次循环。

函数

函数为程序设计人员提供了一个丰常方便的能力。一般在进行一个复杂的程序设计时,老是根据所要完成的功能,将程序划分为一些相对独立的部分,每部分编写一个函数。从而,使各部分充分独立,任务单一,程序清晰,易懂、易读、易维护。JavaScript函数能够封装那些在程序中可能要屡次用到的模块。并可做为事件驱动的结果而调用的程序。从而实现一个函数把它与事件驱动相关联。这是与其它语言不样的地方。

JavaScript函数定义

function 函数名 (参数,变元){

函数体;

return 表达式;

}

说明:

 当调用函数时,所用变量都可做为变元传递。

 函数由关键字Function定义。

 函数名:定义本身函数的名字。

 参数表,是传递给函数使用或操做的值,其值能够是常量,变量或其它表达式。

 经过指定函数名(实参)来调用一个函数。

 必须使用Return将值返回。

 函数名对大小写是敏感的。

函数中的形式参数

在函数的定义中,咱们看到函数名后有参数表,这些参数变量多是一个或几个。那么怎样才能肯定参数变量的个数呢?在JavaScript中可经过arguments.length来检查参数的个数。例:

Function function_Name(exp1,exp2,exp3,exp4)

{

Number =function_Name.arguments.length;

if (Number>1

document.wrile(exp2);

if (Number>2)

document.write(exp3);

if(Number>3)

document.write(exp4);

...

}

事件驱动及事件处理

基本概念

JavaScript是基于对象(object-based)的语言。这与Java不一样,Java是面向对象的语言。而基于对象的基本特征,就是采用事件驱动(event-driven)。它是在图形界面的环境下,使得一切输入变的简单化。一般鼠标或热键的动做咱们称之为事件(Event),而由鼠标或热键引起的一连串程序的动做,称之为事件驱动(Event Driver)。而对事件进行处理程序或函数,咱们称之为事件处理程序(Event Handler)。

事件处理程序

JavaScript中对象事件的处理一般由函数(Function)担任。其基本格式与函数所有同样,能够将前面所介绍的全部函数做为事件处理程序。格式以下:

Function 事件处理名(参数表){

事件处理语句集;

……

}

事件驱动

JavaScript事件驱动中的事件是经过鼠标或热键的动做引起的。它主要有如下几个事件:

单击事件onClick

当用户单击鼠标按钮时,产生onClick事件。同时onClick指定的事件处理程序或代码将被调用执行。一般在下列基本对象中产生:

? button(按钮对象)

? checkbox(复选框)或(检查列表框)

? radio (单选钮)

? reset buttons(重要按钮)

? submit buttons(提交按钮)

例:可经过下列按钮激活change()文件:

<Form> <Input type="button" Value="" onClick="change()"></Form>

onClick等号后,能够使用本身编写的函数做为事件处理程序,也能够使用JavaScript中内部的函数。还能够直接使用JavaScript的代码等。例:

<Input type="button" value=" " οnclick=alert("这是一个例子");

onChange改变事件

当利用texttextarea元素输入字符值改变时发该事件,同时当在select表格项中一个选项状态改变后也会引起该事件。

例:

<Form>

<Input type="text" name="test" value="test"

onCharge="check(this.test)">

</Form>

选中事件onSelect

TextTextarea对象中的文字被选中后,引起该事件。

得到焦点事件onFocus

当用户单击Texttextarea以及select对象时,产生该事件。此时该对象成为前台对象。

失去焦点onBlur

text对象或textarea对象以及select对象再也不拥有焦点、而退到后台时,引起该文件,他与onFocas事件是一个对应的关系。

载入文件onLoad

当文档载入时,产生该事件。onLoad一个做用就是在首次载入一个文档时检测cookie的值,并用一个变量为其赋值,使它能够被源代码使用。

卸载文件onUnload

Web页面退出时引起onUnload事件,并可更新Cookie的状态。

实例

下例程序是一个自动装载和自动卸载的例子。即当装入HTML文档时调用loadform()函数,而退出该文档进入另外一HTML文档时则首先调用unloadform()函数,确认后方可进入。

<HTML>

<HEAD>

<script Language="JavaScript">

<!--

function loadform(){

alert("这是一个自动装载例子!");

}

function unloadform(){

alert("这是一个卸载例子!");

}

//-->

</Script>

</HEAD>

<BODY OnLoad="loadform()" OnUnload="unloadform()">

<a href="test.htm">调用</a>

</BODY></HTML>

内容总结

? JavaScript的程序结构:可将<Script>...</Script>标识放入<head>.. </Head><Body> ...</Body>之间。将JavaScript标识放置<Head>

? JavaScript的四种基本数据类型:数值(整数和实数)、字符串型(用“”号或‘’括起来的字符或数值)、布尔型(使TrueFalse表示)和空值。

? 变量的声明及其做用域: 声明用var 变量名;变量的做用域分全局和局部。全局变量是定义在全部函数体以外,其做用范围是整个函数;而局部变量是定义在函数体以内,只对其该函数是可见的,而对其它函数则是不可见的。

? 运算符:JavaScript中有算术运算符,如+、-*/等;有比较运算符如!=、==等; 有逻辑布尔运算符如!(取反)、|||; 有字串运算如+ 、+=等。

? JavaScript脚本语言的基本构成是由流程控制语句、函数、对象、方法、属性等组成。

? 事件驱动及事件处理:事件(Event),由鼠标或热键引起的一连串程序的动做,事件驱动(Event Driver)。对事件进行处理程序或函数,叫事件处理程序(Event Handler

? 常见的事件:onClick onChange onBlur onLoad onUnload

独立实践

? 创建javascript的两种方式?

? js的数据类型有哪些 ''""有什么区别?

? js变量命名区分大小写吗 函数名区分大小写吗?

? 写2javascript函数, loadAlert函数在文档加载时提示你的文档已经加载, unloadAlert在浏览器关闭时提示你的文档已经卸载;

? 演示跑马灯的例子

第二十七章:JavaScript进阶

学习目标

? 掌握JavaScript中基于对象的经常使用内部对象属性、方法的使用

? 掌握自定义对象的建立方法,以及数组的使用方法

? 了解并熟悉JavaScript内部对象系统

? 熟悉并利用窗口对象的输入输出操做

基于对象的JavaScript语言

JavaScript语言是基于对象的(Object-Based),而不是面向对象的(object-oriented)。之因此说它是一门基于对象的语言,主要是由于它没有提供象抽象、继承、重载等有关面向对象语言的许多功能。而把其它语言所建立的复杂对象统一块儿来,从而造成一个很是强大的对象系统。

虽然JavaScript语言是一门基于对象的,但它仍是具备一些面向对象的基本特征。它能够根据须要建立本身的对象,从而进一步扩大JavaScript的应用范围,加强编写功能强大的Web文本文件。

对象的基础知识

对象的基本结构

JavaScript中的对象是由属性(properties)和方法(methods)两个基本的元素的构成的。前者是对象在实施其所须要行为的过程当中,实现信息的装载单位,从而与变量相关联;后者是指对象可以按照设计者的意图而被执行,从而与特定的函数相关联。

引用对象的途径

一个对象要真正地被使用,可采用如下几种方式得到:

? 引用JavaScript内部对象

? 由浏览器环境中提供

? 建立新对象

这就是说一个对象在被引用以前,这个对象必须存在,不然引用将毫无心义,而出现错误信息。

有关对象操做语句

JavaScript不是一个纯面向对象的语言,它设有提供面向对象语言的许多功能,所以JavaScript设计者之因此把它你基于对象而不是面向对象的语言,在JavaScript中提供了几个用于操做对象的语句和关键词及运算符。

For...in语句

格式以下:

For(对象属性名 in 已知对象名)

说明:

该语句的功能是用于对已知对象的全部属性进行操做的控制循环。它将一个已知对象的全部属性反复赋给一个变量;而不是使用计数器来实现的。

该语句的优势就是无需知道对象中属性的个数便可进行操做。

例:下列函数是显示数组中的内容:

Function showData(object)

for (var X=0; X<30;X++)

document.write(object[i])

该函数是经过数组下标顺序值,来访问每一个对象的属性,使用这种方式首先必须知道数组的下标值,不然若超出范围,则就会发生错误。而使For...in语句,则根本不须要知道对象属性的个数,以下面的例子:

Function showData(object)

for(var prop in object)

document.write(object[prop])

使用该函数时,在循环体中,For自动将的属性取出来,直到最后为此。

this关键词

this是对当前对象的引用,在JavaScript因为对象的引用是多层次,多方位的,每每一个对象的引用又须要对另外一个对象的引用,而另外一个对象有可能又要引用另外一个对象,这样有可能形成混乱,最后本身不知道如今引用的那一个对象,为此JavaScript提供了一个用于将对象指定当前对象的语句this

new运算符

虽然在JavaScript中对象的功能已是很是强大的了。但更强大的是设计人员能够按照需求来建立本身的对象,以知足某一特定的要求。使用New运算符能够建立一个新的对象。其建立对象使用以下格式:

Newobject=new Object(Parameters table);

其中Newobject建立的新对象:object是已经存在的对象; parameters table参数表;newJavaScript中的命令语句。

如建立一个日期新对象

newData=new Data()

birthday=new Data(December 12.1998)

以后就可以使NewDatabirthday做为一个新的日期对象了。

对象属性的引用

对象属性的引用可由下列方式之一实现:

使用点(.)运算符

university.Name=“云南省

university.city=“昆明市

university.Date="1999"

其中university是一个已经存在的对象,NameCityDate是它的三个属性,并经过操做对其赋值。

经过对象的下标实现引用

university[0]=“云南

university[1]=“昆明市

university[2]="1999"

经过数组形式的访问属性,能够使用循环操做获取其值。

function showunievsity(object)

for (var j=0;j<2; j++)

document.write(object[j])

若采用For...in则能够不知其属性的个数后就能够实现:

Function showmy(object)

for (var prop in this)

docament.write(this[prop]);

经过字符串的形式实现

university["Name"]=“云南

university["City"]=“昆明市

university["Date"]="1999"

对象的方法的引用

JavaScript中对象方法的引用是很是简单的。

ObjectName.methods()

实际上methods()=FunctionName方法实质上是一个函数。 如引用university对象中的showmy()方法,则可以使用:

document.write(university.showmy())

或:document.write(university)

如引用math内部对象中cos()的方法,以下:

with(math)

document.write(cos(35));

document.write(cos(80));

若不使用with则引用时相对要复杂些:

document.write(Math.cos(35))

document.write(math.sin(80))

经常使用对象的属性和方法

JavaScript为咱们提供了一些很是有用的经常使用内部对象和方法。用户不须要用脚原本实现这些功能。这正是基于对象编程的真正目的。

JavaScript提供了string(字符串)、math(数值计算)和Date(日期)三种对象和其它一些相关的方法。从而为编程人员快速开发强大的脚本程序提供了很是有利的条件。

经常使用内部对象

JavaScript中对于对象属性与方法的引用,有两种状况:其一是说该对象是静态对象,即在引用该对象的属性或方法时不须要为它建立实例;而另外一种对象则在引用它的对象或方法是必须为它建立一个实例,即该对象是动态对象。

JavaScript内部对象的引用,以是牢牢围绕着它的属性与方法进行的。于是明确对象的静动性对于掌握和理解JavaScript内部对象是具备很是重要的意义。

串对象

string对象:内部静态性。

串对象的属性

该对象只有一个属性,即length。它代表了字符串中的字符个数,包括全部符号。例:

mytest="This is a JavaScript"

mystringlength=mytest.length

最后mystringlength返回mytest字符串的长度为20

串对象的方法

string对象的方法共有19个。主要用于有关字符串在Web页面中的显示、字体大小、字体颜色、字符的搜索以及字符的大小写转换。

其主要方法以下:

锚点anchor():该方法建立如用Html文文件中同样的anchor标记。使用anchor如用Html(A Name="")同样。经过下列格式访问:

string.anchor(anchorName)

有关字符显示的控制方法

big()字体显示, Italics()斜体字显示,bold()粗体字显示,blink()字符闪烁显示,small()字符用小体字显示,fixed()固定高亮字显示、fontsize(size)控制字体大小等。

字体颜色方法:fontcolor(color)

字符串大小写转换

toLowerCase()-小写转换,toUpperCase()大写转换。下列把一个给定的串分别转换成大写和小写格式:

string=stringValue.toUpperCasestring=stringValue.toLowerCase

字符搜索:indexOf[charactor,fromIndex]

从指定formIndtx位置开始搜索charactor第一次出现的位置。

返回字符串的一部分字符串:substring(start,end)

start开始到end的字符所有返回。

 

算术函数的math对象

功能:提供除加、减、乘、除之外的一引些自述运算。如对数,平方根等。

静动性:静态对象

主要属性

math中提供了6个属性,它们是数学中常常用到的常数E、以10为底的天然对数LN10、以2为底的天然对数LN23.14159PI1/2的平方根SQRT1-2,2的平方根为SQRT2

主要方法

绝对值:abs()

正弦余弦值:sin(),cos()

反正弦反余弦 :asin(), acos()

正切反正切:tan(),atan()

四舍五入:round()

平方根:sqrt()

基于几方次的值:pow(base,exponent)

...

日期及时间对象

功能:提供一个有关日期和时间的对象。

静动性:动态性,即必须使用New运算符建立一个实例。例:

MyDate=New Date()

Date对象没有提供直接访问的属性。只具备获取和设置日期和时间的方法。

日期起始值:1770年1月1日00:00:00。

获取日期的时间方法

getYear(): 返回年数

getMonth():返回当月号数

getDate(): 返回当日号数

getDay():返回星期几

getHours():返回小时数

getMintes(:返回分钟数

getSeconds():返回秒数

getTime() : 返回毫秒数

设置日期和时间:

setYear();设置年

setDate():设置当月号数

setMonth():设置当月份数

setHours():设置小时数

setMintes():设置分钟数

setSeconds():设置秒数

setTime ():设置毫秒数

...

JavaScript中的系统函数

JavaScript中的系统函数又称内部方法。它提供了与任何对象无关的系统函数,使用这些函数不需建立任何实例,可直接用。

返回字符串表达式中的值:

方法名:eval(字符串表达式),例:

test=eval("8+9+5/2");

返回字符串ASCII码:

方法名:unEscape (string)

返回字符的编码:

方法名:escape(character)

返回实数:

parseFloat(floustring);

返回不一样进制的数:

parseInt(numbestring ,radix)

其中radix是数的进制,numbestring字符串数

建立新对象

使用JavaScript能够建立本身的对象。虽然JavaScript内部和浏览器自己的功能已十分强大,但JavaScript仍是提供了建立一个新对象的方法。使其没必要像超文本标识语言那样,借助其它多媒体工具,就能完成许多复杂的工做。

JavaScript中建立一个新的对象是十分简单的。首先它必须定义一个对象,接着再为该对象建立一个实例。这个实例就是一个新对象,它具备对象定义中的基本特征。

对象的定义

JavaScript对象的定义,其基本格式以下:

Function Object(属性表)

this.prop1=prop1

this.prop2=prop2

...

this.meth=FunctionName1;

this.meth=FunctionName2;

...

在一个对象的定义中,能够为该对象指明其属性和方法。经过属性和方法构成了一个对象的实例。以下是一个关于University对象的定义:

function university(name,city,creatDate,URL)

this.name=name

this.city=city

this.creatDate=New Date(creatDate)

This.URL=URL

其基本含义以下:

Name-指定一个单位名称。

City单位所在城市。

CreatDate-记载university对象的更新日期。

URL-该对象指向一个网址。

建立对象实例

一旦对象定义完成后,就能够为该对象建立一个实例了:

NewObject=New object();

其中Newobjet是新的对象,Object已经定义好的对象。例:

U1=New university(“云南省昆明市"January 05,199712:00:00","http://www.YN.KM")

U2=New university(“云南电子科技大学昆明”,"January 07,1997 12:00:00","htlp://www.YNKJ.CN")

对象方法的使用

在对象中除了使用属性外,有时还须要使用方法。在对象的定义中,咱们看到This.meth=FunctionName语句,那就是为定义对象的方法。实质对象的方法就是一个函数FunctionName,经过它实现本身的意图。

例在university对象中增长一个方法,该方法是显示它本身自己,并返回相应的字串。

<script>

function university(name,city,createDate,URL)

{

this.name=name;

this.city=city;

this.date=new Date(createDate);

this.URL=URL;

this.show=showuniversity;

this.toString=parseToString;

}

function showuniversity()

{

for(var prop in this)

{

document.write(prop + "=" + this[prop] + "<BR>");

}

}

function parseToString()

{

return "name:"+ this.name + "<br>city:" + this.city + "<br>date:" + this.date;

}

U2=new university("深圳计算机行业协会","深圳","January 07,1997 12:00:00","htlp://www.5itjob.CN");

U2.show();

document.write(U2);

</script>

其中this.show就是定义了一个方法showuniversity()

showuniversity()方法是实现university对象自己的显示。

察看对象全部属性的方法

function showProp(object)

{

for(var prop in object)

{

document.write(prop + " = " + object[prop] + "<br>");

}

}

howProp(document);

JavaScript中的数组

? 使用New建立数组

? JavaScript中没有提供像其它语言具备明显的数组类型,但能够经过function定义一个数组,并使用New对象操做符建立一个具备下标的数组。从而能够实现任何数据类型的存储。

定义对象的数组

function arrayName(size){

this.length=size;

for(var X=1; X<=size;X++)

this[X]=0;

reture this;

}

其中arrayName是定义数组的一个名子,Size是有关数组大小的值

从中能够看出,JavaScript中的数组是从1size,这与其它0到size的数组表示方法有所不一样,固然你可根据须要将数组的下标由1到size调整到0size-1,可由下列实现:

function arrayName (size)

for (var X=0; X<size;X++)

this[X]=0;

this.lenght=size;

return this;

从上面能够看出该方法是只是调整了this.length的位置,该位置是用于存储数组的大小的。从而调整后的数组的下标将与其它语言一致。但请读者注意正是因为数组下标顺序由1到size,使得JavaScript中的对象功能更增强大。

建立数组实例

一个数组定义完成之后,还不能立刻使用,必须为该数组建立一个数组实例:

Myarray=new arrayName(n);

并赋于初值:

Myarray[1]=“字串1

Myarray[2]=“字串2

Myarray[3]=“字串3

Myarray[n]=“字串n”

一旦给数组赋于了初值后,数组中就具备真正意义的数据了,之后就能够在程序设计过程当中直接引用。

建立多维数组

function creatMArray(row,col){

var indx=0;

this.length=(row*10)+col

for(var x=1;x<=row;x++)

for(var y=1;y<=col;y++)

indx=(x*10)+y;

this[indx]=””;

}

myMArray=new creatMArray();

以后可经过myMArray[11]myMArray[12]myMArray[13]myMArray[21]

myMArray[22]myMArray[23]

实例

颜色变化的例子。

<html>

<head>

<script>

<!--

function makearray(n) {

this.length = n;

for(var i = 1; i <= n; i++)

this[i] = 0;

return this;

}

hexa = new makearray(16);

function init()

{

for(var i = 0; i < 10; i++)

hexa[i] = i;

hexa[10]="a";

hexa[11]="b";

hexa[12]="c";

hexa[13]="d";

hexa[14]="e";

hexa[15]="f";

}

function hex(i) {

if (i < 0)

return "00";

else if (i > 255)

return "ff";

else return "" + hexa[Math.floor(i/16)] + hexa[i%16];

}

function setbgColor(r, g, b) {

var hr = hex(r);

var hg = hex(g);

var hb = hex(b);

document.bgColor = "#"+hr+hg+hb;

}

function fade(sr, sg, sb, er, eg, eb, step) {

for(var i = 0; i <= step; i++) {

setbgColor( Math.floor(sr * ((step-i)/step) + er * (i/step)),

Math.floor(sg * ((step-i)/step) + eg * (i/step)), Math.floor(sb *

((step-i)/step) + eb * (i/step)));

}

}

function fadein() {

fade(255,0,0,0,0,255,100);

fade(0,0,255,0,255,0,100);

fade(0,255,0, 0,0,0, 100);

}

init();

fadein();

// -->

</script>

<body>

</body>

</html>

使用内部对象系统

使用浏览器的内部对象系统, 可实现与HTML文档进行交互。它的做用是将相关元素组织包装起来,提供给程序设计人员使用,从而减轻编程人的劳动,提升设计Web页面的能力。

浏览器对象层次及其主要做用

除了前面提到过的文档document对象外,Navigator浏览器中还提供了窗口(Window)对象以及历史(History)和位置(Location)对象。

? 浏览器对象(navigator)

提供有关浏览器的信息

? 窗口对象(Windows)

Window对象处于对象层次的最顶端,它提供了处理Navigator窗口的方法和属性。

? 位置对象(location)

Location对象提供了与当前打开的URL一块儿工做的方法和属性,它是一个静态的对象。

? 历史对象(history)

history对象提供了与历史清单有关的信息。

? 文档对象(document)

document对象包含了与文档元素(elements)一块儿工做的对象,它将这些元素封装起来供编程人员使用。

编程人员利用这些对象,能够对WWW浏览器环境中的事件进行控制并做出处理。在JavaScript中提供了很是丰富的内部方法和属性,从而减轻了编程人员的工做,提升编程效率。这正是基于对象与面向对象的根本区别所在。在这些对象系统中,文档对象属于很是重要的,它位于最低层,但对于咱们实现Web页面信息交互起做关键做用。于是它是对象系统的核心部分。

文档对象功能及其做用

Navigator浏览器中,document文档对象是核心是,同时也是最重要的。见图:

Links Anchor Form Method Prop

连接对象 锚对象 窗体对象 方法 对象

从图中能够看出,document对象的主要做用就是把这些基本的元素(如links,anchor等)包装起来,提供给编程人员使用。从另外一个角度看,document对象中又是由属性和方法组成。

document中三个主要的对象

document中主要有:links,anchor,form等三个最重要的对象:

? anchor锚对象:

anchor对象指的是<a name=...> </a>标识在HTML源码中存在时产生的对象。它包含着文档中全部的anchors信息。

? 连接links对象

link对象指的是用<a href=...> </A>标记的链接一个超文本或超媒体的元素做为一个特定的URL

? 窗体(form)对象

窗体对象是文档对象的一个元素,它含有多种格式的对象储存信息,使用它能够在JavaScript脚本中编写程序进行文字输入,并能够用来动态改变文档的行为。经过document.forms[]数组来使得在同一个页面上能够有多个相同的窗体,使用forms[]数组要比使用窗体名字要方便得多。

:下面就是一个使用窗体数组和窗体名字的例子。该程序使得两个窗体中的字段内容保持一致。

<Html>

<head>

</head>

<body>

<form>

<input type=text onChange="document.my.elements[0].value=this.value;">

</form>

<form NAME="my">

<input type=text

onChange="document.forms[0].elements[0].value=this.value;">

</form>

</body>

</html>

其中用了OnChnge事件(当窗体内容改变时激发)。第一个使用窗体名字标识my,第二个使用窗体数组Forms[]。其效果是一致。

文档对象中的attribute属性

document对象中的attribute属性,主要用于在引用Href标识时,控制着有关颜色的格式和有关文档标题、文档原文件的URL以及文档最后更新的日期。这部分元素的主要含义以下:

? 连接颜色:alinkcolor

这个元素主要用于,当选取一个连接时,连接对象自己的颜色就按alinkcolor指定改变。

? 连接颜色:linkcolor

当用户使用<A Href=...> Text string </A>连接后,Textstring的颜色就会按Linkcolor所指定的颜色更新。

? 浏览事后的颜色:VlinkColor

该属性表示的是已被浏览存储为已浏览过的连接颜色。

? 背景颜色:bgcolor

该元素包含文档背景的颜色。

? 前景颜色:fgcolor

该元素包含HTML文档中文本的前景颜色。

文档对象的基本元素

? 窗体属性:

窗体属性是与HTML文档中<Form>...</Form>相对应的一组对象在HTML文档所建立的窗体数,由length指定。经过document.forms.length反映该文档中所建立的窗体数目。

? 锚属性:anchors

该属性中,包含了HTML文档的全部<A> </A>标记为Name=...的语句标识。

全部的数目保存在document.anchors.length中。

? 连接属性:links

连接属性是指在文档中<A>...</A>的由Href=...指定的数目,其连接数目

保存在document.links.length中。

实例

下面咱们经过一个例子来讲明文档对象的综合应用。

<html>

<head>

</HEAD>

<BOdy>

<Form Name="mytable">

请输入数据:

<Input Type="text" Name="text1" Value="">

</Form>

<A name="Link1" href="test31.htm">连接到第一个文本</a>

<br>

<A name="Link2" href="test32.htm">连接到第二个文本</a>

<br>

<A name="Link2" href="test33.htm">连接到第三个文本</a>

<br>

<A href="#Link1">第一锚点</a>

<A href="#Link2">第二锚点</a>

<A Href="#Link3">第三锚点</a>

<BR>

<Script Language="JavaScript">

document.write("文档有"+document.links.length+"个连接"+"<br>");

document.write("文档有"+document.anchors.length+"个锚点"+"<br>");

document.write("文档有"+document.forms.length+"个窗体");

</script>

</body>

</HTML>

下列程序随机产生每日一语。

<HTML>

<HEAD>

<script Language="JavaScript">

<!--

tips = new Array(6);

tips[0]="每日一语(1";

tips[1]="每日一语(2";

tips[2]="每日一语(3";

tips[3]="每日一语(4";

tips[4]="每日一语(5";

tips[5]="每日一语(6";

index = Math.floor(Math.random() * tips.length);

document.write("<FONT SIZE=8 COLOR=DARKBLUE>" + tips[index]+"</FONT>");

</Script>

</HEAD>

<BODY>

</BODY>

</HTML>

窗口及输入输出

JavaScript是基于对象的脚本编程语言,那么它的输入输出就是经过对象来完成的。其中有关输入可经过窗口(Window)对象来完成,而输出可经过文档(document)对象的方法来实现。

窗口及输入输出

请看下面例子:

<HTML>

<Head>

<script languaga="JavaScript">

var test=window.prompt("请输入数据:");

document.write(test+"JavaScript输入输出的例子");

</script>

</Head>

</HTML>

其中window.prompt()就是一个窗口对象的方法,其基本做用是,当装入Web页面时在屏幕上显示一个具备肯定取消的对话框,让你输入数据。

窗口对象

该对象包括许多有用的属性、方法和事件驱动程序,编程人员能够利用这些对象控制浏览器窗口显示的各个方面,如对话框、框架等。在使用应注意如下几点:

? 该对象对应于HTML文档中的<Body><FrameSet>两种标识;

? onloadonunload都是窗口对象属性;

? 在JavaScript脚本中可直接引用窗口对象。如: window.alert("窗口对象输入方法") 可直接使用如下格式: alert("窗口对象输入方法")

窗口对象的事件驱动

窗口对象主要有装入Web文档事件onload和卸载时onunload事件。用于文档载入和中止载入时开始和中止更新文档。

窗口对象的方法

窗口对象的方法主要用来提供信息或输入数据以及建立一个新的窗口。

? 建立一个新窗口open()

使用window.open(参数表)方法能够建立一个新的窗口。其中参数表提供有窗口的主要特性和文档及窗口的命名。

? 具备OK按钮的对话框

alert()方法能建立一个具备OK按钮的对话框。

? 具备OKCancel按钮的对话框

confirm()方法为编程人员提供一个具备两个按钮的对话框。

? 具备输入信息的对话框

prompt()方法容许用户在对话框中输入信息,并可以使用默认值,其基本格式以下prompt提示信息,默认值)。 

窗口对象中的属性

窗口对象中的属性主要用来对浏览器中存在的各类窗口和框架的引用,其主要属性有如下几个:

? frames 确文档中帧的数目

frames(帧)做为实现一个窗口的分隔操做,起到很是有用的做用,在使用注意如下几点:

frames属性是经过HTML标识<Frames>的顺序来引用的,它包含了一个窗口中的所有帧数。

帧自己已经是一类窗口,继承了窗口对象全部的所有属性和方法。

? parent 指明当前窗口或帧的父窗口。

? defaultstatus:默认状态,它的值显示在窗口的状态栏中。

? status:包含文档窗口中帧中的当前信息。

? top:包括的是用以实现全部的下级窗口的窗口。

? window.指的是当前窗口

? self:引用当前窗口。

输出流及文档对象

JavaScript文档对象中,提供了用于显示关闭、消除、打开HTML页面的输出流。

建立新文档open()方法

使用document.open()建立一个新的窗口或在指定的命令窗口内打开文档。因为窗口对象是所加载的父对象,于是咱们在调用它的属性或方法时,不须要加入Window对象。例用Window. open()open()是同样的。

打开一个窗口的基本格式:

Window .open("URL","窗口名字","窗口属性"]

window属性参数是由一个字符串列表项它由逗号分隔,它指明了有关新建立窗口的属性。

参 数 设定值 含 义

toolbar yes/no 创建或不创建标准工具条

location yes/no 创建或不创建位置输入字段

directions yes/no 创建或不创建标准目录按钮

status yes/no 创建或不创建状态条

menubar yes/no 创建或不创建菜单条

scrollbar yes/no 创建或不创建滚动条

revisable yes/no 可否改变窗口大小

width yes/no 肯定窗口的宽度

Height yes/no 肯定窗口的高度。

在使用Open()方法时,须要注意如下点。

? 一般浏览器窗中,总有一个文档是打开的。于是不须要为输出创建一个新文档。

? 在完成对Web文档的写操做后,要使用或调用close()方法来实现对输出流的关闭。

? 在使用open()来打开一个新流时,可为文档指定一个有效的文档类型,有效文档类型包括text/HTMLtext/giftext/ximtext/plugin等。

write()writeln()输出显示。

该方法主要用来实如今Web页面上显示输出信息。在实际使用中,需注意如下几点:

? writeln()write()惟一不一样之处在于在未尾加了一个换符。

? 为了正常显示其输出信息,必须指明<pre> </Pre>标记,使之告诉编辑器。

? 输出的文档类型,能够由浏览器中的有效的合法文本类型所肯定。

关闭文档流close()

在实现多个文档对象中,必须使用close()来关闭一个对象后,才能打开另外一个文档对象。

清除文档内容clear()

使用该方法可清除已经打开文档的内容。

简单的输入、输出例子

JavaScript中能够很是方便地实现输入输出信息,并与用户进行交互。

JavaScript信息的输入

经过使用JavaScript中所提供的窗口对象方法prompt(), 就能完成信息的输入。该方法提供了最简便的信息输入方式,其基本格式以下:

Window.prompt("提示信", 预约输入信息);

此方法首先在浏览器窗口中弹出一个对话框, 让用户自行输入信息。一旦输入完成后,就返回用户所输入信息的值。例: test=prompt请输入数据:””this is a JavaScript”

实际上prompt()是窗口对象的一个方法。由于缺省状况下所用的对象就是window对象, 因此windows对象能够省略不写。

输出显示

每种语言,都必须提供信息数据的输出显示。JavaScript也是同样,它提供有几个用于信息输出显示的方法。比较经常使用的有window.alert()document.write和及document.writln()方法。

document.write()方法和document.writeln()方法

documentJavaScript中的一个对象在它中封装许多有用的方法,其中write()writeln()就是用于将文本信息直接输出到浏览器窗口中的方法。 document.write()

document.writeln()

说明:

write()writeln()方法都是用于向浏览器窗口输出文本字串;

两者的惟一区别就是writeln()方法自动在文本以后加入回车符。

window.alert()输出

JavaScript为了方便信息输出,JavaScript提供了具备独立的对话框信息输出─alert()方法。

alert()方法是window对象的一个方法,所以在使用时,不须要写window窗口对象名,而是直接使用就好了。它主要用途用在输出时产生有关警告提示信息或提示用户,一旦用户按肯定钮后,方可继续执行其余脚本程序。例:

<HTML>

<HEAD>

<TITLE></TITLE>

</HEAD>

<BODY>

<Script Language="JavaScript">

alert("这是一个JavaScript测试程序");

</Script>

</BODY>

</HTML>

利用输入、输出方法实现交互

JavaScript中,能够利用prompt()方法和write()方法实现与Web页面用户进行交互。例下面就是一个有关实现交互的例子。

Test7_1.htm

<HTML>

<HEAD>

<TITLE></TITLE>

</HEAD>

<BODY>

<Script Language="JavaScript">

<!-- Hide From Other Browsers

document.write("<H1>有关交互的例子");

my=prompt("请输入数据:");

document.write(my+"</H1>");

document.close();

// Stop Hiding from Other Browsers-->

</Script>

</BODY>

</HTML>

从上面程序能够看出:

可经过write()prompt()方法实现交互。

JavaScript脚本语言中能够使用HTML标识语言的代码。从而实现混合编程。其中<H1><Br>就是HTML标识符。

Window案例

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=gb2312">

<title>test</title>

<script>

var popUpWin=0;

function popUpWindow(URLStr, left, top, width, height)

{

if(popUpWin)

{

if(!popUpWin.closed) popUpWin.close();

}

popUpWin = open(URLStr, 'popUpWin', 'toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbar=no,resizable=no,copyhistory=yes,width='+width+',height='+height+',left='+left+', top='+top+',screenX='+left+',screenY='+top+'');

}

</script>

</head>

<body>

<input type="button" value="delete" onClick="confirm('delete');">

<input type="button" value="open" onClick="popUpWindow('login.htm',200,200,400,200);">

</body></html>

内容总结

? JavaScript的对象是由属性(properties)和方法(methods)两个基本的元素的构成的,引用对象的途径:引用JavaScript内部对象;由浏览器环境中提供;建立新对象

? 对象操做语句:For...in语句 this new 点(.)运算符

? JavaScript经常使用对象:string(字符串)、math(数值计算)和Date(日期)三种对象

? 建立新对象首先用function定义一个对象,接着用new 为该对象建立一个实例,经过.运算符访问对象属性和方法。

? JavaScript中的数组:function定义一个数组,并使用New对象操做符建立一个具备下标的数组,经过数组名[下标]访问。

? 浏览器对象层次:文档document对象、窗口(Window)对象、历史(History)和位置(Location)对象

? 重点掌握:document对象的层次结构;window对象的alertpromptconfirm方法

独立实战

? 建立Date对象的一个实例,在一个网页中显示当前的日期(年月日 时分秒)

? 自定义一个student对象,它有name,native,age三个属性,建立该对象的三个实例,在网页中输出

? 弹出一个输入的对话框,输入"javaand java World",把输入的值保存到myvar中,在myvar中统计java出现的次数,并取出第三个到第十个字符,输出到文档

? 网页中制做一个按纽,弹出一个登录对话框(无工具条、无状态栏、无菜单、无滚动条,长300,宽300

? 随机显示img下的一张图片

? 用户登录网页有两个输入框,用javascript实现验证功能(这两个输入框内容不能为空,不然提示请输入值)

第二十八章: Servlet

学习目标

? Java Servlet概述

? Java Servlet API

? HTTP协议简介

? Servlet过滤器

? Servlet监听器

Java Servlet概述

Web刚刚开始用于提供服务,服务供应商们就意识到动态内容的需求。最先朝这个方向发展的技术之一Applet,专一于利用客户平台提供动态的用户体验。与此同时,开发人员也开始研究使用服务器平台达到一样的目的。最初,通用网关接口(CGI)脚本是产生动态内容的主要技术。尽管CGI脚本技术被普遍使用,它却存在着许多缺陷,包括平台相关和缺乏控制性。为了解决这些缺陷,出现了Java Servlet技术。它以可移植的方式提供了动态的,基于用户的网页内容。

Java Servlet是用Java编程语言实现的类。它扩展了经过请求——响应模式访问的应用程序的服务器端的性能。尽管Servlet能够响应任何类型的请求,但一般它们用来扩展Web服务器端的应用程序。对于这样的应用,Java Servlet技术定义了专用于HTTP协议的Servlet类。类包javax.servletjavax.servlet.http提供了编写Servlet的接口和类。全部的Servlet必须实现定义了生命周期方法的Servlet接口。当实现通用服务时,能够使用或扩展由Java Servlet API提供的GenericServlet类。HttpServlet类提供了像doGetdoPost这样专门用于处理HTTP服务的方法。

Servlet可以作什么?

Servlet是用Java代码编写的服务器方软件程序,用于处理客户机和服务器之间的消息传递。Java Servlet API为请求和响应消息定义了一个标准接口,这样Servlet就能够跨平台和跨不一样的Web应用服务器间移植。

Servlet能够经过动态构造一个发回客户机的响应来响应客户机请求。例如:下面是一个响应HTTP请求的Servlet.源代码以下:

package com.servlet;

import java.io.IOException;

import java.io.PrintWriter;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

public class HelloServlet extends HttpServlet {

public HelloServlet() {

super();

}

public void destroy() {

super.destroy(); // Just puts "destroy" string in log

// Put your code here

}

public void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

response.setContentType("text/html");

PrintWriter out = response.getWriter();

out

.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");

out.println("<HTML>");

out.println(" <HEAD><TITLE>A Servlet</TITLE></HEAD>");

out.println(" <BODY>");

out.print(" This is ");

out.print(this.getClass());

out.println(", using the GET method and say you hello");

out.println(" </BODY>");

out.println("</HTML>");

out.flush();

out.close();

}

public void doPost(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

doGet(request, response);

}

public void doPut(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

// Put your code here

}

public void init() throws ServletException {

// Put your code here

}

}

Web.xml配置文件的配置以下:

<?xml version="1.0" encoding="UTF-8"?>

<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee

http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

<servlet>

<description>

This is the description of my J2EE component

</description>

<display-name>

This is the display name of my J2EE component

</display-name>

<servlet-name>HelloServlet</servlet-name>

<servlet-class>com.servlet.HelloServlet</servlet-class>

</servlet>

<servlet-mapping>

<servlet-name>HelloServlet</servlet-name>

<url-pattern>/servlet/HelloServlet</url-pattern>

</servlet-mapping>

</web-app>

在浏览器里输入:

http://localhost:818/beike/servlet/HelloServlet

运行结果以下图

因为Servlet是用Java编程语言编写的,它们能够访问整个Java API集合。这就使它们很是适合实现复杂的商业应用逻辑,特别是访问企业中其它位置的数据。Java Database Connection(JDBC) API就是一个示例,它容许Java程序访问关系数据库。通常咱们不直接在Servelt里面写JDBC编程,而是经过链接池技术或者Hibernate技术来实现JDBC的链接,后面会有综合的例子。因为没有与Servlet关联的图形,所以它不适合访问GUI Java APIAWT/Swing

能够屡次调用一个Servlet来响应来自多个客户机的请求。这是一种多线程的工做方式,该Servlet只有一个实例,多个用户经过多线程的访问,来响应给客户的。所以,一个Servlet能够同时处理多个请求,而且能够使这些请求同步。Servlet能够将请求转发到其它服务器和Servlet.

Servlet如何工做?

ServletJ2EEWeb服务器(Web Container)中运行。而应用服务器是一种特殊的Web服务器;它们扩展了Web服务器的功能,还能够处理Enterprise BeansWeb应用程序的请求。Web服务器和应用服务器之间有明显的差别。虽然二者均可以在同一台机器上运行,Web服务器运行的是Servlet代码。服务器本身装入,执行和管理Servlet。服务器使用Java字节码解释器来运行Java程序;这叫作Java虚拟机(JVM)。大型的应用服务器会自带虚拟机,好比:Weblogic。而小型的Web服务器须要使用操做系统里安装好的虚拟机,好比:tomcat

Servlet的生命周期。

Servlet执行的步骤以下:

1 客户机将请求发送给服务器。

2 服务器从浏览器的地址栏得到请求的信息。并根据Web.xml配置文件找到响应的Servet执行。若是找不到,会报404错误。

3 若是是第一次请求,那么会实例化该Servlet,调用init()方法进行初始化,该方法在Servlet 的生命周期里只执行一次。而后分配线程进行响应。若是不是第一次访问,那么会直接分配个线程进行客户的响应。

4Servlet响应以前,服务器会产生request,response对象,而且把客户请求的信息封装到request对象中,而后把这两个对象传递给Servletservice()方法执行。

5 service()根据请求的方式来调用不一样的方法执行。好比get请求,service()方法会request,response对象传递给doGet()方法执行,把执行后的结果保存到response对象里面。返回给客户。

6 服务器关闭后,会调用Servletdestroy()方法进行销毁。

下面是Servetl执行的线程执行图:

ServletCGI程序的区别:

CGICommon Gateway Interface)公共网关接口程序也是用来建立响应请求的动态Web内容的。但与CGI相比,Servlet还有本身的优点。Servlet可以提供一个基于组件的,独立于平台的方法来建立Web应用,也没有通常CGI程序的性能限制。Servlet具备如下几个特色:

◆ 可跨平台,跨不一样Web服务器进行移植。Servlet使你可以不用编写指定平台的API就能进行服务器端编程。Java Servlet API是标准Java的延伸。

◆ 具备持久性(Persistent)。一个Servlet在装入内存以后,就一直驻留内存,这意味着它可以在请求之间维持系统资源如数据库链接。

◆ 高效的(Efficient)。当一个客户对某个Servlet发出多个请求时,服务器只建立并装入该Servlet一次。每一次重复请求只不过完成商业逻辑处理。CGI过程则对每一个请求都装入一个过程,这会大大下降性能。并且,JVM使用lightweight java thread来处理对Servlet的请求,而不是像CGI那样使用一个weighty操做系统进程。

◆ 可以将表示(presentation)与商业逻辑(business logic)分离。这样一来,将一个项目划分为各个独立部分来进行开发和维护变得容易得多了。

◆ 可以获取给定的HTTP的各类调用而且从Java语言自己的继续开发中获益。

Java Servlet API

什么是Java Servlet API? Java Servet API 是一组类,用于定义Web客户机和Web Servlet之间的标准接口。其实,API将请求封装成对象,这样服务器能够将它们传递到Servlet;响应也是这样的封装,所以服务器能够将它们传递回客户机。

Java Servlet API有两个包。Javax.servlet包含了支持普通协议无关的Servlet的类,javax.servlet.http包括了对HTTP协议的特别支持。本课主要介绍HTTP Servlet

Web上使用的HTTP Servlet

Servlet接口类是Java Servlet API的重要抽象概念。这个类定义了管理Servlet以及它与客户机通讯的方法。要编写在Web上使用的HTTP Servlet,使用HttpServlet类:

◆ HttpServletRequest对象表明发送到Servlet的客户机请求。这个对象封装了从客户机到服务器的通讯。它能够包含关于客户机环境的信息和任何要从Servlet发送到客户机的数据。

◆ HttpServletResponse对象表明从Servlet发回客户机的响应。这一般是动态生成的响应,如HTML页面,而且它是用请求和Servlet访问的其它来源中的数据建立的。

处理HTTP Servlet的关键方法

HttpServlet的子类必须至少覆盖一个方法。一般,Servlet覆盖doGetdoPost方法。GET请求是对Web页面的典型浏览器请求,它是在用户输入URL或使用连接时发出的。POST请求是用户提交指定发出的HTML表单时生成的。HTTP POST方法容许客户机一次将无限长度的数据发送到Web服务器,而且能够在发出信息时使用。

同一个Servlet能够经过让doGet调用doPost,或者反向调用来处理GETPOST

其它经常使用方法包括:

? Servletservice()方法

? Servlet容器激活了Servletservice()方法,传递HttpServletRequestHttpServletResponse对象同HTTP请求和响应进行交互,service()方法从请求对象中获得必要的信息处理请求,而后使用响应对象的方法建立客户响应根据 HTTP传输方法,service()方法把请求发送给另外一个方法,GET请求发送给doGet()方法,POST请求发送给doPost()方法方法之间能够相互调用,doPost()能够调用doGet()方法,大多数人直接调用doGetdoPost

? doPut方法,用于HTTP PUT请求

? doDelete方法,用于HTTP DELETE请求

? init destroy,用于管理为Servlet的使用期限而保留的资源。

? getServletInfo,Servlet用来提供自身信息的方法。

其它相关接口的说明:

javax.servlet.ServletConfig接口:当Servet第一次被装载时,为了向它传递服务设置信息,容器开发者必须实现该接口。

javax.servlet.ServletContext接口:提供给Servlet一些访问它们所运行环境的方法,并容许记录一些重要的事件,由Servlet的编写者来决定记录什么样的数据。

下图是servlet UML

HTTP协议简介

大部分J2EE Web客户端使用HTTP协议和J2EE服务器通信。HTTP协议定义了客户端能够发送给服务器的请求和服务器能够做为回答的响应。每一个请求包含一个URLURL是一个字符串,标识了Web组件或者像HTML页面和图象文件这样的静态资源。

J2EE服务器将HTTP请求转化为一个HTTP请求(Request)对象,并把这个对象传递给URL标识的Web组件。Web组件填充一个HTTP响应(Response)对象,服务器把这个对象转化为HTTP响应,并发送给客户端。

HTTP协议基本概念及其特色

HTTPHyperText Transfer Protocol)是超文本传输协议的简称,是WWW上用于发布信息的主要协议。也能够理解为:在TCP/IP之上的WebRPC(Remote Process Call远程过程调用)。关于HTTP协议更详细的信息,请登录www.w3.org访问。

HTTP定义了一个客户机/服务器结构的简单事务处理,这里的客户机/服务器也就是浏览器/Web 服务器。简单事务处理由如下几步组成:

客户与服务器创建链接

客户向服务器提交请求

若是请求被接受,那么服务器回送一个应答,应答中至少包括状态编码和该文件的内容。

客户或服务器断开链接。

HTTP的基本特色是:

简单。服务器迅速做出浏览器的应答。

无状态。一个请求到另外一个请求不保留任何有关链接的信息。

灵活。容许传送任意类型的数据对象。

无链接。HTTP是一个无链接协议。

HTTP的缺点是每次链接HTTP只完成一次请求。若服务器的一个HTML文件中有许多图象,每传一个图象都要单独创建一次链接。

一个HTTP请求包含请求方法(Request Method),请求URL,头字段(Header Field)和请求体。HTTP1.1定义了下面的请求方法:

GET:获取由请求URL标识的资源。

HEAD:返回由URL标识的头信息。

POST:想Web服务器发送无限制长度的数据。

PUT:存储一个资源到请求的URL

DELETE:删除由URL标识的资源。

OPTIONS:返回服务器支持的HTTP方法。

TRACE:返回TRACE请求附带的头字段。

一个HTTP响应包括响应码,头字段和响应体。HTTP协议要求响应码和全部的头字段都在任何响应体以前返回。

下面是一些经常使用的状态码:

404:指示请求的资源不可用。

401:指示请求须要HTTP验证。

500:指示在HTTP服务器内部发生错误,不能执行请求。

503:指示HTTP服务器暂时性超载,不能处理当前请求。

HTTP请求报头--Resquest

HttpServletRequest对象封装了来自客户端的所有信息. Servlet容器获得一个请求的时候,创建了一个该类型的对象,并传递给Servlet获取参数方法.

public String getParameter(String name)

public Enumeration getParameterNames()

public String[] getParameterValues(String name)

获取内容信息

getCharacterEncoding()方法返回请求的名称和字符编码风格

用法:request.getCharacterEncoding()

public int getContentLength()方法可返回以字节记数的内容长度,若是长度未知,则返回-1

用法:request.getContentLength()

getContentType()方法返回请求内容的MIME类型,未知返回null

用法:request.getContentType()

获取链接信息

getProtocol()方法获取传输协议及版本

用法:request.getProtocol() (HTTP/1.1)

getRemoteAddr()方法返回因特网协议(IP)地址

用法:request.getRemoteAddr() (122.40.18.09)

getRemoteHost()方法返回主机名

用法:request.getRemoteHost() (122.40.18.09)

getScheme()方法返回模式名

用法:request.getScheme() (http)

getServerName()方法返回服务器名

用法:request.getServerName() (localhost)

getServerPort()方法返回服务器端口号

用法:request.getServerPort() (80)

获取Cookie

如下代码显示了所有可用的Cookie

Cookie[] cookies=request.getCookies();

int cookiesLen=cookies.length()

if(cookiesLen>0){

for(int i=0 ; i<cookiesLen; i++)

{

String sName=cookies[i].getName();

String sValue=cookies[i]=getValue();

}

}

获取用户请求信息

req.getAuthtype()方法返回认证模式

req.getMethod()方法返回HTTP方法

req.getPathInfo()方法返回路径信息

req.getPathTranslated()方法返回真实传输路径

req.getQueryString()方法返回请求索引串

req.getRequestURI()方法返回请求URI

req.getServletPath()方法返回Servlet路径

req.getAttributeNames()方法返回请求属性集

req.getAttribute(String name)方法返回属性name的信息

HTTP响应报头--Response

HttpServletResponse对象封装了同客户的所有通讯,并提供了多种方法,可以访问和操做HTTP报头,属性等

常见的方法

setHeader()方法

addCookie()

sendError()

setContentType()

会话管理

因为HTTP是无状态协议,服务器没法识别来自相同客户端请求的顺序,所以须要采起专门的方法识别该类用户

1.隐藏的表单字段:<input name=“id” type=“hidden” value=“005”>

2.改写URL:向地址栏内URL后追加信息

http://localhost/servlets/srvlt?sql==1009&id=005

以上两个信息能够用request.getParameter(“id”)取出

3. 持久CookieCookie

? 创建Cookie

Date dtLogin=new Date();

? Cookie coLt=new Cookie(“loginTime”,dtLogin.toString());

? response.addCookie(coLt);// 发送给客户端的响应中包含该Cookie

? 访问Cookie,获得用户登陆的最后时间

?? for(int i=0; i<reqCookies.length; i++){

? sName=reqCookies[i].getName();

? if(sName!=null && sName.equalsIgnoreCase(“loginTime”))

? sValue=reqCookies[i].getValue();

? }

3.会话跟踪APIHttpSession接口

HttpSession session=request.getSession();

使用HttpSessionsetAttribute()getAttribute()方法把一个属性同会话联系起来

发送请求:可以向另外一个Servlet发送请求

ServletContext sc=this.getServletContext();

RequestDispatcher rd=sc.getRequestDispatcher(‘/srvltCom’); If(rd!=null){

try{

rd.forward(req,res); rd.include(req,res);

}

Catch(Exception e){} }

Servlet过滤器

Servlet过滤器是Servlet的一种特殊用法,主要用来完成一些通用的操做。好比编码的过滤,判断用户的登录状态等等。

Servlet过滤器的适用场合:

A.认证过滤

B.登陆和审核过滤

C.图像转换过滤

D.数据压缩过滤

E.加密过滤

F.令牌过滤

G.资源访问触发事件过滤

HXSL/T过滤

I.多用途INTERNET邮件扩展(Mime)类型过滤

Servlet过滤器接口的构成:

全部的Servlet过滤器类都必须实现javax.servlet.Filter接口。这个接口含有3个过滤器类必须实现的方法:

A.init(FilterConfig)

这是Servlet过滤器的初始化方法,读取web.xml文件中Servlet过滤器的初始化参数

B.doFilter(ServletRequest,ServletResponse,FilterChain):完成实际的过滤操做,当请求访问过滤器关联的URL时,Servlet容器将先调用过滤器的doFilter方法。FilterChain参数用于访问后续过滤器

C.destroy()

Servlet容器在销毁过滤器实例前调用该方法,这个方法中能够释放Servlet过滤器占用的资源

Servlet过滤器的建立步骤:

A.实现javax.servlet.Filter接口的servlet

B.实现init方法,读取过滤器的初始化函数

C.实现doFilter方法,完成对请求或过滤的响应

D.调用FilterChain接口对象的doFilter方法,向后续的过滤器传递请求或响应

E.销毁过滤器

F.web.xml中配置Filter

Servlet过滤器的执行流程图以下:

请看下例进行编码的过滤器:CodeFilter.java

package com.filter;

import java.io.IOException;

import javax.servlet.Filter;

import javax.servlet.FilterChain;

import javax.servlet.FilterConfig;

import javax.servlet.ServletException;

import javax.servlet.ServletRequest;

import javax.servlet.ServletResponse;

public class CodeFilter implements Filter {

FilterConfig config = null;

public void destroy() {

// TODO Auto-generated method stub

config = null;

}

public void doFilter(ServletRequest arg0, ServletResponse arg1,

FilterChain arg2) throws IOException, ServletException {

// 设置请求和响应的字符编码格式为gb2312

arg0.setCharacterEncoding("gb2312");

arg1.setCharacterEncoding("gb2312");

System.out.println("already code filter.....");

arg2.doFilter(arg0, arg1);

}

public void init(FilterConfig arg0) throws ServletException {

// TODO Auto-generated method stub

config = arg0;

}

}

Web.xml配置文件中的主要信息为:

<filter>

<filter-name>code</filter-name>

<filter-class>com.filter.CodeFilter</filter-class>

</filter>

<filter-mapping>

<filter-name>code</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>

能够看到上面的配置信息和Servlet的配置格式彻底同样。由以上信息可知,当浏览器输入访问该Web应用程序的Web组件(Servlet,Jsp),都会先执行该过滤器,进行字符编码。

下面请看判断用户是否登录的过滤器:

CheckLoginFilter.java

package com.filter;

import java.io.IOException;

import javax.servlet.Filter;

import javax.servlet.FilterChain;

import javax.servlet.FilterConfig;

import javax.servlet.ServletException;

import javax.servlet.ServletRequest;

import javax.servlet.ServletResponse;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import javax.servlet.http.HttpSession;

public class CheckLoginFilter implements Filter {

FilterConfig config = null;

public void destroy() {

// TODO Auto-generated method stub

config = null;

}

public void doFilter(ServletRequest arg0, ServletResponse arg1,

FilterChain arg2) throws IOException, ServletException {

// TODO Auto-generated method stub

HttpServletRequest request = (HttpServletRequest) arg0;

HttpSession session = request.getSession(true);

String res = (String) session.getAttribute("islogin");

HttpServletResponse response = (HttpServletResponse) arg1;

if (res == null || !res.equals("true")) {

response.sendRedirect("../error.html");

}

arg2.doFilter(request, response);

}

public void init(FilterConfig arg0) throws ServletException {

// TODO Auto-generated method stub

this.config = arg0;

}

}

Web.xml文件中的主要配置为:

<filter>

<filter-name>LoginFilter</filter-name>

<filter-class>com.filter.CheckLoginFilter</filter-class>

</filter>

<filter-mapping>

<filter-name>LoginFilter</filter-name>

<url-pattern>/success/*</url-pattern>

</filter-mapping>

下面以一个servlet作为测试

servletweb.xml配置文件信息为:

<servlet-mapping>

<servlet-name>CodingServlet</servlet-name>

<url-pattern>/success/code</url-pattern>

</servlet-mapping>

由以上配置的信息可知:当地址栏直接输入

http://localhost:818/WebDemo/success/code时,因为格式既符合/* 又符合/success/*,因此两个过滤器都会执行。

执行的结果会转发到error.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>

<head>

<title>error.html</title>

<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">

<meta http-equiv="description" content="this is my page">

<meta http-equiv="content-type" content="text/html; charset=UTF-8">

<!--<link rel="stylesheet" type="text/css" href="./styles.css">-->

</head>

<body>

sorry ni mei login

<br>

qing

<a href=http://localhost:818/web122/test.html>login</a>

</body>

</html>

在浏览器运行的结果为下图:

获得上面的结果后,也会从tomcat的控制台打印以下图的信息:

其中第一条信息为Session监听器执行后打印的结果,下面立刻会介绍监听器。

Servlet监听器

Servlet监听器主要有如下几种:

ServletRequestListener ,ServletRequestAttributeListener

HttpSessionActivationListener ,HttpSessionBindingListener ,

HttpSessionAttributeListener,HttpSessionListener,

ServletContextListener等等。

这些监听器主要用来监听session,request,application这三个对象里存取数据的变化。固然也有些经常使用的用途。

下面的例子是用来记数的,记录当前时刻,有多少人在访问咱们的网站:

监听器类的定义以下:SessListener.java

package com.listener;

import javax.servlet.ServletContext;

import javax.servlet.http.HttpSession;

import javax.servlet.http.HttpSessionActivationListener;

import javax.servlet.http.HttpSessionAttributeListener;

import javax.servlet.http.HttpSessionBindingEvent;

import javax.servlet.http.HttpSessionBindingListener;

import javax.servlet.http.HttpSessionEvent;

import javax.servlet.http.HttpSessionListener;

public class SessListener implements HttpSessionBindingListener,

HttpSessionActivationListener, HttpSessionListener,

HttpSessionAttributeListener {

private int count;

public void valueBound(HttpSessionBindingEvent arg0) {

// TODO Auto-generated method stub

}

public void valueUnbound(HttpSessionBindingEvent arg0) {

// TODO Auto-generated method stub

}

public void sessionDidActivate(HttpSessionEvent arg0) {

// TODO Auto-generated method stub

}

public void sessionWillPassivate(HttpSessionEvent arg0) {

// TODO Auto-generated method stub

}

public void sessionCreated(HttpSessionEvent arg0) {

// TODO Auto-generated method stub

System.out.println("sessionCreated");

count++;

HttpSession session = arg0.getSession();

session.setMaxInactiveInterval(120);

ServletContext application = session.getServletContext();

application.setAttribute("num", count);

}

public void sessionDestroyed(HttpSessionEvent arg0) {

// TODO Auto-generated method stub

System.out.println("sessionDestroyed");

count--;

if (count <= 0)

count = 1;

HttpSession session = arg0.getSession();

ServletContext application = session.getServletContext();

application.setAttribute("num", count);

}

public void attributeAdded(HttpSessionBindingEvent arg0) {

// TODO Auto-generated method stub

}

public void attributeRemoved(HttpSessionBindingEvent arg0) {

// TODO Auto-generated method stub

}

public void attributeReplaced(HttpSessionBindingEvent arg0) {

// TODO Auto-generated method stub

}

}

该监听器在Web.xml的配置文件中的信息为:

<listener>

<listener-class>com.listener.SessListener</listener-class>

</listener>

当用户访问时就会进行人数的统计。关于本章的例子请参考工程WebDemo

内容总结

? Java Servlet是用Java编程语言实现的类。它扩展了经过请求——响应模式访问的应用程序的服务器端的性能。类包javax.servletjavax.servlet.http提供了编写Servlet的接口和类。全部的Servlet必须实现定义了生命周期方法的Servlet接口。

? Servlet的生命周期:

? HTTP协议:超文本传输协议HTTP的基本特色是简单、无状态、灵活、无链接

? HTTP请求报头ResquestHttpServletRequest对象封装了来自客户端的所有请求信息,包含客户机环境的信息和任何要从Servlet发送到服务器的数据,它有各类获取请求信息的方法

? HTTP响应报头ResponseHttpServletResponse对象封装了Servlet发回客户机的响应,一般是动态生成的响应,如HTML页面,而且它是用请求和Servlet访问的其它来源中的数据建立的。它提供了多种方法,可以访问和操做HTTP报头,属性等

? 会话管理:经过隐藏的表单字段、改写URL和持久Cookie三种手段

独立实践

? 在下列范例中,点击“Submit request!”超连接时将以何种HTTP请求形式调用HelloServlet?

<a href=” http://localhost:8080/servlets/HelloServlet” >submit request!</a>

A. GET

B. POST

C. HEAD

D. DELETE

? 如下是HelloServlet的完整内容:

import javax.servlet.*;

import javax.servlet.http.*;

import java.io.*

public class HelloServlet extends HttpServlet {

PrintWriter out = null;

public void service(HttpServletRequest request,

HttpServletResponse response)

throws ServletException, IOException {

response.setContentType("text/html");

out = response.getWriter();

}

public void doGet(HttpServletRequest request,

HttpServletResponse response)

throws ServletException, IOException {

out.println("doGet() is called");

}

public void doPost(HttpServletRequest request,

HttpServletResponse response)

throws ServletException, IOException {

out.println("doPost() is called");

}

}

若是如下面的方式调用HelloServlet,浏览器将会显示下列哪一个信息?

http://localhost:8080/servlet/HelloServlet

? 下面哪一个方法能够取得HTTP请求所传递的参数?

A. ServletRequest接口的getAttribute()方法

B. ServletRequest接口的getParameter()方法

C. HttpServletRequest接口的getAttribute()方法

D. HttpServletRequest接口的getParameter()方法

? 查看下列程序代码片断:

1. response.setContextType("image/gif");

2

3. out.close()

若是你但愿在ServletHTTP回应中传送一张GIF图片,应该在第2行加入什么程序代码?

? 下列哪一个接口所提供的方法可回传一个RequestDispatcher对象(选择两个正确答案)?

A. HttpServletRequest

B. ServletConfig

C. GenericServlet

D. ServletContext

E. ServletRequest

第二十九章: Jsp 技术

学习目标

? JSP介绍

? JSP语法

? JSP内置对象

? 自定义标签

? 标准标签的使用

JSP介绍

JSP(Java Server Page)Servlet的简化设计。注重的是显示方面,而Servlet主要是负责控制,在MVC模式里面,JSP处于视图层,Servlet处于控制器层。

JSP语法

JSP是由模板元素(html/xml等),指令元素,java脚本,动做元素组成的。

JSP的执行原理以下图:

JSP的执行过程以下图:

请看下例:

JSP源文件 test.jsp

<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>

<head>

<title>My JSP 'test.jsp' starting page</title>

</head>

<body>

<%

for (int i = 0; i < 5; i++) {

out.println("hello<br>");

}

%>

</body>

</html>

经编译后的Servlet源文件

package org.apache.jsp;

import javax.servlet.*;

import javax.servlet.http.*;

import javax.servlet.jsp.*;

import java.util.*;

public final class test_jsp extends org.apache.jasper.runtime.HttpJspBase

implements org.apache.jasper.runtime.JspSourceDependent {

private static java.util.Vector _jspx_dependants;

public java.util.List getDependants() {

return _jspx_dependants;

}

public void _jspService(HttpServletRequest request,

HttpServletResponse response) throws java.io.IOException,

ServletException {

JspFactory _jspxFactory = null;

PageContext pageContext = null;

HttpSession session = null;

ServletContext application = null;

ServletConfig config = null;

JspWriter out = null;

Object page = this;

JspWriter _jspx_out = null;

try {

_jspxFactory = JspFactory.getDefaultFactory();

response.setContentType("text/html;charset=ISO-8859-1");

pageContext = _jspxFactory.getPageContext(this, request, response,

null, true, 8192, true);

application = pageContext.getServletContext();

config = pageContext.getServletConfig();

session = pageContext.getSession();

out = pageContext.getOut();

_jspx_out = out;

out.write("\r\n\r\n<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\r\n<html>\r\n <head>\r\n <title>My JSP 'test.jsp' starting page</title>\r\n </head>\r\n \r\n <body>\r\n ");

for (int i = 0; i < 5; i++) {

out.println("hello<br>");

}

out.write("\r\n </body>\r\n</html>\r\n");

} catch (Throwable t) {

if (!(t instanceof SkipPageException)) {

out = _jspx_out;

if (out != null && out.getBufferSize() != 0)

out.clearBuffer();

if (pageContext != null)

pageContext.handlePageException(t);

}

} finally {

if (_jspxFactory != null)

_jspxFactory.releasePageContext(pageContext);

}

}

}

执行完该Servlet生成的结果以下:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>

<head>

<title>My JSP 'test.jsp' starting page</title>

</head>

<body>

hello<br>

hello<br>

hello<br>

hello<br>

hello<br>

</body>

</html>

页面显示的结果以下图:

模板元素

模板元素主要是指HTML模板,它是JSP引擎里面不执行的部分。这些HTML标记数据直接会传送到客户端的浏览器。关于HTML请找些相关资料,花三个小时就能够搞定了,在此就很少作介绍了。

指令元素

1 页面指令 (page指令)

2 包含指令 (include指令)

3 标签库指令 (taglib指令)

这三个包含在“<%@ %>”卷标里

页面指令

“<%@ page %>”指令做用于整个JSP页面,一样包括静态的包含文件。可是“<%@ page %>”指令不能做用于动态的包含文件,好比 “<jsp:include>”

能够在一个页面中用上多个“<%@ page %>”指令,可是其中的属性只能用一次,不过也有例外,那就是import属性。由于import属性和Java中的import语句相似(参照Java Languageimport语句引入得是Java语言中的类),因此此属性就能多用几回。

不管把“<%@ page %>”指令放在JSP的文件的哪一个地方,它的做用范围都是整个JSP页面。不过,为了JSP程序的可读性,以及好的编程习惯,最好仍是把它放在JSP文件的顶部。

如:

<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>

全部属性用法以下:

<%@ page [ language="java" ]

[ extends="package.class"]

[import="{package.class | package.*},..." ]

[ session="true | false" ]

[ buffer="none | 8kb | sizekb" ]

[ autoFlush="true | false" ]

[ isThreadSafe="true | false" ]

[ info="text" ]

[ errorPage="relativeURL" ]

[ contentType="mimeType [;charset=characterSet]"|"text/html; charset=ISO-8859-1" ]

[ isErrorPage="true | false"]

%>

对主要经常使用属性的解释:

1language="java"

声明脚本语言的种类,目前只能用"java"

2.import="{package.class | package.* },..."

须要导入的Java包的列表,这些包做用于程序段,表达式,以及声明。下面的包在JSP编译时已经导入了,因此就不须要再指明了:

java.lang.* javax.servlet.* javax.servlet.jsp.* javax.servlet.http.*

3errorPage="relativeURL"

设置处理异常事件的JSP文件。

4isErrorPage="true | false"

设置此页是否为出错页,若是被设置为true,你就能使用exception对象

exception对象示例,ErrorPage.jsp文件代码

<%@ page isErrorPage="true" %>

<%= exception.getMessage() %><br>

<%= exception.toString()%><br>

请看下例

<%@ page language="java" import="java.util.Date" session="true"

buffer="12kb" autoFlush="true" info="page test" errorPage="error.jsp"

isErrorPage="false" contentType="text/html; charset=gb2312"%>

<%@ page errorPage="error.jsp"%>

<%@ page isELIgnored="false"%>

<html>

<body>

<h1>

使用page指令的测试页面

</h1>

<%=new Date().toLocaleString()%>

使用表达式语言: \${2>3}:${2>3}

</body>

</html>

显示结果以下:

包含指令

是页面里包含有其余的页面。格式如:<%@ include file=”head.jsp” %>

以下例:

head.jsp

<table height=20 width=100% bgcolor=99ccff>

<tr>

<td align=center>

------head------

</td>

</tr>

</table>

body.jsp

<html>

<body bgcolor=eeffdd>

this is body

<br>

<table height=20 width=100 bgcolor=ff2233>

<tr>

<td align=center>

********body********

</td>

</tr>

</table>

</body>

</html>

footer.jsp

<%@ page contentType="text/html; charset=gb2312"%>

<br>

------footer------

<br>

<table height=50 width=100 bgcolor=518000>

<tr>

<td align=center>

<hr>

<%=new java.util.Date()%>

</td>

</tr>

</table>

include.jsp

<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>

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

<%@ include file="body.html"%>

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

浏览器的运行结果为:

标签库指令

格式

<%@ taglib uri="URIToTagLibrary" prefix="tagPrefix" %>

以下例:testtag.jsp

<%@ page language="java" import="java.util.*" pageEncoding="gbk"%>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

<html>

<body>

<c:if test="true">

hello

</c:if>

</body>

</html>

在浏览器里的运行结果为:

脚本元素

声明

声明的格式为:<%! %>

声明是声明实例变量,在转换成Servlet源文件的时候是以属性的形式生成。

表达式

表达式是或者运算值

表达式的格式为:<%= %>

scriptlet脚本

scriptletjava脚本,里面能够直接写java代码。

scriptlet的格式为:<% %>

以下例:jiaoben.jsp

<%@ page language="java" import="java.util.*" pageEncoding="gbk"%>

<html>

<head>

<title>My JSP 'jiaoben.jsp' starting page</title>

</head>

<%! int age = 28; %>

<body>

<%

String mess = "test scriptlet";

out.print(mess + "<br>");

if(age > 18){

%>

成人拉! <br>

<%}%>

该人的年龄为:

<%=age %>

</body>

</html>

转换成Servlet 的代码以下:

package org.apache.jsp;

import javax.servlet.*;

import javax.servlet.http.*;

import javax.servlet.jsp.*;

import java.util.*;

public final class jiaoben_jsp extends org.apache.jasper.runtime.HttpJspBase

implements org.apache.jasper.runtime.JspSourceDependent {

int age = 28;

private static java.util.Vector _jspx_dependants;

public java.util.List getDependants() {

return _jspx_dependants;

}

public void _jspService(HttpServletRequest request, HttpServletResponse response)

throws java.io.IOException, ServletException {

JspFactory _jspxFactory = null;

PageContext pageContext = null;

HttpSession session = null;

ServletContext application = null;

ServletConfig config = null;

JspWriter out = null;

Object page = this;

JspWriter _jspx_out = null;

try {

_jspxFactory = JspFactory.getDefaultFactory();

response.setContentType("text/html;charset=gbk");

pageContext = _jspxFactory.getPageContext(this, request, response,

null, true, 8192, true);

application = pageContext.getServletContext();

config = pageContext.getServletConfig();

session = pageContext.getSession();

out = pageContext.getOut();

_jspx_out = out;

out.write("\r\n\r\n<html>\r\n <head>\r\n \r\n <title>My JSP 'jiaoben.jsp' starting page</title>\r\n\r\n </head>\r\n ");

out.write("\r\n <body>\r\n ");

String mess = "test scriptlet";

out.print(mess + "<br>");

if(age > 18){

out.write("鎴愪汉鎷夛紒<br>\r\n ");

}

out.write("\r\n 璇ヤ汉鐨勫勾榫勪负锛?);

out.print(age );

out.write("\r\n </body>\r\n</html>\r\n");

} catch (Throwable t) {

if (!(t instanceof SkipPageException)){

out = _jspx_out;

if (out != null && out.getBufferSize() != 0)

out.clearBuffer();

if (pageContext != null) pageContext.handlePageException(t);

}

} finally {

if (_jspxFactory != null) _jspxFactory.releasePageContext(pageContext);

}

}

}

注意:在 <% %>声明的String mess = "test scriptlet"; 是生成在方法里的。在<%! %>声明的int age = 28; 是生成在属性里的。

执行完后生成的HTML代码以下:

<html>

<head>

<title>My JSP 'jiaoben.jsp' starting page</title>

</head>

<body>

test scriptlet<br>

成人拉! <br>

该人的年龄为:28

</body>

</html>

在浏览器里的运行结果以下图:

注意: 在标准的开发里面,是不容许在页面里面直接写java代码的。在页面里面只能以标签的形式出现。本章后面会介绍经过自定义标签来取代直接写java代码的操做。可是在要求不高的小型项目里面,在页面里面直接使用java代码会提升开发效率。

动做元素

常使用的动做元素为:

<jsp:forward>,<jsp:param>,<jsp:include>,jsp:useBean>,

<jsp:getProperty>,<jsp:setProperty>等等

<jsp:forward>动做

向另外一个文件传递一个包含用户请求的request对象 <jsp:forward>标签之后的代码,将不能执行。注意jsp:forwardresponse.sendRedirect的区别在于forward在同一个request做用范围内,而重定向是另外一个请求。

下面的例子传递一个参数到forwardTo.jsp

jspone.jsp

<jsp:forward page="forwardTo.jsp">

<jsp:param name="userName" value="riso" />

</jsp:forward>

forwardTo.jsp

<%

String useName=request.getParameter("userName");

out.println(useName + "谢谢光临!");

%>

输出:

riso谢谢光临!

<jsp:include>

包含一个静态或动态文件

<jsp:include><%@ include file="路径"%>区别是前者是动态的,页面的内容能够改变,然后者是静态的,一经载入页面,将不能修改。

请看下例:

static.html

<html>

<body>

<form method=post action="jsp_include.jsp">

<table>

<tr>

<td>

please input your name:

</td>

</tr>

<tr>

<td>

<input type=text name=name>

</td>

</tr>

<tr>

<td>

input you password:

</td>

<td>

<input type=text name=password>

</td>

</tr>

<tr>

<td>

<input type=submit value=login>

</td>

</tr>

</table>

</body>

</html>

two.jsp

<%@ page contentType="text/html; charset=gb2312" language="java"%>

举例说明include的工做原理:

<br> this is a1=<%=request.getParameter("a1")%><br>

this is a2= <%=request.getParameter("a2")%> <br>

<% out.println("hello from two.jsp");%>

jsp_include.jsp

<%@ page contentType="text/html; charset=gb2312" language="java"%>

<html>

<body>

<%@ include file="static.html"%>

<%//只是把文件包含进来%>

<a href="two.jsp">goto two--></a><br>

this examples show include works

<jsp:include page="two.jsp" flush="true">

<jsp:param name="a1" value="<%=request.getParameter("name")%>" />

<jsp:param name="a2" value="<%=request.getParameter("password")%>" />

</jsp:include>

</body>

</html>

从浏览器里输入:

http://localhost:818/JspDemo/jsp_include.jsp

看到结果为:

下面介绍JavaBean的使用操做:

PersonBean.java

package com.bean;

public class PersonBean {

private String name;

private String password;

public void setName(String name) {

this.name = name;

}

public void setPass(String pass) {

this.password = pass;

}

public String getName() {

return this.name;

}

public String getPass() {

return this.password;

}

}

login.jsp

<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>

<%@ page import="com.bean.*" errorPage="error.jsp"%>

<%

String path = request.getContextPath();

String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";

%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>

<head>

<base href="<%=basePath%>">

<title>My JSP 'login.jsp' starting page</title>

<meta http-equiv="pragma" content="no-cache">

<meta http-equiv="cache-control" content="no-cache">

<meta http-equiv="expires" content="0">

<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">

<meta http-equiv="description" content="This is my page">

<!--

<link rel="stylesheet" type="text/css" href="styles.css">

-->

</head>

<body>

<form action="bean.jsp">

name:

<input type="text" name="name">

<br>

pass:

<input type="password" name="password">

<br>

<input type="submit" value="login">

</form>

</body>

</html>

从浏览器里输入:http://localhost:818/JspDemo/login.jsp

结果为:

当点击login按钮,会跳到bean.jsp

bean.jsp

<jsp:useBean id="myPerson" scope="request" class="com.bean.PersonBean">

<jsp:setProperty name="myPerson" property="name" />

<jsp:setProperty name="myPerson" property="pass" param="password" />

</jsp:useBean>

name:

<jsp:getProperty name="myPerson" property="name" />

pass:

<jsp:getProperty name="myPerson" property="pass" />

运行的结果为:

注意:

JavaBean的属性存取是经过setXXX(),getXXX()方法来执行,而不是根据属性的名字来存取的。

JavaBeanscope="page | request | session | application"

page在包含“<jsp:useBean>”元素的JSP文件以及该文件中全部静态包含文件中使用这个Bean,直到页面执行完毕向客户端发回响应或转到另外一个文件为止。

request 在任何执行相同请求的Jsp文件中使用这个Bean,直到页面执行完毕向客户端发回响应或转到另外一个文件为止。可以使用Request对象访问这个Bean,好比request.getAttribute(beanInstanceName)

session - 从建立Bean开始,就能在任何使用相同sessionjsp文件中使用这个Bean。这个Bean存在于整个Session生存周期内,任何在分享此SessionJsp文件都能使用同一Bean。注意在建立这个BeanJsp文件中“<% @ page %>”指令中必须指定“session="true"”

application - 从建立Bean开始,就能在任何使用相同applicationJsp文件中使用Bean。这个Bean存在于整个application生存周期内,任何在分享此applicationJsp文件都能使用同一Bean.

JSP内置对象

JSP 有如下九种内置对象,包括:

request,请求对象

response,响应对象

pageContext,页面上下文对象

session,会话对象

application,应用程序对象

out,输出对象

config,配置对象

page,页面对象

exception,例外对象

下表对九个内置对象的解释

对象名 类型 做用域

request javax.servlet.ServletRequest 的子类 request

response javax.servlet.ServletResponse 的子类 page

pageContext javax.servlet.jsp.PageContext page

session javax.servlet.http.HttpSession session

application javax.servlet.ServletContext application

out javax.servlet.jsp.JspWriter page

config java.servlet.ServletConfig page

page java.lang.Object page

exception java.lang.Throwable page

request 对象表明的是来自客户端的请求,例如咱们在FORM表单中填写的信息等,是最经常使用的对象。关于它的方法使用较多的是getParametergetParameterNamesgetParameterValues,经过调用这几个方法来获取请求对象中所包含的参数的值;

response 对象表明的是对客户端的响应,也就是说能够通“response”对象来组织发送到客户端的数据。可是因为组织方式比较底层,因此不建议普通读者使用,须要向客户端发送文字时直接使用“out” 对象便可。

pageContext 对象直译时能够称做页面上下文对象,表明的是当前页面运行的一些属性,经常使用的方法包括findAttributegetAttributegetAttributesScopegetAttributeNamesInScope,通常状况下“pageContext” 对象用到得也不是不少,只有在项目所面临的状况比较复杂的状况下,才会利用到页面属性来辅助处理。

session 对象表明服务器与客户端所创建的会话,是在服务器端保存客户的信息。主要是跟踪用户的状态,好比是否登录等。Session对象记载某一特定的客户信息,不一样的客户用不一样的Session对象来记载。Session对象有效期:默认为20分钟,可设定。经常使用的方法包括getIdgetValuegetValueNamesputValue等。

application” 对象负责提供应用程序在服务器中运行时的一些全局信息,经常使用的方法有setAttributegetAttribute等。与Session的区别是:Session对象记载某一特定的客户信息,不一样的客户用不一样的Session对象来记载而Application对象记载全部访问该应用程序的客户信息 。

out 对象表明了向客户端发送数据的对象,与“response” 对象不一样,经过“out” 对象发送的内容将是浏览器须要显示的内容,是文本一级的,能够经过“out” 对象直接向客户端写一个由程序动态生成HTML文件。经常使用的方法除了pirntprintln以外,还包括clearclearBufferflushgetBufferSizegetRemaining,这是由于“out” 对象内部包含了一个缓冲区,因此须要一些对缓冲区进行操做的方法。

config 对象提供一些配置信息,经常使用的方法有getInitParametergetInitParameterNames,以得到Servlet初始化时的参数。

page 对象表明了正在运行的由JSP文件产生的类对象,不建议通常读者使用。

exception 对象则表明了JSP文件运行时所产生的例外对象,此对象不能在通常JSP文件中直接使用,而只能在使用了“<%@ page isErrorPage="true "%>”JSP文件中使用。

out对象的使用操做以下代码:

<%@ page language="java" import="java.util.*" pageEncoding="gbk"%>

<%

response.setContentType("text/html");

out.println("学习使用out对象:<br><hr>");

out.println("<br>out.println(boolean):");

out.println(true);

out.println("<br>out.println(char):");

out.println('a');

out.println("<br>out.println(char[]):");

out.println(new char[]{'a','b'});

out.println("<br>out.println(double):");

out.println(2.3d);

out.println("<br>out.println(float):");

out.println(43.2f);

out.println("<br>out.println(int):");

out.println(34);

out.println("<br>out.println(long):");

out.println(2342342343242354L);

out.println("<br>out.println(object):");

out.println(new java.util.Date());

out.println("<br>out.println(string):");

out.println("string");

out.println("<br>out.newLine():");

out.newLine();

out.println("<br>out.getBufferSize():");

out.println(out.getBufferSize());

out.println("<br>out.getRemaining():");

out.println(out.getRemaining());

out.println("<br>out.isAutoFlush():");

out.println(out.isAutoFlush());

out.flush();

%>

运行的结果以下:

config对象的测试

代码以下:

<%@ page contentType="text/html; charset=gb2312" language="java"

import="java.sql.*" errorPage=""%>

<html>

<head>

<title>Untitled Document</title>

<meta http-equiv="Content-Type" content="text/html; charset=gb2312">

</head>

<body>

<% int count=0;

try

{

count=Integer.parseInt(config.getInitParameter("counter"));

}

catch(Exception e)

{

out.println("org:"+e);

}

out.println("此页面设置的初始值为"+count+"");

%>

</body>

</html>

web.xml中的配置信息为:

<?xml version="1.0" encoding="UTF-8"?>

<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee

http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

<servlet>

<description>

This is the description of my J2EE component

</description>

<display-name>

This is the display name of my J2EE component

</display-name>

<servlet-name>SessionServet</servlet-name>

<servlet-class>com.servlet.SessionServet</servlet-class>

</servlet>

<servlet>

<servlet-name>config_counter</servlet-name>

<jsp-file>/config.jsp</jsp-file>

<init-param>

<param-name>counter</param-name>

<param-value>1000</param-value>

</init-param>

</servlet>

<servlet-mapping>

<servlet-name>config_counter</servlet-name>

<url-pattern>/config_counter</url-pattern>

</servlet-mapping>

<servlet-mapping>

<servlet-name>SessionServet</servlet-name>

<url-pattern>/servlet</url-pattern>

</servlet-mapping>

</web-app>

在浏览器里的运行结果为:

session,application.request对象已经在Servlet里介绍过了。用法彻底同样,这里就很少说了。

剩余的内置对象因为不是太经常使用,在这里也不介绍了。

SessionApplication对象

Session对象记载某一特定的客户信息,不一样的客户用不一样的Session对象来记载

Application对象记载全部访问该应用程序的客户信息

application对象示例

<%@ page contentType="text/html;charset=gb2312"%>

<html>

<head>

<title>application</title>

<head>

<body>

<% out.println("Java Servlet API Version "+application.getMajorVersion()

+"."+application.getMinorVersion()+"<br>");

out.println("peixun2.13.jsp's MIME type is:"+application.getMimeType("peixun2.13.jsp")

+"<br>");

out.println("URL of 'peixun2.13.jsp' is: "+application.getResource(“/peixun2.13.jsp")+"<br>");

out.println("getServerInfo()="+application.getServerInfo()+"<br>");

out.println(application.getRealPath(" "));

application.log("Add a Record to log_file"); %>

</body>

</html>

运行结果

JSP的汉字问题的原理

客户端和服务器用gb2312

HTTP传输编码用ISO8859_1

服务器须要指示客户端文档的编码方式

<%@ page contentType="text/html;charset=gb2312"%>

在服务器端接收到客户端数据时须要转换为gb2312后进行处理,方法

String name=new String(name.getBytes("ISO8859_1"), "gb2312");

自定义标签

在目前中大型项目开发里面,对MVC模式的要求是比较高的。在JSP页面只能出现标签和html/xml模板,而不能出现java代码。那么标签的运行原理是经过页面里的标签,而经过xml文件的配置,去调用后台的java对象执行,而后把结果返回给页面。

请看下例:

首先定义标签执行类HelloWorldTag.java

package com.mytag;

import java.sql.Connection;

import java.sql.ResultSet;

import java.sql.ResultSetMetaData;

import java.sql.Statement;

import javax.servlet.jsp.JspException;

import javax.servlet.jsp.JspWriter;

import javax.servlet.jsp.PageContext;

import javax.servlet.jsp.tagext.Tag;

import com.jdbc.JDBCCon;

public class HelloWorldTag implements Tag {

PageContext pageContext = null;

String mess = null;

public void setMess(String me) {

this.mess = me;

}

public int doEndTag() throws JspException {

// TODO Auto-generated method stub

return this.EVAL_PAGE;

}

public int doStartTag() throws JspException {

// TODO Auto-generated method stub

Connection con = JDBCCon.getCon();

JspWriter out = pageContext.getOut();

String sql = "select * from " + mess;

try {

Statement st = con.createStatement();

ResultSet rs = st.executeQuery(sql);

ResultSetMetaData rsmd = rs.getMetaData();

int lie = rsmd.getColumnCount();

while (rs.next()) {

for (int i = 1; i <= lie; i++) {

out.print(rs.getString(i) + ",");

}

out.println("<br>");

}

out.println("hello " + mess);

} catch (Exception e) {

e.printStackTrace();

} finally {

try {

if (con != null) {

con.close();

}

} catch (Exception e) {

e.printStackTrace();

}

}

return this.SKIP_BODY;

}

public Tag getParent() {

// TODO Auto-generated method stub

return null;

}

public void release() {

// TODO Auto-generated method stub

}

public void setPageContext(PageContext arg0) {

// TODO Auto-generated method stub

this.pageContext = arg0;

}

public void setParent(Tag arg0) {

// TODO Auto-generated method stub

}

}

其次定义标签库的描述文件

<?xml version="1.0" encoding="UTF-8"?>

<taglib version="2.0" xmlns="http://java.sun.com/xml/ns/j2ee"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd ">

<tlib-version>0.0</tlib-version>

<short-name>NMTOKEN</short-name>

<uri>www.kening.com</uri>

<tag>

<name>shuchu</name>

<tag-class>com.mytag.HelloWorldTag</tag-class>

<body-content>empty</body-content>

<attribute>

<name>mess</name>

<required>true</required>

</attribute>

</tag>

<tag>

<name>xuanzhe</name>

<tag-class>com.mytag.BodyTagTest</tag-class>

<body-content>JSP</body-content>

<attribute>

<name>isOut</name>

<required>true</required>

<rtexprvalue>true</rtexprvalue>

</attribute>

</tag>

<tag>

<name>number</name>

<tag-class>com.mytag.CountTag</tag-class>

<body-content>JSP</body-content>

<attribute>

<name>count</name>

<required>true</required>

<rtexprvalue>true</rtexprvalue>

</attribute>

</tag>

<tag>

<name>diedai</name>

<tag-class>com.mytag.IteTag</tag-class>

<body-content>JSP</body-content>

<attribute>

<name>name</name>

<required>true</required>

</attribute>

<attribute>

<name>data</name>

<required>true</required>

<rtexprvalue>true</rtexprvalue>

</attribute>

</tag>

</taglib>

在页面里的使用以下:HelloTag.jsp

<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>

<%@ taglib uri="www.kening.com" prefix="hello"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>

<head>

<title>My JSP 'HelloTag.jsp' starting page</title>

</head>

<body>

<hello:shuchu mess="kebiao" />

</body>

</html>

运行结果以下:

关于标签体,叠代标签的定义请参考JspDemo事例。

标准标签的使用

以下biaozhuntag.jsp

<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>

<%@ taglib uri="http://java.sun.com/jsp/jstl/sql" prefix="sql"%>

<%

String path = request.getContextPath();

String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";

%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>

<head>

<base href="<%=basePath%>">

<title>My JSP 'biaozhuntag.jsp' starting page</title>

<meta http-equiv="pragma" content="no-cache">

<meta http-equiv="cache-control" content="no-cache">

<meta http-equiv="expires" content="0">

<!--<link rel="stylesheet" type="text/css" href="styles.css">-->

</head>

<body>

<c:catch var="myexception">

<% int k = 5/0; %>

</c:catch>

<c:out value="${myexception}"></c:out>

<c:if test="false">

hello

</c:if>

<c:forEach var="i" begin="1" end="10" step="3">

<c:out value="${i}"></c:out>

<br>

</c:forEach>

<c:url var="myurl" value="login.jsp" scope="session">

</c:url>

<c:out value="${myurl}" />

<br>

<c:if test="${param.name == 'andy'}">

<c:redirect url="${myurl}"></c:redirect>

</c:if>

<c:out value="${param.name}" />

<c:forTokens var="token"

items="blue,red,green|yellow|pink,black|white" delims="|">

<c:out value="${token}" />

</c:forTokens>

<sql:setDataSource var="ds1"

driver="com.microsoft.jdbc.sqlserver.SQLServerDriver"

url="jdbc:microsoft:sqlserver://127.0.0.1:1433;DatabaseName=pubs"

user="sa" password="signal" />

<sql:setDataSource var="ds2" dataSource="jdbc/tag" />

<sql:query var="query1" dataSource="${ds2}">

SELECT * FROM student

</sql:query>

<table border="1">

<c:forEach var="row" items="${query1.rows}">

<tr>

<td>

id:

<c:out value="${row.id}" />

</td>

<td>

name:

<c:out value="${row.name}" />

</td>

</tr>

</c:forEach>

</table>

</body>

</html>

运行结果以下图:

关于该例子的源代码请参看附录里:JspDemo工程

内容总结

? JSP是由模板元素(html/xml等),指令元素,java脚本,动做元素组成的。

? 指令元素分为:页面指令(page指令) 包含指令(include指令)标签库指令(taglib指令)这三个包含在“<%@ %>”卷标里

? 脚本元素:声明的格式为<%!变量名=%>转换成Servlet源文件的时候是以属性的形式生成;<% java代码%>嵌入java代码,转换成Servlet源文件的时候是以方法的形式生成;

? 动做元素

<jsp:forward>,<jsp:param>,<jsp:include>,jsp:useBean>,<jsp:getProperty>,<jsp:setProperty>等等

注意jsp:forwardresponse.sendRedirect的区别在于forward在同一个request做用范围内,而重定向是另外一个请求。

? javabean:只有get/set方法的java对象

<jsp:useBean id="myPerson" scope="request"

class="bean.PersonBean">

<jsp:setProperty name="myPerson" property="name" />

<jsp:setProperty name="myPerson" property="pass" />

</jsp:useBean>

? JSP 有如下九种内置对象,包括:

request请求对象 response响应对象

pageContext页面上下文对象 session会话对象

application应用程序对象 out输出对象

config配置对象 page页面对象

exception例外对象

? 自定义标签

独立实战

? jsp的组成部分是什么

? jsp的内置对象有哪些,它们的做用分别是什么

? 说不一样

<%@ include file><jsp:include>

<%! int age = 28; %><% int age=28;%>

<jsp:forward file><% response.sendRedirect %>

pagerequestsessionapplication

<!-- --> <%-- --%>

? 用<jsp:include>实现一个三栏页面布局,用<%@ include包含公共文件command.jsp

? 自定义一个标签titleTag,输出一个标题条

? 用jsp+jdbc实现用户登陆注册和注销功能

第三十章:struts入门

学习目标

? 应用框架介绍

? 理解HTTP, CGI, servlet, JSP

? 使用 Model 2 架构

? 构建简单的应用

Struts简介

StrutsApache软件基金下Jakarta项目的一部分,Struts框架的主要架构设计和开发者是Craig R.McClanahanCraig 也是Tomcat的主要架构师,以及Java Web Services Developer Pack的主要架构师和实现者。他如今是SunJavaServer Faces (JSR-127) 以及J2EE平台的Web层架构的规范领导。

Struts Apache 软件许可 [ASF, License]下对公众是免费的。 使用此软件没有任何得到和再现成本。不象其余一些开源许可,Apache 软件许可对商业用途是友好的。你能够在你的商业项目中使用Struts,并自由分发Struts 库。你也能够将Struts 组件集成到你的框架中,就像他们是你本身编写的同样。

框架之因此叫“Struts”,是为了提醒咱们记住那些支撑咱们房屋,建筑,桥梁,甚至咱们踩高跷时候的基础支撑。 这也是一个解释Struts在开发Web应用程序中所扮演的角色的精彩描述。当创建一个物理建筑时,建筑工程师使用支柱为建筑的每一层提供支持。一样,软件工程师使用Struts为业务应用的每一层提供支持。

什么是应用框架

框架(framework)是可重用的,半完成的应用程序,能够用来产生专门的定制程序[Johnson]

Framework概念并非很新了,伴随着软件开发的发展,在多层的软件开发项目中,可重用、易扩展的,并且是通过良好测试的软件组件,愈来愈为人们所青睐。这意味着人们能够将充裕的时间用来分析、构建业务逻辑的应用上,而非繁杂的代码工程。因而人们将相同类型问题的解决途径进行抽象,抽取成一个应用框架。这也就是咱们所说的Framework

Framework的体系提供了一套明确机制,从而让开发人员很容易的扩展和控制整个Framework开发上的结构。 一般,Framework的结构中都有一个命令和控制组件("command and control" component——Framework Factory and Manager

1 Framework体系

经过基于请求响应(Request-Response)模式的应用Framework,基本上有以下几个表现逻辑结构组成。

? 控制器(Controller——控制整个Framework中各个组件的协调工做。

? 业务逻辑层(Business Logic——Framwork自己来讲,这里仅仅只是概念和几个提够服务的基础组件,真正的实现与客户的业务逻辑接轨,还须要开发人员在Framework上再次扩展。

? 数据逻辑层(Data Logic——绝大应用系统都须要涉及到数据交互,这一层次主要包括了数据逻辑和数据访问接口。对于数据逻辑来讲,若是你了解数据建模(Data Modeling)可能就很容易理解。

框架向开发人员提供一系列具备如下特征的骨架组件:

? 已经知道他们在其余程序上工做的很好;

? 它们随时能够在下一个项目中使用;

? 他们能够被组织的其余团队使用;

WEB框架所要解决的问题

涉及的概念和技术

超文本传输协议 (HTTP) :不少服务器应用程序使用HTTP以外的其余协议。他们在计算机之间维护一个持久性的的链接。应用服务器能够清楚的知道是谁链接上来,并且什么时候中断链接。由于他们知道每个链接的状态,以及每个使用它的人。这称之为状态协议。

相反, HTTP 是一个无状态协议。HTTP server 能够接受来自于各类客户的各类请求,并提供各类响应,即便是这个响应仅仅是说No。没有大量的协商和链接持久性,无状态协议能够处理大量的请求。

HTTP请求的第一行包含方法,其后是请求的来源地址和HTTP版本。HTTP请求头跟在首行后面,能够没有也能够有多个。HTTP 头向服务器提供额外的信息。能够包括浏览器的种类和版本,可接受的文档类型,浏览器的 cookies等等。7种请求方法中, GET POST 是用得最多的。

一旦服务器接收到请求,他就要产生一个HTTP响应。响应的第一行称为状态行,包含了HTTP协议的版本,数字型状态,以及状态的简短描述。状态行后,服务器将返回一个HTTP 响应头,相似于HTTP请求头。

Cookies URL 重写是两个在请求间跟踪用户状态的方式。cookie 是一种特殊的信息包,存储于用户的计算机中。URL 重写是在页面地址中存储一个特殊的标记,Java 服务器能够用它来跟踪用户。

标准的HTTP web 服务器并不传输动态内容。它主要是使用请求来定位文件资源,并在响应中返回此资源。一般这里的文件使用Hypertext Markup Language (HTML) [W3C, HTML] 格式化,以使浏览器能够显示它们。

静态内容直接来自于文本或数据文件,好比HTML 或者 JPEG 文件。这些文件能够随时改变,但经过浏览器请求时,却不能自动改变。

动态内容是临时产生的,典型地,它是针对浏览器的个别请求的响应。

创建简单的Struts应用

首先你该作的

下载并安装JDK 下载并安装 Tomcat 校验Tomcat是否工做正常。

用户注册程序

需求描述:用户将看到一个注册屏幕,包含3个字段:用户名,密码和密码确认。成功的注册要求两次密码相符。若是注册成功,控制将转向一个页面,显示注册成功successful!.。若是两次输入密码不一样,控制流将转向一个显示失败的页面。

问题: 如何建立HTML 表单; 从HTML表单获取输入; 处理输入(业务逻辑);根据动态输入改变控制流;

解决:为完成这个程序,你须要创建 一个ActionForm 一个Action struts-config.xml文件 和三个页面

步骤1 建立ActionForm

ActionForm是个JavaBean,扩展org.apache.struts.ActionForm类。这个对象捕获经过请求传送的输入。当浏览器提交一个表单,它在请求中为每一个表单中的字段建立一个参数。ActionForm针对每一个HTML表单中的字段具备一个对应的属性。ActionServlet匹配请求中的参数和ActionForm中的属性。当匹配好后,ActionServlet 为属性调用setter方法,并将请求中的值传入。在咱们的练习中,表单中的username字段须要一个setUsername(String)方法。Password1字段须要setPassword1(String) setPassword2(String)方法。

RegisterForm 的源代码显示在清单1中。

package app;

import org.apache.struts.action.*;

public class Registerform extends ActionForm {

protected String userName;

protected String password1;

protected String password2;

public String getPassword1() {

return password1;

}

public void setPassword1(String password1) {

this.password1 = password1;

}

public String getPassword2() {

return password2;

}

public void setPassword2(String password2) {

this.password2 = password2;

}

public String getUserName() {

return userName;

}

public void setUserName(String userName) {

this.userName = userName;

}

}

存储在

<BaseDirectory>/webapps/register/WEB-INF/classes/app

步骤2 建立 RegisterAction

Action一个Java类,扩展了org.apache.struts.ActionActionServlet组装ActionForm,而后将其传递给ActionAction一般负责输入校验,存取业务信息,以及决定向Servlet返回哪一个ActionForward

package app;

import org.apache.struts.action.*;

import javax.servlet.http.*;

import java.io.*;

public class RegisterAction extends Action {

public ActionForward execute(ActionMapping mapping, ActionForm form,

HttpServletRequest req, HttpServletResponse res) {

// 转换formRegisterForm

RegisterForm rf = (RegisterForm) form;

String username = rf.getUsername();

String password1 = rf.getPassword1();

String password2 = rf.getPassword2();

// 业务逻辑

if (password1.equals(password2)) {

try {

// 调用服务把用户加到数据库中去

//UserService.getInstance().AddUser(username, password1);

return mapping.findForward("success");

} catch (Exception e) {

return mapping.findForward("failure");

}

}

// Return ActionForward for failure

return mapping.findForward("failure");

}

}

步骤3 建立Struts 配置文件 (struts-config.xml)

struts-config.xml 文件包含了ActionServlet 须要用来处理对应用请求的详细信息。为了练习,咱们建立一个空壳的struts-config.xml 文件。

文件存储在<BaseDirectory>/webapps/register/WEB-INF/目录下,须要改变的是:

首先,添加/register <action>元素的 path 属性。ActionServlet 使用Web容器转发给它的URI来选择正确的Action 类。URI ActionMapping path 属性匹配。这里,请求给出的路径必须在去除前缀和后缀后和/register 匹配。前缀或后缀一般是/do/ 或者 .do。 咱们的练习中,将后缀设置为.do。当URI 具备一个.do 扩展名,容器就知道将请求转发给ActionServletStruts会自动去除 扩展名,因此咱们在配置时没必要加上它们。

下一步添加:registerForm <action> 元素的 name 属性。<action> 元素使用name 属性来识别哪一个ActionForm 将被建立,并将提交的表单组装给他。

而后,添加 app.RegisterAction <action> 元素的 type 属性。ActionServlet 使用这个属性来识别将用来处理请求的Action 类。

接下来,在<forward> 元素下,添加 success name 属性,而且 /success.html path 属性。最后,再在另外一个<forward>下添加 failure name 属性, /failure.html path 属性。 这些元素将建立ActionForward 对象,咱们将用它来选择程序的控制流。<forward> 元素定义了在RegisterAction中使用的逻辑名称之间的关联。

Struts-config.xml 源代码:

<?xml version="1.0" encoding="ISO-8859-1" ?>

<!DOCTYPE struts-config PUBLIC

"-//Apache Software Foundation//DTD Struts Configuration 1.0//EN"

"http://jakarta.apache.org/struts/dtds/struts-config_1_0.dtd">

<struts-config>

<form-beans>

<form-bean name="registerForm" type="app.RegisterForm" />

</form-beans>

<action-mappings>

<action path="/register" type="app.RegisterAction"

name="registerForm">

<forward name="success" path="/success.html" />

<forward name="failure" path="/failure.html" />

</action>

</action-mappings>

</struts-config>

Struts框架将struts-config.xml 文件视为部署描述符使用。它使咱们能够建立和改变ActionMapping 和路径的关联而不用从新编译java类。咱们也能够改变页面之间的链接,而不改变JSP模板。

步骤4 建立success.html, failure.html, 以及register.jsp 页面存放在<Base Directory>/webapps/register目录下。

success.html

<HTML>

<HEAD>

<TITLE>成功</TITLE>

</HEAD>

<BODY>

恭喜你,注册成功了

<P>

<A href="register.jsp">继续注册吗?</A>

</P>

</BODY>

</HTML>

failure.html

<HTML>

<HEAD>

<TITLE>对不起</TITLE>

</HEAD>

<BODY>

注册失败!

<P>

<A href="register.jsp">还要试一试吗?</A>

</P>

</BODY>

</HTML>

Register.jsp

<%@ taglib uri="/WEB-INF/struts-form.tld" prefix="form"%>

<form:form action="register.do">

用户名:<form:text property="username" />

<br>

输入密码:<form:password property="password1" />

<br>

再输一次:<form:password property="password2" />

<br>

<form:submit value="Register" />

</form:form>

整个程序的流程图以下

如今,试一下运行如何。 若是Tomcat没有运行,启动它。

在浏览器中输入如下地址:

http://localhost:8080/register/register.jsp

回顾一下,咱们作了什么

创建Register应用咱们实际上完成了如下内容:

? RegisterForm ActionForm

? RegisterAction Action

? 3个页面

它是如何工做的

当你浏览器地址http://localhost:8080/register/register.jspTomcat按一般状况加工这个页面。输入usernamepassword,点击Register 提交页面。浏览器在请求中post表单的内容。容器检查请求将送到哪个注册的路径去。而后请求被转发到ActionServlet ,并由RegisterAction来处理。在返回成功或失败以前,RegisterAction 校验输入的有效性。最后servlet将控制根据返回的ActionForward转发到响应页面,如图下图

内容总结

? 框架的概念

? 理解HTTP, CGI, servlet, JSP

? 使用 Model 2 架构

? 构建简单的应用

? 理解Struts的执行过程

? 创建简单的Struts应用

独立实践

? 使用struts完成用户注册程序

? 使用struts完成登陆(要求显示详细的错误信息)

? 登陆成功后在成功页面显示你的姓名等信息

? 简述MODEL 1 MODEL 2的区别

? 简述Struts的工做流程

第三十一章:Struts基础

学习目标

? Struts如何实现Model 2, MVC

? Struts控制流程

? Struts组件介绍

? Struts ActionServlet控制器对象

? Struts Action Classes

? Struts Action Mapping

MVC

MVC是一个设计模式,它强制性的使应用程序的输入、处理和输出分开。使用MVC应用程序被分红三个核心部件:

模型、视图、控制器。

它们各自处理本身的任务。

模型(model

模型表示企业数据和业务规则。

MVC的三个部件中,模型拥有最多的处理任务。

模型与数据格式无关,一个模型能为多个视图提供数据。多个视图重用

视图(view

视图是用户看到和交互的界面。

视图由Jsp HTML Flash XHTML ML/XSLWML等标识语言。

视图中没有处理,无论这些数据如何存在,对视图来讲,只是一种输出数据并容许用户操纵的方式。

控制器(Controller

控制器接受输入并调用模型和视图去完成用户的需求。

控制器不输出任何东西和作任何处理。它只是接收请求并决定调用哪一个模型构件去处理请求,而后用肯定用哪一个视图来显示模型处理返回的数据。

控制器通常由servlet承担

MVC的处理过程:首先控制器接收用户的请求,并决定应该调用哪一个模型来进行处理,而后模型用业务逻辑来处理用户的请求并返回数据,最后控制器用相应的视图格式化模型返回的数据,并经过表示层呈现给用户。

struts框架的优点

? 良好的架构和设计

? 可重用,模块化,扩展性好

? Open source

Struts如何实现Model 2, MVC

下表列出了Struts的核心类,即对应的经典的MVC组件职责。

类 描述

ActionForward 用户指向或者视图选择

ActionForm 状态改变的数据

ActionMapping 状态改变事件

ActionServlet 控制器,接受用户请求和状态改变,以及发出视图选择控制器的一部分,于模型交互,执行状态改变或状态查询,以及告诉Action ActionServlet 下一个选择的视图

除了这些核心类,Struts使用一些配置文件和视图助手(view helpers)来沟通控制器和模型。

下表列出了Struts配置文件和描述了他们在架构中的角色。

文件 目的

ApplicationResources.properties 存储本地化信息和标签,以使应用能够国际化

struts-config.xml 存储控制器对象的缺省配置,包括模型支持的用户指向,状态改变,状态查询

为将Struts配置数据暴露给视图,框架以JSP标签的形式提供了大量的助手类,如表:

标记库描述符 目的

struts-html.tld 扩展HTML FormJSP标记

struts-bean.tld 扩展处理JavaBeanJSP标记

Struts 控制流

下图以UML时序图的方式展现了Struts 请求-响应流程

咱们来按这个请求-响应流程走一边

? 客户请求匹配Action URI样式的路径

? 容器将请求传递给ActionServlet.

? 若是这个是模块化应用,ActionServlet选择响应的模块。

? ActionServlet查询路径的映射。

? 若是映射标明了一个form beanActionServlet看是否已经有一个实例,或者建立一个新的实例。若是已经有一个form beanActionServlet重设它(reset),并根据HTTP请求从新组装它。

? 若是mappingvalidate属性设置为true,它将调用form beanvalidate方法,若是验证失败,Servlet将控制转发到input属性标明的路径,控制流终止。

? 若是mapping标明一个Action类型,若是它已经存在或已经实例化,它将被重用。

? 接着Actionperformexecute方法被ActionServlet调用,并传递一个实例化的form bean或者null

? Action能够组装form bean,调用业务对象,以及其余须要作的事情。

? Action返回一个ActionForwardActionServlet

? 若是ActionForward指向另外一个Action URI,从新开始;不然,显示页面或者其余资源,流程结束。一般,结果是一个JSP页面,或者Jasper,或其它相似技术 (Struts)

? 若是JSP使用Struts HTML标记,而且在请求中看到正确的ActionForm,他们会从ActionForm中组装HTML控件。不然,<html:form>标记将建立一个。从Struts1.1开始,若是form标记自行建立一个ActionForm,它将调用ActionFormReset方法。若是你只是想建立一个空白的表单,你能够使用标准的ForwardAction来经过Action传递控制,而后离开页面。

Struts framework的工做原理和组件

对于Struts如何控制、处理客户请求,让咱们经过对struts的四个核心组件介绍来具体说明。这几个组件就是:ActionServletActionClassesActionMapping(此处包括ActionForward)、ActionFromBean

Struts ActionServlet控制器对象

ActionServlet继承自javax.servlet.http.HttpServlet类,其在Struts framework中扮演的角色是中心控制器。它提供一个中心位置来处理所有的终端请求。控制器ActionServlet主要负责将HTTP的客户请求信息组装后,根据配置文件的指定描述,转发到适当的处理器。

按照Servelt的标准,全部得Servlet必须在web配置文件(web.xml)声明。一样,ActoinServlet必须在Web Application配置文件(web.xml)中描述,有关配置信息以下。

<servlet>

<servlet-name>action</servlet-name>

<servlet-class>

org.apache.struts.action.ActionServlet

</servlet-class>

</servlet>

所有的请求URI*.do的模式存在并映射到这个servlet,其配置以下:

<servlet-mapping>

<servlet-name>action</servlet-name>

<url-pattern>*.do</url-pattern>

</servlet-mapping>

一个该模式的请求URI符合以下格式:

http://www.my_site_name.com/mycontext/actionName.do

中心控制器为全部的表示层请求提供了一个集中的访问点。这个控制器提供的抽象概念减轻了开发者创建公共应用系统服务的困难,如管理视图、会话及表单数据。它也提供一个通用机制如错误及异常处理,导航,国际化,数据验证,数据转换等。

当用户向服务器端提交请求的时候,实际上信息是首先发送到控制器ActionServlet,一旦它得到了请求,它就会把提交的请求都委托给RequestProcessor对象。

RequestProcessor使用struts-config.xml文件检查请求URI找到动做Action标示符。接着将请求信息传交给一些辅助类(help classes)处理。这些辅助类知道如何去处理与请求信息所对应的业务操做。

Struts中,这个辅助类就是org.apache.struts.action.Action。一般开发者须要本身继承Aciton类,从而实现本身的Action实例。

Struts Action Classes

若是Struts配置是一个应用系统的大脑,Action 类则是其强健的四肢。它们是Struts应用中真正干活的组件,开发人员通常都在这里耗费大量的时间处理这个类。

Struts Action的核心职责是:

访问业务

为表现层准备数据对象

处理在其中出现的错误

搞定Action对象

Action最为经常使用的是execute()方法。(注意,之前的perform方法在struts1.1中已经再也不支持),还有一个execute()方法,请参考apidoc

public ActionForward execute(ActionMapping mapping,

ActionForm form,

HttpServletRequest request,

HttpServletResponse response)

throws Exception

Controller收到客户的请求的时候,在将请求转移到一个Action实例时,若是这个实例不存在,控制器会首先建立,而后会调用这个Action实例的execute()方法。Struts Framework为应用系统中的每个Action类只建立一个实例。由于全部的用户都使用这一个实例,因此你必须肯定你的Action 类运行在一个多线程的环境中。

Action作些什么?

取表单数据

MyActionForm myform = (MyActionForm) form;

调用业务逻辑

EmployeeService service = new EmployeeService();

service.insertEmployee( employeeDTO );

检测错误

ActionErrors errors = new ActionErrors();

try {

// * 调用业务对象 *

}

catch (ModelException e) {

errors.add(ActionErrors.GLOBAL_ERROR,

new ActionError("error.detail",e.getMessage()));

}

if (!errors.empty()) {

saveErrors(Request, errors);

return (mapping.findForward("error"));

}

// * 正常继续 *

处理异常

<action path="/employeeAction"

type="app.EmployeeDispatchAction" name="employeeForm"

scope="request" validate="true"

input="/employeeForm.jsp">

<exception key="exception.database.error"

type="app.DatabaseException" path="/error.jsp" />

<forward name="insertSuccess" path="/employeeForm.jsp" />

<forward name="updateSuccess" path="/confirmation.jsp" />

</action>

Action的分类

标准Action

ForwardAction 发出一个RequestDispatcher 转发

IncludeAction 发出一个RequestDispatcher 包含

DispatchAction

LookupDispatchAction

SwitchAction

ForwardAction

建立一个请求分派器,并根据ActionMapping提供的上下文相关的URI转发控制,上下文相关路径在ActionMappingparameter属性中给出:

<action path="/saveSubscription"

type="org.apache.struts.actions.ForwardAction"

name="subscriptionForm" scope="request" input="/subscription.jsp"

name="/path/to/application/resource" />

IncludeAction

经过包含转发,你发出一个include指令。当其servlet完成时,控制要返回。相反,Forward一旦一个响应开始就不能发出一个转发,控制也不返回到发出的servlet

ForwardInclude的区别

Action 响应 控制

Forward 一旦响应开始就不能发出 控制不返回

Include 在响应时也能够发出 控制要回

DispatchAction

Struts开发人员的一个经常使用策略时使用同一个Action类来处理几个相关的任务。一个很好的例子是对一个数据记录执行基本的CRUD (建立读取修改和删除)操做,它们很是类似,能够放在一个Action类中处理。经过扩展DispatchAction(org.apache.struts.actions.DispatchAction),开发人员能够将多个方法在一个单一的Action内成

DispatchAction能够经过隐藏字段的关键字自动选择正确的方法;他使用反射机制而不是大部分开发人员所使用的逻辑方法。

public ActionForward create(ActionMapping mapping,

ActionForm form,

HttpServletRequest Request, HttpServletResponse Response)

throws IOException, ServletException;

public ActionForward read(ActionMapping mapping,

ActionForm form,

HttpServletRequest Request, HttpServletResponse Response)

throws IOException, ServletException;

public ActionForward update(ActionMapping mapping,

ActionForm form,

HttpServletRequest Request, HttpServletResponse Response)

throws IOException, ServletException;

public ActionForward delete(ActionMapping mapping,

ActionForm form,

HttpServletRequest Request, HttpServletResponse Response)

throws IOException, ServletException;

Struts-config.xml中配置以下

<action-mappings >

<action

attribute="employeeForm"

input="/employee.jsp"

name="employeeForm"

parameter="method"

path="/employee"

scope="request"

type="app.EmployeeDispatchAction" />

</action-mappings>

为选择 delete 方法, 你能够调用

http://localhost/struts2/employee?method=delete

实践中method字段的值一般是按钮或者表单中隐藏属性的名来设置,

<html:form action= /employee?method=create>

form中加入一个Hidden字段

<html:hidden name=”method” value=”create”/>

LookupDispatchAction

选择dispatch方法的一个方便的方式是将它们链接到按钮,这对于本地化应用来讲是个问题,由于按钮的标签可能根据用户的场所来改变,如英文版本显示Delete按纽而对中文用户要显示删除

LookupDispatchAction(org.apache.struts.actions.LookupDispatchAction)经过将标签映射到资源文件的消息关键字来解决这个问题,但消息关键字不是相应Java方法的名称,开发人员就提供一个hash表来映射消息关键字和dispatch方法名(经过getKeyMethodap方法实现)

protected Map getKeyMethodap(ActionMapping mapping, ActionForm form,

HttpServletRequest Request) {

Map map = new HashMap();

map.put("button.add", "cerate");//button.add映射到create方法

map.put("button.view", "read");

map.put("button.update", "update");

map.put("button.delete", "delete");

return map;

}

public ActionForward create(ActionMapping mapping,

ActionForm form,

HttpServletRequest Request, HttpServletResponse Response)

throws IOException, ServletException;

public ActionForward read(ActionMapping mapping,

ActionForm form,

HttpServletRequest Request, HttpServletResponse Response)

throws IOException, ServletException;

public ActionForward update(ActionMapping mapping,

ActionForm form,

HttpServletRequest Request, HttpServletResponse Response)

throws IOException, ServletException;

public ActionForward delete(ActionMapping mapping,

ActionForm form,

HttpServletRequest Request, HttpServletResponse Response)

throws IOException, ServletException;

JSP中按钮要这样来建立:

<html:form action="/dataRecord">

<html:submit property="method">

<bean:message key="button.add">

</html:submit>

<html:submit property="method">

<bean:message key="button.view">

</html:submit>

<html:submit property="method">

<bean:message key="button.update">

</html:submit>

<html:submit property="method">

<bean:message key="button.delete">

</html:submit>

</html:form>

对应的资源文件

中文ApplicationResources_zh_CN.properties

button.add=增长

button.view=查看

button.update=更新

button.delete=删除

英文ApplicationResources_zh.properties

button.add=Add

button.view=View

button.update=Update

button.delete=Delete

SwitchAction

全部的 Struts应用至少具备一个模块,某些应用可能有多个模块,每一个模块使用其本身的一套配置文件和表现页面,而且能够象应用中仅有一个模块的状况同样独立进行开发。当咱们想从一个模块跳转到另外一个模块时能够使用SwitchAction(org.apache.struts.actions.SwitchAction),它是一个标准Action,能够从一个模块切换到另外一个模块,而后转发控制到此模块中的路径。

SwitchAction须要两个请求参数被传递:

page:一个模块相对的URI(/开始)切换后控制应该转发去的地方

prefix:控制应该切换到的应用模块的模块前缀(/开始)缺省使用0长度的字符串

Struts Action Mapping

上面讲到了一个客户请求是如何被控制器转发和处理的,可是,控制器如何知道什么样的信息转发到什么样的Action类呢?这就须要一些与动做和请求信息相对应的映射配置说明。在struts 中,这些配置映射信息是存储在特定的XML文件(好比struts-config.xml)。这些配置信息在系统启动的时候被读入内存,供struts framework在运行期间使用。在内存中,每个<action>元素都与ActionMapping类的一个实例对应。

下面显示了一个登录的配置映射。

<action-mappings>

<action path="/logonAction" type="com.test.LogonAction"

name="LogonForm" scope="request" input="logoncheck.jsp"

validate="false">

<forward name="welcome" path="/welcome.jsp" />

<forward name="failure" path="/logon_failure.jsp " />

</action>

</action-mappings>

上面的配置表示:当能够经过/logonAction.do提交请求信息的时候,控制器将信息委托com.test.LogonAction处理。调用LogonAction实例的execute()方法。同时将Mapping实例和所对应的LogonForm Bean信息传入。其中name=LogonForm,使用的form-bean元素所声明的ActionForm Bean

form-bean的声明以下

<form-beans>

<form-bean name="LoginForm" type="com.test.LoginForm" />

</form-beans>

使用ActionForward导航

元素<forward>则表示了当Action实例的execute()方法运行完毕或,控制器根据Mapping可将响应信息转到适当的地方。如上面现实,若是客户登录成功,则调用welcome forward,将成功信息返回到/welcome.jsp页面。在你的execute()方法的结尾能够使用下面的实例代码而返回welcome forward。固然你的welcome forward必须在action元素属性中定义,正如上面所声明的那样。

return (mapping.findForward("welcome"));

ActionForward对象是配置对象。这些配置对象拥有独一无二的标识以容许它们按照有意义的名称如“success”“failure”等来检索。ActionForward对象封装了向前进的URL路径且被请求处理器用于识别目标视图。ActionForward对象创建自<forward>元素位于struts-config.xml。下面是一个Struts<forward>元素例子,属于<action>元素范围。

<action path="/editCustomerProfile"

type="packageName.EditCustomerProfileAction" name="customerProfileForm"

scope="request">

<forward name="success" path="/MainMenu.jsp" />

<forward name="failure" path="/CustomerService.jsp" />

</action>

基于执行请求处理器的execute(…)方法的结果,当传递一个值匹配指定于<forward>元素中name属性的值的时候,下一个视图能够在execute(…)方法中被开发者用方便的方法org.apache.struts.action.ActionMapping.findForward(…)选择。ActionMapping.findForward(…)方法既从它的本地范围又从全局范围提供一个ActionForward对象,该对象返回至RequestProcessorRequestDispatcher.forward(…)response.sendRedirect(…)调用下一个视图。当<forward>元素有redirect=“false”属性或redirect属性不存在的时候,RequestDispatcher.forward(…)被执行;当redirect=“true”是,将调用sendRedirect(…)方法。下例举例说明了redirect属性的用法:

<forward name="success" path="/Catalog.jsp" redirect="true"/>

若是redirect=true, URL创建如/contextPath/path由于HttpServletResponse.sendRedirect(…)中解释URL采用”/”开头相对于servlet容器根目录。

若是redirect=false, URI创建如/path由于

ServletContext.getRequestDisptacher(…)采用虚拟目录相关URL

global-forwards

在配置文件中描述了整个应用系统能够使用的ActionForward,而不是仅仅是一个特定的Action

<global-forwards>

<forward name="logout" path="/logout.do" />

<forward name="error" path="/error.jsp" />

</global-forwards>

使用时:

Return mapping.findForward(“logout”)或着

Jsp<html:link forward=”error”/>

Struts ActionForm Bean捕获表单数据

使用Web应用的人一般会花费大量的时间经过HTMLform提交数据,HTML

formWeb开发人员提出了两个挑战:一是在数据被提交时获取数据,以及用户要修改的数据预装入一个表单,以供用户修改。

怎么才能把javabean中的数据和HTML form上的数据链接起来呢?Struts经过标签来组装控件,每一个HTML标签对应一个标准的struts标记元素(taglib, 每一个标签都有一个属性项对应提供bean的属性名称,JavaBean属性的返回值对应HTML控件的value属性。

如:<input name="address"/>Struts标签来代替:

<html:input property="address"/>

ActionForm的做用:

经过Struts的标记库在JavaBeanHtml表单元素间传值

ActionForm 的要求

建立一个ActionForm并不困难,可是你的类必须符合一些要求:

? ActionForm必须扩展自org.apache.struts.ActionForm。基类ActionForm是不能实例化的。

? ActionForm 必须为每一个应该从请求中收集的HTML控件定义一个公共属性。

? 若是你要求ActionForm 在传递属性到Action以前校验它们,你就必须实现validate方法;

? 若是想在组装前初始化属性,必须实现reset,它在ActionForm组装前被调用;

下面是一个简单的ActionForm类:

import org.apache.struts.action.*;

public class MyForm extends ActionForm {

protected String name;

protected String address;

public String getName() {

return this.name;

};

public String getAddress() {

return this.address;

};

public void setName(String name) {

this.name = name;

};

public void setAddress(String address) {

this.address = address;

};

};

ActionForm的处理流程

对于每个客户请求,Struts framework在处理ActionForm的时候,通常须要经历以下几个步骤:

? 检查Action的映射,肯定Action中已经配置了对ActionForm的映射

? 根据name属性,查找form bean的配置信息

? 检查Actionformbean的使用范围,肯定在此范围下,是否已经有此form bean的实例。

? 假如当前范围下,已经存在了此form bean的实例,而是对当前请求来讲,是同一种类型的话,那么就重用。

? 不然,就从新构建一个form bean的实例

? form beanreset()方法备调用

? 调用对应的setter方法,对状态属性赋值

? 若是validatede的属性北设置为true,那么就调用form beanvalidate()方法。

? 若是validate()方法没有返回任何错误,控制器将ActionForm做为参数,传给Action实例的execute()方法并执行。

注意:直接从ActionFrom类继承的reset()validate()方法,并不能实现什么处理功能,因此有必要本身从新覆盖。

使用动态ActionForm

配置动态ActionForm

<form-beans>

<form-bean name="employeeForm"

type="org.apache.struts.action.DynaActionForm">

<form-property name="name" type="java.lang.String" initial=”cjy”/>

<form-property name="password1" type="java.lang.String" />

<form-property name="password2" type="java.lang.String" />

</form-bean>

</form-beans>

动态表单经过子元素来设置动态ActionFrom的属性

name:指定属性名

type指定属性类型,它能够使用如下java类型

java.lang.BigDecimal

java.lang.BigInteger

java.lang.Boolean

java.lang.Byte

java.lang.Character

java.lang.Double

java.lang.Float

java.lang.Integer

...

动态ActionFormreset()的方法

DynaActionForm基类提供了initialize()方法,它把表单全部的属性初始化,初始值有initial属性指定,若是没有设置,初始值由java类型默认,如对象类型为null booleanfalse 整形为为0

访问动态ActionForm的属性

动态ActionForm把全部的属性保存在一个map

public Object get(String name)

public void set(String name,Object value)

get取出指定名称的属性值,用set给指定属性赋值,如

String email=(String)form.get("email");

form.set("email","cjy@21.com");

动态ActionForm的表单验证

DynaActionForm基类的validate()方法没有提供任何默认的行为,必须扩展DynaActionForm类,覆盖validate()方法编程来实现,还能够用Validator验证框架来实现配置验证

ActionForm的继承层次

Struts的其余组件

StrutsFramework自己提供了不少可扩展的组件或subframework,方便的开发人员在其构架上构建web层的应用系统。好比upload,collections,logging等等。让咱们来看看两个比较重要的组件:validationg frameworkstruts taglib。有关其余组件请参考Struts用户手册

http://jakarta.apache.org/struts/userGuide

Validation Framework for Struts

struts1.1中,新增了validation framework。增长了对form数据提交的验证。将本来须要在ActionFrom Beanvalidate()进行的验证经过配置文件的描述进行验证。

有关其详细信息,请参考

http://home.earthlink.net/~dwinterfeldt

我的建议对于小型应用系统能够采用这种配置方式,可是对于应用系统中有大量web层表单应用的系统,而且业务需求变更比较大的,使用validation framework可能会加剧开发难度、系统维护难度。能够借鉴validation frameworkJavascript Validator Tag

Struts TagLib

struts提供了一组可扩展的自定义标签库(TagLib),能够简化建立用户界面的过程。目前包括:Bean TagsHTML TagsLogic TagsNested TagsTemplate Tags 这几个Taglib。有关Struts Taglib的结构和使用,能够参考后面有关章节。

BeanUtils

这个组件的全称是Bean Introspection Utilites。是属于Jakarta Commons项目组的。主要是帮助构建javabean的属性操做的(getter,setter),已经提供一种动态定义和访问bean的属性。有关详细信息,请参考

http://jakarta.apache.org/commons/beanutils.html

Collections

这个组件主要是提供了一些集合或列表对象,在原有的java collections framework的基础上进行了扩展。详细资料请参考:

http://jakarta.apache.org/commons/collections.html

Digester

这个组件翻译成中文的意思是汇编。其主要功能是根据xml配置文件,初始化系统的一些java类对象。Digester帮助你指定XMLjava对象之间映射模型,并且容许客户话定制映射规则(rules)。详细资料请参考

http://jakarta.apache.org/commons/digester.html

内容总结

? 掌握Struts如何实现Model 2, MVC

? 熟练应用Struts控制流程

? 掌握Struts组件介绍

? 灵活运用Struts ActionServlet控制器对象

? 掌握Struts Action Classes

? 掌握Struts Action Mapping

? 可以对Struts组件熟练使用

? 可以在程序中使用struts框架

独立实践

? 按照struts的流程,作一个雇员注册的功能

? 完成一个雇员登陆的功能,若是登陆错误,要求显示详细的错误的信息

? 使用表单验证和框架验证完成对注册信息的验证

? 使用DispachAction

? 使用BeanUtils工具,作一个显示雇员表的数据的功能。

第三十二章:配置Struts组件

学习目标

? Web应用部署描述符

? Struts配置文件

? 应用资源文件

? Ant的构建文件

三个 XML文件和一个属性文件

? web.xmlJSP 容器使用这个文件来载入和配这是Java Servlet 要求的web应用部署描述符。Servlet的应用。

? struts-config.xmlStruts框架的部署描述符。它用来载入和配置Struts框架使用的各类组件。

? Build.xmlJakarta Ant构建工具使用它来编译和部署你的应用。使用Ant不是必需的,但它在Struts人员中很流行。

? Application.properties:该文件为你的Struts应用提供资源。像build.xml文件同样, 它不是严格要求的,可是大Struts应用都要用到。

Web应用部署描述符 web.xml

Struts框架的核心是ActionServletStruts 把它看成是一个控制器。将它当作是一个黑盒。他们老是在web应用部署描述符 (web.xml) 中配置它,而后让它本身工做。

Struts框架有两个组件须要从应用部署描述符中配置:ActionServlet 和标签库(可选),例如注册应用的web.xml

<!--前两行将文件标识为一个web应用部署描述符 -->

<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE web-app

PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"

"http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">

<web-app>

<!--配置 ActionServlet—这一段告诉容器装入action名称下的ActionServlet -->

<servlet>

<servlet-name>action</servlet-name>

<servlet-class>

org.apache.struts.action.ActionServlet

</servlet-class>

<init-param>

<param-name>application</param-name>

<param-value>application</param-value>

</init-param>

<init-param>

<param-name>config</param-name>

<param-value>/WEB-INF/conf/struts-config.xml</param-value>

</init-param>

<init-param>

<param-name>debug</param-name>

<param-value>2</param-value>

</init-param>

<init-param>

<param-name>detail</param-name>

<param-value>2</param-value>

</init-param>

<load-on-startup>2</load-on-startup>

</servlet>

<!-- 标识Struts请求这一段告诉容器将匹配*.do 格式的文件请求转发到action servlet -->

<servlet-mapping>

<servlet-name>action</servlet-name>

<url-pattern>*.do</url-pattern>

</servlet-mapping>

<!-- 建立welcome文件 -->

<welcome-file-list>

<welcome-file>index.jsp</welcome-file>

</welcome-file-list>

<!-- 配置标签库这里咱们配置应用中使用的标签库。 3个核心的Struts标签库—bean, html, logic -->

<taglib>

<taglib-uri>/tags/struts-bean</taglib-uri>

<taglib-location>/WEB-INF/lib/struts-bean.tld</taglib-location>

</taglib>

<taglib>

<taglib-uri>/tags/struts-html</taglib-uri>

<taglib-location>/WEB-INF/lib/struts-html.tld</taglib-location>

</taglib>

<taglib>

<taglib-uri>/tags/struts-logic</taglib-uri>

<taglib-location>/WEB-INF/lib/struts-logic.tld</taglib-location>

</taglib>

</web-app>

ActionServlet的参数的配置

Struts配置

Struts配置文件 (struts-config.xml)用来装入多个关键的框架组件。这些对象一块儿构成了Struts 配置。

<struts-config>

下面配置针对常规ActionFormDynaActionForm<form-bean> 元素配置

<form-bean name="menuForm" type="com.itjob.struts.MenuForm" />

<form-bean name="logonForm"

type="org.apache.struts.action.DynaActionForm">

<form-property name="username" type="java.lang.String" />

<form-property name="password" type="java.lang.String" />

</form-bean>

下面配置全局转发配置这些forward对应用中的每一个Action都有效。你也能够部署一个局部 ActionForward<action> 元素中。局部转发仅针对该ActionMapping有效。

<global-forwards>

<forward name="logoff" path="/logoff.do" />

<forward name="logon" path="/logon.do" />

<forward name="welcome" path="/welcome.do" />

</global-forwards>

下面配置ActionMapping,它描述应用要采起的操做、命令。 ActionURI被用做一个ActionMapping的逻辑标识符,或者路径。当web浏览器请求一个ActionURIActionServlet首先查找相应的ActionMappingActionMapping 则告诉 ActionServlet 哪一个 Action 对象要用于这个URI

<action-mappings> 元素描述了咱们的应用要用来处理请求的ActionMapping对象(org.apache.struts.action.ActionMapping)的集合。请求要到达应用而后到达ActionServlet,它必须匹配上下文和咱们在容器中注册的url-pattern 格式。

若是URL是针对http://localhost/myApp/myAction.do 咱们只须要引用 /myAction做为 ActionMapping的路径

<action path="/logonSubmit" type="app.LogonAction" name="logonForm"

scope="request" validate="true" input="/pagess/Logon.jsp" />

<action path="/logon" type="app.ContinueAction">

<forward name="continue" path="/pagess/Logon.jsp" />

</action>

<action path="/welcome" type="app.ContinueAction">

<forward name="continue" path="/pagess/Welcome.jsp" />

</action>

请求处理器是ActionServlet处理循环的核心。大多数状况下,你能够编写和装入一个请求处理器,来代替建立你本身的ActionServlet子类。

<contrller nocache="true" null="true"

processorClass="com.myCompany.struts.RequestProcessor" />

每一个模块都应该有其本身的缺省消息资源束。

<message-resources parameter="resources.application" />

<message-resources parameter="resources.image" />

plug-inStruts应用中,将验证任务委托给第三方的插件

<plug-in className="org.apache.struts.validator.ValidatorPlugIn">

<set-property property="pathname"

value="/WEB-INF/validator-rules.xml" />

<set-property property="pathname" value="/WEB-INF/validation.xml" />

</plug-in>

data-sources为了帮助开发人员使用链接, Struts 提供了一个数据源管理组件。你能够使用这个组实例化和配置一些实现数据源的对象,而且能够从JavaBean的属性进行总体配置。

下面是一个数据源配置,使用MySQL数据库的Struts 缺省配置:

<data-sources>

<data-source>

<set-property property="maxCount" value="4" />

<set-property property="minCount" value="2" />

<set-property property="description"

value="Artimus:MySQL Data Source Config" />

<set-property property="driverClass"

value="org.gjt.mm.mysql.Driver" />

<set-property property="url"

value="jdbc:mysql://localhost:3306/artimus" />

<set-property property="autoCommit" value="true" />

<set-property property="user" value="root" />

<set-property property="password" value="" />

</data-source>

</data-sources>

应用资源文件

Struts框架提供了好用和灵活的消息系统,JavaJSP代码中,要给定一个消息的关键字;消息文本在运行时丛属性文件中检索。框架文档将消息属性文件引用为 application resources 或者 message resource bundle

若是你想要本地化你的应用,你能够为你想要支持的场所建立一个额外的应用资源文件。这其实是建立一个资源束 (java.util.ResourceBundle)。框架会为每一个用户维护一个标准的Locale对象(java.util.Locale)。针对用户场所的合适的消息会自动从资源束中进行选取。

两个概念:

? Locale(场所) 对象是一个特定的语言和地区的识别符。

? ResourceBundle 对象包含场所特定的对象。当须要一个场所特定的对象时,能够从资源束中取得,它返回匹配用户场所的对象。 Struts 框架为消息文本使用基于字符串的资源束。

属性文件自身是一个平面的文本文件,每一行是一个关键字-值对。你能够使用任何文本编辑器进行编辑,包括Windows Notepad

应用资源文件的缺省名称是经过在web.xmlStruts ActionServlet传递一个初始化参数决定的。在应用中使用Struts应用资源束以前必须首先进行配置。应用资源文件位于应用的CLASSPATH之中,这样Struts能够找到它。最好是放在你的应用的class 文件夹中。这多是在WEB-INF/classes文件夹中,或者,若是你以二进制部署你的应用时在WEB-INF/lib下的一个JAR文件中。

param-value 应该使你的文件按包命名格式的全限定名称。这意味着若是若是你将资源文件直接放在classes下,你能够直接使用文件名,如前面的代码片段所示。

<init-param>

<param-name>

Application</param-name> <param-value>resources.application</param-value>

</init-param>

物理文件的系统路径应该是:

WEB-INF/classes/resources/application.properties

为了本地化你的应用,为每一个支持的场所添加资源文件,并修改基本名称:

WEB-INF/classes/resources/

application.properties

application_es.properties

application_fr_CA.properties

Ant构建文件

构建文件build.xml设置为使用源代码存储在WEB-INF子目录下的项目树。这使得整个应用,包括源代码和编译文件,都集中在一个目录系统之中。这就能使你得应用的工做目录能够位于你的开发服务器中。若是容器能够很好的重装类文件,你就能够从新构建应用来测试最新的改变,而不用重启容器。

<!--project 构建文件整体名称,而且标识一个基础目录和缺省目标-->

<project name="logon basedir=" ." deafult="dist">

<property name="project.title" value="Logon" />

<property name="project.version" value="1.2" />

<property name="dist.name" value="logon" />

<!--构建应用是要使用的classpath 一般是WEB-INF/lib文件夹中的JAR的清单-->

<path id="project.class.path">

<pathelement path="lib/struts.jar" />

<pathelement path="./classes/" />

<pathelement path="${classpath}" />

</path>

<!-- 帮助Ant经过比较类文件和源文件的时间戳来最小化编译工做 -->

<target name="prepare">

<tstamp />

</target>

<!-- 从源代码树中拷贝一些属性文件到classes树。能够保持原始的属性文件和文件源代码中的保持一致 -->

<target name="resources">

<copy todir="classes" includeEmptyDirs="no">

<fileset dir="src/resources">

<patternset>

<include name="**/*.properties" />

</patternset>

</fileset>

</copy>

</target>

<!--首先调用prepare resources 目标,而后开始构建源文件-->

<target name="compile" depends="prepare,resources">

<javac srcdir="src" destdir="classes">

<classpath refid="project.class.path" />

</javac>

</target>

<!--clean目标经过删除和恢复类文件夹来确保全部的东西都从新构建-->

<target name="clean" description="Prepare for clean build">

<delete dir="classes" />

<mkdir dir="classes" />

</target>

<!-- javadoc 目标为应用构建JavaDoc。一般,要标明classpathJAR路径。注意,是冒号分隔的列表-->

<target name="javadoc" description="Generate JavaDoc API docs">

<delete dir="./doc/api" />

<mkdir dir="./doc/api" />

<javadoc sourcepath="./src/java" destdir="./doc/api"

classpath="lib/struts.jar:" packagenames="app.*" author="true"

private="true" version="true" windowtitle=" API Documentation"

doctitle="<h1>${project.title} Documentation (Version ${project.version})</h1>"

bottom="Copyright © 2002" />

</target>

<!-- dist目标为应用建立一个Web归档(WAR)文件。这个文件能够用来在你的生产服务器上部署你的应用。-->

<target name="dist" description="createe binary distribution">

<delete dir="./dist" />

<mkdir dir="./dist" />

<war warfile="./dist/${dist.name}.war"

webxml="../WEB-INF/web.xml" manifest="../META-INF/MANIFEST.MF"

basedir="../"

excludes="WEB-INF/dist,WEB-INF/web.xml,META-INF/MANIFEST.MF" />

</target>

<!-- project 目标将所有构建全部东西,并准备一个二进制的分发包-->

<target name="project" depends="clean,prepare,compile,javadoc,dist" />

</project>

配置Tiles框架

Tiles是一个Struts 框架的可选组件,是一个强大的页面组装工具

步骤1

Struts lib 文件夹拷贝struts-tiles.tld tiles-config.dtd 文件 (若是没有)/WEB-INF 文件夹.

插入下面的语句快到(若是没有)/WEB-INF/web.xml 文件中,而且紧跟其余<taglib> 元素:

<taglib>

<taglib-uri>/tags/tiles</taglib-uri>

<taglib-location>/WEB-INF/tiles.tld</taglib-location>

</taglib>

步骤2 建立一个tiles-defs.xml /WEB-INF文件夹

<!DOCTYPE tiles-definitions PUBLIC

"-//Apache Software Foundation//DTD Tiles Conig//EN"

"http://jakarta.apache.org/struts/dtds/tiles-config.dtd">

<tiles-definitions>

<!-- skeleton definition

<definition name="${name}" path="${path}">

<put name="${name}" value="${value}"/>

</definition>

end blank definition -->

</tiles-definitions>

步骤3 插入这个 <plug-in> 元素到struts-config.xml,位置在关闭的</struts-config> 元素以前:

<plug-in className="org.apache.struts.tiles.TilesPlugin">

<set-property property="definitions-config"

value="/WEB-INF/tiles-defs.xml" />

</plug-in>

内容总结

? Web应用部署描述符

? 应用资源文件

? 掌握Ant的构建文件

? 可以用ant工具对Web应用部署

? 熟练使用Struts配置文件

独立实践

? 按照上一章完成的一个雇员登陆的功能,若是登陆错误,要求用国际化显示详细的错误的信息

? 假设在地址栏中输入一个不存在的页面,要求能显示已经自定义好的404错误页面

? 若是在地址栏中输入:http://127.0.0.1:8080/login,不输入具体页面,要求显示一个自定义的欢迎页面出来

? 用ant 工具部署雇员管理系统

? 用Tiles框架来优化雇员管理系统

第三十三章:Struts标记库

学习目标

? Bean标记

? 逻辑标记

? HTML标记

? 模板标记

Struts标记库taglib介绍

Struts提供了用来封装逻辑的各类定制JSP标记,所以页面设计者能够将主要精力花在页面的可视特征上,而没必要主要考虑Java语法或其它JSP语法

? Struts-bean.tld:使访问和定义bean更容易,为了实现国际化,应使用不一样的属性文件

? struts-html.tld:提供显示HTML对象(如表单、按钮和复选框等)的简便方法

? struts-logic.tld:支持逻辑构造,以即可以有条件地显示文本或者做为处理循环的结果来显示文本

? struts-template.tld:支持使用在运行时能够修改的JSP模板

taglib的使用:要在JSP文件顶部的<taglib>伪指令以下所示:

<%@ taglib uri=“struts-html.tld” prefix=“html”%>

<%@ taglib uri=“struts-bean.tld”prefix=“bean”%>

<%@ taglib uri=“struts-logic.tld”prefix=“logic”%>

每一个<taglib>伪指令都具备与基于 web.xml的<taglib>标记中的URL相匹配的URL,在jsp中经过如下两种形式使用标记

? 没有嵌套内容的标记能够采用如下格式:

<prefix:tagName attributesAndValues/>

? 嵌套内容是在一对标记之间嵌套的:

<prefix:tagName attributesAndValues />

</prefix:tagName>

Bean标记

这个标记库中包含用于定义新bean、访问bean及其属性的标记。Struts框架提供了多种自定义标记用来在JSP页中处理JavaBean。这些标记被封装在一个普通的标记库中,在文件struts-bean.tld中定义了它的标记库描述器。Bean标记库将标记定义在四个子类别中:

? 建立和复制bean的标记

? 脚本变量定义标记

? bean翻译标记

? 消息国际化标记

建立和复制bean的标记

<bean:define>标记用来:

? 定义新字符串常数

? 将现有的bean复制到新定义的bean对象

? 复制现有bean的属性来建立新的bean

<bean:define>标记属性表:

属性 描述

Id 新定义的bean脚本变量名称,必须设置

Type 定义引入脚本变量的类

Value 为id属性定义的脚本变量分配一个新的对象

Name 目标bean的名称。若value属性没有设置,这个属性就必须设置

Property Name 属性定义的bean的属性名称,用来定义新的bean

Scope 源bean的做用域。若没有设置,搜索范围是从页做用域到应用程序做用域

toScope 目标bean的做用域。若没有设置,默认值是页做用域

例如:定义一个bean:

<bean:define id=”test” value=”this is a test”/>

bean在页做用域中被拷贝大于请求做用域中的另外一个bean:

<bean:define id=”targetBean” name=”sourceBean” scope=”page” toScope=”request”/>

定义脚本变量的标记

从多种资源中定义和生成脚本变量,这些资源包括cookie,请求参数,HTTP标头等等。属性以下:

属性 描述

Id 脚本变量和要定义的页做用域属性的名称

Name cookie/标头/参数的名称

multiple 若是这个属性设置了任意一个数值,全部匹配的cookie都会被积累并存储到一个Cookie[](一个数组)类型的bean里。若无设置,指定cookie的第一个值将做为Cookie类型的值

Value 若是没有匹配的cookie或数值,就返回这个属性指定的默认值

例如:<bean:cookie id=”myCookie” name=”userName”/> 脚本变量名称是myCookie,用来建立这个属性的cookie的名称是userName

<bean:header id=”myHeader” name=”Accept-Language”/> 脚本变量名称是myHeader,请求标头的名称是Accept-Language.

<bean:parameter id=”myParameter” name=”myParameter”> 脚本变量名称是myPatameter,它保存的请求参数的名称也是myParameter.

<bean:include>标记将对一个资源的响应进行检索,并引入一个脚本变量和字符串类型的页做用域属性。这个资源能够是一个页,一个ActionForward或一个外部URL。与<jsp:include>的不一样是资源的响应被存储到一个页做用域的bean中,而不是写入到输出流。属性以下:

属性 描述

Id 脚本变量和要定义的页做用域属性的名称

Page 一个内部资源

forward 一个ActionForward

Href 要包含的资源的完整URL

例如:<bean:include id=”myInclude” page=”MyJsp?x=1”/>脚本变量的名称是myInclude,要检索的响应来自资源MyJsp?x=1

<bean:resource>标记将检索web应用中的资源,并引入一个脚本变量和InputStream或字符串类型的页做用域属性。若是在检索资源时发生问题,就会产生一个请求时间异常。属性以下:

属性 描述

Id 脚本变量和要定义的页做用域属性的名称

Name 资源的相对路径

Input 若是这个属性不存在,资源的类型就是字符串

例如:<bean:resource id=”myResource” name=”/WEB-INF/images/myResource.xml”/> 脚本变量的名称是myResource,要检索的资源的名称是myResource.xml

显示Bean属性

标记库中定义了<bean:write>标记,用来将bean的属性输送到封装的JSP页写入器。这个标记与<jsp:getProperty>相似,属性以下:

属性 描述

Name 要进行属性显示的bean的名称

property 要显示的属性的名称。若是这个属性类有java.beans.PropertyEditor,getAsText()toString 方法会被调用

Scope Bean的做用域,若没有设置,搜索范围是从页到应用程序做用域

Filter 若是设置true,属性中的全部特殊HTML字符都将被转化为相应的实体引用

Ignore 若是设置false,当发现属性时会产生一个请求时间异常,不然返回null

例如:<bean:write name=”myBean” property=”myProperty” scope=”request” filter=”true”/> myBean的属性myProperty将会被显示,做用域为请求,若是发现任何HTML特殊字符都将被转化为相应的实体引用。

消息标记和国际化

strtus框架支持国际化和本地化,经过使用<bean:message>标记,以及使用java.util数据包中定义的LocaleResourceBundle类来实现Java2平台对这些任务的支持。Java.text.MessageFormat类定义的技术能够支持消息的格式

strtus实现国际化和本地化

定义资源文件的名称,这个文件会包含用默认语言编写的在程序中会出现的全部消息。这些消息以关键字-的形式存储,以下:

error.validation.location = The entered location is invalid

这个文件须要存储在类的路径下,并且它的路径要做为初始化参数传送给ActionServlet做为参数进行传递时,路径的格式要符合完整Java类的标准命名规范。好比,若是资源文件存储在WEB-INF\classes目录中,文件名是ApplicationResources.properties,那么须要传递的参数值是ApplicationResources。若是文件在WEB-INF\classes\com\test中,那么参数值就应该是com.test. ApplicationResources.

例如:资源文件中定义了一个消息:

info.myKey = The numbers entered are {0},{1},{2},{3}

咱们可以使用下面的消息标记:

<bean:message key=”info.myKey” arg0=”5” arg1=”6” arg2=”7” arg3=”8”/>

这个信息标记输出到JSP页会显示为:

The numbers entered are 5,6,7,8

逻辑标记

逻辑库的标记可以用来处理外观逻辑而不须要使用scriptletStruts逻辑标签库包含的标记可以有条件地产生输出文本,在对象集合中循环从而重复地产生输出文本,以及应用程序流程控制。它也提供了一组在JSP页中处理流程控制的标记。这些标记封装在文件名为struts-logic.tld的标记包中。

逻辑标记库定义的标记可以执行下列三个功能:条件逻辑 重复 转发/重定向响应

条件逻辑:struts有三类条件逻辑。

第一类能够比较下列实体与一个常数的大小: cookie?请求参数?beanbean的参数? 请求标头

如下列出了这一类标记:

标记 功能

<equal> 若是常数与被定义的实体相等,返回true

<notEqual> 若是常数与被定义的实体不相等,返回true

<greaterEqual> 若是常数大于等于被定义的实体,返回true

<lessEqual> 若是常数小于等于被定义的实体,返回true

<lessThan> 若是常数小于被定义的实体,返回true

<greaterThan> 若是常数大于被定义的实体,返回true

这一类的全部标记有相同的属性

属性 描述

Value 要进行比较的常数值

Cookie 要进行比较的HTTP cookie的名称

Header 要进行比较的HTTP请求标头的名称

parameter 要进行比较的HTTP请求参数的名称

Name 若是要进行比较的是beanbean的属性,则这个属性表明bean的名称

property 要进行比较的bean属性的名称

Scope Bean的做用域,若是没有指定做用域,则它的搜索范围是从页到应用程序

例如:

<logic:equal parameter=”name” value=”SomeName”>

The entered name is SomeName

</logic:equal>

判断名为”name”的请求参数的值是不是”SomeName”

<logic:greaterThan name=”bean” property=”prop” scope=”page” value=”7”>

The value of bean.Prop is greater than 7

</logic:greaterThan>

判断在页的做用域中是否有一个名为”bean”bean,它有一个prop属性,这个属性的值是否大于7。若是这个属性可以转化为数值,就进行数值比较,不然就进行字符串比较。

第二类条件标记定义了两个标记:

? <logic:present>

? <logic:notPresent>

它们的功能是在计算标记体以前判断特定的项目是否存在。标记的属性和属性值决定了要进行检查的项目。

属性 描述

Cookie 由这个属性指定的cookie将被检查是否存在

Header 由这个属性指定的请求标头将被检查是否存在

parameter 由这个属性指定的请求参数将被检查是否存在

Name 若是没有设置property属性,那么有这个属性指定的bean将被检查是否存在。若是设置了,那么beanbean属性都将被检查是否存在

property 检查有name属性指定的bean中是否存在指定的属性

Scope 若是指定了bean的名称,这就是bean的做用域。若是没有指定做用域,搜索的范围从页到应用程序做用域

Role 检查当前已经确认的用户是否属于特殊的角色

User 检查当前已经确认的用户是否有特定的名称

例如:<logic:notPresent name=”bean” property=”prop” scope=”page”>

The bean property bean.prop is present

</logic:notPresent>

标记判断在页做用域中是否存在一个名为”bean”bean,这个bean有一个prop属性。

第三类条件标记比较复杂,这些标记根据模板匹配的结果检查标记体的内容。换句话说,这些标记判断一个指定项目的值是不是一个特定常数的子字符串:

? <logic:match>

? <logic:notMatch>

这些标记容许JSP引擎在发现了匹配或是没有发现时计算标记主体。属性以下:

属性 描述

Cookie 要进行比较的HTTP cookie的名称

Header 要进行比较的的HTTP标头 的名称

parameter 要进行比较的的HTTP请求参数的名称

Name 若要对beanbean的属性进行比较,这个属性是用户指定bean的名称

location 若是设置了这个属性的值,将会在这个指定的位置(索引值)进行匹配

scope 若是对bean进行比较,这个属性指定了bean的做用域。若是没有设置这个参数,搜索范围是从页到应用程序做用域

property 要进行比较的bean的属性名称

value 要进行比较的常数值

例如:<logic:match parameter=”name” value=”xyz” location=”1”>

The parameter name is a sub-string of the string xyz from index 1

</logic:match>

标记检查名为”name”的请求参数是不是”xyz”的子字符串,可是子字符串必须从”xyz”的索引位置1开始(也就是说子字符串必须是”y””yz”)。

重复标记

在逻辑标记库中定义了<logic:iterate>标记,它可以根据特定集合中元素的数目对标记体的内容进行重复的检查。集合的类型能够是java.util.Iterator,java.util.Collection,java.util.Map或是一个数组。

有三种方法能够定义这个集合:

? 使用运行时间表达式来返回一个属性集合的集合

? 将集合定义为bean,而且使用name属性指定存储属性的名称。

? 使用name属性定义一个bean,而且使用property属性定义一个返回集合的bean属性。

当前元素的集合会被定义为一个页做用域的bean。属性以下,全部这些属性都能使用运行时表达式。

属性 描述

collection 若是没有设置name属性,它就指定了要进行重复的集合

Id 页做用域bean和脚本变量的名称,它保存着集合中当前元素的句柄

indexed 页做用域JSP bean的名称,它包含着每次重复完成后集合的当前索引

Length 重复的最大次数

Name 做为集合的bean的名称,或是一个bean名称,它由property属性定义的属性,是个集合

Offset 重复开始位置的索引

property 做为集合的Bean属性的名称

Scope 若是指定了bean名称,这个属性设置bean的做用域。若没有设置,搜索范围从页到应用程序做用域

Type 为当前定义的页做用域bean的类型

例如:<logic:iterate id=”currentInt”

collection=”<% =myList %>”

type=”java.lang.Integer”

offset=”1”

length=”2”>

<% =currentint %>

</logic:iterate>

代码将从列表中的第一个元素开始重复两个元素而且可以让当前元素做为页做用域和java.lang.Integer类型的脚本变量来使用。也就是说,若是myList包含元素1234等,代码将会打印12

转发和重定向标记

转发标记

<logic:forward>标记可以将响应转发给重定向到特定的全局ActionForward上。ActionForward的类型决定了是使用PageContext转发响应,仍是使用sendRedirect将响应进行重定向。此标记只有一个”name”属性,用来指定全局ActionForward的名称,例如:

<logic:forward name=”myGlobalForward”/>

重定向标记

<logic:redirect>标记是一个可以执行HTTP重定向的强大工具。根据指定的不一样属性,它可以经过不一样的方式实现重定向。它还容许开发人员指定重定向URL的查询参数。属性以下:

属性 描述

Forward 映射了资源相对路径的ActionForward

Href 资源的完整URL

Page 资源的相对路径

Name Map类型的页名称,请求,会话或程序属性的名称,其中包含要附加大哦重定向URL(若是没有设置 property属性)上的名称-参数。或是具备Map类型属性的bean名称,其中包含相同的信息(没有设置property属性)

Property Map类型的bean属性的名称。Bean的名称由name属性指定。

Scope 若是指定了bean的名称,这个属性指定搜索bean的范围。若是没有设置,搜索范围从页到应用程序做用域

ParamID 定义特定查询参数的名称

ParamName 字符串类型的bean的名称,其中包含查询参数的值(若是没有设置paramProperty属性);或是一个bean的名称,它的属性(paramProperty属性中指定)包含了查询参数值

paramProperty 字符串bean属性的名称,其中包含着查询参数的值

ParamScope ParamName定义的bean的搜索范围. 使用这个标记时至少要指定forward,hrefpage中的一个属性,以便标明将响应重定向到哪一个资源

HTML标记

Struts HTML标记能够大体地分为如下几个功能:

? 显示表单元素和输入控件

? 显示错误信息

? 显示其余HTML元素

显示表单元素和输入控件

strutsHTML表单与为表单操做而定义的ActionForm bean紧密联系在一块儿。表单输入字段的名称与ActionForm bean里定义的属性名称是对应的。当第一次显示表单时,表单的输入字段是从ActionForm bean中移植过来的,当表单被提交时,请求参数将移植到ActionForm bean实例。

全部能够在<form>标记中使用的用来显示HTML输入控件的内嵌标记都使用下列属性来定义JavaScript事件处理器。

属性 描述

Onblur 字段失去了焦点

Onchange 字段失去了焦点而且数值被更改了

Onclick 字段被鼠标点击

Ondblclick 字段被鼠标双击

Onfocus 字段接收到输入焦点

Onkeydown 字段拥有焦点而且有键按下

onkeypress 字段拥有焦点而且有键按下并释放

Onkeyup 字段拥有焦点而且有键被释放

onmousedown 鼠标指针指向字段而且点击

onmousemove 鼠标指针指向字段而且在字段内移动

onmouseout 鼠标指针指向控件,可是指针在元素外围移动

onmouseover 鼠标指针没有指向字段,可是指针在元素内部移动

Onmouseup 鼠标指针指向字段,而且释放了鼠标按键

<form>元素中可以被定义的其余通常属性有:

属性 描述

Accesskey 定义访问输入字段的快捷键

Style 定义输入字段的样式

styleClass 定义输入字段的样式表类

Tabindex 输入字段的tab顺序

表单标记

<html:form>标记用来显示HTML标记,能够指定AcitonForm bean的名称和它的类名。若是没有设置这些属性,就须要有配置文件来指定ActionMapping以代表当前输入的是哪一个JSP页,以及从映射中检索的bean名和类。若是在ActionMapping指定的做用域中没有找到指定的名称,就会建立并存储一个新的bean,不然将使用找到的bean<form>标记可以包含与各类HTML输入字段相对应的子标记。

<html:form>标记属性以下:

属性 描述

Action 与表单相关的操做。在配置中,这个操做也用来标识与表单相关的ActionForm bean

Enctype 表单HTTP方法的编码类型

Focus 表单中须要初始化焦点的字段

Method 表单使用的HTTP方法

Name 与表单相关的ActionForm bean的名称。若是没有设置这个属性,bean的名称将会从配置信息中得到

Onreset 表单复位时的JavaScript事件句柄

Onsubmit 表单提交时的JavaScript事件句柄

Scope 搜索ActionForm bean的范围。若是没有设置,将从配置文件中获取

Style 使用的格式

styleClass 这个元素的格式表类

Type ActionForm bean的完整名称。若是没有设置,将从配置文件得到

例如:

<html:form action=”validateEmploee.do” method=”post”>

</html:form>

与表单相关的操做路径是validateEmployee,而表单数据是经过POST传递的。对于这个表单来讲,ActionForm bean的其余信息,如bean名称类型,做用域,都是从表单指定操做的ActionMapping中检索获得的:

<form-beans>

<form-bean name=”empForm” type=”com.example.EmployeeForm” />

</form-beans>

<action-mappings>

<action path=”/validateEmployee”

type=”com.example.ValidateExampleAction” name=”empForm” scope=”request”

input=”/employeeInput.jsp”>

<forward name=”success” path=”/employeeOutput.jsp”>

</action>

</action-mapping>

若是配置文件中包含上述信息,而且请求URI*.do被映射到ActionServlet,与表单相关的ActionForm bean的名称,类型和做用域分别是empForm,com.example.EmployeeFormrequest.这些属性也能够使用<html:form>标记属性进行显示的定义。

如下标记必须嵌套在<html:form>标记里

按钮和取消标记

<html:button>标记显示一个按钮控件;<html:cancel>标记显示一个取消按钮。属性以下:

属性 描述

Property 定义在表单被提交时返回到服务器的请求参数的名称

Value 按钮上的标记

复位和提交标记

<html:reset><html:submit>标记分别可以显示HTML复位按钮和提交按钮。

文本和文本区标记

<html:text><html:textarea>标记分别HTML文本框和文本区,属性以下:

属性 描述

Property 定义当表单被提交时送回到服务器的请求参数的名称,或用来肯定文本元素当前值的bean的属性名称

Name 属性被查询的bean的名称,它决定了文本框和文本区的值。若是没有设置,将使用与这个内嵌表单相关的ActionForm的名称

<html:text>标记还有如下属性:

属性 描述

Maxlength 可以输入的最大字符数

Size 文本框的大小(字符数)

<html:textarea>标记特有的属性以下:

属性 描述

Rows 文本区的行数

Cols 文本区的列数

检查框和复选框标记

<html:checkbox>标记可以显示检查框控件。<html:multibox>标记可以显示HTML复选框控件,请求对象在传递检查框名称时使用的getParameterValues()调用将返回一个字符串数组。属性以下:

属性 描述

Name Bean的名称,其属性会被用来肯定检查是否以选中的状态显示。若是没有设置,将使用与这个内嵌表单相关的ActionFrom bean的名称

Property 检查框的名称,也是决定检查框是否以选中的状态显示的bean属性名称。在复选框的状况下,这个属性必须是一个数组

Value 当检查框被选中时返回到服务器的请求参数的值

例如:<html:checkbox property=”married” value=”Y”/>

一个名为married的检查框,在表单提交时会返回一个”Y”.

文件标记

<html:file>标记能够显示HTML文件控件。属性以下:

属性 描述

Name Bean的名称,它的属性将肯定文件控件中显示的内容。若是没设置,将使用与内嵌表单相关的ActionForm bean的名称

property 这个属性定义了当表单被提交时送回到服务器的请求参数的名称,以及用来肯定文件控件中显示内容的bean属性名称

Accept 服务器可以处理的内容类型集。它也将对客户浏览器对话框中的可选文件类型进行过滤

Value 按钮上的标记,这个按钮可以在本地文件系统中浏览文件

单选钮标记

<html:radio>标记用来显示HTML单选钮控件,属性以下:

属性 描述

Name Bean的名称,其属性会被用来肯定单选钮是否以选中的状态显示。若是没有设置,将使用与这个内嵌表单相关的ActionFrom bean的名称

property 当表单被提交时送回到服务器的请求参数的名称,以及用来肯定单选钮是否以被选中状态进行显示的bean属性的名称

Value 当单选钮被选中时返回到服务器的值

隐藏标记

<html:hidden>标记可以显示HTML隐藏输入元素,属性以下:

属性 描述

Name Bean的名称,其属性会被用来肯定隐藏元素的当前值。若是没有设置,将使用与这个内嵌表单相关的ActionFrom bean的名称

property 定义了当表单被提交时送回到服务器的请求参数的名称,以及用来肯定隐藏元素当前值的bean属性的名称

Value 用来初始化隐藏输入元素的值

密码标记

<html:password>标记可以显示HTML密码控件,属性以下:

属性 描述

maxlength 可以输入的最大字符数

Name Bean的名称,它的属性将用来肯定密码元素的当前值。若是没有设置,将使用与这个内嵌表单相关的ActionFrom bean的名称

property 定义了当表单被提交时送回到服务器的请求参数的名称,以及用来肯定密码元素当前值的bean属性的名称

redisplay 在显示这个字段时,若是相应的bean属性已经被设置了数据,这个属性决定了是否显示密码的内容

Size 字段的大小

选择标记

<html:select>标记可以显示HTML选择控件,属性以下:

属性 描述

multiple 代表这个选择控件是否容许进行多选

Name Bean的名称,它的属性肯定了哪一个。若是没有设置,将使用与这个内嵌表单相关的ActionFrom bean的名称

property 定义了当表单被提交时送回到服务器的请求参数的名称,以及用来肯定哪一个选项须要被选中的bean属性的名称

Size 可以同时显示的选项数目

Value 用来代表须要被选中的选项

选项标记(这个元素须要嵌套在<html:select>标记里)

<html:option>标记用来显示HTML选项元素集合,属性以下:

属性 描述

collection Bean集合的名称,这个集合存储在某个做用域的属性中。选项的数目与集合中元素的数目相同。Property属性可以定义选项值所使用的bean属性,而labelProperty属性定义选项标记所使用的bean的属性

labelName 用来指定存储于某个做用域的bean,这个bean是一个字符串的集合,可以定义<html:option>元素的标记(若是标志与值不相同)

labelProperty 与collection属性共同使用时,用来定义了存储于某个做用域的bean,这个bean将返回一个字符串集合,可以用来写入<html:option>元素的value属性

Name 若是这是惟一被指定的属性,它就定义了存储于某个做用域的bean,这个bean将返回一个字符串集合,可以用来写入<html:option>元素的value属性

property 这个属性在与collection属性共同使用时,定义了每一个要显示选项值的独立beanname属性。若是不是与collection属性共同使用,这个属性定义了由name属性指定的bean的属性名称(若是有name属性),或是定义了一个ActionForm bean,这个bean将返回一个集合来写入选项的值

咱们看一下这个标记的一些例子:

<html:option collection=”optionCollection” property=”optionValue” labelProperty=”optionLabel”/>

标记假设在某个做用域中有一个名为optionCollection的集合,它包含了一些具备optionValue属性的独立的bean,每一个属性将做为一个选项的值。每一个选项的标志由beanoptionLabel属性属性进行定义。

<html:option name=”optionValues” labelName=”optionLabels”/>

标记中optionValues表明一个存储在某个做用域中的bean,它是一个字符串集合,可以用来写入选项的值,而optionLabels表明一个存储在某个做用域中的bean,它也是一个字符串集合,可以用来写入选项的标志。

显示错误信息的标记

<html:errors>标记可以与ActionErrors结合在一块儿来显示错误信息。这个标记首先要从当前区域的资源文件中读取消息关键字errors.header,而后显示消息的文本。接下去它会在ActionErrors对象(一般做为请求参数而存储在Action.ERROR_KEY关键字下)中循环,读取单个ActionError对象的消息关键字,从当前区域的资源文件中读取并格式化相应的消息,而且显示它们。而后它读取与errors.footer关键字相对应的消息而且显示出来。

经过定义property属性可以过滤要显示的消息,这个属性的值应该与ActionErrors对象中存储ActionError对象的关键字对应。属性以下:

属性 描述

Bundle 表示应用程序做用域属性的名称,它包含着消息资源,其默认值Acion.MESSAGE_KEY

Locale 表示会话做用域属性的名称,它存储着用户当前登陆的区域信息。其默认值是Action.ERROR_KEY

Name 表示请求属性的名称,它存储着ActionErrors对象。其默认值是Action.ERROR_KEY

property 这个属性指定了ActionErrors对象中存储每一个独立ActionError对象的关键字,它能够过滤消息

例子:

<html:errors/>

显示集合中全部的错误。

<html:errors property=”missing.name”/>

显示存储在missing.name关键字的错误。

其余HTML标记

struts HTML标记还定义了下列标记来显示其余HTML元素:

? <html:html> : 显示HTML元素

? <html:img> : 显示图象标记

? <html:link> : 显示HTML连接或锚点

? <html:rewrite> : 建立没有锚点标记的URI

这些标记的详细内容请参照struts文档。

模板标记

动态模板是模块化WEB页布局设计的强大手段。Struts模板标记库定义了自定义标记来实现动态模板。

插入标记

<template:insert>标记可以在应用程序的JSP页中插入动态模板。这个标记只有一个template属性,用来定义模板JSP页。要插入到模板的页是有多个<template:put>标记来指定的,而这些标记被定义为<template:insert>标记的主体内容。

放置标记

<template:put>标记是<template:insert>标记内部使用的,用来指定插入到模板的资源。属性以下:

属性 描述

content 定义要插入的内容,好比一个JSP文件或一个HTML文件

direct 若是这个设置为true,由content属性指定的内容将直接显示在JSP上而不是做为包含文件

Name 要插入的内容的名称

Role 若是设置了这个属性,只有在当前合法用户具备特定角色时才能进行内容的插入

得到标记

在模板JSP页中使用<template:get>标记可以检索由<template:put>标记插入到JSP页的资源。属性以下:

属性 描述

Name 由<template:put>标记插入的内容的名称

Role 若是设置了这个属性,只有在当前合法用户具备特定角色时才能进行内容的检索

使用模板标记

首先编写一个模板JSP页,它将被全部的web页使用:

<html>

<%@ taglib uri=”/template” prefix=”template” %>

<head>

<title></title>

</head>

<body>

<table width=”100%” height=”100%”>

<tr height=”10%”>

<td>

<template:get name=”header” />

</td>

</tr>

<tr height=”80%”>

<td>

<template:get name=”content” />

</td>

</tr>

<tr height=”10%”>

<td>

<template:get name=”footer” />

</td>

</tr>

</table>

</body>

</html>

咱们将这个文件命名为template.jsp。这个文件使用<template:get>标记来得到由JSP页使用<template:put>标记提供的内容,而且将内容在一个HTML表格中显示出来。这三个内容是标题,内容和页脚。典型的内容JSP会是这样:

<%@ taglib uri=”/template” prefix=”/template”%>

<template:insert template=”template.jsp”>

<template:put name=”header” content=”header.html”/>

<template:put name=”content” content=”employeeList.jsp”/>

<template:put name=”footer” content=”footer.html”/>

</template:insert>

这个应用程序JSP页使用<template:insert标记来定义模板,而后使用<template:put>标记将特定内容名称指定的资源放到模板JSP页中。若是咱们有上百个布局相同的页,但忽然想改变这个模板,咱们只须要改变template.jsp文件。

内容总结

? 可以熟练使用Bean标记

? 可以熟练使用HTML标记

? 可以熟练使用逻辑标记

? 可以熟练使用模板标记

? 可以把标签运用到项目里

独立实践

? 修改前两章的练习,在页面里统一换成标签的处理

? 作一个struts登陆,链接数据库进行验证,数据库中有一张信息表P_InfoP_id,p_password,p_name,p_age,p_sex,p_addr

? 登陆成功后能作注册,修改,和显示信息的功能

? 要求用一个Action类进行控制转发

? 用标签完成一个表的分页显示功能

第三十四章:Hibernate基础

学习目标

? 理解ORM机制

? 理解Hibernate的工做原理

? Hibernate的配置和对象-映射文件

? 理解对象持久化

Hibernate简介

HibernateJava应用和关系数据库之间的桥梁,它负责Java 对象关系数据之间的映射。Hibernate 内部封装了经过 JDBC 访问数据库的操做,向上层应用提供了面向对象的数据访问API。在Java 应用中使用Hibernate包含如下步骤。

1) 建立Hibernate的配置文件。

2) 建立持久化类。

3) 建立对象-关系映射文件。

4) 经过Hibernate API编写访问数据库的代码。

创建简单的Hibernate应用

本章经过一个简单的例子 customerApp 应用,演示如何运用 Hibernate 来访问关系数据库。customerApp 应用的功能很是简单:经过 Hibernate 保存、更新、删除、加载以及查询 Customer对象。

建立 Hibernate的配置文件

Hibernate 从其配置文件中读取和数据库链接有关的信息,这个配置文件应该位于应用的 classpath 中。Hibernate 的配置文件有两种形式:一种是 XML 格式的文件;还有一种是Java 属性文件,采用=的形式。 下面介绍如何以Java 属性文件的格式来建立 Hibernate的配置文件。这种配置文件的默认文件名为hibernate.properties

hibernate.properties的内容以下:

hibernate.dialect=net.sf.hibernate.dialect.MySQLDialect

hibernate.connection.driver_class=com.mysql.jdbc.Driver

hibernate.connection.url=jdbc:mysql://localhost:3306/SAMPLEB

hibernate.connection.username=root

hibernate.connection.password=1234

hibernate.show_sql=true

以上hibernate.properties文件包含了一系列属性及其属性值,Hibernate将根据这些属性来链接数据库,本例为链接 MySQL 数据库的配置代码。下表对以上 hibernate.properties文件中的全部属性作了描述。

属性 描述

hibernate.dialect SQL指定数据库使用的方言

hibernate.connection.driver_class 指定数据库的驱动程序

hibernate.connection.url URL指定链接数据库的

hibernate.connection.username 指定链接数据库的用户名

hibernate.connection.password 指定链接数据库的口令

hibernate.show_sql 若是为true,表示在程序运行时,会在控制台输出SQL语句,这有利于跟踪Hibernate的运行状态。默认为false。在应用开发和测试阶段,能够把这个属性设为true,以便跟踪和调试应用程序,在应用发布阶段,应该把这个属性设为false,以便减小应用的输出信息,提升运行性能。

Hibernate可以访问多种关系数据库,如MySQLOracleSybase等。尽管多数关系数据库都支持标准的SQL语言,可是它们每每还有各自的 SQL方言,就象不一样地区的人既能说标准的普通话,还能讲各自的方言同样。hibernate.dialect属性用于指定被访问数据库使用的 SQL 方言,当 Hibernate 生成 SQL 查询语句,或者使用 native 对象标识符生成策略时,都会参考本地数据库的SQL方言。

建立持久化类

持久化类是指其实例须要被 Hibernate 持久化到数据库中的类。持久化类一般都是域模型中的实体域类。持久化类符合JavaBean的规范,包含一些属性,以及与之对应的 getXXX()setXXX()方法。如下定义了一个名为 Customer 的持久化类。

package com.itjob.jiaowu.hibernate;

import java.io.Serializable;

import java.sql.Date;

import java.sql.Timestamp;

public class Customer implements Serializable{

private Long id;

private String name;

private String email;

private String password;

private int phone;

private boolean married;

private String address;

private char sex;

private String description;

private byte[] image;

private Date birthday;

private Timestamp registeredTime;

public Customer(){}

public Long getId(){

return id;

}

public void setId(Long id){

this.id = id;

}

public String getName(){

return name;

}

public void setName(String name){

this.name=name;

}

//此处省略emailpasswordphone等属性的getXXX()setXXX()方法

……

}

持久化类符合JavaBean的规范,包含一些属性,以及与之对应的getXXX()setXXX()方法。getXXX()setXXX()方法必须符合特定的命名规则,“get”“set”后面紧跟属性的名字,而且属性名的首字母为大写,例如 name 属性的 get 方法为 getName(),若是把 get

方法写为getname()或者getNAME(),会致使 Hibernate在运行时抛出如下异常:

net.sf.hibernate.PropertyNotFoundException: Could not find a getter for property name in class com.itjob.jiaowu.hibernate.Customer

若是持久化类的属性为boolean类型,那么它的 get方法名既能够用“get”做为前缀,也能够用“is”做为前缀。例如 Customer 类的 married 属性为 boolean 类型,所以如下两种get方法是等价的:

public boolean isMarried(){

return married;

}

或者

public boolean getMarried(){

return married;

}

Hibernate 并不要求持久化类必须实现 java.io.Serializable 接口,可是对于采用分布式结构的 Java 应用,当 Java 对象在不一样的进程节点之间传输时,这个对象所属的类必须实现Serializable接口,此外,在Java Web应用中,若是但愿对 HttpSession中存放的 Java 对象进行持久化,那么这个Java 对象所属的类也必须实现Serializable接口。

Customer 持久化类有一个id属性,用来唯一标识Customer 类的每一个对象。在面向对象术语中,这个 id 属性被称为对象标识符(OIDObject Identifier),一般它都用整数表示,固然也能够设为其余类型。若是 customerA.getId().equals(customerB.getId())的结果是 true,就表示 customerA customerB 对象指的是同一个客户,它们和 CUSTOMERS 表中的同一条记录对应。 Hibernate 要求持久化类必须提供一个不带参数的默认构造方法,在程序运行时,Hibernate运用Java 反射机制,调用java.lang.reflect.Constructor.newInstance()方法来构造持久化类的实例。若是对这个持久化类使用延迟检索策略,为了使 Hibernate 可以在运行时为这个持久化类建立动态代理,要求持久化类的默认构造方法的访问级别必须是 public protected 类型,而不能是 default private 类型。在Customer类中没有引入任何Hibernate APICustomer类不须要继承Hibernate的类,或实现Hibernate的接口,这提升了持久化类的独立性。若是往后要改用其余的ORM产品,好比由Hibernate改成OJB,不须要修改持久化类的代码。

建立数据库 Schema

在本例中,与 Customer 类对应的数据库表名为 CUSTOMERS,它在 MySQL 数据库中的DDL定义以下:

create table CUSTOMERS(

ID bigint not null primary key,

NAME varchar(15) not null,

EMAIL varchar(128) not null,

PASSWORD varchar(8) not null,

PHONE int ,

ADDRESS varchar(255),

SEX char(1) ,

IS_MARRIED bit,

DESCRIPTION text,

IMAGE blob,

BIRTHDAY date,

REGISTERED_TIME timestamp

);

CUSTOMERS 表有一个 ID 字段,它是表的主键,它和 Customer 类的 id 属性对应。CUSTOMERS表中的字段使用了各类各样的 SQL类型,参见下表。

字段名 SQL类型 说明

ID BIGINT 整数,占8字节,取值范围为:-2^63 ~ 2^63-1

NAME VARCHAR 变长字符串,占0 ~ 255个字节

SEX CHAR 定长字符串,占 0 ~ 255个字节

IS_MARRIED BIT 布尔类型

DESCRIPTION TEXT 长文本数据,占 0 ~ 65535 255字节。若是字符串长度小于255 ,能够用VARCHARCHAR类型来表示。若是字符串长度大于255 ,能够定义为TEXT类型。

IMAGE BLOB 二进制长数据,占 0 ~ 65535字节,BLOBBinary Large Object的缩写。IMAGE在本例中, 字段用来存放图片数据

BIRTHDAY DATE 表明日期,格式为“YYYY-MM-DD”

REGISTERED_TIME TIMESTAMP 表明日期和时间,格式为“YYYYMMDDHHMMSS ”

建立对象-关系映射文件

Hibernate采用XML格式的文件来指定对象和关系数据之间的映射。在运行时,Hibernate将根据这个映射文件来生成各类 SQL 语句。在本例中,将建立一个名为 Customer.hbm.xml的文件,它用于把 Customer 类映射到 CUSTOMERS 表,这个文件应该和 Customer.class 文件存放在同一个目录下。如下为Customer.hbm.xml文件的源代码。

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 2.0//EN"

"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd

">

<hibernate-mapping>

<class name="mypack.Customer" table="CUSTOMERS">

<id name="id" column="ID" type="long">

<generator class="increment"/>

</id>

<property name="name" column="NAME" type="string" not-null="true"/>

<property name="email" column="EMAIL" type="string" not-null="true" />

<property name="password" column="PASSWORD" type="string" not-null="true"/>

<property name="phone" column="PHONE" type="int" />

<property name="address" column="ADDRESS" type="string" />

<property name="sex" column="SEX" type="character"/>

<property name="married" column="IS_MARRIED" type="boolean"/>

<property name="description" column="DESCRIPTION" type="text"/>

<property name="image" column="IMAGE" type="binary"/>

<property name="birthday" column="BIRTHDAY" type="date"/>

<property name="registeredTime" column="REGISTERED_TIME" type="timestamp"/>

</class>

</hibernate-mapping>

映射文件的文档类型定义(DTD

Customer.hbm.xml文件的开头声明了 DTDDocument Type Definition, 文档类型定义),它对XML文件的语法和格式做了定义。HibernateXML解析器将根据DTD来核对XML文件的语法。 每一种 XML 文件都有独自的 DTD 文件。Hibernate 的对象-关系映射文件使用的 DTD文件的下载网址为:http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd

。此外,在Hibernate软件包的src\net\sf\hibernate目录下也提供了 hibernate-mapping-2.0.dtd文件。在这个文件中,描述顶层元素<hibernate-mapping>的代码以下:

<!ELEMENT hibernate-mapping (meta*, import*, (class|subclass|joined-subclass)*, query*, sql-query*)>

描述顶层元素<hibernate-mapping>的子元素<class>的代码以下:

<!ELEMENT class (

meta*,

(cache|jcs-cache)?,

(id|composite-id),

discriminator?,

(version|timestamp)?,

(property|many-to-one|one-to-one|component|dynamic-component|any|map|set|list|bag|idbag|array

|primitive-array)*,

((subclass*)|(joined-subclass*))

)>

<hibernate-mapping>元素是对象-关系映射文件的根元素,其余元素(即以上DTD 代码中括号之内的元素,如<class>子元素)必须嵌入在<hibernate-mapping>元素之内。在<class>元素中又嵌套了好多子元素。在以上DTD 代码中,还使用了一系列的特殊符号来修饰元素,下表描述了这些符号的做用。在建立本身的对象-关系映射文件时,若是不熟悉某种元素的语法,能够参考DTD 文件。

DTD中特殊符号的做用

符号 含义

无符号 该子元素在父元素内必须存在且只能存在一次。

+ 该子元素在父元素内必须存在,能够存在一次或者屡次。

* 该子元素在父元素内能够不存在,或者存在一次或者屡次。它是比较经常使用的符号。

? 该子元素在父元素内能够不存在,或者只存在一次。它是比较经常使用的符号。

2-3 能够看出,在<hibernate-mapping>元素中,<meta><import><class><query>等子元素能够不存在,或者存在一次或者屡次;在<class>元素中,<id>子元素必须存在且只能存在一次,<property>元素能够不存在,或者存在一次或者屡次。 此外,在映射文件中,父元素中的各类子元素的定义必须符合特定的顺序。例如根据<class>元素的DTD 能够看出,必须先定义<id>子元素,再定义<property>子元素,如下映射代码颠倒了<id><property>子元素的位置:

<class name="mypack.Customer" table="CUSTOMERS">

<property name="name" column="NAME" type="string" not-null="true" />

<property name="email" column="EMAIL" type="string" not-null="true" />

<id name="id" column="ID" type="long">

<generator class="increment"/>

</id>

……

</class>

HibernateXML解析器在运行时会抛出 MappingException

[java] 21:27:51,610 ERROR XMLHelper:48 - Error parsing XML:

XML InputStream (24) The content of element type "class" must match "(meta*,(cache|jcs-cache)?,(id|composite-id),discriminator?,(version|timestamp)?,(property|many-to-one|one-to-one|component|dynamic-component|any|map|set|list|bag|idbag|array|primitive-array)*,(subclass*|joined-subclass*))".[java] net.sf.hibernate.MappingException: Error reading resource: mypack/Customer.hbm.xml

at net.sf.hibernate.cfg.Configuration.addClass(Configuration.java:357)

Customer持久化类映射到CUSTOMERS

Customer.hbm.xml文件用于映射 Customer 类。若是须要映射多个持久化类,那么既能够在同一个映射文件中映射全部类,也能够为每一个类建立单独的映射文件,映射文件和类同名,扩展名为“hbm.xml”。后一种作法更值得推荐,由于在团队开发中,这有利于管理和维护映射文件。 <class>元素指定类和表的映射,它的name属性设定类名,table属性设定表名。如下代码代表和Customer 类对应的表为CUSTOMERS表:

<class name="mypack.Customer" table="CUSTOMERS">

若是没有设置<class>元素的table属性,Hibernate将直接以类名做为表名,也就是说,默认状况下,与mypack.Customer 类对应的表为Customer 表。 <class>元素包含一个<id>子元素以及多个<property>子元素。<id>子元素设定持久化类的OID 和表的主键的映射。如下代码代表 Customer 类的 id属性和 CUSTOMERS表中的 ID字段对应。

<id name="id" column="ID" type="long">

<generator class="increment"/>

</id>

<id>元素的<generator>子元素指定对象标识符生成器,它负责为 OID 生成唯一标识符。<property>子元素设定类的属性和表的字段的映射。<property>子元素主要包括 nametypecolumnnot-null属性。

1<property>元素的name属性

<property>元素的name属性指定持久化类的属性的名字。

2<property>元素的type属性

<property>元素的type属性指定 Hibernate映射类型。Hibernate映射类型是Java 类型与SQL 类型的桥梁。

3<property>元素的not-null属性

若是<property>元素的not-null属性为 true,代表不容许为 null,默认为 false。例如如下代码代表不容许Customer 类的name属性为 null

<property name="name" column="NAME" type="string" not-null="true" />

Hibernate在持久化一个 Customer 对象时,会先检查它的 name 属性是否为 null,若是为null,就会抛出如下异常:

net.sf.hibernate.PropertyValueException: not-null property references

a null or transient value: com.itjob.jiaowu.hibernate.Customer.name

若是数据库中CUSTOMERS表的 NAME 字段不容许为 null,但在映射文件中没有设置not-null属性:

<property name="name" column="NAME" type="string" />

那么Hibernate在持久化一个Customer 对象时,不会先检查它的 name属性是否为 null而是直接经过 JDBC API CUSTOMERS 表插入相应的数据,因为 CUSTOMERS 表的NAME 字段设置了not null约束,所以数据库会抛出错误:

708 ERROR JDBCExceptionReporter:58 - General error, message from server:

"Column 'NAME' cannot be null"

4<property>元素的column属性

<property>元素的 column 属性指定与类的属性映射的表的字段名。如下代码代表和address属性对应的字段为ADDRESS字段:

<property name="address" column= "ADDRESS" type="string"/>

若是没有设置< property >元素的 column属性,Hibernate将直接以类的属性名做为字段名,也就是说,默认状况下,与Customer 类的 address属性对应的字段为 address字段。 <property>元素还能够包括<column>子元素,它和<property>元素的 column属性同样,均可以设定与类的属性映射的表的字段名。如下两种设置方式是等价的:

<property name="address" column= "ADDRESS" type="string"/>

或者

<property name="address" type="string">

<column name="ADDRESS" />

</property>

<property>元素的<column>子元素比 column 属性提供更多的功能,它能够更加详细的描述表的字段。例如如下<column>子元素指定 CUSTOMERS 表中的 NAME 字段的 SQL 类型为varchar(15),不容许为null,而且为这个字段创建了索引:

<property name="name" type="string">

<column name="NAME" sql-type="varchar(15)" not-null="true" index="idx_name" />

</property>

经过 Hibernate API 操纵数据库

HibernateJDBC进行了封装,提供了更加面向对象的 API。如下两图对比了直接经过JDBC API以及经过Hibernate API来访问数据库的两种方式。

如下示例的BusinessService类演示了经过 Hibernate APICustomer 对象进行持久化的操做。

package com.itjob.jiaowu.hibernate;

import javax.servlet.*;

import net.sf.hibernate.*;

import net.sf.hibernate.cfg.Configuration;

import java.io.*;

import java.sql.Date;

import java.sql.Timestamp;

import java.util.*;

public class BusinessService{

public static SessionFactory sessionFactory;

/** 初始化Hibernate,建立SessionFactory实例 */

static{

try{

// 根据默认位置的Hibernate配置文件的配置信息,建立一个Configuration实例

Configuration config = new Configuration();

config.addClass(Customer.class);

// 建立SessionFactory实例 */

sessionFactory = config.buildSessionFactory();

}catch(Exception e){e.printStackTrace();}

}

/** 查询全部的Customer对象,而后调用printCustomer()方法打印Customer对象信息 */

public void findAllCustomers(ServletContext context,OutputStream out) throws Exception{…… }

/** 持久化一个Customer对象 *./

public void saveCustomer(Customer customer) throws Exception{…… }

/** 按照OID加载一个Customer对象,而后修改它的属性 */

public void loadAndUpdateCustomer(Long customer_id,String address) throws Exception{……}

/**删除全部的Customer对象 */

public void deleteAllCustomers() throws Exception{

Session session = sessionFactory.openSession();

Transaction tx = null;

try {

tx = session.beginTransaction();

session.delete("from Customer as c");

tx.commit();

}catch (Exception e) {

if (tx != null) {

tx.rollback();

}

throw e;

} finally {

session.close();

}

}

/** 选择向控制台仍是动态网页输出Customer对象的信息 */

private void printCustomer(ServletContext context,OutputStream out,Customer customer)

throws Exception{

if(out instanceof ServletOutputStream)

printCustomer(context,(ServletOutputStream) out,customer);

else

printCustomer((PrintStream) out,customer);

}

/** Customer对象的信息输出到控制台,如DOS 控制台*/

private void printCustomer(PrintStream out,Customer customer)throws Exception{…… }

/** Customer对象的信息输出到动态网页 */

private void printCustomer(ServletContext context,ServletOutputStream out,Customer customer)

throws Exception{……}

public void test(ServletContext context,OutputStream out) throws Exception{

Customer customer=new Customer();

customer.setName("Tom");

");

customer.setEmail("tom@yahoo.com

customer.setPassword("1234");

customer.setPhone(55556666);

customer.setAddress("Shanghai");

customer.setSex('M');

customer.setDescription("I am very honest.");

//设置Customer对象的image属性,它是字节数组,存放photo.gif文件中的二进

//photo.gif文件和BusinessService.class文件位于同一个目录下

InputStream in=this.getClass().getResourceAsStream("photo.gif");

byte[] buffer = new byte[in.available()];

in.read(buffer);

customer.setImage(buffer);

//设置Customer对象的birthday属性,它是java.sql.Date类型

customer.setBirthday(Date.valueOf("1980-05-06"));

saveCustomer(customer);

findAllCustomers(context,out);

loadAndUpdateCustomer(customer.getId(),"Beijing");

findAllCustomers(context,out);

deleteAllCustomers();

}

public static void main(String args[]) throws Exception {

new BusinessService().test(null,System.out);

sessionFactory.close();

}

}

以上例子演示了经过Hibernate API访问数据库的通常流程。首先应该在应用的启动阶段对Hibernate进行初始化,而后就能够经过 HibernateSession接口来访问数据库。

Hibernate的初始化

BusinessService类的静态代码块负责Hibernate的初始化工做,如读取 Hibernate的配置信息以及对象-关系映射信息,最后建立 SessionFactory 实例。当 JVMJava 虚拟机)加载BusinessService类时,会执行该静态代码块。初始化过程包括以下步骤。

1)建立一个 Configuration类的实例,Configuration 类的构造方法把默认文件路径下的hibernate.properties配置文件中的配置信息读入到内存:

Configuration config = new Configuration();

2)调用Configuration类的addClass(Customer.class)方法:

config.addClass(Customer.class);

该方法把默认文件路径下的Customer.hbm.xml文件中的映射信息读入到内存中。

3)调用Configuration类的buildSessionFactory()方法:

sessionFactory = config.buildSessionFactory();

该方法建立一个SessionFactory实例,并把 Configuration对象包含的全部配置信息拷贝到SessionFactory对象的缓存中。SessionFactory表明一个数据库存储源,若是应用只有一个数据库存储源,那么只需建立一个 SessionFactory 实例。当 SessionFactory 对象建立后,该对象不和 Configuration 对象关联。所以若是再修改 Configuration 对象包含的配置信息,不会对SessionFactory对象有任何影响。

访问HibernateSession接口

初始化过程结束后,就能够调用 SessionFactory实例的openSession()方法来得到 Session实例,而后经过它执行访问数据库的操做。Session接口提供了操纵数据库的各类方法,如:

save()方法:把Java 对象保存数据库中。

update()方法:更新数据库中的Java 对象。

delete()方法:把Java 对象从数据库中删除。

load()方法:从数据库中加载Java 对象。

find()方法:从数据库中查询Java 对象。

Session是一个轻量级对象。一般将每个Session实例和一个数据库事务绑定,也就是说,每执行一个数据库事务,都应该先建立一个新的Session实例。若是事务执行中出现异常,应该撤销事务。不论事务执行成功与否,最后都应该调用 Sessionclose()方法,从而

释放Session实例占用的资源。如下代码演示了用Session来执行事务的流程,其中Transaction类用来控制事务。

Session session = factory.openSession();

Transaction tx;

try {

//开始一个事务

tx = session.beginTransaction();

//执行事务

...

//提交事务

tx.commit();

}

catch (Exception e) {

//若是出现异常,就撤销事务

if (tx!=null) tx.rollback();

throw e;

}

finally {

//无论事务执行成功与否,最后都关闭Session

session.close();

}

下图为正常执行数据库事务(即没有发生异常)的时序图。

BusinessService 类提供了保存、删除、查询和更新 Customer 对象的各类方法。BusinessService类的main()方法调用test()方法,test()方法又调用如下方法:

1saveCustomer()方法

该方法调用Sessionsave()方法,把 Customer 对象持久化到数据库中。

tx = session.beginTransaction();

session.save(customer);

tx.commit();

当运行session.save()方法时,Hibernate执行如下 SQL语句:

insert into CUSTOMERS (ID, NAME, EMAIL, PASSWORD, PHONE, ADDRESS, SEX,

IS_MARRIED,DESCRIPTION, IMAGE, BIRTHDAY, REGISTERED_TIME)

','1234',55556666,'Shanghai','M',0,'I am very honest.',

values(1,'Tom','tom@yahoo.com

?,'1980-05-06',null)

test()方法中并无设置Customer 对象的 id属性,Hibernate会根据映射文件的配置,采用increment标识符生成器自动以递增的方式为 OID 赋值。在 Customer.hbm.xml文件中相

关的映射代码以下:

<id name="id" column="ID" type="long">

<generator class="increment"/>

</id>

test()方法中也没有设置Customer 对象的 registeredTime属性,所以在以上 insert语句中,REGISTERED_TIME 字段的值为 null。但因为 REGISTERED_TIME 字段的 SQL 类型为 TIMESTAMP 类型,若是 insert 语句没有为 TIMESTAMP 类型的字段赋值,底层数据库会自动把当前的系统时间赋值给TIMESTAMP类型的字段。所以,执行完以上 insert语句后,REGISTERED_TIME 字段的值并不为 null,而是插入该记录时的系统时间。

2findAllCustomers()方法

该方法调用Sessionfind()方法,查询全部的 Customer 对象。

tx = session.beginTransaction();

List customers=session.find("from Customer as c order by c.name asc");

for (Iterator it = customers.iterator(); it.hasNext();) {

printCustomer(context,out,(Customer) it.next());

}

tx.commit();

Sessionfind()方法有好几种重载形式,本例中传递的是字符串参数“from Customer as c order by c.name asc”,它使用的是Hibernate查询语言。运行session.find()方法时, Hibernate执行如下SQL语句:

select * from CUSTOMERS order by NAME asc;

3loadAndUpdateCustomer ()方法

该方法调用Sessionload()方法,加载 Customer 对象,而后再修改 Customer 对象的属性。

tx = session.beginTransaction();

Customer c=(Customer)session.load(Customer.class,customer_id);

c.setAddress(address);

tx.commit();

以上代码先调用Sessionload()方法,它按照参数指定的 OID 从数据库中检索出匹配的Customer 对象,Hibernate会执行如下 SQL语句:

select * from CUSTOMERS where ID=1;

loadAndUpdateCustomer()方法接着修改 Customer 对象的 address属性。那么,Hibernate会不会同步更新数据库中相应的CUSTOMERS表的记录呢?答案是确定的。Hibernate采用脏检查机制,按照内存中的Customer 对象的状态的变化,来同步更新数据库中相关的数据,

Hibernate会执行如下SQL语句:

update CUSTOMERS set NAME="Tom",EMAIL="Tom@yahoo.com

"…ADDRESS="Beijing"…

尽管只有Customer 对象的address属性发生了变化,可是 Hibernate执行的 update语句中会包含全部的字段。 在BusinessService类的test()方法中按以下方式调用 loadAndUpdateCustomer ()方法:

saveCustomer(customer);

loadAndUpdateCustomer(customer.getId(),"Beijing");

以上代码并无直接给customer 对象的 id属性赋值,当执行 saveCustomer(customer)方法时,Sessionsave()方法把customer 对象持久化到数据库中,并自动为 id属性赋值。

4. printCustomer()方法

该方法打印 Customer 对象的信息,它有三种重载形式。当 helloapp 应用做为独立应用程序运行时,将调用printCustomer(PrintStream out,Customer customer)方法,向控制台输出Customer 信息; 当 customerApp 应用做为Java Web应用运行时,将调用

printCustomer(ServletContext context,ServletOuputStream out,Customer customer)方法向Web客户输出Customer信息:

private void printCustomer(ServletContext context,ServletOutputStream out,

Customer customer)throws Exception{

//Customer对象的image属性包含的二进制图片数据保存到photo_copy.gif文件中

byte[] buffer=customer.getImage();

String path=context.getRealPath("/");

FileOutputStream fout=new FileOutputStream(path+"photo_copy.gif");

fout.write(buffer);

fout.close();

out.println("------如下是"+customer.getName()+"的我的信息------"+"<br>");

out.println("ID: "+customer.getId()+"<br>");

out.println("口令: "+customer.getPassword()+"<br>");

out.println("E-Mail: "+customer.getEmail()+"<br>");

out.println("电话: "+customer.getPhone()+"<br>");

out.println("地址: "+customer.getAddress()+"<br>");

String sex=customer.getSex()=='M'? "":"";

out.println("性别: "+sex+"<br>");

String marriedStatus=customer.isMarried()? "已婚":"未婚";

out.println("婚姻情况: "+marriedStatus+"<br>");

out.println("生日: "+customer.getBirthday()+"<br>");

out.println("注册时间: "+customer.getRegisteredTime()+"<br>");

out.println("自我介绍: "+customer.getDescription().substring(0,25)+"<br>");

out.println("<img src='photo_copy.gif' border=0><p>"); //显示photo_copy.gif图片

}

5deleteAllCustomers()方法

该方法调用Sessiondelete()方法,删除全部的 Customer 对象:

tx = session.beginTransaction();

session.delete("from Customer as c");

tx.commit();

Sessiondelete()方法有好几种重载形式,本例向delete()方法提供了字符串参数“from Customer as c”,它使用的是Hibernate查询语言(HQLHibernate Query Language)。HQL是一种面向对象的语言,“from Customer as c”字符串指定的是 Customer 类的名字,而非

CUSTOMERS表的名字,其中“as c”表示为 Customer 类赋予别名“c”

运行session.delete()方法时,Hibernate先执行 select语句,查询 CUSTOMERS表的全部Customer 对象:

select * from CUSTOMERS;

接下来Hibernate根据Customer 对象的 OID,依次删除每一个对象:

delete from CUSTOMERS where ID=1;

Hibernate工做原理图

内容总结

? 建立Hibernate的配置文件

? 建立Hibernate的对象-关系映射文件

? 在应用程序中经过Hibernate API来访问数据库

? 掌握存储二进制大数据以及长文本数据的技巧

? 建立持久化类

独立实践

? 使用Hibernate完成用户注册的功能

? 使用Hibernate完成用户登录的功能

? 使用Hibernate完成用户信息修改的功能

? 使用Hibernate完成用户信息删除的功能

? 结合Struts,Hibernate完成用户注册,登录,修改和删除等功能

第三十五章: 映射继承关系

学习目标

? 进一步理解Hibernate的运行原理

? 在Hibernate实现域模型中的各类关系

? 在Hibernate中实现复杂的多对一关联

域模型关系

在域模型中,类与类之间除了关联关系和汇集关系,还能够存在继承关系,在下图的域模型中,Company类和Employee类之间为一对多的双向关联关系(假定不容许雇员同时在多个公司兼职),Employee类为抽象类,所以它不能被实例化,它有两个具体的子类HourlyEmployee类和SalariedEmployee类。因为Java只容许一个类最多有一个直接的父类所以Employee类、HourlyEmployee类和SalariedEmployee类构成了一颗继承关系树。

包含继承关系的域模型

在面向对象的范畴中,还存在多态的概念,多态创建在继承关系的基础上。简单的理解,多态是指当一个 Java 应用变量被声明为 Employee 类时,这个变量实际上既能够引用HourlyEmployee类的实例,也能够引用 SalariedEmployee类的实例。如下这段程序代码就体

现了多态:

List employees= businessService.findAllEmployees();

Iterator it=employees.iterator();

while(it.hasNext()){

Employee e=(Employee)it.next();

if(e instanceof HourlyEmployee){

System.out.println(e.getName()+" "+((HourlyEmployee)e).getRate());

}else

System.out.println(e.getName()+" "+((SalariedEmployee)e).getSalary());

}

BusinessService类的findAllEmployees()方法经过Hibernate API从数据库中检索出全部Employee对象。findAllEmployees()方法返回的集和既包含 HourlyEmployee类的实例,也包含 SalariedEmployee 类的实例,这种查询被称为多态查询。以上程序中变量 e 被声明为Employee 类型,它实际上既可能引用 HourlyEmployee 类的实例,也可能引用SalariedEmployee类的实例。

此外,从 Company 类到 Employee 类为多态关联,由于 Company 类的 employees 集合中能够包含 HourlyEmployee 类和 SalariedEmployee 类的实例。从 Employee 类到 Company类不是多态关联,由于Employee类的company属性只会引用 Company类自己的实例。

数据库表之间并不存在继承关系,那么如何把域模型的继承关系映射到关系数据模型中呢?本章将介绍如下三种映射方式:

? 继承关系树的每一个具体类对应一个表:关系数据模型彻底不支持域模型中的继承关系和多态。

? 继承关系树的根类对应一个表:对关系数据模型进行很是规设计,在数据库表中加入额外的区分子类型的字段。经过这种方式,能够使关系数据模型支持继承关系和多态。

? 继承关系树的每一个类对应一个表:在关系数据模型中用外键参照关系来表示继承关系。

继承关系树的每一个具体类对应一个表

把每一个具体类映射到一张表是最简单的映射方式。如上图所示,在关系数据模型中只需定义 COMPANIESHOURLY_EMPLOYEES SALARIED_EMPLOYEES 表。为了叙述的方便,下文把 HOURLY_EMPLOYEES 表简称为 HE 表,把 SALARIED_EMPLOYEES表简称为SE 表。HourlyEmployee类和 HE 表对应,HourlyEmployee类自己的 rate属性,以及从 Employee 类中继承的 id 属性和 name 属性,在 HE 表中都有对应的字段。此外,HourlyEmployee类继承了Employee类与Company类的关联关系,与此对应,在HE 表中定义了参照COMPANIES表的COMPANY_ID 外键。SalariedEmployee 类和 SE 表对应,SalariedEmployee 类自己的 salary 属性,以及从Employee 类中继承的 id 属性和 name 属性,在 SE 表中都有对应的字段。此外,SalariedEmployee 类继承了 Employee 类与 Company 类的关联关系,与此对应,在 SE 表中定义了参照COMPANIES表的COMPANY_ID 外键。

为每一个具体类对应一个表

Company 类、HourlyEmployee 类和 SalariedEmployee 类都有相应的映射文件,而Employee类没有相应的映射文件。图14-3显示了持久化类、映射文件和数据库表之间的对应关系。

持久化类、映射文件和数据库表之间的对应关系

若是 Employee 类不是抽象类,即 Employee 类自己也能被实例化,那么还须要为Employee类建立对应的EMPLOYEES表,此时HE表和 SE 表的结构仍然和上图中的同样。这意味着在 EMPLOYEES 表、HE 表和 SE 表中都定义了相同的 NAME 字段以及参照COMPANIES 表的外键 COMPANY_ID。另外,还需为 Employee 类建立单独的Employee.hbm.xml文件。

建立映射文件

Company 类到 Employee 类是多态关联,可是因为关系数据模型没有描述 Employee类和它的两个子类的继承关系,所以没法映射 Company类的 employees集合。下面的示例是Company.hbm.xml文件的代码,该文件仅仅映射了 Company类的 idname属性。

<hibernate-mapping >

<class name="com.itjob.jiaowu.hibernate.HourlyEmployee"

table="HOURLY_EMPLOYEES">

<id name="id" type="long" column="ID">

<generator class="increment"/>

</id>

<property name="name" type="string" column="NAME" />

<property name="rate" column="RATE" type="double" />

<many-to-one

name="company"

column="COMPANY_ID"

class="mypack.Company"

/>

</class>

</hibernate-mapping>

SalariedEmployee.hbm.xml文件用于把 SalariedEmployee类映射到 SE 表,在这个映射文件中,除了须要映射 SalariedEmployee 类自己的 salary 属性,还须要映射从 Employee 类中继承的name属性,此外还要映射从 Employee类中继承的与Company类的关联关系。下面的示例是SalariedEmployee.hbm.xml文件的代码。

<hibernate-mapping >

<class name="mypack.SalariedEmployee

<id name="id" type="long" column="I

<generator class="increment"/>

</id>

<property name="name" type="string"

<property name="salary" column="SA

<many-to-one

name="company"

column="COMPANY_ID"

class="mypack.Company"

/>

</class>

</hibernate-mapping>

因为Employee类没有相应的映射文件,所以在初始化Hibernate时,只需向Configuration对象中加入Company类、HourlyEmployee类和 SalariedEmployee类:

Configuration config = new Configuration();

config.addClass(Company.class)

.addClass(HourlyEmployee.class)

.addClass(SalariedEmployee.class);

操纵持久化对象

这种映射方式支持多态查询,对于如下查询语句:

List employees=session.find("from Employee");

Hibernate会检索出全部HourlyEmployee对象和SalariedEmployee 对象。此外,也能够单独查询Employee类的两个子类的实例,例如:

List hourlyEmployees=session.find("from HourlyEmployee");

对于本例程序,在运行该程序前,须要在 SAMPLEDB 数据库中手工建立 COMPANIES 表、EMPLOYEES 表、HE 表和 SE 表,而后加入测试数据,相关的SQL脚本文件为/schema/sampledb.sql

DOS命令行下进入chapter14根目录,而后输入命令:

ant -file build3.xml run

就会运行 BusinessService 类。BusinessService main()方法调用 test()方法,test()方法依次调用如下方法:

? findAllHourlyEmployees():检索数据库中全部的 HourlyEmployee对象。

? findAllEmployees():检索数据库中全部的 Employee对象。

? loadCompany():加载一个Company对象。

? saveEmployee():保存一个Employee对象。

1.运行findAllHourlyEmployees()方法,它的代码以下:

tx = session.beginTransaction();

List results=session.find("from HourlyEmployee");

tx.commit();

return results;

在运行Sessionfind()方法时,Hibernate执行如下 select语句:

select * from HOURLY_EMPLOYEES he inner join EMPLOYEES e

on he.EMPLOYEE_ID=e.ID;

select * from COMPANIES where ID=1;

Hibernate经过HE表与EMPLOYEES表的内链接得到HourlyEmployee对象的全部属性值,此外,在加载HourlyEmployee对象时,还会同时加载与它关联的Company对象。

2.运行findAllEmployees()方法,它的代码以下:

tx = session.beginTransaction();

List results=session.find("from Employee");

tx.commit();

return results;

在运行Sessionfind()方法时,Hibernate执行如下 select语句:

select * from EMPLOYEES e

left outer join HOURLY_EMPLOYEES he on e.ID=he.EMPLOYEE_ID

left outer join SALARIED_EMPLOYEES se on e.ID=se.EMPLOYEE_ID;

select * from COMPANIES where ID=1;

Hibernate EMPLOYEES 表与 HE 表以及 SE 表进行左外链接,从而得到HourlyEmployee对象和SalariedEmployee对象的全部属性值。在这种映射方式下,Hibernate支持多态查询,对于以上查询语句得到的查询结果,若是 HE 表的 EMPLOYEE_ID 字段不为null,就建立HoulyEmployee实例,若是 SE 表的EMPLOYEE_ID 字段不为null,就建立SalariedEmployee实例,这些实例所关联的 Company对象也被加载。

3.运行loadCompany()方法,它的代码以下:

tx = session.beginTransaction();

Company company=(Company)session.load(Company.class,new Long(id));

Hibernate.initialize(company.getEmployees());

tx.commit();

这种映射方式支持多态关联。若是在 Company.hbm.xml文件中对employees集合设置了当即检索策略,那么 Sessionload()方法加载的 Company对象的 employees 集合中包含全部关联的Employee对象。因为本书提供的 Company.hbm.xml文件对 employees集合设置了

延迟检索策略,所以以上程序代码还经过 Hibernate 类的静态 initialize()方法来显式初始化employees集合。

4.运行saveEmployee(Employee employee)方法,它的代码以下:

tx = session.beginTransaction();

session.save(employee);

tx.commit();

test()方法中,建立了一个 HourlyEmployee 实例,而后调用 saveEmployee()方法保存这个实例:

Employee employee=new HourlyEmployee("Mary",300,company);

saveEmployee(employee);

Sessionsave()方法能判断employee变量实际引用的实例的类型,若是 employee变量引用HourlyEmployee实例,就执行以下 insert语句:

insert into EMPLOYEES (ID,NAME, COMPANY_ID) values (5, 'Mary', 1);

insert into HOURLY_EMPLOYEES (EMPLOYEE_ID ,RATE) values (5, 300);

可见,每保存一个HourlyEmployee对象,须要分别向 EMPLOYEES表和HE 表插入一条记录,EMPLOYEES表的记录和HE 表的记录共享同一个主键。

选择继承关系的映射方式

本章介绍的三种映射方式各有优缺点,下表对这三种映射方式做了比较。

比较三种映射关系

若是不须要支持多态查询和多态关联,能够采用每一个具体类对应一个表的映射方式,若是须要支持多态查询和多态关联,而且子类包含的属性很少,能够采用根类对应一个表的映射方式,若是须要支持多态查询和多态关联,而且子类包含的属性不少,能够采用每一个类对应一个表的映射方式。若是继承关系树中包含接口,能够把它看成抽象类来处理。

下显示了一颗复杂的继承关系树,其中 DOClass类为抽象类,其余均为具体类。

复杂的继承关系树

能够将上图的继承关系树分解为3