目录
3.1 修改DisPathServlet中init初始化方法
前言
在这篇 自定义MVC框架思想 中我已详细描述了工作原理及流程,本篇主要在此基础上继续做出优化,实现步骤如下:
自定义MVC实现
1. 导入XML配置文件
<?xml version="1.0" encoding="UTF-8"?>
<config>
<action path="/order" type="com.ycxw.servlet.OrderAction">
<forward name="success" path="/index.jsp" redirect="true" />
<forward name="failed" path="/register.jsp" redirect="false" />
</action>
<action path="/book" type="com.ycxw.servlet.BookAction">
<forward name="List" path="/book.jsp" redirect="false" />
<forward name="toList" path="/index.jsp" redirect="true" />
</action>
</config>
将其部署到Source Folder文件中
2. 导入XML解析建模
这里就不一一详细解说了,可以去 XML建模 中了解详细建模实例。
3. 优化中央控制器
3.1 修改DisPathServlet中init初始化方法
在DisPathServlet的init方法中将原有Map集合方式替换成XML建模方式
// 通过xml建模方法进行储存
private ConfigModel configModel;@Override
public void init() throws ServletException {
/**
* 初始化存值就是给每个施工员根据施工证进行存档:
*/
try {
// configModel包含了所有的子控制器
configModel = ConfigModelFactory.build();
} catch (Exception e) {
e.printStackTrace();
}
}
3.2 修改ActionServlet逻辑处理流程
根据请求路径名获取ActionModel
这里如果查不到指定对象,请认真检查xml文件的配置,以及截取的内容是否与xml配置相同
/**
* 获取请求路径
*/
String uri = request.getRequestURI();
// 截取book
uri = uri.substring(uri.lastIndexOf("/"), uri.lastIndexOf("."));
// 要通过uri->> /book,在configModel对象中找
ActionModel actionModel = configModel.pop(uri);
// 判断没找对象等于空就抛出异常
if (actionModel == null)
throw new RuntimeException("action not config");
/**
* 获取config文件中action标签的type属性值
* type指java类
* com.ycxw.servlet.BookAction
*/
String type = actionModel.getType();
3.3 通过反射机制实例化子控制器类
package com.ycxw.framework;
import java.lang.reflect.Method;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 子控制器(action) 处理浏览器请求的类
*
* @author 云村小威
*
* 2023年6月29日 下午8:30:32
*/
public class Action {
public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 获取methodName值,这里指前端点击功能传来的方法名
String methodName = request.getParameter("methodName");
//定义一个变量来保存返回值
String res = "";
/**
* this--->BookAction/BlogAction/PermissionAction...可能是很多对象
* 所以需要通过反射找到对象带request,response参数的methidName方法
*/
Method m = this.getClass().getDeclaredMethod(methodName, HttpServletRequest.class, HttpServletResponse.class);
m.setAccessible(true);
// 动态调用其方法
res = (String) m.invoke(this, request, response);
return res ;
}
}
3.4 中央控制器将请求委托给子控制器处理
Action instance = (Action) Class.forName(type).newInstance();
// 业务代码执行后返回值
String execute = instance.execute(request, response);
3.5 根据请求结果码跳转页面
// 通过返回值拿到,该方法结果是重定向还是转发,还是跳转哪个页面
ForwardModel forwardModel = actionModel.pop(execute);
if (forwardModel != null) {
// 获取forwardModel是否从定向值
boolean redirect = forwardModel.isRedirect();
/**
* 获取xml元素path值
*/
String path = forwardModel.getPath();
// 判断是否为重定向
if (redirect) {
response.sendRedirect(request.getContextPath() + "/" + path);
} else {
request.getRequestDispatcher(path).forward(request, response);
}
}
4. 反射赋值
4.1 创建接口DriverModel<T>
package com.ycxw.framework;
/**
* 模型驱动接口
* Book book = new Book();
* @author 云村小威
*
* @param <T>
*/
public interface ModelDriver<T> {
T getModel();
}
4.2 实现接口类
针对需要进行反射赋值的具体子控制器类,实现该接口DriverModel。
package com.ycxw.servlet;
import java.io.IOException;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.ycxw.entity.Book;
import com.ycxw.framework.Action;
import com.ycxw.framework.ModelDriver;
/**
* 施工类 继承子控制器
*
* @author 云村小威
*
* @2023年6月29日 下午8:32:59
*/
public class BookAction extends Action implements ModelDriver<Book>{
//创建表对应的属性对象
Book book = new Book();
@Override
public Book getModel() {
// TODO Auto-generated method stub
return book;
}
public String load(HttpServletRequest req, HttpServletResponse resp) {
//获取所有的参数
Map<String, String[]> map = req.getParameterMap();
System.out.println("Book查询的业务逻辑");
try {
resp.sendRedirect("index.jsp");
} catch (IOException e) {
e.printStackTrace();
}
return "List";
}
public String query(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("Book查询的业务逻辑");
try {
resp.sendRedirect("index.jsp");
} catch (IOException e) {
e.printStackTrace();
}
return "List";
}
public String edit(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("Book修改的业务逻辑");
try {
resp.sendRedirect("index.jsp");
} catch (IOException e) {
e.printStackTrace();
}
return "toList";
}
public String delete(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("Book删除的业务逻辑");
try {
resp.sendRedirect("index.jsp");
} catch (IOException e) {
e.printStackTrace();
}
return "toList";
}
public String add(HttpServletRequest req, HttpServletResponse resp) throws Exception {
System.out.println("Book新增的业务逻辑");
req.getRequestDispatcher("index.jsp").forward(req, resp);
return "toList";
}
}
只要实现了DriverModel接口,则必须要对实体类进行初始化,并在getModel()方法中返回实例化后的对象。
4.3 反射对象赋值
再次修改ActionServlet中的业务逻辑处理流程,在反射调用方法之前,请先进行反射参数赋值操作。
/**
* 获取config文件中action标签的type属性值
* type指java类
* com.ycxw.servlet.BookAction
*/
String type = actionModel.getType();
// 通过反射创建对象
Action instance;
try {
instance = (Action) Class.forName(type).newInstance();
// 判断bookAction有没有实现ModelDriver接口
if (instance instanceof ModelDriver) {
// 向下转型获取接口方法
ModelDriver md = (ModelDriver) instance;
Object bean = md.getModel();
// 把获取的参数保存到该对象中
BeanUtils.populate(bean, request.getParameterMap());
}
5. 完整优化中央控制器实例
5.1 完整 DisPathServlet类代码
package com.ycxw.framework;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.beanutils.BeanUtils;
import com.ycxw.framework.model.ActionModel;
import com.ycxw.framework.model.ConfigModel;
import com.ycxw.framework.model.ConfigModelFactory;
import com.ycxw.framework.model.ForwardModel;
/**
* 中央控制器(ActionServlet)
*
* @author 云村小威
*
* @2023年6月29日 下午8:14:38
*/
@WebServlet("*.action")
public class DisPathServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
// 通过xml建模方法进行储存
private ConfigModel configModel;
@Override
public void init() throws ServletException {
/**
* 初始化存值就是给每个施工员根据施工证进行存档:
*/
try {
// configModel包含了所有的子控制器
configModel = ConfigModelFactory.build();
} catch (Exception e) {
e.printStackTrace();
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doPost(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// http://localhost:8080/MVC_project/book.action?methodName=delete...
/**
* 获取请求路径
*/
String uri = request.getRequestURI();
// 截取book
uri = uri.substring(uri.lastIndexOf("/"), uri.lastIndexOf("."));
// 要通过uri->> /book,在configModel对象中找
ActionModel actionModel = configModel.pop(uri);
// 判断没找对象等于空就抛出异常
if (actionModel == null)
throw new RuntimeException("action not config");
/**
* 获取config文件中action标签的type属性值
* type指java类
* com.ycxw.servlet.BookAction
*/
String type = actionModel.getType();
// 通过反射创建对象
Action instance;
try {
instance = (Action) Class.forName(type).newInstance();
// 判断bookAction有没有实现ModelDriver接口
if (instance instanceof ModelDriver) {
// 向下转型获取接口方法
ModelDriver md = (ModelDriver) instance;
Object bean = md.getModel();
// 把获取的参数保存到该对象中
BeanUtils.populate(bean, request.getParameterMap());
}
// 业务代码执行后返回值
String execute = instance.execute(request, response);
// 通过返回值拿到,该方法结果是重定向还是转发,还是跳转哪个页面
ForwardModel forwardModel = actionModel.pop(execute);
if (forwardModel != null) {
// 获取forwardModel是否从定向值
boolean redirect = forwardModel.isRedirect();
/**
* 获取xml元素path值
*/
String path = forwardModel.getPath();
// 判断是否为重定向
if (redirect) {
response.sendRedirect(request.getContextPath() + "/" + path);
} else {
request.getRequestDispatcher(path).forward(request, response);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
5.2 测试JSP页面
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>测试</title>
</head>
<body>
<a href="${pageContext.request.contextPath }/book.action?methodName=add&bid=1&bname=aa&price=13">增加</a>
<a href="${pageContext.request.contextPath }/book.action?methodName=delete">删除</a>
<a href="${pageContext.request.contextPath }/book.action?methodName=edit">修改</a>
<a href="${pageContext.request.contextPath }/book.action?methodName=query">查询</a>
<a href="${pageContext.request.contextPath }/book.action?methodName=load">回显</a>
</body>
</html>
5.3 论证反射对象赋值
1. 通过debug调试,运行到当前通过反射拿到指定对象时为空
2. 执行完populate方法后将自定赋值等效于遍历对象一个个赋值
BeanUtils.populate(bean, request.getParameterMap());
最后在讲解一下populate方法的解释:
6. populate方法详解
BeanUtils.populate(Object bean, Map properties)
BeanUtils
位于org.apache.commons.beanutils.BeanUtils
下,populate是BeanUtils工具类的一个方法。
作用:
这个方法会遍历map<key, value>
中的key
,如果bean
中有这个属性,就把这个key
对应的value
值赋给bean
的属性