2010年2月11日
#
在Java的世界里,無論類還是各種數(shù)據(jù),其結(jié)構(gòu)的處理是整個程序的邏輯以及性能的關(guān)鍵。由于本人接觸了一個有關(guān)性能與邏輯同時并存的問題,于是就開始研究這方面的問題。找遍了大大小小的論壇,也把《Java 虛擬機(jī)規(guī)范》,《apress,.java.collections.(2001),.bm.ocr.6.0.shareconnector》,和《Thinking in Java》翻了也找不到很好的答案,于是一氣之下把JDK的 src 解壓出來研究,擴(kuò)然開朗,遂寫此文,跟大家分享感受和順便驗證我理解還有沒有漏洞。 這里就拿HashMap來研究吧。
HashMap可謂JDK的一大實用工具,把各個Object映射起來,實現(xiàn)了“鍵--值”對應(yīng)的快速存取。但實際里面做了些什么呢?
在這之前,先介紹一下負(fù)載因子和容量的屬性。大家都知道其實一個 HashMap 的實際容量就 因子*容量,其默認(rèn)值是 16×0.75=12; 這個很重要,對效率很一定影響!當(dāng)存入HashMap的對象超過這個容量時,HashMap 就會重新構(gòu)造存取表。這就是一個大問題,我后面慢慢介紹,反正,如果你已經(jīng)知道你大概要存放多少個對象,最好設(shè)為該實際容量的能接受的數(shù)字。
兩個關(guān)鍵的方法,put和get:
先有這樣一個概念,HashMap是聲明了 Map,Cloneable, Serializable 接口,和繼承了 AbstractMap 類,里面的 Iterator 其實主要都是其內(nèi)部類HashIterator 和其他幾個 iterator 類實現(xiàn),當(dāng)然還有一個很重要的繼承了Map.Entry 的 Entry 內(nèi)部類,由于大家都有源代碼,大家有興趣可以看看這部分,我主要想說明的是 Entry 內(nèi)部類。它包含了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);
這連續(xù)的兩步就是 HashMap 最牛的地方!研究完我都汗顏了,其中 hash 就是通過 key 這個Object的 hashcode 進(jìn)行 hash,然后通過 indexFor 獲得在Object table的索引值。
table???不要驚訝,其實HashMap也神不到哪里去,它就是用 table 來放的。最牛的就是用 hash 能正確的返回索引。其中的hash算法,我跟JDK的作者 Doug 聯(lián)系過,他建議我看看《The art of programing vol3》可恨的是,我之前就一直在找,我都找不到,他這樣一提,我就更加急了,可惜口袋空空?。。?!
不知道大家有沒有留意 put 其實是一個有返回的方法,它會把相同鍵值的 put 覆蓋掉并返回舊的值!如下方法徹底說明了 HashMap 的結(jié)構(gòu),其實就是一個表加上在相應(yīng)位置的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; //把新的值賦予給對應(yīng)鍵值。
e.recordAccess(this); //空方法,留待實現(xiàn)
return oldvalue; //返回相同鍵值的對應(yīng)的舊的值。
}
}
modCount++; //結(jié)構(gòu)性更改的次數(shù)
addEntry(hash, k, value, i); //添加新元素,關(guān)鍵所在!
return null; //沒有相同的鍵值返回
}
我們把關(guān)鍵的方法拿出來分析:
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,那它經(jīng)過indexfor之后的索引一定都為i,這樣在new的時候這個Entry的next就會指向這個原本的table[i],再有下一個也如此,形成一個鏈表,和put的循環(huán)對定e.next獲得舊的值。到這里,HashMap的結(jié)構(gòu),大家也十分明白了吧?
if (size++ >= threshold) //這個threshold就是能實際容納的量
resize(2 * table.length); //超出這個容量就會將Object table重構(gòu)
所謂的重構(gòu)也不神,就是建一個兩倍大的table(我在別的論壇上看到有人說是兩倍加1,把我騙了),然后再一個個indexfor進(jìn)去!注意!!這就是效率??!如果你能讓你的HashMap不需要重構(gòu)那么多次,效率會大大提高!
說到這里也差不多了,get比put簡單得多,大家,了解put,get也差不了多少了。對于collections我是認(rèn)為,它是適合廣泛的,當(dāng)不完全適合特有的,如果大家的程序需要特殊的用途,自己寫吧,其實很簡單。(作者是這樣跟我說的,他還建議我用LinkedHashMap,我看了源碼以后發(fā)現(xiàn),LinkHashMap其實就是繼承HashMap的,然后override相應(yīng)的方法,有興趣的同人,自己looklook)建個 Object table,寫相應(yīng)的算法,就ok啦。
舉個例子吧,像 Vector,list 啊什么的其實都很簡單,最多就多了的同步的聲明,其實如果要實現(xiàn)像Vector那種,插入,刪除不多的,可以用一個Object table來實現(xiàn),按索引存取,添加等。
如果插入,刪除比較多的,可以建兩個Object table,然后每個元素用含有next結(jié)構(gòu)的,一個table,如果要插入到i,但是i已經(jīng)有元素,用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);
}
}
}
//結(jié)果輸出
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對象元素的記錄數(shù)是:"+hashMap.size());
// hashMap.remove(null);
// System.out.println("刪除鍵值為null的HashMap對象元素的記錄數(shù)是:"+hashMap.size());
// }
第一種:
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來進(jìn)行遍歷,但兩者的遍歷速度是有差別的,下面請看實例: |
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() 返回與此項對應(yīng)的鍵
// entry.getValue() 返回與此項對應(yīng)的值
System.out.print(entry.getValue());
}
System.out.println();
System.out.println(Calendar.getInstance().getTimeInMillis() - bs);
}
對于keySet其實是遍歷了2次,一次是轉(zhuǎn)為iterator,一次就從hashmap中取出key所對于的value。而entryset只是遍歷了第一次,他把key和value都放到了entry中,所以就快了。
注:Hashtable的遍歷方法和以上的差不多!
|
web.xml
// 這里不需要配置字符過濾,網(wǎng)上有的例子加了,實際上
webwork.properties里設(shè)置如下就可以了頁面也是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> //這里是把a(bǔ)pplicationContext里配置的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);
}
}
基于webwork spring hibernate 項目的開發(fā)
這三者的結(jié)合,應(yīng)該是java web編程最好的模式。
首先說明三者各自負(fù)責(zé)的職務(wù):
1 hibernate 負(fù)責(zé)數(shù)據(jù)庫的操作
2 spring 負(fù)責(zé)真正的業(yè)務(wù)操作
3 webwork 負(fù)責(zé)請求轉(zhuǎn)交,并把spring的處理結(jié)果返回給用戶
以往的開發(fā)中,很多人注重MVC模式。的確,這種模式把程序以面向?qū)ο蟮乃枷敕殖?了三個部分。但在web開發(fā)中,并不能單純的運(yùn)用此種模式:web開發(fā)的View是固定的(頁面),而在引入hibernate后,model這一塊也非常簡單和清晰。就剩下control了,這是web開發(fā)的關(guān)鍵部分,現(xiàn)在流行的做法便是將control細(xì)分成兩個部分:dispacher(轉(zhuǎn)交器)和business object(處理業(yè)務(wù)邏輯的對象)。并將后者抽出接口,甚至和model共享接口,一邊真正做到對dispacher隱藏邏輯實現(xiàn)。
而這種M-V-D-B(model-view-dispacher-business object)模式的實現(xiàn)有好多方式。比如一個bo(business object)對象的創(chuàng)建,你可以直接 new,也可以動態(tài)加載,采用工廠方法,抽象工廠。但最好的就是用spring容器。dispacher只管用接口就行了,具體類已經(jīng)有spring的 AOP給注入了。
當(dāng)然spring也可以很好地和hibernate結(jié)合,你可以采用DAO,也可以用spring的hibernate 模板。但這都不重要,因為你的業(yè)務(wù)對象已經(jīng)和調(diào)用層徹底分開了,當(dāng)業(yè)務(wù)層需要和hibernate打交道的時候,直接做個HibernateUtil也未嘗不可呀。怎么做都已經(jīng)不是關(guān)鍵。
下面就具體介紹spring webwork的結(jié)合方式。
在webwork 中的wiki doc中有三種結(jié)合方式(google查),我這里采用的最后一種--采用一個自動裝配的攔截器com.opensymphony.xwork.spring.interceptor.ActionAutowiringInterceptor關(guān)鍵代碼如下:
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文件,文件只包含下面的內(nèi)容
webwork.objectFactory=spring
這種情況下,所有的對象都至少會試圖使用Spring來創(chuàng)建.如果它們不能被Spring創(chuàng)建,然后WebWork會自己創(chuàng)建對象.接下來,在
web.xml打開Spring的Listener
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
由于使用標(biāo)準(zhǔn)的Listener來集成Spring,它可以被配置來支持除了applicationContext.xml之外的配置文件.把下面的幾行添加到
web.xml會讓Spring的ApplicationContext從所有匹配給定的規(guī)則的文件中初始化:
<!-- Context Configuration locations for Spring XML files -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext-*.xml,classpath*:applicationContext
</context-param>
根據(jù)需要配置相應(yīng)的spring上下文文。
(2)、在spring中初始化Action
正常情況下,在xwork.xml里可以為每個action指定類.當(dāng)你使用SpringObjectFactory時WebWork會請求Spring來
創(chuàng)建action并按照缺省指定的自動裝配行為來裝配依賴的組件.SpringObjectFactory 也會設(shè)置所有的bean的后置處理程序
(post processors)來完成類似對Action進(jìn)行事務(wù),安全等等方面的代理的事情.Spring可以不依賴外在的配置來自動確定.
對于大多數(shù)的使用,這就是全部需要的了,用來配置action,設(shè)置它們獲取服務(wù)和依賴組件.
強(qiáng)烈推薦使用一種聲明式的方法來讓Spring知道為action提供什么.這包括讓bean能夠自動裝配,無論是把Action里的
依賴的屬性命名為和Spring應(yīng)該提供的Bean的名字一致(這允許基于名字的自動裝配),或者使用by type方式的自動裝配,也就是在注冊到
Spring的Bean中需要的類型僅擁有一個.也可以包括使用JDK5的標(biāo)準(zhǔn)來聲明事務(wù)和安全需求,而不是必須在你的Spring配置里明確設(shè)置代理.
如果能找到方法讓Spring在沒有任何明確的配置(在_applicationContext.xml_中)的情況下知道需要為action做什么,那么就不
需要在兩個地方維護(hù)這個配置了.
當(dāng)然,有時候可能想要Spring完全來管理bean.這是有實際意義的,例如,如果想要為bean設(shè)置更復(fù)雜的AOP或者Spring相關(guān)的技術(shù),
例如Acegi.為了達(dá)到這個目的,所有必須要做的事情就是在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中對應(yīng)的class值一致。
2、 基于Hibernate3的原生API實現(xiàn)DAO
Hibernate 3.0.1引入了一個新的特性:“帶上下文環(huán)境的Session”。 這一特性使得Hibernate自身具備了每個事務(wù)綁定當(dāng)前 Session 對象的功能。
這與Spring中每個Hibernate的 Session 與事務(wù)同步的功能大致相同。
(1)、 為Dao創(chuàng)建基類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中實現(xiàn)具體持久化操作
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>
重啟服務(wù),在web頁面上觸發(fā)register的action,執(zhí)行后,拋出下面的異常:
Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
google了下,大概明白了是因為沒有配置了事務(wù)導(dǎo)致的錯誤。在配置事務(wù)之前,查看了以前的一個采用HibernateDaoSupport實現(xiàn)的項目,記得
當(dāng)時并不需要配置事務(wù)就可以正常運(yùn)行。于是,讓UserDao繼承于HibernateDaoSupport,修改后的代碼如下:
public class UserDao extends BaseDao implements IUserDao {
public void saveUser(User user) throws HibernateException {
getHibernateTemplate().save(user);
}
}
接下去,修改spring上下文中的相關(guān)配置,
<!--
<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>
保存修改后的,重啟服務(wù),再次觸發(fā)register的action,用戶信息成功保存。
去掉HibernateDaoSupport的dao實現(xiàn)后,又換回基于hibernate3.0原生API的實現(xiàn)方式,根據(jù)之前google后的結(jié)果,給userService配置
事務(wù),拷貝了下之前項目中的配置,并做相應(yīng)修改,修改后的內(nèi)容如下:
<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>
保存修改內(nèi)容,重啟服務(wù),重啟中出現(xiàn)錯誤,查看了spring in action中的相關(guān)配置,發(fā)現(xiàn)baseTransaction這個bean的配置稍有不同,
上面那個配置是參考springside的,當(dāng)時那個項目趕,就直接拿過來用,也沒出現(xiàn)問題,就不認(rèn)真去考慮,現(xiàn)在拷貝到現(xiàn)有項目中,卻出錯了,
于是先根據(jù)書上的介紹做相應(yīng)修改,改后的內(nèi)容如下:
<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",保存修改
重啟服務(wù),并再次觸發(fā)register的action,一切如所愿。
雖然大多數(shù)web文檔的頂部都有doctype聲明,但很多人都沒有注意它。它是在你新建一個文檔時,由web創(chuàng)作軟件草率處理的眾多細(xì)節(jié)之一。
雖然doctype被許多人忽視,但在遵循標(biāo)準(zhǔn)的任何web文檔中,它都是一項必需的元素。doctype會影響代碼驗證,并決定了瀏覽器最終如何顯示你的web文檔。
doctype的作用
doctype聲明指出閱讀程序應(yīng)該用什么規(guī)則集來解釋文檔中的標(biāo)記。在web文檔的情況下,“閱讀程序”通常是瀏覽器或者校驗器這樣的一個程序,“規(guī)則”則是w3c所發(fā)布的一個文檔類型定義(dtd)中包含的規(guī)則。
每個dtd都包括一系列標(biāo)記、attributes和properties,它們用于標(biāo)記web文檔的內(nèi)容;此外還包括一些規(guī)則,它們規(guī)定了哪些標(biāo)記能出現(xiàn)在其他哪些標(biāo)記中。每個web建議標(biāo)準(zhǔn)(比如html 4 frameset和xhtml 1.0 transitional)都有自己的dtd。
假如文檔中的標(biāo)記不遵循doctype聲明所指定的dtd,這個文檔除了不能通過代碼校驗之外,還有可能無法在瀏覽器中正確顯示。對于標(biāo)記不一致的問題,瀏覽器相較于校驗器來說更寬容。但是,不正確的doctype聲明經(jīng)常導(dǎo)致網(wǎng)頁不正確顯示,或者導(dǎo)致它們根本不能顯示。
選擇正確的doctype
為了獲得正確的doctype聲明,關(guān)鍵就是讓dtd與文檔所遵循的標(biāo)準(zhǔn)對應(yīng)。例如,假定文檔遵循的是xhtml 1.0 strict標(biāo)準(zhǔn),文檔的doctype聲明就應(yīng)該引用相應(yīng)的dtd。另一方面,如果doctype聲明指定的是xhtml dtd,但文檔包含的是舊式風(fēng)格的html標(biāo)記,就是不恰當(dāng)?shù)?;類似地,如果doctype聲明指定的是html dtd,但文檔包含的是xhtml 1.0 strict標(biāo)記,同樣是不恰當(dāng)?shù)摹?br />
有的時候,也可以根本不使用一個doctype聲明。如果沒有指定有效的doctype聲明,大多數(shù)瀏覽器都會使用一個內(nèi)建的默認(rèn)dtd。在這種情況下,瀏覽器會用內(nèi)建的dtd來試著顯示你所指定的標(biāo)記。對于一些臨時性的、匆忙拼湊的文檔(這種文檔有許多),你確實可以考慮省略doctype聲明,并接受瀏覽器的默認(rèn)顯示。
完全可以從頭編寫一個doctype聲明,并讓它指向自己選擇的一個dtd。然而,由于大多數(shù)web文檔都需要遵循由w3c發(fā)布的某個國際公認(rèn)的web標(biāo)準(zhǔn),所以那些文檔通常都要包含以下標(biāo)準(zhǔn)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>標(biāo)記以及其他文檔內(nèi)容之前。注意,在xhtml文檔中,doctype的前面偶爾會出現(xiàn)一條xml處理指令(也稱為xml prolog):
<@xml version="1.0" encoding="utf-8"@>
為了確保網(wǎng)頁正確顯示和順利通過驗證,使用正確的doctype是關(guān)鍵。與內(nèi)容相反的、不正確的或者形式錯誤的doctype是大量問題的罪魁禍?zhǔn)?。在未來的專欄文章中,我還會具體解釋如何診斷及糾正這些問題。
用dw設(shè)計網(wǎng)頁時,新建一個文件,看代碼最前面總要出現(xiàn)一個下面的東東,
<!doctype html public "-/w3c/dtd html 4.01 transitional/en"
"http://www.w3.org/tr/html4/loose.dtd">
這個是dw自動在網(wǎng)頁文件頁增加了dtd信息.可以刪.
刪除后,瀏覽器會使用的默認(rèn)dtd.
|