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 閱讀(605) | 評論 (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 閱讀(239) | 評論 (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 閱讀(1072) | 評論 (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 閱讀(732) | 評論 (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 閱讀(333) | 評論 (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 閱讀(1585) | 評論 (0)編輯 收藏

               摘要:   3.2.2  攔截器介紹 攔截器可以實現橫切(crosscutting)功能并使這些實現相對action甚至Struts 2框架保持獨立。這樣可以使核心框架代碼比以前更加簡潔,且使開發人員更快捷地使用新框架的特性。實際上,松耦合機制意味著用戶再也不用等著框架來實現他們所需的新特性;相反,它們可以實現和使用自己所需的特性且不用修改框架的底層源代碼。 使用攔截器可以達到如...  閱讀全文
          posted @ 2010-02-10 16:36 webber 閱讀(2451) | 評論 (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 閱讀(2569) | 評論 (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 閱讀(5168) | 評論 (2)編輯 收藏

          主站蜘蛛池模板: 南宫市| 苗栗县| 社会| 乌鲁木齐市| 澄江县| 固始县| 昌图县| 岳阳市| 靖边县| 朝阳县| 遂川县| 麻城市| 深泽县| 饶河县| 玉田县| 东乌| 文安县| 搜索| 麦盖提县| 阿瓦提县| 延吉市| 宿州市| 焉耆| 新丰县| 广宁县| 嘉荫县| 平凉市| 南平市| 柳河县| 墨竹工卡县| 阜新| 永丰县| 边坝县| 西乌珠穆沁旗| 绥宁县| 布尔津县| 阜新市| 宁安市| 象山县| 鹤山市| 襄城县|