標籤:des style class blog code java
導言
把一件簡單的事情做複雜很容易,把一件複雜的事情做簡單卻不易。在電腦的世界裡,
馮.諾依曼把複雜的電腦簡化為:儲存空間,控制器,運算器和I/O裝置;
丹尼斯·裡奇把晦澀的組合語言簡化為258頁的《C程式設計語言》;
詹姆斯高斯林把繁瑣的跨平台編碼簡化為256條位元組碼指令;
對我們大部分人而言,把簡單的事情做簡單就足夠了。
關於架構
架構是對某一類共通業務的封裝,架構設計應該遵循幾個基本的原則:1 易用性 2 穩定性3 擴充性,架構從來都是給別人用
的,架構的學習成本與他的複雜度成正比,如果你設計了一個功能強大但介面複雜的架構,我想,沒有幾人願意使用;同樣,
穩定性也是必須的,穩定的架構體現設計者所遵循的理念,穩定的架構也會越來越成熟;擴充性是架構的靈魂,沒有擴充性
架構只會像明朝的八股文,禁錮才華橫溢的書生,業務總是在變化,架構必須具備適應這種變化的能力。
java web架構發展,大致經曆以下幾個階段:
刀耕火種時期
早期的java web技術採用serverlt實現,與CGI類似,只不過serverlet採用的是多線程而不是多進程,服務端除了處理商務邏輯之外
還負責頁面的組織,通過Printwirter 列印html標籤,這給當時的開發人員帶來了無盡的麻煩,為了修改一個CSS樣式或者一段javacscript
不得不重新編譯serverlet,為了輸出簡單的處理結果,你不得不用大量的篇幅列印html標籤,下面是一個簡單不過的例子:
import java.io.IOException;import java.io.PrintWriter;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class SimpleServlet extends HttpServlet { private static final long serialVersionUID = 1L; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter out = response.getWriter(); String docType = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n"; out.println(docType + "<HTML>\n" + "<HEAD><TITLE>Hello</TITLE></HEAD>\n" + "<BODY BGCOLOR=\"#FDF5E6\">\n" + "<H1>Hello world</H1>\n" + "</BODY></HTML>"); out.flush(); out.close(); }}
這段代碼唯一的功能就是輸出helloworld。很難想象,在複雜的業務下,程式員要在這種重複的功能上耗費多少時間,這個時期程
序員就像新石器時代的人民,用最原始的工具拓荒,當然這個時期的web還談不上什麼架構。當時的sun公司也注意到這點,隨後
推出了JSP,java 進入下一個時代。
農業文明時期
為瞭解決用serverlet遇到的諸多問題,sun在1999年推出JSP技術,JSP允許在頁面上插入java代碼進行邏輯處理,同時也提供
了大量的tag處理html標籤,大大提高了開發效率,開發人員從繁瑣的out.prinlt中解脫出來,用更多的精力去關注商務邏輯,在此
基礎上演化出jsp+Serverlet兩層架構和 jsp+serverlet+javabean三層架構,在這種架構下,jsp側重頁面展現,serverlet負責業
務邏輯,javabean則負責二者的資料傳遞,這就是MVC的雛形,後續web架構也大多基於這種模式來發展,有的關注其中的某層,如
FreeMarker,有點關注整個流程,如struct。這個階段的程式員開發逐漸邁入固定模式,像農業文明時代一樣,使用著有限的工具,
推動著社會的發展。隨著類似架構的不斷使用和發展,java web邁入另一個跨域式發展的時期。
工業文明時期
就像工業革命標誌工業文明的發展一樣,java web的工業文明時代同樣有著標誌性的事件,那就是2000年5月發布的structs架構,
這個架構標誌著機械化開發的到來,在採用structs後,我們按照固定的模式寫著類似的代碼,配類似的配置,如果你接手過這個時期
的項目,你會發現他們都有大致的結構,這是一種跨越式的發展,20%的市場佔有率也說明當時的程式員對這種架構有多麼的渴求。
同時structs也降低了程式員的要求,找一個新手進行簡單的培訓後就可以進行項目開發,穩定的架構下開發也減少了出錯的可能,
大大降低了項目的開發和維護成本。人們在使用中和思考中不斷修複和革新這些架構,許多優秀的想法不斷萌現,架構也不斷智能化,
引領廣大程式員邁入下一個新紀元。
現代文明時期
現在文明追求智能化讓人類從繁重的體力中解放出來,享受生活,同樣,跨入現代文明的web 開發也追求程式員的解放,大量
優秀的架構湧現:webwork 、Tapestry 、echo、spring等等不甚枚舉,這些架構都和優秀,通過簡單的配置就你實現強大
的功能,但要從中選擇一個標誌性,那麼我將毫不猶豫選擇spring,如果說java讓程式員從作業系統的束縛中解脫出來,那麼
spring就是讓程式員從複雜的設計模式中解脫出來,IOC 、DI、 AOP、 RMI、Proxy,天才Rod Johnson一個不落的包含進來,
為java界帶來新的春天,優秀的思想加上簡單的API成就Spring無可替代的地位。
重複造車輪
討論了這麼多架構和設計,似乎跟我們的標題沒有多大關係,而且有這麼多優秀的架構,我們有必要自己設計嗎?答案是肯定的,
社會發展的動力是革新,而革新的動力是對現有的事物不斷思考,如果都安於現狀,那麼就無進步可言,也許大多數人都認為
web就應該按MVC設計,編碼,當然,我也認同這點,但是隨著對這些架構的應用,讓我不禁想到許多問題,帶著這些問題,
有了一個重複造車輪的想法。帶著對structs的學習,我們也自己實現也該“MVC”架構。 struct很優秀,他主要實現了http
請求道使用者邏輯的封裝,事實上serverlet已經實現了這一點,不過serverlet只能做到將請求映射到一個具體的類,只提供了
service和doPost doGet之類的幾個簡單介面處理使用者請求,而實際的開發中,在面臨千差萬別的業務時,我們不得不在
serverlet裡加大量的if else判斷:
public class SimpleServlet extends BaseServlet { private static final long serialVersionUID = 1L; @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { super.service(request, response); String command = request.getParameter("command"); if ("query".equals(command)) { //doXX } else if ("add".equals(command)) { //doXX } else if ("delete".equals(command)) { //doXX } else if ("update".equals(command)) { //doXX }else{ //doXX } }}
增加一個業務,你就得增加一個If-else,寫類似的代碼,後期的維護會讓你頭痛不已,當你要修改某個分支的功能時,你不得
不把所有的分支用例都跑一遍,確保對其他邏輯沒有影響,實際上,這段代碼有個共通的邏輯:字典尋找功能,根據請求參數找到
對用的處理函數,這就是典型的表驅動模式,也叫表尋找:事先註冊相應的處理邏輯,在需要使用這些邏輯時,給出邏輯的關鍵字
找到對應的實現,當你的代碼中充斥大量if-else或switch-case,你就應該考慮這種方式,讓我們用表驅動來重構以上代碼。
表驅動有三大元素:表 ,介面,管理類。
我們先提取一個簡單的介面:
package com.czp.opensource.mvc.service;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;/** * * @description <p> * 服務介面,每個實作類別處理相應的請求 * </p> * @author Czp * @version 1.0(2014-6-16) * */public interface ServiceItf { /** * * @desc: <p> * 處理http請求 * </p> * @param request * @param respose */ public void process(HttpServletRequest request, HttpServletResponse respose); /** * * @desc: * <p>處理的業務類型</p> * @return */ public String processType();}
可以看到,這個介面並沒有什麼特別的,只是HttpServerlet的簡單封裝,但是他為我們下一步的封裝提供了可能。
接下來我們提供一個管理類,管理介面的實作類別,包括註冊和尋找:
package com.czp.opensource.mvc.service;import java.util.concurrent.ConcurrentHashMap;/** * * @description <p> * 服務工廠:負責註冊 刪除 尋找服務,在整個應用的生命 * 周期類只需要一個工廠,所以實現為單例 * </p> * @author Czp * @version 1.0(2014-6-16) * @since JDK1.5+ * */public class ServiceFactory { private volatile static ServiceFactory INSTANCE; private ConcurrentHashMap<String, ServiceItf> services; private ServiceFactory(){ services = new ConcurrentHashMap<String, ServiceItf>(); } /** * * @desc: * <p> * Double check lock方式延遲初始化 * 只能在JDK1.5+調用 * </p> * @return * INSTANCE */ public ServiceFactory getInstance(){ if(INSTANCE==null) { synchronized (ServiceFactory.class) { if(INSTANCE==null) { INSTANCE = new ServiceFactory(); } } } return INSTANCE; } /** * * @desc: * <p>註冊處理器</p> * @param service * @return */ public boolean regist(ServiceItf service) { ServiceItf putIfAbsent = services.putIfAbsent(service.processType(), service); return putIfAbsent!=null; } /** * * @desc: * <p>根據業務類型尋找處理器</p> * @param proccessType * @return */ public ServiceItf getService(String proccessType) { return services.get(proccessType); } /** * * @desc: * <p>清空表</p> */ public void onClose(){ services.clear(); services = null; }}
接下來我們需要在合適的時間點註冊我們的實作類別,這個操作應該在所有請求到達前,最好的選擇就是在web容器初始化的時,
因此,我們選擇在listener裡註冊
package com.czp.opensource.mvc.service;import javax.servlet.ServletContextEvent;import javax.servlet.ServletContextListener;/** * * @description * <p>上下文環境,負責初始化MVC架構的上下文</p> * @author * Czp * @version * 1.0(2014-6-16) * */public class ContextListener implements ServletContextListener { @Override public void contextDestroyed(ServletContextEvent arg0) { ServiceFactory.getInstance().onClose(); } @Override public void contextInitialized(ServletContextEvent arg0) { ServiceFactory instance = ServiceFactory.getInstance(); instance.regist(new LoginService()); //註冊其他服務,在此不贅述 //instance.regist(new LoginService()); }}
接下來我們就可以用這個簡易的架構重構SimpleServlet
package com.czp.opensource.mvc.service;import java.io.IOException;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class SimpleServlet extends HttpServlet { private static final long serialVersionUID = 1L; @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String command = request.getParameter("command"); ServiceItf service = ServiceFactory.getInstance().getService(command); service.process(request, response); }}
代碼從我們的6個if-else簡化為短短3行代碼,最重要的是,增強了健將性,可維護性和擴充性,當你需要新加任何業務時,
無需要修改和測試原來的邏輯。以上就是spring-mvc struct等所有的MVC架構的本質所在,只不過他們的service是通過xml配置。
這個簡單的架構存在許多問題,如果你也發現了,請留言指出,下篇我們將進一步完善這個架構。