目录
前言
自定义MVC实现
1. 导入XML配置文件
2. 导入XML解析建模
3. 优化中央控制器
3.1 修改DisPathServlet中init初始化方法
3.2 修改ActionServlet逻辑处理流程
3.3 通过反射机制实例化子控制器类
3.4 中央控制器将请求委托给子控制器处理
3.5 根据请求结果码跳转页面
4. 反射赋值
4.1 创建接口DriverModel
4.2 实现接口类
4.3 反射对象赋值
5. 完整优化中央控制器实例
5.1 完整 DisPathServlet类代码
5.2 测试JSP页面
5.3 论证反射对象赋值
6. populate方法详解
前言
在这篇 自定义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
的属性