webber

  BlogJava :: 首页 :: 联系 :: 聚合  :: 管理
  11 Posts :: 2 Stories :: 3 Comments :: 0 Trackbacks

2010年2月10日 #

在Java的世界里,无论类还是各种数据,其结构的处理是整个程序的逻辑以及性能的关键。由于本人接触了一个有关性能与逻辑同时并存的问题,于是就开始研究这方面的问题。找遍了大大小小的论坛,也把《Java 虚拟机规范》,《apress,.java.collections.(2001),.bm.ocr.6.0.shareconnector》,和《Thinking in Java》翻了也找不到很好的答案,于是一气之下把JDK的 src 解压出来研究,扩然开朗,遂写此文,跟大家分享感受和顺便验证我理解还有没有漏洞。 这里就拿HashMap来研究吧。



  HashMap可谓JDK的一大实用工具,把各个Object映射起来,实现了“键--值”对应的快速存取。但实际里面做了些什么呢?

  在这之前,先介绍一下负载因子和容量的属性。大家都知道其实一个 HashMap 的实际容量就 因子*容量,其默认值是 16×0.75=12; 这个很重要,对效率很一定影响!当存入HashMap的对象超过这个容量时,HashMap 就会重新构造存取表。这就是一个大问题,我后面慢慢介绍,反正,如果你已经知道你大概要存放多少个对象,最好设为该实际容量的能接受的数字。

  两个关键的方法,put和get:

  先有这样一个概念,HashMap是声明了 Map,Cloneable, Serializable 接口,和继承了 AbstractMap 类,里面的 Iterator 其实主要都是其内部类HashIterator 和其他几个 iterator 类实现,当然还有一个很重要的继承了Map.Entry 的 Entry 内部类,由于大家都有源代码,大家有兴趣可以看看这部分,我主要想说明的是 Entry 内部类。它包含了hash,value,key 和next 这四个属性,很重要。put的源码如下

public Object put(Object key, Object value) {
Object k = maskNull(key);

  这个就是判断键值是否为空,并不很深奥,其实如果为空,它会返回一个static Object 作为键值,这就是为什么HashMap允许空键值的原因。

int hash = hash(k);
int i = indexFor(hash, table.length);

  这连续的两步就是 HashMap 最牛的地方!研究完我都汗颜了,其中 hash 就是通过 key 这个Object的 hashcode 进行 hash,然后通过 indexFor 获得在Object table的索引值。

  table???不要惊讶,其实HashMap也神不到哪里去,它就是用 table 来放的。最牛的就是用 hash 能正确的返回索引。其中的hash算法,我跟JDK的作者 Doug 联系过,他建议我看看《The art of programing vol3》可恨的是,我之前就一直在找,我都找不到,他这样一提,我就更加急了,可惜口袋空空啊!!!

  不知道大家有没有留意 put 其实是一个有返回的方法,它会把相同键值的 put 覆盖掉并返回旧的值!如下方法彻底说明了 HashMap 的结构,其实就是一个表加上在相应位置的Entry的链表:

for (Entry e = table[i]; e != null; e = e.next) {
 if (e.hash == hash && eq(k, e.key)) {
  Object oldvalue = e.value;
  e.value = value; //把新的值赋予给对应键值。
  e.recordAccess(this); //空方法,留待实现
  return oldvalue; //返回相同键值的对应的旧的值。
 }
}
modCount++; //结构性更改的次数
addEntry(hash, k, value, i); //添加新元素,关键所在!
return null; //没有相同的键值返回
}

  我们把关键的方法拿出来分析:

