自定义MVC

目录

一.什么是MVC

1.1.三层架构和MVC的区别

二.自定义MVC工作原理图 

三.自定义mvc实现

3.1 创建web工程

 3.2 中央处理器

3.3 Action接口定义

3.4 实现子控制器

3.5 完善中央控制器

3.5.1 请求分发功能

3.5.2 使用配置文件配置action

3.5.3 请求参数处理

1. 定义接口

在中央处理器中加入请求参数的处理能力

 2.处理请求参数中的null及空字符串转换问题

为了更方面的处理请求参数中的null及空字符串,转换为数值型数据的问题,加入一个监听器,在应用启动时注册转换器。

3.6 完善Action

1.构建抽象类

2.自定义的Action子控制器示例:

发送请求,示例http://localhost:8080/mvc/studentAction.actionmethodName=addStudent&sid=100&age=23&addr=aabbcc

四.其他公用组件的集成

五.打jar包


一.什么是MVC

MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范。用一种业务逻辑、数据、界面显示分离的方法,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。

MVC结构

  • Model:是应用程序中用于处理应用程序数据逻辑的部分,通常模型对象负责在数据库中存取数据。

  • View:是应用程序中处理数据显示的部分,通常视图是依据模型数据创建的。

  • Controller:是应用程序中处理用户交互的部分,通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据。

MVC是一个框架模式,它强制性的使应用程序的输入、处理和输出分开。使用MVC应用程序被分成三个核心部件:模型、视图、控制器。它们各自处理自己的任务。最典型的MVC就是JSP + servlet + javabean的模式

  • Model:常用javabean去实现,通过各种类来对数据库的数据进行获取,并封装在对象当中。

  • View:常用JSP来实现,通过可直接观察的JSP页面来展示我们从数据库中获取的数据。

  • Controller:常用servlet来实现,通过servlet来获取经过javabean包装过的对象(已存入数据库中的数据),然后再发送数据传输到JSP界面。

1)不能跨层调用; 2)只能由上往下进行调用:View -> Controller -> Model

1.1.三层架构和MVC的区别

  1. 三层架构是基于业务逻辑来分的,而MVC是基于页面来分的;

  2. 三层是种软件架构,通过接口实现编程,MVC模式是一种复合设计模式,一种解决方案;

  3. 三层架构模式是体系结构模式,MVC是设计模式;

  4. 三层架构模式又可归于部署模式,MVC可归于表示模式。

二.自定义MVC工作原理图 

核心组件说明:

  • 中央控制器(ActionServlet): 复杂接收所有的请求,并分别给控制器具体处理。
  • 自控制器(Action):负责处理中央处理器分配的请求
  • 视图(view): jsp页面,负责显示
  • 模型(Model): 负责业务处理逻辑

三.自定义mvc实现

3.1 创建web工程

创建一个web工程,需要加入必要的依赖。

 3.2 中央处理器

通过servlet来实现一个中央控制器,负责所有请求的接收。(后续中央控制器在再将请求转发给各个子控制器,此处可以先把请求接进来,转发功能后面再加)

/**
 * 中央控制器,负责接收所有的请求并分别给控制器具体处理
 * @author Administrator
 */
@WebServlet("*.action")
public class ActionDispatchServlet extends HttpServlet {
	
	@Override
	public void doGet(HttpServletRequest request, HttpServletResponse response) {
		doPost(request, response);
	}
	
	@Override
	public void doPost(HttpServletRequest request, HttpServletResponse response) {
		System.out.println("dopost ..... ");
	}

}

3.3 Action接口定义

Action接口定义了每个子控制器需要遵循的行为,使得所有的子控制器都有一个同一的抽象类型,所以我们可以在中央控制器中使用Action接口类型来引用所有的子控制器。这样就为用户扩展自定义的子控制器提供了条件

/**
 * 每个子控制器必须实现该接口,负责处理中央处理器分配的请求
 * @author Administrator
 */
public interface Action {
	
	/**
	 * 处理请求
	 * @param request  请求
	 * @param response 响应
	 * @return String 返回转发或重定向的jsp页面名称
	 */
	String exeute(HttpServletRequest request, HttpServletResponse response);

}

3.4 实现子控制器

为方便调试,实现两个子控制器

public class BookAction implements Action {

