目录
2.1中央处理器(请求转发给子控制器,然后子控制器将需要处理的参数给中央控制器)
4.4怎么调用增删改通用方法呢?(无返回,就不测试了,直接数据库返回影响行数)
我们先来看看今天的思维导图,今天给友友们分享自定义mvc实现过程的demo,希望对大家有帮助。


核心组件说明:
- 中央控制器(ActionServlet): 负责接收所有的请求,并分别给控制器具体处理。
- 子控制器(Action):负责处理中央处理器分配的请求
- 视图(view): jsp页面,负责显示
- 模型(Model): 负责业务处理逻辑
用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑,简单来说,就是一种软件设计典范。
为什么我们要学习mvc呢?因为它提高了程序的可维护性、可移植性、可扩展性与可重用性,降低了程序的开发难度。它主要分模型、视图、控制器三层。
优势:
- 模型(model): 它是应用程序的主体部分,主要包括业务逻辑模块(web项目中的dao类)和数据模块(pojo类)。pojo一般可以叫做实体域模型,dao和service称为过程域模型。
- 视图(view): 用户与之交互的界面、在web中视图一般由jsp,html组成,其他的还包括android,ios等等。
- 控制器(controller): 接收来自界面的请求 并交给模型进行处理 在这个过程中控制器不做任何处理只是起到了一个连接的做用。
劣势:
- 增加系统结构和实现的复杂性。对于简单的界面,严格遵守MVC,需要使模型、视图与控制器分离,增加系统复杂性
- 视图和控制器之间的关系太过紧密了
为什么要存在模型层?
目的是为了方便对xml配置文件进行建模
注意:在项目的src目录下加入如下配置文件(config.xml)
- config[
- config (action*)>
- action (forward*)>
- forward EMPTY>
- action
- path CDATA #REQUIRED
- type CDATA #REQUIRED
- >
- forward
- name CDATA #REQUIRED
- path CDATA #REQUIRED
- redirect (true|false) "false"
- >
- ]>
- <config>
- <action path="/bookAction" type="com.zking.action.BookAction">
- <forward name="test" path="/test.jsp" redirect="false"/>
- action>
- <action path="/studentAction" type="com.zking.action.StudentAction">
- <forward name="test2" path="/test2.jsp" redirect="false"/>
- action>
- config>
xml文件解析:
action:子控制器;path属性子控制器名,type属性子控制器全路径
forward:重定向跳转;path属性跳转的页面路径,name跳转的页面
为方便解析xml,这里举例三个模型
ActionModel模型层
- /**
- * ActionModel模型
- */
- public class ActionModel {
-
- private String path;
-
- private String type;
- //正则规则
- private static Pattern pattern = Pattern.compile("^/.+$");
-
- private Map
forwardMap = new HashMap<>(); -
- public String getPath() {
- return path;
- }
-
- public void setPath(String path) {
- checkPath(path);
- this.path = path;
- }
-
- public String getType() {
- return type;
- }
-
- public void setType(String type) {
- this.type = type;
- }
-
- //获取xml里forward的name,然后保存到集合中的方法
- public void put(ForwardModel forward) {
- if(forwardMap.containsKey(forward.getName())) {//如果xml中forward的name已存在,就不能添加
- throw new ForwardDuplicateDefinitionException("forward name:"+forward.getName()+"不能重复");
- }
- forwardMap.put(forward.getName(), forward);
- }
-
- //获取xml里forward的name值的方法
- public ForwardModel find(String name) {
- if(!forwardMap.containsKey(name)) {//xml中如果forward里面不包含name报异常
- throw new ForwardNotFoundException("forward name:"+name+"不存在");
- }
- return forwardMap.get(name);
- }
-
-
- @Override
- public String toString() {
- return "ActionModel [path=" + path + ", type=" + type + "]";
- }
-
- //严格规定path必须以/开头的方法
- private void checkPath(String path) {
- Matcher matcher = pattern.matcher(path);
- boolean b = matcher.matches();
- if (!b) {
- throw new InvalidPathException("ForwardModel.path[" + path + "]必须以/开头");
- }
- }
- }
forwardModel模型
- /**
- * ForwardModel模型
- */
- public class ForwardModel {
-
- private String name;
-
- private String path;
-
- private boolean redirect;
-
- private static Pattern pattern = Pattern.compile("^/.+$");
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public String getPath() {
- return path;
- }
-
- public void setPath(String path) {
- checkPath(path);
- this.path = path;
- }
-
- public boolean isRedirect() {
- return redirect;
- }
-
- public void setRedirect(boolean redirect) {
- this.redirect = redirect;
- }
-
- public void setRedirect(String redirect) {
- if("true".equals(redirect)
- || "false".equals(redirect)) {
- this.redirect = Boolean.valueOf(redirect);
- } else {
- throw new RuntimeException("属性redirect的值必须位ture或者false");
- }
- }
-
- @Override
- public String toString() {
- return "ForwardModel [name=" + name + ", path=" + path + ", redirect=" + redirect + "]";
- }
-
- private void checkPath(String path) {
- Matcher matcher = pattern.matcher(path);
- boolean b = matcher.matches();
- if (!b) {
- throw new InvalidPathException("ForwardModel.path[" + path + "]必须以/开头");
- }
- }
- }
configModel模型
- /**
- * configModel模型
- */
- public class ConfigModel {
-
- private Map
actionMap = new HashMap<>(); -
- //获取xml里action的name,然后保存到集合中的方法
- public void put(ActionModel action) {
- if(actionMap.containsKey(action.getPath())) {如果xml中action的name已存在,就不能添加
- throw new ActionDuplicateDefinitionException("action path:" + action.getPath()+ " 不能重复");
- }
- actionMap.put(action.getPath(), action);
- }
-
- //获取xml里action的name值的方法
- public ActionModel find(String path) {
- if(! actionMap.containsKey(path)) {//xml中如果action里面不包含name报异常
- throw new ActionNotFoundException("action path:"+path+"没有找到");
- }
- return actionMap.get(path);
- }
-
- }
通过servlet来实现一个中央控制器,负责所有请求的接收。
- /**
- * 中央处理器,负责接受所有的请求,转发给子控制器
- */
- @WebServlet("*.action")
- public class ActionDispatchServlet extends HttpServlet {
-
- private static ConfigModel configModel= ConfigModelFactory.getConfig();
-
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
-
- doPost(req, resp);
- }
-
- @Override
- protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
-
- String servletPath = req.getServletPath();
- System.out.println("分割前="+servletPath);
- String path = servletPath.split("\\.")[0];//转义
- System.out.println("分割后="+path);
- Action action = getActionByPath(path);
-
- //判断Action需要进行参数处理
- if(action instanceof ModelDrive) {
- ModelDrive model = (ModelDrive)action;
- Object obj = model.getModel();
- try {
- BeanUtils.populate(obj, req.getParameterMap());
- } catch (IllegalAccessException | InvocationTargetException e) {
- throw new ActionDispatchSetParamterException("在中央处理器中设置参数的时候发生异常",e);
- }
- }
-
- //转发
- String name = action.execute(req, resp);
- }
为方便调试,实现两个子控制器
BookAction子控制器
- public class BookAction extends AbstractorAction implements ModelDrive {
-
- private Book book = new Book();
-
- @Override
- public Object getModel() {
- return book;
- }
-
- public String getBook(HttpServletRequest req, HttpServletResponse resp) {
- System.out.println("Book Action getBook id=" + book.getBid());
- return "test";
- }
-
- public String addBook(HttpServletRequest req, HttpServletResponse resp) {
- System.out.println("Book Action addBook id=" + book.getBid());
- return "test";
- }
- }
StudentAction子控制器
- public class StudentAction extends AbstractorAction implements ModelDrive{
-
- private Student student = new Student();
-
- @Override
- public Object getModel() {
- return student;
- }
- public String getstudent(HttpServletRequest req, HttpServletResponse resp) {
- System.out.println("student Action getstudent sid=" + student.getSid());
- return "test2";
- }
-
- public String addstudent(HttpServletRequest req, HttpServletResponse resp) {
- System.out.println("student Action addstudent sid=" + student.getSid());
- return "test2";
- }
- }
Test.jsp页面
- <%@ page language="java" contentType="text/html; charset=UTF-8"
- pageEncoding="UTF-8"%>
- 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=UTF-8">
- <title>Insert title heretitle>
- head>
- <body>
- <h4>This my BookActionh4>
- body>
- html>
Test2.jsp页面
- <%@ page language="java" contentType="text/html; charset=UTF-8"
- pageEncoding="UTF-8"%>
- 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=UTF-8">
- <title>Insert title heretitle>
- head>
- <body>
- <h4>This my StudentActionh4>
- body>
- html>
运行结果:
BookActon


