WEB应用程序的测试与优化

“让你的WEB应用程序完成你想做的事情是一回事,而让他们快速、有效的去做常常是另外一回事。” 在这篇文章里我将初步讨论有关“WEB应用程序的性能”的问题,主要是一些基本概念以及工具,算是抛砖引玉吧!注意这些内容同样适用于J2EE等应用。此后,也许我会写更多关于此方面的文章。当然,是“也许”。好了,祝我们好运吧! 首先,这里有两个性能方面的重要指标。请注意,下面的“定义”并不规范,仅供参考。 * Response Time - 响应时间 从初始化请求到完成响应所用的时间。这是一个测试WEB应用程序速度的重要度量。 * Scalability - 伸缩性 一个可伸缩的应用程序的响应时间随负载的增加而线性增加。 没有WEB应用程序可以处理无限数目的请求,但是一般我们可以预测一个范围,并保证我们的WEB应用程序可以在此范围内“优雅”的伸缩,即始终把响应时间维持在可接受的级别。 如果我们打算优化我们的WEB应用程序,那么至少我们应该知道它到底该不该优化。压力测试可以解答这个问题。 * Load Test - 压力测试 为WEB应用程序模拟用户请求以测量其伸缩性的过程。 它非常有用,虽然开始的时候会觉得有点“变态”。一般我们会模拟大量的用户请求以获得在WEB应用程序的速度恶化到无法接受的级别前能够处理的并发请求数量。 所谓“无法接受的级别”并不是一定要到实例池崩溃、应用服务器瘫痪甚至服务器当机的时候,这要视需求而定。 一般压力测试包含如下步骤: * 确定接受请求并完成响应的最大允许的延时。 * 估计WEB应用程序的最大并发用户数量。 * 模拟用户请求,以一个比较小的负载开始,逐渐增加模拟用户的数量, 直到WEB应用程序的 相应延时超过最大延时。 * 如果负载比估计的用户数量小,那么应该优化这个WEB程序,否则你选择性的执行优化。 你不会在考虑自己写一个测试程序吧?算了,何必再重新发明一次轮子?这里有一些压力测试工具,它们各有特色,先介绍免费的: * Web Application Stress Tool, Microsoft, http://www.microsoft.com * JMeter, Java Apache Project, http://www.apache.org/ * LoadItUp, BroadGun Software, http://www.broadgun.com 如果你或你的公司很有Money,或者需要更加丰富的功能,可以使用以下商业软件,不过它们都价值$10,000,甚至更多: * WebLoad, RadView Software, http://www.radview.com/ * SilkPerformer, Segue Software, http://www.segue.com/ * Benchmark Factory, Quest Software, http://www.benchmarkfactory.com/ * LoadRunner, Mercury Interactive, http://www.mercuryinteractive.com/ 无论你选择哪种工具,它都至少应该提供以下的功能,以便为以提供丰富且有意义的测试数据: * 发送GET和POST请求 * “记录”从浏览器发送的GET和POST请求(以免开发者需要手写这些合适的请求)。 * 获取和发送COOKIE。 * 多线程 * 模拟用户延迟 * 记录性能数据 * 控制带宽 我打赌如果你以前没有接触过以上这些内容,那么你的WEB应用程序很难在压力测试中获得令人满意的结果。你会看到响应时间会随着请求数量的增多而暴涨,甚至出现一些我们不想看到的情况,比如“拒绝连接”。 一旦没能通过压力测试我们应该如何应对呢?优化!没错,不过我们怎么知道那里应该优化呢?Profiler可以对此提供很多的帮助。(我不知道怎么翻译它更贴切一些,所以干脆不翻译了!) Profiler提供这样的功能,它可以检测你的应用程序并提供一些有用的运行时信息,比如某块代码的执行时间、内存/堆的使用情况、内存中的对象实例数量等等。比如,我们想知道到底是哪个Java对象的哪个方法耗费了更多的时间。 以下是一些Profiler: * Quantify,Rational Software,http://www.rational.com/ * Optimizeit,Intuitive Systems,http://www.optimizeit.com/ * JProbe,Sitraka Software,http://www.jprobe.com/ 请注意,我们不能过分依赖工具,虽然它们很多时候可以极大的是我们的工作变得简单、轻松。一般如果你知道了系统的瓶颈所在,修改往往是一件相对轻松的事情。个人认为寻找、发现系统的瓶颈所在才是最关键、也是最体现一个人功力的步骤。这是一个非常专业的问题,它需要你对所使用的应用平台、软件架构、数据库系统、网络环境等等诸多方面非常深的造诣。这并不夸张,任何一个会JSP和JDBC的人都可以写出一个WEB应用程序来,但是那还相差太远……限于篇幅,话题先进行到这里。其实你可以在很多地方找到有关测试和优化技巧的文章,都非常有针对性,比如TheServerSide.com、JavaLobby.com等。好啦!休息一下!Java之路很长,一步一步走吧!! <淘宝热门商品:
 

 

【上海商盟】高更食品极美滋新奥尔良系列烧烤腌料皇冠旗舰店

 

 

新势力品牌旗舰店/LED手表 手机话筒 负离子表 钛项圈大量批发!

大量批发


来源:程序员网

小小豆叮

一个简单实用的数据库操作框架

前言
这个小小的数据库操作封装框架是参考IBM开发网上的两篇文章并在其基础上扩充了一些功能而得到的。所以首先要感谢两篇文章的作者。
学习JDBC以来一直想实现一个简单的封装来方便编程但是由于水平有限一直没有较好的办法,看了IBM开发网上的两篇文章以后感觉作者的设计思想很好一定能扩充成一个实用的JDBC封装。所以我在文章提供的源码基础上加了一些功能这些功能包括支持多种数据类型,处理了空值,利用反射方便的在Row对象和值对象之间进行转换,还有加了一个我自认为通用的DAO类来方便用户的操作。
我把源码提供出来有两个目的一个是希望能帮助比我还初学的初学者熟悉JDBC,另外就是请各位高手不吝赐教,改进程序中的错误如果能将你们的对JDBC的封装方法提供出来那就更好了(不要说你们只用EJB或者Hibernate,JDO什么的)。
IBM开发网的那两篇文章分别是《一个简单的 JDBC 包装器》《对一个简单的 JDBC 包装器的扩展及应用》,我的邮箱是xsimple2003@yahoo.com.cn有事请与我联系。
设计思想
 把DBMS抽象成类Database,这个类负责管理数据库连接以及提供表对象。
 把数据库中的一张或多张表抽象成类Table,这个类中提供对表的添加,修改,删除的JDBC封装。
 将数据库表中的一条记录抽象成类Row,这个类用HashMap保存关系数据库中表格中一行数据的字段名和值并提供一些相关操作。另外这个类还提供了两个静态方法用于在Row对象和ValueObject之间进行方便的转换。
 把对个Row的集合抽象成RowSet,这个类中用一个vector把多个Row对象保存起来并提供一些相关操作。
代码分析
由于已经给出源码所以我只对代码中关键的和需要注意的地方加以说明,大家可以执行源码一边演示一边体会。
 Database类源码如下:
package com.gdrj.util.database;
import java.sql.*;
import javax.sql.*;
import com.gdrj.util.servicelocator.*;
public class Database {
/**
* 这个数据库连接成员只有在与数据库直接建立连接的情况下是有效的
*/

private Connection conn = null;
/**
* 当这个参数有效时,表明程序是直接与数据库建立的连接而不是从连接池里取得连接
*/

private String url, user, password;
/**
* 当这个参数有效时,表明程序是从连接池里取得连接。
*/

private String datasource;
/**
* 用数据库地址,用户名,密码初始化数据库对象,这个构造器用于程序是直接
* 与数据库建立连接的情况。
* @param url
* @param user
* @param password
*/

public Database(String url, String user, String password) {
this.url = url;
this.user = user;
this.password = password;
}

/**
* 用JNDI数据源名初始化数据库对象,这个构造器用于从连接池取数据库连接的情况。
* @param datasource
*/

public Database(String datasource) {
this.datasource = datasource;
}

/**
* 得到数据库连接,对于是否从连接池里取连接做了自动处理即根据用户调用了哪个构造器
* 来判断是否直接与数据库建立连接还是从连接池里取连接。
* 对于用户来说不用考虑程序是从那里取得连接,他只管正确的初始化数据库对象。
* @return
* @throws SQLException
*/

public Connection getConnection() throws Exception {
if (datasource == null) { //直接与数据库建立连接
if (conn == null) {
conn = DriverManager.getConnection(url, user, password);
}
}
else { //从应用服务器的连接池里取得连接
ServiceLocator sl = ServiceLocator.getInstance();
DataSource ds = sl.getDataSource(datasource);
return ds.getConnection();//每调用一次都返回一个连接池中的数据库连接
}
return conn;
}

/**
* 释放连接,如果是直接与数据库连接的情况则什么也不做
* 如果是从连接池中取得的连接那么释放传来的连接
* @param conn
*/

public void disConnect(Connection connection) {
if (datasource != null) { //只处理从连接池取连接的情况
try {
if (connection != null) {
connection.close();
}
}
catch (Exception ex) {}
}
}

/**
* 得到与参数名对应的表对象,注意这里不作任何数据库操作
* @param name
* @return
*/

public Table getTable(String name) {
return new Table(this, name);
}
}

这个类是对DBMS的抽象,所以使用时应用程序中只要有一个Database对象就够了,如果你是以与数据库之间建立连接的方式使用那么你用Database(String url, String user, String password)构造器进行初始化。如果是从应用服务器的连接池中取得连接的方式使用那么用Database(String datasource)构造器初始化,这样以后你使用这个对象进行getConnection和disConnection时就不用去考虑始终保持一个连接(C/S方式),还是将连接返回连接池了因为在disConnection中已经做了处理。集体使用方法将Table类。在getConnection中的从连接池中取连接的代码你只要参考以下《J2EE核心模式》中的服务定位器模式就知道是怎么回事了,你在用Database(String url, String user, String password)初始化时其中的代码不起作用。
 Table类源码如下:
package com.gdrj.util.database;
import java.sql.*;
import java.util.*;
import com.gdrj.util.*;
public class Table {
/**
* 通过这个数据库对象得到数据库连接
*/

private Database database;
/**
* 数据库中一个或多个(只限查询)表的名
*/

private String name;
/**
* 初始化表对象,此时不作任何数据库相关操作
* 一般通过database的getTable调用
* @param database
* @param name
*/

public Table(Database database, String name) {
this.database = database;
this.name = name;
}
/**
* 查询某一行
* @return
*/

public Row getRow(String fields, String criteria, Object[] args) throws
DBAccessException {
RowSet rows = executeQuery(fields, criteria, args);
if (rows == null) {
return null;
}
return rows.get(0);
}
/**
* 得到一个多行记录
* @param criteria 查询条件
* @param args 查询条件的参数列表
* @return
*/

public RowSet getRows(String fields, String criteria, Object[] args) throws
DBAccessException {
return executeQuery(fields, criteria, args);
}
/**
* 执行SQL查询
* @param fields 要查询的字段,如果传入null则表示查询表中所有字段
* @param criteria用户输入的查询Where条件
* @param args 用到的参数数组
* @return 返回符合结果行集
*/

private RowSet executeQuery(String fields, String criteria, Object[] args) throws
DBAccessException {
Connection conn = null;
RowSet rows = new RowSet();
String sql = null;
if (fields == null) {
fields = "*";
}
try {
conn = database.getConnection(); //取得数据库连接,在方法内部对不同的连接情况进行了处理
sql = "select " + fields + " from " + name +
( (criteria == null) ? "" :
(" where " + criteria));
PreparedStatement pstmt = conn.prepareStatement(sql);
if (args != null) { //如果有查询参数则设置参数
for (int i = 0; i < args.length; i++) {
pstmt.setObject(i + 1, args[i]);
}
}
ResultSet rs = pstmt.executeQuery();
ResultSetMetaData rsmd = rs.getMetaData();
int cols = rsmd.getColumnCount();
/**@todo 判断是否为零*/
if (cols == 0) {
return null;
}
while (rs.next()) {
Row row = new Row();
for (int i = 1; i <= cols; i++) {
String name = rsmd.getColumnName(i);
Object value = rs.getObject(i); //作通用类型处理,这样row中的类型都是Object型的。
/**
* 这里要做空值处理,因为在进行RowToValueObject转换时如果是空值则不能得到值的类型
* 所以如果是空值那么把value设置成类型信息
*/

if (value == null) {
value = Class.forName(rsmd.getColumnClassName(i));
}
// System.out.println(value.getClass());//用于得到数据库中的类型对应Java中的什么类型
row.put(name, value);
}
rows.add(row);
}
rs.close();
pstmt.close();
}
catch (Exception ex) {
throw new DBAccessException(InforGeter.getErrorInfor(this, "executeQuery",
ex, "执行SQL(" + sql + ")查询时出错!"));
}
finally {
database.disConnect(conn); //调用数据库对象的释放连接方法(此方法内对取得连接方式的不同情况做了处理)
}
return rows;
}
/**
* 增加一行
* @param row
*/

public int putRow(Row row) throws DBAccessException {
return putRow(row, null, null);
}
/**
* 修改一行(没有条件就是增加)
* @param row
* @param conditions
*/

public int putRow(Row row, String conditions, Object[] args) throws
DBAccessException {
String ss = "";
int affectableRow = 0; //执行SQL后影响的行数
if (conditions == null) {
ss = "INSERT INTO " + name + "(";
for (int i = 0; i < row.length(); ++i) {
String k = row.getKey(i);
ss += k;
if (i != row.length() - 1) {
ss += ", ";
}
}
ss += ") VALUES (";
for (int j = 0; j < row.length(); ++j) {
ss += (row.get(j) == null) ? "null" : "?"; //如果row中有空值则设置为null,否则设置为查询参数
if (j != row.length() - 1) {
ss += ", ";
}
}
ss += ")";
}
else {
ss = "UPDATE " + name + " SET ";
for (int i = 0; i < row.length(); ++i) {
String k = row.getKey(i);
ss += k + "=" + ( (row.get(i) == null) ? "null" : "?"); //设置查询参数
if (i != row.length() - 1) {
ss += ", ";
}
}
ss += " WHERE ";
ss += conditions;
}
Connection conn = null;
try {
conn = database.getConnection();
PreparedStatement st = conn.prepareStatement(ss);
int j = 0; //查询参数计数器
for (int i = 0; i < row.length(); i++) {
if (row.get(i) != null) { //如果不是空则解析查询参数
st.setObject(++j, row.get(i)); //解析查询参数
}
}
if (args != null) {
for (int i = 0; i < args.length; i++) {
st.setObject(++j, args[i]);//预定的规则,null不能放到查询参数中要以name=null的静态形式存放
}
}
affectableRow = st.executeUpdate();
st.close();
}
catch (Exception ex) {
ex.printStackTrace();
throw new DBAccessException(InforGeter.getErrorInfor(this, "putRow", ex,
"更新表" + name + "中的数据时出错!"));
}
finally {
database.disConnect(conn);
}
return affectableRow;
}

/**
* 删除一行
* @param row
*/

public int delRow(Row row) throws DBAccessException {
String ss = "";
int affectableRow = 0;
ss = "delete from " + name + " where ";

for (int i = 0; i < row.length(); ++i) {
String k = row.getKey(i);
ss += k + ((row.get(i) == null)?" is null":"=?"); //设置查询参数有空值处理
if (i != row.length() - 1) {
ss += " and ";
}
}
Connection conn = null;
try {
conn = database.getConnection();
PreparedStatement st = conn.prepareStatement(ss);
int j = 0;//查询参数计数器
for (int i = 0; i < row.length(); i++) {
if (row.get(i) != null) {
st.setObject(++j, row.get(i)); //解析查询参数
}
}
affectableRow = st.executeUpdate();
st.close();
}
catch (Exception ex) {
throw new DBAccessException(InforGeter.getErrorInfor(this, "delRow", ex,
"删除表" + name + "中的数据时出错!"));
}
finally {
database.disConnect(conn);
}
return affectableRow;
}
/**
* 有条件的删除即删除多行
* @param condition
* @param args
*/

public int delRow(String condition, Object[] args) throws DBAccessException {
String ss = "";
int affectableRow = 0;
ss = "delete from " + name + " where ";
ss += condition;
Connection conn = null;
try {
conn = database.getConnection();
PreparedStatement st = conn.prepareStatement(ss);
if (args != null) {
for (int i = 0; i < args.length; i++) {
st.setObject(i + 1, args[i]);
}
}
affectableRow = st.executeUpdate();
st.close();
}
catch (Exception ex) {
throw new DBAccessException(InforGeter.getErrorInfor(this, "delRow", ex,
"删除表" + name + "中的数据时出错!"));
}
finally {
database.disConnect(conn);
}
return affectableRow;
}
}

