J2EE基础:自定义mvc01

本文详细介绍了MVC模式的发展过程,从最初的每个操作对应一个Servlet,到反射版的增删改查,再到自定义MVC框架的实现。通过逐步优化,解决了类数量过多和代码重复的问题,实现了中央控制器DispatcherServlet和Action接口,降低了代码耦合度,提高了代码复用性。
摘要由CSDN通过智能技术生成

目录

一.mvc简介

1.什么是mvc?

二.最初的增删改查

上述存在问题:

        1.关于单个实体/表操作场景越多,需要新建的类也就越多,造成了项目中类的数量过于庞大

解决问题1: 

三.反射版的增删改查

上述问题:

2.当新增了业务,除了要添加该业务对应的方法(load方法),同时还要改动原有的代码

解决问题2:

四.对比问题的所在

上述问题:

3.反射相关代码,在每一个实体类对应的servlet中都存在

4.每一个servlet中都有doget、dopost方法

 五.自定义MVC工作原理图

这个原理图:解决了的问题就是上面的第3点第4点问题

六.自定义mvc后台代码实现(按照原理图实现) 

我们走一下流程:


一.mvc简介

1.什么是mvc?

m:model        模型层

v:view            视图层

c:contriller      控制层

分工明确,各司其职

jsp--->jsp

jsp--->servlet

jsp--->servlet--->dao

jsp--->servlet--->biz--->dao

为什么这么演变:降低代码的耦合度

完成一个书籍的增删改查,以前我们就是按照这个步骤写的

entity--->book        实体类

dao---->bookDao   数据访问层

biz--->bookBiz        业务逻辑层

web--->                   servlet层

        AddbookServlet        增加

        DelbookServlet        删除

        EditbookServlet        修改

        ListbookServlet        查询           

二.最初的增删改查

这里就没有写dao方法和biz方法了

 增加的servlet

package com.jiangwenjuan.web;

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;

@WebServlet("/book/add")
public class AddBookServlet extends HttpServlet{

	@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 {
        //这里就是你写增加书籍的方法
		System.out.println("处理书籍的 增加 业务,调用BookBiz");
	}
	
}

删除的servlet

package com.jiangwenjuan.web;

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;

@WebServlet("/book/del")
public class DelBookServlet extends HttpServlet{

	@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 {
		System.out.println("处理书籍的 删除 业务,调用BookBiz");
	}
	
}

修改的servlet

package com.jiangwenjuan.web;

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;

@WebServlet("/book/edit")
public class EditBookServlet extends HttpServlet{

	@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 {
		System.out.println("处理书籍的 编辑 业务,调用BookBiz");
	}
	
}

 查询的servlet

package com.jiangwenjuan.web;

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;

@WebServlet("/book/list")
public class ListBookServlet extends HttpServlet{

	@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 {
		System.out.println("处理书籍的 查询 业务,调用BookBiz");
	}
	
}

 测试一下

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

<h3>目前增删改查的方法</h3>
<a href="${pageContext.request.contextPath }/book/add">增加</a>
<a href="${pageContext.request.contextPath }/book/del">删除</a>
<a href="${pageContext.request.contextPath }/book/edit">修改</a>
<a href="${pageContext.request.contextPath }/book/list">查询</a>

</body>
</html>

最终的展示:

上述存在问题:

        简析:我只不过做一个表的一个功能,就是说一个表有增删改查四个功能以及查询单个功能,可能我们还为了通过它的id修改一个值,那么就又要创建一个方法。

        1.关于单个实体/表操作场景越多,需要新建的类也就越多,造成了项目中类的数量过于庞大

解决问题1: 

我们直接写一个BookServlet

package com.jiangwenjuan.web;

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;

@WebServlet("/book.action")
public class BookServlet extends HttpServlet{

