??xml version="1.0" encoding="utf-8" standalone="yes"?>久久久久一级精品亚洲国产成人综合AV区 ,亚洲色欲久久久综合网,亚洲JIZZJIZZ中国少妇中文http://www.tkk7.com/Victor/articles/23099.htmlVictorVictorFri, 09 Dec 2005 03:05:00 GMThttp://www.tkk7.com/Victor/articles/23099.htmlhttp://www.tkk7.com/Victor/comments/23099.htmlhttp://www.tkk7.com/Victor/articles/23099.html#Feedback0http://www.tkk7.com/Victor/comments/commentRss/23099.htmlhttp://www.tkk7.com/Victor/services/trackbacks/23099.html
一般,我们用log4j写log的时候,很习惯于如下面记录:

2005-01-11 09:57:57,816 DEBUG (BizProc.java:275) Begin to get connection...
2005-01-11 09:57:57,816 DEBUG (BizProc.java:275)   Obtain connection 2345@defd
2005-01-11 09:57:57,816 DEBUG (BizProc.java:275)   Begin transaction [TX_2015_RECV] processing...
2005-01-11 09:57:57,925 DEBUG (BizProc.java:328)     All of the values were:

q里会把Java Source Code中的行号带出来,查找错误很方?..

但,q也是问题之所?..文章中介l,Log4j之所以可以得到行P是用了JVM的一个特性,不知道大家有没有注意刎ͼ如果我们的JavaE序在运行时出现错误Q会出现下面的信息:

javax.naming.NamingException: Cannot create resource instance
 at org.apache.naming.factory.ResourceFactory.getObjectInstance(ResourceFactory.java:189)
 at javax.naming.spi.NamingManager.getObjectInstance(NamingManager.java:301)
 at org.apache.naming.NamingContext.lookup(NamingContext.java:834)
 at org.apache.naming.NamingContext.lookup(NamingContext.java:181)
 at org.apache.naming.NamingContext.lookup(NamingContext.java:822)
 at org.apache.naming.NamingContext.lookup(NamingContext.java:181)
 at org.apache.naming.NamingContext.lookup(NamingContext.java:822)
 at org.apache.naming.NamingContext.lookup(NamingContext.java:181)
 at org.apache.naming.NamingContext.lookup(NamingContext.java:822)
 at org.apache.naming.NamingContext.lookup(NamingContext.java:194)
 at org.apache.naming.SelectorContext.lookup(SelectorContext.java:183)
 at javax.naming.InitialContext.lookup(InitialContext.java:347)

看到黑体字没有,原来JVM已经内嵌支持Debug号信息了Q可是,q里是出错的时候,JVM抛出的,那ؓ什么用 log4j.info() 的时候,也可以看到行号呢Q?

下面是重点QLog4j中记录行Lq程...

(1) 调用?log4j.info() 或是log4j.debug()Ҏ...
(2) 建立一?CODE style="FONT-WEIGHT: bold; COLOR: rgb(0,0,255)">Throwable的对象来取得当前q行堆栈的快?..Throwable.fillInStackTrace();
(3) 从抛出的Throwable对象中,来分析出当前log信息的行?..

但是Q请注意Q徏立运行时的堆栈跟t,对JVM来说Q是一w常大的开销Q?/SPAN>

所以,我们在配|log4j的PatternLayout的时候,如果是要上线到正式环境,一定要记者把参数xQ?BR>一般来_??d %-5p  %m%n" 可以满了Q?/P>
原文请参考这里:Sneaky, sneaky Log4J

===========================================================================
补遗~~~~~Q?BR>
log4j.rootLogger=info, Console
log4j.logger.net.csdn.blog.xport=debug, RollingFile
log4j.additivity.net.csdn.blog.xport=false

log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d %-5p [%t] %C{1} - %m%n

log4j.appender.RollingFile=org.apache.log4j.RollingFileAppender
log4j.appender.RollingFile.File=SystemLog.log
log4j.appender.RollingFile.Append=true
log4j.appender.RollingFile.MaxFileSize=2MB
log4j.appender.RollingFile.layout=org.apache.log4j.PatternLayout
log4j.appender.RollingFile.layout.ConversionPattern=%d %-5p [%t] %C{1} - %m%n


Victor 2005-12-09 11:05 发表评论
]]>
Java开源Blog实现QRoller 2.0 的安装手讎ͼ (?http://www.tkk7.com/Victor/articles/23097.htmlVictorVictorFri, 09 Dec 2005 02:55:00 GMThttp://www.tkk7.com/Victor/articles/23097.htmlhttp://www.tkk7.com/Victor/comments/23097.htmlhttp://www.tkk7.com/Victor/articles/23097.html#Feedback0http://www.tkk7.com/Victor/comments/commentRss/23097.htmlhttp://www.tkk7.com/Victor/services/trackbacks/23097.html

Roller 是一个Struts + Hibernate ?Java 开源实玎ͼ正好拿来研究研究Q首先就是要把它安装h了,整理
了一下步骤和注意事项Q记录如下:

准备需要的基本环境Q?BR>
1. Java 2 SDK 1.4.2_10
2. Tomcat 5.0.28
3. Roller 2.0
4. MySQL 5.0.16

首先Q确保MySQL开启了对InnoDB和UTF-8的支持,在my.ini中开启下面的参数Q?BR>

[mysqld]
default-storage-engine=innodb
default-character-set=utf8


启动好MySQL待用...D:\MySQL\mysqld.exe --console --datadir=I:\MyData\Data

假设我的Tomcatȝ录ؓQ?FONT style="COLOR: rgb(0,0,255)" size=2>$CATALINA_HOME=D:\J2EE_HOME\AppServer\Tomcat

把压~的Roller文g roller-2.0-incubating.tar 解开...
q放?/SPAN>
$CATALINA_HOME\WebApp\roller下面...如图...



开始徏立MySQL中的数据库:
    I:> CD %CATALINA_HOME%\webapps\roller\WEB-INF\dbscripts\mysql
I:> mysql -u root -p
password: *****
mysql> create database roller;
mysql> grant all on roller.* to scott@'%' identified by 'tiger';
mysql> grant all on roller.* to scott@'localhost' identified by 'tiger';
mysql> use roller;
mysql> source createdb.sql
mysql> quit


?$CATALINA_HOME\conf\Catalina\localhost 中放|Roller的Web Application的部|?BR>配置档案 roller.xmlQ内容如下:

<Context path="/roller" docBase="${catalina.home}/webapps/roller" debug="0">
<Realm className="org.apache.catalina.realm.JDBCRealm"
driverName="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/roller?autoReconnect=true&amp;useUnicode=true&amp;characterEncoding=utf-8&amp;mysqlEncoding=utf8"
connectionName="scott"
connectionPassword="tiger"
userTable="rolleruser"
userNameCol="username"
userCredCol="passphrase"
userRoleTable="userrole"
roleNameCol="rolename" debug="0" />

<Resource name="jdbc/rollerdb" auth="Container" type="javax.sql.DataSource" />
<ResourceParams name="jdbc/rollerdb">
<parameter>
<name>factory</name>
<value>org.apache.commons.dbcp.BasicDataSourceFactory</value>
</parameter>
<parameter>
<name>driverClassName</name>
<value>com.mysql.jdbc.Driver</value>
</parameter>
<parameter>
<name>url</name>
<value>jdbc:mysql://localhost:3306/roller?autoReconnect=true&amp;useUnicode=true&amp;characterEncoding=utf-8&amp;mysqlEncoding=utf8</value>
</parameter>
<parameter>
<name>username</name>
<value>scott</value>
</parameter>
<parameter>
<name>password</name>
<value>tiger</value>
</parameter>
<parameter>
<name>maxActive</name>
<value>20</value>
</parameter>
<parameter>
<name>maxIdle</name>
<value>3</value>
</parameter>
<parameter>
<name>removeAbandoned</name>
<value>true</value>
</parameter>
<parameter>
<name>maxWait</name>
<value>3000</value>
</parameter>
</ResourceParams>

<!--//
To enable email notification of comments: uncomment the resouce below,
set your mailhost, and make sure you have mail.jar and activation.jar
in <tomcat>/common/lib.
//-->

<!--//
<Resource name="mail/Session" auth="Container" type="javax.mail.Session"/>
<ResourceParams name="mail/Session">
<parameter>
<name>mail.smtp.host</name>
    
        <value>mailhost.example.com</value>
        </parameter>
    </ResourceParams>
//-->

</Context>

完成上面的设定后Q接下来Q我们修改一下Tomcat中的Connector的设定,使它能够支持UTF-8的编码,
打开 $CATALINA_HOME\conf\server.xmlQ做如下修改...

<!-- Define a non-SSL Coyote HTTP/1.1 Connector on port 8080 -->
<Connector port="80"
    URIEncoding="UTF-8"
    maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
    enableLookups="false" redirectPort="8443" acceptCount="100"
    debug="0" connectionTimeout="20000"
    disableUploadTimeout="true" />

到这里,基本上Tomcat相关的设定就完成了,因ؓRoller Blog可以支持上传附g和全文检索等功能Q?BR>所以,我们q有一个设定的动作...

?$CATALINA_HOME\WebApp\roller 中新Z面层ơ的目录Q?BR>

 $CATALINA_HOME\WebApp\roller
                         |
                         +---data
                               |
                               +---search-index
                               +---uploads

扑ֈ $CATALINA_HOME\webapps\roller\WEB-INF\classes\roller.properties  Q做如下修改Q?BR>

# The directory in which Roller will upload files
# uploads.dir=${user.home}/roller_data/uploads
uploads.dir=${TOMCAT_HOME}/webapps/roller/data/uploads

# force Roller to recreate the entire search index)
# search.index.dir=${user.home}/roller_data/search-index
search.index.dir=${TOMCAT_HOME}/webapps/roller/data/search-index



好了Q大功告成,启动TomcatQ可以运行Roller了!



Victor 2005-12-09 10:55 发表评论
]]>
Jakarta-Common-BeanUtils研究心得(2)[转蝲]http://www.tkk7.com/Victor/articles/21918.htmlVictorVictorWed, 30 Nov 2005 02:39:00 GMThttp://www.tkk7.com/Victor/articles/21918.htmlhttp://www.tkk7.com/Victor/comments/21918.htmlhttp://www.tkk7.com/Victor/articles/21918.html#Feedback0http://www.tkk7.com/Victor/comments/commentRss/21918.htmlhttp://www.tkk7.com/Victor/services/trackbacks/21918.html
六、ConstructorUtils补遗
创徏对象q有一个方法:invokeExactConstructorQ该Ҏ对参数要?BR>更加严格Q传递进ȝ参数必须严格W合构造方法的参数列表?BR>例如Q?BR>Object[] args={new Integer(1), "Jan"};
Class[] argsType={int.class, String.class};
Object obj;
//下面q句调用不会成功,因ؓargs[0]的类型ؓIntegerQ而不是int
//obj = ConstructorUtils.invokeExactConstructor(Month.class, args);

//q一句就可以Q因为argsType指定了类型?BR>obj = ConstructorUtils.invokeExactConstructor(Month.class, args, argsType);
Month month=(Month)obj;
System.out.println(BeanUtils.getProperty(month,"value"));


七、MethodUtils
与ConstructorUtilscMQ不q调用的时候,通常需要再指定一个method name的参数?BR>
八、DynaClass/DynaBean
q似乎是BeanUtils中最有趣的部分之一了,很简单,单到光看q两个接口中的方法会不明?BR>Z么要设计q两个接口。不q看到ResultSetDynaClass后,明白了。下面是java doc中的代码Q?BR>   ResultSet rs = ...;
   ResultSetDynaClass rsdc = new ResultSetDynaClass(rs);
   Iterator rows = rsdc.iterator();
   while (rows.hasNext())  {
     DynaBean row = (DynaBean) rows.next();
     ... process this row ...
   }
   rs.close();
原来q是一个ResultSet的包装器QResultSetDynaClass实现了DynaClassQ它的iteratorҎq回一?BR>ResultSetIteratorQ则是实CDynaBean接口?BR>在获得一个DynaBean之后Q我们就可以?BR>     DynaBean row = (DynaBean) rows.next();
     System.out.println(row.get("field1")); //field1是其中一个字D늚名字

再看另一个类RowSetDynaClass的用法,代码如下Q?BR>String driver="com.mysql.jdbc.Driver";
String url="jdbc:mysql://localhost/2hu?useUnicode=true&characterEncoding=GBK";
String username="root";
String password="";

java.sql.Connection con=null;
PreparedStatement ps=null;
ResultSet rs=null;
try {
Class.forName(driver).newInstance();
con = DriverManager.getConnection(url);
ps=con.prepareStatement("select * from forumlist");
rs=ps.executeQuery();
//先打C下,用于验后面的l果?BR>while(rs.next()){
System.out.println(rs.getString("name"));
}
rs.beforeFirst();//q里必须用beforeFirstQ因为RowSetDynaClass只从当前位置向前滚动

RowSetDynaClass rsdc = new RowSetDynaClass(rs);
rs.close();
ps.close();
List rows = rsdc.getRows();//q回一个标准的ListQ存攄是DynaBean
for (int i = 0; i <rows.size(); i++) {
DynaBean b=(DynaBean)rows.get(i);
System.out.println(b.get("name"));
}
} catch (Exception e) {
e.printStackTrace();
}
finally{
try {
con.close();
} catch (Exception e) {
}
}

是不是很有趣Q封装了ResultSet的数据,代h是占用内存。如果一个表?0万条记录Qrsdc.getRows()
׃q回10万个记录。@_@

需要注意的是ResultSetDynaClass和RowSetDynaClass的不同之处:
1QResultSetDynaClass是基于Iterator的,一ơ只q回一条记录,而RowSetDynaClass是基?BR>List的,一ơ性返回全部记录。直接媄响是在数据比较多时ResultSetDynaClass会比较的快速,
而RowSetDynaClass需要将ResultSet中的全部数据都读出来Qƈ存储在其内部Q,会占用过多的
内存Qƈ且速度也会比较慢?BR>2QResultSetDynaClass一ơ只处理一条记录,在处理完成之前,ResultSet不可以关闭?BR>3QResultSetIterator的next()Ҏq回的DynaBean其实是指向其内部的一个固?BR>对象Q在每次next()之后Q内部的值都会被改变。这样做的目的是节约内存Q如果你需要保存每
ơ生成的DynaBeanQ就需要创建另一个DynaBeanQƈ数据复制过去,下面也是java doc中的代码Q?BR>   ArrayList results = new ArrayList(); // To hold copied list
   ResultSetDynaClass rsdc = ...;
   DynaProperty properties[] = rsdc.getDynaProperties();
   BasicDynaClass bdc =
     new BasicDynaClass("foo", BasicDynaBean.class,
                        rsdc.getDynaProperties());
   Iterator rows = rsdc.iterator();
   while (rows.hasNext()) {
     DynaBean oldRow = (DynaBean) rows.next();
     DynaBean newRow = bdc.newInstance();
     PropertyUtils.copyProperties(newRow, oldRow);
     results.add(newRow);
   }

事实上DynaClass/DynaBean可以用于很多地方Q存储各U类型的数据。自己想吧。嘿ѝ?BR>

九、自定义的CustomRowSetDynaClass
两年前写q一个与RowSetDynaClass目标相同的类Q不q多一个功能,是分页Q只取需要的数据Q?BR>q样内存占用׃减少?BR>
先看一D代码:
String driver="com.mysql.jdbc.Driver";
String url="jdbc:mysql://localhost/2hu?useUnicode=true&characterEncoding=GBK";
String username="root";
String password="";

java.sql.Connection con=null;
PreparedStatement ps=null;
ResultSet rs=null;
try {
Class.forName(driver).newInstance();
con = DriverManager.getConnection(url);
ps=con.prepareStatement("select * from forumlist order by name");
rs=ps.executeQuery();
/*
while(rs.next()){
System.out.println(rs.getString("name"));
}
rs.beforeFirst();
*/

//W二个参数表C第几页Q第三个参数表示늚大小
CustomRowSetDynaClass rsdc = new CustomRowSetDynaClass(rs, 2, 5);
//RowSetDynaClass rsdc = new RowSetDynaClass(rs);
rs.close();
ps.close();
List rows = rsdc.getRows();
for (int i = 0; i <rows.size(); i++) {
DynaBean b=(DynaBean)rows.get(i);
System.out.println(b.get("name"));
}
} catch (Exception e) {
e.printStackTrace();
}
finally{
try {
con.close();
} catch (Exception e) {
}
}
在这里用C一个CustomRowSetDynaClassc,构造方法中增加了page和pageSize两个参数Q?BR>q样Q不数据库里有多少条记录,最多只取pageSize条记录,若pageSize==-1Q则功能?BR>RowSetDynaClass一栗这在大多数情况下是适用的。该cȝ代码如下Q?BR>
package test.jakarta.commons.beanutils;

import java.io.*;
import java.sql.*;
import java.util.*;

import org.apache.commons.beanutils.*;

/**
* @author SonyMusic
*
* To change this generated comment edit the template variable "typecomment":
* Window>Preferences>Java>Templates.
* To enable and disable the creation of type comments go to
* Window>Preferences>Java>Code Generation.
*/
public class CustomRowSetDynaClass implements DynaClass, Serializable {

// ----------------------------------------------------------- Constructors

/**
* <p>Construct a new {@link RowSetDynaClass} for the specified
* <code>ResultSet</code>.  The property names corresponding
* to column names in the result set will be lower cased.</p>
*
* @param resultSet The result set to be wrapped
*
* @exception NullPointerException if <code>resultSet</code>
*  is <code>null</code>
* @exception SQLException if the metadata for this result set
*  cannot be introspected
*/
public CustomRowSetDynaClass(ResultSet resultSet) throws SQLException {

this(resultSet, true);

}

/**
* <p>Construct a new {@link RowSetDynaClass} for the specified
* <code>ResultSet</code>.  The property names corresponding
* to the column names in the result set will be lower cased or not,
* depending on the specified <code>lowerCase</code> value.</p>
*
* <p><strong>WARNING</strong> - If you specify <code>false</code>
* for <code>lowerCase</code>, the returned property names will
* exactly match the column names returned by your JDBC driver.
* Because different drivers might return column names in different
* cases, the property names seen by your application will vary
* depending on which JDBC driver you are using.</p>
*
* @param resultSet The result set to be wrapped
* @param lowerCase Should property names be lower cased?
*
* @exception NullPointerException if <code>resultSet</code>
*  is <code>null</code>
* @exception SQLException if the metadata for this result set
*  cannot be introspected
*/
public CustomRowSetDynaClass(ResultSet resultSet, boolean lowerCase)
throws SQLException {

this(resultSet, 1, -1, lowerCase);

}

public CustomRowSetDynaClass(
ResultSet resultSet,
int page,
int pageSize,
boolean lowerCase)
throws SQLException {

if (resultSet == null) {
throw new NullPointerException();
}
this.lowerCase = lowerCase;
this.page = page;
this.pageSize = pageSize;

introspect(resultSet);
copy(resultSet);

}

public CustomRowSetDynaClass(ResultSet resultSet, int page, int pageSize)
throws SQLException {
this(resultSet, page, pageSize, true);
}

// ----------------------------------------------------- Instance Variables

/**
* <p>Flag defining whether column names should be lower cased when
* converted to property names.</p>
*/
protected boolean lowerCase = true;

protected int page = 1;
protected int pageSize = -1;

/**
* <p>The set of dynamic properties that are part of this
* {@link DynaClass}.</p>
*/
protected DynaProperty properties[] = null;

/**
* <p>The set of dynamic properties that are part of this
* {@link DynaClass}, keyed by the property name.  Individual descriptor
* instances will be the same instances as those in the
* <code>properties</code> list.</p>
*/
protected Map propertiesMap = new HashMap();

/**
* <p>The list of {@link DynaBean}s representing the contents of
* the original <code>ResultSet</code> on which this
* {@link RowSetDynaClass} was based.</p>
*/
protected List rows = new ArrayList();

// ------------------------------------------------------ DynaClass Methods

/**
* <p>Return the name of this DynaClass (analogous to the
* <code>getName()</code> method of <code>java.lang.Class</code), which
* allows the same <code>DynaClass</code> implementation class to support
* different dynamic classes, with different sets of properties.</p>
*/
public String getName() {

return (this.getClass().getName());

}

/**
* <p>Return a property descriptor for the specified property, if it
* exists; otherwise, return <code>null</code>.</p>
*
* @param name Name of the dynamic property for which a descriptor
*  is requested
*
* @exception IllegalArgumentException if no property name is specified
*/
public DynaProperty getDynaProperty(String name) {

if (name == null) {
throw new IllegalArgumentException("No property name specified");
}
return ((DynaProperty) propertiesMap.get(name));

}

/**
* <p>Return an array of <code>ProperyDescriptors</code> for the properties
* currently defined in this DynaClass.  If no properties are defined, a
* zero-length array will be returned.</p>
*/
public DynaProperty[] getDynaProperties() {

return (properties);

}

/**
* <p>Instantiate and return a new DynaBean instance, associated
* with this DynaClass.  <strong>NOTE</strong> - This operation is not
* supported, and throws an exception.</p>
*
* @exception IllegalAccessException if the Class or the appropriate
*  constructor is not accessible
* @exception InstantiationException if this Class represents an abstract
*  class, an array class, a primitive type, or void; or if instantiation
*  fails for some other reason
*/
public DynaBean newInstance()
throws IllegalAccessException, InstantiationException {

throw new UnsupportedOperationException("newInstance() not supported");

}

// --------------------------------------------------------- Public Methods

/**
* <p>Return a <code>List</code> containing the {@link DynaBean}s that
* represent the contents of each <code>Row</code> from the
* <code>ResultSet</code> that was the basis of this
* {@link RowSetDynaClass} instance.  These {@link DynaBean}s are
* disconnected from the database itself, so there is no problem with
* modifying the contents of the list, or the values of the properties
* of these {@link DynaBean}s.  However, it is the application's
* responsibility to persist any such changes back to the database,
* if it so desires.</p>
*/
public List getRows() {

return (this.rows);

}

// ------------------------------------------------------ Protected Methods

/**
* <p>Copy the column values for each row in the specified
* <code>ResultSet</code> into a newly created {@link DynaBean}, and add
* this bean to the list of {@link DynaBean}s that will later by
* returned by a call to <code>getRows()</code>.</p>
*
* @param resultSet The <code>ResultSet</code> whose data is to be
*  copied
*
* @exception SQLException if an error is encountered copying the data
*/
protected void copy(ResultSet resultSet) throws SQLException {
int abs = 0;
int rowsCount = 0;
int currentPageRows = 0;
resultSet.last();
rowsCount = resultSet.getRow();
if (pageSize != -1) {
int totalPages = (int) Math.ceil(((double) rowsCount) / pageSize);
if (page > totalPages)
page = totalPages;
if (page < 1)
page = 1;
abs = (page - 1) * pageSize;

//currentPageRows=(page==totalPages?rowsCount-pageSize*(totalPages-1):pageSize);
} else
pageSize = rowsCount;
if (abs == 0)
resultSet.beforeFirst();
else
resultSet.absolute(abs);
//int
while (resultSet.next() && ++currentPageRows <= pageSize) {
DynaBean bean = new BasicDynaBean(this);
for (int i = 0; i < properties.length; i++) {
String name = properties[i].getName();
bean.set(name, resultSet.getObject(name));
}
rows.add(bean);
}

}

/**
* <p>Introspect the metadata associated with our result set, and populate
* the <code>properties</code> and <code>propertiesMap</code> instance
* variables.</p>
*
* @param resultSet The <code>resultSet</code> whose metadata is to
*  be introspected
*
* @exception SQLException if an error is encountered processing the
*  result set metadata
*/
protected void introspect(ResultSet resultSet) throws SQLException {

// Accumulate an ordered list of DynaProperties
ArrayList list = new ArrayList();
ResultSetMetaData metadata = resultSet.getMetaData();
int n = metadata.getColumnCount();
for (int i = 1; i <= n; i++) { // JDBC is one-relative!
DynaProperty dynaProperty = createDynaProperty(metadata, i);
if (dynaProperty != null) {
list.add(dynaProperty);
}
}

// Convert this list into the internal data structures we need
properties =
(DynaProperty[]) list.toArray(new DynaProperty[list.size()]);
for (int i = 0; i < properties.length; i++) {
propertiesMap.put(properties[i].getName(), properties[i]);
}

}

/**
* <p>Factory method to create a new DynaProperty for the given index
* into the result set metadata.</p>
*
* @param metadata is the result set metadata
* @param i is the column index in the metadata
* @return the newly created DynaProperty instance
*/
protected DynaProperty createDynaProperty(
ResultSetMetaData metadata,
int i)
throws SQLException {

String name = null;
if (lowerCase) {
name = metadata.getColumnName(i).toLowerCase();
} else {
name = metadata.getColumnName(i);
}
String className = null;
try {
className = metadata.getColumnClassName(i);
} catch (SQLException e) {
// this is a patch for HsqlDb to ignore exceptions
// thrown by its metadata implementation
}

// Default to Object type if no class name could be retrieved
// from the metadata
Class clazz = Object.class;
if (className != null) {
clazz = loadClass(className);
}
return new DynaProperty(name, clazz);

}

/**
* <p>Loads and returns the <code>Class</code> of the given name.
* By default, a load from the thread context class loader is attempted.
* If there is no such class loader, the class loader used to load this
* class will be utilized.</p>
*
* @exception SQLException if an exception was thrown trying to load
*  the specified class
*/
protected Class loadClass(String className) throws SQLException {

try {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
if (cl == null) {
cl = this.getClass().getClassLoader();
}
return (cl.loadClass(className));
} catch (Exception e) {
throw new SQLException(
"Cannot load column class '" + className + "': " + e);
}

}

}