使用时可以用Database对象的getTable方法传入数据库表的名称来得到一个Table对象。得到这个对象后就可以对这个数据库表进行操作了,这个类提供了六个方法根据传过来的参数对数据库表进行添加修改删除操作。代码中没有特别难懂的地方,需要注意的是我在原有代码的基础上对空值进行的处理,在查询时如果表中的数据是空值的话那么我把字段对应的Java类型放到Row对象里,因为在进行Row对象到值对象的转换时用到了java反射API必须知道Row中的字段值的类型才能去调用值对象的setXXXX方法(见Row对象的toValueObject方法)。
 行对象的源码如下:
package com.gdrj.util.database;
import java.util.*;
import java.math.BigDecimal;
import java.lang.reflect.*;
public class Row {
/**
* 排序,由于Hashtable不提供通过索引取得值的方法,并且其中的键值对也不是按照put上去时的顺序排列的。
* 注意:Vector中加入的对象是有序的,即按加入的顺序排列并且能够根据索引访问,可以看成是可变大小的数组
* List可以取代Vector
*/

private Vector ordering = new Vector();
/**
* 存放键值对(表中字段名称与字段值)
*/

private HashMap map = new HashMap();
public Row() {
}
/**
* 向HashMap中追加键值对,即字段名称与字段值
* @param name
* @param value
*/

public void put(String name, Object value) {
if (!map.containsKey(name)) {
ordering.addElement(name); //将键保存起来
}
map.put(name, value);
}
/**
* 得到行对象中字段的个数
* @return
*/

public int length() {
return map.size();
}
/**
* 根据字段名称取得字段值
* @param name
* @return
*/

public Object get(String name) {
return map.get(name);
}
/**
* 根据字段在HashMap中的编号取得字段值
* @param which
* @return
*/

public Object get(int which) {
String key = (String) ordering.elementAt(which);
return map.get(key);
}
/**
* 根据字段序号取得字段名称
* @param which
* @return
*/

public String getKey(int which) {
String key = (String) ordering.elementAt(which);
return key;
}
/**
* 打印,用于调试
*/

public void dump() {
for (Iterator e = map.keySet().iterator(); e.hasNext(); ) {
String name = (String) e.next();
Object value = map.get(name);
System.out.print(name + "=" + value + ", ");
}
System.out.println("");
}
/**
* 将行对象转换成值对象
* @param row
* @param type值对象类型
* @return
* @throws java.lang.Exception 这里的异常一般在DAO中处理,因为DAO调用
* 这个方法进行Row和ValueObject的转换
*/

public static Object toValueObject(Row row, Class type) throws Exception {
Object vo = type.newInstance(); //创建一个值对象
Field[] fields = type.getDeclaredFields(); //得到值对象中所有字段
for (int i = 0; i < fields.length; i++) {
String name = fields[i].getName(); //得到JavaBean的字段名
String nameInRow = toInRowName(name);//在此进行值对象名称到行对象名称的转换
Object value = row.get(nameInRow); //得到从数据库中取出的与字段名对应的值
String methodName = "set" + Character.toUpperCase(name.charAt(0)) +
name.substring(1); //得到setXXXX方法名
Class argClass = null;
if (value instanceof Class) {
argClass = (Class)value;
value = null;
}else{
argClass = value.getClass();
}
Method method = type.getMethod(methodName, new Class[] {argClass}); //得到set方法
method.invoke(vo, new Object[] {value});//调用setXXXX方法
}
return vo;
}
/**
* 根据传过来的值对象和类型把值对象转换到行对象中
* @param vo
* @return
* @throws java.lang.Exception 这里的异常一般在DAO中处理,因为DAO调用
* 这个方法进行Row和ValueObject的转换
*/

public static Row fromValueObject(Object vo) throws Exception {
Row row = new Row();
Class type = vo.getClass(); //得到Class用于进行反射处理
Field[] fields = type.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
String name = fields[i].getName();
String methodName = "get" + Character.toUpperCase(name.charAt(0)) +
name.substring(1);
Method method = type.getMethod(methodName, new Class[] {});
Object value = method.invoke(vo, new Object[] {});
String nameInRow = toInRowName(name);//在此进行值对象中的名称向行对象中的名称转换
row.put(nameInRow, value);
}
return row;
}
/**
* 将值对象中属性名转换成对应的行对象中的字段名(因为行对象中的字段名
* 在更新数据库时必须与数据库表中字段名完全匹配)
* 一般规则为 fsiId ---> fsi_id(现在假设的情况是如果出现有两个单词
* 以上的值对象属性名则数据库表中的字段名一定是有下划线的)
* @param voName
* @return
*/

public static String toInRowName(String voName) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < voName.length(); i++) { //遍历voName如果有大写字母则将大写字母转换为_加小写
char cur = voName.charAt(i);
if (Character.isUpperCase(cur)) {
sb.append("_");
sb.append(Character.toLowerCase(cur));
}
else {
sb.append(cur);
}
}
return sb.toString();
}
}

Row对象中用了一个HashMap对象存放着对应数据库表中的字段名和对应值,由于Map对象的无序性,所以用了一个vector(当然也可以用List代替)来存放字段名(按用户添加的顺序)这样就可以提供get(int i)方法来顺序取得Map中的值了。要注意的是三个静态辅助方法toValueObject,fromVauleObject,toInRowName。toValueObject方法用于将一个行对象转换为值对象方法中利用了Java的多态和反射机制(请大家参考反射API)。FromValueObject是上一个方法的逆操作,toInRowName方法是实现值对象中的属性名向数据库表中字段名的转换,因为一般在数据库建表时是用的这种形式stu_id,而Java中JavaBean的属性是这样的stuId。
 RowSet的代码如下:
package com.gdrj.util.database;
import java.util.*;
public class RowSet {
private Vector vector = new Vector();

public RowSet() {
}

public void add(Row row) {
vector.addElement(row);
}

public int length() {
return vector.size();
}

public Row get(int which) {
if (length() < 1) {
return null;
}
else {
return (Row) vector.elementAt(which);
}
}

public void dump() {
for (Enumeration e = vector.elements(); e.hasMoreElements(); ) {
( (Row) e.nextElement()).dump();
}
}
}

这个类就是把Row对象放到Vector以便操作。就不多说了。
 为了方便使用我写了一个GeneralDAO类(我对DAO模式还在理解中请各位高手批评指教)代码如下:
package com.gdrj.util.database;
import java.util.*;
public class GeneralDAO {
/**
* 这个DAO对应的表对象
*/

private Table table;
/**
* 默认构造函数
*/

public GeneralDAO() {
}

/**
* 用数据库对象和表名初始化DAO
* @param db
* @param tableName
*/

public GeneralDAO(Database db, String tableName) {
getTable(db, tableName);
}
private void getTable(Database db, String name) {
table = db.getTable(name);
}
/**
* 根据条件将查找到的数据以值对象集合的形式返回
* @param fields 要查找的字段(*或null表示所有字段)
* @param criteria查询条件
* @param args与查询条件对应的参数数组
* @param voType值对象的类型
* @return
* @throws java.lang.Exception
*/

public Collection findDatas(String fields, String criteria, Object[] args,
Class voType) throws Exception {
RowSet rows = table.getRows(fields, criteria, args);
Collection col = new ArrayList();
for (int i = 0; i < rows.length(); i++) {
Object vo = Row.toValueObject(rows.get(i), voType); //返回一个值对象,注意是voType类型的对象
col.add(vo);
}
return col;
}

/**
* 向表中插入一条数据
* @param vo 与表对象对应的值对象
* @return
* @throws java.lang.Exception
*/

public int insertData(Object vo) throws Exception {
return table.putRow(Row.fromValueObject(vo));
}

/**
* 更新一条数据
* @param vo 与表对象对应的值对象
* @param criteria更新条件
* @param args与更新条件对应的参数数组
* @return
* @throws java.lang.Exception
*/

public int updateData(Object vo, String criteria, Object[] args) throws
Exception {
return table.putRow(Row.fromValueObject(vo), criteria, args);
}

/**
* 删除一条数据(条件比较严格各个字段的值必须与值对象中属性的值匹配)
* @param vo
* @return
* @throws java.lang.Exception
*/

public int deleteData(Object vo) throws Exception {
return table.delRow(Row.fromValueObject(vo));
}
/**
* 删除多条数据
* @param condition 删除条件
* @param args与条件对应的参数数组
* @return
* @throws java.lang.Exception
*/

public int deleteDatas(String condition, Object[] args) throws Exception {
return table.delRow(condition, args);
}
}

