自定义MVC 框架

目录

1. 什么是MVC

2. 自定义MVC工作原理图

3. 自定义mvc实现

3.1 创建web工程

3.2 中央处理器

3.3 Action接口定义

3.4 实现子控制器

3.5 完善中央控制器

3.5.1 请求分发功能

3.5.2 使用配置文件配置action

3.5.3 请求参数处理

3.6 完善Action

最后完整的中央控制器  与 mvc架包 下载

打包号的mvc架包 下载 内涵分页插件  需要配置tid文件

4. 其他公用组件的集成

5. 打jar包


1. 什么是MVC

MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写。 它是一种软件设计典范。

用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。

提高了程序的可维护性、可移植性、可扩展性与可重用性,降低了程序的开发难度。它主要分模型、视图、控制器三层。

  • 模型(model): 它是应用程序的主体部分,主要包括业务逻辑模块(web项目中的dao类)和数据模块(pojo类)。pojo一般可以叫做实体域模型,dao和service称为过程域模型。
  • 视图(view): 用户与之交互的界面、在web中视图一般由jsp,html组成,其他的还包括android,ios等等。
  • 控制器(controller): 接收来自界面的请求 并交给模型进行处理 在这个过程中控制器不做任何处理只是起到了一个连接的做用。

不足的地方:

  • 增加系统结构和实现的复杂性。对于简单的界面,严格遵守MVC,需要使模型、视图与控制器分离,增加系统复杂性
  • 视图和控制器之间的关系太过紧密了

2. 自定义MVC工作原理图

 核心组件说明:

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

3. 自定义mvc实现

3.1 创建web工程

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

所使用的所有架包 与 配置文件路径 

配置文件 请看 上几篇文章   重点看3.建模 即可 

(2条消息) Xml的(DTD,xml解析,xml建模)_嘴强程序员的博客-CSDN博客

所需的架包下载

xml解析架包密码:hlx8

除了xml解析 要有反射的一些工具依赖 还有标签库

上述重要点

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>

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

@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();

}

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

@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);
	......
}

3) 处理请求参数中的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";
	}

}

最后完整的中央控制器  与 mvc架包 下载

package com.baidu.resourt;

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

import javax.servlet.ServletConfig;
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.tomcat.util.descriptor.web.ContextService;

@WebServlet("*.action")
public class ActionDispatchServlet extends HttpServlet {

	ConfigModel confing = null;
	/**
	 * 
	 */
	 @Override
	public void init(ServletConfig config) throws ServletException {
		 String initParameter = config.getInitParameter("mvcXml");
		 if(config.getInitParameter("mvcXml")==null) {
			 this.confing=ConfigModelFactory.getConfing();
		 }else {
			 if(confing==null) {
				 try {
					confing=ConfigModelFactorys.bulid(initParameter);
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			 }
			 }
		 

		//必须调用 否则报错  
		super.init(config);
	}
	
	private static final long serialVersionUID = 1L;

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doPost(req, resp);
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		
		
		String servletPath = req.getServletPath();
		String path = servletPath.split("\\.")[0];

		Action action = this.actionPath(path);
		if (action instanceof ModelDrive) {
			ModelDrive moder = (ModelDrive) action;
			//获取用户 给的 实体对象
			Object model = moder.getModel();
			try {
				//反射帮助类中提供的的方法  参数对应 直接赋值   map格式 <String , String[]>
				BeanUtils.populate(model, req.getParameterMap());
			} catch (IllegalAccessException | InvocationTargetException e) {
				new ActionDispatchSetParamterException("在中央处理器中处理参数时发生了异常 参数=" + model.toString(), e);
			}
		}

		String name = action.execute(req, resp);
		// 处理ajax 请求
		if (name == null || "".equals(name)) {
			return;
		}
		
		String pathServlet = this.getServletContext().getContextPath();

		ForwardModel forw = confing.getWord(path).getForw(name);
		if (forw.isRedirect()) {
			resp.sendRedirect(pathServlet+forw.getPath());
		} else {
			req.getRequestDispatcher(forw.getPath()).forward(req, resp);
		}

	}

	/**
	 * 使用反射实例化 配置文件中的对象
	 * 
	 * @param path
	 *            访问地址
	 * @return Action 接口
	 */
	public Action actionPath(String path) {
		if(confing==null) {
			confing=ConfigModelFactory.getConfing();
		}
		ActionModel word = confing.getWord(path);
		String type = word.getType();
		// 通过反射实力话配置对象
		Action action = null;
		try {
			action = (Action) getClass().forName(type).newInstance();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return action;
	}

}

config配置文件 可以配置中央控制器时配置参数 进行更改  默认是 扫描config.xml文件  如果在web.xml中配置请求路径 可更改为别的扫描别的配置文件  

实例

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
  
  <description>配置编码格式过滤器   顺序优先</description>
    <filter>
  	<filter-name>encodingFilter</filter-name>
  	<filter-class>com.baidu.utils.EncodingFiter</filter-class>
  </filter>
  <filter-mapping>
  	<filter-name>encodingFilter</filter-name>
  	<url-pattern>/*</url-pattern>
  </filter-mapping>
  
  
  <display-name>mavendemo</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
<--           下面是配置中央控制器                                       -->
  <servlet>
    <servlet-name>ActionDispatchServlet</servlet-name>
    <servlet-class>com.baidu.resourt.ActionDispatchServlet</servlet-class>
<--           配置参数                                     -->
    <init-param>
             <param-name>mvcXml</param-name>
             <param-value>/config.xml</param-value>
     </init-param>
</servlet>
<servlet-mapping>
 <servlet-name>ActionDispatchServlet</servlet-name>
  <url-pattern>*.action</url-pattern>
</servlet-mapping>


</web-app>

打包号的mvc架包 下载 内涵分页插件  需要配置tid文件

<!DOCTYPE taglib
		PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
		"http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<!-- 标签库描述符 -->
<taglib xmlns="http://java.sun.com/JSP/TagLibraryDescriptor">
	<!-- 代表标签库的版本号 -->
	<tlib-version>1.0</tlib-version>
	<!-- 代表jsp的版本 -->
	<jsp-version>1.2</jsp-version>
	<!-- 你的标签库的简称 -->
	<short-name>test</short-name>
	<!-- 你标签库的引用uri -->
	<uri>com.lkf.zking.lork</uri>

	<tag>
		<name>page</name>
		<tag-class>com.baidu.tags.PageTag</tag-class>
		<body-content>empty</body-content>
		<attribute>
			<name>pb</name>
			<required>true</required>
			<rtexprvalue>true</rtexprvalue>
		</attribute>
	</tag>
	<tag>
		<name>forEach</name>
		<tag-class>com.baidu.tags.ForEachTag</tag-class>
		<body-content>jsp</body-content>
		<attribute>
			<name>items</name>
			<required>true</required>
			<rtexprvalue>true</rtexprvalue>
		</attribute>
		<attribute>
			<name>val</name>
			<required>true</required>
			<rtexprvalue>false</rtexprvalue>
		</attribute>
	</tag>
	
</taglib>

分页插件的使用  

(2条消息) 自定义分页加标签封装_嘴强程序员的博客-CSDN博客

mvc框架架包下载

文件  mvc.jar

密码:c20c

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

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

4. 其他公用组件的集成

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

5. 打jar包

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

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

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值