	@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 methodName= req.getParameter("methodName");//假设这个方法名就叫parameter
		if("add".equals(methodName)) {
			//如果前台传递到后台的是一个新增的请求,那么后台就调用新增方法
			add(req,resp);
		}
		else if("del".equals(methodName)) {
			//如果前台传递到后台的是一个删除的请求,那么后台就调用删除方法
			del(req,resp);
		}
		else if("edit".equals(methodName)) {
			//如果前台传递到后台的是一个修改的请求,那么后台就调用修改方法
			deit(req,resp);
		}
		else if("list".equals(methodName)) {
			//如果前台传递到后台的是一个查询的请求,那么后台就调用查询方法
			list(req,resp);
		}
	}

	private void list(HttpServletRequest req, HttpServletResponse resp) {
		System.out.println("在同一个servlet中调用 list 方法");
	}

	private void deit(HttpServletRequest req, HttpServletResponse resp) {
		System.out.println("在同一个servlet中调用 deit 方法");
	}

	private void del(HttpServletRequest req, HttpServletResponse resp) {
		System.out.println("在同一个servlet中调用 del 方法");
	}

	private void add(HttpServletRequest req, HttpServletResponse resp) {
		System.out.println("在同一个servlet中调用 add 方法");
	}
	
}

 测试一下:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h3>目前增删改查的方法</h3>
<a href="${pageContext.request.contextPath }/book/add">增加</a>
<a href="${pageContext.request.contextPath }/book/del">删除</a>
<a href="${pageContext.request.contextPath }/book/edit">修改</a>
<a href="${pageContext.request.contextPath }/book/list">查询</a>

<!-- 
	上述问题
	1.	关于单个实体/表操作场景越多,需要新建的类也就越多,造成了项目中类的数量过于庞大
 -->
 
 <h3>类数量过多问题的优化</h3>
 	<a href="${pageContext.request.contextPath }/book.action?methodName=add">增加</a>
	<a href="${pageContext.request.contextPath }/book.action?methodName=del">删除</a>
	<a href="${pageContext.request.contextPath }/book.action?methodName=edit">修改</a>
	<a href="${pageContext.request.contextPath }/book.action?methodName=list">查询</a>
 

</body>
</html>

最终运行为:

那么这种做法就解决了第一个多个类的问题,我去操作一张表增删改查我只需要写一个实体类,它能够极大的减少这个类的构建,当然也减少了代码量,但是这个方式也存在了问题,进一步在解决

三.反射版的增删改查

上述问题:

解析:我们能不能在BookServlet中,加一个查询单个的方法,然而上面也要加,能不能上面的不加,我直接在下面去加可不可以,不管加多少方法,上面的原本不变,该怎么实现这个方法,反射,那么我们就动态调用方法就可以了,最终反正就我们传什么调什么.

2.当新增了业务,除了要添加该业务对应的方法(load方法),同时还要改动原有的代码

解决问题2:

bookServlet改进代码块:

package com.jiangwenjuan.web;

import java.io.IOException;
import java.lang.reflect.Method;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/book.action")
public class BookServlet extends HttpServlet{

	@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 methodName = req.getParameter("methodName");//假设这个方法名就叫parameter
		//		methodName可能是add/del/edit/list/load/xxx/yyy/aaa...
//		前台传递什么方法,就调用当前类的对应方法	//方法对象
		try {		   //BookServlet.class
			Method m = this.getClass().getDeclaredMethod(methodName, HttpServletRequest.class,HttpServletResponse.class);
			//打开访问权限
			m.setAccessible(true);
			//调用当前类实例的methodName 方法,你传什么调什么
			m.invoke(this, req,resp);
		} catch (Exception e) {
			e.printStackTrace();
		}	
		
		
//		if("add".equals(methodName)) {
//			//如果前台传递到后台的是一个新增的请求,那么后台就调用新增方法
//			add(req,resp);
//		}
//		else if("del".equals(methodName)) {
//			del(req,resp);
//		}
//		else if("edit".equals(methodName)) {
//			deit(req,resp);
//		}
//		else if("list".equals(methodName)) {
//			list(req,resp);
//		}
//		else if("load".equals(methodName)) {
//			load(req,resp);
//		}
	}

	private void load(HttpServletRequest req, HttpServletResponse resp) {
		System.out.println("在同一个servlet中调用 查询单个 方法");
	}
	
	private void list(HttpServletRequest req, HttpServletResponse resp) {
		System.out.println("在同一个servlet中调用 list 方法");
	}
	
	private void deit(HttpServletRequest req, HttpServletResponse resp) {
		System.out.println("在同一个servlet中调用 deit 方法");
	}

	private void del(HttpServletRequest req, HttpServletResponse resp) {
		System.out.println("在同一个servlet中调用 del 方法");
	}

	private void add(HttpServletRequest req, HttpServletResponse resp) {
		System.out.println("在同一个servlet中调用 add 方法");
	}
	
}