void addEntry(int hash, Object key, Object value, int bucketIndex) {
table[bucketIndex] = new Entry(hash, key, value, table[bucketIndex]);

  因为 hash 的算法有可能令不同的键值有相同的hash码并有相同的table索引,如:key=“33”和key=Object g的hash都是-8901334,那它经过indexfor之后的索引一定都为i,这样在new的时候这个Entry的next就会指向这个原本的table[i],再有下一个也如此,形成一个链表,和put的循环对定e.next获得旧的值。到这里,HashMap的结构,大家也十分明白了吧?

if (size++ >= threshold) //这个threshold就是能实际容纳的量
resize(2 * table.length); //超出这个容量就会将Object table重构

  所谓的重构也不神,就是建一个两倍大的table(我在别的论坛上看到有人说是两倍加1,把我骗了),然后再一个个indexfor进去!注意!!这就是效率!!如果你能让你的HashMap不需要重构那么多次,效率会大大提高!

  说到这里也差不多了,get比put简单得多,大家,了解put,get也差不了多少了。对于collections我是认为,它是适合广泛的,当不完全适合特有的,如果大家的程序需要特殊的用途,自己写吧,其实很简单。(作者是这样跟我说的,他还建议我用LinkedHashMap,我看了源码以后发现,LinkHashMap其实就是继承HashMap的,然后override相应的方法,有兴趣的同人,自己looklook)建个 Object table,写相应的算法,就ok啦。

  举个例子吧,像 Vector,list 啊什么的其实都很简单,最多就多了的同步的声明,其实如果要实现像Vector那种,插入,删除不多的,可以用一个Object table来实现,按索引存取,添加等。

  如果插入,删除比较多的,可以建两个Object table,然后每个元素用含有next结构的,一个table,如果要插入到i,但是i已经有元素,用next连起来,然后size++,并在另一个table记录其位置

HashMap用法

 

 

package hashmap;

import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;

public class HashMap1 {
//初始化
private void init(Map map,String kind)
{
   if(map != null)
   {
    for(int i=1; i<6; i++)
    {
     map.put(String.valueOf(i),kind+i);
    }
   }
}

//结果输出
private void outPut(Map map)
{
   if(map != null)
   {
    Object key    = null;
    Object value = null;
    Iterator iterater = map.keySet().iterator();
    while(iterater.hasNext())
    {
     key = iterater.next();
     value = map.get(key);
     System.out.print(key+": "+value+"\t");
    }
    System.out.println("\n");
   }
}
public static void main(String args[])
{
    HashMap hashmap = new HashMap();
    hashmap.put("x", "1");
    hashmap.put("u", "2");
    hashmap.put("z", "3");
    hashmap.put("h", "4");
    hashmap.put("a", "5");
    hashmap.put("o", "6");
    hashmap.put("g", "7");
    hashmap.put("u", "8");
    hashmap.put("a", "9");
    hashmap.put("n", "10");
    hashmap.put("g", "11");

     Object key    = null;
     Object value = null;
     Iterator iterater = hashmap.keySet().iterator();
     while(iterater.hasNext())
     {
      key = iterater.next();
      value = hashmap.get(key);
      System.out.print(key+": "+value+"\t");
     }
     System.out.println("\n");

}
//声明HashMap对象
private void setHashMap()
{
   HashMap hashMap = new HashMap();
   init(hashMap,"HashMap");
   hashMap.put(null,"键值为空");
   hashMap.put("值为空",null);
   System.out.println("这是HashMap对象的键与值:");
   outPut(hashMap);
}
     //声明Hashtable对象
private void setHashtable(){
   Hashtable hashtable = new Hashtable();
   init(hashtable,"Hashtable");
   //hashtable.put(null,"键值为空"); Hashtable不允许键或值为null;
   //hashtable.put("值为空",null);
   System.out.println("这是Hashtable对象的键与值:");
   outPut(hashtable);
}
     //声明LinkedHashMap对象
private void setLinkedHashMap(){
   LinkedHashMap linkedHashMap = new LinkedHashMap();
   init(linkedHashMap,"LinkedHashMap");
   linkedHashMap.put(null,"键值为空");
   linkedHashMap.put("值为空",null);
   System.out.println("这是LinkedHashMap对象的键与值:");
   outPut(linkedHashMap);
}
     //声明TreeMap对象
private void setTreeMap(){
   TreeMap treeMap = new TreeMap();
   //TreeMap treeMap = new TreeMap(new MySort());//按自定义的方式排序
   init(treeMap,"TreeMap");
   treeMap.put("0", "后插入的值");
   //treeMap.put(null,"键值为空"); TreeMap不允许键或值为null
   //treeMap.put("值为空",null);
   System.out.println("这是TreeMap对象的键与值:");
   outPut(treeMap);
}
// public static void main(String[] args){
//   HashMapDemo tm = new HashMapDemo();
//   tm.setHashMap();
//   tm.setHashtable();
//   tm.setLinkedHashMap();
//   tm.setTreeMap();
//
//   Map hashMap = new HashMap();
//   hashMap.put(null, "键值为null");
//   hashMap.put("值为null", null);
//   System.out.println("新建HashMap对象元素的记录数是:"+hashMap.size());
//   hashMap.remove(null);
//   System.out.println("删除键值为null的HashMap对象元素的记录数是:"+hashMap.size());
// }

posted @ 2010-03-26 17:08 webber 阅读(595) | 评论 (0)编辑 收藏

 

第一种:
Map map = new HashMap();
Iterator iter = map.entrySet().iterator();
while (iter.hasNext()) {
    Map.Entry entry = (Map.Entry) iter.next();
    Object key = entry.getKey();
    Object val = entry.getValue();
}
效率高,以后一定要使用此种方式!
                                                                                    
第二种:
Map map = new HashMap();
Iterator iter = map.keySet().iterator();
while (iter.hasNext()) {
    Object key = iter.next();
    Object val = map.get(key);
}
效率低,以后尽量少使用!
HashMap的遍历有两种常用的方法,那就是使用keyset及entryset来进行遍历,但两者的遍历速度是有差别的,下面请看实例:
import java.util.*;
            public class HashMapTest {
            public static void main(String[] args) {
            HashMap< Integer,String> hashmap = new HashMap< Integer,String>();
            for (int i = 0; i <1000; i++ ) {
            hashmap.put(i, "thanks");
            }
            long bs = Calendar.getInstance().getTimeInMillis();
            Iterator iterator = hashmap.keySet().iterator();
            while (iterator.hasNext()) {
            System.out.print(hashmap.get(iterator.next()));
            }
            System.out.println();
            System.out.println(Calendar.getInstance().getTimeInMillis() - bs);
            listHashMap();
            }
            public static void listHashMap() {
            java.util.HashMap< Integer,String> hashmap = new java.util.HashMap< Integer,String>();
            for (int i = 0; i < 1000; i++ ) {
            hashmap.put(i, "thanks");
            }
            long bs = Calendar.getInstance().getTimeInMillis();
            Iterator< Map.Entry< Integer,String>> it = hashmap.entrySet().iterator();
            while (it.hasNext()) {
            Map.Entry< Integer,String> entry = it.next();
            // entry.getKey() 返回与此项对应的键
            // entry.getValue() 返回与此项对应的值
            System.out.print(entry.getValue());
            }
            System.out.println();
            System.out.println(Calendar.getInstance().getTimeInMillis() - bs);
            } 

对于keySet其实是遍历了2次,一次是转为iterator,一次就从hashmap中取出key所对于的value。而entryset只是遍历了第一次,他把key和value都放到了entry中,所以就快了。

注:Hashtable的遍历方法和以上的差不多!

posted @ 2010-03-26 16:57 webber 阅读(228) | 评论 (0)编辑 收藏

web.xml  
// 这里不需要配置字符过滤,网上有的例子加了,实际上
webwork.properties里设置如下就可以了页面也是GBK
webwork.locale=zh_CN
webwork.i18n.encoding=GBK
---------------------------
<!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>

     <context-param>
         <param-name>contextConfigLocation</param-name>
         <param-value>/WEB-INF/classes/applicationContext.xml</param-value>
     </context-param>

<listener>
   <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<listener>
   <listener-class>com.atlassian.xwork.ext.ResolverSetupServletContextListener</listener-class>
</listener>
     <!--
     <servlet>
     <servlet-name>context</servlet-name>
              <servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
              <load-on-startup>1</load-on-startup>
      </servlet>
      -->
     <servlet>
         <servlet-name>webwork</servlet-name>
         <servlet-class>com.opensymphony.webwork.dispatcher.ServletDispatcher</servlet-class>
         <load-on-startup>3</load-on-startup>
     </servlet>

<servlet>
   <servlet-name>freemarker</servlet-name>
   <servlet-class>com.opensymphony.webwork.views.freemarker.FreemarkerServlet</servlet-class>
   <load-on-startup>10</load-on-startup>
</servlet>

     <servlet-mapping>
         <servlet-name>webwork</servlet-name>
         <url-pattern>*.action</url-pattern>
     </servlet-mapping>

<servlet-mapping>
   <servlet-name>freemarker</servlet-name>
   <url-pattern>*.ftl</url-pattern>
</servlet-mapping>

     <welcome-file-list>
         <welcome-file>index.html</welcome-file>
     </welcome-file-list>

     <taglib>
         <taglib-uri>webwork</taglib-uri>
         <taglib-location>/WEB-INF/webwork.tld</taglib-location>
     </taglib>

</web-app>

---------------------------

xwork.xml

==================---------------------------------------------
<?xml version="1.0"?>
<!DOCTYPE xwork PUBLIC "-//OpenSymphony Group//XWork 1.0//EN" "http://www.opensymphony.com/xwork/xwork-1.0.dtd">

<xwork>

   <include file="webwork-default.xml"/>
     <package name="users" extends="webwork-default"
         externalReferenceResolver="com.atlassian.xwork.ext.SpringServletContextReferenceResolver">

         <interceptors>
             <interceptor name="reference-resolver" class="com.opensymphony.xwork.interceptor.ExternalReferencesInterceptor"/>
             <interceptor-stack name="myDefaultWebStack">
                 <interceptor-ref name="defaultStack"/>
                 <interceptor-ref name="reference-resolver"/>
                 <interceptor-ref name="model-driven"/>
        <interceptor-ref name="params"/>
             </interceptor-stack>
         </interceptors>

<default-interceptor-ref name="myDefaultWebStack"/>
         <action name="blogUser" class="com.jsblog.action.BlogUserAction">
    <external-ref name="baseDao">baseDaoTarget</external-ref>      //这里是把applicationContext里配置的DAO 注入action里 action里要有baseDao属性
    <result name="success">/add.htm</result>
   </action>
-------------------------------------------------------------------------

applicationContext.xml

---------------------------------------------------------------------------
<?xml version="1.0"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans default-autowire="no" default-dependency-check="none" default-lazy-init="false">
     <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
         <property name="driverClassName">
             <value>com.microsoft.jdbc.sqlserver.SQLServerDriver</value>
         </property>
         <property name="url">
             <value>jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=jsblog;SelectMethod=cursor</value>
         </property>
         <property name="username">
             <value>sa</value>
         </property>
         <property name="password">
             <value>jfy</value>
         </property>
     </bean>

     <bean id="sessionFactory"
           class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
         <property name="dataSource">
             <ref local="dataSource"/>
         </property>
         <property name="mappingResources">
             <list>
                 <value>com/jsblog/BlogUserForm.hbm.xml</value>
             </list>
         </property>
         <property name="hibernateProperties">
             <props>
                 <prop key="hibernate.dialect">
                     net.sf.hibernate.dialect.SQLServerDialect
                 </prop>
                 <prop key="hibernate.show_sql">true</prop>
             </props>
         </property>
     </bean>

     <bean id="transactionManager"
           class="org.springframework.orm.hibernate.HibernateTransactionManager">
         <property name="sessionFactory">
             <ref local="sessionFactory"/>
         </property>
     </bean>


     <bean id="baseDaoTarget" class="com.jsblog.dao.BlogUserDao">
         <property name="sessionFactory">
             <ref local="sessionFactory"/>
         </property>
     </bean>


</beans>
---------------------------------------------------------------------------

BlogUserDao.java
---------------------------------------------------------------------------
package com.jsblog.dao;

import org.springframework.orm.hibernate.support.HibernateDaoSupport;
import org.springframework.orm.hibernate.HibernateCallback;
import org.springframework.orm.hibernate.SessionFactoryUtils;
import com.jsblog.BlogUserForm;

import java.io.Serializable;
import java.util.List;

import net.sf.hibernate.HibernateException;
import net.sf.hibernate.Session;

public class BlogUserDao extends HibernateDaoSupport implements BaseDao {

     public void insert(BlogUserForm bloguser) {
         getHibernateTemplate().save(bloguser);
     }

}

posted @ 2010-03-15 10:10 webber 阅读(1062) | 评论 (0)编辑 收藏

 

基于webwork spring hibernate 项目的开发
这三者的结合,应该是java web编程最好的模式。

首先说明三者各自负责的职务:

1 hibernate 负责数据库的操作

2 spring 负责真正的业务操作

3 webwork 负责请求转交,并把spring的处理结果返回给用户


以往的开发中,很多人注重MVC模式。的确,这种模式把程序以面向对象的思想分成 了三个部分。但在web开发中,并不能单纯的运用此种模式:web开发的View是固定的(页面),而在引入hibernate后,model这一块也非常简单和清晰。就剩下control了,这是web开发的关键部分,现在流行的做法便是将control细分成两个部分:dispacher(转交器)和business object(处理业务逻辑的对象)。并将后者抽出接口,甚至和model共享接口,一边真正做到对dispacher隐藏逻辑实现。

而这种M-V-D-B(model-view-dispacher-business object)模式的实现有好多方式。比如一个bo(business object)对象的创建,你可以直接 new,也可以动态加载,采用工厂方法,抽象工厂。但最好的就是用spring容器。dispacher只管用接口就行了,具体类已经有spring的 AOP给注入了。

当然spring也可以很好地和hibernate结合,你可以采用DAO,也可以用spring的hibernate 模板。但这都不重要,因为你的业务对象已经和调用层彻底分开了,当业务层需要和hibernate打交道的时候,直接做个HibernateUtil也未尝不可呀。怎么做都已经不是关键。

下面就具体介绍spring webwork的结合方式。

在webwork 中的wiki doc中有三种结合方式(google查),我这里采用的最后一种--采用一个自动装配的拦截器com.opensymphony.xwork.spring.interceptor.ActionAutowiringInterceptor关键代码如下:


   ApplicationContext applicationContext = (ApplicationContext)ActionContext.getContext().getApplication().get(
          WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
        factory = new SpringObjectFactory();
        factory.setApplicationContext(getApplicationContext());
    Action bean = invocation.getAction();
    factory.autoWireBean(bean);
   
    ActionContext.getContext().put(APPLICATION_CONTEXT, context);

1、webwork、spring的集成
  (1)、开启spring的集成:
         首先将最新的spring的jar加到classpath中,然后在src目录下建立webwork.properties文件,文件只包含下面的内容
        webwork.objectFactory=spring
         这种情况下,所有的对象都至少会试图使用Spring来创建.如果它们不能被Spring创建,然后WebWork会自己创建对象.接下来,在
        web.xml打开Spring的Listener
           <listener>
             <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
           </listener>
        由于使用标准的Listener来集成Spring,它可以被配置来支持除了applicationContext.xml之外的配置文件.把下面的几行添加到
       web.xml会让Spring的ApplicationContext从所有匹配给定的规则的文件中初始化:

       <!-- Context Configuration locations for Spring XML files -->
        <context-param>
             <param-name>contextConfigLocation</param-name>
             <param-value>/WEB-INF/applicationContext-*.xml,classpath*:applicationContext   
       </context-param>
       根据需要配置相应的spring上下文文。
   (2)、在spring中初始化Action
         正常情况下,在xwork.xml里可以为每个action指定类.当你使用SpringObjectFactory时WebWork会请求Spring来
         创建action并按照缺省指定的自动装配行为来装配依赖的组件.SpringObjectFactory 也会设置所有的bean的后置处理程序
        (post processors)来完成类似对Action进行事务,安全等等方面的代理的事情.Spring可以不依赖外在的配置来自动确定.
         对于大多数的使用,这就是全部需要的了,用来配置action,设置它们获取服务和依赖组件.
         强烈推荐使用一种声明式的方法来让Spring知道为action提供什么.这包括让bean能够自动装配,无论是把Action里的
         依赖的属性命名为和Spring应该提供的Bean的名字一致(这允许基于名字的自动装配),或者使用by type方式的自动装配,也就是在注册到
        Spring的Bean中需要的类型仅拥有一个.也可以包括使用JDK5的标准来声明事务和安全需求,而不是必须在你的Spring配置里明确设置代理.
         如果能找到方法让Spring在没有任何明确的配置(在_applicationContext.xml_中)的情况下知道需要为action做什么,那么就不
         需要在两个地方维护这个配置了.
         当然,有时候可能想要Spring完全来管理bean.这是有实际意义的,例如,如果想要为bean设置更复杂的AOP或者Spring相关的技术,
         例如Acegi.为了达到这个目的,所有必须要做的事情就是在Spring的 applicationContext.xml 里配置bean,然后在 xwork.xml
         里改变你的WebWork action的类属性来使用在Spring里面定义的bean的名字,而不再使用类名. 
        xwork.xml文件也会改变action类的属性,最后留下的就像这样    
         
        <xwork>
       <!-- Include webwork defaults (from WebWork JAR). -->
         <include file="webwork-default.xml" />

       <!-- Configuration for the default package. -->
         <package name="default" extends="webwork-default">
           <action name="register" class="userAction" method="register">
              <result name="success">/pages/registerSuccess.jsp</result>
           </action>
         </package>
       </xwork>
        在applicationContext.xml 里定义了一个名字为 "userAction"的Spring的bean.注意cn.com.nawang.Action.UserAction不需要
        改变,因为它可能是自动装配的:
        <bean id="userAction" class="cn.com.nawang.action.UserAction" > 
             <property name="userService" ref="userService"/>
        </bean>
       注:bean中的id值必须与xwork.xml中对应的class值一致。
        
   2、 基于Hibernate3的原生API实现DAO
        Hibernate 3.0.1引入了一个新的特性:“带上下文环境的Session”。 这一特性使得Hibernate自身具备了每个事务绑定当前 Session 对象的功能。
         这与Spring中每个Hibernate的 Session 与事务同步的功能大致相同。
   (1)、 为Dao创建基类BaseDao
         public class BaseDao {
         private SessionFactory  sessionFactory;

         public void setSessionFactory(SessionFactory sessionFactory) {
          this.sessionFactory = sessionFactory;
         }
 
         public Session getSession(){
                 Session session = this.sessionFactory.getCurrentSession();
                 return session;
         }
         }
   (2)、在子类Dao中实现具体持久化操作
        public class UserDao extends BaseDao implements IUserDao {
        public void saveUser(User user) throws HibernateException {
              getSession().save(user);
        }     
        }
   (3)、在上下文中配置
        <bean id="baseDao" class="cn.com.nawang.dao.BaseDao">
           <property name="sessionFactory" ref="sessionFactory"/>
        </bean>
    
        <bean id="userDao" class="cn.com.nawang.dao.impl.UserDao" parent="baseDao"/>
       
        <bean id="userService" class="cn.com.nawang.service.impl.UserService">
           <property name="userDao" ref="userDao"/>
        </bean>
   
        <bean id="userAction" class="cn.com.nawang.action.UserAction" > 
          <property name="userService" ref="userService"/>
        </bean>    
     重启服务,在web页面上触发register的action,执行后,抛出下面的异常:
      Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
    google了下,大概明白了是因为没有配置了事务导致的错误。在配置事务之前,查看了以前的一个采用HibernateDaoSupport实现的项目,记得
     当时并不需要配置事务就可以正常运行。于是,让UserDao继承于HibernateDaoSupport,修改后的代码如下:
         public class UserDao extends BaseDao implements IUserDao {
        public void saveUser(User user) throws HibernateException {
              getHibernateTemplate().save(user);
        }     
        }
     接下去,修改spring上下文中的相关配置,
       <!--
        <bean id="baseDao" class="cn.com.nawang.dao.BaseDao">
           <property name="sessionFactory" ref="sessionFactory"/>
        </bean>-->
    
        <bean id="userDao" class="cn.com.nawang.dao.impl.UserDao">
           <property name="sessionFactory" ref="sessionFactory"/>
        </bean>
       
        <bean id="userService" class="cn.com.nawang.service.impl.UserService">
           <property name="userDao" ref="userDao"/>
        </bean>
   
        <bean id="userAction" class="cn.com.nawang.action.UserAction" > 
          <property name="userService" ref="userService"/>
        </bean> 
     保存修改后的,重启服务,再次触发register的action,用户信息成功保存。
    
     去掉HibernateDaoSupport的dao实现后,又换回基于hibernate3.0原生API的实现方式,根据之前google后的结果,给userService配置
     事务,拷贝了下之前项目中的配置,并做相应修改,修改后的内容如下:
       <bean id="baseTransaction"
       class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" abstract="true">
        <property name="transactionManager" ref="transactionManager"/>
        <property name="proxyTargetClass" value="true"/>
        <property name="transactionAttributes">
           <props>                
               <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
               <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
               <prop key="load*">PROPAGATION_REQUIRED,readOnly</prop>                
               <prop key="save*">PROPAGATION_REQUIRED</prop>                
               <prop key="add*">PROPAGATION_REQUIRED</prop>                
               <prop key="update*">PROPAGATION_REQUIRED</prop>                
               <prop key="delete*">PROPAGATION_REQUIRED</prop>            
           </props>       
        </property>  
    </bean>
   
    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory"/>
    </bean>      
   
    <bean id="baseDao" class="cn.com.nawang.dao.BaseDao">
        <property name="sessionFactory" ref="sessionFactory"/>
    </bean>
    
    <bean id="userDao" class="cn.com.nawang.dao.impl.UserDao" parent="baseDao"/>
   
    <bean id="userServiceTarget" class="cn.com.nawang.service.impl.UserService">
       <property name="userDao" ref="userDao"/>
    </bean>
   
    <bean id="userService" parent="baseTransaction">
       <property name="target" ref="userServiceTarget"/>
    </bean>
   
    <bean id="userAction" class="cn.com.nawang.action.UserAction" > 
       <property name="userService" ref="userService"/>
    </bean>
   
    保存修改内容,重启服务,重启中出现错误,查看了spring in action中的相关配置,发现baseTransaction这个bean的配置稍有不同,
    上面那个配置是参考springside的,当时那个项目赶,就直接拿过来用,也没出现问题,就不认真去考虑,现在拷贝到现有项目中,却出错了,
    于是先根据书上的介绍做相应修改,改后的内容如下:
       <bean id="baseTransaction"
      class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" lazy-init="true">
        <property name="transactionManager" ref="transactionManager"/>
        <property name="transactionAttributes">
           <props>                
               <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
               <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
               <prop key="load*">PROPAGATION_REQUIRED,readOnly</prop>                
               <prop key="save*">PROPAGATION_REQUIRED</prop>                              
               <prop key="update*">PROPAGATION_REQUIRED</prop>                
               <prop key="delete*">PROPAGATION_REQUIRED</prop>            
           </props>       
        </property>  
    </bean>
    去掉了<property name="proxyTargetClass" value="true"/>的配置,将abstract="true"改为lazy-init="true",保存修改
    重启服务,并再次触发register的action,一切如所愿。 

posted @ 2010-03-15 10:00 webber 阅读(723) | 评论 (0)编辑 收藏

虽然大多数web文档的顶部都有doctype声明,但很多人都没有注意它。它是在你新建一个文档时,由web创作软件草率处理的众多细节之一。

虽然doctype被许多人忽视,但在遵循标准的任何web文档中,它都是一项必需的元素。doctype会影响代码验证,并决定了浏览器最终如何显示你的web文档。


doctype的作用
doctype声明指出阅读程序应该用什么规则集来解释文档中的标记。在web文档的情况下,“阅读程序”通常是浏览器或者校验器这样的一个程序,“规则”则是w3c所发布的一个文档类型定义(dtd)中包含的规则。

每个dtd都包括一系列标记、attributes和properties,它们用于标记web文档的内容;此外还包括一些规则,它们规定了哪些标记能出现在其他哪些标记中。每个web建议标准(比如html 4 frameset和xhtml 1.0 transitional)都有自己的dtd。

假如文档中的标记不遵循doctype声明所指定的dtd,这个文档除了不能通过代码校验之外,还有可能无法在浏览器中正确显示。对于标记不一致的问题,浏览器相较于校验器来说更宽容。但是,不正确的doctype声明经常导致网页不正确显示,或者导致它们根本不能显示。


选择正确的doctype
为了获得正确的doctype声明,关键就是让dtd与文档所遵循的标准对应。例如,假定文档遵循的是xhtml 1.0 strict标准,文档的doctype声明就应该引用相应的dtd。另一方面,如果doctype声明指定的是xhtml dtd,但文档包含的是旧式风格的html标记,就是不恰当的;类似地,如果doctype声明指定的是html dtd,但文档包含的是xhtml 1.0 strict标记,同样是不恰当的。

有的时候,也可以根本不使用一个doctype声明。如果没有指定有效的doctype声明,大多数浏览器都会使用一个内建的默认dtd。在这种情况下,浏览器会用内建的dtd来试着显示你所指定的标记。对于一些临时性的、匆忙拼凑的文档(这种文档有许多),你确实可以考虑省略doctype声明,并接受浏览器的默认显示。

完全可以从头编写一个doctype声明,并让它指向自己选择的一个dtd。然而,由于大多数web文档都需要遵循由w3c发布的某个国际公认的web标准,所以那些文档通常都要包含以下标准doctype声明之一:

html 2:

<!doctype html public "-/ietf/dtd html 2.0/en">

html 3.2:

<!doctype html public "-/w3c/dtd html 3.2 final/en">

html 4.01 strict:

<!doctype html public "-/w3c/dtd html 4.01/en"
"http://www.w3.org/tr/html4/strict.dtd">

html 4.01 transitional:

<!doctype html public "-/w3c/dtd html 4.01 transitional/en"
"http://www.w3.org/tr/html4/loose.dtd">

html 4.01 frameset:

<!doctype html public "-/w3c/dtd html 4.01 frameset/en"
"http://www.w3.org/tr/html4/frameset.dtd">

xhtml 1.0 strict:

<!doctype html public "-/w3c/dtd xhtml 1.0 strict/en"
"http://www.w3.org/tr/xhtml1/dtd/xhtml1-strict.dtd">

xhtml 1.0 transitional:

<!doctype html public "-/w3c/dtd xhtml 1.0 transitional/en"
"http://www.w3.org/tr/xhtml1/dtd/xhtml1-transitional.dtd">

xhtml 1.0 frameset:

<!doctype html public "-/w3c/dtd xhtml 1.0 frameset/en"
"http://www.w3.org/tr/xhtml1/dtd/xhtml1-frameset.dtd">

xhtml 1.1:

<!doctype html public "-/w3c/dtd xhtml 1.1/en"
"http://www.w3.org/tr/xhtml11/dtd/xhtml11.dtd">

xhtml 1.1 plus mathml plus svg:

<!doctype html public
"-/w3c/dtd xhtml 1.1 plus mathml 2.0 plus svg 1.1/en"
"http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd">

除了上面列出的doctype声明,具有特殊要求的一些文档还使用了其他几种声明。

doctype声明通常是文档的第一行,要在<html>标记以及其他文档内容之前。注意,在xhtml文档中,doctype的前面偶尔会出现一条xml处理指令(也称为xml prolog):

<@xml version="1.0" encoding="utf-8"@>

为了确保网页正确显示和顺利通过验证,使用正确的doctype是关键。与内容相反的、不正确的或者形式错误的doctype是大量问题的罪魁祸首。在未来的专栏文章中,我还会具体解释如何诊断及纠正这些问题。

用dw设计网页时,新建一个文件,看代码最前面总要出现一个下面的东东,
<!doctype html public "-/w3c/dtd html 4.01 transitional/en"
"http://www.w3.org/tr/html4/loose.dtd">
这个是dw自动在网页文件页增加了dtd信息.可以删.
删除后,浏览器会使用的默认dtd.
posted @ 2010-02-11 10:49 webber 阅读(322) | 评论 (0)编辑 收藏

webwork2.0配置详解

首先下载WebWork2 的最新版本(http://www.opensymphony.com/webwork/)。

这里我们所谈及的WebWork,实际上是Webwork+XWork的总集,Webwork1.x 版本中,
整个框架采用了紧耦合的设计(类似Struts),而2.0 之后,Webwork被拆分为两个部分,
即Webwork 2.x +XWork 1.x,设计上的改良带来了系统灵活性上的极大提升。

本例的部署如下图所示

webwork能为我们做什么:
1.将Web页面中的输入元素封装为一个(请求)数据对象。
2.根据请求的不同,调度相应的逻辑处理单元,并将(请求)数据对象作为参数传入。
3.逻辑处理单元完成运算后,返回一个结果数据对象。
4.将结果数据对象中的数据与预先设计的表现层相融合并展现给用户。
首先来看登录界面:
index.jsp
<html>
<body>
<form action="/login.action">
<p align="center">
登录<br> </p>
用户名:<input type="text" name="model.username" /><br>
密 码 :<input type="password" name="model.password" /><br>
<p align="center"><input type="submit" value="提交" name="B1"/><input type="reset" value="重置" name="B2"/></p>
</form>
</body>
</html>
 这里的index.jsp实际上是由纯html 组成,非常简单,其中包含一个表单:
<form action="/login.action">
这表明其提交对象为/login.action . 表单中同时包含两个文本输入框,
<input type="text" name="model.username" />
<input type="password" name="model.password" />
可以看到,两个输入框的名称均以“model”开头,这是因为在这里我们采用了WebWork
中Model-Driven的Action驱动模式
当表单被提交之时,浏览器会以两个文本框的值作为参数,向Web 请求以/login.action命名的服务。
标准HTTP协议中并没有.action结尾的服务资源。我们需要在web.xml中加以设定:
……
<servlet>
<servlet-name>webwork</servlet-name>
<servlet-class>
com.opensymphony.webwork.dispatcher.ServletDispatcher
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>webwork</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>

 此后,所有以.action结尾的服务请求将由ServletDispatcher 接管。
ServletDispatcher 接受到Servlet Container 传递过来的请求,将进行一下几个动作:
1. 从请求的服务名(/login.action)中解析出对应的Action名称(login)
2. 遍历 HttpServletRequest、HttpSession、ServletContext 中的数据,并将其复制到
Webwork的Map实现中,至此之后,所有数据操作均在此Map结构中进行,从而将内部结构与Servlet API相分离。
至此,Webwork 的工作阶段结束,数据将传递给XWork 进行下一步处理。从这里也可以看
到Webwork和xwork之间的切分点,Webwork为xwork提供了一个面向Servlet 的协议转换
器,将Servlet 相关的数据转构转换成xwork所需要的通用数据格式,而xwork将完成实际的
服务调度和功能实现。
这样一来,以xwork为核心,只需替换外围的协议转换组件,即可实现不同技术平台之间的
切换(如将面向Servlet的Webwork替换为面向JMS的协议转换器实现,即可在保留应用逻
辑实现的情况下,实现不同外部技术平台之间的移植)。
3. 以上述信息作为参数,调用ActionProxyFactory创建对应的ActionProxy实例。
ActionProxyFactory 将根据Xwork 配置文件(xwork.xml)中的设定,创建
ActionProxy实例,ActionProxy中包含了Action的配置信息(包括Action名称,
对应实现类等等)。
4. ActionProxy创建对应的Action实例,并根据配置进行一系列的处理程序。包括
执行相应的预处理程序(如通过Interceptor 将Map 中的请求数据转换为Action
所需要的Java 输入数据对象等),以及对Action 运行结果进行后处理。
ActionInvocation 是这一过程的调度者。而com.opensymphony.xwork.
DefaultActionInvocation 则是XWork 中对ActionInvocation 接口的标准实现,如
果有精力可以对此类进行仔细研读,掌握了这里面的玄机,相信XWork的引擎
就不再神秘。

下面我们来看配置文件:
xwork.xml:
<!DOCTYPE xwork PUBLIC "-//OpenSymphony Group//XWork 1.0//EN"
"http://www.opensymphony.com/xwork/xwork-1.0.dtd">
<xwork>
<include file="webwork-default.xml" />

<package name="default" extends="webwork-default">

<action name="login" class="net.xiaxin.webwork.action.LoginAction">
<result name="success" type="dispatcher">

<param name="location">/main.jsp</param>
</result>
<result name="loginfail" type="dispatcher">

<param name="location">/index.jsp</param>
</result>
<interceptor-ref name="params" />
<interceptor-ref name="model-driven"/>
</action>
</package>
</xwork>
⑴ include
通过include 节点,我们可以将其他配置文件导入到默认配置文件xwork.xml 中。
从而实现良好的配置划分。
这里我们导入了Webwork 提供的默认配置webwork-default.xml(位于
webwork.jar 的根路径)。
⑵ package
XWork中,可以通过package对action进行分组。类似Java 中package和class的
关系。为可能出现的同名Action提供了命名空间上的隔离。
同时,package还支持继承关系。在这里的定义中,我们可以看到:
extends="webwork-default"
"webwork-default"是webwork-default.xml文件中定义的package,这里通
过继承,"default" package 自动拥有"webwork-default" package 中的所有
定义关系。
这个特性为我们的配置带来了极大便利。在实际开发过程中,我们可以根据自身
的应用特点,定义相应的package模板,并在各个项目中加以重用,无需再在重复
繁琐的配置过程中消耗精力和时间。
此外,我们还可以在Package节点中指定namespace,将我们的action分为若干个
逻辑区间。如:
<package name="default" namespace="/user"
extends="webwork-default">
就将此package中的action定义划归为/user 区间,之后在页面调用action的时候,
我们需要用/user/login.action 作为form action 的属性值。其中的/user/就指定了此
action的namespace,通过这样的机制,我们可以将系统内的action进行逻辑分类,
从而使得各模块之间的划分更加清晰。
⑶ action
Action配置节点,这里可以设定Action的名称和对应实现类。
⑷ result
通过result 节点,可以定义Action 返回语义,即根据返回值,决定处理模式以及
响应界面。
这里,返回值"success"(Action 调用返回值为String 类型)对应的处理模式为
"dispatcher"。
可选的处理模式还有:
1. dispatcher
本系统页面间转向。类似forward。
2. redirect
浏览器跳转。可转向其他系统页面。
3. chain
将处理结果转交给另外一个Action处理,以实现Action的链式处理。
4. velocity
将指定的velocity模板作为结果呈现界面。
5. xslt
将指定的XSLT 作为结果呈现界面。
随后的param节点则设定了相匹配的资源名称。
⑷ interceptor-ref
设定了施加于此Action的拦截器(interceptor)。关于拦截器,请参见稍后的“XWork
拦截器体系”部分。
interceptor-ref定义的是一个拦截器的应用,具体的拦截器设定,实际上是继
承于webwork-default package,我们可以在webwork-default.xml 中找到
对应的"params"和"model-driven"拦截器设置:
<interceptors>
……
<interceptor name="params"
class="com.opensymphony.xwork.interceptor.ParametersInt
erceptor" />
<interceptor name="model-driven"
class="com.opensymphony.xwork.interceptor.ModelDrivenIn
terceptor" />
……
</interceptors>
"params"大概是Webwork 中最重要、也最常用的一个Interceptor。上面曾经将
MVC工作流程划分为几个步骤,其中的第一步:
“将 Web 页面中的输入元素封装为一个(请求)数据对象”
就是通过"params"拦截器完成。Interceptor 将在Action 之前被调用,因而,
Interceptor 也成为将Webwork传来的MAP 格式的数据转换为强类型Java 对象的

最佳实现场所。
"model-driven"则是针对Action 的Model驱动模式的interceptor 实现。具体描
述请参见稍后的“Action驱动模式”部分
很可能我们的Action 都需要对这两个interceptor 进行引用。我们可以定义一个
interceptor-stack,将其作为一个interceptor 组合在所有Action 中引用。如,上面
的配置文件可修改为:
<xwork>
<include file="webwork-default.xml" />
<package name="default" extends="webwork-default">
<interceptors>
<interceptor-stack name="modelParamsStack">
<interceptor-ref name="params" />
<interceptor-ref name="model-driven" />
</interceptor-stack>
</interceptors>
<action name="login"
class="net.xiaxin.webwork.action.LoginAction">
<result name="success" type="dispatcher">
<param name="location">/main.jsp</param>
</result>
<result name="loginfail" type="dispatcher">
<param name="location">/index.jsp</param>
</result>
<interceptor-ref name="modelParamsStack" />
</action>
</package>
</xwork>
通过引入interceptor-stack,我们可以减少interceptor 的重复申明。
下面是我们的Model对象:
LoginInfo.java:
public class LoginInfo {
private String password;
private String username;
private List messages = new ArrayList();
private String errorMessage;

public List getMessages() {
return messages;
}
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
很简单,这只是一个纯粹的值对象(Value-Object)。这里,它扮演着模型(Model)的
角色,并与Action的输入输出密切相关。
与 SpringMVC中的Command对象不同,Webwork 中的Model对象,扮演着承上启下
的角色,它既是Action的输入参数,又包含了Action处理的结果数据。
换句话说,输入的Http请求参数,将被存储在Model对象传递给Action进行处理,Action
处理完毕之后,也将结果数据放置到Model 对象中,之后,Model 对象与返回界面融合生
成最后的反馈页面。
也正由于此,笔者建议在实际开发中采用Model-Driven 模式,而非Property-Driven 模
式(见稍后“Action驱动模式”部分),这将使得业务逻辑更加清晰可读。
对应的Action代码
public class LoginAction implements Action, ModelDriven {
private final static String LOGIN_FAIL="loginfail";
LoginInfo loginInfo = new LoginInfo();
public String execute() throws Exception {
if ("erica".equalsIgnoreCase(loginInfo.getUsername())
&& "mypass".equals(loginInfo.getPassword())) {
//将当前登录的用户名保存到Session
ActionContext ctx = ActionContext.getContext();
Map session = ctx.getSession();
session.put("username",loginInfo.getUsername());
//出于演示目的,通过硬编码增加通知消息以供显示
loginInfo.getMessages().add("message1");
loginInfo.getMessages().add("message2");
loginInfo.getMessages().add("message3");
return SUCCESS;
}else{
loginInfo.setErrorMessage("Username/Password Error!");
return LOGIN_FAIL;
}
}
public Object getModel() {
return loginInfo;
}
}

 

 LoginAction实现了两个接口:
1. Action
Action接口非常简单,它指定了Action的入口方法(execute),并定义了
几个默认的返回值常量:
public interface Action extends Serializable {
public static final String SUCCESS = "success";
public static final String NONE = "none";
public static final String ERROR = "error";
public static final String INPUT = "input";
public static final String LOGIN = "login";
public String execute() throws Exception;
}

SUCCESS、NONE、ERROR、INPUT、LOGIN 几个字符串常量定义了常用的
几类返回值。我们可以在Action 实现中定义自己的返回类型,如本例中的
LOGIN_FAIL定义。
而execute方法,则是Action的入口方法,XWork将调用每个Action的execute
方法以完成业务逻辑处理。
2. ModelDriven
ModelDriven接口更为简洁:
public interface ModelDriven {
Object getModel();
}
ModelDriven仅仅定义了一个getModel方法。XWork在调度Action时,将通
过此方法获取Model 对象实例,并根据请求参数为其设定属性值。而此后的
页面返回过程中,XWork 也将调用此方法获取Model 对象实例并将其与设定
的返回界面相融合。
注意这里与Spring MVC 不同,Spring MVC 会自动为逻辑处理单元创建
Command Class实例,但Webwork不会自动为Action创建Model对象实例,
Model 对象实例的创建需要我们在Action 代码中完成(如LoginAction 中
LoginInfo对象实例的创建)。
另外,如代码注释中所描述,登录成功之后,我们随即将username保存在Session之中,
这也是大多数登录操作必不可少的一个操作过程。
这里面牵涉到了Webwork中的一个重要组成部分:ActionContext。
ActionContext为Action提供了与容器交互的途径。对于Web 应用而言,与容器的交互
大多集中在Session、Parameter,通过ActionContext我们在代码中实现与Servlet API无关的
容器交互。
如上面代码中的:
ActionContext ctx = ActionContext.getContext();
Map session = ctx.getSession();
session.put("username",loginInfo.getUsername());
同样,我们也可以操作Parameter:
ActionContext ctx = ActionContext.getContext();
Map params = ctx.getParameters();
String username = ctx.getParameters("username");
上述的操作,将由XWork根据当前环境,调用容器相关的访问组件(Web 应用对应的
就是Webwork)完成。上面的ActionContext.getSession(),XWork 实际上将通过Webwork
提供的容器访问代码“HttpServletRequest.getSession()”完成。
注意到,ActionContext.getSession返回的是一个Map类型的数据对象,而非HttpSession。
这是由于WebWork对HttpSession进行了转换,使其转变为与Servlet API无关的Map对象。
通过这样的方式,保证了Xwork 所面向的是一个通用的开放结构。从而使得逻辑层与表现
层无关。增加了代码重用的可能。
此 外, 为了提供与Web 容器直接交互的可能。WebWork 还提供了一个
ServletActionContext实现。它扩展了ActionContext,提供了直接面向Servlet API的容器访
问机制。
我们可以直接通过ServletActionContext.getRequest 得到当前HttpServletRequest 对象的
引用,从而直接与Web 容器交互。
获得如此灵活性的代价就是,我们的代码从此与ServletAPI 紧密耦合,之后系统在不
同平台之间移植就将面临更多的挑战(同时单元测试也难于进行)。
平台移植的需求并不是每个应用都具备。大部分系统在设计阶段就已经确定其运行平
台,且无太多变更的可能。不过,如果条件允许,尽量通过ActionContext 与容器交互,而
不是平台相关的ServletActionContext,这样在顺利实现功能的同时,也获得了平台迁移上
的潜在优势,何乐而不为。
登录成功界面:
main.jsp:
<%@ taglib prefix="ww" uri="webwork"%>
<html>
<body>
<p align="center">Login Success!</p>
<p align="center">Welcome!
<ww:property value="#session['username']"/>
</p>
<p align="center">
<b>Messages:</b><br>
<ww:iterator value="messages" status="index">
<ww:if test="#index.odd == true">
!<ww:property/><br>
</ww:if>
<ww:else>
*<ww:property/><br>
</ww:else>
</ww:iterator>
</p>
</body>
</html>
这里我们引入了Webwork的taglib,如页面代码第一行的申明语句。
下面主要使用了三个tag:
 <ww:property value="#session['username']"/>
读取Model对象的属性填充到当前位置。
value指定了需要读取的Model对象的属性名。
这里我们引用了LoginAction在session中保存的’username’对象。
由于对应的Model(LoginInfo)中也保存了username 属性。下面的语句与之
效果相同:
<ww:property value="username"/>
与 JSP2中的EL类似,对于级联对象,这里我们也可以通过“.”操作符获得
其属性值,如value="user.username"将得到Model 对象中所引用的user
对象的username 属性(假设LoginInfo中包含一个User 对象,并拥有一个名
为“username”的属性)。
关于EL的内容比较简单,本文就不再单独开辟章节进行探讨。
Webwork中包括以下几种特殊的EL表达式:
parameter[‘username’] 相当于request.getParameter(“username”);
request[‘username’] 相当于request.getAttribute(“username”);
session[‘username’] 从session中取出以“username”为key的值
application[‘username’] 从ServletContext中取出以“username”为key
的值
注意需要用“#”操作符引用这些特殊表达式。
另外对于常量,需要用单引号包围,如#session['username'] 中的
'username'。
 <ww:iterator value="messages" status="index">
迭代器。用于对java.util.Collection、java.util.Iterator、java.util.Enumeration,、
java.util.Map、Array类型的数据集进行循环处理。
其中,value属性的语义与<ww:property>中一致。
而 status属性则指定了循环中的索引变量,在循环中,它将自动递增。
而在下面的<ww:if>中,我们通过“#”操作符引用这个索引变量的值。
索引变量提供了以下几个常用判定方法:
first 当前是否为首次迭代
last 当前是否为最后一次迭代
odd 当前迭代次数是否奇数
even 当前迭代次数是否偶数
<ww:if test="#index.odd == true">和<ww:else>
用于条件判定。
test属性指定了判定表达式。表达式中可通过“#”操作符对变量进行引用。
表达式的编写语法与java 表达式类似。
类似的,还有<ww:elseif test="……">。
登录失败界面
实际上,这个界面即登录界面index.jsp。只是由于之前出于避免干扰的考虑,隐藏了
index.jsp中显示错误信息的部分。
完整的index.jsp如下:
<%@ page pageEncoding="gb2312"
contentType="text/html;charset=gb2312"%>
<%@ taglib prefix="ww" uri="webwork"%>
<html>
<body>
<form action="/login.action">
<p align="center">
登录<br>
<ww:if test="errorMessage != null">
<font color="red">
<ww:property value="errorMessage"/>
</font>
</ww:if>
</p>
用户名:
<input type="text" name="model.username" />
<br>
密 码 :
<input type="password" name="model.password" />
<br>
<p align="center">
<input type="submit" value="提交" name="B1"/>
<input type="reset" value="重置" name="B2"/>
</p>
</form>
</body>
</html>
这里首先我们进行判断,如果Model中的errorMessage不为null,则显示错误信息。这
样,在用户第一次登录时,由于Model对象尚未创建,errorMessage自然为null,错误信息
不会显示,即得到了与之前的index.jsp同样的效果。
posted @ 2010-02-10 16:44 webber 阅读(1567) | 评论 (0)编辑 收藏

     摘要:   3.2.2  拦截器介绍 拦截器可以实现横切(crosscutting)功能并使这些实现相对action甚至Struts 2框架保持独立。这样可以使核心框架代码比以前更加简洁,且使开发人员更快捷地使用新框架的特性。实际上,松耦合机制意味着用户再也不用等着框架来实现他们所需的新特性;相反,它们可以实现和使用自己所需的特性且不用修改框架的底层源代码。 使用拦截器可以达到如...  阅读全文
posted @ 2010-02-10 16:36 webber 阅读(2435) | 评论 (0)编辑 收藏

document.form.action,表单分向提交,javascript提交表单
同一个表单可以根据用户的选择,提交给不同的后台处理程序。即,表单的分向提交。如,在编写论坛程序时,如果我们希望实现用户在发送贴子的时候,既发送提交功能又有预览功能时,就会遇到上述问题。即,当用户点击提交按钮时,我们希望表单提交给"提交"处理程序;而当用户点击预览按钮时,我们希望表单提交给"预览"处理程序。那么,如何实现上述功能呢?下面代码可以很好的解决这个问题。 
<form name="form" method="post">
测试表单:<input name="test"><br>
<input type="button" value="提交" onClick=send()>
<input type="button" value="预览" onClick=preview()>
</form>
<script language=javascript>
function send()
  {
    document.form.action="send.asp"
    document.form.submit()
   }
function preview()
   {
     document.form.action="preview.asp"
     document.form.submit()
   }
</script>

         关于上面实例的两点说明:
        1、在整个表单中,不应有名字为action或submit的标签,否则将会产生"对象不支持此属性和方法"的错误。如代码 "<input type='xxxx' name='action' >"在表单中是不允许出现的;
        2、在form标签中应该存在name属性。即,应该给表单取一个名字。语句document.form.action和document.form.submit中的"form"也就是表单的名字。
        表单的分向提交不仅仅使用在论坛的程序中,它还可以运用在许多场合下。恰当的运用表单的分向提交功能可以大大的增强网站的人性化程度。

        昨天,我调试程序就出现了这样的问题,就是出现了"对象不支持此属性和方法"的错误,一直无法定位出来,都快疯掉了,后来在发现一个button命名为submit了。

1.--------  
   
  <form   name="formname"   action="">  
  <input   name="inputname">  
  <input   type="submit"   onclick="document.formname.action='a.asp'"   value="button   a">  
  <input   type="submit"   onclick="document.formname.action='b.asp'"   value="button   b">  
  <input   type="submit"   onclick="document.formname.action='c.asp'"   value="button   c">  
  </form>      
  2.----------  
  <input   type=button   name="a"   value="a"   onclick="this.form.action='a.asp';this.form.submit();">  
  <input   type=button   name="b"   value="b"   onclick="this.form.action='b.asp';this.form.submit();">  
  <input   type=button   name="c"   value="c"   onclick="this.form.action='c.asp';this.form.submit();">  
  <input   type=button   name="d"   value="d"   onclick="this.form.action='d.asp';this.form.submit();">
posted @ 2010-02-10 16:35 webber 阅读(2555) | 评论 (1)编辑 收藏

用过办公软件word的人都知道,快捷键对我们的工作有多么重要。据说,真正的电脑高手们很少使用鼠标操作,绝大多数操作都直接通过键盘实现。但有些操作在软件本身并没有自带,这种情况下需要我们开动脑筋,自已动手创建快捷键。

下面我们以“粘贴”中的“选择性粘贴”为例,介绍下自己创建系统中所没有的快捷键的步骤。其它的快捷键以此类推。

用word时,复制粘贴是必不可少的操作步骤。以前在word 97 时代,能粘贴到Word中的只是纯文本的文字,现在更多的人使用的是2003、xp版本,粘贴结果就是有格式的文字,还包括图片、甚至表格。这个新功能当然有它的好处,可是如果我只需要源文件中的纯文字,不想要格式、图片,那就需要用到它所附带的选择性粘贴功能。目前word2007提供的方法有两个:

1. 在粘贴结束后,会自动出现一个浮动的“粘贴选项”按钮,用鼠标按这个按钮,选择“仅保留文本”即可。这个方法的缺点是,粘贴动作比较慢(因为无用的内容比较多)

2. 不是用一般的“粘贴”功能,而在菜单中选“编辑”--“选择性粘贴”--“无格式文本”即可。这个方法执行速度快,但操作步骤多,太麻烦。

下面就通过自己创建宏来解决这个问题,实现快捷键操作,就象按一下ctrl+V就可以实现粘贴一样。这个过程分两步:一是建立一个实现“选择性粘贴”的宏,二是给这个宏指定键盘快捷键。

一、创建“选择性粘贴”功能的宏

1、打开word 2007,选择开发工具下的“Visual Basic编辑器”;或者直接按Alt+F11也可以。这时会出现一个Visual Basic编辑窗口,现在看看你的窗口中左侧位置是否有某个模块存在?如果已经有了,你可以跳过建立新模块这一步。如果你的系统在Normal下面不存在任何模块,那么在Normal上,打开右键菜单,选插入模块。

2、现在模块已经有了,用鼠标点击使之高亮,然后按F7键(直接双击模块也可以)。这样右侧就会出现代码窗口。

3、将下面的代码粘贴到“代码窗口”中。关闭Visual Basic编辑窗口。这样,一个宏就建立好了。

Sub 无格式粘贴()

'无格式粘贴 Macro

'宏在 2005-9-22 由 SQ 录制

Selection.PasteSpecial Link:=False, DataType:=wdPasteText, Placement:=wdInLine, DisplayAsIcon:=False
End Sub

到这一步,可以自动运行选择性粘贴的宏就创建成功了,但这时候使用它还需要到“工具—宏—宏”,选择相应的宏名称来运行它,比较麻烦。想快捷使用,继续向下看。


二、将宏拖到工具栏,并指定键盘快捷键

在Word主窗口中,找到word选项,选择工具按钮中的“自定义”。

选“从下列位置选择命令”选项卡,选“宏”,会出现我们刚建立的宏。用鼠标将这个宏拖到右边快速访问工具栏位置,松开鼠标键。请单击修改按钮,在弹出菜单中作相应的图标、名称。这样工具栏中就有了刚才制作的选择性粘贴的宏的快捷按钮了。

再指定快捷键:此时“自定义”对话框依然打开着,请按“键盘快捷方式”按钮旁的“自定义”。“类别”选“宏”,在“宏”中选定EditPasteNoFormat。这时“请按新快捷键”应该是空白的,用鼠标点一下这里,然后按一下你想要的快捷键。这里,假设我们参考粘贴的快捷键ctrl+V,而使用alt+V,出现“未指定”,说明这是一个可用的快捷键,和其它功能不发生冲突。按“指定”按钮。现在alt+V被指定为这个宏的快捷键了。按“关闭”按钮,再 关闭“自定义”窗口。(上面的步骤中既设置了工具栏按钮,也设置了键盘快捷键,也可以只指定其中的一个,看各人使用习惯而定。)

以后再使用WORD时,我们可以按这个工具按钮,或者使用alt+v快捷键方便地实现“粘贴为纯文本”的功能了。其它功能的快捷键也可以照葫芦画瓢。

posted @ 2010-02-10 10:56 webber 阅读(5156) | 评论 (2)编辑 收藏