搭建类似于SpringMVC的简单SimpleMVC框架

搭建类似于SpringMVC的简单SimpleMVC框架

SpringMVC核心组件

  • DispatcherServlet:前端控制器,用于接收所有请求。
  • HandlerMapping:用于配置请求路径与Controller组件的对应关系。
  • Controller:控制器,具体处理请求的组件。
  • ModelAndView:Controller组件处理完请求后得到的结果,由数据与视图名称组成。
  • ViewResolver:视图解析器,可根据视图名称确定需要使用的视图组件。

SimpleMVC实现的核心组件有
DispatcherServlet,HandlerMapping,Controller视图使用的是JSP

下面开始搭建项目

本次项目是依据下面的顺序来进行的

  • JSP、Controller、HandlerMapping、DispatcherServlet

下面开始

1.创建一个maven项目,注意这里要选择war包,因为需要用到wabapp/WEB-INF/web.xml这个配置文件。
2.在wabapp/WEB-INF下创建一个hello.jsp页面。
在这里插入图片描述
3.有了显示页面以后,就需要有Controller来处理客户端的请求,所以在src/main/java下创建包名为controller的HelloController.java类,类中有处理客户端hello.do请求的hello方法,当前方法上的注解报错,是因为当前注解还不存在,所以下面我们需要创建该注解。
在这里插入图片描述
4.在HelloController中需要用到@requestMapping注解,但是当前项目中还没有该注解,所以需要在src/main/java下创建一个包名为bean.annotation的注解RequestMapping。当该注解创建完成后,HelloController只需要导一下包就不会报错了。

package bean.annotation;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * 自定义映射注解
 * 映射客户端请求与对应的方法
 * 
 * 
 * @Retention 注解中的注解,成为元注解
 * 有一个value属性:是RetentionPolicy类型的,它是一个枚举
 * RetentionPolicy:有3个值分别是CLASS、RUNTIME、SOURCE
 * 
 * 按声明周期划分:
 * RetentionPolicy.SOURCE:注解只保留在源文件,当java文件编译成class文件,注解被遗弃
 * 
 * RetentionPolicy.CLASS:注解保留到class文件,jvm加载class文件时,注解被遗弃,
 * 这是默认的生命周期
 * 
 * RetentionPolicy.RUNTIME:注解不仅保存在class文件,jvm加载class文件之后,仍然存在
 * 这3个生命周期分别对应于:Java源文件(.java文件) ---> .class文件 ---> 内存中的字节码。
 */
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {
	
	/**
	 * 当前value对应注解括号中的值
	 */
	public String value();
}


5.当控制器和映射注解都有了以后,我们接下来就需要在src/main/java下创建一个包名为bean.web的DispatcherServlet的前端控制器,这是一个servlet而不是一个class,内容如下:

package bean.web;

import java.io.IOException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * 前端控制器
 */
public class DispatcherServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
	
	/**
	 * 初始化方法
	 */
	public void init(ServletConfig config) throws ServletException {
	
	}
	
	/**
	 * 接收客户端请求方法
	 */
	protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	
	}

}

当前Servlet的配置在web.xml下可以看到,配置如下,注意这里的<url-pattern>*.do</url-pattern>是跟上面HelloController方法上@RequestMapping(hello.do)注解括号中的请求结尾对应。

<?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_2_5.xsd" version="2.5">
  <display-name>simpleMVC</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>
    <description></description>
    <display-name>DispatcherServlet</display-name>
    <servlet-name>DispatcherServlet</servlet-name>
    <servlet-class>bean.web.DispatcherServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>DispatcherServlet</servlet-name>
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>
</web-app>

可以将配置精简为

<?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_2_5.xsd" version="2.5">
  <servlet>
    <servlet-name>DispatcherServlet</servlet-name>
    <servlet-class>bean.web.DispatcherServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>DispatcherServlet</servlet-name>
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>
</web-app>

配置DispatcherServlet,因为当前Servlet的init()需要在tomcat容器启动时,就被加载,所以需要在web.xml配置文件中添加<load-on-startup>1</load-on-startup>

<?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_2_5.xsd" version="2.5">
  <servlet>
    <servlet-name>DispatcherServlet</servlet-name>
    <servlet-class>bean.web.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>DispatcherServlet</servlet-name>
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>
</web-app>

为所有的Controller配置一个映射文件simplemvc.xml,当前只有一个HelloController.java需要配置,在src/mian/resources下新建一个xml文件,如:simplemvc.xml,配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans>
	<!-- class属性配置的值为HelloController类所在的类全名 -->
	<bean class="controller.HelloController" />
	<!-- 后续如果还有控制器,依据HelloController的配置,继续配置就行 -->
</beans>

接下来是需要导入dom4j的依赖,在项目的pom.xml下配置,为解析配置有Controller的simplemvc.xml文件做准备。

<dependencies>
  	<dependency>
  		<groupId>dom4j</groupId>
  		<artifactId>dom4j</artifactId>
  		<version>1.6.1</version>
  	</dependency>
</dependencies>

如上的依赖和xml配置文件都准备好了以后,接下来只需在DispatcherServlet中添加List objHandlerMapping handlerMapping:(这个对象需要创建,当前项目中还没有)属性和init()方法中进行解析和配置映射关系即可,如下:

init():方法的配置

package bean.web;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

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

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import bean.common.HandlerMapping;

/**
 * 前端控制器
 */