最终在测试运行的结果为: 

也就是我新增了一个业务方法的话,我只需要在下面加方法就可以了,上面的代码就不用变了,在S阶段就进一步第2个优化,第一:解决了类的数量过多的问题,第二:解决了新增业务需要改变它原有的代码的问题。

四.对比问题的所在

我们就在创建一个orderServlet其实就是copy一份BookServlet就是把book改变了order

package com.jiangwenjuan.web;

import java.io.IOException;
import java.lang.reflect.Method;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/order.action")
public class OrderServlet extends HttpServlet{

	@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 methodName = req.getParameter("methodName");//假设这个方法名就叫parameter
		//		methodName可能是add/del/edit/list/load/xxx/yyy/aaa...
//		前台传递什么方法,就调用当前类的对应方法	//方法对象
		try {		   //BookServlet.class
			Method m = this.getClass().getDeclaredMethod(methodName, HttpServletRequest.class,HttpServletResponse.class);
			//打开访问权限
			m.setAccessible(true);
			//调用当前类实例的methodName 方法,你传什么调什么
			m.invoke(this, req,resp);
		} catch (Exception e) {
			e.printStackTrace();
		}
		

	private void load(HttpServletRequest req, HttpServletResponse resp) {
		System.out.println("在同一个orderservlet中调用 查询单个 方法");
	}
	
	private void list(HttpServletRequest req, HttpServletResponse resp) {
		System.out.println("在同一个orderservlet中调用 list 方法");
	}
	
	private void deit(HttpServletRequest req, HttpServletResponse resp) {
		System.out.println("在同一个orderservlet中调用 deit 方法");
	}

	private void del(HttpServletRequest req, HttpServletResponse resp) {
		System.out.println("在同一个orderservlet中调用 del 方法");
	}

	private void add(HttpServletRequest req, HttpServletResponse resp) {
		System.out.println("在同一个orderservlet中调用 add 方法");
	}
	
}

在测试中运行的结果:

上述问题:

解析:我们对应一下BookServlet和orderservlet的区别:下面这段代码是一样的除了下面的方法不一样,那么copy过来干嘛,是不是多此一举,这里就是我们要优化第三个点,我能不能不copy把下面这段代码删掉,你也可以调用下面的方法,甚至每一个servlet中都含有doget,和dopost,我能不能把这个也删掉全部都删掉,他也给我调doget、dopost增删改查方法

3.反射相关代码,在每一个实体类对应的servlet中都存在

4.每一个servlet中都有doget、dopost方法

@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		//为了区分当前请求的目的,增删改查的目的,就从前台将要调用的方法名传递到后台
		String methodName = req.getParameter("methodName");//假设这个方法名就叫parameter
		//		methodName可能是add/del/edit/list/load/xxx/yyy/aaa...
//		前台传递什么方法,就调用当前类的对应方法	//方法对象
		try {		   //BookServlet.class
			Method m = this.getClass().getDeclaredMethod(methodName, HttpServletRequest.class,HttpServletResponse.class);
			//打开访问权限
			m.setAccessible(true);
			//调用当前类实例的methodName 方法,你传什么调什么
			m.invoke(this, req,resp);
		} catch (Exception e) {
			e.printStackTrace();
		}
}

 五.自定义MVC工作原理图

这个原理图:解决了的问题就是上面的第3点第4点问题

这里面的所有的类都是要自己写的。

浏览器发了一个请求过来,因为我们最终解决这个反射的相关代码只出现一次,不能出现多次,那么想都能想到,这个代码放在一个公共的地方,它不会放在具体某一个a.servlet和b.servlet,

那么这个公共的地方我们就称为中央控制器ActionServlet,那么在这个里面它不处理任何业务逻辑,由谁处理,由Action,这个Action里面就包含了增删改查所有的方法,也就以后我们怎么写了,我们就不要继承HTTPServlet
我就写一个Action子控制器,就专门处理浏览器的请求,那么处理浏览器请求就对应写增删改查的方法了,我们的解决方案就在这两个,我们来实现一下它,

它的流程就是这样的不管你对书籍做增删改查还是对商品或者其他的增删改查首先接收的请求是ActionServlet那么它能接收到所有的请求上面写的就是(*.action)了

 

六.自定义mvc后台代码实现(按照原理图实现) 

 