大部分代码从BeanUtils的源码中取得Q只做了单的修改Q没有加多余的注释。如果要正式使用Q?BR>需要再做精加工?BR>
Q=Q=Q=Q=Q=Q=Q=Q=Q=Q=Q=Q=Q=Q=Q=Q=Q=Q=Q=Q=
关于q个包,只准备测试到q里了,不过已经有了大概的印象了Q至,知道q个包可以做些什么?BR>其实q个W记也只是v到这个作用。@_@


Victor 2005-11-30 10:39 发表评论
]]>
Jakarta-Common-BeanUtils研究心得(1)[转蝲]http://www.tkk7.com/Victor/articles/21917.htmlVictorVictorWed, 30 Nov 2005 02:38:00 GMThttp://www.tkk7.com/Victor/articles/21917.htmlhttp://www.tkk7.com/Victor/comments/21917.htmlhttp://www.tkk7.com/Victor/articles/21917.html#Feedback0http://www.tkk7.com/Victor/comments/commentRss/21917.htmlhttp://www.tkk7.com/Victor/services/trackbacks/21917.html
一、概q?BR>W一ơ看到BeanUtils包,是在Struts目中,作ؓStruts一个工h使用的,
估计功能弄强Q就UdCommon目中了吧?BR>
BeanUtils一共有四个package:
org.apache.commons.beanutils
org.apache.commons.beanutils.converters
org.apache.commons.beanutils.locale
org.apache.commons.beanutils.locale.converters
后三个包主要是用于数据的转换Q围l着一个Converter接口Q该接口只有一个方法:
java.lang.Object convert(java.lang.Class type, java.lang.Object value) Q?BR>用于一个value转换成另一个类型ؓtype的Object。在一些自动化的应用中应该会有用?BR>q里不作评论Q以后有兴趣了,或者觉得有用了Q再行研I?BR>q里只讲W一个包?BR>
二、测试用的Bean
在开始所有的试之前Q我写了一个简单的BeanQ以便于试Q代码如下:
package test.jakarta.commons.beanutils;

/**
 * @author SonyMusic
 *
 */
public class Month {
 private int value;
 private String name;
 private int[] days={11,22,33,44,55};

 public Month(int v, String n){
   value=v;
   name=n;
 }
 
 /**
  * Returns the name.
  * @return String
  */
 public String getName() {
   return name;
 }

 /**
  * Returns the value.
  * @return int
  */
 public int getValue() {
   return value;
 }

 /**
  * Sets the name.
  * @param name The name to set
  */
 public void setName(String name) {
   this.name = name;
 }

 /**
  * Sets the value.
  * @param value The value to set
  */
 public void setValue(int value) {
   this.value = value;
 }

 /**
  * @see java.lang.Object#toString()
  */
 public String toString() {
   return value+"("+name+")";
 }

 public int[] getDays() {
   return days;
 }

 public void setDays(int[] is) {
   days = is;
 }

}

三、BeanUtils
q是一个主要应用于Bean的UtilQ呵呵,q个解释很绝吧)Q以下是其中几个Ҏ的例?BR>
//static java.util.Map describe(java.lang.Object bean)
//q个Ҏq回一个Object中所有的可读属性,q将属性名/属性值放入一个Map中,另外q有
//一个名为class的属性,属性值是Object的类名,事实上class是java.lang.Object的一个属?BR> Month month=new Month(1, "Jan");
 
 try {
   Map map=BeanUtils.describe(month);
   Set keySet=map.keySet();
   for (Iterator iter = keySet.iterator(); iter.hasNext();) {
     Object element = (Object) iter.next();
     System.out.println("KeyClass:"+element.getClass().getName());
     System.out.println("ValueClass:"+map.get(element).getClass().getName());
     System.out.print(element+"\t");
     System.out.print(map.get(element));
     System.out.println();
   }
 } catch (IllegalAccessException e) {
   e.printStackTrace();
 } catch (InvocationTargetException e) {
   e.printStackTrace();
 } catch (NoSuchMethodException e) {
   e.printStackTrace();
 }
输出为:
KeyClass:java.lang.String
ValueClass:java.lang.String
value  1
KeyClass:java.lang.String
ValueClass:java.lang.String
class  class test.jakarta.commons.beanutils.Month
KeyClass:java.lang.String
ValueClass:java.lang.String
name  Jan
注意到所有Map中的key/value都是StringQ而不object中实际的值是多少?BR>与此对应的还有static void populate(java.lang.Object bean, java.util.Map properties)
用于刚才describe出的Map再装配成一个对象?BR>

再看q样一D代?BR>Ҏ钢也许还记得Qؓ了取一个不定对象的propertyQ着实花了不时_
隑ֺ不大Q但要做?00%的正,仍然需要付出很大的_֊?BR>//static java.lang.String getProperty(java.lang.Object bean, java.lang.String name)
 Month month=new Month(1, "Jan");
 
 try {
   System.out.println(BeanUtils.getProperty(month,"value"));
 } catch (Exception e) {
   e.printStackTrace();
 }
//输出是:1

与getPropertycM的还有getIndexedProperty, getMappedPropertyQ?BR>以getIndexedPropertyZQ?BR> Month month=new Month(1, "Jan");
 
 try {
   System.out.println(BeanUtils.getIndexedProperty(month,"days",1));
   System.out.println(BeanUtils.getIndexedProperty(month,"days[1]"));
 } catch (Exception e) {
   e.printStackTrace();
 }
q两个调用是相同的?BR>

BeanUtils中还有一个方法:
static void copyProperties(java.lang.Object dest, java.lang.Object orig)
它真是太有用Q还记得struts中满天飞的都是copyPropertiesQ我甚至怀疑整个BeanUtils最?BR>是不是因个方法的需求才写出来的?BR>它将对象orig中的属性复制到dest中去?BR>

四、PropertyUtils
q个cdBeanUtilscd多的Ҏ在参C都是相同的,但返回g同?BR>BeanUtils着重于"Bean"Q返回值通常是StringQ而PropertyUtils着重于属性,
它的q回值通常是Object?BR>

五、ConstructorUtils
q个cM的方法主要分成两U,一U是得到构造方法,一U是创徏对象?BR>事实上多数时候得到构造方法的目的是创徏对象Q这里只介绍一下创建对象?BR>//static java.lang.Object ConstructorUtils.invokeConstructor
//(java.lang.Class klass, java.lang.Object[] args)
//Ҏ一个java.lang.Class以及相应的构造方法的参数Q创Z个对象?BR> Object obj=ConstructorUtils.invokeConstructor(Month.class, {new Integer(1), "Jan"});
 Month month=(Month)obj;
 try {
   System.out.println(BeanUtils.getProperty(month,"value"));
 } catch (Exception e) {
   e.printStackTrace();
 }
输出证明Q构造方法的调用是成功的?BR>如果需要强制指定构造方法的参数cdQ可以这栯用:
   Object[] args={new Integer(1), "Jan"};
   Class[] argsType={int.class, String.class};
   Object obj;
   obj = ConstructorUtils.invokeExactConstructor(Month.class, args, argsType);
   Month month=(Month)obj;
   System.out.println(BeanUtils.getProperty(month,"value"));
argsType指定了参数的cd?BR>

Victor 2005-11-30 10:38 发表评论
]]>
实施Jakarta Commons-DbUtilshttp://www.tkk7.com/Victor/articles/21911.htmlVictorVictorWed, 30 Nov 2005 02:18:00 GMThttp://www.tkk7.com/Victor/articles/21911.htmlhttp://www.tkk7.com/Victor/comments/21911.htmlhttp://www.tkk7.com/Victor/articles/21911.html#Feedback0http://www.tkk7.com/Victor/comments/commentRss/21911.htmlhttp://www.tkk7.com/Victor/services/trackbacks/21911.html作者: Builder.com
Monday, April 26 2004 4:43 PM

JDBC码是Java译码的一个部分,它给已写的编码带来了数量惊h的重复。另外,JDBC码几乎会l常性的带来一些低U错误。写出好的JDBC~码q不难,但是很痛苦?/P>
Click here to find out more!


DbUtilslg是一个精密而简单的lgQ它q不做什么复杂的事而仅仅只是很多的JDBCd对开发者来说变得稍Ҏ一炏V尽这时候很多持久框架和包都可以用来使数据持久变得更ҎQ然而JDBC仍然是大多数Java和Java2企业?J2EE)开发者赖以生存的工具。因此,M能让使用JDBC工作更容易的东西都是好消息?/P>

 