	@Override
	public String exeute(HttpServletRequest request, HttpServletResponse response) {
		
		return "bookList";
	}

}
public class StudentAction implements Action {

	@Override
	public String exeute(HttpServletRequest request, HttpServletResponse response) {
		// TODO Auto-generated method stub
		return "students";
	}

}

3.5 完善中央控制器

为了便于理解,我们可以分步骤的,循序渐进的完善中央控制器:

  • 编写简单的请求分发实现功能
  • 实现通过配置文件来配置子控制器的功能
  • 完善请求参数处理功能

3.5.1 请求分发功能

为了在中央控制器中完成请求的分发,需要在中央控制器中维护所有子控制器的实例,并且能够依据请求路径,将请求转发给与其关联的子控制器。

/**
 * 中央控制器,负责接收所有的请求并分别给控制器具体处理
 * @author Administrator
 */
@WebServlet("*.action")
public class ActionDispatchServlet extends HttpServlet {
	
	//用于保存path与action子控制器的映射
	public static Map<String, Action> actionMap = new HashMap<>();
	
	static {
		actionMap.put("/studentAction", new StudentAction());
		actionMap.put("/bookAction", new BookAction());
	}
	
	@Override
	public void doGet(HttpServletRequest request, HttpServletResponse response) {
		doPost(request, response);
	}
	
	@Override
	public void doPost(HttpServletRequest request, HttpServletResponse response) {
		String servletPath = request.getServletPath();
		String path = servletPath.split("\\.")[0];
		
		Action action = actionMap.get(path);
		
		String rpath = action.exeute(request, response);
		
		System.out.println(rpath);
	}

}

3.5.2 使用配置文件配置action

在上面的示例中,在中央控制器中直接创建action子控制器,如果新增一个子控制器需要在中央控制器中添加,这样并不实用。 为了增加灵活性,可以将action转移到配置文件中配置,中央控制器通过配置来初始化action子控制器。

1 此时需要将config.xml文件的解析和建模项目的功能集成进来。
(ConfigModel,ActionModel,ForwardModel,ConfigModelFactory)

2 在项目的src目录下加入如下配置文件(config.xml)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE config[
	<!ELEMENT config (action*)>
	<!ELEMENT action (forward*)>
	<!ELEMENT forward EMPTY>
	<!ATTLIST action
	  path CDATA #REQUIRED
	  type CDATA #REQUIRED
	>
	<!ATTLIST forward
	  name CDATA #REQUIRED
	  path CDATA #REQUIRED
	  redirect (true|false) "false"
	>
]>
<config>
	<action path="/studentAction" type="org.lisen.mvc.action.StudentAction">
		<forward name="students" path="/students/studentList.jsp" redirect="false"/>
	</action>
</config>

 完善中央处理器,通过配置文件来获取子控制器配置

@WebServlet("*.action")
public class ActionDispatchServlet extends HttpServlet {
	
	//用于保存path与action子控制器的映射
	//public static Map<String, Action> actionMap = new HashMap<>();
	
	private static ConfigModel configModel;
	
	static {
		//actionMap.put("/students", new StudentAction());
		//actionMap.put("/books", new BookAction());
		configModel  = ConfigModelFactory.getConfigModel();
	}
	
	@Override
	public void doGet(HttpServletRequest request, HttpServletResponse response) 
			throws IOException, ServletException {
		doPost(request, response);
	}
	
	@Override
	public void doPost(HttpServletRequest request, HttpServletResponse response) 
			throws IOException, ServletException {
		
		String servletPath = request.getServletPath();
		String path = servletPath.split("\\.")[0];
		
		Action action = getActionByPath(path);
		String name = action.exeute(request, response);
		
		ForwardModel forwardModel = getForwardModel(path, name);
		if (forwardModel.isRedirect()) {
			response.sendRedirect(request.getContextPath() + "/" + forwardModel.getPath());
		} else {
			request.getRequestDispatcher(forwardModel.getPath()).forward(request, response);
		}
	}
	
	//通过请求路径获取对应的action实例
	private Action getActionByPath(final String path) {
		ActionModel action = configModel.find(path);
		try {
			Class<?> clazz = Class.forName(action.getType());
			return (Action)clazz.newInstance();
		} catch (Exception e) {
			throw new RuntimeException("创建Action实例异常"+e.getMessage(), e);
		}
	}
	