这里我就不用ActionServlet了,因为我们学开源框架它的核心处理器就是叫DispatcherServlet

package com.jiangwenjuan.framework;

import java.io.IOException;
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 com.jiangwenjuan.web.BookAction;

/**
 * 中央控制器:
 * 	主要职能:接收浏览器请求,找到对应的处理人
 * @author 蒋文娟
 *
 * @date 2022年6月25日 上午11:36:12
 */
//  只要以action结尾的都会传递过来
@WebServlet("*.action")
public class DispatcherServlet extends HttpServlet{
	//这里就有一个变量     map集合我通过一个新的那肯定找到一个键值对的形式
	private Map<String, Action> actions = new HashMap<String, Action>();
	
	
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		doPost(req, resp);
	}
	
//	程序启动时,只会加载一次
	@Override
	public void init() throws ServletException {
//		这里肯定不能定s后面在优化	这里就用了建模	
		actions.put("/book", new BookAction());
		//那么你还要一个order就直接在下面写
//		actions.put("/order", new BookAction());
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		//那么在这里面会接收浏览器所以的请求
//		http:localhost:8080:/J_mvc/book.action?methodName=list
		//拿到路径
		String uri = req.getRequestURI();
		//那么现在我们要把/book截取出来
		//拿到/book,就是最后一个/到最好一个.的位置
		uri = uri.substring(uri.lastIndexOf("/"),uri.lastIndexOf("."));
		
		//接收传递过来的地址
		Action action = actions.get(uri);
		//执行了反射代码
		action.execute(req, resp);
		
	}
}

在这里要重写一个方法:


action代码块:

package com.jiangwenjuan.framework;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 子控制器:
 * 	对应请求的处理人
 * @author 蒋文娟
 *
 * @date 2022年6月25日 上午11:39:01
 */
public interface Action {
	//封装
	 void execute(HttpServletRequest req, HttpServletResponse resp);
	

}

 ActionSupport代码块:

package com.jiangwenjuan.framework;

import java.io.IOException;
import java.lang.reflect.Method;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 支持
 * @author 蒋文娟
 *
 * @date 2022年6月25日 上午11:48:57
 */
public class ActionSupport implements Action{

	//你写了我就给你调用
	@Override
	public void execute(HttpServletRequest req, HttpServletResponse resp) {
			//为了区分当前请求的目的,增删改查的目的,就从前台将要调用的方法名传递到后台
			String methodName = req.getParameter("methodName");//假设这个方法名就叫parameter
			//		methodName可能是add/del/edit/list/load/xxx/yyy/aaa...
//			前台传递什么方法,就调用当前类的对应方法	//方法对象
			try {		   //BookServlet.class
				Method m = this.getClass().getDeclaredMethod(methodName, HttpServletRequest.class,HttpServletResponse.class);
				//打开访问权限
				m.setAccessible(true);
				//调用当前类实例的methodName 方法,你传什么调什么
				m.invoke(this, req,resp);
			} catch (Exception e) {
				e.printStackTrace();
			}
	}
	

}

BookAction代码块:

package com.jiangwenjuan.web;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.jiangwenjuan.framework.Action;
import com.jiangwenjuan.framework.ActionSupport;

/**
 * 子类
 * @author 蒋文娟
 *
 * @date 2022年6月25日 上午11:45:04
 */
public class BookAction extends ActionSupport{

	//现在就可以直接在这里写方法了
	
	private void load(HttpServletRequest req, HttpServletResponse resp) {
		System.out.println("在同一个servlet中调用 查询单个 方法");
	}
	
	private void list(HttpServletRequest req, HttpServletResponse resp) {
		System.out.println("在同一个servlet中调用 list 方法");
	}
	
	private void deit(HttpServletRequest req, HttpServletResponse resp) {
		System.out.println("在同一个servlet中调用 deit 方法");
	}

	private void del(HttpServletRequest req, HttpServletResponse resp) {
		System.out.println("在同一个servlet中调用 del 方法");
	}

	private void add(HttpServletRequest req, HttpServletResponse resp) {
		System.out.println("在同一个servlet中调用 add 方法");
	}

}

 最终运行结果:

 

我们走一下流程:

先运行一下:

我们来看看这个路径怎么来的:

 接下来就是截取/book

最主要的就是这个action到底是本身还是bookaction



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值