DbUtils可以免费下蝲Q它不依赖于M其它的通用lg而只是依赖下面这些:

  • Java Development Kit (JDK) 1.2 (or later)
  • JDBC 2.0 (or later)


 DbUtils文档q不是最好的Q但是以你的工作正常q行。在下一节,你会看到DbUtils中最有用的类以及一些关于它们的用法的例子。你应该能够很容易地使用q些~码和例子,然后能够马上在你自己的项目中开始用DbUtils。我会集中_֊于两个类Qorg.apache.commons.dbutils.DbUtils  和org.apache.commons.dbutils.QueryRunnerQ和一个接口(org.apache.commons.dbutils.ResultSethandlerQ?在我l你们一些关于它们的用法的例子之前,让我们深入DbUtils里面来仔l看看它l我们提供了些什么?/P>

DbUtils
DbUtils是一个ؓ做一些诸如关闭连接、装载JDBC驱动E序之类的常规工作提供有用方法的c,它里面所有的Ҏ都是静态的?BR>

q个c里的重要方法有Q?/P>

  • closeQDbUtilscL供了三个重蝲的关闭方法。这些方法检查所提供的参数是不是NULLQ如果不是的话,它们关闭连接、声明和l果集(ResultSetQ?
  • CloseQuietly: CloseQuietlyq一Ҏ不仅能在q接、声明或者结果集QResultSetQؓNULL情况下避免关闭,q能隐藏一些在E序中抛出的SQLEeception。如果你不想捕捉q些异常的话Q这对你是非常有用的。在重蝲CloseQuietlyҎӞ特别有用的一个方法是closeQuietly(Connection conn,Statement stmt,ResultSet rs)Q这是因为在大多数情况下Q连接、声明和l果集(ResultSetQ是你要用的三样东西Q而且在最后的块你必须关闭它们。用这一ҎQ你最后的块就可以只需要调用这一Ҏ卛_?
  • CommitAndCloseQuietly(Connection conn)Q这一Ҏ用来提交q接Q然后关闭连接,q且在关闭连接时不向上抛出在关闭时发生的一些SQL异常?
  • LoadDriver(String driveClassName): q一Ҏ装蝲q注册JDBC驱动E序Q如果成功就q回TRUE。用这U方法,你不需要去捕捉q个异常ClassNotFoundException。用loadDriveҎQ编码就变得更容易理解,你也得C一个很好的Booleanq回|q个q回g告诉你驱动类是不是已l加载成功了?
ResultSetHandler
Click here to find out more!

正如它的名字所提示的,q一接口执行处理一个jaca.sql.ResultSetQ将数据转变q处理ؓM一UŞ式,q样有益于其应用而且使用h更容易。这一lg提供了ArrayHandler, ArrayListHandler, BeanHandler, BeanListHandler, MapHandler, MapListHandler, and ScalarHandler{执行程序?BR> 

ResultSetHandler接口提供了一个单独的ҎQ?I>Object handle (java.sql.ResultSet .rs)。因此Q何ResultSetHandler 的执行需要一个结果集QResultSetQ作为参C入,然后才能处理q个l果集,再返回一个对象。因回类型是java.lang.ObjectQ所以除了不能返回一个原始的Javacd之外Q其它的q回cdq没有什么限制。如果你发现q七个执行程序中没有M一个提供了你想要的服务Q你可以自己写执行程序ƈ使用它?/P>

QreryRunner

q个cM执行SQL查询单化了,它与ResultSetHandler串联在一h效地履行着一些^常的dQ它能够大大减少你所要写的编码。QueryRunnercL供了两个构造器Q其中一个是一个空构造器Q另一个则拿一?javax.sql.DataSource 来作为参数。因此,在你不用Z个方法提供一个数据库q接来作为参数的情况下,提供l构造器的数据源(DataSource) 被用来获得一个新的连接ƈl进行下厅R?/P>

 

q一cM的重要方法包括以下这些:

  • query(Connection conn, String sql, Object[] params, ResultSetHandler rsh):q一Ҏ执行一个选择查询Q在q个查询中,对象阵列的D用来作ؓ查询的置换参数。这一Ҏ内在地处理PreparedStatement 和ResultSet  的创建和关闭。ResultSetHandlerҎ?ResultSet得来的数据{变成一个更Ҏ的或是应用程序特定的格式来用?
  • query(String sql, Object[] params, ResultSetHandler rsh):q几乎与W一U方法一P唯一的不同在于它不将数据库连接提供给ҎQƈ且它是从提供l构造器的数据源(DataSource) 或用的setDAtaSource Ҏ中重新获得的?
  • query(Connection conn, String sql, ResultSetHandler rsh):q执行一个不要参数的选择查询?
  • update(Connection conn, String sql, Object[] params):q一Ҏ被用来执行一个插入、更新或删除操作。对象阵列ؓ声明保存着|换参数?

 

现在让我们来看一个例子,在这里你可以从一个数据库中获得一些数据。比如说Q我正在使用MySQL 数据?/A>.你还需要下?A >MYSQL JDBC驱动E序。我正在使用的MySQL数据库在本地LQ端口号?306上运行。这个数据库地名字叫做test。你要用到的Student表的l构如下Q?BR>Columns    Type
-------    ----
StudId     int
Name      varchar

在列表A中,你将会从Student表中得到一些信息,而且你可以按照你自己的额外需要修改这些信息。尽你在用JDBCQ但要注意你几乎没写JDBC~码?你可能要改变在例子中所规定的用户名和密码,q是以你的具体的数据库配|ؓ基础的?

q个~码遵从以下步骤Q?/P>

Click here to find out more!

1Q加载JDBC驱动E序c,q用DriverManager来得C个数据库q接?/P>

2Q例C?QueryRunner cR?/P>

3Q用连接、SQL查询、参数和ResultSetHandler来作入的查询Ҏ。你使用一个类org.apache.commons.dbutils.handlers.MapListHandlerQ一个类 MapListHandler来获得一个结果集QResultSetQƈq回一个jaca.util.Map的实例java.util.List。因此结果集QResultSetQ?的每一行都变成了一个java.util.Map,所有这些java.util.Map的实例绑在一h在一个java.util.List 中?/P>

4. 反复得到列表(List)的值就是通过在列?List)中获得每一个Map的倹{?/P>

5.用QueryRunner 来执行一个没有参数的Ҏ。在q里你要用BeanListHandler Q它是一个非常有用的ResultSetHandler Q因Z可以把ResultSet 转变成一个指定的Bean的列表中。这时你可以指定一个BeancdBean StudentBean中,如同?A >列表B中所昄的那栗?/P>

6. 你通过反复从列表(ListQ中得到多个beanQ然后就可从每一个StudentBean实例中获取倹{?/P>

 

注释Q在列表B中,StudentBean cM的StudId 必须是intQ这是因Student的StudId列的cd是int。坚持这个类型的匚w是我们需要遵从的唯一规则?BR> 

因ؓ在这U情况下QStudentBean cȝ属性和表Student 的字D|完好的对映着的,只要StuentBean cMZ个参数就是一个技巧。字D值用和字D名一L名字插入到类的属性中。然而,如果你想要更多地控制bean的创建,则类BeanListHandler提供了第二个构造器QBeanListHandler(java.lang.Class type, RowProcessor convert). 接口Rowprocessor的执行把l果集(ResultSetQ的各行转化成一个对象组。在 StudentBeanq一案例中,RowProcessor中的BasicRowProcessor 的执行被利用上了Q它能够执行q项d。然而,你可以写一个新的执行ƈ把它提供lBeanListHandler的构造器?BR> 

当然Q执行这一~码的输出取决于你从表Student中获得哪些数据。对我来_我得C以下q些输出Q?BR>***Using MapListHandler***
  Id >>1
  Name >>One
  Id >>2
  Name >>Two
***Using BeanListHandler***
  Id >>1
  Name >>One
  Id >>2
  Name >>Two
  Id >>3
  Name >>Three

除了到目前ؓ止你已经看了的类以外Q另外一些你需要研I的cLQ?/P>

  • org.apache.commons.dbutils.QueryLoader:QueryLoader是一个从一个文件加载查询到一个Map的简单的cR然后,当需要的时候,你从 Map 中选择一些查询。在没有专门L触代码的情况下,一个文件中的Having查询也可以改变得可能的单?
  • org.apache.commons.dbutils.wrappers.SqlNullCheckedResultSet:q个cd使用一个系l方法来解决NULL值问题是很有用的。用一?SqINullCheckedResultSet 的实例来限制一个常规的l果集(ResultSetQ?Q然后详l地说明在遇NULL值的情况下应该做些什么?
  • org.apache.commons.dbutils.wrappers.StringTrimmedResultSet:用类StringTrimmedResultSet 来约束一个结果集Q这样一来,你就可以修整所有getString()和getObject()Ҏq回的字W串?

 

DbUtils lg很好也很yQ很值得在所有用到JDBC的项目中M用?BR>
DBUtils的JUnit例子Q?BR>

//使用dbutils1.0版本

import java.util.*;
import java.util.logging.*;
import java.sql.*;
import org.apache.commons.dbutils.*;
import org.apache.commons.dbutils.handlers.*;

public class TestDBUnits {
 
 public static void main(String[]args) throws Exception {
  TestDBUnits test = new TestDBUnits();
  
  for(int i = 0 ; i < 1 ; i++) {   
   test.testQuery1();
   test.testQuery2();
   test.testUpdate();
  }
 }
 
 public void testQuery1(){
  try {
   QueryRunner qr = new QueryRunner() ;
   ResultSetHandler rsh = new ArrayListHandler();   
   String strsql = "select * from test1";   
   ArrayList result = (ArrayList)qr.query(getConnection() ,strsql ,rsh);
   //System.out.print("");
  } catch(Exception ex) {
   ex.printStackTrace(System.out);
  }
 }
 
 public void testQuery2(){
  try {
   QueryRunner qr = new QueryRunner() ;
   ResultSetHandler rsh = new MapListHandler();   
   String strsql = "select * from test1";   
   ArrayList result = (ArrayList)qr.query(getConnection() ,strsql ,rsh);
   for(int i = 0 ; i < result.size() ; i++) {
    Map map = (Map)result.get(i);
    //System.out.println(map);    
   }
   //System.out.print("");
  } catch(Exception ex) {
   ex.printStackTrace(System.out);
  }
 }
 
 public void testUpdate(){
  try {
   QueryRunner qr = new QueryRunner() ;
   ResultSetHandler rsh = new ArrayListHandler();
   String strsql = "insert test1(page ,writable ,content)values('ttt','ttt','faskldfjklasdjklfjasdklj')";
   qr.update(getConnection() ,strsql);
   //System.out.print("");
  } catch(Exception ex) {
   ex.printStackTrace(System.out);
  }
 }
 
 private  Connection getConnection() throws InstantiationException,
   IllegalAccessException, ClassNotFoundException, SQLException {
  
  String strDriver = "org.gjt.mm.mysql.Driver";
  String strUrl = "jdbc:mysql://localhost:3306/test";
  String strUser = "root";
  String strPass = "";
   
  Class.forName(strDriver).newInstance();  
  return DriverManager.getConnection(strUrl, strUser, strPass);
 }
}



Victor 2005-11-30 10:18 发表评论
]]>
实现囑ŞJSFlghttp://www.tkk7.com/Victor/articles/21857.htmlVictorVictorTue, 29 Nov 2005 10:11:00 GMThttp://www.tkk7.com/Victor/articles/21857.htmlhttp://www.tkk7.com/Victor/comments/21857.htmlhttp://www.tkk7.com/Victor/articles/21857.html#Feedback0http://www.tkk7.com/Victor/comments/commentRss/21857.htmlhttp://www.tkk7.com/Victor/services/trackbacks/21857.html--很简单地构徏一个纯HTML无法L实现的图形Web应用E序lg

旉Q?005-07-07
作者:Marc Durocher
览ơ数Q? 2464
本文关键字:JSF
文章工具
推荐l朋?src="http://dev2dev.bea.com.cn/images/letter001.gif" 推荐l朋?/A>
打印文章 
打印文章

  开发h员认为,如果有合适的工具来创Z互式Web界面Q他们就能将旉集中在核心需求和定制上,q在规定旉内及时得交付应用E序。与其他技术如JavaServer Pages或Apache Struts 相比QJavaServer Faces (JSF)技术ؓ创徏交互式Web应用E序带来了很多便利。JSF在程序逻辑和GUI表示之间划出一条清晰的界限Q提高了对WebE序的维护能力,qؓWeb用户界面lg的开发和重用提供了一个框架?/P>

  如今Q许多Web应用E序开发h员都在{而用JSFQ但是他们发玎ͼ预先定制的JSF UIlg受到基本DHTMLH口部g的限制。监或业务程监控之类的高U应用程序需要能与JSF框架兼容的高U可视化lg。JSF框架的标准化使它易于开发能够重用的自定义Web GUIlg。另外,Weblg开发商现在能提供更复杂的组Ӟq承诺Web应用E序开发h员能够轻村֜使用q些lg。此cJSF用户界面lg必须集成q|到JSFq行时框架中去,q在其中完全展开Q还必须在设计时很好地集成到提供JSF支持的IDE中去?/P>

  管JSF带来了标准用L面框Ӟ但对于开发第一个自定义JSFlg而言Q还是存在几个缺陷和漏洞。让我们看看怎样创徏一个纯HTML无法L创徏的图形JSFlg。图形JSFlg的特点不仅要求生成DHTMLQ而且q需要对囑փ生成和客L交互提供补充支持。我们将以一个图形组件的例子来阐q这些特炏V该囑Şlg能够提供曲线图,qؓ各种客户端导航和交互提供便利。我们还会了解到该囑Şlg集成到JSF-enabled IDE中所需要的步骤。通过理解囑Şlg的设计方法,您将会更好地理解如何实现JSFlgQ而这应该能您开发出定制的JSF囑Şlg?/P>

什么是JSFQ?/STRONG>

  JSF是一U能够简化Web应用E序表示层结构的标准服务器端框架。定义JSF框架的JSR 127Q参见参考资料)带有一个能提供基本UIlgQ如输入栏和按纽Q的参考实现。您可以可重用用户界面lg集中h创徏Web,这些组件绑定到应用数据源上Qƈ用服务器端事件控制程序处理客L事g。根据说明书介绍Q组件供应商能编写与JSFq行时框枉成的lgQƈ其集成到在设计时与JSF兼容的IDE中去?/P>

  从很大程度上ԌJSFlg同在HTML 2.0技术要求下可用的HTMLlg和标{接相W合。对许多Web应用E序而言Q这套相对简单的lg是够用的。然而,许多应用E序如监或监控E序需要更复杂的数据显CZ交互Q比如制表、制囑֒映射。由于JSFlg在HTML中直接提交复杂图形小部g的能力有限,所以设计这些高U组件的能力q不H出。解x案要求服务器端组件向客户传输囑փQ却会给自n带来问题Q因为在基本HTML囑փ上进行交互要受到限制。最后,使用JavaScriptӞ必须能调用客L交互来用户能对数据q行D和交互?/P>

  让我们看看开发一个简单的、将CSS输入HTML面的JSFlg需要哪些步骤。当开发高UJSF囑ŞlgӞq一单组件的描述和代码样本会作ؓ背景。图1昄了如何用即开发的lgQƈ昄要得到的结果。用这U组件的好处是能够通过改变某个JSF动作的组件|来改变整个页面的外观?

?Q显CZ我们如何使用一个非常简单的JSFlgCSS输入某个HTML面q得出结果?/P>

开发组?/STRONG>

  JSFlg包含若干个Javacd配置文g。ؓ创徏一个自定义JSFlgQ您需要开发一个扩展JSF基本lgcȝJavac;为默认呈现Y件包开发呈现程序;开发一个将在JSP面中用于描q标{Javac;~写一个标{ֺ定义QTLDQ文Ӟ~写JSF配置文g。让我们更深入地了解q?个步骤?/P>

  开发组件JavacR组件类负责理代表lg状态的属性。因此,我们必须Ҏlg的行为(如输入组件或输出lgQ,l组仉择适当的基c(参见清单1Q。这里描q的lg可进行扩展javax.faces.component.UIOutputQ以昄指向某个样式表文件的URLQ或内联式样式表的内宏V该lg可用于在JSF动作中将某个样式表{换成另一个样式表。关联属性规定着值的cdQ要么是一个URLQ要么是内联样式。该lgq必能够在向服务器发送请求期_使用l过JSF框架处理的对象,来存储ƈ修复自己的状态。组件的状态由重徏对象所需的重要属性值组成。JSF框架自动调用saveState()和restoreState()ҎQ我们可以在lg中实现这两种Ҏ来达到这一目标?/P>

清单1. lgcȝ理显C组件状态的属性。可依据lg的行为,为其选择一个适当的基cR在本例中,该组件扩展javax.faces.component.UIOutputQ以昄指向某个样式表文件的URLQ或者某个内联式样式表的内容?STRONG>

import javax.faces.component.*;
public class CSSComponent extends UIOutput {
	private Boolean link; 
	public String getFamily() { 
		return "faces.CSSFamily"; 
	} 
	public boolean isLink() { 
		if (link != null) 
			return link.booleanValue(); 
			ValueBinding vb = getValueBinding("link"); 
	if (vb != null) { 
		Boolean bvb = (Boolean) vb.getValue(
			FacesContext.getCurrentInstance()); 
		if (bvb != null) 
			return bvb.booleanValue(); 
	} 
	return false; 
	} 
	public void setLink(boolean link) { 
		this.link = new Boolean(link); 
	} 
	public Object saveState(FacesContext context) { 
		return new Object[] { super.saveState(context), 
			link }; 
	} 
	public void restoreState(FacesContext context, 
	Object stateObj) { 
		Object[] state = (Object[]) stateObj; 
		super.restoreState(context, state[0]); 
		link = (Boolean) state[1]; 
	} 
}

开发呈现程序?/STRONG>呈现E序有两个作用。第一Q呈现程序负责发送适当的HTMLE序D,该程序段能在客户端中呈现lg。通常情况下,q个HTMLE序D는一些适于呈现整个Web览器的HTML标签l成。此JSF生存周期UC~码阶段或呈现—响应阶Dc该响应阶段q能发送增强客L交互性的JavaScript代码?/P>

  呈现E序的第二个作用是解析来自客L的数据,从而对服务器端的组件状态进行更斎ͼ如用户在文本字段输入的文本)。标准呈现程序Y件包h强制性,但也可以提供其他呈现E序软g包,用于提供可替换的客户端表C法或SVG之类的语aQ参见参考资料)。通过验组件的q接属性,您实现的呈现E序Q参见清?Q将选择在HTML面中发送的CSS样式?/P>

清单2. 标准呈现E序软g包具有强制性,但是Q您可以使用其他呈现E序软g包,来提供可替换的客L表示法或语言。通过验组件的q接属性,您实现的呈现E序选择在HTML面中发出的CSS样式?/P>

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.render.Renderer;
public class CSSRenderer extends Renderer {
public void encodeEnd(FacesContext context,
UIComponent component)
	throws IOException {
		super.encodeEnd(context, component);
		if (component instanceof CSSComponent) {
			CSSComponent cssComponent  =
				(CSSComponent) component;
			String css = (String)cssComponent.getValue();
			boolean isLink = cssComponent.isLink();
			if (css != null) 
				if (isLink)  
				context.getResponseWriter().write(
				"<link type='text/css' rel='stylesheet' 
				href='" + css + "'/>");
			else 
				context.getResponseWriter().write(
					"<style>;\n" + css + "\n<style/>\n"); 
		}
	}
}

开发标{?/STRONG>同样QJSF框架提供了用于扩展的基类Q来~写与组件相关的标签。该标签c负责定义ƈ呈现在faces-config.xml文g中应用的lg样式Q这U样式的描述很简短)。它q负责创建JSFlgQ由JSF框架来处理)Q传递JSF标签中所包含的属性,该属性用于初始化lgQ参见清?Q?/P>

清单3. 该标{定义了将在faces-config.xml文g中应用的lg的样式和lg呈现方式?/P>

import javax.faces.webapp.UIComponentTag;
public class CSSTag 
	extends UIComponentTag {
	private String value;
	private String link;
	public String getComponentType() {
		return "faces.CSSComponent";
	}
	public String getRendererType() {
		return “HTML.LinkOrInlineRenderer";
	}
	protected void setProperties(UIComponent component) {
		super.setProperties(component);
		Application app = 
			getFacesContext().getApplication();
		if (value != null)
			if (isValueReference(value)) 
				component.setValueBinding("value", 
				app.createValueBinding(value));
			else
				component.getAttributes().put("value", value);
		if (link != null) 
			if (isValueReference(link))
				component.setValueBinding("link",
				app.createValueBinding(link));
			else
				component.getAttributes().put("link",
				new Boolean(link));
	}
	public String getLink() {
		return link;
	}
	public void setLink(String link) {
		this.link = link;
	}
	public String getValue() {
		return value;
	}
	public void setValue(String value) {
		this.value = value;
	}
}

  该标{提供setter和getter来管理链接和值属性。组件一旦创建,便会调用setProperties()ҎQ对标签属性进行初始化。每个标{ֱ性都无外乎两U:要么是文字|要么是bean属性的一个绑定?/P>

~写标签库定义(TLDQ?/STRONG>TLD是一个XML文gQ它通过标{֐与相应的Javacȝ兌来描q标{。TLDq描qC标签所允许的属性(参见清单4Q。这个TLD定义了一个名为css的标{,该标{定到CSSTagcR它q声明了链接和值标{ֱ性?/P>

清单4. TLD是一个通过标{֐与相应的Javacȝ兌来描q标{XML文g。TLD定义了名为css的标{,使其与CSSTagcȝ定。它q声明了链接和值标{ֱ性?/P>

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE taglib PUBLIC 
	"-//Sun Microsystems, Inc.//
	DTD JSP Tag Library  1.2//EN" 
	"http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
	<tlib-version>1.0</tlib-version>
	<jsp-version>1.2</jsp-version>
	<short-name>custom</short-name>
	<uri>http://www.ilog.com/jviews/tlds/css.tld</uri>
	<description>This tag library contains a tag for a 
		sample custom JSF Component.</description>
	<tag>
		<name>css</name>
		<tag-class>path.to.CSSTag</tag-class>
		<description>A component that displays the style 
			inline or a link a to a css file</description>
		<attribute>
			<name>id</name>
			<required>false</required>
			<rtexprvalue>false</rtexprvalue>
			<type>java.lang.String</type>
			<description>The id of this component.
			</description>
		</attribute>
		<attribute>
			<name>binding</name>
			<required>false</required>
			<rtexprvalue>false</rtexprvalue>
			<type>java.lang.String</type>
			<description>The value binding expression 
				linking this component to a property in a
				backing bean. If this attribute is set, the 
				tag does not create the component itself but
				retrieves it from the bean property. This
				attribute must be a value binding.
			</description>
		</attribute>
		<attribute>
			<name>value</name>
			<required>true</required>
			<rtexprvalue>false</rtexprvalue>
			<type>java.lang.String</type>
			<description>The inline css text or the url to 
				the css file to link.</description>
		</attribute>
		<attribute>
			<name>link</name>
			<required>false</required>
			<rtexprvalue>false</rtexprvalue>
			<type>java.lang.String</type>
			<description>Whether the value is a link or 
				the inline style.</description>
		</attribute>
	</tag>
</taglib>

~写JSF配置文g?/STRONG>Z某个JSFlg集成到框架中Q您必须提供一个名为faces-config.xml的配|文件。该文g组件类型和呈现E序cdQ用于JSP定制标签处理E序Q与对应的Javacd联v来。它q能描述与每个组件一同用的呈现E序Q参见清?Q。该文g定义了faces.CSSFamilylg家族。在本例中,该家族由faces.CSSComponentq一个组件类型(该类型与CSSComponentcȝ定)l成。最后,HTML.LinkOrInlineRenderercd的呈现程序(由CSSComponentcd玎ͼ要与faces.CSSFamily家族相关联?/P>清单5. 该文件将lgcd和呈现程序类型与对应的Javac联pv来,q描qC每个lg一同用的呈现E序。它q定义了faces.CSSFamilylg家族?

<!DOCTYPE faces-config PUBLIC 
	"-//Sun Microsystems, Inc.//
	DTD JavaServer Faces Config 1.0//EN" 
	"http://java.sun.com/dtd/web-facesconfig_1_0.dtd">
<faces-config>
	<component>
		<component-type>faces.CSSComponent
		</component-type>
		<component-class>path.to.CSSComponent
		</component-class>
		<component-extension>
			<component-family>faces.CSSFamily
			</component-family>
			<renderer-type>HTML.LinkOrInlineRenderer
			</renderer-type>
		</component-extension>
	</component>
	<render-kit>
		<renderer>
			<component-family>faces.CSSFamily
			</component-family>
			<renderer-type> HTML.LinkOrInlineRenderer 
			</renderer-type>
			<renderer-class>path.to.CSSRenderer
			</renderer-class>
		</renderer>
	/render-kit>
</faces-config>

开始制?

  如果您希望将自己的组仉成到JSF-enabled IDE中,您还可以提供补充说明。比如说Q除提供其他的设计时信息外,q可以提供一个名为sun-faces-config.xml的XML配置文gQ用于描q应在IDE中公开的组件属性?

  既然已经看到如何创徏一个简单的JSFlgQ不妨再来看看怎样创徏一个图形JSFlg。我们将遵@同样的基本步骤来设计一个高UJSF囑Şlg。让我们以一个图形组Ӟ如ILOG JSF囑ŞlgQؓ例,通过一l分c,该组件ؓ数据值分布提供了可视化表C。该囑Ş能够以条型统计图、圆形分格统计图和气泡式l计囄各种昄Ҏ来显C数据集合。该JSF囑Şlg有两个初始设计限Ӟ

  我们已经拥有Java囑ŞbeanlgQ它具备所有图形显C力。该lg可以昄很多囑ŞQ而且可定制性很高。在理想情况下,我们希望利用beanlgQ用它的功能来构成我们的JSFlg的基?

  普通JSF应用E序需要重新蝲入整个页面以更新视图。这U方法适合Z表单的应用程序,但在很多情况下却不适用于高度图形化的用L面。因此,我们的JSF囑Şlg必须能在不更新整个页面的前提下处理某些简单的DQ以提供更好的用户体验?

  以下是满些需求的解决ҎQ该JSF囑Şlg管理图形beanlgQ包括创建图形bean、定制该bean以及使该bean可用于服务器端操作。呈现JSFlg分Z个阶D完成。JSF呈现E序会生一?lt;img>标签和一套JavaScript对象Q参见图2Q。客L请求服务器发回一张图像。这一h由某个servlet完成Q该servlet获得囑ŞbeanQƈ利用囑Ş提供的方法生成一q图像(参见?Q。Q何只改变该图形外观的q一步用户交互(攑֤、扫视、更Ҏ式表{)都会引v囑Ş的一ơ增量更新。如果客L不只是要求对囑Ş囑փq行更新Q那么将提交该页面(参见?Q?/P>

  ?JSF囑Şlg理囑ŞbeanlgQ包括创建图形bean、对其进行定Ӟq其可用于服务器端动作。JSF呈现E序生成一?lt;img>标签和一套JavaScript对象?/P>

  ? 客户机通过servlet要求服务器获得一张图像。该servlet获得囑ŞbeanQƈ通过由图形提供的Ҏ生成一q图像?

  ?如果客户端不只是要求对图形外观的q行更新Q那么页面将被提交?/P>

  JSF囑Şlgq配有一套附加的JSFlg?EM>overview可显C囑Ş整体视图Q显CZ个代表图形视囄长方形,q应允许用户扫描可视区域?EM>legendlg可显C数据集合的相关信息Q还能自行在囑Ş中显C,依被昄数据的样式而定。也能提供客L?EM>interactors如扫描和攑֤Q这些功能可看成是客L交互Q表CZ囑Ş的交互不会像一ơ正常的JSF交互那样重新载入整个面?/P>

  要想呈现囑ŞlgQ只需使用chartView标签Q?

<jvcf:chartView id="c" style="width:500px;height:300px" ?/>

  该数据在HTML面中作为图像显C。该囑փ由servlet创徏Q旨在响应一ơHTTPhQ该h包括指定l果囑փ、生成图像映以及生成内联式图例{各U参敎ͼ。结果图像随之被嵌入客户端DOMQ页面中只有囑փ自nq一部分被更新?/P>

应用E序核心部g

  让我们看看简单的定制JSFlg和高U图形组件之间的一些区别。JSF囑Şlgcd像一个标准组Ӟ不过是多了一个可讉K囑ŞbeanQ该囑Şbean负责生成在HTML面中显C的囑փQ的囑Ş属性。JSFlg可以通过某个l定值或在当前会话中对这个图形beanq行局部检索。当JSF囑Şlg成ؓ某个应用E序的核心部件时Q可选的JSFlgQ如概览或图例)便与d形相兌Q来昄附加信息Q见清单6Q?/P>

清单6. 当JSF囑Şlg成ؓ某个应用E序的核心部件时Q可选的JSFlg便与d形相兌Q来昄附加信息?/P>

<jvcf:chartZoomInteractor id="chartZoomInteractor" 
	XZoomAllowed="true" 
	YZoomAllowed="true" /> 
<jvcf:chartView id="chartView" 
						 chart="#{myBean.chart}" 
					 servlet="demo.ImageMapServlet" 
			interactorId="chartZoomInteractor" 
						 width="500" 
						height="300" 
			 styleSheets="/data/line.css" 
			waitingImage="data/images/wait.gif" 
			 imageFormat="PNG"  />
<jvcf:chartOverview id="chartOverview" 
					style="height:100;width:150px" 
						viewId="chartView" 
						lineWidth="3" 
						lineColor="red" />

<jvcf:chartLegend id="legendView" 
							viewId="chartView" 
							 width="400" 
							height="180" 
							layout="vertical" 
				waitingImage="data/images/wait.gif" />

  呈现E序是实现这个JSF的一大难炏V如前所qͼ呈现E序q不生成单的HTMLQ而是生成由HTMLQ?lt;IMG> tagQ和JavaScript proxyQ代理程序)l成的动态HTMLQDHTMLQ?/P>

  Proxy是一个负责管理客hlg囑փ昄的JavaScriptcd例。该对象是服务器端Javalgcd客户端显C;它与lgcd有相同属性。页面上的每个组件、图形及光仉有一个proxy实例。呈现JavaScriptӞ在每个可视的JavaScript变量上用facesContext.getExternalContext().encodeNamespace(name)Ҏ是个很好的实c这样做在今后方便地组仉成到到JSR 168-compliant端口环境中?/P>

  ZD例说明客h上的proxyQ必d面上导入JavaScript支持库。ؓ保持客户端尽量瘦Q需要基于JavaScript库支持的proxyc,对JavaScript库进行模块化。因此,需要给每个proxyc输入一套不同的、有可能重叠的库。图形呈现的困难部分Q出现在发送这些script库的阶段。每个组件的呈现E序都要声明自己需要哪个库Q以及什么时候发送引用的库,必须认清已发送的库,以避免重复。仅在页面呈现期间的存在script理器负责这筛选工作。每当呈现程序想要发送整套库输入Ӟ它都会向{选出已发送库的script理器提供列表?/P>

  客户端proxy的目的在于允许编写脚本,q免不必要的页面更新。一旦呈C囑ŞQ在客户端便可用proxyQ以便动态安装interactorQƈ昄或隐藏图像映。Proxy对象也可供支持JavaScript鼠标事g处理的常规JSFlg使用?

<jvcf:chartView id=
	"chartView" .. />
<h:selectBooleanCheckbox id=
	"genImageMap" onclick=
	"chartView.setGenerateImageMap(
	this.checked ? true : false, 
 true);" />

  对组件客Lproxyq行局部修改的问题在于Q其状态不再与服务器上的Javalg的状态同步。ؓ解决q个问题Qproxy使用一个隐藏的输入标签Q?lt;INPUT TYPE="HIDDEN">Q来保存客户Z的新状态。当执行某个标准JSF动作q提交页面时Q呈现程序将解析该隐藏状态,使客h与服务器同步。这U行为需要呈现程序类中有专门的破解行为。标准破解方法得以改q,以便解析来自客户机的状态,q更新服务器端组件的状态?/P>

试实例

  囑Ş及其相关lg之间的关联由标记引用与绑定来完成。ؓ佉K面设计具有灵zL,一个组件可以在呈现之前被引用。因此,在呈现时间内Q如果某个组件属性引用另一个尚未呈现的lgQ那么,gq发送依赖于客户行解决的JavaScript代码Q直到呈现已引用的组件。此工作可由依赖性管理器完成?/P>

实这一点,不妨看一个典型实例,该实例涉及某个概览,该概览引用一张图形?/P>

<jvcf:overview viewId=
	"chart" [...] /> 
<jvcf:chartView id=
	"chart" [....] />

  存在两种情况。被引用囑Şlg已经呈现Q因此不存在M问题

JSP:
<jvcf:chartView id=
	"chart" [....] />
<jvcf:overview viewId=
	"chart" id="overview" [...] /> 

render:
[...]
var chart = 
	new IlvChartViewProxy ( .. );
[...]

var overview= 
	new IlvFacesOverviewProxy (
	 .. );
overview.setView(chart);
[...]

  已引用图形的lg在依赖的概览lg之前不会呈现。既然如此,可在依赖性管理器上注册一个组件创建监视器。已引用囑Şlg最l呈现时Q其呈现E序会通知自己创徏的依赖性管理器。此Ӟ发送解决依赖性所需的代码:

JSP:
<jvf:overview viewId=
	"chart" id="overview" [...] /> 
<jvdf:chartView id=
	"chart" [....] />

render:
[...]
var overview = 
	new IlvFacesOverviewProxy (
	 .. );
[...]

var chart = 
	new IlvChartViewProxy ( .. );
overview.setView(chart);
[...]

  开发JSFlg的目的之一Q是能够它们应用于M与JSF兼容的IDE。尽如此,JSF兼容性ƈ不以保证这U设计时集成会有效。下面是在开发JSFlgq程中,Z便于在今后与IDE集成需要注意的一些简单思想Q?/P>

  首先Q定制JSFlg应该提供一个基本HTML呈现E序。在设计ӞJSF IDE不能呈现h有效数据或app服务器连接的动态图形组件。因此,带有复杂的或非传l的Q比如不是HTMLQ呈现程序的lgQ应该用Beans.isDesignTime()来确定是提供一个基本HTML表示法,q是提供真正的组件呈现程序?/P>

  另一个设计时问题是组件的位置和大。不同IDE使用不同的标志和属性。能够调整大的lgQ如一q图像)应能处理定义大小的不同方式?/P>

  最后,Z与IDE集成Q组件必L供尚未被JSF说明定义的补充信息。遗憄是,当前每个IDE都需要特D处理程序来集成lgQ即Q在一U情况就需要XML文gQ而在另一U情况下需要eclipse插gQ如此等{。下一个JSF JSRQ?.0版)的主要目的之一Q将是指定附加的元数据格式?/P>

  如您所见,~写一个简单的JSFlgq不难,因ؓ框架已经完成了大部分工作。JSF框架理着lg状态、呈现程序等{。在本文中,我们已经扩展了这些基本概念,来设计一个能够显C复杂元数据、提供增量更新、支持大量客L交互q与配套lg协作的高U图形JSFlg。支持这些特炚w要对基本JSFlg的结构进行许多改q。当Ӟ增量更新的概念今后对JSF框架是一个很好的完善Q因为它只允许呈现页面已改变的部分,避免了更新整个页面。按照JSF说明书工作往往不以确保组件完全集成到JSF IDE中;一个新JSR应能及时解决q些N。尽存在缺PJSF框架仍能极大地加快Weblg开发速度、方便的融合来自各种资源的组Ӟ以创建完整的、复杂的Web应用E序?/P>

参考资?/STRONG>

作者简?/STRONG>

  Marc Durocher是ILOG的一名Y件架构师QILOG是企业软glg和服务的主要提供商。Marc Durocher在ILOG负责开发ILOG JViews生U上的JSFlg。可以通过mdurocher@ilog.fr联系Marc?/P>

原文出处

http://www.ftponline.com/weblogicpro/2005_03/magazine/features/mdurocher/



Victor 2005-11-29 18:11 发表评论
]]>
用Spring改进J2EE~程http://www.tkk7.com/Victor/articles/18071.htmlVictorVictorFri, 04 Nov 2005 00:45:00 GMThttp://www.tkk7.com/Victor/articles/18071.htmlhttp://www.tkk7.com/Victor/comments/18071.htmlhttp://www.tkk7.com/Victor/articles/18071.html#Feedback0http://www.tkk7.com/Victor/comments/commentRss/18071.htmlhttp://www.tkk7.com/Victor/services/trackbacks/18071.html作者:Peter Braswell
本文关键字:spring

?. 接口可插?/P>

  q个例子表CZ个温度测量系l。几个传感器对象属于不同的类型,但都实现了ProtocolAdapterIfc接口Q因此在它们插入TemperatureSensor对象Ӟ它们是可互换的。在需要TemperatureSensorӞpȝ中的某个实体必须知道要生成ƈ与该传感器对象关联的ProtocolAdapterIfc的具体类型。在本例中,该传感器可基于命令行参数、数据库中的行或通过属性文件进行配|。本例还不以造成挑战或展CZ个复杂框Ӟ但它以阐明IoC基础?/P>

  但是Q想象一下:在一个相当复杂的应用E序中这U情况屡屡发生,而您q希望能动态地——至要在外部——改变对象关联。假设有一个DummyProtocolAdapterQ它Lq回42q个|使用它来q行试。ؓ什么不提供一个单个的l一框架Q——让开发h员能够依靠该框架Q以一U一致的、外部配|的方式建立cM间的兌Qƈ且不引v工厂单元素类(factory singleton classe)的异常增加。这听v来可能没什么大不了Q但它要依赖于IoC的简单性?/P>  我们使用一个TemperatureSensorc,它与一个实现ProtocolAdapterIfc接口的类有关联。TemperatureSensor用该委托cL获得温度倹{如UML图所C,在实现ProtocolAdapterIfcq且随后可用于该兌的应用程序中有若q个cR我们将使用IoC框架Q在本例中是SpringQ来声明要用的ProtocolAdaperIfc的实现。Spring在q行时徏立关联。我们先来看XML代码Q它实例化TemperatureSensor对象q将一个ProtocolAdapterIfc实现与它兌h。该代码如下所C:

<bean id="tempSensor"
class="yourco.project.sensor.TemperatureSensor">
  <property name="sensorDelegate">
    <ref bean="sensor"/>
  </property>
</bean>

<!-- Sensor to associate with tempSensor -->
<bean id="sensor" class="yourco.project.comm.RS232Adapter"/>

  看了q些代码之后Q对于其目的应该非常清楚了。我们配|Spring来实例化TemperatureSensor对象Qƈ其与RS232Adapter相关联,作ؓ实现ProtocolAdapterIfc接口的类。若x变已l与TemperatureSensor兌的实玎ͼ惟一需要更改的是sensor bean标记中的class倹{只要实CProtocolAdapterIfc接口QTemperatureSensor׃再关心关联了什么?/P>

  这应用于应用程序相当简单。我们必d接入Spring框架Q将它指向正的配置文gQ然后根据名U向Spring索取tempSensor对象的实例。下面是相应的代码:

ClassPathXmlApplicationContext appContext =
  new ClassPathXmlApplicationContext(
  new String[]
  { "simpleSensor.xml" });
BeanFactory bf = (BeanFactory) appContext;
TemperatureSensor ts = (TemperatureSensor)
  bf.getBean("tempSensor");
System.out.println("The temp is: "+
  ts.getTemperature());

  可以看出Q这些代码ƈ不是非常难。首先是启动Springq指定要使用的配|文件。接下来Ҏ名称QtempSensorQ引用Bean。Spring使用q样一U机ӞZsimpleSensor.xml文g的描q创对象q与其他对象兌。它用于注入依赖性——在本例中,通过它作ؓ一个参C递给sensorDelegate()Ҏ而实例化RS232Adapter对象q将其与TemperatureSensor对象兌?/P>

  比较hQ用编E式Java完成q一d也不是很难。如下所C:

TemperatureSensor ts2 = new TemperatureSensor();
ts2.setSensorDelegate(new RS232Adapter());

  UaM者或怼认ؓ实际上这是更好的Ҏ。代码行数少Qƈ且可L可能更强。确实如此,但这U方法的灉|性要得多?/P>

  • 可以随意换入和换Z同层中不同对象的不同实现。例如,若Web层中的组仉要来自新业务对象的额外的功能Q您只需该业务对象与Web层对象相兌Q就像上面TemperatureSensor例子中的做法。它被“注入”到Web对象中以随时使用?
  • 能够重新配置整个应用E序的结构,意味着可以L更改数据源。比如说Q或者ؓ不同的部|场景创Z同的配置文gQ或者ؓ试场景创徏更有用的、不同的配置文g。在试场景中可能会注入实现接口的模拟对象,而不注入真正的对象。稍后我们将介绍一个这L例子?

  上面所q的例子可能是依赖注入的最单Ş式。利用相同的{略Q我们不仅能够关联不同的c,q能够在cM安装属性。诸如字W串、整数或点Ccȝ属性,只要hJavaBean样式的存取器Q就可以通过Spring配置文g它们注入类中。我们还可以通过构造函数来创徏对象和安装属性或bean引用。其语法只比通过属性进行设|稍E复杂一些?/P>

  所有这一切都是利用一U灵zȝ声明性配|完成的。无需更改代码Q徏立依赖关联的所有艰难Q务都由Spring来完成?/P>

Spring--标准化的定位器模?/STRONG>

  我一直将服务定位器模式视作良好的J2EE规范的主要组成部分。对于不熟悉q一术语的h来说Q可以这L解它Q我们一般认为典型的J2EE应用E序pq层l成。通常有Web层、服务层QEJB、JMS、WS、WLS控gQ以及数据库。一般来_完成某一h所需的“查䏀服务中都包含了一些方法。Service LocatorQ服务定位器Q模式认为,这些方法包装在某种隐藏了生成或查找l定服务的复杂性的工厂cM是一个好L。这减少了JNDI或只会造成Web层操作类混ؕ的其他服务品代码的增加。在Spring出现以前Q这通常是由l过考验证明可靠?tried-and-true)SingletoncL实现的。Singleton/Locator/Factory模式可以描绘为:


?. 定位器模式的序?/P>

  q是Ҏ布在整个Web控制器代码中的增加的JNDI查找代码的一个巨大改q。它被y妙地隐藏在工厂内部的协作cM。我们可以用Spring来改q这一术语。此外,该解x案将适用于EJB、Web services、异步JMS调用Q甚臌有基于WLS控g的服务。由Spring实现的这U定位器模式的变体考虑了业务服务之间的一些抽象化和同质性。换句话_Web控制器的开发h员真的可以不考虑他们所使用的服务的U类Q一个类g“WLS控g”但是更通用的概c?/P>

  IoC框架大大改进了这U模式的效用Q而且实际上废除了复杂而特D的singleton代码来实现它。通过借用上例中引入的概念Q我们实际上无需额外代码便能构徏一个非常强大且无处不在的Service Locator模式。ؓ此,在一开始有一个简单的要求Q即Web操作的开发h员应专门处理实现接口的那些事情。这基本上已l通过EJB~程实现Q但q不是说Web操作的开发h员处理的服务必须通过EJB来实现。它们可能只是普通Java对象或Web services。要Ҏ应当通过接口Q这样实现能够换入换出)来编写服务程序,q且q行旉|能够由Spring处理?/P>

  Spring之所以非帔R合于Service Locator模式Q是因ؓ它或多或能够统一地处理不同类型的对象。通过许的规划和大量使用IoCQ我们多都能够以一U通用方式来处理大多数对象Q而不用管它们的特性(EJB、POJO{等Q如何,q且不会引vSingleton工厂cȝ增加。这使Web层编E变得更加轻村֒灉|?/P>  我们先来看一个关于这U模式如何应用于EJB的例子。我们都知道使用EJB可能是最复杂的方法,因ؓ要将一个活动的引用引入EJB要做很多工作。若使用SpringQ徏议用EJB接口扩展非特定于EJB的业务接口。这样做有两个目的:保持两个接口自动同步Q以及帮助保证业务服务对非EJB实现是可交换的,以便q行试或清?stubbing)。我们可以利用Spring固有的实用工h定位和创建EJB实例Q同时ؓ我们处理所有难以处理的工作。相应代码如下所C:

<bean id="myBizServiceRef"
         class="org.springframework.ejb.access.
         LocalStatelessSessionProxyFactoryBean">
  <property name="jndiName">
    <value>myBizComponent</value>
  </property>
  <property name="businessInterface">
    <value>
      yourco.project.biz.MyBizInterface
    </value>
  </property>
</bean>

  接下来可以检索beanq开始用它Q方法如下:

  MyBizInterface myService = bf.getBean("myBizServiceRef");

  q将q回Spring动态创建ƈ包装了底层目标(在本例中是一个本地EJB实例Q的一个对象。这U方法非常好Q因为它完全隐藏了我们在处理EJBq一事实。我们将与一个实现简单业务接口的代理对象交互。Spring已经Z“真正的”业务对象考虑周到地动态生成了该对象。所包装的对象当然就是Spring定位和检索引用所要获得的本地EJB。此外,您还会注意到Q这U代码Ş式与前面用于索tempSensor对象的代码完全相同?/P>

  那么如果我们改变LQ想用普通Java对象来实C务组Ӟ或者可能在试中,我们想用一个返回“固?canned)”响应的已清?stubbed)对象来替换重量EJBQ该怎么做呢Q利用IoC和SpringQ通过更改Spring上下文文件就可轻而易丑֜实现q些目标。我们只需使用更常规一点的东西Q如我们在第一个Spring例子中所看到的)来替换EJB代理的连接即可:

<bean id="myBizServiceRef"

class="yourco.project.biz.MyStubbedBizService">

</bean>

  h意,我只更改了Spring框架所q回的内容的l节Q没有更改bean id。最后的l果是业务对象的解决Ҏ未变Q它看上d以前完全一P

MyBizInterface myService =

bf.getBean("myBizServiceRef");

  最大的区别昄是实现该业务接口的对象现在由一个普通Java对象QPOJOQ支持,q且只是该接口的一个已清除(stubbed)版本。这l单元测试或改变业务服务的特性带来了极大方便Q而对客户端代码的影响很小?/P>

使用Spring来标准化异常

Spring的一大A献是“模板化”代码块。这在纯JDBC~程中表现得最为明显。我们都曑ֆq具有下q功能的代码Q?/P>

  • 创徏一个数据库q接Q可以的话从某个池创建?
  • 构造一个查询字W串q提交?
  • q代l果q将数据送到域对象中?
  • 处理不同阶段出现的大量异常?
  • 保记得~写finally代码块以关闭q接?

  但是各处的这U代码往往都会或多或少地有点“样板化”。一般来说这是有害的Q不仅因Z需要的代码会增加,q因为有些东西可能会遗漏Q如非常重要的关闭连接,如果没有实现它,可能D数据资源池的泄漏?/P>

  虽然我敢肯定我们都曾多次写过q类“样李쀝代码,但是SpringҎ和直接的JDBC实现对照来看Q其l果会有趣而又Ҏ鲜明。“传l”的JDBC实现可能如下Q?/P>

Connection con = null;
try
{
  String url = "jdbc://blah.blah.blah;";
  con = myDataSource().getConnection();
  Statement stmt = con.createStatement();
  String query = "SELECT TYPE FROM SENSORS";
  ResultSet rs = stmt.executeQuery(query);
  while(rs.next()){
    String s = rs.getString("TYPE);
    logger.debug(s + "   " + n);
  }
} catch(SQLException ex)
{
  logger.error("SQL ERROR!",ex);
}
finally
{
  con.close();
}

  对于该方法要做一些说明。首先,它是有效的!该代码绝对不会出CQ何错误。它会连接到数据库,q从‘SENSOR’表获取所需的数据。该Ҏ的基本问题源于缺乏抽象化。在大型应用E序中,必须反复剪切和粘贴这D代码,或者至会出现cM的其他情c较大的问题在于它依赖于~程人员d“该做的事”。我们都知道Q不数据库操作的结果是什么,都必ȝfinally语句来关闭该数据库。有时我们忘记做该做的事。我和所有h一h到内疚!

  ~写一个小框架来解册一问题会非常容易。我怿大家也都曾这样做q,但ؓ何不让Spring帮我们处理这一问题呢?我不敢保证我能想Z个更整洁、更优雅的解x案。我们来看Spring框架是如何处理这U样板JDBC场景的?/P>

  Spring支持各种各样的JDBC、Hibernate、JDO和iBatis模板。模杉K用样板概念,q将它{换ؓ合法的编E术语。例如,下面的代码片断封装了上面列出的各个步骤:

DataSource ds = (DataSource) bf.getBean("myDataSource");
JdbcTemplate temp = new JdbcTemplate(ds);
List sensorList = temp.query("select sensor.type FROM sensors",
  new RowMapper() {
     public Object mapRow(ResultSet rs, int rowNum) throws SQLException;
        return rs.getString(1);
     }
  });

  q段短的代码消除了JDBC~程的冗长,代表了前面提及的h的思\。请注意我们使用了Spring的IoC来查找该查询的数据源。Springq支持对已检查异怋用未查异常;因此许多已检查的JDBC异常会重新映到通常更有用而且更友好的未检查异常层ơ结构中。在Spring的上下文文g中配|该数据源类g下面代码Q?/P>

<bean id="myDataSource"
  class="org.apache.commons.dbcp.BasicDataSource">
  <property name="driverClassName">
    <value>org.gjt.mm.mysql.Driver</value>
  </property>
  <property name="url">
    <value>jdbc:mysql://romulus/sensors</value>
  </property>
  <property name="username">
    <value>heater</value>
  </property>
  <property name="password">
    <value>hotshot</value>
  </property>
</bean>

  在本例中Q我们利用Apache commons工具配|了一个基本数据源。但q不是说我们只能使用它。我们可以改变配|,使用在JNDI中配|ƈ装蝲的数据源。Spring提供了一些实用工具和IoC功能以配|和q回存储在JNDI中的对象。例如,若想配置q用与JNDI上下文关联的数据源,则可以输入以下代码,替换先前的数据源配置Q?/P>

<bean id="myDataSource"
  class="org.springframework.jndi.
  JndiObjectFactoryBean">
  <property name="tempSensorDS">
    <value>ConnectionFactory</value>
  </property>
</bean>

  该代码突ZSpring所提供的关于表格的试灉|性。该代码可以在“容器内”运行(从JNDI查找数据源)Q经q细微的改动之后也可在“容器外”运行?/P>

  虽然模板化机制仅适用于某些特定场合,但我们可以泛化这一概念Q将它应用于更广泛的场合。例如,一U将已检查异常{变ؓ未检查异常、此外还可能为异常处理提供一些中间或l一的异常处理策略的机制会很有帮助。我们还可以使用该机制将底层的基本异常“Y化”得更合乎h意。我们可以将PacketFrameParityFaultException软化为CommunicationsUnreliableException。这个重新映的较Y的异常表C情况可能ƈ不那么严重,重新h也是可以的?/P>

  Spring已经具备了一U类g包装EJB调用Q在最后一节介l)的机Ӟ但遗憄是它q不具备M“通用”的东西Q至在异常软化意义上是q样的。但Spring的确有一个非常健壮的AOPQ面向方面编E)框架Q我们可以用它来Dq种行ؓ。下面是一个关于这UY化适用领域的(公认的)_ֿ设计的例子?/P>

  我们再来看看本文前面已经开始探讨的一些概c在W一节中我们介绍了一个基于远E传感器的小应用E序。现在我们l探讨这个例子。我们将从一个简单的传感器接口开始介l,该接口代码如下:

public interface ProtocolAdapterIfc
{
  public Integer getRemoteSensorValue()
    throws CommChecksumFault,
           CommConnectFailure,
           CommPacketSequenceFault;
}

  qƈ没有什么特别之处。显然实现该接口的Q何h都会获得一个远E值ƈ它q回l调用者。在此期间调用者可能要面对某些可怕的NQ该接口可能抛出的已查异常就是例证?/P>

  接下来我们来看该接口的一个实现程序。实现ProtocolAdapter的类是CarrierPigeonQ其代码cM于:

public class CarrierPigeon
      implements ProtocolAdapterIfc
{
  private boolean isTired = true;
  private boolean canFlapWings = false;

  public Integer getRemoteSensorValue()
    throws CommChecksumFault,
           CommConnectFailure,
           CommPacketSequenceFault
    {
        if(isTired && !canFlapWings )
        {
          throw new
               CommConnectFailure("I'm Tired!");
        }
        return new Integer(42);
    }
}


  为简zv见,q里省略了属性的getter和setterҎ。当调用getRemoteSensorValue()ӞCarrierPigeonҎ检查它是否使用q度以及它还能否执行。如果它是使用q度q且不能执行Q我们将无法获得M|必须抛出CommConnectionFailureQ它是一个已查异常。到现在为止Q一直都q不错。但别高兴得太早了!我已l决定不让应用程序编Eh员应对“疲劳的信鸽”。可以将它包装在某种对象中,q在辑ֈ目的之前捕获异常Q但必须处理20U不同的传感器。此外,我更喜欢对这些东西进行适度透明地处理,随着对该pȝ了解的增多,或许q会更改异常处理E序中的逻辑。我惌的是一个兼容APIQ应用程序编Eh员能够用它Qƈ且随着旉的推U能够改变或增强。我x板化异常处理Qƈ让应用程序编Eh员能够处理Y的未查异常,而不是硬异常。Spring非常W合q种情况。下面是为此而用的{略的要点:

  • 定义一个较软的消除已检查异常的最接口。这是应用~程人员用的接口?
  • 使用Spring AOPl构Q开发一个客h调用和目标对象调用之间的拦截器,在本例中是“信鸽”?
  • 使用Spring安装该拦截器q运行?

  首先来看q个较Y的接口:

public interface SensorIfc
{
  public Integer getSensorValue();
}

  h意,在限定范围内可以重命名方法,使其更有意义。还可以消除已检查异常,像q里所做的一栗接下来Q也可能会更有趣的是我们惌让Spring其注入调用栈的拦截器:

import org.aopalliance.intercept.MethodInterceptor;

public class SensorInvocationInterceptor
  implements MethodInterceptor
{
  public Object invoke(MethodInvocation
     invocationTarget) throws Throwable
  {
    // Return object reference
    Object o = null;

    // Convert it to the protocol interface type
    ProtocolAdapterIfc pai =
      (ProtocolAdapterIfc) invocationTarget.getThis();

    try
    {
      o = pai.getRemoteSensorValue();
    }
    catch (CommChecksumFault csf)
    {
      throw new SoftenedProtocolException(
       "protocol error [checksum error]: "
        + csf.getMessage());
    }
    catch (CommConnectFailure cf)
    {
      throw new SoftenedProtocolException(
        "protocol error [comm failure]: "
        + cf.getMessage());
    }
    catch (CommPacketSequenceFault psf)
    {
      throw new SoftenedProtocolException(
        "protocol error [message sequence error]"
        + psf.getMessage());
    }

    return o;
  }
}

  通过实现Spring MethodInterceptor接口q将该类插入SpringpȝQ稍后我们将q行讨论Q,我们通过MethodInvocation参数获得实际的目标方法。这样就能提取我们想要的真正对象。请CQ我们更改了被调用者看作SensorIfc的接口,该接口目标对象得以实现ProtocolAdapterIfc。我们这样做是ؓ了简化调用,更是Z消除所有的已检查异常。该拦截器只调用用来捕获可能抛出的Q何已查异常、ƈ用SoftenedProtocolException它们重新包装的目标Ҏ。实际上Q我们只重新包装消息Q但要做到心中有数。SoftenedProtocolException扩展了RuntimeExceptionQ后者无疑是一个未查异常。该操作的最l结果是应用E序开发h员不必处理Q何已查异常。如果他们真惛_理异常,他们只需Q非强制圎ͼ处理一个:SoftenedProtocolException。不错吧Q?/P>

  那么Q如何才能让Spring完成q一切呢Q答案就是要有正的配置文g。我们现在就来看一看该配置文gQƈ了解其工作原理:

<!-- TARGET OBJECT -->
<bean id="protocolAdapter"
  class="yourco.project.comm.CarrierPigeon">
  <property name="isTired">
    <value>true</value>
  </property&gl;
  <property name="canFlapWings">
    <value>true</value>
  </property&gl;
</bean&gl;

<!-- INTERCEPTOR -->
<bean id="sensorInterceptor"
  class="yourco.project.springsupport.
  SensorInvocationInterceptor"/>

<!--WIRE EVERYTHING UP, HAND BACK TO THE USER-->
<bean id="temperatureSensorOne"
  class="org.springframework.aop.framework.
  ProxyFactoryBean">
  <property name="proxyInterfaces">
    <value>
yourco.project.interfaces.SensorIfc
    </value>
  </property>
  <property name="target">
    <ref local="protocolAdapter"/>
  </property>
  <property name="interceptorNames">
    <list>
      <value>sensorInterceptor</value>
    </list>
  </property>
</bean>

  我们逐节看这些代码时Q可以看到它比我们前面看到的Spring配置文g要稍微复杂一些。“TARGET OBJECT”注释下面的W一节指定了要作用的目标对象的类。还记得拦截器曾一个参C入其中来表示目标对象吗?q就是其工作原理。基于该参数QSpring现在知道要将哪个对象传递进来。“INTERCEPTOR”下面的代码节是在目标方法之前调用的cR此Ӟ如果目标cL出已查异常,要开始处理异常,q进行Y化。Spring它与目标类相关联。最后一节是各U元素联pv来。用戯h的bean位于bean键temperatureSensorOne下。ProxyFactoryBean生成ƈq回一个代理类Q它用来实现yourco.project.interfaces.SensorIfc接口。目标对象当然就是protocolAdapter beanQ它由yourco.project.comm.CarrierPigeon的实例支持。只要用戯用代理的Ҏ插入ƈ调用的拦截器位于bean键sensorInterceptor下,q由yourco.project.springsupport.SensorInvocationInterceptor支持。请注意Q可使用多个拦截器,因ؓ该属性是一个列表,因此可以许多拦截器对象兌到方法调用中Q这是一个非常有用的理念?/P>

  q行该应用程序时Q根据插入的CarrierPigeon|我们可以看到一些有的行ؓ。如果我们的CarrierPigeon没有使用q度q能执行Q我们将看到q样的输出:

The sensor says the temp isQ?2

  昄“信鸽”没问题而且状况很好Qƈ计算出温度ؓ42。如果由于改变CarrierPigeon Spring节中的|而造成“信鸽”用过度或不能执行Q我们将得到如下所C的l果Q?/P>

yourco.project.exceptions.comm.SoftenedProtocolException: protocol 
 error [comm failure]: I'm Tired!
  at yourco.project.springsupport.SensorInvocationInterceptor.invoke
	(SensorInvocationInterceptor.java:57)
  at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed
    (ReflectiveMethodInvocation.java:144)
  at org.springframework.aop.framework.JdkDynamicAopProxy.invoke
	(JdkDynamicAopProxy.java:174)
  at .getSensorValue(Unknown Source)
  at yourco.project.main.Main.main(Main.java:32)

  在这U情况下Q“信鸽”或者用过度,或者不能执行,因此得到的结果是SoftProtocolExceptionQƈ附带一条消息说明发生的情况Q“I'm tired!”够酷吧Q?/P>

  我希望h们能够开始了解Spring的强大及其多U功能。Spring框架悄然兴vQƈ成ؓ开发h员的~程工具׃的真正的“瑞士军刀”。Spring在实现让开发h员能够集中于应用E序的基本组成部分这一传说般的承诺斚w做得不错Q这也正是它得以大行光的业务逻辑。Spring您从J2EE中的一些错l复杂的斚w中解攑և来。Spring的强大在于它能够使大多数东西看v来就如同非常普通的Java对象一P而不它们的性质或来源如何。既然Spring自n肩负起创建、关联和配置的重担,那么开发h员要做的只是掌握使用对象而不是构造对象的Ҏ。Spring如同在杂货店购买的预煮的饭菜。您要做的只是决定想吃什么、把它带回家、加热,然后吃!

l合说明

  Spring是一个非常健壮的轻量U框Ӟ它极好地弥补了J2EE/EJB环境的不뀂Spring真正伟大的一点在于它不走极端。您可以以一U非常简单的方式开始用SpringQ正如我所做的那样Q,只是建立常见的关联,作ؓ定位器模式的一U实现。稍后,您将发现其惊人的功能Qƈ且很快对它的要求会越来越多?/P>

  我所发现的一个惊Z处是Spring所提供的测试灵zL。现在h们对通过Junitq行单元试日益重视Q这增加了测试,而不是减测试。J2EE容器的用ɋ试极端复杂Q以至于难以q行。这U困隑ֱ面源于业务逻辑和容器框架服务之间生的耦合。借助于Spring的配|机Ӟ使实现可以动态切换,q样有助于业务对象从容器中释攑և来。我们已l看刎ͼ如果只想试WeblgQ将zd的EJB换成其代理或一个已清除的业务服务ƈ不会造成太大影响。借助于JDBC或Hibernate数据讉K对象Q我们可以用常见的单JDBC、非XA的数据源q接来测试这些组Ӟq将它们无缝地换出,代之以健壮的ZJTA、JNDI的对应连接。结论是Q如果代码易于测试,q因此测试得更多Q那么质量必然会提高?/P>

l束?/STRONG>

  本文_略地概括介l了IoCQƈ详细介绍了Spring。Spring框架h许多功能Q其中许多功能在本文中只是点Cؓ止。从基本的依赖注入到复杂的AOP操作Q这些组成了Spring的强大功能,q是它的主要优点之一。能够根据问题需要用或多或的IoC功能是一个极具吸引力的理念,我认为,q在通常错综复杂的J2EE~程领域也是颇受Ƣ迎的。现在看完了q篇文章Q我衷心地希望它能对您有所帮助Qƈ可以应用到您的工作中。欢q随时提供反馈!

参考资?/STRONG>

原文出处

http://dev2dev.bea.com/pub/a/2005/07/better_j2eeing.html

 作者简?/SPAN>

Peter Braswell是ALTERThought的首席科学家。Peter具备过15q的软g工程斚w的专业经验。他N导多个行业的前沿性Q务关键型pȝ和应用程序的实现工作Q这些行业包括高U技、金融、零售和电信{?/P>



Victor 2005-11-04 08:45 发表评论
]]>
NetUI面:Struts的发?/title><link>http://www.tkk7.com/Victor/articles/18070.html</link><dc:creator>Victor</dc:creator><author>Victor</author><pubDate>Fri, 04 Nov 2005 00:41:00 GMT</pubDate><guid>http://www.tkk7.com/Victor/articles/18070.html</guid><wfw:comment>http://www.tkk7.com/Victor/comments/18070.html</wfw:comment><comments>http://www.tkk7.com/Victor/articles/18070.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/Victor/comments/commentRss/18070.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/Victor/services/trackbacks/18070.html</trackback:ping><description><![CDATA[旉Q?005-10-18<BR>作者:<A >Srinivas Jaini</A><BR>本文关键字:<A >struts</A>, <A href="http://dev2dev.bea.com.cn/products/search.jsp?searchtype=keywords&keywords= netui"> netui</A>, <A href="http://dev2dev.bea.com.cn/products/search.jsp?searchtype=keywords&keywords= page flow"> page flow</A>, <A href="http://dev2dev.bea.com.cn/products/search.jsp?searchtype=keywords&keywords= Controls"> Controls</A>, <A href="http://dev2dev.bea.com.cn/products/search.jsp?searchtype=keywords&keywords= 控g"> 控g</A>, <A href="http://dev2dev.bea.com.cn/products/search.jsp?searchtype=keywords&keywords= 面?> 面?/A>, <A href="http://dev2dev.bea.com.cn/products/search.jsp?searchtype=keywords&keywords= MVC"> MVC<BR></A><BR>Struts是一个用来构Z业J2EE应用E序的流行框架。通过使用StrutsQJ2EE Web应用E序的开发变得更加轻松且更易于管理。Beehive是Apache软g基金会(Apache Software FoundationQASFQ的一个开源项目,它在Struts的基上构Z个简单的面(Page FlowQ模型,使Web应用E序的开发变得更加简单明了。通过使用新的 JSR-175?JSR-181元数据注释功能,Beehive减少了J2EE应用E序开发的~码量。本文将介绍Beehive面技术,以及如何使用它来提高Struts软g的生产力和质量。本文还介绍了如何将该技术迁Ud普通的Struts应用E序中去。本文假定读者对Struts有一定的了解?STRONG> </STRONG> <P><STRONG>普通的Struts </STRONG></P> <P>  Struts是Apache软g基金会的Jakarta目的一部分Q是一个基于模型-视图Q控制器QModel-View-ControllerQMVCQ设计模式构建Web应用E序的开源框架。Struts使用动作(action)cL构徏框架的控制器lg。典型的Struts应用E序要求使用大量动作cL处理某一程(process flowQ的几个动作Qƈ要求使用一个XML配置文g来声明蟩转。因此,像数据管理和l护之类的问题已l成为现有Struts应用E序的一个主要关注点。图1昄了一个示例应用程序的程图,意在对Struts和页面流作一比较?/P> <P><IMG height=243 src="http://dev2dev.bea.com.cn/images/051018/0510180101.jpg" width=315> <BR></P> <P align=center>?. LoginProcessFlow应用E序的流E图</P> <P>  在Struts中开发该应用E序的时候,通常要求针对每个动作Qbegin、signup、login和logoutQ用动作类。清?昄了一个典型的动作c,在本例中是图中描l的begin动作?</P> <P>清单1. BeginAction.java </P><PRE class=code>package comparision.struts; import org.apache.struts.action.Action; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; import org.apache.struts.action.ActionForm; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public final class BeginAction extends Action { public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { return (mapping.findForward("success")); } } </PRE> <P>  为完成该Struts应用E序Q需要针对signup、login和logout使用cM的动作类。HTTP会话被用来跨动作传递数据,q得管理和l护数据变得很困难。Struts使用一个配|文Ӟstruts-config.xmlQ来声明到JSP面的蟩转。因此,要更改业务规则通常q需要更改动作类和配|文件。具体来_更改业务逻辑要求更改动作Q而更改业务流E则要求更改配置文gQ通常二者都需要更改)。清?昄的是LoginProcessFlow应用E序的典型的struts-config.xml文g?/P> <P>清单2.struts-config.xml<BR></P><PRE class=code><!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd" > <struts-config> <!-- Form Bean Definitions --> <form-beans> <form-bean name="UserForm" type="Samples.Struts.UserForm"/> </form-beans> <!-- Action Mapping Definitions --> <action-mappings> <action path="/Startup" type="Samples.Struts.begin" scope="request"> <forward name="login" path="/index.jsp"/> </action> <action path="/LoginForm" forward="/mypage.jsp"/> <action path="/login" type="Samples.Struts.Login" name="LoginForm" scope="request" validate="false" > <forward name="success" path="/mypage.jsp"/> </action> </action-mappings> <!-- message resources --> <message-resources parameter="ApplicationResources" null="false" /> </struts-config><BR> </PRE><STRUTS-CONFIG> <P><STRONG>面?/STRONG></P> <P>  了解开发Struts应用E序的复杂性后Q让我们来深入研I面流及其提供的功能。Beehive的NetUI面框架得开发和l护工作更加L和易于管理。清?是一个页面流控制器(通常写ؓJava面?Java Page FlowQJPF)Q?用于前面所q的LoginProcessFlow应用E序。页面流框架使用一个JPF文g来代替所有的动作cd配置文g?/P> <P>清单3. LoginPageFlowController.java</P><PRE class=code>package comparison.pageflows; import org.apache.beehive.netui.pageflow.PageFlowController; import org.apache.beehive.netui.pageflow.Forward; import org.apache.beehive.netui.pageflow.annotations.Jpf; public class LoginPageFlowController extends PageFlowController { @Jpf.Action( forwards = { @Jpf.Forward(name = "success", path = "/index.jsp") } ) protected Forward begin() { //initial processing return new Forward("success"); } @Jpf.Action( forwards = { @Jpf.Forward(name = "success", path = "/mypage.jsp"), @Jpf.Forward(name = "failure", path = "/index.jsp") } ) protected Forward login() { //authentication logic if(user_authenticated) return new Forward("success"); else return new Forward("failure") } @Jpf.Action( forwards = { @Jpf.Forward(name = "success", path = "/confirm.jsp") } ) protected Forward signup() { return new Forward("success"); } @Jpf.Action( forwards = { @Jpf.Forward(name = "success", path = "/begin.do") } ) protected Forward logOut() { return new Forward("success"); } } </PRE> <P>  清单3清楚地显CZStruts和页面流所要求的开发工作量和维护Q务方面的差别?/P> <P>  如前所qͼJPF是注释驱动的Qƈ且不需要在配置文g中声明蟩转。页面流利用Beehive框架自动生成Z注释的配|文Ӟ如:JPF中声明的 @Jpf.ActionQ。清?昄了如何在一个JPF文g中处理所有的动作以及以注释的形式声明的蟩转。例如begin动作Q如果成功的话,p转到index.jsp面。您可以清楚地看到动作如何处理逻辑Q以及简单的注释如何处理跌{Q这些注释在使用前要q行声明。注释可以在动作U别声明Q也可以在页面流U别声明。在Beehive提供的众多注释中Q@Jpf.Forward、@Jpf.Controller?@Jpf.Action是最常用的。对所有注释的讨论不在本文的范围之内。要获得有关的更多信息,误?<A target=_blank>Page Flow Annotations</A>说明文档?/P> <P>  Struts应用E序要求Ҏ个动作用一个动作类Q而用页面流开发应用程序只需一个JPF文gp够了。页面流提供一U简单的、单一文g的编E模型。在构徏复杂的Web应用E序Ӟ面开发h员能够立卛_始开发,而无需l历与学习Struts有关的曲折过E。当遇到因ؓ业务规则的更改而引L修改Ӟ面显得更为灵zR?/P> <P><STRONG>会话数据理</STRONG></P> <P>  面提供的~程框架d了多处改q,刚刚是注释驱动的自动生成和同步XML配置文g的例子。接下来让我们看看页面流的另一个优点:使用面?Page Flow-scoped)变量可以更轻村֜跨多个动作管理和l护数据?/P> <P>  在Struts中,动作c获取会话对象ƈ讄属性(或赋|Q其后其他动作会对这些属性进行检索。清?昄的例子有两个Struts动作c:LoginAction和LogOutActionQ它们用HTTP会话׃n一个变量username?/P> <P>清单4. LoginAction.java和LogOutAction.java</P><PRE class=code>package comparision.struts; import org.apache.struts.action.Action; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; import org.apache.struts.action.ActionForm; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public final class LoginAction extends Action { public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { String username = form.getUserName(); // get this session HttpSession session = request.getSession(); session.setAttribute("username", username); return (mapping.findForward("login")); } } package comparision.struts; import org.apache.struts.action.Action; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; import org.apache.struts.action.ActionForm; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public final class LogOutAction extends Action { public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { // get this session HttpSession session = request.getSession(); String username = session.getAttribute("username"); //logic to log out user return (mapping.findForward("logout")); } } </PRE> <P>  上面的代码清楚地昄了LoginAction和LogOutAction昑ּ地共享username变量的方式,在本例中是用HTTP会话。跨多个动作cȝ理这些会话变量可能会相当复杂?/P> <P>  相比之下Q页面流提供一个以JPFU别声明的简单变量,它可以跨所有的面动作共享数据。在清单5的例子中Q以面别声明了一个String变量QusernameQ,q在login和logout动作中用它?/P> <P>清单5. LoginPageFlowController.java</P><PRE class=code>package comparision.pageflows; import org.apache.beehive.netui.pageflow.PageFlowController; import org.apache.beehive.netui.pageflow.Forward; import org.apache.beehive.netui.pageflow.annotations.Jpf; public class LoginPageFlowController extends PageFlowController { private String username; @Jpf.Action( forwards = { @Jpf.Forward(name = "success", path = "/index.jsp") } ) protected Forward begin() { //intial processing return new Forward("success"); } @Jpf.Action( forwards = { @Jpf.Forward(name = "success", path = "/mypage.jsp"), @Jpf.Forward(name = "failure", path = "/index.jsp") } ) protected Forward login(ProfileForm form) { username = form.getUsername(); //authentication logic if(user_authenticated) return new Forward("success"); else return new Forward("failure") } @Jpf.Action( forwards = { @Jpf.Forward(name = "success", path = "/confirm.jsp") } ) protected Forward signup() { return new Forward("success"); } @Jpf.Action( forwards = { @Jpf.Forward(name = "success", path = "/begin.do") } ) protected Forward logOut(ProfileForm form) { logout(username); return new Forward("success"); } } </PRE> <P>  如您所见,׃n变量的处理方式简单直观?/P> <P><STRONG>面的优点</STRONG></P> <P>  除了易于使用和其他优点之外,q里q列Z面的一些重要优点:</P> <UL> <LI>面是模块化的Q简单的Web应用E序可能包括几个面,每个面处理一部分逻辑相关的功能。可以用共享页面流或全局面来处理异常和未处理的动作? <LI>面是可嵌套的Q页面流嵌套提供l粒度、可重用的模块化Q以处理功能和数据? <LI>面是有状态的Q由于可以声明页面流U的变量Q所以数据管理非常容易? <LI>面是注释驱动的:最后但q最不重要的是,注释驱动的编E是一个受Ƣ迎的改变,它替代了多文件的、分散代码的~程?</LI></UL> <P>  管本文仅仅是介l一些基本知识,但是我希望读者能对这些特性如何有助于创徏可管理的大型应用E序有所了解。模块化和嵌套特性对于开发可重用的Web应用E序lg特别有用Q而诸如会话数据管理和丰富的数据绑定标{之cȝҎ则有助于简化编E?/P> <P><STRONG>集成Struts和页面流</STRONG></P> <P>  我希望前面的一节能够说明页面流是Struts的一个有用扩展。现在,让我们来看一看用什么方法能够把面的功能d到现有Struts目中,或者如何Struts和页面流应用E序能够互操作?/P> <P>  面构ZApache Struts 1.1的基之上。每个页面流都被~译Z个Struts模块。因此,面和Struts 1.1应用E序可以紧密合作?/P> <P>  有两U在面应用程序中使用Strutslg的方法:</P> <UL> <LI>利用Struts互操作性:在页面流应用E序中直接用Strutslg? <LI>利用Struts合ƈ功能Q将单的Struts应用E序导入到新的页面流应用E序中?</LI></UL> <P> </P> <P><STRONG>集成QStruts互操作?/STRONG></P> <P>  Struts互操作性功能允许用户用现有的ZStruts的组件但不修改它们。这允许现有的Strutslg与页面流lg交互Qƈ可以充分利用现有的JSP面、动作类、表单bean和流配置?/P> <P>  Struts和页面流应用E序可以在一个Web应用E序中共存ƈ交互。要从页面流跌{刎ͼU)Struts模块Q只需引用Struts模块中对应的动作卛_。反之亦Ӟ在Struts模块中,只需配置一个指向页面流中对应方法的动作Q就可以跌{到页面流?/P> <P>  面可以用作前端控制器处理初始hQ然后把控制权{交给Struts中的动作Q这些动作进行某U处理之后会控制权再传回给面。在嵌套面中可以看到cM的模式。也可以用这U方式在Struts应用E序和页面流之间׃n表单bean?/P> <UL> <LI>在页面流中,可以导入q用由Struts 1.1模块定义的表单bean。因此用户可以快速地利用现有的Struts代码Q而不必修改Struts动作cd表单bean? <LI>在Struts 1.1动作cMQ可以导入和使用面定义的表单bean?</LI></UL> <P>  包含Struts模块和页面流Q如果它们要q行互操作的话)的文件必d同一个Web目中?/P> <H4>对Struts模块和页面流的要?/H4> <P>  q里是对在同一个Web目中进行互操作的Struts模块和页面流的要求:</P> <UL> <LI>现有的Struts应该是版?.1? <LI>包含Struts模块的文件必d面属于同一个Web目? <LI>JAR文g中所有已~译的、与Struts相关的类Q字节码Q必d攑֜WEB-INF/lib目录下? <LI>为避免命名冲H,Struts模块名称和页面流目录名称在整个Web目中必L惟一的? <LI>同一个页面流目录下的JSP面L在页面流JPF上下文中呈现。调用动作和呈现在Struts模块上下文中的JSP面应当存放到其他目录下Q比?PROJECT-ROOT>/strutsModule目录下? <LI>如果面控制器cdStruts动作cd享同一表单beanQ可以将表单bean定义为内部类Q也可以其定义为外部Java文g。将表单beancd入页面流JPF和将要用它的Struts动作cM? <LI>在共享表单beanӞStruts模块的XML文g?<FORM-BEAN name="...">属性值必M面的jpf-struts-config-<PAGEFLOW-NAME>.xml文g中生成的name属性值精匹配? <LI>默认情况下,面的范围是从表单bean实例到请求。共享数据的最Ҏ的方式就是在Struts和页面流之间传递会话(session-scoped)表单bean。这可以通过使用Struts合ƈ功能(为页面流生成的)Struts配置XML合ƈC话表单bean中来实现? <LI>如果一个Web目l合使用了页面流和Struts模块Q必通过~辑目?/WEB-INF/web.xml文g注册每个Struts模块?</LI></UL> <H4>互操作性的一个例?/H4> <P>  本例昄了页面流动作如何调用Struts动作以及Struts动作如何调用面动作。本例的目的仅仅是显CZ操作性,因此代码q不完整。PageFlowActionA控制权和会话表单bean传递给StrutsActionA。StrutsActionAq行某种处理q蟩转到Page.jspQ后者包含一个Struts动作StrutsActionB。StrutsActionB控制权传回PageFlowActionB?</P> <P><IMG style="WIDTH: 252px; HEIGHT: 194px" height=175 src="http://dev2dev.bea.com.cn/images/051018/0510180102.gif" width=220> </P> <P><STRONG>集成QStruts合ƈ</STRONG></P> <P>  Struts合ƈ可以简单的Struts应用E序转换为页面流应用E序。这U方法是简单的Struts应用E序q移到页面流框架中的一U快h式?/P> <P>  Struts合ƈ功能不同于Struts互操作功能。对于Struts合ƈ功能Q可以在控制器别指定Struts XML配置文gQ如下: </P><PRE class=code>/** * @jpf:controller struts-merge="/WEB-INF/struts-config-merge-example.xml" */ public class ExampleStrutsMergeController extends PageFlowController { ... } </PRE> <P>  q一步允许现有的UStruts的XML配置文g与页面流的生成的jpf-struts-config-<PAGEFLOW>.xml文g合ƈQ在目~译Ӟ。Struts合ƈ功能的目的是重写面的默认|或者ؓ面指定页面流注释或其属性不提供的设|。Struts合ƈ文g通常很小Q而且仅仅修改面及其动作和表单bean。尽可以将动作映射d到Struts合ƈ文g中,但ƈ不推荐这U实cStruts动作映射应当使用 @jpf注释在页面流?.jpf文g中声明,然后Q如果需要的话)使用Struts合ƈ文g修改?/P> <P>  q可以用struts合ƈ功能Q将UStruts应用E序中的配置数据d面应用程序的配置文g。一般来_面的配置文g完全由应用程序的Java源文Ӟ具体地说是由散布于控制器文g的元数据注释Q生成。但是,如果要把Struts模块集成到应用程序中Q配|文件既可以由Java源文件生成,也可以由Struts模块的配|文件生成。如果是后者,可以在生成的配|文件中灉|地更ҎdM标签。例如,可以一个动作表单的默认作用域从hU重写ؓ会话U。ؓ此,只需创徏重写面配|文件的Struts配置文g的适当部分Q然后从面的Java源文件中引用q个重写后的文gQ正如上面的例子所C?/P> <P>  考虑到诸如现有Struts的复杂性、时间和工作量等因素Q可以在面中l合使用Struts合ƈ功能和Struts互操作性功能,以取得最x果?/P> <P><STRONG>Beehive</STRONG></P> <P>  Apache Beehive ProjectQ蜂巢计划)不只包含面。它有三个子目Q?/P> <UL> <LI>NetUI PageFlowsQNetUI面)Q本文中介绍的Web应用E序框架? <LI>ControlsQ一个帮助程序员构徏lgQ如QJavaBeanQ它元数据合ƈ到它的编E模型中Q的轻量U组件框架。该目也提供一些预制的控g? <LI>Web ServicesQ一个针对JSR-181规范所描述的Web services的注释驱动的~程模型?</LI></UL>  Java面全面支持控Ӟ控g是访问企业资源和包装业务逻辑的简单的~程模型抽象。Java控g使开发h员可以在一个简单直观的环境中用方法、事件和属性访问J2EEq_的强大功能(安全性、事务和异步Q,而且能够构徏遵从面向服务架构QSOAQ的最佛_늚Web应用E序。例如,可以在页面流中用一个简单的注释来声明一个Web service控gQ如下所C: <PRE class=code>@Control public MyWebService myWebService; </PRE> <P>  环境会自动将控g插入面中?/P> <P>  与XMLBeans相结合,Beehive可以提供无~地构徏和部|企业SOA的基架构。Beehive 1.0预定在本月底发布Q?.0的规划正在进行中。可以用诸如BEA WebLogic Workshop之类的IDE或Pollinate目中开发的Eclipse插g来开发Beehive应用E序?/P> <P><STRONG>l束?/STRONG></P> <P>  面非常坚定地承诺要企业软g的开发和l护变得更加L快捷。在本文中,我们介绍了页面流越Struts的一些优炏V页面流如何增强现有的Struts应用E序Q以及可以用于升U和无缝构徏企业USOA业务应用E序的几U方法,包括Struts互操作性和Struts合ƈ?/P> <P><STRONG>参考资?/STRONG></P> <UL> <LI><A target=_blank>Apache Beehive</A>——官方主? <LI><A target=_blank>Apache Beehive Page Flow说明文档</A> <LI><A target=_blank>Apache Struts</A> </LI></UL> <P><STRONG>原文出处</STRONG></P> <P><A target=_blank>http://dev2dev.bea.com/pub/a/2005/07/pageflows.html</A></P><img src ="http://www.tkk7.com/Victor/aggbug/18070.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/Victor/" target="_blank">Victor</a> 2005-11-04 08:41 <a href="http://www.tkk7.com/Victor/articles/18070.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Ajax?转自EBA)http://www.tkk7.com/Victor/articles/18069.htmlVictorVictorFri, 04 Nov 2005 00:35:00 GMThttp://www.tkk7.com/Victor/articles/18069.htmlhttp://www.tkk7.com/Victor/comments/18069.htmlhttp://www.tkk7.com/Victor/articles/18069.html#Feedback0http://www.tkk7.com/Victor/comments/commentRss/18069.htmlhttp://www.tkk7.com/Victor/services/trackbacks/18069.html作者:David Teare
本文关键字:ajaxdhtmldwr javascript

作ؓJ2EE开发h员,我们gl常x“后端机Ӟbackend mechanicsQ”。我们通常会忘讎ͼJ2EE的主要成功之处在Web应用E序斚wQ许多原因得h们喜Ƣ利用Web开发应用程序,但主要还是因为其易于部v的特点允许站点以可能低的成本拥有上百万的用戗遗憄是,在过dq中Q我们在后端投入了太多的旉Q而在使我们的Web用户界面对用戯然和响应灉|斚w却投入不?

  本文介绍一U方法,AjaxQ用它可以构徏更ؓ动态和响应更灵敏的Web应用E序。该Ҏ的关键在于对览器端的JavaScript、DHTML和与服务器异步通信的组合。本文也演示了启用这U方法是多么单:利用一个Ajax框架Q指DWRQ构造一个应用程序,它直接从览器与后端服务q行通信。如果用得当,q种强大的力量可以应用E序更加自然和响应灵敏,从而提升用L览体验?/P>

  该应用程序中所使用的示例代码已打包为单独的WAR文gQ可供下载?/P>

?/STRONG>

  术语Ajax用来描述一l技术,它ɋ览器可以ؓ用户提供更ؓ自然的浏览体验。在Ajax之前QWeb站点强制用户q入提交/{待/重新昄范例Q用L动作L与服务器的“思考时间”同步。Ajax提供与服务器异步通信的能力,从而用户从请?响应的@环中解脱出来。借助于AjaxQ可以在用户单击按钮Ӟ使用JavaScript和DHTML立即更新UIQƈ向服务器发出异步hQ以执行更新或查询数据库。当hq回Ӟ可以用JavaScript和CSS来相应地更新UIQ而不是刷新整个页面。最重要的是Q用L至不知道览器正在与服务器通信QWeb站点看v来是x响应的?/P>

  虽然Ajax所需的基架构已经出现了一D|_但直到最q异步请求的真正威力才得到利用。能够拥有一个响应极其灵敏的Web站点实Ȁ动h心,因ؓ它最l允许开发h员和设计人员使用标准的HTML/CSS/JavaScript堆栈创徏“桌面风格的Qdesktop-likeQ”可用性?/P>

  通常Q在J2EE中,开发h员过于关注服务和持久性层的开发,以至于用L面的可用性已l落后。在一个典型的J2EE开发周期中Q常怼听到q样的话Q“我们没有可投入UI的时间”或“不能用HTML实现”。但是,以下Web站点证明Q这些理由再也站不住脚了Q?/P>

  所有这些Web站点都告诉我们,Web应用E序不必完全依赖于从服务器重新蝲入页面来向用户呈现更攏V一切似乎就在瞬间发生。简而言之,在涉及到用户界面的响应灵敏度Ӟ基准讑־更高了?/P>

定义Ajax

  Adaptive Path公司的Jesse James Garrettq样定义AjaxQ?/P>

  Ajax不是一U技术。实际上Q它由几U蓬勃发展的技术以新的强大方式l合而成。Ajax包含Q?/P>

  • ZXHTML?A target=_blank>CSS标准的表C;
  • 使用Document Object Modelq行动态显C和交互Q?
  • 使用XMLHttpRequest与服务器q行异步通信Q?
  • 使用JavaScriptl定一切?

  q非常好Q但Z么要以Ajax命名呢?其实术语Ajax是由Jesse James Garrett创造的Q他说它是“Asynchronous JavaScript + XML的简写”?/P>

Ajax的工作原?/STRONG>

  Ajax的核心是JavaScript对象XmlHttpRequest。该对象在Internet Explorer 5中首ơ引入,它是一U支持异步请求的技术。简而言之,XmlHttpRequest使您可以使用JavaScript向服务器提出hq处理响应,而不d用户?/P>

  在创建Web站点Ӟ在客L执行屏幕更新为用h供了很大的灵zL。下面是使用Ajax可以完成的功能:

  • 动态更新购物R的物品LQ无需用户单击Updateq等待服务器重新发送整个页面?
  • 提升站点的性能Q这是通过减少从服务器下蝲的数据量而实现的。例如,在Amazon的购物R面Q当更新子中的一物品的数量Ӟ会重新蝲入整个页面,q必M?2K的数据。如果用Ajax计算新的总量Q服务器只会q回新的总量|因此所需的带宽仅为原来的癑ֈ之一?
  • 消除了每ơ用戯入时的页面刷新。例如,在Ajax中,如果用户在分列表上单击NextQ则服务器数据只h列表而不是整个页面?
  • 直接~辑表格数据Q而不是要求用户导航到新的面来编辑数据。对于AjaxQ当用户单击EditӞ可以静态表格刷Cؓ内容可编辑的表格。用户单击Done之后Q就可以发出一个Ajaxh来更新服务器Qƈh表格Q其包含静态、只ȝ数据?

  一切皆有可能!但愿它能够激发您开始开发自qZAjax的站炏V然而,在开始之前,让我们介l一个现有的Web站点Q它遵@传统的提?{待/重新昄的范例,我们q将讨论Ajax如何提升用户体验?/P>

Ajax可用于那些场景?——一个例子:MSN Money面

  前几天,在浏览MSN Money面的时候,有一?A target=_blank>关于房地产投资的文章引v了我的好奇心。我军_使用站点的“Rate this article”(评h本文Q功能,鼓励其他的用戯一Ҏ间来阅读q篇文章。在我单击vote按钮q等待了一会儿之后Q整个页面被hQ在原来投票问题所在的地方出现了一个漂亮的感谢画面?/P>

  而Ajax能够使用L体验更加愉快Q它可以提供响应更加灉|的UIQƈ消除面h所带来的闪烁。目前,׃要刷新整个页面,需要传送大量的数据Q因为必重新发送整个页面。如果用AjaxQ服务器可以q回一个包含了感谢信息?00字节的消息,而不是发?6,813字节的消息来h整个面。即使用的是高速InternetQ传?6K?/2K的差别也非常大。同样重要的是,只需要刷C投票相关的一节Q而不是刷新整个屏q?/P>

  让我们利用Ajax实现自己的基本投系l?/P>

原始的AjaxQ直接用XmlHttpRequest

  如上所qͼAjax的核心是JavaScript对象XmlHttpRequest。下面的CZ文章评hpȝ带您熟悉Ajax的底层基本知识:http://tearesolutions.com/ajax-demo/raw-ajax.html。注Q如果您已经在本地WebLogic容器中安装了ajax-demo.warQ可以导航到http://localhost:7001/ajax-demo/raw-ajax.htmlQ?/P>

  览应用E序Q参与投,q亲眼看它如何运转。熟悉了该应用程序之后,l箋阅读Q进一步了解其工作原理l节?/P>  首先Q您拥有一些简单的定位Ҏ讎ͼ它连接到一个JavaScriptcastVote(rank)函数?

function castVote(rank) {
  var url = "/ajax-demo/static-article-ranking.html";
  var callback = processAjaxResponse;
  executeXhr(callback, url);
}

  该函Cؓ您想要与之通信的服务器资源创徏一个URLq调用内部函数executeXhrQ提供一个回调JavaScript函数Q一旦服务器响应可用Q该函数p执行。由于我希望它运行在一个简单的Apache环境中,“cast vote URL”只是一个简单的HTML面。在实际情况中,被调用的URL记录票数ƈ动态地呈现包含投票L的响应?/P>  下一步是发出一个XmlHttpRequesthQ?

function executeXhr(callback, url) {
  // branch for native XMLHttpRequest object
  if (window.XMLHttpRequest) {
    req = new XMLHttpRequest();
    req.onreadystatechange = callback;
    req.open("GET", url, true);
    req.send(null);
  } // branch for IE/Windows ActiveX version
  else if (window.ActiveXObject) {
    req = new ActiveXObject("Microsoft.XMLHTTP");
    if (req) {
      req.onreadystatechange = callback;
      req.open("GET", url, true);
      req.send();
    }
  }
}

  如您所见,执行一个XmlHttpRequestq不单,但非常直观。和q_一P在JavaScript领域Q大部分的工作量都花在确保浏览器兼容斚w。在q种情况下,首先要确定XmlHttpRequest是否可用。如果不能用Q很可能要用Internet ExplorerQ这样就要用所提供的ActiveX实现?/P>

executeXhr()Ҏ中最关键的部分是q两行:

req.onreadystatechange = callback;
req.open("GET", url, true);

  W一行定义了JavaScript回调函数Q您希望一旦响应就l它p动执行,而req.open()Ҏ中所指定的“true”标志说明您惌异步执行该请求?/P>  一旦服务器处理完XmlHttpRequestq返回给览器,使用req.onreadystatechange指派所讄的回调方法将被自动调用?

function processAjaxResponse() {
  // only if req shows "loaded"
  if (req.readyState == 4) {
    // only if "OK"
    if (req.status == 200) {
      502 502'votes').innerHTML = req.responseText;
    } else {
      alert("There was a problem retrieving the XML data:
" +
      req.statusText);
    }
  }
} 

  该代码相当简z,q且使用了几个敎ͼq得难以一下子看出发生了什么。ؓ了弄清楚q一点,下面的表|引用?A target=_blank>http://developer.apple.com/internet/webcontent/xmlhttpreq.htmlQ列举了常用的XmlHttpRequest对象属性?/P>

属?/STRONG>

描述

onreadystatechange

每次状态改变所触发事g的事件处理程?/P>

readyState

对象状态|

  • 0 = 未初始化QuninitializedQ?
  • 1 = 正在加蝲QloadingQ?
  • 2 = 加蝲完毕QloadedQ?
  • 3 = 交互QinteractiveQ?
  • 4 = 完成QcompleteQ?

responseText

从服务器q程q回的数据的字符串Ş?/P>

responseXML

从服务器q程q回的DOM兼容的文档数据对?/P>

status

从服务器q回的数字代码,比如404Q未扑ֈQ或200Q就l)

statusText

伴随状态码的字W串信息

  现在processVoteResponse()函数开始显C出其意义了。它首先查XmlHttpRequest的整体状态以保证它已l完成(readyStatus == 4Q,然后Ҏ服务器的讑֮询问h状态。如果一切正常(status == 200Q?׃用innerHTML属性重写DOM的“votes”节点的内容?/P>

  既然您亲眼看CXmlHttpRequest对象是如何工作的Q就让我们利用一个旨在简化JavaScript与Java应用E序之间的异步通信的框架来对具体的l节q行抽象?/P>

Ajax: DWR方式

  按照与文章评Ll相同的程Q我们将使用Direct Web RemotingQDWRQ框架实现同L功能?/P>

  假定文章和投结果存储在一个数据库中,使用某种对象/关系映射技术来完成抽取工作。ؓ了部|v来尽可能地简单,我们不会使用数据库进行持久性存储。此外,Z应用E序可能通用Q也不用Web框架。相反,应用E序从一个静态HTML文g开始,可以认ؓ它由服务器动态地呈现。除了这些简化措施,应用E序q应该用Spring Framework兌一切,以便L看出如何在一个“真实的”应用程序中使用DWR?/P>

  现在应该下蝲CZ应用E序q熟悉它。该应用E序被压~ؓ标准的WAR文gQ因此您可以把它攄CQ何一个Web容器中——无需q行配置。部|完毕之后,可以导航到http://localhost:7001/ajax_demo/dwr-ajax.html来运行程序?/P>

  可以查看HTML 源代?/A>Q了解它如何工作。给人印象最q是,代码如此单——所有与服务器的交互都隐藏在JavaScript对象ajaxSampleSvc的后面。更加o人惊讶的是,ajaxSampleSvc服务不是由手工编写而是完全自动生成的!让我们l,看看q是如何做到的?/P>

引入DWR

  如同在“原始的Ajax”一节所演示的那P直接使用XmlHttpRequest创徏异步h非常ȝ。不仅JavaScript代码冗长Q而且必须考虑服务器端为定位Ajaxh到适当的服务所需做的工作Qƈ结果封送到览器?/P>

  设计DWR的目的是要处理将Web面安装到后端服务上所需的所有信息管道。它是一个Java框架Q可以很L地将它插入到Web应用E序中,以便JavaScript代码可以调用服务器上的服务。它甚至直接与Spring Framework集成Q从而允许用L接向Web客户机公开bean?/P>

  DWR真正的y妙之处是Q在用户配置了要向客h公开的服务之后,它用反来生成JavaScript对象Q以便Web面能够使用q些对象来访问该服务。然后Web面只需接合到生成的JavaScript对象Q就像它们是直接使用服务一PDWR无缝地处理所有有关Ajax和请求定位的琐碎l节?/P>

  让我们仔l分析一下示例代码,弄清它是如何工作的?/P>

应用E序l节QDWR分析

  关于应用E序Q首先要注意的是Q它是一个标准的Java应用E序Q用分层架构(Layered ArchitectureQ设计模式。用DWR通过JavaScript公开一些服务ƈ不媄响您的设计?

  下面是一个简单的Java服务Q我们将使用DWR框架直接其向JavaScript代码公开Q?/P>

package com.tearesolutions.service;

public interface AjaxSampleSvc { 
  Article castVote(int rank);
}

  q是一个被化到几乎不可能的E度的例子,其中只有一文章可以投。该服务由Spring理Q它使用的bean名是ajaxSampleSvcQ它的持久性需求则依赖于ArticleDao。详情请参见applicationContext.xml?/P>

  Z把该服务公开为JavaScript对象Q需要配|DWRQ添加dwr.xml文g到WEB-INF目录下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dwr PUBLIC
 "-//GetAhead Limited//DTD Direct Web Remoting 0.4//EN"
 "http://www.getahead.ltd.uk/dwr/dwr.dtd">
	
<dwr>
 <allow>
  <create creator="spring" javascript="ajaxSampleSvc">
   <param name="beanName" value="ajaxSampleSvc" />
  </create>
  <convert converter="bean" match="com.tearesolutions.model.Article"/>
  <exclude method="toString"/>
  <exclude method="setArticleDao"/>
 </allow>
</dwr>

  dwr.xml文g告诉DWR哪些服务是要直接向JavaScript代码公开的。注意,已经要求公开Spring bean ajaxSampleSvc。DWR自动找到由应用E序讄的SpringApplicationContext。ؓ此,必须使用标准的servletqo器ContextLoaderListener来初始化Spring ApplicationContext?/P>  DWR被设|ؓ一个servletQ所以把它的定义d到web.xmlQ?

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD 
 Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
 <display-name>Ajax Examples</display-name>

 <listener>
  <listener-class>
      org.springframework.web.context.ContextLoaderListener
  </listener-class>
 </listener>
	
 <servlet>
  <servlet-name>ajax_sample</servlet-name>
  <servlet-class>com.tearesolutions.web.AjaxSampleServlet</servlet-class>
  <load-on-startup>1</load-on-startup>
 </servlet>

 <servlet>
  <servlet-name>dwr-invoker</servlet-name>
  <display-name>DWR Servlet</display-name>
  <description>Direct Web Remoter Servlet</description>
  <servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class>
  <init-param>
   <param-name>debug</param-name>
   <param-value>true</param-value>
  </init-param>
 </servlet>

 <servlet-mapping>
  <servlet-name>ajax_sample</servlet-name>
  <url-pattern>/ajax_sample</url-pattern>
 </servlet-mapping>

 <servlet-mapping>
  <servlet-name>dwr-invoker</servlet-name>
  <url-pattern>/dwr/*</url-pattern>
 </servlet-mapping>
</web-app>

  做完q些之后Q可以加?A href="http://localhost:7001/ajax-demo/dwr" target=_blank>http://localhost:7001/ajax-demo/dwrQ看看哪些服务可用。结果如下:

?. 可用的服?/P>  单击ajaxSampleSvc链接Q查看有兛_何在HTML面内直接用服务的CZ实现。其中包含的两个JavaScript文g完成了大部分的功能:

<script type='text/javascript' 
   src='/ajax-demo/dwr/interface/ajaxSampleSvc.js'></script>
<script type='text/javascript' 
   src='/ajax-demo/dwr/engine.js'></script>

ajaxSampleSvc.js是动态生成的Q?/P>

function ajaxSampleSvc() { }

ajaxSampleSvc.castVote = function(callback, p0)
{ 
  DWREngine._execute(callback, '/ajax-demo/dwr', 
 'ajaxSampleSvc', 'castVote', p0);
}

  现在可以使用JavaScript对象ajaxSampleSvc替换所有的XmlHttpRequest代码Q从而重构raw-ajax.html文g。可以在dwr-ajax.html文g中看到改动的l果Q下面是新的JavaScript函数Q?/P>

function castVote(rank) {
  ajaxSampleSvc.castVote(processResponse, rank);
}
function processResponse(data) {
 var voteText = "

Thanks for Voting!

" + "

Current ranking: " + data.voteAverage + " out of 5

" + "

Number of votes placed: " + data.numberOfVotes + "

"; 502 502'votes').innerHTML = voteText; }

  惊h地简单,不是吗?由ajaxSampleSvc对象q回的Article域对象序列化Z个JavaScript对象Q允许在它上面调用诸如numberOfVotes()和voteAverage()之类的方法。在动态生成ƈ插入到DIV元素“votes”中的HTML代码内用这些数据?/P>

下一步工?/STRONG>

   在后l文章中Q我l有关Ajax的话题,涉及下面q些斚wQ?/P>

  • Ajax最佛_?

  像许多技术一PAjax是一把双刃剑。对于一些用例,其应用程序其实没有必要用AjaxQ用了反而有损可用性。我介l一些不适合使用的模式,H出说明Ajax的一些消极方面,q展CZ些有助于~和q些消极斚w的机制。例如,?A >Netflix电媄览?/A>来说QAjax是合适的解决Ҏ吗?或者,如何提示用户实Z一些问题,而再ơ单L钮也无济于事Q?/P>

  • 理跨请求的状?

  在用AjaxӞ最初的文档DOM会发生一些变化,q且有大量的面状态信息存储在客户端变量中。当用户跟踪一个链接到应用E序中的另一个页面时Q状态就丢失了。当用户按照惯例单击Back按钮Ӟ呈现l他们的是缓存中的初始页面。这会用户感到非常qhQ?/P>

  • 调试技?

  使用JavaScript在客L执行更多的工作时Q如果事情不按预期方式进行,需要一些调试工h帮助弄清出现了什么问题?/P>

l束?/STRONG>

  本文介绍了AjaxҎQƈ展示了如何用它来创Z个动态且响应灉|的Web应用E序。通过使用DWR框架Q可以轻村֜把Ajax融合到站点中Q而无需担心所有必L行的实际道工作?/P>

  特别感谢Getahead IT咨询公司的Joe Walker和他的团队开发出DWRq样奇的工兗感谢你们与世界׃n它!

下蝲

  本文中演C的应用E序源代码可供下载:ajax-demo.warQ?.52 MBQ?/P>

Victor 2005-11-04 08:35 发表评论
]]>
Log4J配置之全套功?/title><link>http://www.tkk7.com/Victor/articles/14073.html</link><dc:creator>Victor</dc:creator><author>Victor</author><pubDate>Mon, 26 Sep 2005 03:19:00 GMT</pubDate><guid>http://www.tkk7.com/Victor/articles/14073.html</guid><wfw:comment>http://www.tkk7.com/Victor/comments/14073.html</wfw:comment><comments>http://www.tkk7.com/Victor/articles/14073.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/Victor/comments/commentRss/14073.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/Victor/services/trackbacks/14073.html</trackback:ping><description><![CDATA[<H1 class=block_title><A id=viewpost.ascx_TitleUrl ><FONT color=#4a664d>Log4J配置之全套功?/FONT></A></H1> <div id="z3lzjvt" class=post> <div id="rp1l7tf" class=postcontent> <P>LOG4J的配|之单它遍及于来多的应用中了:</P> <P>Log4J配置文g实现了输出到控制台、文件、回滚文件、发送日志邮件、输出到数据库日志表、自定义标签{全套功能。择其一二用就够用了,</P><BR> <P>log4j.rootLogger=DEBUG,CONSOLE,A1,im<BR>log4j.addivity.org.apache=true<BR><BR><BR><BR># 应用于控制台<BR><BR>log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender<BR>log4j.appender.Threshold=DEBUG<BR>log4j.appender.CONSOLE.Target=System.out<BR>log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout<BR>log4j.appender.CONSOLE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n<BR>#log4j.appender.CONSOLE.layout.ConversionPattern=[start]%d{DATE}[DATE]%n%p[PRIORITY]%n%x[NDC]%n%t[THREAD] n%c[CATEGORY]%n%m[MESSAGE]%n%n<BR><BR><BR>#应用于文?/P> <P>log4j.appender.FILE=org.apache.log4j.FileAppender<BR>log4j.appender.FILE.File=file.log<BR>log4j.appender.FILE.Append=false<BR>log4j.appender.FILE.layout=org.apache.log4j.PatternLayout<BR>log4j.appender.FILE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n<BR># Use this layout for LogFactor 5 analysis<BR><BR><BR><BR># 应用于文件回?BR><BR>log4j.appender.ROLLING_FILE=org.apache.log4j.RollingFileAppender<BR>log4j.appender.ROLLING_FILE.Threshold=ERROR<BR>log4j.appender.ROLLING_FILE.File=rolling.log<BR>log4j.appender.ROLLING_FILE.Append=true<BR>log4j.appender.ROLLING_FILE.MaxFileSize=10KB<BR>log4j.appender.ROLLING_FILE.MaxBackupIndex=1<BR>log4j.appender.ROLLING_FILE.layout=org.apache.log4j.PatternLayout<BR>log4j.appender.ROLLING_FILE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n<BR><BR><BR>#应用于socket<BR>log4j.appender.SOCKET=org.apache.log4j.RollingFileAppender<BR>log4j.appender.SOCKET.RemoteHost=localhost<BR>log4j.appender.SOCKET.Port=5001<BR>log4j.appender.SOCKET.LocationInfo=true<BR># Set up for Log Facter 5<BR>log4j.appender.SOCKET.layout=org.apache.log4j.PatternLayout<BR>log4j.appender.SOCET.layout.ConversionPattern=[start]%d{DATE}[DATE]%n%p[PRIORITY]%n%x[NDC]%n%t[THREAD]%n%c[CATEGORY]%n%m[MESSAGE]%n%n<BR><BR><BR># Log Factor 5 Appender<BR>log4j.appender.LF5_APPENDER=org.apache.log4j.lf5.LF5Appender<BR>log4j.appender.LF5_APPENDER.MaxNumberOfRecords=2000<BR><BR><BR><BR># 发送日志给邮g<BR><BR>log4j.appender.MAIL=org.apache.log4j.net.SMTPAppender<BR>log4j.appender.MAIL.Threshold=FATAL<BR>log4j.appender.MAIL.BufferSize=10<BR><A href="mailto:log4j.appender.MAIL.From=web@www.wuset.com"><FONT color=#4a664d>log4j.appender.MAIL.From=web@www.wuset.com</FONT></A><BR>log4j.appender.MAIL.SMTPHost=www.wusetu.com<BR>log4j.appender.MAIL.Subject=Log4J Message<BR><A href="mailto:log4j.appender.MAIL.To=web@www.wusetu.com"><FONT color=#4a664d>log4j.appender.MAIL.To=web@www.wusetu.com</FONT></A><BR>log4j.appender.MAIL.layout=org.apache.log4j.PatternLayout<BR>log4j.appender.MAIL.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n<BR><BR><BR><BR># 用于数据?BR>log4j.appender.DATABASE=org.apache.log4j.jdbc.JDBCAppender<BR>log4j.appender.DATABASE.URL=jdbc:mysql://localhost:3306/test<BR>log4j.appender.DATABASE.driver=com.mysql.jdbc.Driver<BR>log4j.appender.DATABASE.user=root<BR>log4j.appender.DATABASE.password=<BR>log4j.appender.DATABASE.sql=INSERT INTO LOG4J (Message) VALUES ('[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n')<BR>log4j.appender.DATABASE.layout=org.apache.log4j.PatternLayout<BR>log4j.appender.DATABASE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n<BR><BR><BR>log4j.appender.A1=org.apache.log4j.DailyRollingFileAppender<BR>log4j.appender.A1.File=SampleMessages.log4j<BR>log4j.appender.A1.DatePattern=yyyyMMdd-HH'.log4j'<BR>log4j.appender.A1.layout=org.apache.log4j.xml.XMLLayout<BR><BR>#自定义Appender<BR><BR>log4j.appender.im = net.cybercorlin.util.logger.appender.IMAppender<BR><BR>log4j.appender.im.host = mail.cybercorlin.net<BR>log4j.appender.im.username = username<BR>log4j.appender.im.password = password<BR>log4j.appender.im.recipient = corlin@cybercorlin.net<BR><BR>log4j.appender.im.layout=org.apache.log4j.PatternLayout<BR>log4j.appender.im.layout.ConversionPattern =[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n<BR></P></DIV></DIV><img src ="http://www.tkk7.com/Victor/aggbug/14073.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/Victor/" target="_blank">Victor</a> 2005-09-26 11:19 <a href="http://www.tkk7.com/Victor/articles/14073.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Log4j 学习W记http://www.tkk7.com/Victor/articles/13704.htmlVictorVictorThu, 22 Sep 2005 00:47:00 GMThttp://www.tkk7.com/Victor/articles/13704.htmlhttp://www.tkk7.com/Victor/comments/13704.htmlhttp://www.tkk7.com/Victor/articles/13704.html#Feedback0http://www.tkk7.com/Victor/comments/commentRss/13704.htmlhttp://www.tkk7.com/Victor/services/trackbacks/13704.html 转脓自:http://zooo.51.net/heavyz_cs/notebook/log4j.html

Log4j 学习W记

by heavyz
2003-04-15


Log4j主页Q?A >http://jakarta.apache.org/log4j


Index

  1. Log4j的类?
  2. LoggerQ日志写出器
    1. Logger的输出方?
    2. Logger的命名规?
    3. Log level
    4. CZ代码
    5. 关于logger的两点说?
  3. AppenderQ日志目的地
    1. ConsoleAppender
    2. FileAppender
    3. RollingFileAppender
  4. LayoutQ日志格式化?
    1. PatternLayout
    2. patterns in PatternLayout
  5. ConfigurationQ配|?
    1. 默认的log4j初始化过E?
    2. BasicConfigurator.configure()
    3. xml格式的log4j配置文g概述
    4. 在xml文g中配|appender和layout
    5. 我自q一个用xml文g配置log4j环境的很单的例子
  6. Log4j的编码习?
  7. 参考资?


Log4j的类?/B>

  1. Logger - 日志写出器,供程序员输出日志信息
  2. Appender - 日志目的圎ͼ把格式化好的日志信息输出到指定的地方?
    1. ConsoleAppender - 目的Cؓ控制台的Appender
    2. FileAppender - 目的Cؓ文g的Appender
    3. RollingFileAppender - 目的Cؓ大小受限的文件的Appender
  3. Layout - 日志格式化器Q用来把E序员的logging request格式化成字符?
    1. PatternLayout - 用指定的pattern格式化logging request的Layout


LoggerQ日志写出器

Logger对象是用来取?TT>System.out或?TT>System.err的日志写出器Q用来供E序员输出日志信息?/P>
Logger的输出方?/B>

Loggercd象提供一pdҎ供程序员输出日志信息?/P>

        

        ------ Log4j APIs : class Logger ------
        
        
        
        // Printing methods :
        
        public void debug(Object msg);
        public void debug(Object msg, Throwable t);
        
        public void info(Object msg);
        public void info(Object msg, Throwable t);
        
        public void warn(Object msg);
        public void warn(Object msg, Throwable t);
        
        public void error(Object msg);
        public void error(Object msg, Throwable t);
        
        public void fatal(Object msg);
        public void fatal(Object msg, Throwable t);
        
        // Generic printing method :
        
        public void log(Level l, Object msg);
      


Logger的命名规?/B>

Logger׃个Stringcȝ名字识别Qlogger的名字是大小写敏感的Q且名字之间hl承的关p,子名有父名作为前~Q用点号.分隔。如Q?TT>x.y?TT>x.y.z的父二Ӏ?/P>

根logger (root logger)是所有logger的祖先,它具有如下属性:1) 它L存在的;2) 它不可以通过名字获得?/P>

通过调用public static Logger Logger.getRootLogger()获得root loggerQ通过调用public static Logger Logger.getLogger(String name)或?TT>public static Logger Logger.getLogger(Class clazz)获得Q或者创建)一?TT>named logger。后者相当于调用Logger.getLogger(clazz.getName())?/P>

在某对象中,用该对象所属的cMؓ参数Q调?TT>Logger.getLogger(Class clazz)以获得logger被认为是目前所知的最理智的命名logger的方法?/P>


Log level

每个logger都被分配了一个日志?(log level)Q用来控制日志信息的输出。未被分配level的logger承它最q的父logger的level?/P>

每条输出到logger的日志请?logging request)也都有一个levelQ如果该request的level大于{于该logger的levelQ则该request被处理Q称为enabledQ;否则该request被忽略。故可得知:

  1. logger的level低Q表Clogger详l?
  2. logging request的level高Q表Clogging request优先输?

LevelcM预定义了五个levelQ它们的大小关系如下Q?/P>

        Level.ALL < Level.DEBUG < Level.INFO < Level.WARN < Level.ERROR < Level.FATAL < Level.OFF
      


CZ代码

以下代码用自己所属的cMؓ参数Q创Z个loggerQ启用默认配|,讄其levelq向其输qlogging request?/P>

import org.apache.log4j.Logger;
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Level;

public class Log4jTest {
    public static void main(String argv[]) {
    
        
        // Create a logger by the name of class Log4jTest.
        
        Logger logger = Logger.getLogger(Log4jTest.class);
        
        // Use the default configuration.
        
        BasicConfigurator.configure();
        
        // Set the logger level to Level.INFO
        
        logger.setLevel(Level.INFO);
        
        // This request will be disabled since Level.DEBUG < Level.INFO.
                  
        logger.debug("This is debug.");
        
        // These requests will be enabled.
                  
        logger.info("This is an info.");
        logger.warn("This is a warning.");
        logger.error("This is an error.");
        logger.fatal("This is a fatal error.");
        
        return;
    }
}
      


关于logger的两点说?/B>

  1. 用同名参数调?TT>Logger.getLogger(String name)返回同一个logger的引用。故可以在一个地斚w|loggerQ在另外一个地方获得配|好的loggerQ而无ȝ互间传递logger的引用?
  2. logger的创建可以按照Q意的序Q即Q父logger可以后于子logger被创建。log4j自动维护logger的承树?



AppenderQ日志目的地

每个logger都可以拥有一个或者多个appenderQ每个appender表示一个日志的输出目的圎ͼ比如console或者某个文件。可以?TT>Logger.addAppender(Appender app)为logger增加一个appenderQ可以?TT>Logger.removeAppender(Appender app)为loggerU除一个appender?/P>

默认情况下,logger的additive标志被设|ؓtrueQ表C子logger承父logger的所有appenders。该选项可以被重新设|,表示子logger不再承父logger的appenders?/P>

root logger拥有目标?TT>system.out?TT>consoleAppenderQ故默认情况下,所有的logger都将l承该appender?/P>

      

      ------ Log4j APIs : class Logger ------
      
      
      
      // 为logger对象增加或者移除一个Appender对象 :.
      
      public void appAppender(Appender app);
      public void removeAppender(Appender app);
      
      // 获得和设|additive标志Q是否承父logger的appenders :.
      // 注意Q?/B>在设|additive标志为falseӞ必须保证已经logger讄了新的appenderQ?:.
      // 否则log4j报错:log4j:WARN No appenders could be found for logger (x.y.z). :.
      
      public boolean getAdditivity();
      public void setAdditivity(boolean additive);
    

ConsoleAppender

可以使用ConsoleAppender对象把日志输出到控制台。每?TT>ConsoleAppender都有一?TT>targetQ表C它的输出目的地。它可以?TT>System.outQ标准输备(~冲昄屏)Q或者是System.errQ标准错误设备(不缓冲显C屏Q?TT>ConsoleAppender的用方法参考如下API :.

        

        ------ Log4j APIs : class ConsoleAppender extends WriterAppender ------
        
      
        
        // 构造方法,使用一个Layout对象构造一个ConsoleAppender对象 :.
        // 默认情况下,ConsoleAppender的target是System.out :.
        
        public ConsoleAppender(Layout layout);
        
        // 构造方法,使用一个Layout对象和一个target字符串构造ConsoleAppender对象 :.
        // target的可能取gؓConsoleAppender.SYSTEM_OUT和ConsoleAppender.SYSTEM_ERR :.
        
        public ConsoleAppender(Layout layout, String target);
      


FileAppender

可以使用FileAppender对象把日志输出到一个指定的日志文g中去。用方法可以参考如下的API :.

        

        ------ Log4j APIs : class FileAppender extends WriterAppender ------
        
      
        
        // 构造方法,使用一个Layout对象和日志文件名构造一个FileAppender对象 :.
        
        public FileAppender(Layout layout, String filename)
            throws IOException;
        public FileAppender(Layout layout, String filename, boolean append)
            throws IOException;
      


RollingFileAppender

可以使用FileAppender的子c?TT>RollingFileAppender对象Q把日志输出C个指定的日志文g中。不同的是该日志文g的大受到限Ӟ当日志内容超出最大的寸Ӟ该文件将向上滚动Q最老的日志被擦除)。还可以在该cd象中指定为日志文件做多少个备份。具体用方法参考如下API :.

        

        ------ Log4j APIs : class RollingFileAppender extends FileAppender ------
        
      
        
        // 构造方法,使用一个Layout对象和日志文件名构造一个RollingFileAppender对象 :.
        
        public RollingFileAppender(Layout layout, String filename)
            throws IOException;
        public RollingFileAppender(Layout layout, String filename, boolean append)
            throws IOException;
        
        // 获得和设|日志备份文件的个数 :.
        
        public int getMaxBackupIndex();
        public void setMaxBackupIndex(int index);
        
        // 获得和设|滚动日志文件的最大尺?:.
        
        public long getMaximumFileSize();
        public void setMaximumFileSize(long size);
      



LayoutQ日志格式化?/B>

每个appender都和一个layout相联p;layout的Q务是格式化用Llogging requestQappender的Q务是把layout格式化好的输出内定w往指定的目的地?/P>
PatternLayout

PatternLayout是Layout的一个子c,用来使用cMC语言?TT>printf函数中用的格式控制字符串来控制日志的输出格式。用方法参考如下API :.

        

        ------ Log4j APIs : class PatternLayout extends Layout ------
        
      
        
        // 无参数构造方法,使用DEFAULT_CONVERSION_PATTERN构造一个PatternLayout :.
        // 注意Q?/B>DEFAULT_CONVERSION_PATTERN?%m%n"Q只打印消息信息 :.
        
        public PatternLayout();
        
        // 构造方法,使用自定义的pattern构造一个PatternLayout :.
        
        public PatternLayout(String pattern);
        
        // 获得和设|PatternLayout对象的日志pattern :.
        
        public String getConversionPattern();
        public void setConversionPattern(String pattern);
      


patterns in PatternLayout

未完待箋



ConfigurationQ配|?/B>

对log4j环境的配|就是对root logger的配|,包括把root logger讄为哪个?level)Qؓ它增加哪些appenderQ等{。这些可以通过讄pȝ属性的Ҏ来隐式地完成Q也可以在程序里调用XXXConfigurator.configure()Ҏ来显式地完成?/P>
默认的log4j初始化过E?/B>

Loggercȝ静态初始化?static initialization block)中对log4j的环境做默认的初始化?B>注意Q?/B>如果E序员已l通过讄pȝ属性的Ҏ来配|了log4j环境Q则不需要再昑ּ地调?TT>XXXConfigurator.configure()Ҏ来配|log4j环境了?/P>

Logger的静态初始化块在完成初始化过E时检查一pdlog4j定义的系l属性。它所做的事情如下Q?

  1. 查系l属?TT>log4j.defaultInitOverrideQ如果该属性被讄为falseQ则执行初始化;否则Q只要不是falseQ无论是什么|甚至没有|都是否则Q,跌初始化?
  2. 把系l属?TT>log4j.configuration的Dl变量resource。如果该pȝ变量没有被定义,则把resource赋gؓ"log4j.properties"?B>注意Q?/B>在apache的log4j文档中徏议用定?TT>log4j.configurationpȝ属性的Ҏ来设|默认的初始化文件是一个好Ҏ?
  3. 试图把resource变量转化成ؓ一?TT>URL对象url。如果一般的转化Ҏ行不通,p?TT>org.apache.log4j.helpers.Loader.getResource(resource, Logger.class)Ҏ来完成{化?
  4. 如果url?.html"l尾Q则调用ҎDOMConfigurator.configure(url)来完成初始化Q否则,则调用方?TT>PropertyConfigurator.configure(url)来完成初始化。如?TT>url指定的资源不能被获得Q则跛_初始化过E?


BasicConfigurator.configure()

BasicConfigurator.configure()Ҏ使用最的方法配|log4j环境?B>注:所谓配|log4j环境Q就是指配置root loggerQ因为所有其它的logger都是root logger的后代,所以它们(默认情况下)都将l承root logger的性质?/P>

BasicConfigurator.configure()完成的Q务是Q?

  1. 用默认pattern创徏PatternLayout对象pQ?BR>PatternLayout p = new PatternLayout("%-4r[%t]%-5p%c%x - %m%n");
  2. 用p创徏ConsoleAppender对象aQ目标是system.outQ标准输备:
    ConsoleAppender a = new ConsoleAppender(p,ConsoleAppender.SYSTEM_OUT);
  3. 为root logger增加一个ConsoleAppender pQ?BR>rootLogger.addAppender(p);
  4. 把root logger的log level讄为DEBUGU别Q?BR>rootLogger.setLevel(Level.DEBUG);


xml格式的log4j配置文g概述

xml格式的log4j配置文g需要?TT>org.apache.log4j.html.DOMConfigurator.configure()Ҏ来读入。对xml文g的语法定义可以在log4j的发布包中找刎ͼorg/apache/log4j/xml/log4j.dtd?/P>
log4j的xml配置文g的树状结?/B>

log4j的xml配置文g的树状结构如下所C,注意下图只显CZ常用的部分?:.

          xml declaration and dtd
            |
          log4j:configuration
            |
            +-- appender (name, class)
            |     |
            |     +-- param (name, value)
            |     +-- layout (class)
            |           |
            |           +-- param (name, value)
            +-- logger (name, additivity)
            |     |
            |     +-- level (class, value)
            |     |     |
            |     |     +-- param (name, value)
            |     +-- appender-ref (ref)
            +-- root
                  |
                  +-- param (name, class)
                  +-- level
                  |     |
                  |     +-- param (name, value)
                  +-- appender-ref (ref)  
        


xml declaration and dtd

xml配置文g的头部包括两个部分:xml声明和dtd声明。头部的格式如下Q?:.

          <?xml version="1.0" encoding="UTF-8" ?>
          <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
        


log4j:configuration (root element)

  1. xmlns:log4j [#FIXED attribute] : 定义log4j的名字空_取定?TT>"http://jakarta.apache.org/log4j/"
  2. appender [* child] : 一个appender子元素定义一个日志输出目的地
  3. logger [* child] : 一个logger子元素定义一个日志写出器
  4. root [? child] : root子元素定义了root logger


appender

appender元素定义一个日志输出目的地?/P>

  1. name [#REQUIRED attribute] : 定义appender的名字,以便被后文引?
  2. class [#REQUIRED attribute] : 定义appender对象所属的cȝ全名
  3. param [* child] : 创徏appender对象时传递给cL造方法的参数
  4. layout [? child] : 该appender使用的layout对象


layout

layout元素定义与某一个appender相联pȝ日志格式化器?/P>

  1. class [#REQUIRED attribute] : 定义layout对象所属的cȝ全名
  2. param [* child] : 创徏layout对象时传递给cL造方法的参数


logger

logger元素定义一个日志输出器?/P>

  1. name [#REQUIRED attribute] : 定义logger的名字,以便被后文引?
  2. additivity [#ENUM attribute] : 取gؓ"true"Q默认)或?false"Q是否承父logger的属?
  3. level [? child] : 定义该logger的日志?
  4. appender-ref [* child] : 定义该logger的输出目的地


root

root元素定义Ҏ志输出器root logger?/P>

  1. param [* child] : 创徏root logger对象时传递给cL造方法的参数
  2. level [? child] : 定义root logger的日志?
  3. appender-ref [* child] : 定义root logger的输出目的地


level

level元素定义logger对象的日志别?/P>

  1. class [#IMPLIED attribute] : 定义level对象所属的c,默认情况下是"org.apache.log4j.Levelc?
  2. value [#REQUIRED attribute] : 为level对象赋倹{可能的取g到大依ơؓ"all"?debug"?info"?warn"?error"?fatal"?off"。当gؓ"off"时表C没有Q何日志信息被输出
  3. param [* child] : 创徏level对象时传递给cL造方法的参数


appender-ref

appender-ref元素引用一个appender元素的名字,为logger对象增加一个appender?/P>

  1. ref [#REQUIRED attribute] : 一个appender元素的名字的引用
  2. appender-ref元素没有子元?


param

param元素在创建对象时为类的构造方法提供参数。它可以成ؓappender?TT>layout?TT>filter?TT>errorHandler?TT>level?TT>categoryFactory?TT>root{元素的子元素?/P>

  1. name and value [#REQUIRED attributes] : 提供参数的一l名值对
  2. param元素没有子元?



在xml文g中配|appender和layout

创徏不同的Appender对象或者不同的Layout对象要调用不同的构造方法。可以用param子元素来讑֮不同的参数倹{?/P>
创徏ConsoleAppender对象

ConsoleAppender的构造方法不接受其它的参数?:.

          ... ... ... ...
          <appender name="console.log" class="org.apache.log4j.ConsoleAppender">
            <layout ... >
              ... ...
            </layout>
          </appender>
          ... ... ... ...
        


创徏FileAppender对象

可以为FileAppendercȝ构造方法传递两个参敎ͼFile表示日志文g名;Append表示如文件已存在Q是否把日志q加到文件尾部,可能取gؓ"true"?false"Q默认)?:.

          ... ... ... ...
          <appender name="file.log" class="org.apache.log4j.FileAppender">
            <param name="File" value="/tmp/log.txt" />
            <param name="Append" value="false" />
            <layout ... >
              ... ...
            </layout>
          </appender>
          ... ... ... ...
        


创徏RollingFileAppender对象

除了File?TT>Append以外Q还可以为RollingFileAppendercȝ构造方法传递两个参敎ͼMaxBackupIndex备䆾日志文g的个敎ͼ默认?个)Q?TT>MaxFileSize表示日志文g允许的最大字节数Q默认是10MQ?:.

          ... ... ... ...
          <appender name="rollingFile.log" class="org.apache.log4j.RollingFileAppender">
            <param name="File" value="/tmp/rollingLog.txt" />
            <param name="Append" value="false" />
            <param name="MaxBackupIndex" value="2" />
            <param name="MaxFileSize" value="1024" />
            <layout ... >
              ... ...
            </layout>
          </appender>
          ... ... ... ...
        


创徏PatternLayout对象

可以为PatternLayoutcȝ构造方法传递参数ConversionPattern?:.

          ... ... ... ...
          <layout class="org.apache.log4j.PatternLayout>
            <param name="Conversion" value="%d [%t] %p - %m%n" />
          </layout>
          ... ... ... ...
        



我自q一个用xml文g配置log4j环境的很单的例子

为WSOTA目开发java web start的胖客户端时Q用了如下的xml文g配置log4j环境Q文件名为wsota-rc.log4j.htmlQ::.

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
  
  <!-- ================================================================= -->
  <!--                     a rolling file appender                       -->
  <!-- ================================================================= -->
  
  <appender name="wsota-rc.file.log" class="org.apache.log4j.RollingFileAppender">
    <param name="File" value="/tmp/wsota-rc.log" />
    <param name="Append" value="false" />
    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern" value="%d [%t] %p - %m%n" />
    </layout>
  </appender>
  
  <!-- ================================================================= -->
  <!--                       a console appender                          -->
  <!--     debug can be turned off by setting level of root to "off"     -->
  <!-- ================================================================= -->
  
  <appender name="wsota-rc.console.log" class="org.apache.log4j.ConsoleAppender">
    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern" value="%d [%t] %p - %m%n" />
    </layout>
  </appender>
  
  <!--  use this to turn on debug to a rolling file. -->
  
  <root>
    <level value="debug" />
    <appender-ref ref="wsota-rc.file.log" />
  </root>
  
  <!--  use this to turn on debug to console. -->
  <!--
  <root>
    <level value="off" />
    <appender-ref ref="wsota-rc.console.log" />
  </root>
  -->
  
  
  <!--  use this to turn off debug. -->
  <!--
  <root>
    <level value="off" />
    <appender-ref ref="wsota-rc.console.log" />
  </root>
  -->
  
</log4j:configuration>
      

在胖客户E序中用了如下代码来用外部xml文g配置log4j环境Q注意该代码D位于程序的main class的静态初始化块中Q含有以下代码的cdxml配置文g在同一个目录下Q?.

  import org.apache.log4j.html.DOMConfigurator;
  
  public class SapFrame extends JFrame {
      static {
          DOMConfigurator.configure(SapFrame.class.getResource("wsota-rc.log4j.html"));
      }
      ... ... ... ...
  }
      



Log4j的编码习?/B>

  1. 让每个类都拥有一个private static的Logger对象Q用来输cM的全部日志信?
  2. 使用xml文g来完成对log4j环境的配|。在目的main class中的静态初始化块里放log4j环境的配|代码。注意:在一个项目中Qlog4j环境只需要被配置一ơ,而不是在每个使用了logger的类里都需要调用一?
  3. ?TT>MyClass.class作ؓ参数创徏该类的静态Logger对象
  4. 补充?..


参考资?/B>

  1. Log4j主页上的相关文档Q?A >http://jakarta.apache.org/log4j/docs
  2. Ashley J.S Mills的log4j教程Q?A >http://supportweb.cs.bham.ac.uk/documentation/tutorials/docsystem/build/tutorials/log4j/log4j.html


Victor 2005-09-22 08:47 发表评论
]]>
Hibernate+Spring+Struts扩展Struts 3(? http://www.tkk7.com/Victor/articles/13012.htmlVictorVictorWed, 14 Sep 2005 05:16:00 GMThttp://www.tkk7.com/Victor/articles/13012.htmlhttp://www.tkk7.com/Victor/comments/13012.htmlhttp://www.tkk7.com/Victor/articles/13012.html#Feedback0http://www.tkk7.com/Victor/comments/commentRss/13012.htmlhttp://www.tkk7.com/Victor/services/trackbacks/13012.html创徏你自qRequestProcessor

  通过上面Q我们了解到了RequestProcessor的默认实现是如何工作的。现在我们要演示一个例子来说明如何定制你自qRequestProcessor。ؓ了展C创建用户定制的RequestProcessorQ我们将会让我们的示例实C面两个业务需求:

  ·我们惛_Z个ContactImageActionc,它将生成囄而不是^常的HTML面?BR>
  ·在每个请求处理之前,我们都想通过查session中的userName属性来定用户是否已经登陆。如果那个属性没有找刎ͼ我们会把用户重定向到登陆面?BR>
  我们分两步实现q些业务需求?BR>
  1、创Z的CustomRequestProcessorc,它将l承自RequestProcessorc,如下Q?BR>
public class CustomRequestProcessor
extends RequestProcessor {
 protected boolean processPreprocess (
  HttpServletRequest request,HttpServletResponse response) {
   HttpSession session = request.getSession(false);
   //If user is trying to access login page
   // then don't check
   if( request.getServletPath().equals("/loginInput.do")
|| request.getServletPath().equals("/login.do") )
    return true;
   //Check if userName attribute is there is session.
   //If so, it means user has allready logged in
   if( session != null && session.getAttribute("userName") != null)
    return true;
   else{
    try{
     //If no redirect user to login Page
     request.getRequestDispatcher("/Login.jsp").forward(request,response);
    }catch(Exception ex){
    }
   }
   return false;
  }

 protected void processContent(HttpServletRequest request,
HttpServletResponse response) {
  //Check if user is requesting ContactImageAction
  // if yes then set image/gif as content type
  if( request.getServletPath().equals("/contactimage.do")){
   response.setContentType("image/gif");
   return;
  }
   super.processContent(request, response);
 } 
}

  在CustomRequestProcessorcȝprocessPreprocessҎ中,我们查session的userName属性,如果没有扑ֈQ就用户重定向到登陆页面?BR>
  对于生成囄作ؓ输出的需求,我们必须覆盖processContentҎQ首先检查请求是否是/contactimage路径。如果是的话Q我们就会将contentType讄为image/gifQ否则设|ؓtext/html?BR>
  2、在你的struts-config.xml文g的<action-mappintQ元素之后加入下面的文字Q告诉Struts CustomRequestProcessor应当被用作RequestProcessorc:

QcontrollerQ?BR>Qset-property property="processorClass"value="com.sample.util.CustomRequestProcessor"/Q?BR>Q?controllerQ?/TD>

  h意,当你只有很少的actionc需要生成非text/htmlcd的输出时Q你覆写processContent()Ҏ是OK的。如果不是这样子的话Q你应该创徏一个Struts的子应用来处理请求生成图片的ActionQƈ为它们将contentType讄为image/gif?BR>
  Struts的Tiles框架是使用它自qRequestProcessor来装饰Struts的输出?BR>
  ActionServlet

  如果你查看你的Struts web应用的web.xmlQ你会看到这L文字Q?BR>
Qweb-app Q?BR> QservletQ?BR>  Qservlet-nameQaction=Q?servlet-nameQ?BR>  Qservlet-classQorg.apache.struts.action.ActionServletQ?servlet-classQ?BR>  Q?-- All your init-params go here--Q?BR> Q?servletQ?BR> Qservlet-mappingQ?BR>  Qservlet-nameQactionQ?servlet-nameQ?BR>  Qurl-patternQ?.doQ?url-patternQ?BR> Q?servlet-mappingQ?BR>Q?web-app Q?/TD>

  q意味着ActionServlet负责处理你所有Struts的请求。你可以创徏一个ActionServlet的子c,当应用启动,关闭Q每个请求的时候做一些特定的事情。但是在l承ActionServletcM前,你应该尽量创Z个PlugIn或RequestProcessor去解决你的问题。在Servlet1.1之前QTiles框架是基于ActionServlet来修饰生成的响应。但是从1.1之后Q它开始用TilesRequestProcessorcR?BR>
  ȝ

  军_开发你自己的MVC框架是一个非常大的决定,你必要考虑开发和l护框架代码所p的时间和资源。Struts是一个非常强大和E_的框Ӟ你可以修改它来满你l大多数的业务需求?BR>
  但另一斚wQ也不要草率地做出扩展Struts的决定。如果你在RequestProcessor中写了一些性能比较低的代码Q它会在每ơ请求时执行Q因而降低你整个应用的效率。而且q是有一些情况,开发自qMVC框架要比扩展Struts好?BR>

Victor 2005-09-14 13:16 发表评论
]]>Hibernate+Spring+Struts扩展Struts 2(? http://www.tkk7.com/Victor/articles/13011.htmlVictorVictorWed, 14 Sep 2005 05:07:00 GMThttp://www.tkk7.com/Victor/articles/13011.htmlhttp://www.tkk7.com/Victor/comments/13011.htmlhttp://www.tkk7.com/Victor/articles/13011.html#Feedback0http://www.tkk7.com/Victor/comments/commentRss/13011.htmlhttp://www.tkk7.com/Victor/services/trackbacks/13011.htmlRequest是如何被处理?BR>
  ActionServlet是Struts框架中唯一的ServletQ它负责处理所有request。无Z时接收到一个requestQ它都会先尝试ؓ当前的requestL一个sub-application。一旦一个sub-application被找刎ͼActionServlet׃为那个sub-application创徏一个RequestProcessor对象Q调用这个对象的process()Ҏq把HttpServletRequest和HttpServletResponse对象传入?BR>
  RequestProcessor.process()是大部分request被处理的地方。process()Ҏ使用了Template Method模式实现Q其中有很多独立的方法来执行h处理的每一步骤Q这些方法将会在processҎ中依ơ被调用。比如,会有一个独立的Ҏ用来L当前request对应的ActionFormc,一个方法来查当前用h否有执行action mapping所必须的权限。这些给与我们极大的灉|性。在发布的Struts包中有一个RequestProcessorcL供了h处理每一步骤的默认实现。这意味着你可以仅仅重写你感兴的ҎQ其它的使用默认的实现。D例来_默认地Struts调用request.isUserInRole()来检查用h否有权限执行当前的ActionMappingQ这时如果你想通过查询数据库来实现Q你所要做的就是重写processRoles()ҎQ通过查询出的用户是否拥有必须的权限来q回true或false?BR>
  首先我们会看到~省情况下,process()Ҏ是如何实现的Q然后我会详细解释默认的RequestProcessorcM的每一个方法,q样你就可以军_哪一部分是你惌改变的?BR>
public void process(HttpServletRequest request,HttpServletResponse response)
throws IOException, ServletException {
 // Wrap multipart requests with a special wrapper
 request = processMultipart(request);
 // Identify the path component we will
 // use to select a mapping
 String path = processPath(request, response);
 if (path == null) {
  return;
 }
 if (log.isDebugEnabled()) {
  log.debug("Processing a '" + request.getMethod() + "' for path '" + path + "'");
 }
 // Select a Locale for the current user if requested
 processLocale(request, response);
 // Set the content type and no-caching headers
 // if requested
 processContent(request, response);
 processNoCache(request, response);
 // General purpose preprocessing hook
 if (!processPreprocess(request, response)) {
  return;
 }
 // Identify the mapping for this request
 ActionMapping mapping =
 processMapping(request, response, path);
 if (mapping == null) {
  return;
 }
 // Check for any role required to perform this action
 if (!processRoles(request, response, mapping)) {
  return;
 }
 // Process any ActionForm bean related to this request
 ActionForm form = processActionForm(request, response, mapping);
 processPopulate(request, response, form, mapping);
 if (!processValidate(request, response, form, mapping)) {
  return;
}
// Process a forward or include specified by this mapping
if (!processForward(request, response, mapping)) {
 return;
}
if (!processInclude(request, response, mapping)) {
 return;
}
// Create or acquire the Action instance to
// process this request
Action action =
processActionCreate(request, response, mapping);
if (action == null) {
 return;
}
// Call the Action instance itself
ActionForward forward = processActionPerform(request, response,action, form, mapping);
// Process the returned ActionForward instance
processForwardConfig(request, response, forward);
}

  1、processMutipart()Q在q个Ҏ中,Struts会drequest来检查request的contentType是否是multipart/form-data。如果是的话Q将会解析requestq且之包装到HttpServletRequest中。当你创Z一个HTML FORM用来提交数据Q那么request的contentType默认是application/x-www-form-urlencoded。但是如果你的form使用了filecd的input控g允许用户上传文g的话Q你必dcontentType改ؓmultipart/form-data。如果是q样的情况,你就不能再通过getParameter()来获取用h交的数据Q你必须request作ؓ一个InputStream来读取,q且自己解析它来获得参数倹{?BR>
  2、processPath()Q在q个Ҏ中,Struts会drequest的URIQ来定路径元素Q这个元素是用来获取ActionMappint元素?BR>
  3、processLocale()Q在q个Ҏ中,Struts会为当前request取得LocaleQ如果配|过的话Q还可以这个对象作为HttpSession中org.apache.struts.action.LOCALE属性的D保存。作个方法的副作用,HttpSession会被创建,如果你不惛_建的话,你可以在ControllerConfig中将locale属性设为falseQ在struts-config.xml中象如下q样Q?BR>
QcontrollerQ?BR> Qset-property property="locale" value="false"/Q?BR>Q?controllerQ?/TD>

  4、processContent()Q通过调用response.setContentType()来ؓresponse讄contentType。这个方法首先会试从struts-config.xml配置中得到contentType。缺省情况下使用text/html。如果想覆盖它,可以象如下这P

QcontrollerQ?BR>Qset-property property="contentType" value="text/plain"/Q?BR>Q?controllerQ?/TD>

  5、processNoCache()Q如果配|是no-cacheQStruts会为每个response讄下面三个headersQ?BR>
requested in struts config.xml
response.setHeader("Pragma", "No-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 1);

  如果你想讄no-cache headerQ在struts-config.xml中加入下面信息:

QcontrollerQ?BR>Qset-property property="noCache" value="true"/Q?BR>Q?controllerQ?

  6、processPreprocess()Q这个方法ؓ预处理提供一个hookQ可以在子类中覆盖它。它的缺省实现没有作M事情QLq回true。返回false的话会l止当前h的处理?BR>
  7、processMapping()Q这个方法将会用路径信息得到一个ActionMapping对象。也是struts-config.xml文g中的QactionQ元素:

Qaction path="/newcontact" type="com.sample.NewContactAction" name="newContactForm" scope="request"Q?BR>Qforward name="sucess" path="/sucessPage.do"/Q?BR>Qforward name="failure" path="/failurePage.do"/Q?BR>Q?actionQ?/TD>

  ActionMapping元素包含了Actioncȝ名称和处理请求用的ActionForm{等信息。它q包含当前ActionMapping配置的ActionForwards信息?BR>
  8、processRoles()QStruts web应用提供了一个授权方案。也是_一旦一个用L入了容器Qstruts的processRoles()Ҏ会通过调用request.isUserInRole()Q来查他是否有必ȝ角色来运行一个给定的ActionMapping?BR>
Qaction path="/addUser" roles="administrator"/Q?

  假设你有一个AddUserActionq且你只惌administrator能够增加新的user。你所要做的就是给你的AddUserAction元素增加一个role属性,q个属性的gؓadministrator。这P在运行AddUserAction之前Q这个方法会保用户拥有administraotr的角艌Ӏ?BR>
  9、processActionForm()Q每一个ActionMapping都一个相应的ActionFormcR当Struts处理一个ActionMapping的时候,它将会从QactionQ元素的name属性中扑և对应的ActionFormcȝ名称?BR>
Qform-bean name="newContactForm" type="org.apache.struts.action.DynaActionForm"Q?BR> Qform-property name="firstName" type="java.lang.String"/Q?BR> Qform-property name="lastName" type="java.lang.String"/Q?BR>Q?form-beanQ?/TD>

  在我们的例子中,它会先在request scope中检查是否有一个org.apache.struts.action.DynaActionFormcȝ对象存在。如果有它将会用这个对象,如果没有它将会创Z个新的对象ƈ把它讄在request scope?BR>
  10、processPopulate()Q在q个Ҏ中,Struts会用相匚w的request参数装配ActionForm的实例变量?BR>
  11、processValidate()QStruts会调用你的ActionFormcȝvalidateҎ。如果你从validate()q回ActionErrorsQ它会user重定向到QactionQ元素的input属性指定的面?BR>
  12、processForward()和processInclude()Q在q些Ҏ中,Struts会查<actionQ元素的forward或include属性,如果扑ֈ了,会把forward或includeh攄到配|的面中?BR>
Qaction forward="/Login.jsp" path="/loginInput"/Q?BR>Qaction include="/Login.jsp" path="/loginInput"/Q?/TD>

  你可以从q些Ҏ的名字上猜测它们的不同:processForward()最l调用RequestDispatcher.forward()Q而processInclude()调用RequestDispatcher.include()。如果你同时配置了forward和include属性,它将会L调用forwardQ因为forward先被处理?BR>
  13、processActionCreate()Q这个方法从QactionQ元素的type属性中获取获得Actioncȝ名字q且创徏q回它的实例。在我们的例子中Q它会创徏一个com.sample.NewContactActioncȝ实例?BR>
  14、processActionPerform()Q这个方法调用你的Actioncȝexcute()ҎQ你的业务逻辑也就是在excuteҎ中?BR>
  15、processForwardConfig()Q你的Actioncȝexcute()Ҏ会q回一个ActionForward对象Q这个对象将指出哪个面是显C给用户的页面。因此,Struts会为那个页面创Z个RequestDispatcherQƈ且调用RequestDispatcher.forward()?BR>
  上面的列表说明了默认的RequestProcessor实现在处理请求时每一步作的工作,以及执行的顺序。正如你所看到的,RequestProcessor是非常灵zȝQ允怽通过讄QcontrollerQ元素的属性来配置它。D例来_如果你的应用准备生成XML内容来代替HTMLQ你可以通过讄controller元素的属性来通知Strutsq些情况?BR>

Victor 2005-09-14 13:07 发表评论
]]>Hibernate+Spring+Struts扩展Struts 1(?http://www.tkk7.com/Victor/articles/13010.htmlVictorVictorWed, 14 Sep 2005 05:06:00 GMThttp://www.tkk7.com/Victor/articles/13010.htmlhttp://www.tkk7.com/Victor/comments/13010.htmlhttp://www.tkk7.com/Victor/articles/13010.html#Feedback0http://www.tkk7.com/Victor/comments/commentRss/13010.htmlhttp://www.tkk7.com/Victor/services/trackbacks/13010.html
  Struts不仅仅是一个强大的框架Q同时它也是可扩展的。你可以以三U方式来扩展Struts?BR>
  1、PlugInQ如果你惛_application startup或shutdown的时候做一些业务逻辑的话Q那创Z自己的PlugIncR?BR>
  2、RequestProcessorQ如果你惛_h被处理的q程中某个时d一些业务逻辑的话Q那么创Z自己的RequestProcessorcR比如说Q在每次h执行之前Q你可以扩展RequestProcessor来检查用h否登陆了以及他是否有权限L行某个特定的action?BR>
  3、ActionServletQ如果你惛_application startup和shutdown的时候以及请求被处理的时候做某些业务逻辑Q你也可以扩张ActionServletcR不q你应当在PlugIn和RequestProcessor都不能解决你的需求的时候来使用ActionServlet?BR>
  在这文章中Q我们将使用一个Struts应用的示例来C如何使用q三U方式来扩展Struts。示例程序的代码可以从http://www.onjava.com/onjava/2004/11/10/examples/sample1.zip下蝲。两个扩展Struts成功的范例是Struts自n的Validation和Tiles框架?BR>
  我们假设你已l比较熟悉Struts框架q且知道如何使用它创Z个简单的应用。如果你想知道更多关于Struts的内容,请参考官方主c?BR>
  PlugIn
 
  PlugIn是一个接口,你可以创Z个实现该接口的类Q当application startup或shutdown的时候做些事情?BR>
  比方_我创Z一个用Hibernate作ؓ持久层的web应用Q我惛_应用启动的时候就初始化HibernateQ这样子当我的web应用受到W一个请求的时候,Hibernate已l是配置好的q且可用的。同时我们想当application关闭的时候关闭Hibernate。我们可以用一个Hibernate PlugIn来实现这个需求,通过如下的两步:

  1、创Z个类实现了PlugIn接口Q?BR>
public class HibernatePlugIn implements PlugIn{
 private String configFile;
 // This method will be called at application shutdown time
 public void destroy() {
  System.out.println("Entering HibernatePlugIn.destroy()");
  //Put hibernate cleanup code here
  System.out.println("Exiting HibernatePlugIn.destroy()");
 }
 //This method will be called at application startup time
 public void init(ActionServlet actionServlet, ModuleConfig config)
  throws ServletException {
   System.out.println("Entering HibernatePlugIn.init()");
   System.out.println("value of init parameter " +
   getConfigFile());
   System.out.println("Exiting HibernatePlugIn.init()");
  }
 public String getConfigFile() {
  return name;
 }
 public void setConfigFile(String string) {
  configFile = string;
 }
}

  实现PlugIn接口的类必须完成两个ҎQinit()和destroy()。当application startup的时候init()Ҏ被调用,当shutdown的时候destroy()Ҏ被调用。Strutsq允许给你的PlugIncM递初始化参数。ؓ了传递参敎ͼ你必dPlugIncM为每一个参数创建JavaBean式的setterҎ。在我们的HibernatePlugIncMQ我会把configFile的name作ؓ参数传进去,而不是硬~码到程序中?BR>
  2、在struts-config.xml中添加如下的代码来通告Struts有新的PlugInQ?BR>
Qstruts-configQ?BR> ...
 Q?-- Message Resources --Q?BR> Qmessage-resources parameter= "sample1.resources.ApplicationResources"/Q?BR>
 Q?-- Declare your plugins --Q?BR> Qplug-in className="com.sample.util.HibernatePlugIn"Q?BR>  Qset-property property="configFile" value="/hibernate.cfg.xml"/Q?BR> Q?plug-inQ?BR>Q?struts-configQ?/TD>

  属性className是实CPlugIn接口的类的全限定名。对于每一个初始化参数Q可以用<set-propertyQ元素传递参数。在我们的例子中Q我要把config文g的名字传q去Q所以用了一个带有配|文件\径的Qset-propertyQ?BR>
  Struts的Tiles和Validator框架都用PlugIn来读取配|文件进行初始化。另外两件PlugIn可以帮你做到的事情是Q?BR>
  ·如果你的application依赖于某些配|文Ӟ那么你可以在PlugIncM查它们是否可用,如果不可用的话抛Z个ServletExceptionQ这样就可以使ActionServlet变ؓ不可用?BR>
  ·PlugIn接口的init()Ҏ是你可以改变ModuleConfig的最后机会,ModuleConfig是一l静态配|信息的集合Q用来描q基于Struts模块。Struts会在所有PlugIn处理完后释放ModuleConfig?BR>

Victor 2005-09-14 13:06 发表评论
]]>
log4j--新的日志操作ҎQ{Q?http://www.tkk7.com/Victor/articles/10609.htmlVictorVictorSun, 21 Aug 2005 02:37:00 GMThttp://www.tkk7.com/Victor/articles/10609.htmlhttp://www.tkk7.com/Victor/comments/10609.htmlhttp://www.tkk7.com/Victor/articles/10609.html#Feedback0http://www.tkk7.com/Victor/comments/commentRss/10609.htmlhttp://www.tkk7.com/Victor/services/trackbacks/10609.html1.1准备工作
 一。Tomcat已正配|与使用?BR> 二。Y件下载:log4j------::URL::http://www.apache.org/dist/jakarta/log4j/jakarta-log4j-1.2.8.zip

 1.2. Log4j?BR> 在强调可重用lg开发的今天Q除了自׃头到ּ发一个可重用的日志操作类外,Apache为我们提供了一个强有力的日志操作包-Log4j?/P>

 Log4j是Apache的一个开放源代码目Q通过使用Log4jQ我们可以控制日志信息输送的目的地是控制台、文件、GUIlg、甚x套接口服务器、NT的事件记录器、UNIX Syslog守护q程{;我们也可以控制每一条日志的输出格式Q通过定义每一条日志信息的U别Q我们能够更加细致地控制日志的生成过E。最令h感兴的是Q这些可以通过一个配|文件来灉|地进行配|,而不需要修改应用的代码?/P>

 此外Q通过Log4j其他语言接口Q您可以在C、C++?Net、PL/SQLE序中用Log4jQ其语法和用法与在JavaE序中一P使得多语a分布式系l得C个统一一致的日志lg模块。而且Q通过使用各种W三Ҏ展,您可以很方便地将Log4j集成到J2EE、JINI甚至是SNMP应用中。本文介l的Log4j版本?.2.8Q怎样通过一个配|文件来灉|地进行配|,主要的应用^台是Tomcat4.

 1.3。Log4j的配|?/P>

 首先到jakarta下蝲一个log4j的组件。把jakarta-log4j-1.2.8\dist\lib下的log4j-1.2.8.jar文gcopy到classpath指定的目录下Q可以是Tomcat的common\lib目录下,也可以是你需要用到log4j的application下的lib目录?/P>

 1.4在Application目录下的web.xml文g加入以后代码

 <servlet>
 <servlet-name>log4j</servlet-name>
 <servlet-class>com.apache.jakarta.log4j.Log4jInit</servlet-class>
 <init-param>
 <param-name>log4j</param-name>
 <param-value>/WEB-INF/log4j.properties</param-value>
 </init-param>
 <load-on-startup>1</load-on-startup>
 </servlet>

 q段代码的意思是_在Tomcat启动时加载com.apache.jakarta.log4j.Log4jInitq个名叫Log4jInit.classq个cL件。其中Log4jInit.class的源代码如下

 package com.apache.jakarta.log4j;
 import org.apache.log4j.PropertyConfigurator;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 public class Log4jInit extends HttpServlet {
 public void init() {
 String prefix = getServletContext().getRealPath("/";
 String file = getInitParameter("log4j";
 // if the log4j-init-file is not set, then no point in trying
 System.out.println("................log4j start";
 if(file != null) {
 PropertyConfigurator.configure(prefix+file);
 }
 }
 public void doGet(HttpServletRequest req, HttpServletResponse res) {
 }
 }
 q段代码很简单,可以看出Q在加蝲的过E中Q程序会d/WEB-INF/log4j.propertiesq个文g
 q个文g是本文的重点,也就是log4j的配|文件?/P>

 # Set root logger level to DEBUG and its only appender to A1 
 #log4j中有五logger 
 #FATAL 0 
 #ERROR 3
 #WARN 4 
 #INFO 6 
 #DEBUG 7 
 #配置根LoggerQ其语法为:
 #log4j.rootLogger = [ level ] , appenderName, appenderName, ?BR> log4j.rootLogger=INFO, A1 ,R
 #q一句设|以为着所有的log都输?BR> #如果为log4j.rootLogger=WARN, 则意味着只有WARN,ERROR,FATAL
 #被输出,DEBUG,INFO被屏蔽?
 # A1 is set to be a ConsoleAppender. 
 #log4j中Appender有几层如控制台、文件、GUIlg、甚x套接口服务器、NT的事件记录器、UNIX Syslog守护q程{?BR> #ConsoleAppender输出到控制台 
 log4j.appender.A1=org.apache.log4j.ConsoleAppender 
 # A1 使用的输出布局Q其中log4j提供4U布局. org.apache.log4j.HTMLLayoutQ以HTML表格形式布局Q?BR> #org.apche.log4j.PatternLayoutQ可以灵zd指定布局模式Q,
 #org.apache.log4j.SimpleLayoutQ包含日志信息的U别和信息字W串Q,
 #org.apache.log4j.TTCCLayoutQ包含日志生的旉、线E、类别等{信息)
 log4j.appender.A1.layout=org.apache.log4j.PatternLayout 
 #灉|定义输出格式 具体查看log4j javadoc org.apache.log4j.PatternLayout 
 #d 旉 .... 
 log4j.appender.A1.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss} [%c]-[%p] %m%n 
 #R 输出到文?nbsp;RollingFileAppender的扩展,可以提供一U日志的备䆾功能?BR> log4j.appender.R=org.apache.log4j.RollingFileAppender 
 #日志文g的名U?BR> log4j.appender.R.File=log4j.log 
 #日志文g的大?BR> log4j.appender.R.MaxFileSize=100KB 
 # 保存一个备份文?BR> log4j.appender.R.MaxBackupIndex=1 
 log4j.appender.R.layout=org.apache.log4j.TTCCLayout
 #log4j.appender.R.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss} [%c]-[%p] %m%n
 配置以这里就差不多了Q如果你x深入了解配置文g的各个细节,可以L看docs。还有,在文章的最后面我们提供配置文g中一些主要的语法。下面我们来看看怎样在程序中使用log4j.
 1.4 Log4j的用?BR> 使用Log4jQ第一步就是获取日志记录器Q这个记录器负责控制日志信息。其语法为:
 public static Logger getLogger( String name)Q?BR> 必须在用前要把q个cd?BR> import org.apache.log4j.Logger;
 name一般是cL件的名字Q如下:
 static Logger logger = Logger.getLogger ("".class.getName () ) ;
 您就可以LC用不同优先别的日志记录语句插入到您惌录日志的M地方Q其语法如下Q?BR> logger.debug ( Object message ) ;
 logger.info ( Object message ) ;
 logger.warn ( Object message) ;
 logger.error ( Object message ) ;
 Z么这里要分别的呢?试想一下,我们在写E序的时候,Z调试E序Q会在很多会出错的地方加入大量的logger.info();信息。当然程序调试完毕,我们不需要这些输Z息了Q那怎么办呢Q以前的做法是把每个E序中的logger.info删除Q但q是不现实的Q如果程序不大还可以Q但如果E序很多Q做q些事情很烦h了。但因ؓlog4j分别了Q当我们不需要输栯试时用到的log.info()Ӟ我们可以把输出的U别调高Q如调到warn,或errorU别Q这样infoU别及以下的U别׃会出输出了,是不是很方便的呢Q?BR> 其实除了q种使用方式Qlog4jq有其它的用方面,不需要配|文Ӟ直接在程序中定义输入出别,层次{信息,如果要了解这Ҏ的用,可以参考文档?BR> 1.5。附注:
 以下是配|文件的一些重要的语法
 定义配置文g
 其实您也可以完全不用配|文Ӟ而是在代码中配置Log4j环境。但是,使用配置文g您的应用E序更加灉|?BR> Log4j支持两种配置文g格式Q一U是XML格式的文Ӟ一U是JavaҎ文Ӟ?|。下面我们介l用JavaҎ文件做为配|文件的ҎQ?BR> 配置根LoggerQ其语法为:
 log4j.rootLogger = [ level ] , appenderName, appenderName, ?BR> 其中Qlevel 是日志记录的优先U,分ؓOFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL或者您定义的别。Log4j只用四个别,优先U从高到低分别是ERROR、WARN、INFO、DEBUG。通过在这里定义的U别Q您可以控制到应用程序中相应U别的日志信息的开兟뀂比如在q里定义了INFOU别Q则应用E序中所有DEBUGU别的日志信息将不被打印出来?BR> appenderName是指定日志信息输出到哪个地斏V您可以同时指定多个输出目的地?BR> 配置日志信息输出目的地AppenderQ其语法?BR> log4j.appender.appenderName = fully.qualified.name.of.appender.class
 log4j.appender.appenderName.option1 = value1
 ?BR> log4j.appender.appenderName.option = valueN
 其中QLog4j提供的appender有以下几U:
 org.apache.log4j.ConsoleAppenderQ控制台Q,
 org.apache.log4j.FileAppenderQ文ӞQ?BR> org.apache.log4j.DailyRollingFileAppenderQ每天生一个日志文ӞQorg.apache.log4j.RollingFileAppenderQ文件大到达指定尺寸的时候生一个新的文ӞQ?BR> org.apache.log4j.WriterAppenderQ将日志信息以流格式发送到L指定的地方)
 配置日志信息的格式(布局Q,其语法ؓQ?BR> log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class
 log4j.appender.appenderName.layout.option1 = value1
 ?BR> log4j.appender.appenderName.layout.option = valueN
 其中QLog4j提供的layout有以下几U:
 org.apache.log4j.HTMLLayoutQ以HTML表格形式布局Q,
 org.apache.log4j.PatternLayoutQ可以灵zd指定布局模式Q,
 org.apache.log4j.SimpleLayoutQ包含日志信息的U别和信息字W串Q,
 org.apache.log4j.TTCCLayoutQ包含日志生的旉、线E、类别等{信息)



本文引用通告地址Q?http://blog.csdn.net/zlfh/services/trackbacks/459662.aspx



Victor 2005-08-21 10:37 发表评论
]]>
վ֩ģ壺 þþƷƷ޾Ʒ| ޾ƷҺһ| ߹ۿav| ƷƵ| һþ| ˳վ߹ۿ| ƷþƵ| Ʒþ8xѹۿ| ij˾þþþӰԺѹۿ | ѹۿëƬվ| þþžAVѾƷ| eeussӰԺsscom| ˳ɳƵ | һ| ɫþþ99Ʒ91| պɫѹۿ| þҹƵ| 131ŮëƬ| ĻƵ| ձһѹۿ | avҹƬƷվ | һVR| AëƬ| ѹۿ| һĻ| ѰƵ̫ˬ| ޹ۺ߾Ʒ| ձ˵ɫwwwһ| ޾ƷպAV | þƵƷ| ҰĻƵ| þþþѿӰƬ| þóѴƬ| Ů߾Ʒѹۿ| ׾ƷŮ| 츾avһ | 9420ѸƵ| þþþƷ2019ѹۿ| ֻˬִ̼| AVɫ| ޳AƬ߹ۿ3D|