自定义MVC 框架

本文详细介绍了自定义MVC框架的原理和实现过程,包括模型-视图-控制器的职责、如何减少重复代码、使用反射和设计模式进行优化。通过演示从servlet到中央控制器的演变,阐述了配置文件的可修改存放路径,以提高代码的灵活性和可维护性。
摘要由CSDN通过智能技术生成

思维导图:
在这里插入图片描述

一,什么是自定义MVC

MVC全称 Model View Controller,是模型(Model)-视图(View)-控制器(Controller)的缩写,是一种软件设计思想,MVC能提升开发效率,提高代码的重用,对于后期的更新升级具有莫大的好处。

Model:处理大部分的业务逻辑和数据操作

View:负责页面设计,以html的方式呈现给用户

Controller:处理用户请求,负责从视图读取数据,控制用户输入,并向模型发送数据,进行处理

自定义MVC框架原理图 框架:反射+设计模式(极大减少了代码量 重复性代码交给框架完成)
在这里插入图片描述
好处:1,通用分页+通用的增删改 2,各层(mc)数据dao层,控制层代码缩减 3,前台代码的增减优化

二,自定义MVC原理(含演绎过程)

在我们写项目时,大多有的一个通病,就是有大量的重复代码,重复思路,(servlet,dao包等)为解决此问题,我们就可以用一个框架来完成。