public class DispatcherServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	// 存储所有实例化的控制器类
	private List obj = new ArrayList<>();
	
	// 创建处理映射关系的类HandlerMapping
	private HandlerMapping handlerMapping = new HandlerMapping();
	
	/**
	 * 初始化方法
	 */
	public void init(ServletConfig config) throws ServletException {
		// 获取解析容器
		SAXReader sax = new SAXReader();
		// 获取解析文件所在
		InputStream in = getClass().getClassLoader().getResourceAsStream("simplemvc.xml");
		try {
			// 将文件解析出来,需要处理DocumentException异常
			Document doc = sax.read(in);
			// 获取doc的根节点
			Element root = doc.getRootElement();
			// 获取根节点下所有的子节点
			List<Element> list = root.elements();
			// 遍历所有子节点,获取所有class属性所对应的controller
			for (Element e : list) {
				// 获取到类名
				String className = e.attributeValue("class");
				// 将该类实例化,需要处理异常
				Object o = Class.forName(className).newInstance();
				// 将实例化的控制器类,添加到obj集合中
				obj.add(o);
			}
			// 调用HandlerMapping中的process方法
			handlerMapping.process(obj);
		} catch (Exception e) {
			e.printStackTrace();
			// 打桩,看配置是否没有问题
			System.out.println("初始化失败");
		}
	}

	/**
	 * 接收客户端请求方法
	 */
	protected void service(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

	}

}

HandlerMapping类的创建src/main/java/bean/common下,在该类需要使用Handler类,所以还需要创建Handler类:
HandlerMapping

package bean.common;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import bean.annotation.RequestMapping;

/**
 * 处理控制器方法与客户端请求的映射关系
 */
public class HandlerMapping {

	// 存储请求路径与控制器实例和方法的Map
	private Map<String, Handler> handlerMap = new HashMap<>();
	// 存放控制器与控制器方法的Handler
	private Handler handler = new Handler();
	
	public void process(List obj) {
		// 遍历所有实例对象
		for (Object o : obj) {
			// 查看当前实例的类上是否有注解
			RequestMapping orm = o.getClass().getAnnotation(RequestMapping.class);
			// 类上注解的映射路径
			String path = "";
			// 当前类有注解
			if(orm != null) {
				path = orm.value();
			}
			// 当前类没有注解
			// 获取实例中所有方法
			Method[] cla = o.getClass().getDeclaredMethods();
			// 遍历所有的方法
			for (Method m : cla) {
				// 获取方法上的注解
				RequestMapping mrm = m.getAnnotation(RequestMapping.class);
				// 方法上有注解的情况
				if(mrm != null) {
					// 获取注解中的值
					path += mrm.value();
					// 当前控制器实例
					handler.setO(o);
					// 当前控制器有注解的方法
					handler.setM(m);
					handlerMap.put(path, handler);
				}
			}
		}
		// 查看所有映射与对应的handler
		System.out.println("HandlerMap=" + handlerMap);
	}

	// 根据path获取Handler
	public Handler getHandler(String path) {
		return handlerMap.get(path);
	}
}

Handler

package bean.common;

import java.lang.reflect.Method;

/**
 * 存储控制器与对应方法的封装类
 */
public class Handler {
	// 封装的控制器实例
	private Object o;
	// 封装的控制器对应方法
	private Method m;
	
	public Object getO() {
		return o;
	}
	public void setO(Object o) {
		this.o = o;
	}
	public Method getM() {
		return m;
	}
	public void setM(Method m) {
		this.m = m;
	}
	
}

将simpleMVC项目添加到tomcat容器中,并启动容器,如果一切都正常则会在控制台,显示如下
HandlerMap={hello.do=bean.common.Handler@3cd3e762},这行语句就是我们在HandlerMapping的process()方法中的输出语句。

在这里插入图片描述
6.当如上配置一切都正常,接下来,就可以在service()方法中获取客户端的请求,然后根据请求路径到HandlerMapping的getHandler()方法中获取对应的控制器与方法,利用反射机制使其运行。

/**
	 * 接收客户端请求方法
	 */
	protected void service(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		// 获取客户端请求,一般是localhost:8080/simpleMVC/hello.do这样的一个整体
		String requestPath = request.getRequestURI();
		System.out.println("requestPath=" + requestPath);
		// 需要获取应用名后面的路径hello.do,所以需要现获得应用名simpleMVC/
		String app = request.getContextPath();
		// 截取hello.do
		String path = requestPath.substring(app.length());
		// 根据path获取Handler
		System.out.println("path=" + path);
		Handler handler = handlerMapping.getHandler(path);
		// 判断当前handler是否为空,如果路径所对应的Handler不存在的话,为null
		if(handler == null) {
			// 返回404
			response.sendError(404);
			return;
		}
		
		// handler不为空,获取handler中的controller和method
		Object o = handler.getO();
		Method m = handler.getM();
		try {
			// 处理invoke异常,根据反射机制,调用该方法
			String viewName = m.invoke(o).toString();
			System.out.println("viewName=" + viewName);
			request.getRequestDispatcher("/WEB-INF/" + viewName + ".jsp").forward(request, response);
		} catch (Exception e) {
			e.printStackTrace();
		} 
		
	}

7.当上述方法写完以后,就可以启动tomcat,然后在浏览器上输入localhost:8080/simpleMVC/hello.do进行测试即可,如果一切正常将会在浏览器上看到
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值