webber

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

          2010年3月26日 #

          在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)編輯 收藏

          2010年3月15日 #

          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)編輯 收藏

          2010年2月11日 #

          雖然大多數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)編輯 收藏

          2010年2月10日 #

          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
          瀏覽器跳轉??赊D向其他系統頁面。
          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,出現“未指定”,說明這是一個可用的快捷鍵,和其它功能不發生沖突。按“指定”按鈕?,F在alt+V被指定為這個宏的快捷鍵了。按“關閉”按鈕,再 關閉“自定義”窗口。(上面的步驟中既設置了工具欄按鈕,也設置了鍵盤快捷鍵,也可以只指定其中的一個,看各人使用習慣而定。)

          以后再使用WORD時,我們可以按這個工具按鈕,或者使用alt+v快捷鍵方便地實現“粘貼為純文本”的功能了。其它功能的快捷鍵也可以照葫蘆畫瓢。

          posted @ 2010-02-10 10:56 webber 閱讀(5168) | 評論 (2)編輯 收藏

          2010年1月13日 #

          *****************web開發的問題匯總********************


          2.      Hibernate優化問題。如何優化數據庫訪問,使程序訪問數據庫更優化。

           初用HIBERNATE的人也許都遇到過性能問題,實現同一功能,用HIBERNATE與用JDBC性能相差十幾倍很正常,如果不及早調整,很可能影響整個項目的進度。

            大體上,對于HIBERNATE性能調優的主要考慮點如下:

            * 數據庫設計調整

            * HQL優化

            *  API的正確使用(如根據不同的業務類型選用不同的集合及查詢API)

            * 主配置參數(日志,查詢緩存,fetch_size, batch_size等)

          * 映射文件優化(ID生成策略,二級緩存,延遲加載,關聯優化)

            * 一級緩存的管理

            * 針對二級緩存,還有許多特有的策略

            * 事務控制策略。

            1、 數據庫設計

            a) 降低關聯的復雜性

            b) 盡量不使用聯合主鍵

            c) ID的生成機制,不同的數據庫所提供的機制并不完全一樣

            d) 適當的冗余數據,不過分追求高范式

            2、 HQL優化

            HQL如果拋開它同HIBERNATE本身一些緩存機制的關聯,HQL的優化技巧同普通的SQL優化技巧一樣,可以很容易在網上找到一些經驗之談。

            3、 主配置

            a) 查詢緩存,同下面講的緩存不太一樣,它是針對HQL語句的緩存,即完全一樣的語句再次執行時可以利用緩存數據。但是,查詢緩存在一個交易系統(數據變更頻繁,查詢條件相同的機率并不大)中可能會起反作用:它會白白耗費大量的系統資源但卻難以派上用場。

            b) fetch_size,同JDBC的相關參數作用類似,參數并不是越大越好,而應根據業務特征去設置

            c) batch_size同上。

            d) 生產系統中,切記要關掉SQL語句打印。

            4、 緩存

            a) 數據庫級緩存:這級緩存是最高效和安全的,但不同的數據庫可管理的層次并不一樣,比如,在ORACLE中,可以在建表時指定將整個表置于緩存當中。

            b) SESSION緩存:在一個HIBERNATE SESSION有效,這級緩存的可干預性不強,大多于HIBERNATE自動管理,但它提供清除緩存的方法,這在大批量增加/更新操作是有效的。比如,同時增加十萬條記錄,按常規方式進行,很可能會發現OutofMemeroy的異常,這時可能需要手動清除這一級緩存:Session.evict以及Session.clear

            c) 應用緩存:在一個SESSIONFACTORY中有效,因此也是優化的重中之重,因此,各類策略也考慮的較多,在將數據放入這一級緩存之前,需要考慮一些前提條件:

            i. 數據不會被第三方修改(比如,是否有另一個應用也在修改這些數據?)

            ii. 數據不會太大

            iii. 數據不會頻繁更新(否則使用CACHE可能適得其反)

            iv. 數據會被頻繁查詢

            v. 數據不是關鍵數據(如涉及錢,安全等方面的問題)。

            緩存有幾種形式,可以在映射文件中配置:read-only(只讀,適用于很少變更的靜態數據/歷史數據),nonstrict-read-write,read-write(比較普遍的形式,效率一般),transactional(JTA中,且支持的緩存產品較少)

            d) 分布式緩存:同c)的配置一樣,只是緩存產品的選用不同,在目前的HIBERNATE中可供選擇的不多,oscache, jboss cache,目前的大多數項目,對它們的用于集群的使用(特別是關鍵交易系統)都持保守態度。在集群環境中,只利用數據庫級的緩存是最安全的。

            5、 延遲加載

            a) 實體延遲加載:通過使用動態代理實現

            b) 集合延遲加載:通過實現自有的SET/LIST,HIBERNATE提供了這方面的支持

            c) 屬性延遲加載:

            6、 方法選用

            a) 完成同樣一件事,HIBERNATE提供了可供選擇的一些方式,但具體使用什么方式,可能用性能/代碼都會有影響。顯示,一次返回十萬條記錄(List/Set/Bag/Map等)進行處理,很可能導致內存不夠的問題,而如果用基于游標(ScrollableResults)或Iterator的結果集,則不存在這樣的問題。

            b) Session的load/get方法,前者會使用二級緩存,而后者則不使用。

            c) Query和list/iterator,如果去仔細研究一下它們,你可能會發現很多有意思的情況,二者主要區別(如果使用了Spring,在HibernateTemplate中對應find,iterator方法):

            i. list只能利用查詢緩存(但在交易系統中查詢緩存作用不大),無法利用二級緩存中的單個實體,但list查出的對象會寫入二級緩存,但它一般只生成較少的執行SQL語句,很多情況就是一條(無關聯)。

            ii. iterator則可以利用二級緩存,對于一條查詢語句,它會先從數據庫中找出所有符合條件的記錄的ID,再通過ID去緩存找,對于緩存中沒有的記錄,再構造語句從數據庫中查出,因此很容易知道,如果緩存中沒有任何符合條件的記錄,使用iterator會產生N+1條SQL語句(N為符合條件的記錄數)

            iii. 通過iterator,配合緩存管理API,在海量數據查詢中可以很好的解決內存問題,如:

            while(it.hasNext()){

            YouObject object = (YouObject)it.next();

            session.evict(youObject);

           sessionFactory.evice(YouObject.class, youObject.getId());

            }

            如果用list方法,很可能就出OutofMemory錯誤了。

            iv. 通過上面的說明,我想你應該知道如何去使用這兩個方法了。

            7、 集合的選用

            在HIBERNATE 3.1文檔的“19.5. Understanding Collection performance”中有詳細的說明。

            8、 事務控制

            事務方面對性能有影響的主要包括:事務方式的選用,事務隔離級別以及鎖的選用

            a) 事務方式選用:如果不涉及多個事務管理器事務的話,不需要使用JTA,只有JDBC的事務控制就可以。

            b) 事務隔離級別:參見標準的SQL事務隔離級別

            c) 鎖的選用:悲觀鎖(一般由具體的事務管理器實現),對于長事務效率低,但安全。樂觀鎖(一般在應用級別實現),如在HIBERNATE中可以定義VERSION字段,顯然,如果有多個應用操作數據,且這些應用不是用同一種樂觀鎖機制,則樂觀鎖會失效。因此,針對不同的數據應有不同的策略,同前面許多情況一樣,很多時候我們是在效率與安全/準確性上找一個平衡點,無論如何,優化都不是一個純技術的問題,你應該對你的應用和業務特征有足夠的了解。

            9、 批量操作

            即使是使用JDBC,在進行大批數據更新時,BATCH與不使用BATCH有效率上也有很大的差別。我們可以通過設置batch_size來讓其支持批量操作。

            舉個例子,要批量刪除某表中的對象,如“delete Account”,打出來的語句,會發現HIBERNATE找出了所有ACCOUNT的ID,再進行刪除,這主要是為了維護二級緩存,這樣效率肯定高不了,在后續的版本中增加了bulk delete/update,但這也無法解決緩存的維護問題。也就是說,由于有了二級緩存的維護問題,HIBERNATE的批量操作效率并不盡如人意!

          3.      網站是動態的,需要頻繁訪問數據庫,如何提高訪問速度,減少服務器壓力?

          提供JDBC數據庫鏈接池共享,大大降低數據庫壓力;

          智能動態HTML,大大減少網絡數據流量;

          海量數據優化,大大提高登錄和訪問速度;

          用戶界面和數據緩存,大大提高登錄速度;

          多層次體系結構,支持多個應用程序服務器并行,大大提高系統伸縮性,并發訪問用戶數目和數據安全性

          4.      搜索引擎優化,如何提高網站排名。優化有哪些具體技術措施?

             網站結構設計中面向搜索引擎的優化注意事項包括:

          1)鏈接引用的重要性

          a.以量取勝:不一定加入傳統門戶網站的分類目錄才是網站推廣,來自其他網站的任何反相鏈接都是有用的

          b. 以質取勝:被PageRank高的網站引用能更快地提高PageRank

          c. 了解搜索引擎的"價值觀":不要通過Link Farm提高自身的站點排名:Google會懲罰那些主動鏈接到Link Farm站點以提高自身排名站點,相應站點的頁面將不會被收入到索引中。但如果你的頁面被別的Link Farm鏈接了也不必擔心,因為這種被動的鏈接是不會被懲罰的。

          d. 不要吝嗇給其他網站的鏈接:如果一個網頁只有大量的進入鏈接,而缺乏導出鏈接,也會被搜索引擎認為是沒有價值的站點。

          2)如何突出關鍵詞:網頁標題、主題的設計

          a.Theme Engine正在逐步超過PR,成為結果排序中更主要的因素

          b.不要空著標題:空著<title></title>無異于浪費了最有價值的一塊陣地

          c. 標題長度和內容:不要過長,一般在40個字(80個字節)以內,并充分突出關鍵詞的比重

          d. 如果網頁很多的話,盡量使用不同的網頁標題,爭取讓自己網站的內容更多的進入搜索引擎索引范圍

          e. 除了<title></title>外,還可以用<h1></h1>標題行突出內容主題, 加強標題的效果

          3)頁面及站點結構設計注意事項

          a. 靜態鏈接: 大部分搜索引擎都認為靜態鏈接的網頁是優質網頁,Google在優先抓取索引的網頁中70%以上是不帶參數鏈接的靜態網頁。而且即使同樣的內容,靜態網頁也 會比動態網頁權重高

          b. 能夠進入Google索引的頁面數量越多越好

          c. 網站目錄結構要扁平,因為每深一級目錄,PAGERANK降低1-2個檔次。假設首頁是3,其子可能目錄就是1了,更深可能就無法列入評級范圍了。

          d. 表現和內容的分離:“綠色”網頁

          網頁中的javascript和css盡可能和網頁分離,一方面提高代碼重用度(也方便頁面緩存),另外一方面,由于有效內容占網頁長度的百分比高,也能提高相關關鍵詞在頁面中的比重也增加了。總之,應該鼓勵遵循w3c的規范,使用更規范的XHTML和XML作為顯示格式便于內容更長時間的保存。

          e. 讓所有的頁面都有能夠快速入口:站點地圖, 方便網頁爬蟲(spider)快速遍歷網站所有需要發布的內容。如果首頁就是用Flash或圖片進入的話,無異于將搜索引擎拒之門外,除了UI設計的用戶 友好外,spider friendly也是非常重要的

          f. 保持網站自身的健康:經常利用壞 鏈檢查工具檢查網站中是否有死鏈

          g. 保持網頁內容/鏈接的穩定性和持久性:在搜索引擎索引中網頁存在的歷史也是一個比較重要的因素,而且歷史比較久的網頁被鏈接的幾率越高。為了 保證自己網頁能夠被比較持久的被其他網站的頁面引用,如果自己網頁中有鏈接更新時,最好能保留舊的頁面并做好鏈接轉向,以保持內容的連續性。

          h. 文件類型因素:Google有對PDF, Word(Power Point, Excel), PS文檔的索引能力,由于這種文檔的內容比一般的HTML經過了更多的整理,學術價值一般比較高,所以這些類型的文檔天生就比一般的HTML類型的文檔 PageRank要高。因此,對于比較重要的文檔:技術白皮書,FAQ,安裝文檔等建議使用PDF PS等高級格式存取,這樣在搜索結果中也能獲得比較靠前的位zhi點訪問統計的重要性等;,的設計 

          4)以及站點訪問統計的重要性等

          5)Google的站點設計指南

          1.Make a site with a clear hierarchy and text links. Every page should be reachable from at least one static text link.  讓網站有著清晰的結構和文本鏈接,所有的頁面至少要有一個靜態文本鏈接入口

          批注:盡量不要用圖片和JAVASCRIPT

          2.Offer a site map to your users with links that point to the important parts of your site. If the site map is larger than 100 or so links, you may want to break the site map into separate pages.

          為用戶提供一個站點地圖:轉向網站的重要部分。如果站點地圖頁面超過100個鏈接,則需要將頁面分成多個頁面。

          批注:索引頁不要超過100個鏈接:SPIDER只考慮頁面中頭100個鏈接

          3.Create a useful, information-rich site and write pages that clearly and accurately describe your content.

          用一些有用的,信息量豐富的站點,清晰并正確的描述你的信息。

          4.Think about the words users would type to find your pages, and make sure that your site actually includes those words within it.

          想像用戶可能用來找到你的關鍵詞,并保證這些關鍵詞在網站中出現。

          批注:少用“最大”,“最好”之類的形容詞,用用戶最關心的詞,比如:下載,歌星名字,而不是一些抽象名詞。

          5.Try to use text instead of images to display important names, content, or links. The Google crawler doesn't recognize text contained in images.

          盡可能使用文本,而不是圖片顯示重要的名稱,內容和鏈接。GOOGLE的機器人不認識圖片中的文字。

          6.Make sure that your TITLE and ALT tags are descriptive and accurate.

          保證:頁面的TITLE和ALT標記正確的精確描述

          7.Check for broken links and correct HTML.

          檢查壞鏈并修正這些HTML錯誤。

          8.If you decide to use dynamic pages (i.e., the URL contains a '?' character), be aware that not every search engine spider crawls dynamic pages as well as static pages. It helps to keep the parameters short and the number of them small.

          如果你打算使用動態頁面:鏈接中包含"?",必須了解:并非所有的搜索引擎的機器人能想對待靜態頁面一樣對待動態頁面,保持動態頁面的參數盡可能的少也會 很有幫助。

          9.Keep the links on a given page to a reasonable number (fewer than 100).

          讓一個頁面中的鏈接少于100個。

          批注:用lynx -dump http://www.chedong.com/ 可以模擬從robot角度看到的頁面。其最后有鏈接統計

          5.      hibernate對動態查詢的理解,如何應用,并作應用示例。

          定義:

          ?靜態查詢:在編程時已經確定要查詢的字段,這時編寫的HQL或QBC稱為靜態查詢。

          ?動態查詢:在編程時無法確定要查詢的字段,這時編寫的HQL或QBC稱為動態查詢。比如組合查詢時,往往需要查詢的項很多,但不是每個項都必需。

          (HQL適用于靜態查詢,QBC適用于動態查詢)

          以下分別用HQL和QBC實現動態查詢:

          1)下面的程序通過對字符串的拼裝使用HQL語句實現動態查詢:

          Public List findStu(String name, int age){

          StringBuffer queryString= new StringBuffer();

          Boolean conditionFound= false;

          if(name!=null){

          queryString.append(“lower(s.name) like :name”);

          conditionFound= true;

          }if(age!= 0){

          if(conditionFound) queryString.append(“and”);

          queryString.append(“s.age= :age”);

          conditionFound=true;

          }

          String fromClause= conditionFound?”fromStudent s where” : ”fromStudent s”;

          queryString.insert(0,fromClause).append(“order by s.name”);

          Query query=getSession().createQuery(“queryString.toString()”);

          if(name!=null)query.setString(“name”,’%’+name.toLowerCase()+’%’);

          if(age!=0)query.setInteger(“age”,newInteger(age));

          return query.list();

          }

          上面的代碼雖然可以正常工作,但是把簡單的功能實現的相當復雜,維護起來不方便。我們來看一下使用QBC查詢。

          Public List findStu(String name, int age){

          Criteria crit= getSession().createCriteria(Student.class);

          if(name!=null){

          crit.add(Restrictions.like(“name”,name,MatchMode.ANYWHERE));

          }if(age!=0){

          crit.add(Restrictions.eq(“age”,newInteger(age)));

          }

          crit.addOrder(Order.asc(“name”));

          return crit.list();

          }

          6.      Hibernate問題

          ●     a different object with the same identifier value was already associated with the session是什么原因導致的?

          在hibernate中同一個session里面有了兩個相同標識但是是不同實體,當這時運行saveOrUpdate(object)操作的時候就會報這個錯誤。種錯誤經常出現在一對多映射和多對多映射

          在hibernate的開發中,HBM文件中會涉及到n2m的種種關系,但是,只有在出于必要性考慮的時候,才加上cascade="all" ,同時,需小心規避由此引起的程序Exception.

          考慮不周,會導致可能出現的最常見Exception是:

          net.sf.hibernate.NonUniqueObjectException

          a different object with the same identifier value was already associated with the session:......

          解決辦法是:

          1. 去掉引起Exception的associated Class對映HBM中的不必要cascade;

          2. 檢查Exception發生位置的session的使用情況;

          ●     Object references an unsaved transient instance-save the transient instance before flushing是什么原因導致的?

          某個對象的某個屬性是一個實體,在這個實體沒有保存之前就保存這個對象而造成了這個錯誤。

          以下舉個例子說明以下

          Session session = dao.getSession();

          Transaction tx = session.beginTransaction();

          Bo.setBman( form ,man,session);

          Bo.saveChangeTable( man,session); // 把man當作屬性賦給ChangeTable,并保存ChangeTable. 就是這出的錯,

          dao.save(man,session);  // 保存man

          tx.commit();

          ==================== 應該這樣寫:===============

          Session session = dao.getSession();

          Transaction tx = session.beginTransaction();

          Bo.setBman( form ,man,session);

          dao.save(man,session);  // 保存man

          Bo.saveChangeTable( man,session); // 把man當作屬性賦給ChangeTable,并保存ChangeTable

          tx.commit();

          這樣,問題就解決了。

           

          ●     如果修改一個列表中的一個數據,會發現這個數據不穩定,一會是修改后的,一會是修改前的,說出其原因

          因為hibernate利用了緩存技術,sql適時提交,當數據修改以后,數據并不一定及時提交到數據庫,而是放在hibernate的緩存中,當我們察看數據時,可能是提交完的,也可能是沒有提交的,所以就會出現數據的臟讀。

          如何避免使用緩存技術所帶來的臟數據問題呢?

          在設計、實現和測試時,應該清晰定義緩存數據的更新:

          i. 不考慮緩存數據的更新,重啟軟件系統是一種必要的方式;

          ii. 不考慮緩存數據的更新,緩存數據不可能成為臟數據(但在軟件系統中,往往“不可能”會在一次又一次的重構之后變為“可能”);

          iii. 考慮緩存數據的更新,當源數據變化時,實時更新緩存數據。

          ●     對于數據庫自增長來說,在映射文件中主鍵配置,用哪種配置方案最好,最不容易出現問題?

          Hibernate標識生成策略:

          標識符生成器 描述

          increment 適用于代理主鍵。由Hibernate自動以遞增方式生成。

          identity     適用于代理主鍵。由底層數據庫生成標識符。

          sequence  適用于代理主鍵。Hibernate根據底層數據庫的序列生成標識符,這要求底層數據庫支持序列。

          hilo       適用于代理主鍵。Hibernate分局high/low算法生成標識符。

          seqhilo     適用于代理主鍵。使用一個高/低位算法來高效的生成long,short或者int類型的標識符。

          native適用于代理主鍵。根據底層數據庫對自動生成標識符的方式,自動選擇identity、sequence或hilo。

          uuid.hex   適用于代理主鍵。Hibernate采用128位的UUID算法生成標識符。

          uuid.string       適用于代理主鍵。UUID被編碼成一個16字符長的字符串。

          assigned   適用于自然主鍵。由Java應用程序負責生成標識符。

          foreign     適用于代理主鍵。使用另外一個相關聯的對象的標識符。

          就這個問題我認為應該用increment、native都可以

          7.       概括你常用的框架的優缺點。

          1. Struts的優缺點:

          具體來講,Struts的優點有:

          1. 實現MVC模式,結構清晰,使開發者只關注業務邏輯的實現.

          2. 有豐富的tag可以用 ,Struts的標記庫(Taglib),如能靈活動用,則能大大提高開發效率。另外,就目前國內的JSP開發者而言,除了使用JSP自帶的常用標記外,很少開發自己的標記,或許Struts是一個很好的起點。

          3. 頁面導航.頁面導航將是今后的一個發展方向,事實上,這樣做,使系統的脈絡更加清晰。通過一個配置文件,即可把握整個系統各部分之間的聯系,這對于后期的維護有著莫大的好處。尤其是當另一批開發者接手這個項目時,這種優勢體現得更加明顯。

          4. 提供Exception處理機制 .

          5. 數據庫鏈接池管理

          6. 支持I18N

          缺點:

          一、 轉到展示層時,需要配置forward,每一次轉到展示層,相信大多數都是直接轉到jsp,而涉及到轉向,需要配置forward,如果有十個展示層的jsp,需要配置十次struts,而且還不包括有時候目錄、文件變更,需要重新修改forward,注意,每次修改配置之后,要求重新部署整個項目,而tomcate這樣的服務器,還必須重新啟動服務器,如果業務變更復雜頻繁的系統,這樣的操作簡單不可想象?,F在就是這樣,幾十上百個人同時在線使用我們的系統,大家可以想象一下,我的煩惱有多大。

          二、 Struts 的Action必需是thread-safe方式,它僅僅允許一個實例去處理所有的請求。所以action用到的所有的資源都必需統一同步,這個就引起了線程安全的問題。

          三、 測試不方便. Struts的每個Action都同Web層耦合在一起,這樣它的測試依賴于Web容器,單元測試也很難實現。不過有一個Junit的擴展工具Struts TestCase可以實現它的單元測試。

          四、 類型的轉換. Struts的FormBean把所有的數據都作為String類型,它可以使用工具Commons-Beanutils進行類型轉化。但它的轉化都是在Class級別,而且轉化的類型是不可配置的。類型轉化時的錯誤信息返回給用戶也是非常困難的。

          五、 對Servlet的依賴性過強. Struts處理Action時必需要依賴ServletRequest 和ServletResponse,所有它擺脫不了Servlet容器。

          六、 前端表達式語言方面.Struts集成了JSTL,所以它主要使用JSTL的表達式語言來獲取數據??墒荍STL的表達式語言在Collection和索引屬性方面處理顯得很弱。

          七、 對Action執行的控制困難. Struts創建一個Action,如果想控制它的執行順序將會非常困難。甚至你要重新去寫Servlet來實現你的這個功能需求。

          八、 對Action 執行前和后的處理. Struts處理Action的時候是基于class的hierarchies,很難在action處理前和后進行操作。

          九、 對事件支持不夠. 在struts中,實際是一個表單Form對應一個Action類(或DispatchAction),換一句話說:在Struts中實際是一個表單只能對應一個事件,struts這種事件方式稱為application event,application event和component event相比是一種粗粒度的事件。

          Struts重要的表單對象ActionForm是一種對象,它代表了一種應用,這個對象中至少包含幾個字段,這些字段是Jsp頁面表單中的input字段,因為一個表單對應一個事件,所以,當我們需要將事件粒度細化到表單中這些字段時,也就是說,一個字段對應一個事件時,單純使用Struts就不太可能,當然通過結合JavaScript也是可以轉彎實現的。

           

                 2.Hibernate的優缺點:

          Hibernate是一個開放源代碼的對象關系映射框架,它對JDBC進行了非常輕量級的對象封裝,使得Java程序員可以隨心所欲的使用對象編程思維來操縱數據庫。

          Hibernate可以應用在任何使用JDBC的場合,既可以在Java的客戶端程序實用,也可以在Servlet/JSP的Web應用中使用,最具革命意義的是,Hibernate可以在應用EJB的J2EE架構中取代CMP,完成數據持久化的重任。

          大多數開發機構經常采取創建各自獨立的數據持久層。一旦底層的數據結構發生改變,那么修改應用的其余部分使之適應這種改變的代價將是十分巨大的。Hibernate適時的填補了這一空白,它為Java應用提供了一個易用的、高效率的對象關系映射框架。hibernate是個輕量級的持久性框架,功能卻非常豐富。

          優點:

          a. Hibernate 使用 Java 反射機制而不是字節碼增強程序來實現透明性。

          b. Hibernate 的性能非常好,因為它是個輕量級框架。映射的靈活性很出色。

          c. 它支持各種關系數據庫,從一對一到多對多的各種復雜關系。

          缺點:它限制您所使用的對象模型。(例如,一個持久性類不能映射到多個表)其獨有的界面和可憐的市場份額也讓人不安,盡管如此,Hibernate 還是以其強大的發展動力減輕了這些風險。其他的開源持久性框架也有一些,不過都沒有 Hibernate 這樣有市場沖擊力。

          3. Spring框架的優缺點

          它是一個開源的項目,而且目前非?;钴S;它基于IoC(Inversion of Control,反向控制)和AOP的構架多層j2ee系統的框架,但它不強迫你必須在每一層中必須使用Spring,因為它模塊化的很好,允許你根據自己的需要選擇使用它的某一個模塊;它實現了很優雅的MVC,對不同的數據訪問技術提供了統一的接口,采用IoC使得可以很容易的實現bean的裝配,提供了簡潔的AOP并據此實現Transcation Managment,等等

          優點

             a. Spring能有效地組織你的中間層對象,不管你是否選擇使用了EJB。如果你僅僅使用了Struts或其他為J2EE的 API特制的framework,Spring致力于解決剩下的問題。

             b. Spring能消除在許多工程中常見的對Singleton的過多使用。根據我的經驗,這是一個很大的問題,它降低了系統的可測試性和面向對象的程度。

             c. 通過一種在不同應用程序和項目間一致的方法來處理配置文件,Spring能消除各種各樣自定義格式的屬性文件的需要。曾經對某個類要尋找的是哪個魔法般的屬性項或系統屬性感到不解,為此不得不去讀Javadoc甚至源編碼?有了Spring,你僅僅需要看看類的JavaBean屬性。Inversion of Control的使用(在下面討論)幫助完成了這種簡化。

            d.  通過把對接口編程而不是對類編程的代價幾乎減少到沒有,Spring能夠促進養成好的編程習慣。

            e. Spring被設計為讓使用它創建的應用盡可能少的依賴于他的APIs。在Spring應用中的大多數業務對象沒有依賴于Spring。

            f. 使用Spring構建的應用程序易于單元測試。

            g.  Spring能使EJB的使用成為一個實現選擇,而不是應用架構的必然選擇。你能選擇用POJOs或local EJBs來實現業務接口,卻不會影響調用代碼。

            h. Spring幫助你解決許多問題而無需使用EJB。Spring能提供一種EJB的替換物,它們適用于許多web應用。例如,Spring能使用AOP提供聲明性事務管理而不通過EJB容器,如果你僅僅需要與單個數據庫打交道,甚至不需要一個JTA實現。

            i.  Spring為數據存取提供了一個一致的框架,不論是使用的是JDBC還是O/R mapping產品

          Spring確實使你能通過最簡單可行的解決辦法來解決你的問題。而這是有有很大價值的。

          缺點:使用人數不多、jsp中要寫很多代碼、控制器過于靈活,缺少一個公用控制器

          8.       是否了解設計模式,將幾種常用的設計模式的思想、并舉例。

          創建模式:

          Factory、Prototype(原型)、Builder、Singleton

          結構模式:

          Facade(外觀)、Proxy(代理)、Adapter(適配器)、Composite(組合)、Decorator(油漆工)、Bridge、Flyweight(享元)

          行為模式:

          Template、Memento(備忘機制)、Observer、Chain of Responsibility(職責鏈)、Command、State、Strategy(策略)、Mediator(中介者)、Interdivter(解釋器)、Visitor

          9.       頁面重復刷新,如何解決?客戶的行為是無法控制的,那如何控制客戶的重復刷新導致的重復提交。

          Jsp防止頁面刷新表單自提交重復提交思路:

          1. 提交后禁用提交按鈕(大部分人都是這樣做的)

          2. 用javascript實現

          10.   如何精確記錄同時在線人數

             我們可以利用Servlet規范中定義的事件監聽器(Listener)來解決這個問題,實現更準確的在線人數統計功能。對每一個正在訪問的用戶,J2EE應用服務器會為其建立一個對應的HttpSession對象。當一個瀏覽器第一次訪問網站的時候,J2EE應用服務器會新建一個HttpSession對象,并觸發HttpSession創建事件,如果注冊了HttpSessionListener事件監聽器,則會調用HttpSessionListener事件監聽器的sessionCreated方法。相反,當這個瀏覽器訪問結束超時的時候,J2EE應用服務器會銷毀相應的HttpSession對象,觸發HttpSession銷毀事件,同時調用所注冊HttpSessionListener事件監聽器的sessionDestroyed方法。

            可見,對應于一個用戶訪問的開始和結束,相應的有sessionCreated方法和sessionDestroyed方法執行。這樣,我們只需要在HttpSessionListener實現類的sessionCreated方法中讓計數器加1,在sessionDestroyed方法中讓計數器減1,就輕松實現了網站在線人數的統計功能。

            下面就是利用HttpSessionListener實現在線人數統計的一個例子,這個例子已經在中創軟件的J2EE應用服務器InforWeb中測試通過。

            首先,編寫一個簡單的計數器,代碼如下:

          package gongfei.cmc.articles.onlinecounter;

          public class OnlineCounter {

              private static long online = 0;    

              public static long getOnline() {

                  return online;

              }    

              public static void raise(){

                  online++;

              } 

              public static void reduce(){

                  online--;

             }

          }

            然后,編寫HttpSessionListener實現類,在這個實現類的sessionCreated方法中調用OnlineCounter的raise方法,在sessionDestroyed方法中調用OnlineCounter的reduce方法,代碼如下:

          package gongfei.cmc.articles.onlinecounter;

          import javax.servlet.http.HttpSessionEvent;

          import javax.servlet.http.HttpSessionListener;

          public class OnlineCounterListener implements HttpSessionListener {

              public void sessionCreated(HttpSessionEvent hse) {

                  OnlineCounter.raise();

              }

              public void sessionDestroyed(HttpSessionEvent hse) {

                  OnlineCounter.reduce();

              }

          }

            再然后,把這個HttpSessionListener實現類注冊到網站應用中,也就是在網站應用的web.xml中加入如下內容:

          <web-app>

              ……

              <listener>

                  <listener-class>

                      gongfei.cmc.articles.example.OnlineCounterListener

                  </listener-class>

              </listener>

              ……

          </web-app>

          OK,在線人數統計功能已經實現,只要在JSP頁面中加入下面這樣的腳本就能顯示但前在線人數了:

          <%@ page language="java" pageEncoding="GB2312" %>

          <%@ page language="java" pageEncoding="GB2312" %>

          <%@ page import="gongfei.cmc.articles.onlinecounter.OnlineCounter" %>

          <html>

              <head><title>On Line Counert</title></head>

              <body bgcolor="#FFFFFF">

                  On line:<%=OnlineCounter.getOnline()%>

              </body>

          </html>

          11.   亂碼解決方案。是否遇到過?有哪些?講解具體遇到的情形,并說出你在具體的應用中的解決方案。

          1.JSP輸出中文的亂碼問題

          所謂在jsp輸出中文,即直接在jsp中輸出中文,或者給變量賦中文值再輸出等,這種情況下的亂碼問題往往是因為沒有給jsp頁面制定顯示中文字符的編碼方式,解決辦法如下:

          1)在jsp頁面頭部加上語句<%@ page contentType="text/html;charset=utf-8"%>(在Servlet中使用httpServletResponse.setContentType("text/html;charset=utf-8"),最好同時在jsp頁面的head部分加上<meta http-equiv="Content-Type" content="text/html;charset="utf-8">

          2)在每次要輸出中文的地方主動轉換編碼方式,比如要在頁面中輸入“中文”二字,就可以用以下的方法:

          <%

             String str="中文";

             byte[] tmpbyte=str.getBytes("ISO8859_1");

             str=new String(tmpbyte);

             out.println(str);

          %>

          對于以上這兩種方法,顯然第一種方法更通用一點,只需要在一個頁面中添加一次代碼即可;而對于第二種方法,在每個需要輸出中文的地方都需要轉碼,如果這樣的地方很多,這將是一個繁重的工作。

          2.獲取表單提交的數據時的中文亂碼問題

          在沒有加任何其他處理之前,用request.getParameter("paramName")獲取表單提交中的數據,且表單數據中含有中文時,返回的字符串會呈現亂碼。出現這種問題的原因是Tomcat的j2ee實現對表單提交,即以POST方式提交的參數采用默認的ISO-8859-1來處理。

          解決此問題的辦法有兩個:

          1)不修改其他配置,只是在將表單中的中文數據區出來后再轉換編碼,方法如語句 String str=request.getParameter("chStr");String str = new String(str.getBytes("ISO-8859-1"),"UTF-8");但這種方法只是從一個局部來考慮問題,如果這樣的情況很多,就要寫很多次,勢必加大工作量。

          2)讓對所有頁面的請求都通過一個Filter,將處理字符集設置為utf-8(根據自己需要也可以設置成其他的,如gb2312,gbk)。具體做法參考Tomcat的webapps/servlet-exemples目錄有一個完整的例子,也可以參考其中web.xml和SetCharacterEncodingFilter的配置.

          3.URL中的中文問題

          對于直接通過在url中傳遞中文參數,如http://localhost:8080/a.jsp?str="中文"這樣的get請求,在服務器端用request.getParameter("name")時返回的往往是亂碼。按照以上的做法設置Filter沒有用,用request.setCharacterEncoding("utf-8")的方式,仍然不管用。造成這種結果的原因是Tomcat中以get方式提交的請求對query-string處理時采用了和post方法不一樣的處理方式。

          解決這個問題的方法是是打開Tomcat安裝目錄下的/conf/server.xml文件,找到Connector塊,往其中添加URLEncoding="utf-8"/>

          4.數據庫訪問時的亂碼問題

          數據庫中所有表的編碼方式和jsp中的使用的編碼要保持一致,這樣做的目的可以減少不必要的編碼轉換問題.另外,在使用jdbc連接MySQL數據庫時,連接字符串寫成如下形式可以避免一些中文問題:

          jdbc://mysql://hostname:port/DBname?user=username&password=pwd&useUnicode=true&character Encoding=utf-8

          如果是以數據源的方式連接數據庫,配置文件中使用:

          <parameter>

             <name>url</name>

             <value>jdbc:mysql://hostname:port/DBname?&useUnicode=true&characterEncoding=utf-8

             </value>

          </parameter>

          但是如果使用一個已經存在的數據庫,數據庫的編碼方式為ISO-8859-1,而Web應用中使用的utf-8,且數據庫已經有很多重要的信息,因此不能通過更改數據庫的編碼方式來解決。這個時候,在往數據庫中寫數據時,一定要在jdbc連接字符串中加入“useUnicode=true&characterEncoding=ISO-8859-1”,這樣可以順利的王數據庫寫入正常的數據。但是,在將數據讀出數據庫時,亂碼又會出現,這個時候就應該在數據取出時對其轉碼,可以將轉碼功能寫為一個函數,具體實現如下:

          public String charConvert(String src){

          String result=null;

          if(src!=null){

            try{

             result=new String(src.getBytes("ISO-8859-1"),"UTF-8");

            }catch(Exception e){

             result=null;

            }

          }

          return result;

          }

          于是,在數據庫讀出數據過后調用charConvert(rs.getString("colName"));這樣就可以正常的顯示數據庫中的中文數據了。

          posted @ 2010-01-13 15:26 webber 閱讀(366) | 評論 (0)編輯 收藏

          主站蜘蛛池模板: 博白县| 马公市| 广丰县| 灌云县| 滨州市| 沐川县| 竹山县| 谢通门县| 来安县| 高阳县| 岳池县| 宽甸| 乐业县| 潼关县| 静海县| 高阳县| 巴里| 南安市| 诏安县| 岢岚县| 邵武市| 六枝特区| 太白县| 永昌县| 吉木萨尔县| 敦化市| 沽源县| 东兴市| 信丰县| 榆林市| 沛县| 平昌县| 西乌珠穆沁旗| 金门县| 客服| 龙江县| 郓城县| 汉阴县| 怀安县| 平遥县| 文成县|