	public ForwardModel getForwardModel(String path, String name) {
		return configModel.find(path).find(name);
	}

}

注: 本例的实现中Action子控制器是多例模式的,及每个请求对应一个Action实例

3.5.3 请求参数处理

1. 定义接口

/**
 * 对于需要处理请求参数的Action可以通过实现该接口获取请求参数的
 * 处理能力,中央控制器将会使用该接口来获取Model对象,并统一处理
 * 参数
 * @author Administrator
 */
public interface ModelDrive {
	
	Object getModel();

}

在中央处理器中加入请求参数的处理能力

@Override
public void doPost(HttpServletRequest request, HttpServletResponse response) 
		throws IOException, ServletException {
	
	......
	Action action = getActionByPath(path);
	
	//处理请求参数
	if(action instanceof ModelDrive) {
		Object model = ((ModelDrive) action).getModel();
		if(model != null) {
			try {
				BeanUtils.populate(model, request.getParameterMap());
			} catch (Exception e) {
				throw new RuntimeException("在中央处理器中处理请求参数时发生异常", e);
			}
		}
	}
	
	String name = action.exeute(request, response);
	......
}

 2.处理请求参数中的null及空字符串转换问题


为了更方面的处理请求参数中的null及空字符串,转换为数值型数据的问题,加入一个监听器,在应用启动时注册转换器。

/**
 * ServletContextListener接口为Servlet API中的接口,用于监听ServletContext对象的生命周期。
 * 当Servlet 容器启动或终止Web 应用时,会触发ServletContextEvent事件,该事件由
 * ServletContextListener来处理。
 * @author Administrator
 */
@WebListener
public class BeanUtilsListener implements ServletContextListener {

	/**
	 * 当Servlet 容器终止Web 应用时调用该方法。在调用该方法之前,
	 * 容器会先销毁所有的Servlet和Filter 过滤器。
	 */
	@Override
	public void contextDestroyed(ServletContextEvent arg0) {
		// TODO Auto-generated method stub
	}

	/**
	 * 当Servlet 容器启动Web应用时调用该方法。
	 * 在调用完该方法之后,容器再对Filter 初始化,
	 * 并且对那些在Web应用启动时就需要被初始化的Servlet进行初始化。
	 */
	@Override
	public void contextInitialized(ServletContextEvent arg0) {
		ConvertUtils.register(new IntegerConverter(null), Integer.class);
		ConvertUtils.register(new FloatConverter(null), Float.class);
		ConvertUtils.register(new DoubleConverter(null), Double.class);
		ConvertUtils.register(new LongConverter(null), Long.class);
		ConvertUtils.register(new BigDecimalConverter(null), BigDecimal.class);
	}

}

3.6 完善Action

每个Action只能有一个execute方法,如果处理一个模块的增删改查则需要单独编写多个Action,这样会比较麻烦。如果在一个Action实例中可以处理多个请求方法,则框架会更加灵活。

  • 规定请求参数中必须包含一个“methodName”参数,用于指定处理请求的Action中的方法
  • 构建一个抽象类,该类实现Action子控制器接口,通过反射机制调用其子类中的方法,方法名有请求参数“methodName”指定。
  • 需要开发的Action子控制器,集成上一步构建的抽象类,编写的用于处理请求的方法名要与请求参数“methodName”指定的方法名匹配,同时需要HttpServletRequest和HttpServletResponse两个参数(保留该参数主要为了方面对请求的处理)

1.构建抽象类

public abstract class AbstractDispatchAction implements Action {

	@Override
	public String exeute(HttpServletRequest request, HttpServletResponse response) {
		
		String methodName = request.getParameter("methodName");
		Class<? extends AbstractDispatchAction> clazz = this.getClass();
		try {
			Method method = clazz.getDeclaredMethod(
					methodName, 
					HttpServletRequest.class,
					HttpServletResponse.class);
			return (String)method.invoke(this, request,response);
		} catch (Exception e) {
			throw new RuntimeException("在调用Action中的["+methodName+"]方法是异常", e);
		} 
	}

}

2.自定义的Action子控制器示例:

public class StudentAction extends AbstractDispatchAction implements ModelDrive {
	
	private Student student = new Student();
	
	@Override
	public Object getModel() {
		return student;
	}

	/*@Override
	public String exeute(HttpServletRequest request, HttpServletResponse response) {		
		System.out.println("StudentAction = " + student);	
		return "students";
	}*/
	