StudentAction

目的
子控制器的接口,规定每个子控制器必须实现的方法
- /**
- * 每个子控制器必须实现该接口,负责处理中央处理器分配的请求
- * @author Administrator
- */
- public interface Action {
-
- /**
- * 处理请求
- * @param request 请求
- * @param response 响应
- * @return String 返回转发或重定向的jsp页面名称
- */
- String exeute(HttpServletRequest request, HttpServletResponse response);
-
- }
目的
参数处理接口,实现了该接口的Action,表示需要中央控制器统一处理参数
参数将被存放到getModel方法返回的对象中
- /**
- * 参数处理接口,实现了该接口的Action,表示需要中央控制器统一处理
- * 参数,参数将被存放到getModel方法返回的对象中
- */
- public interface ModelDrive {
-
- Object getModel();
-
- }
目的
在提交请求的时候,做一个规定:必须要有一个参数,叫做methodName,参数的值需要处理请求的方法名、在execute再通过反射机制去进行方法调用。
- /**
- * 在提交请求的时候,做一个规定:必须要有一个参数,叫做methodName,参数的值需要处理请求的方法名
- * 在execute再通过反射机制去进行方法调用
- */
- public abstract class AbstractorAction implements Action {
-
-
- @Override
- public String execute(HttpServletRequest req, HttpServletResponse resp) {
-
- String methodName = req.getParameter("methodName");
- System.out.println(methodName);
- Class extends AbstractorAction> clazz = (Class extends AbstractorAction>)this.getClass();
- try {
- Method method = clazz.getDeclaredMethod(
- methodName,
- HttpServletRequest.class,
- HttpServletResponse.class);
- return (String) method.invoke(this, req,resp);
- } catch (Exception e) {
- e.printStackTrace();
- }
- return null;
- }
- }
完善中央处理器,通过配置文件来获取子控制器配置,通过反射,动态的获取子控制器实例。
为了在中央控制器中完成请求的分发,需要在中央控制器中维护所有子控制器的实例,并且能够依据请求路径,将请求转发给与其关联的子控制器。
- /**
- * 中央处理器,负责接受所有的请求,转发给子控制器
- */
- @WebServlet("*.action")
- public class ActionDispatchServlet extends HttpServlet {
-
- private static ConfigModel configModel= ConfigModelFactory.getConfig();
-
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
-
- doPost(req, resp);
- }
-
- @Override
- protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
-
- String servletPath = req.getServletPath();
- System.out.println("分割前="+servletPath);
- String path = servletPath.split("\\.")[0];//转义
- System.out.println("分割后="+path);
- Action action = getActionByPath(path);
-
- //判断Action需要进行参数处理
- if(action instanceof ModelDrive) {
- ModelDrive model = (ModelDrive)action;
- Object obj = model.getModel();
- try {
- BeanUtils.populate(obj, req.getParameterMap());
- } catch (IllegalAccessException | InvocationTargetException e) {
- throw new ActionDispatchSetParamterException("在中央处理器中设置参数的时候发生异常",e);
- }
- }
-
- //转发
- String name = action.execute(req, resp);
-
- //处理ajax请求
- if(name == null || "".equals(name)) {
- return;
- }
-
- ForwardModel fm = configModel.find(path).find(name);
- if(fm.isRedirect()) {
- resp.sendRedirect(fm.getPath());
- } else {
- req.getRequestDispatcher(fm.getPath()).forward(req, resp);
- }
-
- }
-
- //使用反射机制获取Action实例
- private Action getActionByPath(String path) {
- ActionModel am = configModel.find(path);
- String tmp = am.getType();
-
- Action action = null;
- try {
- Class
actionClass = (Class)Class.forName(tmp); - action = actionClass.newInstance();
- } catch (Exception e) {
- e.printStackTrace();
- }
- return action;
- }
- }
带分页的查询
- /**
- * 执行查询,分页,没有条件
- * @param sql 查询语句
- * @param pageBean 分页条件
- * @param clazz 存放数据的实体类型
- * @return list
- */
- public static
List query(String sql, - PageBean pageBean,
- Class
clazz) { - return query(sql, new Object[]{}, pageBean, clazz);
- }
不分页查询
- /**
- * 执行查询,不分页
- * @param sql sql语句
- * @param args 查询参数,对象数组
- * @param clazz 数据记录对应的实体对象类型
- * @return list
- */
- public static
List query(String sql, - Object[] args,
- Class
clazz) { -
- return query(sql, args, null, clazz);
- }
具体实现方法
- /**
- * 分页查询功能
- * @param sql sql语句
- * @param args 查询参数,对象数组
- * @param pageBean 分页对象
- * @param clazz 数据记录对应的实体对象类型
- * @return
- */
- public static
List query(String sql, - Object[] args,
- PageBean pageBean,
- Class
clazz) { -
- List
datas = new ArrayList<>(); -
- Connection con = null;
- PreparedStatement ps = null;
- ResultSet rs = null;
-
- //如果需要分页,则统计总记录数
- if(pageBean != null && pageBean.isPagination()) {
- String sqlCount = "SELECT COUNT(*) FROM (" + sql + ") t";
-
- try {
- con = DBUtil.getConection();
- ps = con.prepareStatement(sqlCount);
-
- //设置查询参数
- int i = 1;
- for(Object arg: args) {
- ps.setObject(i, arg);
- i++;
- }
-
- rs = ps.executeQuery();
-
- while(rs.next()) {
- pageBean.setTotal(rs.getInt(1));
- }
- } catch (SQLException e) {
- DBUtil.closeDB(rs, ps, con);
- throw new RuntimeException("统计总记录数异常", e);
- } finally {
- DBUtil.closeDB(rs, ps);
- }
-
- if(pageBean.getTotal()== 0) {
- return datas;
- }
- }
-
-
- try {
- String pagingSql = sql;
- if(pageBean != null && pageBean.isPagination()) {
- pagingSql = sql + " limit "
- + pageBean.getStartIndex() + "," + pageBean.getRows();
- }
-
- con = con == null ? DBUtil.getConection() : con;
- ps = con.prepareStatement(pagingSql);
-
- //设置查询参数
- int i = 1;
- for(Object arg: args) {
- ps.setObject(i, arg);
- i++;
- }
-
- rs = ps.executeQuery();
-
- Map
columnFieldMap = getColumnFieldMap(clazz); -
- int columnNum = rs.getMetaData().getColumnCount();
- while(rs.next()) {
- E bean = clazz.newInstance();
- for(int index = 1; index <= columnNum; index++) {
- String cn = rs.getMetaData().getColumnName(index);
- //如果实体类中没有定义与列名对应的属性,则直接放弃
- if(!columnFieldMap.containsKey(cn) || rs.getObject(index) == null) continue;
- BeanUtils.setProperty(bean, columnFieldMap.get(cn), rs.getObject(index));
- }
- datas.add(bean);
- }
-
- } catch (SQLException e) {
- throw new QueryRecordException("查询分页数据异常", e);
- } catch (InstantiationException | IllegalAccessException e) {
- throw new QueryRecordException("查询分页数据异常", e);
- } catch (InvocationTargetException e) {
- throw new QueryRecordException("查询分页数据异常", e);
- } finally {
- DBUtil.closeDB(rs, ps, con);
- }
-
- return datas;
- }
控制台结果:

