先看mvc模式流程圖(其實mvc設計模式就是java中的model2。):
就像圖上所標識的c層主要是servlet層控制頁面跳轉,m層就是具體的業務處理邏輯,而jsp就是所謂的v層。mvc是有別于我們所說的三層,我們平常所說的三層是ui層、bll層、dal層,具體的區別如圖:
從圖上能看出來,jsp和servlet構成了ui層,而model層分成了bll層和dal層(也就是業務邏輯和數據持久層)。
從理論上認清了mvc設計模式之后,下面開始動手敲一個mvc設計模式示例代碼:
jsp索引頁面index.jsp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
<%@ page language= "java" contenttype= "text/html; charset=gb18030" pageencoding= "gb18030" %> <!doctype html public "-//w3c//dtd html 4.01 transitional//en" "http://www.w3.org/tr/html4/loose.dtd" > <html> <head> <meta http-equiv= "content-type" content= "text/html; charset=gb18030" > <title>insert title here</title> </head> <body> <form action= "servlet/adduser.action" method= "post" > 姓名:<input type= "text" name= "username" > <input type= "submit" value= "提交" > </form> </body> </html> |
業務邏輯代碼usermanager:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
package com.bjpowernode.servlet; import java.util.arraylist; import java.util.list; public class usermanager { public void adduser(string username){ system.out.println( "usermanager.addusre()--->username:" +username); } public void deluser(string username){ system.out.println( "usermanager.deluser()--->username:" +username); } public void modifyuser(string username){ system.out.println( "usermanager.modifyuser()--->username" +username); } public list queryuser(string username){ system.out.println( "usermanager.queryuser()--->username" +username); list userlist= new arraylist(); userlist.add( "a" ); userlist.add( "b" ); userlist.add( "c" ); return userlist; } } |
servlet控制代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
package com.bjpowernode.servlet; import java.io.ioexception; import java.util.list; import javax.servlet.servletexception; import javax.servlet.http.httpservlet; import javax.servlet.http.httpservletrequest; import javax.servlet.http.httpservletresponse; public class testservlet extends httpservlet { protected void doget(httpservletrequest request, httpservletresponse response) throws servletexception, ioexception { string requesturi=request.getrequesturi(); system.out.println( "request=" +requesturi); string path=requesturi.substring(requesturi.indexof( "/" , 1 ),requesturi.indexof( "." )); system.out.println( "path=" +path); string username=request.getparameter( "username" ); usermanager usermanager= new usermanager(); //usermanager.adduser(username); string forward= "" ; if ( "/servlet/deluser" .equals(path)){ usermanager.deluser(username); forward= "/del_success.jsp" ; } else if ( "/servlet/adduser" .equals(path)){ usermanager.adduser(username); forward= "/add_success.jsp" ; } else if ( "/servlet/modifyuser" .equals(path)){ usermanager.modifyuser(username); forward= "/modify_success.jsp" ; } else if ( "/servlet/queryuser" .equals(path)){ list userlist=usermanager.queryuser(username); request.setattribute( "userlist" , userlist); forward= "/query_success.jsp" ; } else { throw new runtimeexception( "請求失敗" ); } request.getrequestdispatcher(forward).forward(request, response); } |
這個servlet代碼主要實現的功能判斷是那個頁面請求服務器做那些操作,之后調用業務邏輯實現相應業務操作。
配置servlet:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
<?xml version= "1.0" encoding= "utf-8" ?> <web-app xmlns:xsi= "http://www.w3.org/2001/xmlschema-instance" xmlns= "http://java.sun.com/xml/ns/javaee" xmlns:web= "http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemalocation= "http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id= "webapp_id" version= "3.0" > <display-name>test_servlet</display-name> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file> default .html</welcome-file> <welcome-file> default .htm</welcome-file> <welcome-file> default .jsp</welcome-file> </welcome-file-list> <servlet> <servlet-name>testservlet</servlet-name> <servlet- class >com.cjq.servlet.testservlet</servlet- class > </servlet> <servlet-mapping> <servlet-name>testservlet</servlet-name> <url-pattern>*.action</url-pattern> </servlet-mapping> </web-app> |
輸出結果:
通過上面的示例已經對mvc設計模式有了初步的認識,其實這個示例是對struts框架學習的基礎,只有弄清楚了這個實例才能弄清楚struts框架的實現原理和struts框架使用。
那么我們怎么才能通過這個示例引入struts框架呢?這個問題從if-eles開始。
首先我們看到了testservlet中出現了許多if-else語句,這樣是非常不穩定的,這樣的程序是非常不靈活的,以后如果有變化,那么維護是非常差的;而且我們在if-else中出現了大量的字符串,這樣在coding的時候會出現寫錯,這樣無形中給調試帶來了麻煩。所以去掉if-else成了我們重構的第一步,也是我們進行struts框架學習的第一步。因為在testservlet中出現了if-else語句塊,所以讓程序變得不再靈活,讓應付需求變化時變得笨拙。所以就承接上篇文章來重構一下testservlet代碼,主要是用繼承多態來進一步對testservlet進行重構。
下面進入重構階段:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
package com.bjpowernode.servlet; import java.io.ioexception; import java.util.list; import javax.servlet.servletexception; import javax.servlet.http.httpservlet; import javax.servlet.http.httpservletrequest; import javax.servlet.http.httpservletresponse; public class testservlet extends httpservlet { protected void doget(httpservletrequest request, httpservletresponse response) throws servletexception, ioexception { string requesturi=request.getrequesturi(); system.out.println( "request=" +requesturi); string path=requesturi.substring(requesturi.indexof( "/" , 1 ),requesturi.indexof( "." )); system.out.println( "path=" +path); string username=request.getparameter( "username" ); usermanager usermanager= new usermanager(); //usermanager.adduser(username); string forward= "" ; if ( "/servlet/deluser" .equals(path)){ usermanager.deluser(username); forward= "/del_success.jsp" ; } else if ( "/servlet/adduser" .equals(path)){ usermanager.adduser(username); forward= "/add_success.jsp" ; } else if ( "/servlet/modifyuser" .equals(path)){ usermanager.modifyuser(username); forward= "/modify_success.jsp" ; } else if ( "/servlet/queryuser" .equals(path)){ list userlist=usermanager.queryuser(username); request.setattribute( "userlist" , userlist); forward= "/query_success.jsp" ; } else { throw new runtimeexception( "請求失敗" ); } request.getrequestdispatcher(forward).forward(request, response); } } |
首先我們看到了在每個語句塊中都出現了給forward賦值,其實也就是給頁面跳轉的路徑賦值,針對每個請求路徑判斷來賦值跳轉路徑。另外每個if-else語句塊中都有業務處理,我們要把這些業務處理分別放到類里面,讓職責更加單
一,這樣更加符合面向對象的思路。
就從這里我們開始重構,我們可以將這個跳轉路徑和業務邏輯封裝起來。
既然封裝,那么我們就抽象出來一個借口,主要完成一個方法,這個方法主要的功能就是要完成業務邏輯封裝和路徑跳轉的返回。隨后建立四個類,主要實現相應的增刪改查的業務處理和處理之后的跳轉路徑返回。
代碼如下:
接口action:
1
2
3
4
5
6
7
8
9
10
|
package com.bjpowernode.servlet; import javax.servlet.http.httpservletrequest; import javax.servlet.http.httpservletresponse; public interface action { public string execute(httpservletrequest request,httpservletresponse response) throws exception; } |
增刪改查實現類:
添加用戶實現類:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
package com.bjpowernode.servlet; import javax.servlet.http.httpservletrequest; import javax.servlet.http.httpservletresponse; public class adduseraction implements action { public string execute(httpservletrequest request, httpservletresponse response) throws exception { string username=request.getparameter( "username" ); usermanager usermanager= new usermanager(); usermanager.adduser(username); return "/add_success.jsp" ; } } |
刪除用戶實現類:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
package com.bjpowernode.servlet; import javax.servlet.http.httpservletrequest; import javax.servlet.http.httpservletresponse; public class deluseraction implements action { public string execute(httpservletrequest request, httpservletresponse response) throws exception { string username=request.getparameter( "username" ); usermanager usermanager= new usermanager(); usermanager.deluser(username); return "/del_success.jsp" ; } } |
更新用戶實現類:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
package com.bjpowernode.servlet; import javax.servlet.http.httpservletrequest; import javax.servlet.http.httpservletresponse; public class modifyuseraction implements action { @override public string execute(httpservletrequest request, httpservletresponse response) throws exception { string username=request.getparameter( "username" ); usermanager usermanager= new usermanager(); usermanager.modifyuser(username); return "/modify_success.jsp" ; } } |
查詢用戶實現類:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
package com.bjpowernode.servlet; import java.util.list; import javax.servlet.http.httpservletrequest; import javax.servlet.http.httpservletresponse; public class queryuseraction implements action { @override public string execute(httpservletrequest request, httpservletresponse response) throws exception { string username=request.getparameter( "username" ); usermanager usermanager= new usermanager(); list userlist=usermanager.queryuser(username); request.setattribute( "userlist" , userlist); return "/query_success.jsp" ; } } |
testservlet類重構如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
package com.bjpowernode.servlet; import java.io.ioexception; import java.util.list; import javax.servlet.servletexception; import javax.servlet.http.httpservlet; import javax.servlet.http.httpservletrequest; import javax.servlet.http.httpservletresponse; public class testservlet extends httpservlet { protected void doget(httpservletrequest request, httpservletresponse response) throws servletexception, ioexception { string requesturi=request.getrequesturi(); system.out.println( "request=" +requesturi); string path=requesturi.substring(requesturi.indexof( "/" , 1 ),requesturi.indexof( "." )); system.out.println( "path=" +path); action action= null ; if ( "/servlet/deluser" .equals(path)){ action= new deluseraction(); } else if ( "/servlet/adduser" .equals(path)){ action= new adduseraction(); } else if ( "/servlet/modifyuser" .equals(path)){ action= new modifyuseraction(); } else if ( "/servlet/queryuser" .equals(path)){ action= new queryuseraction(); } else { throw new runtimeexception( "請求失敗" ); } string forward= null ; try { forward=action.execute(request, response); } catch (exception e){ e.printstacktrace(); } request.getrequestdispatcher(forward).forward(request, response); } } |
運行結果:
這樣testservlet類雖然沒有徹底去掉if-else,但是這樣的代碼變得更加簡練,利用多肽實現業務邏輯處理和路徑跳轉返回。職責更加清晰,讓維護變得更加輕松。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
package com.bjpowernode.servlet; import java.io.ioexception; import java.util.list; import javax.servlet.servletexception; import javax.servlet.http.httpservlet; import javax.servlet.http.httpservletrequest; import javax.servlet.http.httpservletresponse; public class testservlet extends httpservlet { protected void doget(httpservletrequest request, httpservletresponse response) throws servletexception, ioexception { string requesturi=request.getrequesturi(); system.out.println( "request=" +requesturi); string path=requesturi.substring(requesturi.indexof( "/" , 1 ),requesturi.indexof( "." )); system.out.println( "path=" +path); action action= null ; if ( "/servlet/deluser" .equals(path)){ action= new deluseraction(); } else if ( "/servlet/adduser" .equals(path)){ action= new adduseraction(); } else if ( "/servlet/modifyuser" .equals(path)){ action= new modifyuseraction(); } else if ( "/servlet/queryuser" .equals(path)){ action= new queryuseraction(); } else { throw new runtimeexception( "請求失敗" ); } string forward= null ; try { forward=action.execute(request, response); } catch (exception e){ e.printstacktrace(); } request.getrequestdispatcher(forward).forward(request, response); } } |
解決字符串問題,當然就要用到配置文件了,用到配置文件就要有用來讀取配置文件的相關的類和方法,這里就用dom4j中的類來讀取配置文件,這里的配置文件的書寫是有點邏輯上的難度的。
我們來看testservlet中的代碼,我們要在這個testservlet中實現讀取配置文件和path比較,還有利用多肽實例化相應的實現類,最后通過實例化的實現類的方法來返回跳轉路徑,最終跳轉到相應的頁面。
所以我們的配置文件就要不僅配上testservlet中出現的字符串,還要配置相應的action接口的實現類(我們可以利用反射來實例化該類的對象,進而使用這個類的所有屬性和方法),另外還有跳轉路徑字符串。這樣我們的配置文件就變成了如下代碼所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
<?xml version= "1.0" encoding= "utf-8" ?> <action-config> <action path= "/servlet/deluser" type= "com.cjq.servlet.deluseraction" > <forward name= "success" >/del_success.jsp</forward> <forward name= "error" >/del_error.jsp</forward> </action> <action path= "/servlet/adduser" type= "com.cjq.servlet.adduseraction" > <forward name= "success" >/add_success.jsp</forward> <forward name= "error" >/add_error.jsp</forward> </action> <action path= "/servlet/modifyuser" type= "com.cjq.servlet.modifyuseraction" > <forward name= "success" >/modify_success.jsp</forward> <forward name= "error" >/modify_error.jsp</forward> </action> <action path= "/servlet/queryuser" type= "com.cjq.servlet.queryuseraction" > <forward name= "success" >/query_success.jsp</forward> <forward name= "error" >/query_error.jsp</forward> </action> </action-config> |
我們有了配置文件之后就要想法通過相關類讀取,并且實現相應的功能。所以這里用dom4j來讀取完成。其實如果能把這個邏輯捋順之后就能發現,其實懂我們利用dom4j讀取完配置文件的時候,我們是取得的是一個配套的匹配路徑字符串、相應業務邏輯類還有處理業務邏輯之后跳轉頁面路徑字符串。這樣我們就能直截了當的去掉了if-else。(這里可能邏輯上會出現一些困難,但是看到下面的重構之后的testservlet中的代碼和讀取配置文件之后的代碼就會一目了然)。
現在等待解決的問題就是我們要把從配置文件取得的一整套內容放到那里,當然這是毋庸置疑的要放到類中。所以我們就建立一個actionmapping類來放我們的那一整套內容。
actionmapping中的代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
package com.bjpowernode.servlet; import java.util.map; public class actionmapping { private string path; private object type; private map forwardmap; public string getpath() { return path; } public void setpath(string path) { this .path = path; } public object gettype() { return type; } public void settype(object type) { this .type = type; } public map getforwardmap() { return forwardmap; } public void setforwardmap(map forwardmap) { this .forwardmap = forwardmap; } } |
現在actionmapping類已經有了,剩下的工作就是要利用dom4j來讀取配置文件類,具體代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
|
package com.bjpowernode.servlet; import java.io.inputstream; import java.util.hashmap; import java.util.iterator; import java.util.map; import org.dom4j.document; import org.dom4j.documentexception; import org.dom4j.element; import org.dom4j.io.saxreader; public class xmlconfigreader { private static xmlconfigreader instance= new xmlconfigreader(); actionmapping actionmapping= new actionmapping(); private document doc; private map actionmap= new hashmap(); private xmlconfigreader(){ try { saxreader reader= new saxreader(); inputstream in=thread.currentthread().getcontextclassloader().getresourceasstream( "action_config.xml" ); doc=reader.read(in); } catch (documentexception e) { // todo auto-generated catch block e.printstacktrace(); } } public actionmapping getactionmapping(string path){ synchronized ( this ){ object type= null ; /*if(action.containskey(path)){ type=action.get(path); }*/ element eltaction = (element)doc.selectobject("//action[@path=\"" + path + "\"]"); try{ type=class.forname(eltaction.attributevalue("type")).newinstance(); }catch(exception e){ e.printstacktrace(); } element eltforwards = eltaction.element("forward"); for (iterator iter = eltforwards.elementiterator(); iter.hasnext();) { element eltforward = (element) iter.next(); actionmap.put( eltforward.attributevalue("name"),eltforward.gettexttrim()); } actionmapping.setpath(path); actionmapping.settype(type); actionmapping.setforwardmap(actionmap); return actionmapping; } } public static synchronized xmlconfigreader getinstance(){ return instance; } /** * @param args */ public static void main(string[] args) { // todo auto-generated method stub actionmapping actionmapping=xmlconfigreader.getinstance().getactionmapping( "/servlet/deluser" ); system.out.println(actionmapping.getpath()); system.out.println(actionmapping.gettype()); system.out.println(actionmapping.getforwardmap().tostring()); } } |
我們通過返回actionmapping來動態創建出action相應的實現類,進而完成業務邏輯和頁面跳轉,重構之后的testservlet代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
package com.bjpowernode.servlet; import java.io.ioexception; import java.util.list; import javax.servlet.servletexception; import javax.servlet.http.httpservlet; import javax.servlet.http.httpservletrequest; import javax.servlet.http.httpservletresponse; public class testservlet extends httpservlet { protected void doget(httpservletrequest request, httpservletresponse response) throws servletexception, ioexception { string requesturi=request.getrequesturi(); system.out.println( "request=" +requesturi); string path=requesturi.substring(requesturi.indexof( "/" , 1 ),requesturi.indexof( "." )); system.out.println( "path=" +path); string forward= "" ; actionmapping actionmapping=xmlconfigreader.getinstance().getactionmapping(path); action action=(action)actionmapping.gettype(); try { forward=action.execute(request, response); } catch (exception e) { // todo auto-generated catch block e.printstacktrace(); } request.getrequestdispatcher(forward).forward(request, response); } protected void dopost(httpservletrequest request, httpservletresponse response) throws servletexception, ioexception { doget(request,response); } } |
我們可以清晰的看到if-else已經沒有了,字符串也已經沒有了。通過這篇文章對if-else還有字符串問題的解決,又一次重構了testservlet代碼,程序相對靈活許多。通過這一次的重構,我們已經看到了struts框架的雛形