前言

  MVC是三个单词的缩写,这三个单词分别为:模型(Model)、视图(View)和控制(Controller)。一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。

开发自己的MVC框架

第一步:准备jar包


博主也为懒人准备了下载地址 👉 dom4j下载地址
该jar包的作用就是解析xml文件

第二步:准备配置文档

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
<?xml version="1.0" encoding="UTF-8"?>
<!-- 定义我们的DOC约束文件 -->
<!-- 定义根节点 (包含元素)-->
<!-- ELEMENT 表示元素 -->
<!-- ATTLIST 表示属性 -->
<!DOCTYPE myMVC[
<!-- 表示 myMVC根节点下有actions子节点-->
<!ELEMENT myMVC (actions)>

<!-- 一个actions根节点下可以有多个action节点 -->
<!ELEMENT actions (action*)>

<!-- 一个action下可以有多个result节点 -->
<!ELEMENT action (result*)>

<!--
CDATA表示字符串的数据
#REQUIRED表示该属性必须得写
#IMPLIED表示该属性可写可不写
redirect可以简单理解为重定向,默认false转发
-->
<!ATTLIST action
name CDATA #REQUIRED
class CDATA #REQUIRED
>
<!ATTLIST result
name CDATA #IMPLIED
redirect (true|false) "false"
>
]
>
<myMVC>
<actions>
<!-- 根据上面的约束条件配置而成 -->
<action name="loginAction" class="action.LoginAction">
<result name="success">success.jsp</result>
<result name="input">index.jsp</result>
</action>
</actions>
</myMVC>

第三步:我们准备自己的Action接口,用于存放结果集和要执行的方法

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
package action;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* 我们准备自己的Action接口,用于存放结果集和要执行的方法
* @author Garen
*
*/
public interface Action {
/**
* 定义字符串常量
*/
public static final String SUCCESS="success";
public static final String NONE="none";
public static final String ERROR="error";
public static final String INPUT="input";
public static final String LOGIN="login";

/**
* 准备一个方法,用于获取数据
* @param request
* @param response
* @return
* @throws Exception
*/
public String execute(HttpServletRequest request, HttpServletResponse response)throws Exception;
}

第四步:定义一个ActionMapping用来存放Action节点

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
package action;

import java.util.HashMap;
import java.util.Map;

/**
* 定义一个ActionMapping用来存放Action节点
* Action 的配置文件信息
* @author Garen
*
*/
public class ActionMapping {
//访问的Action的名称
private String name;
//访问的Action的对应的Action的类全称
private String ClassName;
//result定义的结果集
private Map<String,String> resultMAp=new HashMap<String, String>();

/**
* 往集合里面添加配置文件中的数据信息
* @param resultName
* @param result
*/
public void addResult(String resultName,String result){
resultMAp.put(resultName, result);
}

/**
* 根据resultName获取对应的result页面
* @param resultName
* @return
*/
public String getResult(String resultName){
return resultMAp.get(resultName);
}

public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getClassName() {
return ClassName;
}
public void setClassName(String className) {
ClassName = className;
}
}

第五步:定义ActionMappingManager,管理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
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
92
package action;

import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