带分页的查询
- @Override
- public List
queryUser2(User u, PageBean pb) { - String sql = "select id,name,loginName,pwd,rid from t_oa_user ";
-
- List
- if (u !=null && !"".equals(u.getName()) && u.getName() != null ) {
- sql += " where name like ?";
- param.add(u.getName() + "%");
- }
- return DbTemplate.query(sql, param.toArray(), pb, User.class);//.toArray();集合转为数组
-
- }
不带分页的查询
- @Override
- public List
listMeetingMember(){ - String sql="select * from t_oa_user";
-
- return DbTemplate.query(sql, MeetingMember.class);
- }
- /**
- * 新增,更新,或删除记录
- * @param sql 更新sql语句
- * @param args 参数数组
- * @return int 影响的行数
- */
- public static
int update(String sql, Object[] args) { -
- Connection con = null;
- PreparedStatement ps = null;
-
- try {
- con = DBUtil.getConection();
- ps = con.prepareStatement(sql);
-
- int i = 1;
- for(Object arg: args) {
- ps.setObject(i, arg);
- i++;
- }
-
- return ps.executeUpdate();
- } catch (SQLException e) {
- throw new UpdateRecordException("执行:"+ sql, e);
- } finally {
- DBUtil.closeDB(null, ps, con);
- }
- }
新增示例
- @Override
- public void addUser(User u) {
-
- String sql="insert into t_oa_user(name,loginName,pwd,rid) values(?,?,?,?);";
-
- DbTemplate.update(sql, new Object[] {u.getName(),u.getLoginName(),u.getPwd(),u.getRid()});
-
- }
修改示例
- @Override
- public void updateUser(User user) {
-
- String sql = "update t_oa_user set name=?, loginName=?, rid=? where id = ? ";
-
- DbTemplate.update(sql,new Object[]{user.getName(),user.getLoginName(),user.getRid(),user.getId()});
-
- }