演绎过程页面:
在这里插入图片描述
演绎过程(一)(处理servlet)(四个servlet)(其中doGet方法没有实际用处,)
在这里插入图片描述
在这里插入图片描述
运行结果:(方法在控制台打印)
在这里插入图片描述
演绎过程(二)(建立BooKServlet来写四个方法)
在这里插入图片描述
在这里插入图片描述
演绎过程(三)(反射优化)

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		String methodName = req.getParameter("methodName");
		try {
			Method m = this.getClass().getDeclaredMethod(methodName, HttpServletRequest.class,HttpServletResponse.class);
			m.setAccessible(true);
			m.invoke(this ,req, resp);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
 
	private void load(HttpServletRequest req, HttpServletResponse resp) {
		System.out.println("bookdao.load()...调用了回显方法");
	}
 
	private void list(HttpServletRequest req, HttpServletResponse resp) {
		System.out.println("bookdao.list()...调用了查询方法");
	}
 
	private void delete(HttpServletRequest req, HttpServletResponse resp) {
		System.out.println("bookdao.delete()...调用了删除方法");	
	}
 
	private void edit(HttpServletRequest req, HttpServletResponse resp) {
		System.out.println("bookdao.edit()...调用了修改方法");
	}
 
	private void add(HttpServletRequest req, HttpServletResponse resp) {
		System.out.println("bookdao.add()...调用了新增方法");
	}
	
	private void ref(HttpServletRequest req, HttpServletResponse resp) {
		System.out.println("bookdao.ref()...调用了关联查询方法");
	}
	
	private void other(HttpServletRequest req, HttpServletResponse resp) {
		System.out.println("bookdao.other()...调用了其他方法");
	}

在这里插入图片描述
运行结果:
在这里插入图片描述
演绎过程(四)(中央控制器及子控制器优化 )

Action子控制器 (抽取所有处理请求的方法作为父类)

ActionSupport (实现Action 重写execte方法 ,通过反射获取对应方法)

BookAction (处理浏览器发过来的请求)

优化子控制器(Action)


package com.whw.framework;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
/*
 * 子控制器(它来处理浏览器请求)
 * 针对于方法(如add)进行向上抽取,抽象  abstract
 */
public interface Action {
	
//	这是一个针对于方法(如add)进行向上抽取,抽象方法
//	作用:能够处理浏览器的“所有”请求。(如add)
//	通过返回值来决定跳转哪个页面(至于重定向/还是转发由中央控制器决定)
	public String execute(HttpServletRequest req, HttpServletResponse resp);
	
    }

(ActionSupport)


package com.whw.framework;
 
import java.lang.reflect.Method;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
/*
 * 作用:能够处理浏览器的“所有”请求,(如add)
 */
public class ActionSupport implements Action{
 
	public String execute(HttpServletRequest req, HttpServletResponse resp) {
		String methodName = req.getParameter("methodName");
		String res = null;
		try {
			Method m = this.getClass().getDeclaredMethod(methodName, HttpServletRequest.class,HttpServletResponse.class);
			m.setAccessible(true);
			res = (String) m.invoke(this ,req, resp);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return res;
	}
}

(BookAction)

package com.whw.web;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import com.lgs.entity.Book;
import com.lgs.framework.ActionSupport;
import com.lgs.framework.ModelDriver;
 
public class BookAction extends ActionSupport implements ModelDriver<Book>{
 
//	从父类继承了execute方法,就把反射动态调用的方法继承过来了
//	BookAction-->BookServlet
//	当前子控制器在哪里调用?把子控制器与浏览器关联起来
	
	public Book book = new Book();
	
	private String add(HttpServletRequest req, HttpServletResponse resp) {
		
//		book.setBid(req.getParameter("bid"));
//		book.setBname(req.getParameter("bname"));
//		book.setPrice(req.getParameter("price"));
//		book.setAthor(req.getParameter("athor"));
//		book.setPublish(req.getParameter("publish"));
//		System.out.println(book);
		
	System.out.println("bookdao.add(book)...调用了新增方法"+"===="+"book");
	return "list";
	}
 
	private void list(HttpServletRequest req, HttpServletResponse resp) {
		System.out.println("bookdao.list()...调用了查询方法");
	}
	
	private String toEdit(HttpServletRequest req, HttpServletResponse resp) {
		System.out.println("bookdao.list()...调用了查询方法");
		return "toEdit";
	}
 
	private void delete(HttpServletRequest req, HttpServletResponse resp) {
		System.out.println("bookdao.delete()...调用了删除方法");	
	}
 
	public Book getModel() {
		// TODO Auto-generated method stub
		return book;
	}
	
}

DispatchServlet中央控制器(在当前中央控制器中必然会有所有的子控制器的集合)

优化中央控制器 (DispatchServlet)

package com.whw.framework;
 
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
 
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 org.apache.commons.beanutils.PropertyUtils;
 
import com.lgs.util.StringUtils;
import com.lgs.web.BookAction;
 
/*
 * 中央控制器
 * jsp: /book.action
 */
//@WebServlet("*.action")
public class DispatchServlet extends HttpServlet{
 
//	在当前中央控制器中必然会有所有的子控制器的集合
//	缺陷:如何有新的实体类要进行增删改查,那么意味着要改动代码,代码设计不够灵活
//	private Map<String, ActionSupport> actions = new HashMap<>();
//	在不改动代码的情况下也能获得子控制器
//	方案:我把加子控制器的逻辑,放到代码中完成(代码更加灵活)
//	configModel又同过建模的知识把所有的配置信息读取过来了
	private ConfigModel configModel = null;
	
//	初始化所有的子控制器到当前的中央控制器中
		public void init() throws ServletException {
//			在集合中就有了一个子控制器
//			actions.put("/book", new BookAction());
//			actions.put("/goods", new BookAction());
			try {
//				String configurationLocation = "/luo.xml";
				String configurationLocation = this.getInitParameter("configurationLocation");
//				web.xml中有配置
				if(StringUtils.isNotBlank(configurationLocation)) {
					configModel = ConfigModelFactory.build(configurationLocation);
				}else {//没有配置
					configModel = ConfigModelFactory.build();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
			
		}
		
		public static void main(String[] args) throws Exception {
			ConfigModel build = ConfigModelFactory.build("/luo.xml");
			System.out.println(build);
		}
	
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		doPost(req, resp);
	}
	
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//		把子控制器与浏览器请求关联起来,能够处理请求的子控制器
//		http://localhost:8080/book.action?methodName=add-->BookAction.add();
		/*
		 * 1,url-->book
		 * 2,通过book字符串在action找到BookAction
		 * 3,调用BookAction的add,想要调用add,实际上只要统一调用execute方法就可以了
		 */
//		获取到浏览器的请求地址
		String url = req.getRequestURI();
//		1,url-->book
		url = url.substring(url.lastIndexOf("/"), url.lastIndexOf("."));
//		2,通过book字符串在action找到BookAction
//		ActionSupport action = actions.get(url);
//		原来在map中寻找子控制器,现在在配置文件中寻找子控制器
		/*
		 * 1,通过/book找到对应的ActionModel对象
		 * 2,通过ActionModel对象拿到类的全路径
		 * 3,反射实例化对象
		 */
		ActionModel actionModel = configModel.pop(url);
//		类的全路径名
		String type = actionModel.getType();
		ActionSupport action;
		try {
//			BookAction/...
			action = (ActionSupport) Class.forName(type).newInstance();
//			完成实体类参数的封装
			if(action instanceof ModelDriver) {
//				当前子控制器实现了模型驱动接口
				ModelDriver m = (ModelDriver) action;
//				Book/Goods/...
				Object bean = m.getModel();
//				所有的请求参数都在这里,需要将所有的请求参数封装到Book/Goods/...
				BeanUtils.populate(bean, req.getParameterMap());
//				PropertyUtils.getProperty(bean, name);
			}
//			执行业务逻辑  bookAction.add方法的返回值 "list"
			
			String res = action.execute(req, resp);
			ForwardModel forwardModel = actionModel.pop(res);
			String path = forwardModel.getPath();
			boolean isRedirect = forwardModel.isRedirect();
			if(isRedirect) {
				resp.sendRedirect(req.getContextPath()+path);
			}else {
				req.getRequestDispatcher(path).forward(req, resp);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
}

(中央控制器相当于前台,若要与后台进行联系需要服务员,则需与浏览器进行连接)

@WebServlet("*.action")

连接思路:(详细如上代码中有)
把子控制器与浏览器请求关联起来,能够处理请求的子控制器
http://localhost:8080/book.action?methodName=add–>BookAction.add();
1,url–>book 2,通过book字符串在action找到BookAction
3,调用BookAction的add,想要调用add,实际上只要统一调用execute方法就可以了

在这里插入图片描述
运行结果:
在这里插入图片描述

结论:(当有了新的需求时,需要将子控制器添加到集合中,就是说需要改的代码,不够灵活)

解决方案:我把加子控制器的逻辑,放到代码中完成(代码更加灵活)configModel又通过建模的知识把所有的配置信息读取过来了(建模的代码在CSDN主页中有)
在这里插入图片描述
luo.xml文件


<?xml version="1.0" encoding="UTF-8"?>
<config>
	<!-- 
	在这里每加一个配置,就相当于actions.put("/book", new BookAction());
	这样就解决了代码性灵活性的问题   false代表转发  true代表重定向(避免重複提價)  
 	-->
	 <action path="/book" type="com.lgs.web.BookAction">
		<forward name="list" path="/BookList.jsp" redirect="false" />
		<forward name="toEdit" path="/BookEdit.jsp" redirect="true" />
	</action> 
	
</config>

主控制器代码修改


/**
		 * 1、通过url来找到config文件中对应的action对象
		 * 2、然后通过该对象来取到路径名servlet.BookAction
		 * 3、然后找到对应的方法执行
		 */
		String url = req.getRequestURI();
		url = url.substring(url.lastIndexOf("/"), url.lastIndexOf("."));
		ActionModel actionmodel=configModel.pop(url);
		String type = actionmodel.getType();
		ActionSupport action=null;
	    try {
			action = (ActionSupport) Class.forName(type).newInstance();
			action.excute(req, resp);
		} catch (Exception e) {
			e.printStackTrace();
		}

以上优化还存在遗留问题
1,代码冗余问题 req.getparameter("")(参数过多,费事费力)
2,页面跳转问题(避免垃圾代码重复敲写)

问题一:代码冗余问题

ModelDriver (帮助中央控制器完成参数封装的工程)


package com.whw.framework;
 
/*
 * 模型驱动接口
 * 作用:帮助中央控制器完成参数封装的工程
    book.setBid(req.getParameter("bid"));
	book.setBname(req.getParameter("bname"));
	book.setPrice(req.getParameter("price"));
	book.setAthor(req.getParameter("athor"));
 */
public interface ModelDriver<T> {
 
	T getModel();
	
}

注意:(在没有写ModelDriver类时,BookAction是没有实现ModelDriver接口的)
在这里插入图片描述
问题二:页面跳转问题

<forward name="list" path="/BookList.jsp" redirect="false" />

<forward name="toEdit" path="/BookEdit.jsp" redirect="true" />

name:接收到的返回值  path:跳转路径  redicect:跳转方式

false代表转发  true代表重定向(避免重复提交)

思路:

1,点击操作时,进入中央控制器,获取url,取得要跳转到的实体类

2,进入子控制器实现类,获取要实现的方法

3,在从BookAction类中找到对应的返回值

4,最后经过xml文件获取内容进行判断跳转

DispatchServlet(中央控制器)

注意:resp.sendRedirect(req.getContextPath()+path); req.getContextPath()记得加入转发路径,否则会报错

String res = action.execute(req, resp);
			ForwardModel forwardModel = actionModel.pop(res);
			String path = forwardModel.getPath();
			boolean isRedirect = forwardModel.isRedirect();
			if(isRedirect) {
				resp.sendRedirect(req.getContextPath()+path);
			}else {
				req.getRequestDispatcher(path).forward(req, resp);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

ActionSupprot(子控制器接口实现类)


package com.whw.framework;
 
import java.lang.reflect.Method;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
/*
 * 作用:能够处理浏览器的“所有”请求,(如add)
 */
public class ActionSupport implements Action{
 
	@Override
	public String execute(HttpServletRequest req, HttpServletResponse resp) {
		String methodName = req.getParameter("methodName");
		String res = null;
		try {
			Method m = this.getClass().getDeclaredMethod(methodName, HttpServletRequest.class,HttpServletResponse.class);
			m.setAccessible(true);
			res = (String) m.invoke(this ,req, resp);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return res;
	}
}

BookAction(标记处为返回值)
在这里插入图片描述
luo.xml文件
在这里插入图片描述
运行结果:(跳转成功)
在这里插入图片描述

五,配置文件可修改存放路径

当把文件导成jar包之后我们在外面是打不开的,这时候需要修改存放路径

//@WebServlet("*.action") 注释联系

新建一个xml文件进行配置

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
 
  <display-name>T269-lgs_mvc</display-name>
  
  <servlet>
 	 <servlet-name>mvc</servlet-name>
     <servlet-class>com.lgs.framework.DispatchServlet</servlet-class>
  <init-param>
  	 <param-name>configurationLocation</param-name>
  	 <param-value>/luo.xml</param-value>
  </init-param>
  </servlet>
  
  <servlet-mapping>
  	  <servlet-name>mvc</servlet-name>
  	  <url-pattern>*.action</url-pattern>
  </servlet-mapping>
</web-app>

configurationLocation接收从主控制器传递文件名,可以在主控制器拿到
/luo.xml这边建什么文件,主控制器就能拿到

DispatchServlet(主控制器)
在这里插入图片描述
测试+运行结果:(跳转成功)
在这里插入图片描述
到这里就结束了,有不对或补充的地方欢迎大家评论,谢谢!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值