今日開(kāi)始進(jìn)行OA項(xiàng)目了,OA是一個(gè)大型的辦公自動(dòng)化管理系統(tǒng)。湯老師使用6天的時(shí)間帶領(lǐng)我們做這個(gè)項(xiàng)目,顯然是不可能全部完成的,我們要做其中重點(diǎn)的幾個(gè)模塊。這個(gè)項(xiàng)目將對(duì)我們之前學(xué)習(xí)的struts1、hibernate3、jbpm3、jstl1.1、junit4進(jìn)行綜合性的系統(tǒng)練習(xí)。
在跟著老師學(xué)習(xí)新技術(shù)時(shí),課堂上我們能很好的理解各知識(shí)點(diǎn)。但放到一起,在實(shí)際項(xiàng)目中應(yīng)用時(shí)還時(shí)感覺(jué)有點(diǎn)陌生。不過(guò)還好,以前的工作經(jīng)驗(yàn)和每天整理學(xué)習(xí)日志讓這個(gè)項(xiàng)目的開(kāi)端并未對(duì)我構(gòu)成多大挑戰(zhàn)。但這對(duì)一個(gè)新人來(lái)說(shuō),是需要反復(fù)揣摩的,也正是這種實(shí)踐性的練習(xí)才讓我們掌握和混用理論性的知識(shí)。
做為一個(gè)應(yīng)用級(jí)別的開(kāi)發(fā)人員不僅要熟練掌握編程技術(shù),而且也要對(duì)各業(yè)務(wù)流程有一定的了解。這在日后的開(kāi)發(fā)工作中將起到重要的作用。說(shuō)的直白些,就是熟練、穩(wěn)健的使用編程技術(shù)將業(yè)務(wù)流程搭建起來(lái)。所以在這種實(shí)踐性項(xiàng)目中,我不僅要摸清各項(xiàng)技術(shù)的應(yīng)用,也要熟悉業(yè)務(wù)流程,更要學(xué)好項(xiàng)目的架構(gòu)。但是這個(gè)項(xiàng)目多少有些讓我失望,因?yàn)樗闹攸c(diǎn)是讓同學(xué)們將之前學(xué)習(xí)的知識(shí)做個(gè)綜合練習(xí),所以沒(méi)有從詳細(xì)的需求分析、編程文檔...一一到來(lái)。
早上湯兄只是簡(jiǎn)單的詳解了OA各大模塊,然后選出了其中的幾個(gè)小模塊做練習(xí)。下面我來(lái)總結(jié)一下。
一、OA辦公自動(dòng)化系統(tǒng)
利用網(wǎng)絡(luò)通訊基礎(chǔ)及先進(jìn)的網(wǎng)絡(luò)應(yīng)用平臺(tái),建設(shè)一個(gè)安全、可靠、開(kāi)放、高效的信息網(wǎng)絡(luò)和辦公自動(dòng)化、信息管理電子化系統(tǒng),為管理部門(mén)提供現(xiàn)代化的日常辦公條件及豐富的綜合信息服務(wù),實(shí)現(xiàn)檔案管理自動(dòng)化和辦公事務(wù)處理自動(dòng)化,以提高辦公效率和管理水平,實(shí)現(xiàn)企業(yè)各部門(mén)日常業(yè)務(wù)工作的規(guī)范化、電子化、標(biāo)準(zhǔn)化,增強(qiáng)檔案部門(mén)文書(shū)檔案、人事檔案、科技檔案、 財(cái)務(wù)檔案等檔案的可管理性,實(shí)現(xiàn)信息的在線查詢、借閱,最終實(shí)現(xiàn)"無(wú)紙"辦公。
其詳細(xì)資料可以查詢互聯(lián)網(wǎng)...。
二、環(huán)境搭建
1.開(kāi)發(fā)環(huán)境MyEclipse
2.創(chuàng)建一個(gè)WEB工程
3.在工程下添加資源目錄:
process:jbpm使用的目錄
config:hibernate、jbpm等工程使用的配置文件
test:?jiǎn)卧獪y(cè)試類存放的目錄
4.在WebRoot目錄下添加:
script:腳本文件存放目錄
style:CSS樣式文件存放目錄
5.在WEB-INF目錄下添加:
pages:頁(yè)面存放目錄
pages/department:與部門(mén)相關(guān)的頁(yè)面
pages/employee:與職員相關(guān)的頁(yè)面
pages/role:與角色相關(guān)的頁(yè)面,角色是一種權(quán)限分配的方法。經(jīng)理、人事、秘書(shū)...這就是角色,角色具有相應(yīng)的權(quán)限組。比如經(jīng)理具有審核員工、要員調(diào)動(dòng)等權(quán)限。
6.向工程中導(dǎo)入jar文件:
struts1、hibernate、mysql、jbpm、junit4相應(yīng)該的jar文件。
可以在工程上右鍵-->"MyEclipse"-->"...",添加相應(yīng)的框架jar文件。
7.向工程中添加配置文件:
Struts1和hibernater的配置文件。
三、基礎(chǔ)功能
1.DAO:
將WEB應(yīng)用分三層架構(gòu),這樣使得程序更加易于編寫(xiě)與維護(hù)。三層架構(gòu)并不總適合于各WEB應(yīng)用,在某些WEB應(yīng)用中業(yè)務(wù)邏輯簡(jiǎn)單,這樣使用三層就沒(méi)必要了。所以將service與dao層合并到一起。湯兄說(shuō)先搞成三層的,然后我們?cè)倏从袥](méi)有必要這樣搞。然后再搞成二層的。
我們的層與層之間使用接口連接,這樣是為了可以方便的更換其中某一層的實(shí)現(xiàn)。比如數(shù)據(jù)訪問(wèn)層,我們將數(shù)據(jù)庫(kù)更換為XML文件。但不需要修改service的代碼。這個(gè)早就說(shuō)過(guò)了,只不過(guò)今日再次提出讓大家體會(huì)更深刻些。
湯老師使用staruml工具畫(huà)圖,接口與類的關(guān)系。我也用這個(gè)工具畫(huà)一下,感覺(jué)老好了:

