先看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框架的雏形