MVC

引入

前面的小小水果摊 虽然完成了全部功能,但是整体结构比较复杂,一个页面对应的请求需要多个Servlet进行响应,代码量重复复杂。

image-20220406195831872

优化1

将所有的代码集成为私有函数,页面都对同一个fruit.do进行响应,但是同时传入一个operate操作符,用于判断对页面的操作,同时利用反射,优化调用函数的代码量,将所有的反射方法调用到dispatchServlet中,同时将一个负责判断转发的操作交给该类进行,而fruitservlet变为控制器,只保存一些调用方法

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
/**对所有的.do结尾的进行响应,fruit.do,user.do等等都可以*/
@WebServlet("*.do")
public class DispatcherServlet extends ViewBaseServlet {

private Map<String ,Object> map=new HashMap<>();
//初始化方法,在xml文件中通过反射获得响应的类,利用map存储关系
@Override
public void init() {
//需要同时也初始化ViewBaseServlet的方法,否则themeleaf无法进行使用
super.init();
try {

InputStream inputStream=getClass().getClassLoader().getResourceAsStream("applicationContext.xml");
DocumentBuilderFactory documentBuilderFactory= DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder=documentBuilderFactory.newDocumentBuilder();
//获取document对象
Document document= documentBuilder.parse(inputStream);
//获取所有bean节点
NodeList beanNodeList=document.getElementsByTagName("bean");
for (int i=0;i<beanNodeList.getLength();i++){
Node beanNode=beanNodeList.item(i);
if (beanNode.getNodeType()== Node.ELEMENT_NODE){
Element beanElement=(Element) beanNode;
String beamId=beanElement.getAttribute("id");
String className=beanElement.getAttribute("class");
Class clas=Class.forName(className);
Object beanObj=clas.newInstance();

map.put(beamId,beanObj);
}
}
} catch (ParserConfigurationException | ClassNotFoundException | InstantiationException | IllegalAccessException | SAXException | IOException e) {
e.printStackTrace();
}


}

@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException{
req.setCharacterEncoding("UTF-8");
//如果说url是:http://localhost:8080/fruit/fruit.do
//servletPath是: /fruit.do
String servletPath=req.getServletPath();
//而我接下来需要得到fruit/fruit.do中第二个fruit
//先找到最后一个/的索引位置
int lastslash=servletPath.lastIndexOf("/")+1;
//获得最后一个点的位置
int lastpoint=servletPath.lastIndexOf(".");
servletPath=servletPath.substring(lastslash,lastpoint);

//使fruit与fruitcontroller对应,先得到controller
Object controllerObj=map.get(servletPath);
System.out.println(servletPath);
String operate =req.getParameter("operate");
if (operate == null | Objects.equals(operate, "")) {
operate="inquire";
}
try {
//传入参数,得到对应的方法
Method method=controllerObj.getClass().getDeclaredMethod(operate,HttpServletRequest.class);
method.setAccessible(true);
//获得参数返回的对象
Object obj=method.invoke(controllerObj,req);
String meth=(String) obj;
//判定是组件内转发,还是重新定向
if (meth.startsWith("sendRedirect")){
String sendRedStr=meth.substring("sendRedirect".length());
resp.sendRedirect(sendRedStr);
}
else {
super.processTemplate(servletPath+"/"+meth,req,resp);
}
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
e.printStackTrace();
throw new RuntimeException("operate非法");
}

}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<xml version="1.0" encodeing="utf-8">
<!--因为是可扩展的标签,所以任何标签都可以自己定义-->
<beans>
<!--下面代表fruit与该controller相对应-->
<bean id="fruit" class="com.zss.myssm.myspringmvc.controller.FruitController"/>

</beans>
<!--
html是超文本标记语言
而xml是可扩展的标记语言

xml包含三个
1.xml声明,而且必须在第一啊很难过
2.DTD支持文档类型
3.XML正文
-->

</xml>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private String  inquire(HttpServletRequest req) throws IOException {
req.setCharacterEncoding("UTF-8");
int pageNum;
String pagePer=req.getParameter("pagePer");
if (pagePer == null|pagePer==""){
pagePer="1";
}
pageNum=Integer.parseInt(pagePer);
List<Fruit> list=fruitDAO.inquFruit(new Fruit(),pageNum);
int lastPageNum=((fruitDAO.getColumn()-1)/5)+1;
HttpSession session=req.getSession();
session.setAttribute("lastPage",lastPageNum);
session.setAttribute("pageNo",pageNum);
session.setAttribute("fruitList",list);
//给中央控制器传入是转发,还是重定向操作
return "FruitShow";
}

优化2

虽然我们将这些方法进行了集成,但是可以发现,我们的fruitcontroller中仍旧还是有重复的的参数需要获取,而且每一个参数都不相同,假如我们有user,还需要给他单独设置参数,那么就不能在设置转发方法的时候,同时完成参数的转发吗???
于是我们更改service方法部分代码

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
try {
Method[] methods = controllerObj.getClass().getDeclaredMethods();
for (Method method : methods) {

if (method.getName().equals(operate)) {
//获取所有的参数
Parameter[] parameters = method.getParameters();
//设计一个用于存放参数值的数组
Object []objectParameters=new Object[parameters.length];
for (int i=0;i<objectParameters.length;i++ ){
Parameter parameter=parameters[i];
if ("req".equals(parameter.getName())){
objectParameters[i]=req;
continue;
}
//获取网页端的值
String parameValue=req.getParameter(parameter.getName());
//将参数赋值
objectParameters[i]=parameValue;
}
method.setAccessible(true);
Object obj = method.invoke(controllerObj, objectParameters);
String meth = (String) obj;
if (meth.startsWith("sendRedirect")) {
String sendRedStr = meth.substring("sendRedirect".length());
resp.sendRedirect(sendRedStr);
} else {
super.processTemplate(servletPath + "/" + meth, req, resp);
}
}

}
} catch (InvocationTargetException | IllegalAccessException e) {
e.printStackTrace();
}