按照上邊的關(guān)系,編寫(xiě)接口與實(shí)現(xiàn)類,這個(gè)非常簡(jiǎn)單。我們使用hibernate訪問(wèn)數(shù)據(jù)庫(kù),所以在DaoBaseImpl類的方法中使用hibernate的Session進(jìn)行數(shù)據(jù)庫(kù)訪問(wèn)。
2.事務(wù)管理:
在一個(gè)請(qǐng)求中,我們?nèi)绾伪WC這個(gè)請(qǐng)求的所有操作使用的是一個(gè)Session?比如,一個(gè)請(qǐng)求同時(shí)調(diào)用了save和delete操作。我們需要增強(qiáng)代碼的重用,如何使得這兩次調(diào)用只打開(kāi)和關(guān)閉一次Session?
我們有什么好方法?編寫(xiě)一個(gè)過(guò)濾器,在調(diào)用doFilter之前打開(kāi)一個(gè)Session,在調(diào)用doFilter返回后關(guān)閉這個(gè)Session。我們這個(gè)過(guò)濾器過(guò)濾一“*.do”的請(qǐng)求。但這樣會(huì)導(dǎo)致,不需要Session操作數(shù)據(jù)庫(kù)的請(qǐng)求也會(huì)創(chuàng)建和關(guān)閉一個(gè)Session,這是完全浪費(fèi)資源的。但我們有一好的解決辦法,看下面的實(shí)現(xiàn)。
我需要需要編寫(xiě)一個(gè)HibernateSessionUtils類,用于管理hibernate的Session:
package cn.itcast.oa.utils; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; public class HibernateSessionUtils { // Session工廠 private static SessionFactory sessionFactory = null; static { sessionFactory = new Configuration().configure().buildSessionFactory(); } // 一個(gè)請(qǐng)求對(duì)應(yīng)一個(gè)線程,為了使一個(gè)請(qǐng)求統(tǒng)一使用一個(gè)session。 // 所以在這里的使用ThreadLocal,ThreadLocal<Session>是Map<Thread, Session>的簡(jiǎn)化形式。 private static ThreadLocal<Session> sessionMap = new ThreadLocal<Session>(); /** * 取當(dāng)前線程的session * 即使使用了過(guò)濾器,但沒(méi)有使用Session的請(qǐng)求是不會(huì)調(diào)用getCurrentSession(true)的。 * @param creat * true且當(dāng)session為null時(shí)自動(dòng)創(chuàng)建session,否則返回null。 * @return */ public static Session getCurrentSession(boolean creat) { Session session = sessionMap.get(); if (creat && session == null) { session = sessionFactory.openSession(); // 任何地方當(dāng)初次使用Session時(shí)便開(kāi)戶事務(wù) session.beginTransaction(); sessionMap.set(session); } return session; } /** * 關(guān)閉和刪除session */ public static void closeAndRemoveSession() { Session session = sessionMap.get(); if (session != null) { session.close(); sessionMap.remove(); } } } |
在一個(gè)請(qǐng)求中,我們?nèi)绾伪WCSession事務(wù)的正確處理?比如,經(jīng)典的銀行轉(zhuǎn)賬示例。Ok,看了上邊的代碼,我們?cè)诔醮问褂?/span>Session時(shí)便開(kāi)戶事務(wù),那何時(shí)提交或回滾事務(wù)呢?我們?cè)谶^(guò)濾器中實(shí)現(xiàn):
package cn.itcast.oa.web.filter; import java.io.IOException; import javax.servlet.*; import org.hibernate.Session; import cn.itcast.oa.utils.HibernateSessionUtils; /** * 過(guò)濾*.do的請(qǐng)求 * @author Administrator * */ public class HibernateSessionFilter implements Filter { public void destroy() { } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { try { // 繼續(xù)調(diào)用Action chain.doFilter(request, response); // 獲取當(dāng)前請(qǐng)求的Session,返回null表示當(dāng)前請(qǐng)求沒(méi)有使用session Session session = HibernateSessionUtils.getCurrentSession(false); if(session != null) // 如果當(dāng)前請(qǐng)求使用了seesion,則提交事務(wù)。 session.getTransaction().commit(); } catch (Exception e) { // 獲取當(dāng)前請(qǐng)求的Session,返回null表示當(dāng)前請(qǐng)求沒(méi)有使用session Session session = HibernateSessionUtils.getCurrentSession(false); if(session != null) // 如果當(dāng)前請(qǐng)求使用了seesion,則回滾事務(wù)。 session.getTransaction().rollback(); throw new ServletException(e); } finally { // 關(guān)閉和刪除session HibernateSessionUtils.closeAndRemoveSession(); } } public void init(FilterConfig arg0) throws ServletException { } } |
使用過(guò)濾器進(jìn)行session管理還不算是一個(gè)好的方法。因?yàn)槲覀冞€沒(méi)有實(shí)現(xiàn)service層,所以在這里我們使用了Filter。管理session更好一些的方式是在service層中。因?yàn)?/span>service層中的一個(gè)方法處理一個(gè)業(yè)務(wù)邏輯請(qǐng)求。
四、BEAN對(duì)象管理
我們每編寫(xiě)一個(gè)實(shí)現(xiàn)類的功能時(shí),需要進(jìn)行單元測(cè)試在這里就不多說(shuō)了。
我們?cè)趩卧獪y(cè)試或service層中需要?jiǎng)?chuàng)建Dao的接口對(duì)象,來(lái)完成對(duì)數(shù)據(jù)的訪問(wèn)。如果我們直接new DaoImpl,這樣并未實(shí)現(xiàn)層與層之間的分離。此時(shí)經(jīng)典的工廠模式派上了用場(chǎng),這個(gè)模式從JAVA基礎(chǔ)增強(qiáng)、JAVAWEB到現(xiàn)在都有應(yīng)用過(guò)。
我們通過(guò)工廠獲取需要的接口實(shí)現(xiàn)類對(duì)象,但如果整個(gè)系統(tǒng)中共需要100個(gè)或者更多的不同接口實(shí)現(xiàn)類,難道我們需要在工廠類中添加相應(yīng)數(shù)量的getxxx方法?湯老師經(jīng)驗(yàn)豐富且十分有才,他使用了一個(gè)方法就可以解決獲取任意接口實(shí)現(xiàn)類的對(duì)象:
package cn.itcast.oa.utils; import java.io.*; import java.util.Properties; public class BeanFactory { /** * 獲取指定簡(jiǎn)單接口名對(duì)應(yīng)的實(shí)現(xiàn)類 * @param <T> * @param clazz * @return */ public static <T> T getBean(Class<T> clazz){ InputStream ins = null; try { // 加載BeanFactory.properties屬性文件 Properties pros = new Properties(); ins = BeanFactory.class.getResourceAsStream("/BeanFactory.propreties"); pros.load(ins); // 讀取實(shí)現(xiàn)類名 String className = pros.getProperty(clazz.getSimpleName()); // 返回類實(shí)例 return (T) Class.forName(className).newInstance(); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } finally{ if(ins != null) try { ins.close(); } catch (IOException e) { e.printStackTrace(); throw new RuntimeException(e); } } } } |
這個(gè)BeanFactory非常經(jīng)典!
湯兄授課是對(duì)某一應(yīng)用的實(shí)現(xiàn)由粗糙到細(xì)致、由狂亂到優(yōu)雅,這一教學(xué)方式使得大家懂得最終的實(shí)現(xiàn)方式是如此優(yōu)雅。
湯老師有些內(nèi)項(xiàng),但他十分愿意與學(xué)生們分享學(xué)習(xí)心得,并且給學(xué)生們很大的鼓勵(lì)。老張、老方、老佟、老徐、湯兄都鼓勵(lì)學(xué)生們努力學(xué)習(xí)...非常好!