/**
* 定义ActionMappingManager,管理ActionMapping
* ActionMapping 的管理类
* @author Garen
*
*/
public class ActionMappingManager {
/**
* 管理ActionMapping 一个ActionMapping表示一个Action
* 但是我们配置文件中可能出现多个Action节点,所以我们需要一个ActionMapping的管理类
*/
private static Map<String,ActionMapping> actionMappings=new HashMap<String,ActionMapping>();

/**
* 无参构造函数
*/
public ActionMappingManager() {

}

/**
* 带参构造函数
* @param configFileNames
*/
public ActionMappingManager(String[] configFileNames){
for (String filaName : configFileNames) {
//调用根据文件名读取配置文件的方法
init(filaName);
}
}

/**
* 根据Action名称获取到某一个具体的Action
* @param actionName
* @return
*/
public ActionMapping getActionMapping(String actionName){
//根据ActionName获取到Action
ActionMapping actionMapping=actionMappings.get(actionName);
return actionMapping;
}

public void init(String configFileName){
try {
//读取配置文件,肯定用到了输入流
InputStream is=this.getClass().getResourceAsStream("/"+configFileName);
//开始读取xml文件
Document doc=new SAXReader().read(is);
//获取根节点
Element root=doc.getRootElement();
//获取Actions节点
Element actions = (Element)root.elementIterator("actions").next();
//开始遍历Actions节点
for(Iterator<Element> action=actions.elementIterator("action");action.hasNext();){
//获取到Action节点,将其属性进行封装
Element actionElement=action.next();
//获取到name
String name=actionElement.attributeValue("name");
//获取到ClassName
String ClassName=actionElement.attributeValue("class");
//一个Action对应着一个ActionMapping,创建ActionMapping进行赋值
ActionMapping actionMapping =new ActionMapping();
actionMapping.setName(name);
actionMapping.setClassName(ClassName);
//遍历Action的子元素result
for(Iterator<Element> result=actionElement.elementIterator("result");result.hasNext();){
Element resultElement = result.next();
//获取result属性值
String resultName=resultElement.attributeValue("name");
String resultValue=resultElement.getText();
//将每个result封装到ActionMapping中去
actionMapping.addResult(resultName, resultValue);
}
//将ActionMapping放入ActionMappingManager中去
actionMappings.put(actionMapping.getName(), actionMapping);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

第六步:利用反射机制,找到具体类的实例

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 action;

/**
* 利用反射机制,根据类的类类型获取到类的实例
* @author Garen
*
*/
public class ActionManager {
/**
* 根据类的类类型获取到类的实例
* @param className
* @return
*/
public static Action creatAction(String className){
Class clazz=null;

try {
//判断当前线程是否有该Action
clazz = Thread.currentThread().getContextClassLoader().loadClass(className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
if(clazz==null){
try {
//根据类的全路径,手动创建一个类的类类型
clazz=Class.forName(className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
Action action=null;
try {
//根据类的类类型创建出一个类的实例
action=(Action)clazz.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return action;
}
}

第七步:写业务逻辑Action

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package action;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* 写业务逻辑Action
* @author Garen
*
*/
public class LoginAction implements Action {
@Override
public String execute(HttpServletRequest request, HttpServletResponse sponse) throws Exception {
//写具体的业务逻辑
return SUCCESS;
}
}

第八步:准备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
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
package servlet;

import java.io.IOException;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import action.Action;
import action.ActionManager;
import action.ActionMapping;
import action.ActionMappingManager;

public class MVCServlet extends HttpServlet {
ActionMappingManager actionMappingManager=null;

public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doPost(request, response);
}

public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
//根据ActionName获取到ActionMapping
ActionMapping actionMapping = actionMappingManager.getActionMapping(getActionName(request));
//根据ActionMapping中的ClassName获取到具体的类的实例
Action action = ActionManager.creatAction(actionMapping.getClassName());
//执行业务逻辑,获取到resultName
String resultName = action.execute(request, response);
//根据resultName获取具体的result视图
String result = actionMapping.getResult(resultName);
//重定向到页面
response.sendRedirect(result);

} catch (Exception e) {
e.printStackTrace();
}
}

/**
* 根据请求的上下文获取到ActionName
* @param request
* @return
*/
public String getActionName(HttpServletRequest request){
//获取到URI
String uri = request.getRequestURI();
//获取上下文路径
String contextPath = request.getContextPath();
//从上下文中截取ActionPath
String actionPath = uri.substring(contextPath.length());
//获取到ActionName
String actionName=actionPath.substring(1,actionPath.lastIndexOf('.')).trim();
return actionName;
}

/*
* 在加载Servlet的时候就读取配置文件信息
* (非 Javadoc)
* @see javax.servlet.GenericServlet#init(javax.servlet.ServletConfig)
*/
@Override
public void init(ServletConfig config) throws ServletException {
//读取配置信息
String configStr = config.getInitParameter("config");
String[] fileNames=null;
if(configStr==null||configStr.isEmpty()){
fileNames=new String[]{"myMVC.xml"};
}else{
fileNames=configStr.split(",");
}
//读取配置文件,将文件中的信息保存到ActionMappingManager中
actionMappingManager=new ActionMappingManager(fileNames);
}
}

第九步:修改web.xml,配置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
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
<display-name>The_custom_of_MVC</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>

<!-- web.xml里配置servlet -->
<servlet>
<description>This is the description of my J2EE component</description>
<display-name>This is the display name of my J2EE component</display-name>
<servlet-name>MVCServlet</servlet-name>
<servlet-class>servlet.MVCServlet</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>MVCServlet</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
</web-app>

第十步:测试login.jsp是否能跳转到success.jsp


博主总结


ps:因作者能力有限,有错误的地方请见谅

  • 喜欢这篇文章的话可以用快捷键 Ctrl + D 来收藏本页

最后更新: 2018年09月19日 15:59

原始链接: https://blog.hdqyf.club/2018/05/04/20180504-学渣尝鲜—自定义MVC框架/

× 请我吃糖~
打赏二维码