	public String getStudents(HttpServletRequest request, HttpServletResponse response) {	
		System.out.println("getStudents");
		System.out.println("StudentAction = " + student);
		return "students";
	}
	
	public String addStudent(HttpServletRequest request, HttpServletResponse response) {
		System.out.println("addStudent");
		System.out.println("add student = " + student);
		return "students";
	}

}

发送请求,示例
http://localhost:8080/mvc/studentAction.actionmethodName=addStudent&sid=100&age=23&addr=aabbcc

注意:在请求中需要添加一个methodName的固定参数,该参数指定了需要调用的Action中的方法的名称。

四.其他公用组件的集成

可以将通用分页,字符编码过滤器等组件一集成到mvc框架中便于复用。

五.打jar包

将自定义mvc框架打成jar包,以便于在其他项目中使用。

项目 --(右击)-->Export

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/44834.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

QT DAY1

1.思维导体 2.作业 #include "widget.h"Widget::Widget(QWidget *parent): QWidget(parent) {qDebug()<<this->size();qDebug()<<this->rect().size();qDebug()<<this->geometry().size();qDebug()<<this->frameGeometry().siz…

安防视频管理平台GB设备接入EasyCVR, 如何获取RTMP与RTSP视频流

安防视频监控平台EasyCVR可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安防视频监控的能力&#xff0c;比…

六边形架构和分层架构的区别?

六边形架构和分层架构是什么&#xff1f; 六边形架构&#xff08;Hexagonal Architecture&#xff09;和分层架构&#xff08;Layered Architecture&#xff09;是两种常见的软件架构模式。六边形架构强调将核心业务逻辑与外部依赖解耦&#xff0c;通过接口与外部世界进行通信。…

桥接模式-处理多维度变化

程序员小名去摆摊卖奶茶了&#xff0c;口味有香、甜。 型号有大、中、小。假如小名先在家里把这些奶茶装好&#xff0c;那么最少要装2x3 6杯奶茶&#xff0c;如果此时新增一个口味&#xff1a;酸&#xff0c;那么就需要多装3杯奶茶了。而且这样做&#xff0c;等客户买走一种&a…

【【51单片机直流电机调速】】

学会电机调速&#xff0c;掌握中国速度 PWM的生成方法 先用户设定一个比较值&#xff0c;然后计数器定时自增。 当计数器<比较值&#xff0c;输出0 当计数器>比较值&#xff0c;输出1 main.c #include <REGX52.H> #include"delay.h" #include"…

【弹力设计篇】聊聊熔断设计

为什么需要熔断 熔断这个词一听从生活中就是保险丝超过一定的温度后自动断开&#xff0c;以此来保护家用电器&#xff0c;属于电路中自我保护装置。如果没有熔断&#xff0c;那么家用电器一定会损坏的。 进一步再来分析一下&#xff0c;在分布式系统中&#xff0c;各个系统之间…

酷雷曼无人机技能培训考试圆满举办

2023年7月18日、19日&#xff0c;以“向云端起航&#xff0c;让技术落地”为主题的酷雷曼无人机技能提升培训会在酷雷曼北京运营中心隆重举行&#xff0c;来自全国各地的众多合作商参加了本次培训&#xff0c;通过系统、全面的学习成功取得了专业无人机飞行员执照&#xff0c;为…

django跨域设置

