日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
MVC框架的映射和解耦

最近在寫一個(gè)業(yè)務(wù)上用到的框架,回想起接觸過的一些MVC框架,尤其是主要貢獻(xiàn)在后端表現(xiàn)層上的那些,它們之間有太多的相似,在不斷解耦的過程中,層數(shù)和模塊數(shù)也越來越多,需要不斷引入層與層之間的映射邏輯將不同層次之間關(guān)聯(lián)起來,我們不妨來查看一下這個(gè)過程,能否尋找一些MVC框架的共性和啟示。

MVC 1到MVC 2模型的進(jìn)化

這個(gè)話題有點(diǎn)老。MVC 1在桌面程序中應(yīng)用較多,業(yè)務(wù)邏輯當(dāng)然放在Model里面,Controller負(fù)責(zé)將用戶的請(qǐng)求數(shù)據(jù)傳遞到Model去,之后就放手不管了,讓View通過觀察者模式不斷獲知Model的最新變化(可以是Model變化后通知View,也可以是View自己來獲?。?。這個(gè)模式看起來很簡(jiǎn)單,不過很容易發(fā)現(xiàn)一個(gè)嚴(yán)重的問題,View必須對(duì)Model了如指掌,要不然怎么觀察它呢。這實(shí)在是一件不甚合理的事情。

MVC 2則解開了Model和View的耦合,Controller變成了中介者一樣的角色,View接收了用戶輸入,Controller把處理請(qǐng)求分派給Model,處理完后,又把結(jié)果交回View來展現(xiàn)。不過,這樣的代價(jià)是Controller變成了一個(gè)百事通,如果它要關(guān)心Model和View的具體實(shí)現(xiàn),耦合的問題只是換了一件外衣而已。所以,需要對(duì)Controller進(jìn)一步解耦。下面的話題,也是借由這一點(diǎn)展開的。

從這個(gè)改變就可以看出在解耦方面的進(jìn)化,但是依然沒有做足,后來ASP.NET又出了MVC 3、MVC 4,我沒有去了解其中的變更。這只是關(guān)于解耦的一個(gè)前傳,下面讓我們回到正題,看看那些隨著解耦的進(jìn)一步進(jìn)行,新產(chǎn)生的映射邏輯和配置。

#p#

URL Mapping

也許最早會(huì)有人這樣寫代碼:

 
 
 
 
  1. if("/ServletTest/eg1".equals(request.getURI()))  
  2.     xxx;  
  3. else if("/ServletTest/eg2".equals(request.getURI()))  
  4.     xxx;  
  5. ... ... 

但是現(xiàn)在應(yīng)該已經(jīng)不會(huì)有人這樣寫了,印象中即便是最早只是用JSP+Servlet寫程序的那一批程序員寫網(wǎng)站應(yīng)用的時(shí)候,URL和控制器入口的映射邏輯也已經(jīng)被獨(dú)立出來了,例如Tomcat的web.xml:

 
 
 
 
  1.  
  2.     Example  
  3.     com.xxx.xxx.Example  
  4.  
  5.  
  6.     Example  
  7.     /example  
  8.  

但是這樣的配置匹配的表達(dá)式不夠靈活(例如無法自定義匹配邏輯),而且配置過于冗長(通常來說,我是一個(gè)xml配置文件的痛恨者),于是現(xiàn)今的MVC框架都提供了一套自己的映射匹配邏輯,例如Struts2:

 
 
 
 
  1.  
  2.     /success.jsp  
  3.       
  4.  

還可以配置全局的跳轉(zhuǎn)邏輯,傳遞參數(shù)等等,總之配置是靈活多了,可是每寫一段控制器的邏輯,還是需要配置一段到XML文件中。這樣的問題也是可以解決的,將變化點(diǎn)獨(dú)立到Action里,配置文件中只寫這個(gè)變化的參數(shù),這樣只需要一個(gè)配置就可以完成大部分跳轉(zhuǎn)了。

對(duì)于不同參數(shù)名稱和參數(shù)個(gè)數(shù)變化的情況,上面的辦法支持得又不好了,好在許多框架都提供了注解配置的辦法,把URL映射的邏輯變成短短的注解:

 
 
 
 
  1. @Results( { @Result(name = "success", location = "/success.jsp"), @Result(name = "error", location = "/error.jsp") }) 

可是這幫難伺候的程序員啊,還是嫌麻煩,這就需要利用CoC原則(規(guī)約優(yōu)于配置,Convention over Configuration)。在Spring MVC中,聲明了ControllerClassNameHandlerMapping以后,對(duì)于這樣沒有配置任何映射信息的方法:

 
 
 
 
  1. @Controller 
  2. public class ExampleController extends MultiActionController{  
  3.    
  4.     @RequestMapping 
  5.     public void e1(...);  

類名叫做ExampleController,方法名叫做e1,因此在使用如下URI進(jìn)行訪問的時(shí)候,就自動(dòng)mapping到這個(gè)example方法上:

 
 
 
 
  1. /example/e1 

好了,這總滿足懶惰的程序員你了吧。

#p#

Data Binding

這里Data Binding(數(shù)據(jù)綁定)指的是將用戶請(qǐng)求提交上來的數(shù)據(jù)和領(lǐng)域模型綁定起來,即生成若干個(gè)攜帶數(shù)據(jù)的模型對(duì)象。

自不必說,最原始的方式應(yīng)該是類似這樣的解決方法:

 
 
 
 
  1. User user = new User();  
  2. user.setName = (String)request.getParameter("name");  
  3. user.setAge = (Integer)request.getParameter("age");  
  4. ... ... 

這當(dāng)然不會(huì)入程序員的法眼了,于是框架替你把參數(shù)綁定到一個(gè)數(shù)據(jù)集合的對(duì)象上,你獲取起來就容易多了,比如在Grails框架中,可以這樣寫:

 
 
 
 
  1. def example = {  
  2.     def name = params.name;  
  3.     def age = params.age;  
  4.     ... ...  

或者干脆換成參數(shù)映射的配置文件,可是還是好啰嗦,于是“規(guī)約優(yōu)于配置”又來了,以Struts2為例:

 
 
 
 
  1. class UserAction{  
  2.     private User user;  
  3.     //get/set方法  

這種情況下,只要提交這樣的請(qǐng)求:

 
 
 
 
  1. /example/userAction?user.name=Jim&user.age=18 

這個(gè)name為Jim、age為18的User對(duì)象就自動(dòng)被塞進(jìn)這個(gè)Action去了。Spring MVC的情況類似,只不過粒度更小,參數(shù)注入的不是類Action實(shí)例的屬性,而是Controller方法的參數(shù)——當(dāng)然,思想是一樣的。

#p#

視圖指向

你可能猜到我要說的內(nèi)容了。程序員最原始的做法應(yīng)該是類似這個(gè)樣子的:

 
 
 
 
  1. request.getRequestDispatcher("/common/success.jsp").forward(request, response); 

之后進(jìn)化成為配置文件配置的形式、注解配置的形式,有了前文的介紹,這實(shí)在是沒什么特別的。值得一提的是,我用過一個(gè)框架,它對(duì)于URL Mapping(front-controller做的事情)和View Routing(backend-controller做的事情)通過這樣一種有趣的機(jī)制來完成:

根據(jù)URL路徑和Controller返回的結(jié)果字符串去尋找相應(yīng)目錄下對(duì)應(yīng)名稱的handler;

如果找不到就找defaultHandler;

如果還是找不到就往上一級(jí)目錄去找,依此類推。

舉例來說,Controller返回View的路徑為“user/admin/do”,就到…/user/admin目錄下尋找一個(gè)do.handler的文件,找不到就尋找同目錄下的default.handler,再找不到就往父目錄去遞歸尋找。這種機(jī)制就使得URL Mapping和View Routing的過程變得具有天然的繼承性(比如公用的success.handler可以放在頂級(jí)目錄中)。

這種方式其實(shí)也是配置,但是既不是配置文件,也不是注解,更不是代碼,而是一個(gè)文件和文件夾的組織結(jié)構(gòu)。

最后,你肯定知道我還是要回到“規(guī)約優(yōu)于配置”上面來。效果就是,例如訪問user/do默認(rèn)完成后就去尋找…/user/success.jsp,異常后通過異常攔截器首先尋找…/user/failed.jsp。

#p#

頁面聚合

對(duì)于服務(wù)端頁面模板的組織在我看來一直是網(wǎng)站應(yīng)用編程中比較薄弱的一塊(客戶端頁面聚合即前端頁面聚合我在此先不討論),直到現(xiàn)在,頁面模板的代碼還是極容易陷入過于復(fù)雜和不易理解的境地。最開始追溯到JSP誕生以前的時(shí)代,頁面是可以由Servlet一行一行輸出的:

 
 
 
 
  1. PrintWriter pw = response.getWriter();  
  2. pw.write("xxx");  
  3. ... ... 

那個(gè)時(shí)候還沒有頁面模板的概念。于是JSP出現(xiàn),可以把頁面HTML和頁面上用于展示的Java代碼糅合在一起。至于JSP最初就容易被誤用做了更多的展示以外的事情,那其實(shí)并不是工具本身的錯(cuò)。直到現(xiàn)在,還有許多人對(duì)于Servlet和JSP有相當(dāng)?shù)钠?,在程序員聊天的時(shí)候,你要是說你的網(wǎng)站是用Servlet+JSP做的,對(duì)方往往會(huì)直接鄙視你,用那么老土的技術(shù)。其實(shí)技術(shù)本身并沒有任何錯(cuò),Servlet+JSP依然可以非常漂亮地解決很多實(shí)際問題。

對(duì)于頁面模板,無論你是使用JSP,還是FreeMarker、Velocity,你都會(huì)面對(duì)一個(gè)問題,一個(gè)和Java代碼、C++代碼一樣需要依賴和組織的問題。于是程序員就將頁面分為幾個(gè)子頁面,通過這樣的方式引入:

 
 
 
 
  1. <%@ page import="xxx.jsp" %> 

不過,對(duì)于一些公共的頁面而言,可能要被許多頁面引用,幾乎所有的結(jié)果頁面都要引入header.jsp、menu.jsp、footer.jsp……并傳遞一些類似的參數(shù)。這讓啰嗦的程序員又覺得不開心了,我應(yīng)該把我有限的精力專注到業(yè)務(wù)特有的邏輯和頁面上去,這些通用的部分框架能不能替我聚合,而我就不需要關(guān)心了?

這和異常處理很像,很多項(xiàng)目都喜歡定義自己的總異常,繼承自RuntimeException,不需要聲明,而且在通用異常攔截器內(nèi)統(tǒng)一處理這些未被捕獲的異常,完成通用的邏輯處理和頁面轉(zhuǎn)向;而錯(cuò)誤信息就通過異常攜帶出來了,程序員就不需要把精力分散到大量的異常信息傳遞上面——比如通過返回碼這種需要單獨(dú)處理的形式,記得在老項(xiàng)目(特別是存儲(chǔ)過程)的業(yè)務(wù)邏輯中還經(jīng)常看到錯(cuò)誤信息的返回碼,現(xiàn)在真是越來越少見了。

于是Tiles給了這樣的頁面聚合辦法,配置文件:

 
 
 
 
  1.  
  2.       
  3.       
  4.     ... ...  
  5.  

并且可以靈活地使用繼承和參數(shù)傳遞,可是依舊不爽,每一個(gè)頁面跳轉(zhuǎn)都要配置這樣一塊豆腐干,實(shí)在是很啰嗦。SiteMesh提供了一種更為簡(jiǎn)潔的配置方式:

 
 
 
 
  1.  
  2.     /*  
  3.  
  4.  
  5.     /admin/*  
  6.  

這樣一來,所有的請(qǐng)求(除了匹配“/admin/*”這樣的)全部都走到基于main.jsp聚合的邏輯中去了,通用的部分全部在main.jsp中完成,變化的頁面依然根據(jù)原有的View Routing的映射來尋找頁面,聚合這件事情,就真正對(duì)后續(xù)開發(fā)的程序員透明了。

對(duì)于框架來說,還有進(jìn)一步解耦的需求嗎?有。比如可配置的攔截器,對(duì)于不同的請(qǐng)求能夠使用配置為不同數(shù)量和不同個(gè)數(shù)攔截器的“攔截器?!眮眄憫?yīng),既可能有前置處理,也可能有后置處理。攔截器把原本在許多業(yè)務(wù)里都要重復(fù)做的事情(比如權(quán)限校驗(yàn))通過AOP這種形式橫向切一刀給做了。再比如序列化,如果要返回頁面,形式可能是text/html的,而要傳遞對(duì)象,形式可能就是application/json這樣的,將頁面或者對(duì)象轉(zhuǎn)換成html或者JSON響應(yīng)的活兒,程序員當(dāng)然也不想干……

縱觀上面介紹的這些MVC框架在解耦和映射方面做的貢獻(xiàn),我們很容易看到,在不斷地解耦過程中,層數(shù)、模塊數(shù)不斷在增加,復(fù)雜性應(yīng)該說也在增加,配置當(dāng)然更復(fù)雜,可是愛偷懶的程序總有辦法讓復(fù)雜變得簡(jiǎn)單。這個(gè)因解耦引起層與層之間映射的配置便是如此:

程序員自己實(shí)現(xiàn);框架實(shí)現(xiàn),但是需要手動(dòng)配置;規(guī)約優(yōu)于配置。

正是程序員對(duì)于懶惰的追求,造就了一個(gè)又一個(gè)好用的MVC框架,現(xiàn)在開發(fā)一個(gè)網(wǎng)站對(duì)于十多年前來說,實(shí)在是簡(jiǎn)便太多太多了,在今天談?wù)摰慕嵌壬?,未來MVC框架還會(huì)有怎樣的發(fā)展趨勢(shì)呢?還有哪一些通用的部分會(huì)被解耦出來,你又怎么看?

原文鏈接:http://www.raychase.net/850


當(dāng)前題目:MVC框架的映射和解耦
新聞來源:http://m.5511xx.com/article/dheeohc.html