这个DAO类是对Table类的一个方便的封装。用户如果向操作数据库只要创建一个Database对象,一个DAO对象,一个值对象(对应表结构),然后就可以进行方便的数据库操作了,下面给出一个实例来演示这个小小框架的用法。
演示程序
首先建立一个teacher表,语法如下
create table teacher (
id int not null,
name varchar(20) not null,
birthday smalldatetime null,
address varchar(100) null,
income money null,
constraint id PRIMARY KEY NONCLUSTERED ( id )
)
然后建立一个与teacher表对应的值对象类。
public class TeacherVO implements Serializable {
private Integer id;
private String name;
private String address;
private BigDecimal income;
private java.sql.Timestamp birthday;
public TeacherVO() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public java.sql.Timestamp getBirthday() {
return birthday;
}
public void setBirthday(java.sql.Timestamp birthday) {
this.birthday = birthday;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public java.math.BigDecimal getIncome() {
return income;
}
public void setIncome(java.math.BigDecimal income) {
this.income = income;
}
public String toString(){
return " 编号:" + id + " 姓名:" + name + " 生日:" + birthday
+ " 地址:" + address + " 收入:" + income;
}
}

最后主程序的源码如下:
package org.together.jdbcwrap.test;
import java.util.*;
import com.gdrj.util.database.*;

public class GeneralDAOExample {
public static void main(String[] args)throws Exception {
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
Database db = new Database("jdbc:odbc:emmis","sa","815023");
GeneralDAO dao = new GeneralDAO(db,"teacher");
/**
* 利用GeneralDAO进行查询
*/

Collection col = dao.findDatas("*","birthday is null",null,TeacherVO.class);
for (Iterator iter = col.iterator(); iter.hasNext(); ) {
Object item = iter.next();
System.out.println("item = " + item);
}
/**
* 利用GeneralDAO进行添加
*/

TeacherVO vo = new TeacherVO();
vo.setAddress("沈阳");
vo.setBirthday(new java.sql.Timestamp(0));
vo.setId(new Integer(11));
vo.setIncome(new java.math.BigDecimal(1000));
vo.setName("陶小川");
// dao.insertData(vo); //添加一条记录
// dao.updateData(vo,"id=10",null); //更新一条记录
// dao.deleteData(vo); //删除一条记录
// dao.deleteDatas("id>5",null); //添加符合条件记录
}
}

<淘宝热门商品:
 

12.00 元  

08VIVI杂志推荐 风靡韩国牛仔感七分打底裤

 

¥:21.0 

【华佗天然居】天然花草茶花茶中草药中药材

花草茶之瘦腿茶:迷迭香+柠檬草+马鞭草30包21元包快递美容减肥茶


来源:程序员网

小小豆叮

如何用jsp输出存在于oracle数据库Blob字段中的jpg图片

在web-oa系统中,档案管理好象不可或缺。其中员工照片常常做成这样:用户通过浏览器上传员工相片,服务端程序接收图片文件,保存到数据库中的某个表的Blob字段里(关于文件上传和blob字段的更新,请参考http://www.javaresearch.org/article/showarticle.jsp?column=106&thread=11003和http://forum.hibernate.org.cn/viewtopic.php?t=254)。通过(jdbc/jdo)和(jsp/servlet),服务器再把保存在Blob字段中的图片文件展现给用户。我这里给出一个关于展现的简单例子。 1.在数据库中建表,并且表要包含Blob字段。 2.用plsql developer这种客户端数据库工具,修改某条记录的Blob字段,存入一个jpg文件。 3.用jdbc或者hibernate获取某条记录的Blob字段数据。(请参考参考资料2) 4.写一个jsp文件,来输出图片。如下所示 //////////////////////cwry_pic.jsp///////////////////////// <%@page import="java.io.*"%> <%@page import="com.jagie.business.profile.*" %> <%@page import="java.sql.Blob"%> <% response.reset(); //这个设置很重要,否则客户端浏览器不能识别输出内容,导致弹出下载的对话框。 response.setContentType("image/jpeg"); ServletOutputStream sos = response.getOutputStream(); //这里是用hibernate来根据id装载对象,你可以用别的方式如jdbc来组装对象,附文中有 //BaseInfo这个javabean的示意性代码. BaseInfo bif= ProFileOperator.loadProFile(Long.valueOf("1")); //输出图片 if(bif!=null&&bif.getPic()!=null){ Blob blob=(Blob)bif.getPic(); InputStream pi = blob.getBinaryStream(); int blobsize = (int)blob.length(); byte[] blobbytes = new byte[blobsize]; int bytesRead = 0; while ((bytesRead = pi.read(blobbytes)) != -1) { sos.write(blobbytes, 0, bytesRead); } pi.close(); sos.flush(); } %> 发布这个jsp到你的web服务器,再用浏览器访问这个jsp页面,你就可以看到保存在Blob字段中的图片了。 附文: ///////////////////BaseInfo.java示意性代码/////////////////////// package com.jagie.business.profile; public class BaseInfo{ //pk private Long id; //相片 private java.sql.Blob pic; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public java.sql.Blob getPic() { return pic; } public void setPic(java.sql.Blob pic) { this.pic = pic; } } 最后,希望这篇文章能对你有所启发,如有错误,敬请批评指正! 参考资料: 1:http://www-900.ibm.com/developerWorks/cn/java/l-imgtxt/index.shtml 2:http://forum.hibernate.org.cn/viewtopic.php?t=254 <淘宝热门商品:
 

32.00 元  

卖疯了 牙亮白 7天显著洁白牙齿

 

6.80 元  

北欧橱窗

三冠 三角形强力吸墙角架/置物架/杂物架 承重3公斤 不伤墙面0.14


来源:程序员网

小小豆叮

Java的“前世今生”

1995年,Java还仅仅是一种计算机语言概念;而到了今天,经过8年发展,Java技术已经渗透到了世界的各个角落—小到生活中的电话、烤面包机,大到汽车生产乃至控制“火星漫游者”的临界任务系统中,都有它的身影。   据不完全统计,当今全球已经拥有超过300万的Java开发者,超过2.67亿部支持Java的电话,以及有超过3亿的Java卡在世界各地被配置。   更有甚者,在与其对手“.NET”的竞争中,Java也逐渐站稳了脚跟。   那么,Java将如何加快自己的市场化步伐,带领Sun走出连续几个财季的亏损呢?在下一个8年,Java又将走向何方?它会不会被更新的技术所取代?许多关于Java的问题始终萦绕在关心其发展的人的周围。   初生:一鸣惊人   天下父母皆对子女充满信心,James Gosling对Java的应用前景更是深信不疑。他认为没有任何技术可以取代它,而Java也必将带来技术领域的一次革命。   郭旭:   作为Java语言的创始人,请您为我们的广大读者介绍一下Java的应用前景,特别是与“.NET”的比较。   James Gosling:   作为一种编程语言,Java是目前世界上最为完备的,它有独到的一整套运作环境,那就是J2EE。Java非常适于设计大型的、企业级的系统。   从某种意义上来说要我去评价“.NET”很难,因为作为其一部分的C#语言只是在复制Java功能上做得很成功。目前看来,“.NET”的市场基本是在小型系统上。   Java与竞争者的最大不同在于:前者首先提供了开放性的竞争市场,比如说用户要购买一台服务器的话,它可以根据行业特点选择最适合的产品,进而与本行业内做得最好的厂商来合作,而不管这产品是IBM的、BEA的,还是Sun的;其次,Java有先进的组织方式(JCP,团体程序),其涵义指的是Java的所有程序都是由使用Java的人来设计的。不同的专家组组成了技术的中坚力量,专家组成员有SUN公司的工程师,同时也有来自其他世界著名公司、大学的相关专家。   郭旭:   但“.NET”也是微软与许多合作伙伴来共同开发的。Java的JCP与微软的合作伙伴开发有什么本质区别吗?   James Gosling:   最大的区别在于微软的合作伙伴和开发群体只允许使用“.NET”一种工具,而Java开发中,各种工具都可以参与进来。   从技术上来说,“.NET”架构更适用于小型的、简单的系统,如果系统运用再成熟、再复杂一点的话,它就不适用了。而Java则有一整套能帮助用户的数据库、交易、集群、分布、表格等功能,它能把网络中不同的资源进行集中,提供端到端的运营体系。   郭旭:   Java技术、“.NET”技术将来会呈怎样的发展趋势,它们会不会随着技术的进步被更新的技术所取代?   James Gosling:   我可以断言:在与“.NET”的竞争中,最后的胜利者一定是Java,几年后Java就将是此领域内的事实标准。目前看来,要找到一种新的技术来全面取代现有技术是非常困难的。   Java已经在所有领域和其他技术展开了全面竞争,且取得了较大的领先优势。比如在嵌入式领域,在网络终端越来越智能化的情况下,我们成功设计了Java智能化手机功能并抢占了巨大市场;而在远程控制、汽车、家电和工业自动化领域中,我们也都取得了巨大的成功。   前不久,我们为巴西医疗系统设计了一个控制软件,使得所有的X光机、CT机,以及其他设备和病人的资料、处方情况都通过Web Service联系起来,并分配到每个医生手中。病人相关资料可以随时被激活,整个医疗系统完全整合在了一起。这些都是几年内其他技术所难以追赶的,更别说取代了。   成长:经风历雨   围绕是否在Windows中捆绑Java,以及如何捆绑的问题,Sun和微软已上了不止10次法庭。但随着微软确定Windows不再带Java“玩”的最后期限,这些争执似乎都快结束了。Java将如何推广自身市场?   郭旭:   一直以来,Sun公司给业界的印象是,技术做得很好但市场做得并不好,这也直接影响了Sun的销售业绩,降低了Sun的竞争力,您怎么看?   James Gosling:   其实我们的每一次市场营销计划都是和我们的产品、技术相结合的。当然,Sun一直以技术见长,这也是我在SUN长期工作的最大原因。SUN始终坚信只有拥有优质的产品才可能占领最大的市场,我们投入更多的资金在产品研发而不是在产品的市场推广上。   郭旭:   目前,微软已经停止在其Windows XP中预置Java,而到明年1月,其他版本Windows系统中也将不再预置Java,这显然给Java在PC市场上的推广出了个不小的难题。那么,Java应该怎样或者已经怎样来进行市场推广呢?   James Gosling:   实际上我个人非常高兴微软宣布对Java虚拟机采取这样的手段,这恰恰说明了Java技术的强大。但应该是微软改变它对Java一些非常过分做法的时候了,因为就为这些做法微软被美国许多法庭判决有罪,现在的微软官司缠身。   微软不预置我们的产品,并不会对我们造成太大影响。因为即使是现在捆绑在Windows上的Java也是6年前的产品了。目前我们很多用户是直接到我们的网站上去下载最新的Java版本的。   我们已和很多的原设备制造商签订了合作协议,在他们的产品中捆绑我们的Java产品。这对Java的市场推广来说是一个更好的消息。而我们的产品也可以在其他系统如Linux中使用,所以我们并不担心市场推广。   郭旭:   我们知道,Java公共平台开发较早,技术、市场也比较成熟。但在一些国家由于受使用习惯的影响,以前经常使用微软的VB、VC等开发软件,所以相比Java来说肯定更多的人会愿意选择微软的“.NET”。那么,是不是在发达地区比如美国等地使用Java的人多一点,而在如中国这样的“IT发展中国家”使用“.NET”的会多一些呢?   James Gosling:   事实恰恰相反。Java在新兴市场做得更好,业绩远远超过“.NET”。这首先是因为“.NET”的许可使用费昂贵,而Java的性价比更高;其次,微软对“.NET”的控制太严格,不允许用户有丝毫改动,而Java则鼓励用户改变;此外,“.NET”是和微软其他系统联在一起的,对于非Windows下的应用开发就没用了,而Java开放性强,适合于开发基于所有其他系统的应用。   郭旭:   您是否满意于目前Java在中国的推广?   James Gosling:   中国的Java推广得非常好,中国应该更快接受这个技术,并对其进行适用性改造,进而开发更多用“.NET”无法开发的应用,迎来中国软件产业发展的契机。   而今:期待反哺   当IBM、HP等利用Java疯狂盈利的时候,作为Java的创始者的Sun却在经历连续几个财季的亏损,这是为什么呢?   郭旭:   作为创造者,Sun虽然不遗余力地推广Java,但却并不是最大的受益者,那么Sun每年从Java直接得来的收益到底有多大?未来又如何?   James Gosling:   目前公司的财务状况的确不像前几年那么好,但这主要是因为公司过去的收入全部来自销售企业级系统和硬件,而受限于全球经济和需求的状况,目前在这一块还有困难。   正因为如此,Sun在加强多样化发展战略,正逐步将注意力转移到其他系统上去。我们正从软件方面得到更多的收入,因为其拥有较高的利润率。同时,我们的多样化战略还包括开发低端的、低成本的产品。只要给我们多一些时间,相信SUN有能力做得更好。   郭旭:   什么样的低成本产品?   James Gosling:   我所说的低成本产品主要是指一些小型服务器,集成子系统等,在这方面我们是和Intel及Linux厂商合作的。   未来:充满希望   郭旭:   您认为近几年都有哪些具有革命性的信息技术?在可以预见的未来是否能有什么新兴的、可以带来产业革命的新技术出现?   James Gosling:   应该说没有单个的革命性创新。其实任何一个革命性的创新技术都是一系列的、不断进化的、渐进性的革新的总和。   近几年有两大较大的发展趋势是比较重要的:首先是计算机的计算能力、功能越来越强大。可以预测的是计算机的能力肯定会以几何级速来增长。这种程度的增长必定会导致应用模式等的变化。其次就是网络的普遍性越来越快,所有的处理器、设备都被联到了网络之中。   郭旭:   怎样看待Linux与开放源代码?   James Gosling:   Sun一直不遗余力地支持着开放源代码软件的开发工作,当然也包括Linux。我想虽然没有人做过财务上的统计,但SUN做的支持开放源代码软件开发的工作肯定比其他任何公司都多。如果说未来会有什么新兴技术出现的话,应该就是Linux带来的,但目前看来这还并不怎么明显。   人物印象*人物印象*人物印象*人物印象*人物印象   “软件之父”还是“软件疯子”?   “叮……”狂燥的电话铃声在一个风雨交加的凌晨3点突然响起,James Gosling的朋友拿起电话破口就嚷:“你疯了吗?Gosling,半夜三更的,还让不让人睡觉呀?!”听着朋友出离愤怒的声音,Gosling笑了,因为他知道他这个想法成功了—今天威风八面的Java就是在这样一个电话中萌芽的,这也是“Java之父”的诞生场景。   对搞软件的人来说,如果能被称为“某某之父”,那显然是对其全身心投身软件事业的最大承认,显然,James Gosling在业界已经获得了这样的荣誉。但从另一个侧面看,他却拥有另一种荣誉:如果让12个Java开发者用一个词语来形容James Gosling的话,则必有一打的人会选择“疯子”这个词—这也是记者与James Gosling长达2小时谈话中的主线。   在James Gosling眼中,一个合格的程序技术人员首先必须具备激情与锲而不舍,并准备时刻发疯。“他们不能容忍发疯式的创造,所以我选择了离开。”很多人不愿谈起自己不成功的经历,但James Gosling却并不在乎,他如此坦然面对仅仅在IBM工作了一年半的经历,实在是因为在他看来,因价值观的不同而离开一个公司没什么丢人的,相反,却值得颂扬。   在James Gosling看来,一个成功的软件人士需要一定的技能,但不是全部,你必须将这些技能与你在正确的时间做正确的事情、与你百折不挠的精神、与你发疯的激情结合在一起。精力充沛、永远有好奇心是必需的,而且必须要时常发疯,做一些别人看来不可思议的事情。当你将你的一个想法告诉朋友,他们都认为你是疯人做疯事的时候,那么你已经成功了一半—这就是James Gosling的终极人生观。   “在最底层的软件开发上,中国和世界上其他国家是一样的,但在上层的应用开发上却有自己鲜明的民族特点,突出个性化是中国软件工程师最最要的,我相信凭中国人的韧劲完全没有问题”,在采访结束时,他还不忘叮嘱记者转告中国软件工程师这段话,当然,他最后还是补充了一句:“多一些发疯!” <淘宝热门商品:
 

¥:180.00 

安婴房婴儿用品童装旗舰店/上海总经销可批发、团购

【双皇冠】热卖再次到货!原单DISNEY维尼小熊 专业婴儿定型枕

 

6.80 元  

北欧橱窗

三冠 三角形强力吸墙角架/置物架/杂物架 承重3公斤 不伤墙面0.14


来源:程序员网

小小豆叮

工作技能调查:知己知彼,百战百胜

本文是由 FTPOnline杂志根据几家大型人力网站最近所进行的工作职务问卷调查结果显示,市场对於各类Java相关技术的需求相当殷切,尤其是领衔的几项技术,需求量非常大。 今年五月,一年一度的Java专业人员薪资调查(Java Pro salary survey)对Java开发工程师进行了问卷查访,其内容包括薪资、技术、教育、工作满意度、以及很多其他方面的资讯。我们由开发工程师方面所得到的资讯相当完整,但是雇主方面的看法又是如何呢?,有37%的Java开发工程师在问卷中表示计画在未来的6个月之内转换工作,外加一些可能还在找工作的人,我们不禁要问:「有哪些Java相关技术是雇主们所需要的呢?」 为了寻找答案,我们在2002年7月下旬查阅了刊登在几家大型人力网站上的150个求才项目。此项调查并没有地区与薪资高低的限制,其中前75项来自http://www.dice.com/ 网站,其余的75项则是从http://www.monster.com/ 与http://www.hotjobs.com/ 两个网站取得,数量相当。我们输入的搜寻项目是「Java开发工程师」和「Java程式设计师」,因此,所有的求才项目都出现了Java这个字。然而问题是,除了Java的技术之外,雇主对Java开发工程师还有哪些其他的要求呢?请参阅表一的调查结果。 在这一类的调查中,较小规模的取样范围会产生些微的结果误差,但是仍然可以看得出来,需求最强烈的是JSP、J2EE、EJB、与XML4项技术,全部都有45%至50%的出现率。目前最热门的似乎是Web-based企业的开发技术,其中包括网路服务项目。 需求第二高的项目包括SQL、servlets、以及HTML,出现率大约在35%左右。再其次是WebSphere、Unix、WebLogic、JavaScript、JDBC、以及Oracle,出现率都在20-30%之间。很明显地,应用程式伺服器与资料库工作技术的需求量极高,同时包括HTML及JavaScript等网路相关专业技术。 最後一组较高的出现率是介於10-20%之间,逐渐下降,其中包括面向对象技术C++、UML、以及Perl等。接下来便是许多出现率稍低於10%的技术项目,例如Struts、Rational Rose、JMS、VisualAge、XSLT、Swing、Windows NT、以及MQSeries等。 其余出现率在5%以下的有SOAP、Tomcat、CORBA、Schema、JUnit、RUP、Apache、CVS、ABAP、JNI、JDK、以及Crystal Reports等。 我们将上述调查结果与最近在五月所进行的薪资调查访问报告对照後发现,雇主最殷切需要的正是大多数Java开发工程师所具备的技术,仅有少许特殊的情况例外。 本次的工作技能调查发现,除了Java语言之外,需求量最大的程式语言还有─XML、SQL、HTML、JavaScript、以及C++等─是薪资调查访问受访人工作技能选项中,最常出现项目。该份薪资调查结果显示,Oracle是开发资料库的最最重要工具,同时也是本次调查访问中最常出现的项目。不同的是,在薪资调查中,有很高比例的受访者提到Microsoft databases,但是在此次的求才项目中,并未出现与Microsoft databases有关的专业需求。 除此之外,该项薪资调查的问卷内容并未按照工作技能加以分类,例如JSP、J2EE、EJB、servlets、WebSphere、WebLogic、或是UML等。这些技能可能被归类在Client/Server、Database Access、及其他项目之下,以做为薪资等级的依据。有趣的是,需求最强的工作技能,其薪资给付未必最高,譬如client/server、database access、以及网路服务等技术的需求量很高,但是根据薪资调查结果显示,这几类技能的薪资所得并非最高。 请特别注意报告中最尖端的项目,因为我们在问卷报告中收集了所有出现过的技能项目,而非仅限於预设的项目而已。因此未来您可能会发现,目前需求量偏低的技能,日後却提高了,例如Struts、XSLT、以及SOAP等。 <淘宝热门商品:
 

¥:1880.00 

正和宜时尚保健品 托玛琳颈肩腰膝腿护理 减肥产品-有礼品-快!

托玛琳电气石锗石玉石双温双控保健床垫 双人

 

18.50 元 

出口韩国 棉质弹性不透内搭显瘦踩脚裤/打底裤


来源:程序员网

小小豆叮

谈JDBC SQLSERVER"Error establishing socket"

jdbc配置语句为: jdbc:microsoft:sqlserver://server_name:1433 如运行程序时出现 "Error establishing socket" 错误,则应进行如下调试: 1 检查SQL SERVER 是否允许远程访问.具体步骤: 1)打开"企业管理器",打开控制台根目录>SQL Server 组>数据库 2)在相应"数据库"上单击右键,选择"属性" 3)选择"连接"选项卡,检查"远程服务器连接"下,RPC服务是否选择. 2 使用telnet IP地址 1433,系统是否提示连接出错,如系统提示出错 检查是否防火墙屏蔽了SQL SERVER 或 java IDE 的网络访问端口 如果是,关闭防火墙,重新启动SQL SERVER和java IDE,进行测试, 如果系统仍提示上述错误,尝试下列步骤 3 检查SQL SERVER 端口号及是否启用了TCP/IP协议,具体步骤: 1)打开"企业管理器",打开控制台根目录>SQL Server 组>数据库 2)在相应"数据库"上单击右键,选择"属性" 3)选择"常规"选项卡,点击"网络配置",如启用的协议中无"TCP/IP协议"将其加入 4)选择"TCP/IP协议",点击"属性",检查其端口号是否为1433 5)如端口号为1433将其修改为其它端口号,修改jdbc连接语句,将端口号同样改为新启用的端口号,如jdbc:microsoft:sqlserver://server_name:1400(假设新端口号为 1400) <淘宝热门商品:
 

99.00 元  

皇冠联盟增高鞋垫 鞋(足)护理原单服装批发店

【韩国内增高魔力秀腿鞋】◆增高6公分◆惊爆价99元◆银粉

 

¥:89.00 

爱相依外贸童装.精品低价.15天无理由退货

特价 贺年款 精美棉大衣外套/棉衣/棉袄 帽子可拆卸 3971


来源:程序员网

小小豆叮

关于JAVA的分页查询操作技术

Servlet版性能测试 主要考虑的Servlet版运行方式有: 一:Servlet在Web容器中的运行机制 1. 单独一个无状态的Servlet实例运行 即Web容器里的多个线程调用一个Servlet实例的运行方式 2. 多个Servlet实例 在Web容器中有多个Servlet实例的对象池,并有多个Web容器线程来分别调用执行 二:Servlet 连接数据库的方式 1. 一对一 即可每个Servlet实例都有直接的数据库连接。 具体方式有: 1> 在Servlet实例的每个处理方法中每次都调用数据库连接,然后用此连接进行数据库的查询等操作,最后关闭并释放此连接。 2> 在Servlet实例的初始化操作时就连接一个“长”的数据库连接,直到Servlet实例在destroy时关闭并释放此数据库连接。 因为现在的数据库操作主要是查询,没有对数据库的增加、修改等操作,多用户业务查询、Web容器多线程同时对一个Servlet的同一个数据库连接进行操作应该会没有数据操作同步等问题。 2. 使用Web容器的数据源 这里主要是使用Web容器的数据源-数据库连接池。 在理论上这种方式能提供最佳的性能。这是也是测试各种Web容器产品在数据库连接池上实现的性能情况。 这里主要看Web容器的在各种应用情况下的最优化配置。 Servlet与数据源连接的实现方式: Servlet直接从Web容器配置中取得数据源及其连接对象,然后通过此连接对象来操作数据库。对于数据库连接对象的管理由Web容器来管理。 三:要考虑的问题: 1. 大数据量传输问题 大数据量通过Servlet实例从数据库中取得并整理后,如何有效的传输到客户端IE,并且Servlet实例如何有效在Web容器中处理这些大数据量。 2. 对各种JDBC版本的测试 即不同的数据库使用其自己专用的JDBC来连接,在性能上应该要好一些。 这里也可比较Weblogic Server中实现JDBC与各种数据库(MSSQL、Oracle)专用的差别,从测试的结果看出Weblogic Server的技术实例以及是否真正做到了数据库连接等处理的优化了吗。 3. Weblogic Server的优化配置 3.1 对象池配置 包括应用逻辑处理对象的对象池化以及使用数据源时的数据库连接对象池在各种具体应用环境下的优化配置。 3.2 线程池配置 以上两个方面涉及到对象池化和串行化处理的策略。 3.3 Weblogic Server 的配置的各种参数的相应情况下的配置 1> JAVA VM (JAVA 虚拟机)参数在各种应用情况下的配置。 2> Weblogic Server 本身的各种参数配置。 鉴于以上的考虑对Servlet版的测试规划为以下几种测试用例: 序号 部署包名(*.JAR *.WAR *.EAR 等) 数据源配置 Weblogic Server 的配置 预期结果 说明 可能出现的问题和现象 1 ServletQueryForPerConn.war 在每此业务处理时创建数据库连接,操作完毕后关闭并释放。 通过Web.xml配置文件来配置JDBC的驱动类型和连接。 直接部署ServletQueryForPerConn.jar部署包。 Web容器中只有一个Serverlet实例。 建议配置较多的线程数量。 性能差。 在每此业务处理时创建数据库连接,操作完毕后关闭并释放。 此包中没有设计到线程同步的有关代码。 数据库很忙(因为数据库要接收频繁的数据库连接)。 可能瓶颈在数据库对频繁的连接处理。 数据库事务方面:由于是在每次处理时就调用数据库连接并查询,因此数据库的事务处理应该是单独在一个独立的处理过程中,与并行的其他线程的处理没有关系。 2 ServletQueryForOnceConn.war Servlet对象只是的初始化时连接与数据库的一个连接,在以后的操作中式中使用这个连接。 通过Web.xml配置文件来配置JDBC的驱动类型和连接。 直接部署ServletQueryForOnceConn.jar包; Web容器只有一个Servlet实例。 建议配置较多的线程数量。 性能较差。 Servlet对象只是的初始化时连接与数据库的一个连接,在以后的操作中式中使用这个连接。 此包中没有设计到线程同步的有关代码。 数据库连接只有一个。 可能瓶颈在Web容器的多个线程对同一个数据库连接对象的同步等处理(这些同步处理是Web容器自己管理的)。 可能出现查询的数据在多个客户请求中打乱(因为同时使用同一个数据库通信通道); 并且多个线程(单独的处理单元)可能会在同一个处理事务中,可能各个处理单元会串行操作数据库(这要看数据库的具体实现了)。 3 ServletQueryForConnPool.war 直接使用Web容器的数据源和数据库连接池。 配置数据源及数据库连接池。 建议根据实际情况优化配置数据源和连接池。如可建立多个连接池等配置。 性能好。 Servlet实例不管数据库连接,而是直接从Web容器中取得数据库连接。数据库的连接对象有Web容器全权管理。 此包中没有设计到线程同步的有关代码。 对Web容器的数据库连接池的配置可能要根据具体情况进行有效的调整(如数据库连接对象个数和Web容器配额的线程个数的关系等)。如果配置不佳可能是性能瓶颈在Web容器或者在数据库方。 4 ServletQueryForConnPool.war (同测试3) 同测试3 Web容器的数据源重新配置为数据库产品专用的JDBC驱动器。 性能好。 测试目的是比较各种不同的JDBC数据连接驱动器的性能,以便得出根据不同的数据库产品选择最佳的JDBC驱动器。 只测试数据库产品提供的专用JDBC驱动器。 (说明:因为测试3在理论上性能是最好,因此选用测试3。测试方法和测试3一样,这样才有可比性。) 同测试3。 5 servletQueryDS_Cache.war 同测试3 同测试3 性能一般 使用一变量来缓存查询的数据,用户以后的分页查询查询操作是直接从此缓存中取得的。 这种方式对Web容器的内存要求高,效果不是很好,对数据量查询小的效果可能会好些。 优点: 减少的了对数据库访问的次数。 缺点: 需要较大的内存。对Weblogic容器的内存要求高,对于有大量用户的查询操作,并且查询的结果集较大时,可能对整个系统的性能是个很大的瓶颈。 对大量数据的分页处理 问题描述: 背景1:一客户通过IE请求Web服务器查询数据,而查询结果是上千条甚至是上万条记录,要求查询结果传送到IE客户端并分页显示。 背景2:一客户通过IE或者其他方式请求Web服务器查询数据,而查询结果是上千条甚至是上万条记录,并要求查询结果把包传送到客户的E-mail中。 问:对于这样的有大量数据的结果集,在Web服务器端如何有效的处理? 可能涉及到的问题: 1. 内存占用 大量数据的结果集,可能要 2. 传输速度及策略 具体的分页处理技术 序号 名称 处理方法 针对的数据库 例子说明 备注 1 游标查询 直接使用ResultSet来处理。ResultSet是直接在数据库上建立游标,然后通过ResultSet的行位置定位接口来获得指定行位置的记录。 当用户第一请求数据查询时,就执行SQL语句查询,获得的ResultSet对象及其要使用的连接对象都保存到其对应的会话对象中。 以后的分页查询都通过第一次执行SQL获得的ResultSet对象定位取得指定行位置的记录。 最后在用户不再进行分页查询时或会话关闭时,释放数据库连接和ResultSet对象等数据库访问资源。 说明:在用例分页查询的整个会话期间,一个用户的分页查询就要占用一个数据库连接对象和结果集的游标,这种方式对数据库的访问资源占用比较大,并且其利用率不是很高。 所有的数据库产品。 优点: 减少了数据库连接对象的多次分配获取,减少了对数据库的SQL查询执行。 缺点: 占用数据库访问资源-数据库连接对象,并占用了数据库上的资源-游标。而这些资源都是十分宝贵的有限制的。 结论: 这种的数据库查询分页处理方式不是最佳的。一般不适用这种方式。 2 定位行集SQL查询 主要是直接使用数据库产品的提供的对查询的结果集可定位行范围的SQL接口技术。 在用户的分页面查询请求中,每次可取得查询请求的行范围的参数,然后使用这些参数生产取得指定行范围的的SQL查询语句,然后每次请求获得一个数据库连接对象并执行SQL查询,把查询的结果返回给用户,最后释放说有的数据库访问资源。 说明:这种方式需要每次请求时都要执行数据库的SQL查询语句;对数据库的访问资源是使用完就立即释放,不白白占用数据库访问资源。 对特定(提供了对查询结果集可定位功能的)的数据库产品。 如:Oracle,DB2, PostgreSQL,mySQL等。(MS SQL Server 没有提供此技术。) 如: 1. Oracle数据库使用关键字:rowid或rownum 2. DB2: rowid或rownum () 3. PostgreSQL 使用LIMIT 和 OFFSET 4. MySQL 使用Limit 优点: 这种技术是直接使用数据库产品自己提供的可对查询结果集定位行范围过滤的功能,因此直接利用了数据库的性能对此分页查询的优化功能。 对数据库的访问资源(数据库连接对象,数据库游标等)没有浪费,这些资源的充分重复的利用。 对查询的结果对Web容器没有什么特别要求。 缺点: 要执行多次数据库SQL查询操作。对每次的分页面操作请求都要指定相应范围的结果集来执行SQL语句的数据库查询操作,这对数据库有一定的影响。 对每次分页面查询请求要频繁的从Web容器中获得数据库访问资源(数据库连接对象和数据库游标)。 要依赖于具体的数据库产品。因为对没有实现没有提供此技术的数据库产品不能使用此方式。 结论: 由于每次对数据库的SQL查询操作相对而言耗用的数据资源比较少,并且在实际用户的操作中,有可能用户对查询的所有结果集只是需要查看其中的部分页面。 因此这种方式是最佳的。 3 特别处理的定位行集SQL查询 这种方式是在方式2的基础上针对不提供对查询结果集行范围定位的数据库产品。 其在Web容器端的操作逻辑大致和方式2相同。 只是先要对要查询的数据库表要有一字段的数据能区别每条不同的数据记录。第一次查询时,获得用来可唯一标识不同记录的字段的所有结果集,并缓存起来以备后面的分页面查询指定要查询的结果集的行范围。 主要是针对不同对查询行集可定位范围获得的数据库产品,如MS SQL Server等。 假设从A,B,C三个表中选取数据。且A有字段ID用来可唯一区别不同的记录。 那么第一次查询的时候,会查询两次1. select A.id from A,B,C where condition. 2. 把A的ID缓存到SESSION中⾿3.从Session中。现可按照次序来取得相应页面范围的ID来,并构造下一个查询语句:select A.name, B.add from A,B,C where condition && ( A.ID in 本页面范围的 ID ) 以后每次翻页的时候,依次获得对应页的ID只要表中唯一的就可以了。无所谓大小,顺序⾿这样,SESSSION缓存的就只是一列而不是所有列了。当然,对于列数不多的,效果并不好。 也可使用存储过程实现,可参照:http://expert.csdn.net/Expert/topic /2365/2365596.xml?temp=.7529261 优点: 同方式2 缺点: 同方式2; 还要在要查询的数据库表中建立一个相应的ID,用来唯一区别每条记录。 结论: 同方式2。 4 缓存一次SQL查询的结果集 优点: 缺点: 既然我们要缓存结果,那么用户就可能会看到过期的数据 说明:对于实际情况的应用来说,一般结合实际情况,结合使用方式2(或方式3)和方式4。如:一个应用场景:对公司的产品的查询是经常的,但是产品的种类不是很多,这时可使用缓存方式;但是对有些查询结果集较大,数据库和Web容器之间的网络访问由可能是远程的,这时候可考虑使方式2(或者方式3)。 测试用例代码实现说明 一:测试用例3-ServletQueryForConnPool 版本 1.结构图 2.代码实现结构 3.运行时序图 4.测试运行情况说明 4.1 数据库连接和数据库游标占用可能比较大 由于数据库的查询及其分页处理是直接使用JDBC的,并在分页中是使用RseultSet的查询结果集-游标形式实现的,并且每个客户对应一个会话,每个会话对应一个数据库连接和一个结果集(游标),数据库连接和游标是在会话终止时才释放的。因此在多个客户的请求过程中,可能对数据库的访问资源(数据库连接和用于数据操作的游标)占用比较大。 因此数据库访问及其数据库的处理可能是个瓶颈。 4.2 资源没有释放的问题 会话对应的数据库连接和游标可能在会话终止时没有释放。 为了更好的体现出使用Web容器数据库连接池的优点,应该合理的设置连接池中连接对象的“非活动超时时间”,建议次值和Servlet对象的会话超时时间长度一直。 5.此测试用例操作说明 5.1 部署的包的位置: ServletQueryForConnPool.war 5.2 部署 1.通过Weblogic 的控制台工具部署此包 2.相关的参数请看ServletQueryForConnPool.war包中的配置文件web.xml中相应的servlet配置参数 5.3 测试URL Http://Server:port/WebAppName 即: Http://Web服务器名:端口/Servlet部署的应用程序名 二:测试用例4 ServletQueryForConnPool_cache 版本 1.结构图 和“一:测试用例3”相同 2.代码实现结构 3.运行时序 说明:使用第四种“缓存一次SQL查询的结果集”的分页面查询技术,即一次SQL查询,把从数据库查询出来的结果保存到会话中,以后的客户分页查询操作都从此缓存中取得。 4.测试运行情况说明 由于使用的是缓存结果集的方式,对Web容器服务器的内存要求比较高,可能在测试过程中,Web容器服务器因内存问题而影响整个系统的响应性能。 5.此测试用例操作说明 5.1 部署的包的位置: ServletQueryForConnPool_cache.war 其他情况参照 “一:测试用例 3 ServletQueryForConnPool.war, 基本一致。 <淘宝热门商品:
 

 

淘宝紫砂壶第一皇冠!淘宝最大的信用度第一的紫砂壶大卖家!

 

网络游戏点卡 

网游点卡武汉移动话费自动充专卖店


来源:程序员网

小小豆叮

建造灵活与可维护的J2EETM 应用程序的设计模式

建造灵活与可维护的J2EETM 应用程序的设计模式 Vijay Ramachandran 2002 年1月 鉴于JavaTM2 企业版 (J2EETM) 已经成为服务器端应用程 序平台的首选,共享开发者的经验与设计就成了一件至关紧要的事。本 文介绍了一些可重复使用的设计模式,以便读者在建造灵活与容易维护 的J2EE应用程序时使用。 本文不对范例的正式摸板与UML图作介绍。您可以在 J2EE蓝图程序 找到这些细节与代码样例。本文所要解决的是那些能够影响J2EE应用程 序灵活性和可维护性的问题,并且给出推荐的解决方案。 什么是设计模式? 当您设计建造不同的应用程序时,您时而不时地会碰到相同或者非 常类似的问题域。每次碰到这些问题时您都必须去重新寻找解决方案 。为了节省时间与精力,如果有一个知识库,能够捕获这样的具有共 性的问题域与被证明是正确的解决方案,将是非常理想的。 用最简单的话来说,这样的通用解决方案就是一个设计模式。这样 的知识库,或者说参考处,包含了这些模式,就是设计模式目录。 式摸板进行描述,比如最流行的、由“四个伙计” Gang of Four定义的摸板。 模式摸板通常包含一个用来描述模式所代表的意义的名字,紧跟着 的是模式的适用范围、动机、在实现过程中的问题等等。除了描述问 题与阐明解决方案,模式还解释在使用本模式的实现过程中可能存在 的问题和后果。 使用这样的模式让应用程序的设计变得透明。这些模式已经被不同 的开发者在不同的领域内成功地使用过,所以,一个模式的优点和缺 点(也包括实现过程中的问题)都已经事先知道。所有的设计模式都 是可重复使用的,并且在特定的场合中适用。这就给了您灵活性。同 J2EE应用程序有关的设计模式的使用在J2EE平台技术方面提供了更多 优势来展示了解决方案。 建造灵活与可维护的J2EE应用程序的设计模式 多层的J2EE应用程序由位于中间层的一些可能是分布式的不同视图 和组件组成。在本文的以下章节中,给出了一些能帮助您让典型的J2EE 应用程序具备可扩展性、灵活性和可维护性的设计模式。本文不使用那 些抽象的文字,而使用了一个假想的实际例子,试图让您尽可能地理解 这些模式。一旦您了解了这个应用程序例子,您就能很容易地将这个例 子中使用的模式应用到其他应用程序中。 以一个Web上的金融服务企业级应用程序作为例子。浏览者可以在这 个站点上浏览服务项目列表、建造帐户、订购该金融服务机构提供的各 种产品等等。我们假定这个应用程序允许已有的客户改变各自的帐户细 节与个人资料,并使用这些服务,等等。典型地,这样的应用程序有多 个视图或者屏幕,用户可以通过鼠标点击,在这些视图间切换,查找服 务项目列表,存取个人资料,使用各种服务项目,或者取得其他信息。 事务逻辑代表着用户的帐户、资料、服务目录、服务定单等等,这种形 式就如同在企业版JavaBeans(EJB)中的分开的实体。把这个例子放在心 里,然后再看看那些重复的问题,您就能明白如何使用特定的模式来建 造灵活的、可维护的应用程序。 模型-视图-控制器 问题域 如果您所建造的这个企业级应用程序只是给单一类型的客户使用的, 那问题就简单了。我们可以简单地将数据存取/数据修改逻辑与不同的 客户视图逻辑混合在一起。但是随着一个完全互连的无线世界的出现, 客户端的设备从PDA、蜂窝电话到一个功能强大的桌面系统上的浏览器 都有,这还不包括其他的传统设备。在这种情况下,将数据存取与视 图混合在一起来作为解决方案,将会有很多问题,这是因为: 您必须开发这个应用程序的不同版本,以便适应与支持各种不同 的客户需要 由于视图的代码与数据存取/修改的代码纠缠在一起,重复的数 据存取/修改代码散步在各处,这就使得应用程序几乎是不可维护的。 开发生命周期被不必要地扩展了 建议的解决方案 为了找到这个问题的解决方案,请注意以下几点: 不论客户类型如何,被存取与显示的数据必须来自同一个企业级 的数据源。 所有的客户必须能够对数据源进行修改。 不论是修改一个客户类型,还是修改数据存取/修改逻辑,都不应 该影响到应用程序的其他组件。 您需要一个能让您开发出松散耦合的应用程序的解决方案。建议采用 模型-视图-控制器(MVC)结构。MVC已经被非常有效地应用在GUI类型的 应用程序的设计上。通过在J2EE应用程序上采用MVC结构,您可以将数据 存取逻辑与数据表现逻辑分别开来。您也可以建造一个灵活的并且是很 容易扩充的控制器来控制应用程序的整个流程。下图展示了MVC结构。 如下所述,MVC结构可以映射到多层的企业级J2EE应用程序: 所有的企业级数据与用于处理数据的事物逻辑都能用这个模型来 表示。 视图可以通过这个模型存取数据,然后决定如何将数据表达给客 户。当模型改变时,视图必须确认给用户所表达的内容也跟着改变。 控制器可以与视图交互,并且将客户的行为转换为模型可以理解 与执行的行为。控制器也根据最新的客户行为与相关的模型行为来决 定下一个要被显示的视图是什么。 将上述的逻辑应用到金融应用程序例子中,您将这样建造应用程序: 应用程序的事务逻辑由组成MVC结构中的模型的EJB来代表。模型 对来自控制器的要求作出反映,并存取/修改相应的数据。 应用程序界面的不同屏组成MVC结构中的视图。当模型改变时,视 图自动更新。 应用程序的控制器是一些对象的集合,这些对象接收用户的行为, 并且将其转换为模型可理解的请求,一旦模型完成了对请求的处理, 就决定下一个要显示在屏幕上的内容是什么。 想要在一个小例子中将MVC结构完全展示出来是非常困难的。 J2EE蓝图 程序 中的Java宠物商店演示程序是一个很好的基于MVC结构的完整的J2EE应用 程序的例子。 注意事项 MVC结构适合于高 MVC结构适合于高度交互的系统,这种系统需要可扩展性、可维护性与多用 户视图。 MVC 将表述、用户交互与系统模型解耦合。 由于不存在耦合,将多数据集表述在多视图中就变得很容易。同时也使得 为新的客户类型提供支持更为简单。 使用这种结构,代码冗余被最大限度地减少了。 通过将表述、模型、全面的应用程序流程这三者分开,这个结构使得不同 的开发者可以负责不同的模块,于是,产品上市的时间就快了。 <淘宝热门商品:
 

运动鞋 

阿迪耐克正品折扣店

 

保健品/滋补品 

淑芳阁_苗条姿_最有效的减肥瘦身与丰胸专卖店


来源:程序员网

小小豆叮

用RMI和CORBA进行分布式Java编程

用RMI和CORBA进行分布式Java编程 Qusay H. Mahmoud 2002年1月 Java远程方法调用(RMI)机制和公用对象请求代理体系(CORBA)是最重要 和使用最广泛的两种分布式对象系统。每个系统都有其特点和短处。它们在行 业中被用于从电子交易到保健医疗的各个领域。一个项目如果要从这两种分布式 机制中选用一个,往往难以抉择。本文概括地介绍了RMI和CORBA,更重要的是, 它将介绍如何开发一个有用的应用程序,用于从远程主机下载文件。然后它将: 简要介绍分布式对象系统 简要介绍RMI和CORBA 让你对在RMI和CORBA中开发应用程序所涉及的工作有个初步印象 演示如何使用RMI和CORBA,从远程主机传送文件 对RMI和CORBA进行简单比较 客户机/服务器模型 客户机/服务器模型是分布式计算的一种形式,在这种形式中,一个程序(客 户机)与另一个程序(服务器)通讯以便交换信息。在这种模型中,客户机和服 务器通常都说同样的语言--也就是说客户机和服务器能理解同一个协议--这 样它们才能通讯。 虽然客户机/服务器模型的实现方式多种多样,但典型做法是使用底层套接字。 使用套接字开发客户机/服务器系统意味着,我们必须设计一个协议,也就是客户 机和服务器都认识的一组命令集,通过这些命令它们就能通讯了。举例来说, HTTP协议中提供了一个名为GET的方法,所有Web服务器都必须实现这个方法,所 有Web客户机(浏览器)都必须使用这个方法,才能获取文档。 分布式对象模型 基于分布式对象的系统是一组对象的集合,这些对象以一种明确定义封装的接 口把服务的请求者(客户机)和服务的提供者(服务器)分隔开。换言之,客户 机从服务的实现中分离出来,变成数据的呈现和可执行代码。这就是基于分布式 对象的模型与纯粹的客户机/服务器模型的主要区别之一。 在基于分布式对象的模型中,客户机向对象发送消息,然后对象解释该消息以 便决定要执行什么服务。这项服务,也就是方法,可以选择是让对象还是让代理 来执行。Java远程方法调用(RMI)和公用对象请求代理体系(CORBA)就是这种 模型的例子。 RMI RMI是一个分布式对象系统,它使你能够轻松地开发出分布式Java应用程序。 在RMI中开发分布式应用程序比用套接字开发要简单,因为不需要做设计协议这种 很容易出错的工作。在RMI中,开发者会有一种错觉,似乎是从本地类文件调用的 本地方法,其实参数传送给了远程目标,目标解释参数后再把结果发回给调用方。 RMI应用程序初步 使用RMI开发分布式应用程序包括以下步骤: 定义一个远程接口 实现这个远程接口 开发服务器 开发客户机 生成存根和基干,启动RMI注册表、服务器和客户机 下面我们将通过开发一个文件传输程序来实践这些步骤。 范例: 文件传输程序 这个应用程序允许客户机从远程主机上传送(即下载)任何类型的文件(纯 文本或二进制文件)。第一步是定义一个远程接口,这个接口规定了服务器所提 供方法的信号,客户机将调用这些方法。 定义一个远程接口 用于文件下载应用程序的远程接口如代码范例1所示。接口 FileInterface提供了一个方法downloadFile,这个 方法接受String参数(文件名),将文件的数据以字节数组的形式 返回。 代码范例1 1: FileInterface.java import java.rmi.Remote; import java.rmi.RemoteException; public interface FileInterface extends Remote { public byte[] downloadFile(String fileName) throws RemoteException; } 请注意FileInterface的以下特征: 它必须声明为public,这样客户机才能加载实现远程接口 的远程对象。 它必须扩展为Remote接口,以满足使该对象成为远程对象的 要求。 这个接口中的每种方法都必须投出一个java.rmi.RemoteException。 实现远程接口 下一步是实现接口FileInterface。实现的范例见代码范例2。 请注意,除了实现FileInterface之外,还把FileImpl 类扩展为UnicastRemoteObject。这表示FileImpl类 将用于创建一个单独的、不可复制的远程对象,它使用RMI缺省的基于TCP的传送 通道进行通讯。 代码范例2: FileImpl.java import java.io.*; import java.rmi.*; import java.rmi.server.UnicastRemoteObject; public class FileImpl extends UnicastRemoteObject implements FileInterface { private String name; public FileImpl(String s) throws RemoteException{ super(); name = s; } public byte[] downloadFile(String fileName){ try { File file = new File(fileName); byte buffer[] = new byte[(int)file.length()]; BufferedInputStream input = new BufferedInputStream(new FileInputStream(fileName)); input.read(buffer,0,buffer.length); input.close(); return(buffer); } catch(Exception e){ System.out.println("FileImpl: "+e.getMessage()); e.printStackTrace(); return(null); } } } 开发服务器 第三个步骤是开发服务器。服务器需要做三件事: 创建RMISecurityManager的一个实例并安装它 创建远程对象(在本例中是FileImpl)的一个实例 在RMI注册表中登记这个创建的对象。实现的范例见代码范例3。 代码范例 3: FileServer.java import java.io.*; import java.rmi.*; public class FileServer { public static void main(String argv[]) { if(System.getSecurityManager() == null) { System.setSecurityManager(new RMISecurityManager()); } try { FileInterface fi = new FileImpl("FileServer"); Naming.rebind("//127.0.0.1/FileServer", fi); } catch(Exception e) { System.out.println("FileServer: "+e.getMessage()); e.printStackTrace(); } } } 语句Naming.rebind("//127.0.0.1/FileServer", fi)假定RMI 注册表在缺省的端口号1099上运行。但是,如果RMI注册表在其他端口号上运行, 就必须在这一句中指定端口号。例如,如果RMI注册表在端口4500上运行,那么 这一句就变成: Naming.rebind("//127.0.0.1:4500/FileServer", fi) 另外,在这里要着重指出,我们假定rmi注册表和服务器是在同一台电脑上运 行。如果不是这样,只需修改rebind方法中的地址即可。 开发客户机 下一步是开发客户机。客户机可以远程调用远程接口 (FileInterface)中指定的任何方法。但是为了能这么做,客户 机首先必须从RMI注册表中获得指向该远程对象的引用。获得引用之后就可以调 用downloadFile方法了。客户机的实现请见代码范例4。在这个实 现中,客户机从命令行接收两个参数: 第一个参数是要下载文件的名称,第二个参数是要下载的文件所在主机的地 址,也就是运行文件服务器的那台电脑的地址。 代码范例4: FileClient.java import java.io.*; import java.rmi.*; public class FileClient{ public static void main(String argv[]) { if(argv.length != 2) { System.out.println("Usage: java FileClient fileName machineName"); System.exit(0); } try { String name = "//" + argv[1] + "/FileServer"; FileInterface fi = (FileInterface) Naming.lookup(name); byte[] filedata = fi.downloadFile(argv[0]); File file = new File(argv[0]); BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(file.getName())); output.write(filedata,0,filedata.length); output.flush(); output.close(); } catch(Exception e) { System.err.println("FileServer exception: "+ e.getMessage()); e.printStackTrace(); } } } 运行应用程序 为了运行应用程序,我们需要生成存根和基干,编译服务器和客户机,启动 RMI注册表,最后是启动服务器和客户机。 为了生成存根和基干,请使用rmic编译器: prompt> rmic FileImpl 这将生成两个文件:FileImpl_Stub.class和 FileImpl_Skel.class。存根是一个客户机代理,基干是一个服 务器基干。 下一步是编译服务器和客户机。用javac编译器来做这件事。但是请注意:如 果服务器和客户机是在两台不同的机器上开发的,为了编译客户机,需要把接口 (FileInterface)复制一份。 最后,启动RMI注册表并运行服务器和客户机。为了在缺省的端口号上启动 RMI注册表,请在Windows中使用命令rmiregistry或 start rmiregistry。为了在其他端口号上启动RMI注册表,可以 提供该端口号作为RMI注册表的一个参数: prompt> rmiregistry portNumber 运行RMI注册表之后,就可以启动服务器FileServer了。但是, 因为在服务器应用程序中正在使用RMI安全管理员,所以需要一个安全方针来与之 相配。下面是一个安全方针范例: grant { permission java.security.AllPermission "", ""; }; 注意: 这只是一个方针的例子。它允许任何人做任何事情。对于关键 性事务应用程序,你需要指定更严格的安全方针。 现在,为了启动服务器,需要把除了客户机类 (FileClient.class)之外的所有类(包括存根和基干)复制一 份。请使用以下命令启动服务器,假定安全方针位于文件policy.txt中: prompt> java -Djava.security.policy=policy.txt FileServer 为了在另一台机器上启动客户机,需要复制远程接口 (FileInterface.class)和存根 (FileImpl_Stub.class)。请使用以下命令启动客户机: prompt> java FileClient fileName machineName 其中fileNamefileName是要下载的文件,machineName 是该文件所在的机器(运行文件服务器的那台机器)。如果一切顺利,那么客户 机就存在了,下载完的文件保存在本地的机器上。 <淘宝热门商品:
 

50.00 元  

韩服CASH之家

韩服 HF 跑跑卡丁车 新劳迪 劳迪Introduction 永久 支持韩文名

 

 

:◤╭ Shanghai 秀卡蒂╮╰女性健康購物中心 ╯◥◣瘦身╮減肥╮


来源:程序员网

小小豆叮

通过套接字传递对象

通过套接字传递对象 Qusay H. Mahmoud 2001 年12月 使用JavaTM远程方法调用(RMI),可以很方便地开发分布式的基于对象的应用程序。RMI的简单性,是由网络通信的费用作为代价的。底层的套接字可以用来开发客户/服务器系统,但是由于大多数Java I/O类和对象不太容易匹配,如何通过套接字传递完成的对象呢?对象序列化是一个允许您以比特流方式读/写完成对象的机制。 将底层的套接字和对象序列化结合在一起,您将得到一个强大的、高效的、可替代RMI的机制,通过套接字来传递对象还能克服使用RMI的高费用的问题。 本文: 简单概述对象序列化 介绍如何应用对象序列化 解释如何应用已经存在的对象与用户对象 介绍如何通过套接字传递对象 提供多线程的服务器例子 提供一个基于对象的实现daytime协议的例子 最后,简单比较了RMI和有对象序列化的套接字。 对象序列化概述 对象序列化机制对于需要将对象的状态保存到文件中,而后能够通过读入对象状态来重新构造对象,恢复程序状态,或者使用套接字在网络上传送对象的程序来说,是很有用的。通过让类实现java.io.Serializable 接口可以将类序列化。这个接口是一个制造者(marker)接口。也就是说,对于要实现它的类来说,该接口不需要实现任何方法。它主要用来通知Java虚拟机(JVM),需要将一个对象序列化。 将对象读出或者写入流的主要类有两个: ObjectOutputStream与ObjectInputStream 。ObjectOutputStream 提供用来将对象写入输出流的writeObject方法, ObjectInputStream提供从输入流中读出对象的readObject方法。注意使用这些方法的对象必须被序列化,这非常重要。也就是说,这些类必须实现Serializable接口。 序列化已经存在的类 了解了对象序列化的基础知识之后,我们来看看如何对流读/写对象或现有的已序列化类实例。要将一个对象写入输出流,先建立一个输出流,然后使用writeObject对象保存到文件中。 -------------------------------------------------------------------------------- 注意: Date类是可序列化的。换句话说,它实现 Serializable接口。 -------------------------------------------------------------------------------- 例程 1: SaveDate.java import java.io.*; import java.util.Date; public class SaveDate { public static void main(String argv[]) throws Exception { FileOutputStream fos = new FileOutputStream("date.out"); ObjectOutputStream oos = new ObjectOutputStream(fos); Date date = new Date(); oos.writeObject(date); oos.flush(); oos.close(); fos.close(); } } 读入对象,然后重新构造它的状态都很容易。例程2中的代码向您展示了如何读一个已经序列化的对象并且打印它的信息。 例程 2: ReadDate.java import java.io.*; import java.util.Date; public class ReadDate { public static void main(String argv[]) throws Exception { FileInputStream fis = new FileInputStream("date.out"); ObjectInputStream ois = new ObjectInputStream(fis); Date date = (Date) ois.readObject(); System.out.println("The date is: "+date); ois.close(); fis.close(); } } 在上面的例子中我们使用了Date类的一个实例,这是一个现成的已序列化的Java类。也许您会问这样的问题:是不是所有现成的Java类都是可序列化的?答案是否定的。这不仅因为这样做没必要,而且将某些类序列化是毫无意义的。使用JDK中的serialver工具,可以判断一个类是不是可序列化的。您可以在命令行模式下使用如下命令: c:> serialver java.util.Date java.util.Date: static final long serialVersionUID = 7523967970034938905L; (这个例子测试Date类是不是可序列化。输出结果表示Date类是可序列化的,并且打印出了这个类的版本唯一标识符。) 或者,您也可以使用如下命令启动图形界面下的serialver工具: c:> serialver -show 这个命令将弹出一个如图1所示的窗口,在这个窗口中写入您想检查的类的名字(包括路径)。图1显示的结果表示 Date类是可序列化的。 再说一次,不是所有的Java类都是可序列化的。举个例子,图2表明Socket类是不可序列化的。 序列化用户自己的类 现在,让我们看看如何序列化用户自己写的类。在这个例子中,我们将建立一个用户类UserInfo,见例程3。为了让它可序列化,UserInfo类实现了Serializable接口。 例程 3: UserInfo.java import java.io.*; import java.util.*; public class UserInfo implements Serializable { String name = null; public UserInfo(String name) { this.name = name; } public void printInfo() { System.out.println("The name is: "+name); } } 下一步就是建立一个能创建UserInfo类实例的类,然后将对象写入输出流中,如例程4。本例中的输出流是一个名为"name.out"的文件。要注意的是,例程4 中的writeObject方法可以被调用任意多次,将任意多个对象写入输出流。 例程 4: SaveInfo.java import java.io.*; import java.util.Date; public class SaveInfo { public static void main(String argv[]) throws Exception { FileOutputStream fos = new FileOutputStream("name.out"); ObjectOutputStream oos = new ObjectOutputStream(fos); // create two objects UserInfo user1 = new UserInfo("Java Duke"); UserInfo user2 = new UserInfo("Java Blue"); // write the objects to the output stream oos.writeObject(user1); oos.writeObject(user2); oos.flush(); oos.close(); fos.close(); } } 最后,我们写一个将已经保存的对象读入的类,并且调用一个如例程5所示的方法。和writeObject 一样,readObject方法能被调用任意多次,从输入流中读入任意多个对象。 例程 5: ReadInfo.java import java.io.*; import java.util.Date; public class ReadInfo { public static void main(String argv[]) throws Exception { FileInputStream fis = new FileInputStream("name.out"); ObjectInputStream ois = new ObjectInputStream(fis); // read the objects from the input stream (the file name.out) UserInfo user1 = (UserInfo) ois.readObject(); UserInfo user2 = (UserInfo) ois.readObject(); // invoke a method on the constructed object user1.printInfo(); user2.printInfo(); ois.close(); fis.close(); } } 要测试这个例子,请编译如下源文件:UserInfo.java, SaveInfo.java, 和 ReadInfo.java。运行 SaveInfo,然后运行ReadInfo,将看到类似下面的输出结果: The name is: Java Duke The name is: Java Blue <淘宝热门商品:
 

52.00 元  

【卡盟在线】

皇冠在线充|QQ黑钻腾讯黑钻地下城与勇士(DNF)黑钻3月

 

80.00 元  

托玛琳厂家直营店

托玛琳预防富贵病;脂降压降糖减肥 活效清肠杯 一馈赠佳品限量版


来源:程序员网

小小豆叮

JProfiler跟踪和检查系统性能的好工具

从事Java开发有段时间了,经常为Java抛内存溢出和系统时常的罢工而烦恼。有时你也许有这样的经历为了,找寻系统的漏洞,几乎把所有的代码都翻了一遍, 也许这比你原来写Code还花费精力和时间;有时你也许在梦想有个工具能时时监测系统,提供漏洞和bug的蛛丝马迹(有点象打广告哦,不过我觉得我这个我非常喜爱的工具-JProfiler打打广告,我非常乐意)。如果找bug就象看病一样,那么Jprofiler就是那台可以为提供诊断依据和建议的X光扫描器,不信你用用就知道了。 1 JProfiler是做什么的哦? JProfiler工具主要用于检查和跟踪系统(限于Java开发的)的性能。JProfiler可以通过时时的监控系统的内存使用情况,随时监视垃圾回收,线程运行状况等手段,从而很好的监视JVM运行情况及其性能。 2 如何获取JProfiler 你可以试试下载JProfiler的评估版(有十天的使用时间),目前的最新版本为2.4版本 你可以在在这儿获取:http://www.ej-technologies.com/products/jprofiler/overview.html 你需要注册,然后JProfiler公司会通过Email方式发送一个key给你,在第一次使用时需要输入这个key。 3 如何监视你的第一个系统 首先请打开你的Jprofiler。 这时展现在你眼前的第一个页面,如上图。第一个页面就是你原来将来过的监视Project(就当成一个Project好了)。第二个页面是需要创建新的监视Project。 这时你就可以点击按钮"New session"按钮了。下一个页面将会出现在你的眼帘之中。 第一个就是Session名字了,随便取(不修改也行), 第二个Session type就是监视类型了,我们就选择监视本地的系统吧。 然后选择Java VM。在安装Jprofiler时,搜索出系统已经安装了的jvm,选择一个好了。 Working directory:这个目录启动系统的运行目录,方便于你找那儿些配置文件,以及寻找你classpath中配置的jar文件 VM arguments参数,如果你想配置JVM的系统参数请在此配置,比如么配置内存的最大,最小值,配置方式和常用配置方式类似。 Main class or executable JAR:选择可以直接运行的jar文件或者填上要运行主Class。(如果你的jar文件中META-INF/MANIFEST.MF 包含有Main-Class:就可以直接运行了) Arguments就是main(String[] args)中要传递的参数 然后就该Additional java file path中加入所有需要的classpath或者jar文件 这些配置搞定后就可以点击ok按钮了,在评估版本中将会弹出一个对话框(就是通知你评估版本还有多长时间过期),不管那么多点击按钮"Evaluate",然后继续点击按钮"ok" 好了,我们可以看看我们的劳动成果了,倒杯Coffee,享受那浓浓Coffee情。 这是内存使用情况 这是每个class,甚至每个方法的内存使用比率 这是线程使用情况 里面还有更多更多的好东西,等待你的发现哦。 非常愿意和你共享 <淘宝热门商品:
 

105.00 元 

Levi's 复古铜系列情侣款女装牛仔裤

 

 

【广州商盟】意外金喜服装◎抵制暴利低价有好货◎收藏小店有惊喜


来源:程序员网

小小豆叮

使用Java 输出/输出流读写数据

Java 输入/输出(I/O)机制提供了一套简单的,标准化的API以便从不同的数据源读取和写入字符和字节数据。在“面向对象编程:Java collection更有效管理elements”一文中,我们讨论了Java 集合类架构中的类和功能并介绍了它的排序功能。在本文中,我们将学习Java 平台提供的这些I/O类,接口和操作。让我们先从了解Java 数据流开始。 数据流 Java所有的I/O机制都是基于数据流的,这些数据流表示了字符或者字节数据的流动序列。Java的I/O流提供了读写数据的标准方法。任何Java中表示数据源的对象都会提供以数据流的方式读写它的数据的方法。 Java.io是大多数面向数据流的输入/输出类的主要软件包。这个软件包包含了两个抽象类,InputStream和OutputStream。所有其它面象数据流的输入/输出类都要扩展这两个基类。 java.io软件包提供了一些类和接口,它们在由InputStream和OuputStream类提供的读写操作的顶端定义了一些有用的抽象。例如,ObjectInputStream类提供了让你把输入/输出流中的数据当成对象来读取的方法,而ObjectOutputStream类提供了让你能够把Java对象写入数据流中的方法。 优化读写过程 JDK 1.1 增加了一套读写类,它们提供了比现有数据流类更有用的抽象和更好的输入/输出性能。例如,BufferedReader和BufferedWriter 类被用来从基于字符的输入和输出流中读取和写入文本。BufferdReader 类缓存字符以更高效的读取字符串,数组和文本行。BufferedWriter类缓存字符以更高效的写入字符串,数组和文本行。BufferedReader和BufferedWriter 类可以按需求进行设置。 Java输入/输出架构提供的读取器和写入器类包括 LineNumberReader 类,CharArrayReader类,FileReader类,FilterReader类,PushbackReader类,PipedReader类,StringReader类以及其它一些类。这些类是在InputStream和OuputStream类顶部的包裹类因此提供了与InputStream和OuputStream类相似的方法。但是,这些类为读写特定的对象,比方文件,字符数组和字符串等等提供了更高效而有用的抽象。 读取数据 当你从一个相应的数据源对象里提取输入流或者是创建一个读取器对象的时候就会自动打开一个输入流。例如,要为一个文件打开输入流,我们只需要以下面的方式把文件名传递给Java.io.FileReader对象的构造函数: java.io.FileReader fileReader = new java.io.FileReader("/home/me/myfile.txt"); 要按顺序读取FileReader底层的输入流中的一个字节数据,只需要使用不带参数的read方法。表A中的代码段从一个文件读取文本数据,一次一个字符,然后把它写入System.out里。 要从输入流读取指定数目的字节数据到char数组里,只需要使用带一个char[]参数的read方法。数组的长度被用来确定应该读取的字符的个数。表B演示了这个技术。 要关闭一个输入流以及这个流使用的所有系统资源,你只需要以下面的方式调用close方法: fileReader.close(); 写入数据 象一个输入流一样,输出流通常在你从相应的数据源提取它或者是在你创建一个写入对象的时候被自动的打开。例如,要为一个文件打开输出流,我们把文件的名字传递给java.io.FileWriter对象的构造函数,如下所示: java.io.FileWriter fileWriter = new java.io.FileWriter("/home/me/out.txt"); 要将一个特定的字符写入到输出流中,可以使用带一个int参数的write方法,int参数代表要定入的字符。 int aChar = (int)"X"; fileWriter.write(aChar); 要在输出流给定的偏移地址写入一个char数组中特定数目的字符,你可以使用带一个char[]参数,一个int 偏移量参数和一个int长度参数的write方法,如下面的例子所示: fileWriter.write(buffer, 0, byteCount); 要关闭一个输出流并释放所有与之相关的系统资源,可以使用close方法,就象这样: fileWriter.close(); 要强迫写出一个输出流中的所有数据,可以使用下面的flush方法: fileWriter.flush(); 把它们全部综合起来 我们可以使用我们学习过的这些函数从一个文件中读取数据并同时写到另一个文件中去,如表C所示。 总结 Java的输入/输出机制为从不同的数据源读取和写入字符增加了一套简单而标准化的API。你对一种数据源使用Java流的经验能够让你容易的使用其它由Java提供的数据源类型。 在我们下一篇文章中,我们将会开始学习Java平台的联网和远程通讯架构。我们将会把我们对Java流的讨论扩展到这些环境并演示如何打开远程数据源,并象操作本地数据源,比方文件一样,写入数据和读取数据 <淘宝热门商品:
 

 

极限特攻-新奇产品仓库   金秋时节好礼送不停

 

88.00 元 

双皇冠热卖 日本原装 RAKU系列30倍运动硅胶


来源:程序员网

小小豆叮

Java数据压缩格式程序设计方法之JAR压缩格式

1、JAR格式文件介绍 在JDK1.1版中,定义了用于将与Java应用程序相关的多个文件以及相关资源整合在一起的数据文件类型-JAR。也许读者会注意到:Sun以及其它公司发布的多种类型Java库都是以JAR形式进行文件压缩和封装的。 从本质上讲,JAR压缩文件采用ZIP格式进行数据压缩。由于最初引入JAR数据压缩类型的目的在于将开发完成的软件进行进行发布,因此,在JDK中定义了JAR类型文件管理工具,用于创建、浏览、解压缩以及执行JAR文件等操作。另外,在JDK API中还定义了封装在java.util.jar包中的多个对象,用于在Java应用程序中对JAR文件进行管理。 2、 利用JDK工具操作JAR文件 在JDK安装目录的bin子目录中,包括名称为“jar.exe”的可执行程序。该程序在执行过程中可以利用如下表所示的各个选项,实现JAR文件的多种类型操作: 表1 JDK压缩文件管理工具应用选项表 那么,对于包含了class字节码文件和包含相应资源文件的一个目录,利用JDK文档管理工具,如何创建JAR文件从而实现数据压缩的呢?让我们看下面的例子: 例如,对于一个要发布的软件项目Demo,在该目录中包括一个名称为Demo.class的Java字节码文件和包含声音文件的目录Audio、包含图片文件的目录Images,如下图所示: 那么,利用下面的命令即可实现将Demo项目创建成可发布的JAR类型压缩文档: C:>jar cvf Demo.jar Demo.class Audio Images 利用上述命令创建的JAR类型压缩文件不仅将Demo目录中的多种资源进行打包,而且将这些数据文件进行了压缩。如果读者不需要在打包过程中进行数据压缩,可以设置jar执行选项为“cvf0”。 如果利用jar工具的tf选项查看新创建的JAR文件,读者会发现在该文件中新增了名称为META-INF的目录,在该目录中包含MANIFEST.MF文件,该文件的作用是什么呢?总的说来:由于在创建JAR文件过程中可以进行数字签名、版本控制、包封装以及多种类型的功能扩展,因此,MANIFEST.MF文件即用于说明当前的JAR文件在创建过程中采用了何种类型的功能扩展,典型的MANIFEST.MF文件代码如下: Manifest-Version: 1.0 Name: java/math/BigDecimal.class SHA1-Digest: TD1GZt8G11dXY2p4olSZPc5Rj64= MD5-Digest: z6z8xPj2AW/Q9AkRSPF0cg= 该文件的代码说明了压缩文件采用的数字签名内容。 3、 JAR包对象定义结构 除了可以利用JAR文档管理工具对jar文件进行管理之外,在JDK API中,还定义了java.util.jar包,其中封装了用于在Java应用程序中创建、维护和操作jar文件的多种类型对象,主要对象包括: Attributes:用于将JAR文件中说明属性内容映射到该类的各个字符串变量中; JarEntry:用于对应JAR文件中封装的各个被压缩文件; JarFile:对应磁盘文件系统中的JAR压缩文档对象; JarInputStream:从JAR文件中读取压缩内容的流对象; JarOutputStream:创建JAR文件的流对象; Manifest:对应JAR文件中说明内容的对象。 在实际的应用程序设计过程中,与创建和管理JAR压缩文档相关的对象为JarInputStream和JarOutputStream。在下面的内容中,将演示在Java应用程序中创建JAR类型压缩文档的方法。 4、创建JAR文档应用程序设计方法 下面的代码为创建JAR类型压缩文档的Java应用程序代码: //JARDemo.java import java.io.*; import java.util.jar.*; public class JARDemo { public static void main(String[] args) { if (args.length !=2 ) { System.out.println("请输入被压缩文件的名称和压缩文件的名称!"); System.exit(1); } try { //创建文件输入流对象 FileInputStream in = new FileInputStream( args[0] ); //创建文件输出流对象 FileOutputStream out = new FileOutputStream( args[1] ); //创建JAR数据输出流对象 JarOutputStream jarOut = new JarOutputStream( out ); //创建指向压缩原始文件的入口 JarEntry entry = new JarEntry( args[0] ); jarOut.putNextEntry( entry ); //向压缩文件中输出数据 int nNumber; byte[] buffer = new byte[512]; while ((nNumber=in.read(buffer)) != -1) jarOut.write(buffer,0,nNumber); //关闭创建的流对象 jarOut.close(); out.close(); in.close(); } catch(IOException e) { System.out.println( e ); } } 在该程序的实现代码中,首先创建用于进行文件输入和输出的FileInputStream和FileOutputStream对象,并以FileOutputStream对象实例为参数创建JarOutputStream对象实例,从而为创建JAR格式压缩文件建立数据流基础。 在随后的代码中,以被压缩文件的文件名为参数,创建在JAR压缩文件中指向压缩原始文件的Entry对象实例,并利用putNextEntry方法将该Entry存储进入压缩文件中。最后,利用JAROutputStream对象中定义的write方法向输出流中写出从压缩原始文件中读取的数据,从而创建JAR格式的压缩文件。 利用Java语言创建JAR格式压缩文件的方法很简单,那么,如何利用JDK API中定义的对象将被压缩的文件解压缩呢?请读者看下一节的内容。 5、 JAR文档解压缩方法 下面的程序代码用于将JAR类型文档解压缩后,恢复成为独立的数据文件: //UnjarDemo.java import java.io.*; import java.util.jar.*; public class UnjarDemo { public static void main(String[] args) { if ( args.length !=2 ) { System.out.println("Usage:java UnjarDemo "); System.exit(1); } try { //创建文件输入流对象实例 FileInputStream in = new FileInputStream( args[0] ); //创建Zip压缩格式输入流对象实例 JarInputStream jarin = new JarInputStream( in ); //创建文件输出流对象实例 FileOutputStream out = new FileOutputStream( args[1] ); byte[] buffer = new byte[1024]; int nNumber; while ((nNumber = jarin.read(buffer, 0, buffer.length)) != -1) out.write(buffer, 0, nNumber); //关闭文件流对象 jarin.close(); out.close(); in.close(); } catch(IOException e) { System.out.println(e); } } } 该程序的代码结构不是很复杂,请读者自行分析该文件中的对象应用形式以及JAR压缩文件解压缩方法。 6、 小结 在本部分的内容中,首先介绍了JAR类型文档的结构和JDK中jar文档管理工具的应用方法。在随后的内容中,以java.util.jar包中的应用对象为基础,讲解了Java语言创建和维护jar压缩文档程序设计方法。 <淘宝热门商品:
 

4.60 元  

旺智通讯手机配件商城-----诚邀各地商家加盟合作(可代发货)

皇冠推荐 特卖 诺基亚原装二手BL-5C电池 型号BL5C

 

¥:78.00 

皇冠舒友阁 顶级特效美容护肤 效果才是硬道理

皇冠热销 印疤修复液 消除深浅痘印等色印、有效改善凹凸疤痕


来源:程序员网

小小豆叮

在JSP中操作文件

综述:无论是用JavaServer Page(JSP)技术,还是ASP、PHP技术实现的网站,都可能有计数器、投票等功能,这些功能的实现离不开对文件的操作。由此可见,文件操作对网站的建设来说,有着很重要的作用。   本章首先介绍了JSP中文件的基本操作,包括读取操作、写入操作以及追加操作,然后在此基础上,通过实例,说明如何通过这三种基本操作,来实现计数器、投票等复杂功能。 JSP对文件的基本操作有哪些?   读取操作   读取操作是文件操作的基本功能之一,在计数器、投票统计中有着广泛的应用。那么,该操作在JSP中是如何实现的呢?请看下面的例子。   本例用到了两个文件,一个jsp文件,一个Javabean文件。通过jsp中调用Javabean可以轻松读取文本文件,注意请放置一个文本文件afile.txt到web根目录的test目录下,Javabean文件编译后将class文件放到对应的class目录下(tomcat环境)。 Read.jsp <html> <head> <title>读取一个文件</title> </head> <body bgcolor="#000000"> <%--调用Javabean --%> <jsp:useBean id="reader" class="DelimitedDataFile" scope="request"> <jsp:setProperty name="reader" property="path" value="/test/afile.txt" /> </jsp:useBean> <h3>文件内容:</h3> <p> <% int count = 0; while (reader.nextRecord() != -1) { count++; %> <b>第<% out.print(count); %>行:</b> <% out.print(reader.returnRecord()); %><br>     <% } %> </p> </body> </html> DelimitedDataFile.Java import Java.io.*; import Java.util.StringTokenizer; public class DelimitedDataFile { private String currentRecord = null; private BufferedReader file; private String path; private StringTokenizer token; //创建文件对象 public DelimitedDataFile() {      file = new BufferedReader(new InputStreamReader(System.in),1); } public DelimitedDataFile(String filePath) throws FileNotFoundException {      path = filePath;      file = new BufferedReader(new FileReader(path)); }    //设置文件路径   public void setPath(String filePath)   {      path = filePath; try { file = new BufferedReader(new FileReader(path)); } catch (FileNotFoundException e) {       System.out.println("file not found");      } } //得到文件路径 public String getPath() {    return path; } //关闭文件 public void fileClose() throws IOException {    file.close(); } //读取下一行记录,若没有则返回-1 public int nextRecord() {      int returnInt = -1;      try {      currentRecord = file.readLine();      } catch (IOException e)      {      System.out.println("readLine problem, terminating.");      }      if (currentRecord == null)      returnInt = -1;      else      {       token = new StringTokenizer(currentRecord);      returnInt = token.countTokens();      }      return returnInt; }     //以字符串的形式返回整个记录 public String returnRecord() { return currentRecord; } } 写入操作   本例使用两个文件,一个jsp文件,一个Javabean文件。通过jsp中调用Javabean可以轻松读取文本文件,注意请放置一个文本文件afile.txt到web根目录的test目录下,Javabean文件编译后将class文件放到对应的class目录下(tomcat环境)。 WriteOver.jsp <html> <head> <title>Write over a file</title> </head> <body bgcolor="#000000"> <jsp:useBean id="writer" class="WriteOver" scope="request"> <jsp:setProperty name="writer" property="path" value="/path/to/afile.txt" /> <jsp:setProperty name="writer" property="something" value="Something already set as a property in WriteOver" /> </jsp:useBean> <h3>Write to the file</h3> <p> <% writer.setSomething("Something to write to the file"); out.print(writer.getSomething()); out.print(writer.writeSomething()); %> </p> </body> </html> WriteOver.Java import Java.io.*; public class WriteOver { private String path; private String something; // WriteOver构造器,用于初始化参数 public WriteOver() { path = null; something = "Default message"; } //设置文件路径 public void setPath(String apath) { path = apath; } //获取路径参数 public String getPath() { return path; } //获取something参数值 public void setSomething(String asomething) { something = asomething; } //设置something参数 public String getSomething() { return something; } //将something参数的值写入paht指定的文件中 public String writeSomething() { try {      File f = new File(path);      PrintWriter out = new PrintWriter(new FileWriter(f));      out.print(this.getSomething() + " ");      out.close(); return "Alles ist Gut."; } catch (IOException e) {      return e.toString(); }     } } 追加操作   如何用jsp将数据追加到一个文件中呢?继续下面的例子吧。 writeAppend.jsp <html> <head> <title>Append a file</title> </head> <body bgcolor="#000000"> <jsp:useBean id="writer" class="WriteAppend" scope="request"> <jsp:setProperty name="writer" property="path" value="/path/to/afile.txt" /> <jsp:setProperty name="writer" property="something" value="Something already set as a property in WriteAppend" /> </jsp:useBean> <h3>Write to the file</h3> <p> <% writer.setSomething("Something to write to the file"); out.print(writer.getSomething()); out.print(writer.writeSomething()); %> </p> </body> </html> WriteAppend.Java import Java.io.*; public class WriteAppend { private String path; private String something; // WriteAppend构造器,用于初始化参数 public WriteAppend() { path = null; something = "Default message"; } //设置path参数 public void setPath(String apath) { path = apath; } //获取path参数值 public String getPath() { return path; } //设置参数something public void setSomething(String asomething) { something = asomething; } //获取something参数 public String getSomething() { return something; } //追加操作 public String writeSomething() { try {      FileWriter theFile = new FileWriter(path,true); PrintWriter out = new PrintWriter(theFile);      out.print(something + " ");      out.close();     theFile.close();      return "Das war sehr gut!"; } catch (IOException e) {      return e.toString(); }     } }   注意源程序中的加粗行,它是实现追加操作的关键。在创建FileWriter对象时,使用了FileWriter类的FileWriter(String filename , boolean append)构造函数,其中append参数用于决定对将写入的数据是否进行追加操作。 如何利用文件操作实现简单的计数器?   计数器是一般网站必备的东东,别小看它了,每当站长看着小小计数器上的数字飞速增长的时候,感觉实在是好极了。以前我们用cgi、asp来写计数器,这方面的文章很多了,在这里,我们将会采用jsp技术演示如何做一个计数器。   其中用到了两个文件,test.jsp文件用于在浏览器中运行,counter.Java是后台的一个小Java bean程序,用来读计数器的值和写入计数器的值。而对于计数器的保存,我们采用了一个文本文件lyfcount.txt。 下面是详细的程序代码(test.jsp放到web目录下,counter.Java放到class目录): //test.jsp文件 <%@ page contentType="text/html;charset=gb2312"%> <HTML> <HEAD> <meta http-equiv="Content-Type" content="text/html; charset=gb2312"> <META NAME="GENERATOR" CONTENT="Oracle JDeveloper"> <TITLE>计数器演示程序</TITLE> </HEAD> <BODY> <!--创建并调用bean(counter)--> <jsp:useBean id="counter" class="counter" scope="request"> </jsp:useBean> <% //调用counter对象的ReadFile方法来读取文件lyfcount.txt中的计数 String cont=counter.ReadFile("/lyfcount.txt"); //调用counter对象的ReadFile方法来将计数器加一后写入到文件lyfcount.txt中 counter.WriteFile("/lyfcount.txt",cont); %> 您是第<font color="red"><%=cont%></font>位访问者 </BODY> </HTML> //counter.Java 读写文件的一个bean import Java.io.*; public class counter extends Object { private String currentRecord = null;//保存文本的变量 private BufferedReader file; //BufferedReader对象,用于读取文件数据 private String path;//文件完整路径名 public counter() { } //ReadFile方法用来读取文件filePath中的数据,并返回这个数据 public String ReadFile(String filePath) throws FileNotFoundException { path = filePath; //创建新的BufferedReader对象 file = new BufferedReader(new FileReader(path ); String returnStr =null; try{ //读取一行数据并保存到currentRecord变量中 currentRecord = file.readLine(); }catch (IOException e) { //错误处理 System.out.println("读取数据错误."); } if (currentRecord == null) //如果文件为空 returnStr = "没有任何记录"; else { //文件不为空 returnStr =currentRecord; } //返回读取文件的数据 return returnStr; } //WriteFile方法用来将数据counter+1后写入到文本文件filePath中 //以实现计数增长的功能 public void WriteFile(String filePath,String counter) throws FileNotFoundException { path = filePath; //将counter转换为int类型并加一 int Writestr = Integer.parseInt(counter)+1; try { //创建PrintWriter对象,用于写入数据到文件中 PrintWriter pw = new PrintWriter(new FileOutputStream(filePath)); //用文本格式打印整数Writestr pw.println(Writestr); //清除PrintWriter对象 pw.close(); } catch(IOException e) { //错误处理 System.out.println("写入文件错误"+e.getMessage()); } } }   到这里,程序写完了,将counter.Java编译为counter.class,同样放在对应的class目录下,在根目录下建立一个lyfcount.txt文件,文件内容就一个数字0,直接在浏览器中敲入地址就可以看到计数器了,刷新浏览器会看到不断变幻的数字。   如果运行时候提示找不到文件,请将上面test.jsp中的readfile那一句注释后运行一次则lyfcount.txt文件自动建立,然后就可以正常运行。   如何实现投票统计?   投票统计是文件操作的又一个应用。一来,网站可以根据网民的投票结果,为自己的运营提供符合人们要求的决策信息;另外还可追踪人们对热点问题的看法,激发网民兴趣,提高网站的访问量。可谓一举两得。   那么投票统计是如何实现的呢?他要用到何种技术呢?看了下面的例子,相信读者会找到答案。 一、前言   该例子涉及到文件的读写和jpg图片的自动生成。利用jsp+servlet的技术,jsp调用servlet生成图片。   二、首文件index.jsp如下: <%@ page contentType="text/html;charSet=gb2312"%> <% response.setHeader("Cache-Control","no-store"); response.setDateHeader("Expires",0); %> <%! //getQuestion(String)函数用来获得文本文件中的问题 public String[] getQuestion(String s) { String[] strQ = new String[4]; String strTemp = null; int i; Java.io.RandomAccessFile rf = null; try { rf = new Java.io.RandomAccessFile(s,"r"); } catch(Exception e) { System.out.println(e); System.exit(0); } for(i=0;i<4;i++) { try { strTemp = rf.readLine(); } catch(Exception e) { strTemp = "None Question"; } if(strTemp==null)strTemp = "None Question"; strQ = strTemp; } return strQ; } %> <% String s = null; String[] question = new String[4]; s = request.getRealPath("question.txt"); question = getQuestion(s); %> <html> <head> <title></title> <link href="css.css" rel="StyleSheet" type="text/css"></link> </head> <body> <table width="180" border="1" bordercolor="#999999"> <tr> <td align=center>冰帆调查</td> </tr> <form name=frm method=post action=write.jsp> <tr> <td> <% String ss = null; for (int i=0;i<4;i++) { ss = "<input type="radio" name="choice" value=" + I + ">" + (char)("A"+i) + "、" + question[i]+"<br>"; out.println(ss); } %> </td> </tr> <tr> <td align=center><input type=submit value="我 投 一 票"></td> </tr> <tr> <td align=center><img src="/vote/servlet/VoteImage" width=150 height=100> </td> </tr> </form> </table> </body> </html> 三、写文件write.jsp <%! public int[] getNumber(String s) { int[] mCount = new int[4]; String strTemp = null; int i; Java.io.RandomAccessFile rf = null; try { rf = new Java.io.RandomAccessFile(s,"r"); } catch(Exception e) { System.out.println(e); System.exit(0); } for(i=0;i<4;i++) { try { strTemp = rf.readLine(); } catch(Exception e) { strTemp = "0"; } if(strTemp==null) strTemp = "0"; mCount[i] = new Integer(strTemp).intValue(); } return mCount; } public void setNumber(String s,int[] x) { try { Java.io.PrintWriter pw = new Java.io.PrintWriter(new Java.io.FileOutputStream(s)); for (int i=0;i<4;i++) { pw.println(x[i]+""); } pw.close(); } catch(Exception e) { System.out.println("Write file error:"+e.getMessage()); } } %> <% String tmp = null; int choice = -1; int[] count = new int[4]; tmp = request.getParameter("choice"); if (tmp==null){ } else { choice = new Integer(tmp).intValue(); } String s = request.getRealPath("count.txt"); if (choice>=0) { count = getNumber(s); count[choice]++; setNumber(s,count); } response.sendRedirect("index.jsp"); %> 四、servlet原代码:VoteImage.Java : import Java.io.*; import Java.util.*; import com.sun.image.codec.jpeg.*; import Javax.servlet.*; import Javax.servlet.http.*; import Java.awt.*; import Java.awt.geom.*; import Java.awt.image.*; public class VoteImage extends HttpServlet { private String strFile = null; private Color color[]={Color.red,Color.black,Color.orange,Color.green}; private int baseAng = 30; public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { strFile = request.getRealPath("count.txt"); float[][] xy = new float[4][2]; xy = getNumAndPercent(strFile); int[] ang = new int[4]; ang[0] = (int)(xy[0][1]*360); ang[1] = (int)(xy[1][1]*360); ang[2] = (int)(xy[2][1]*360); ang[3] = 360-ang[0]-ang[1]-ang[2]; response.setHeader("Cache-Control","no-store"); response.setDateHeader("Expires",0); response.setContentType("image/jpeg"); ServletOutputStream out=response.getOutputStream(); BufferedImage image = new BufferedImage ( 150 , 100 , BufferedImage. TYPE_INT_RGB); Graphics2D g = (Graphics2D)image.getGraphics(); g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON); g.setColor(Color.white); g.fillRect(0,0,150,100); AffineTransform at = null; Arc2D arc = null; int fromAng = baseAng; at = AffineTransform.getRotateInstance((-20*Java.lang.Math.PI)/180,45,37); g.setTransform(at); int r =6; int dx = (int) ( r * Java.lang.Math.cos ( ( baseAng + ang[0] ) / 2.0 * Java.lang.Math.PI / 180 ) ); int dy = ( int ) ( r * Java.lang.Math.sin (( baseAng + ang[0] ) / 2.0 * Java.lang.Math.PI / 180 ) ); arc = new Arc2D.Double(10+dx,24-dy,80,50,fromAng,ang[0],Arc2D.PIE); g.setColor(color[0]); g.fill(arc); fromAng+=ang[0]; for (int i=1;i<4;i++) { g.setColor(color[i]); arc = new Arc2D.Double(10,24,80,50,fromAng,ang[i],Arc2D.PIE); g.fill(arc); fromAng+=ang[i]; if (fromAng>360) { fromAng-=360; } } at = AffineTransform.getRotateInstance(0,arc.getCenterX(),arc.getCenterY()); g.setTransform(at); for (int i=0;i<4;i++) { g.setColor(color[i]); g.fillRect(100,15*i+20,10,10); g.drawString((char)("A"+i)+"",120,15*i+20+8); } JPEGImageEncoder encoder=JPEGCodec.createJPEGEncoder(out); encoder.encode(image); out.close(); } public void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { doGet(request,response); } public synchronized float[][] getNumAndPercent(String sFileName) { float xx[][] = new float[4][2]; int totalNum = 0 ; String strTemp = null; int i = 0; Java.io.RandomAccessFile rf = null; try { rf = new Java.io.RandomAccessFile (sFileName,"r"); } catch(Exception e) { System.out.println(e); System.exit(0); } for (i=0;i<4;i++) { int m=0; try { strTemp = rf.readLine(); } catch (Exception e){ strTemp = "0"; } if (strTemp == null) strTemp = "0"; m = new Integer(strTemp).intValue(); xx[i][0]=m; totalNum += m; } if (totalNum==0) totalNum=1; for ( i=0;i<4;i++) { xx[i][1] = xx[i][0]/totalNum; } return xx; } }   五、在index.jsp目录下建立question.txt和count.txt文件分别用来保存投票的问题和投票的数量,用户投票后,就修改count.txt的值。 question.txt: Yes,I think so! No,I dont think so! Sorry,I dont know the answer! count.txt: 12 9 5 9 六、目录结构:   (1) jsp文件和txt文件同一个目录   (2) Java文件是servlet目录下   七、测试:   http://[server:port]/dir/index.jsp   对文本文件的操作,其实以前的例子已经讲的很清楚了,之所以还要介绍该例,是想让读者了解JSP中文件操作的功能,可以结合其他技术,像该例中jpeg图片的自动生成,来实现更为强大的功能。对你有所启发吗?   如何实现文件上传功能?   文件上传,相信很多读者都比较熟悉了,用E-Mail给同学、朋友寄张近照,以表问候,这时就得用到此项功能。那么文件是如何传到服务器上的呢?看了下面的例子,相信读者会找到答案。   基本原理:使用ServletRequest类的getInputStream()方法获得一个客户端向服务器端发出的数据流,然后处理这个数据流,从中分析、得到文件上载中传递到服务器的各个参数和数据,然后将其中的文件数据存储为一个文件,这样就实现了文件上载的功能。   由于代码太长,我们将它放在了配套光盘上,详细内容请见UploadServlet.Java。下面我们就来分析一下该代码。   // MAX_SIZE用来定义上传文件的最大长度。   static final int MAX_SIZE = 102400;   //rootPath用于存放根路径,successMessage用来存放成功信息   String rootPath, successMessage;   Init()方法用于初始化参数。最关键的部分是doPost()函数: 在获取数据流之前,首先确定数据流的内容类型是否为multipart/form-data: if(contentType != null && contentType.indexOf("multipart/form-data") != -1) { … }   在内容类型确定为multipart/form-data后,接着就将数据流的内容读到临时缓冲区dataByte[]中: in = new DataInputStream(request.getInputStream()); int formDataLength = request.getContentLength(); byte dataBytes[] = new byte[formDataLength]; while (totalBytesRead < formDataLength) { //check for maximum file size violation   sizeCheck = totalBytesRead + in.available();   if (sizeCheck > MAX_SIZE)   {     out.println("Sorry, file is too large to upload.");     return;   }   bytesRead = in.read(dataBytes, totalBytesRead, formDataLength);   totalBytesRead += bytesRead; }   下面的工作就是分析文件上载中传到服务器的各个参数和数据了。在该例中,程序分析了Directory、SuccessPage、OverWrite以及OverWritePage参数,这些参数在服务器生成最终文件时使用。最后程序将数据流中的文件内容输出到文件流中,生成文件。 fileOut = new FileOutputStream(fileName); fileOut.write(file.getBytes(),0,file.length()); <淘宝热门商品:
 

1.02 元  

冰之.点卡店

【112热血传奇元宝】1个*游戏交易*

 

1.50元  

星钻喜铺◣皇冠信誉◢{100%满意婚庆一站式店铺}特色婚庆用品大全


来源:程序员网

小小豆叮