1.安装 (venv) ***\data_analyse_web>pip install django-cors-headers 2.添加应用 :在settings.py中添加应用,放到任意位置都行 INSTALLED_APPS {# ...corsheaders,# ... } 3. 设置中间层&#xff0c;在settings.py中添加中间层&#xff0c;放到最前面 MIDDLEWARE [c…

mac m1 触控栏TouchBar功能栏异常

电脑可能在高温下运行时间过长&#xff0c;导致TouchBar之前正常显示的调整屏幕亮度与调整声音等功能的按钮均丢失&#xff0c;然后看了一眼键盘设置&#xff0c;设置也是正常的&#xff0c;已勾选显示功能栏 下面请看 如何在MacBook Pro&#xff08;macOS Monterey&#xff0…

PHP百度小程序rtc-room组件token获取经历

【前言】 目前就职盘古网络集团&#xff0c;一名PHPer程序员。我们的主营业务是百度产品相关&#xff0c;所以最近有了一个百度小程序项目&#xff0c;涉及其音视频组件做直播。 开发文档 百度智能小程序文档 鉴权token 百度智能小程序文档 嗯&#xff0c;很好的功能。结果测…

【Redis学习】01Redis基础

Redis&#xff08;B站黑马&#xff09;学习笔记 01Redis基础 文章目录 Redis&#xff08;B站黑马&#xff09;学习笔记前言01Redis基础初始Redis认识NoSQL认识Redis安装RedisLinux版安装官网压缩包下载使用yum下载&#xff08;个人不推荐&#xff0c;找不到安装目录&#xff0…

golang+layui提升界面美化度--[推荐]

一、背景 golanglayui提升界面美化度--[推荐]&#xff1b; golang后端写的页面很难看&#xff0c;如何好看点呢&#xff0c;那就是layui https://layui.dev/ 也是一个简单上手容易使用的框架&#xff0c;类似jquery&#xff0c;对于后端开发来说满足使用需求 二、使用注意点…

Python 逻辑回归:理论与实践

文章目录 1. 介绍1.1 什么是逻辑回归&#xff1f;1.2 逻辑回归的应用领域 2. 逻辑回归的原理2.1 Sigmoid 函数2.2 决策边界2.3 损失函数 3. 逻辑回归的实现3.1 数据准备3.2 创建逻辑回归模型3.3 模型训练3.4 模型预测3.5 模型评估 4. 可视化决策边界4.1 绘制散点图4.2 绘制决策…

TortoiseGit安装

1、TortoiseGit简介 TortoiseGit是基于TortoiseSVN的Git版本的Windows Shell界面。它是开源的&#xff0c;可以完全免费使用。 TortoiseGit 支持你执行常规任务&#xff0c;例如commit、显示日志、区分两个版本、创建分支和标签、创建补丁等。 2、TortoiseGit下载 (1)Tortois…

RocketMQ第一课-快速实战以及集群架构搭建

一、RocketMQ产品特点 1、RocketMQ介绍 ​ RocketMQ是阿里巴巴开源的一个消息中间件&#xff0c;在阿里内部历经了双十一等很多高并发场景的考验&#xff0c;能够处理亿万级别的消息。2016年开源后捐赠给Apache&#xff0c;现在是Apache的一个顶级项目。 ​ 早期阿里使用Act…

【如何训练一个中译英翻译器】LSTM机器翻译seq2seq字符编码(一)

系列文章 【如何训练一个中译英翻译器】LSTM机器翻译seq2seq字符编码&#xff08;一&#xff09; 【如何训练一个中译英翻译器】LSTM机器翻译模型训练与保存&#xff08;二&#xff09; 【如何训练一个中译英翻译器】LSTM机器翻译模型部署&#xff08;三&#xff09; 训练一个…

手机变局2023:一场瞄准产品和技术的“思维革命”

以折叠屏冲高端&#xff0c;已成为中国手机厂商们的共识。 在这个苹果未涉足的领域&#xff0c;国产手机厂商们加快脚步迭代推新&#xff0c;积极抢占机遇。但平心而论&#xff0c;虽然国产折叠屏机型众多&#xff0c;但市场上始终缺乏一款突破性的产品作为标杆&#xff0c;为…

Spring Boot 中的日志

一、日志有什么用&#xff1f; 日志是程序的重要组成部分&#xff0c;想象一下&#xff0c;如果程序报错了&#xff0c;不让你打开控制台看日志&#xff0c;那么你能找到报错的原因吗&#xff1f; 答案是否定的&#xff0c;写程序不是买彩票&#xff0c;不能完全靠猜&#xf…

【前端知识】React 基础巩固(三十一)——store数据的订阅和Redux的优化

React 基础巩固(三十一)——store数据的订阅和Redux的优化 一、store数据的订阅 store/index.js const { createStore } require("redux");// 初始化数据 const initialState {name: "test",title: "hello redux", };function reducer(state …

Mybatis的基本操作--增删改查

目录 查看数据 无参数 一个参数 多个参数 添加数据 修改数据 删除数据 查看数据 分三种情况&#xff1a;无参&#xff0c;有一个参数&#xff0c;有多个参数的情况。 &#xff08;这里的详细操作步骤是博主的上一篇博客写的&#xff1a;初识Mybatis&#xff0c;并创建第…