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

按照上邊的關系,編寫接口與實現類,這個非常簡單。我們使用hibernate訪問數據庫,所以在DaoBaseImpl類的方法中使用hibernate的Session進行數據庫訪問。
2.事務管理:
在一個請求中,我們如何保證這個請求的所有操作使用的是一個Session?比如,一個請求同時調用了save和delete操作。我們需要增強代碼的重用,如何使得這兩次調用只打開和關閉一次Session?
我們有什么好方法?編寫一個過濾器,在調用doFilter之前打開一個Session,在調用doFilter返回后關閉這個Session。我們這個過濾器過濾一“*.do”的請求。但這樣會導致,不需要Session操作數據庫的請求也會創建和關閉一個Session,這是完全浪費資源的。但我們有一好的解決辦法,看下面的實現。
我需要需要編寫一個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(); } // 一個請求對應一個線程,為了使一個請求統一使用一個session。 // 所以在這里的使用ThreadLocal,ThreadLocal<Session>是Map<Thread, Session>的簡化形式。 private static ThreadLocal<Session> sessionMap = new ThreadLocal<Session>(); /** * 取當前線程的session * 即使使用了過濾器,但沒有使用Session的請求是不會調用getCurrentSession(true)的。 * @param creat * true且當session為null時自動創建session,否則返回null。 * @return */ public static Session getCurrentSession(boolean creat) { Session session = sessionMap.get(); if (creat && session == null) { session = sessionFactory.openSession(); // 任何地方當初次使用Session時便開戶事務 session.beginTransaction(); sessionMap.set(session); } return session; } /** * 關閉和刪除session */ public static void closeAndRemoveSession() { Session session = sessionMap.get(); if (session != null) { session.close(); sessionMap.remove(); } } } |
在一個請求中,我們如何保證Session事務的正確處理?比如,經典的銀行轉賬示例。Ok,看了上邊的代碼,我們在初次使用Session時便開戶事務,那何時提交或回滾事務呢?我們在過濾器中實現:
package cn.itcast.oa.web.filter; import java.io.IOException; import javax.servlet.*; import org.hibernate.Session; import cn.itcast.oa.utils.HibernateSessionUtils; /** * 過濾*.do的請求 * @author Administrator * */ public class HibernateSessionFilter implements Filter { public void destroy() { } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { try { // 繼續調用Action chain.doFilter(request, response); // 獲取當前請求的Session,返回null表示當前請求沒有使用session Session session = HibernateSessionUtils.getCurrentSession(false); if(session != null) // 如果當前請求使用了seesion,則提交事務。 session.getTransaction().commit(); } catch (Exception e) { // 獲取當前請求的Session,返回null表示當前請求沒有使用session Session session = HibernateSessionUtils.getCurrentSession(false); if(session != null) // 如果當前請求使用了seesion,則回滾事務。 session.getTransaction().rollback(); throw new ServletException(e); } finally { // 關閉和刪除session HibernateSessionUtils.closeAndRemoveSession(); } } public void init(FilterConfig arg0) throws ServletException { } } |
使用過濾器進行session管理還不算是一個好的方法。因為我們還沒有實現service層,所以在這里我們使用了Filter。管理session更好一些的方式是在service層中。因為service層中的一個方法處理一個業務邏輯請求。
四、BEAN對象管理
我們每編寫一個實現類的功能時,需要進行單元測試在這里就不多說了。
我們在單元測試或service層中需要創建Dao的接口對象,來完成對數據的訪問。如果我們直接new DaoImpl,這樣并未實現層與層之間的分離。此時經典的工廠模式派上了用場,這個模式從JAVA基礎增強、JAVAWEB到現在都有應用過。
我們通過工廠獲取需要的接口實現類對象,但如果整個系統中共需要100個或者更多的不同接口實現類,難道我們需要在工廠類中添加相應數量的getxxx方法?湯老師經驗豐富且十分有才,他使用了一個方法就可以解決獲取任意接口實現類的對象:
package cn.itcast.oa.utils; import java.io.*; import java.util.Properties; public class BeanFactory { /** * 獲取指定簡單接口名對應的實現類 * @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); // 讀取實現類名 String className = pros.getProperty(clazz.getSimpleName()); // 返回類實例 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); } } } } |
這個BeanFactory非常經典!
湯兄授課是對某一應用的實現由粗糙到細致、由狂亂到優雅,這一教學方式使得大家懂得最終的實現方式是如此優雅。
湯老師有些內項,但他十分愿意與學生們分享學習心得,并且給學生們很大的鼓勵。老張、老方、老佟、老徐、湯兄都鼓勵學生們努力學習...非常好!