手写 | springMVC框架

手写 | springMVC框架

SpringMVC的运行流程


⑴ 用户发送请求至前端控制器DispatcherServlet

⑵ DispatcherServlet收到请求调用HandlerMapping处理器映射器。

⑶ 处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。

⑷ DispatcherServlet通过HandlerAdapter处理器适配器调用处理器

⑸ 执行处理器(Controller,也叫后端控制器)。

⑹ Controller执行完成返回ModelAndView

⑺ HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet

⑻ DispatcherServlet将ModelAndView传给ViewReslover视图解析器

⑼ ViewReslover解析后返回具体View

⑽ DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)。

⑾ DispatcherServlet响应用户。

手写SpringMVC思路

1.web.xml加载
 为了读取web.xml中的配置,我们用到ServletConfig这个类,它代表当前Servlet在web.xml中的配置信息。通过web.xml中加载我们自己写的MyDispatcherServlet和读取配置文件。
2、初始化阶段
  在前面我们提到DispatcherServlet的initStrategies方法会初始化9大组件,但是这里将实现一些SpringMVC的最基本的组件而不是全部,按顺序包括:

  • 加载配置文件
  • 扫描用户配置包下面所有的类
  • 拿到扫描到的类,通过反射机制,实例化。并且放到ioc容器中(Map的键值对 beanName-bean) beanName默认是首字母小写
  • 初始化HandlerMapping,这里其实就是把url和method对应起来放在一个k-v的Map中,在运行阶段取出

3、运行阶段
  每一次请求将会调用doGet或doPost方法,所以统一运行阶段都放在doDispatch方法里处理,它会根据url请求去HandlerMapping中匹配到对应的Method,然后利用反射机制调用Controller中的url对应的方法,并得到结果返回。按顺序包括以下功能:

  • 异常的拦截
  • 获取请求传入的参数并处理参数
  • 通过初始化好的handlerMapping中拿出url对应的方法名,反射调用

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!-- Spring MVC 核心控制器 DispatcherServlet 配置 -->
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>com.sym.servlet.ExtDispatcherServlet
        </servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <!-- 拦截所有/* 的请求,交给DispatcherServlet处理,性能最好 -->
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

定义注解@Controller @RequestMapping

package com.sym.Annotation;

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

/**
 * @author 孙一鸣 on 2020/3/1
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExtController {
}

package com.sym.Annotation;

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

/**
 * @author 孙一鸣 on 2020/3/1
 */
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ExtRequestMapping {
        String value() default "";
}

编写处理器


public class ExtDispatcherServlet extends HttpServlet {
	// mvc bean key=beanid ,value=对象
	private ConcurrentHashMap<String, Object> mvcBeans = new ConcurrentHashMap<String, Object>();
	// mvc 请求方法 key=requestUrl,value=对象
	private ConcurrentHashMap<String, Object> mvcBeanUrl = new ConcurrentHashMap<String, Object>();
	// mvc 请求方法 key=requestUrl,value=方法
	private ConcurrentHashMap<String, String> mvcMethodUrl = new ConcurrentHashMap<String, String>();

	/**
	 * 初始化自定义SpringMVC容器
	 */
	public void init() throws ServletException {
		try {
			// 1.获取当前包下所有的类
			List<Class<?>> classes = ClassUtil.getClasses("com.itmayiedu.ext.controller");
			// 2.初始化当前包下所有的类,使用Java反射机制初始化对象存放在SpringMVC容器中key(beanId)-value(
			// 当前实例对象)
			findClassMVCBeans(classes);
			// 3.初始化HandlerMapping方法,将url和方法对应上
			handlerMapping(mvcBeans);

		} catch (Exception e) {

		}
	}

	// 2.初始化当前包下所有的类,使用Java反射机制初始化对象存放在SpringMVC容器中key(beanId)-value(
	// 当前实例对象)
	public void findClassMVCBeans(List<Class<?>> classes)
			throws ClassNotFoundException, InstantiationException, IllegalAccessException {
		mvcBeans = new ConcurrentHashMap<String, Object>();
		for (Class<?> classInfo : classes) {
			ExtController extController = classInfo.getDeclaredAnnotation(ExtController.class);
			if (extController != null) {
				// 默认类名小写 作为bean的名称
				String beanId = ClassUtil.toLowerCaseFirstOne(classInfo.getSimpleName());
				mvcBeans.put(beanId, ClassUtil.newInstance(classInfo));
			}
		}

	}

	// 3.初始化HandlerMapping方法,将url和方法对应上
	public void handlerMapping(ConcurrentHashMap<String, Object> mvcBeans) {
		// 遍历mvc bean对象
		for (Map.Entry<String, Object> entry : mvcBeans.entrySet()) {
			// springmvc 注入object对象
			Object mvcObject = entry.getValue();
			// 判断类上是否有@ExtRequestMapping注解
			Class<? extends Object> classInfo = mvcObject.getClass();
			String requestBaseUrl = null;
			ExtRequestMapping classExtRequestMapping = classInfo.getAnnotation(ExtRequestMapping.class);
			if (classExtRequestMapping != null) {
				requestBaseUrl = classExtRequestMapping.value();
			}
			// 遍历当前类的所有方法,判断方法上是否有注解
			Method[] declaredMethods = classInfo.getDeclaredMethods();
			for (Method method : declaredMethods) {
				ExtRequestMapping methodExtRequestMapping = method.getDeclaredAnnotation(ExtRequestMapping.class);
				if (methodExtRequestMapping != null) {
					String httpRequestUrl = methodExtRequestMapping.value();
					mvcBeanUrl.put(requestBaseUrl + httpRequestUrl, mvcObject);
					mvcMethodUrl.put(requestBaseUrl + httpRequestUrl, method.getName());
				}
			}
		}
	}

	@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 {
		try {
			doDispatch(req, resp);
		} catch (Exception e) {
			// TODO: handle exception
		}
	}

	public void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
		// 1.获取请求url地址
		String requestUrl = req.getRequestURI();
		// 2.使用请求url查找对应mvc 控制器bean
		Object object = mvcBeanUrl.get(requestUrl);
		if (object == null) {
			resp.getWriter().println("http ext not found  controller 404");
			return;
		}
		// 3.获取对应的请求方法
		String methodName = mvcMethodUrl.get(requestUrl);
		if (StringUtils.isEmpty(methodName)) {
			resp.getWriter().println("http ext not found Method 404");
			return;
		}
		// 4.使用java反射技术执行方法
		Class<? extends Object> classInfo = object.getClass();
		String resultPage = (String) methodInvoke(classInfo, object, methodName);
		// 5.视图展示
		viewdisplay(resultPage, req, resp);
	}

	// 执行方法
	public Object methodInvoke(Class<? extends Object> classInfo, Object object, String methodName) {
		try {
			Method method = classInfo.getMethod(methodName);
			Object result = method.invoke(object);
			return result;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	// 视图展示
	public void viewdisplay(String pageName, HttpServletRequest req, HttpServletResponse res)
			throws ServletException, IOException {
		// 获取后缀信息
		String suffix = ".jsp";
		// 页面目录地址
		String prefix = "/";
		req.getRequestDispatcher(prefix + pageName + suffix).forward(req, res);
	}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值