知道DispatcherServlet为你们做了多少事吗?

DispatcherServlet

DispatcherServlet是前置控制器,配置在web.xml文件中的。拦截匹配的请求,Servlet拦截匹配规则要自己定义,把拦截下来的请求,依据相应的规则分发到目标Controller来处理,是配置spring MVC的第一步。
DispatcherServlet是前端控制器设计模式的实现,提供Spring Web MVC的集中访问点,而且负责职责的分派,而且与Spring IoC容器无缝集成,从而可以获得Spring的所有好处。

简单地说就是承上启下,核心中的核心。

在这里插入图片描述

手写一个DispatcherServlet

理清职责:
在这里插入图片描述
简化版的SpringMVC

DispatcherServlet:

package core.web;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

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 core.common.Handler;
import core.common.HandlerMapping;


public class DispatcherSerlvet extends HttpServlet {
	private static final long serialVersionUID = 1L;
	
	private HandlerMapping handlerMapping;
       
    @Override
    /**
     *  init方法只会执行一次,而且一定是在service方法执行之前先执行
     *  一般可以在init方法当中做一些初始化操作(也就是为service方法的执行
     *  做一些准备工作)。
     *  在这儿,init方法主要做三件事:
     *  1、读取配置文件中的处理器类名。
     *  2、将处理器类实例化。
     *  3、将处理器实例交给HandlerMapping来处理。
     */
	public void init() throws ServletException {
    	SAXReader saxReader = new SAXReader();
    	/*
    	 * 通过读取初始化参数来获得配置文件名
    	 * getInitParameter方法来自于GenericServlet
    	 * 是HttpServlet的父类
    	 */
    	String configLocation = getInitParameter("configLocation");
    	
    	// 先拿到方法区中的类对象,再拿到容器(比如Tomcat)提供的类加载器,再通过它的getResources方法去获取资源
    	// 放在容器中以后,所有类的加载都会由容器提供的类加载器完成
    	// 放到容器中后,需要这样读取文件
    	InputStream in = getClass().getClassLoader().getResourceAsStream(configLocation);
    	try {
			Document doc = saxReader.read(in);
			Element root = doc.getRootElement();
			List<Element> elements = root.elements();
			// beans集合用于存放处理器实例
			List<Object>beans = new ArrayList<Object>();
			for (Element element : elements) {
				// 获得处理器的类名
				String className = element.attributeValue("class");
				System.out.println("className:"+className);
				// 将处理器实例化
				Object bean = Class.forName(className).newInstance();
				beans.add(bean);
				
			}
			System.out.println("beans:"+beans);
			
			handlerMapping = new HandlerMapping();
			// 将处理器实例交给映射处理器来处理
			handlerMapping.process(beans);
			
		} catch (Exception e) {
			e.printStackTrace();
			throw new ServletException(e);
		}
	}



	@Override
	protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		String path = req.getRequestURI().substring(req.getContextPath().length());
		Handler handler = handlerMapping.getHandler(path);
		if (handler==null) {
			// 没有对应处理器时返回错误码404
			System.out.println("请求路径为:"+path+"没有提供对应的处理器");
			resp.sendError(404);
			return;
		}
		
		// 获取参数并激活方法
		Method method = handler.getMethod();
		Object obj = handler.getObj();
		try {
			Class[]types = method.getParameterTypes();
			Object rv = null;
			if (types.length>0) {
				Object[]params = new Object[types.length];
				for (int i = 0; i < types.length; i++) {
					if (types[i]==HttpServletRequest.class) {
						params[i]=req;
					}
					if (types[i]==HttpServletResponse.class) {
						params[i]=resp;
					}
				}
				rv = method.invoke(obj, params);
			}else {
				rv = method.invoke(obj);
			}
			
		
			
			String viewName = rv.toString();
			System.out.println("viewName:"+viewName);
			//重定向
			if (viewName.startsWith("redirect:")) {
				String redirectPath = req.getContextPath()+"/"+viewName.substring("redirect:".length());
				resp.sendRedirect(redirectPath);
			}else {
				
				// 转发
				// 所有的绝对路径,必须以/开头。转发的路径是从应用名后开始的。
				req.getRequestDispatcher("/WEB-INF/"+viewName+".jsp").forward(req,resp);
				
			}
			
			
			
		} catch (Exception e) {
			e.printStackTrace();
			// 将异常扔给容器来处理
			throw new ServletException(e);
		}
	}


}

RequestMapping注解:

package core.annotation;

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

/**
 * 用于设置请求路径的注解
*@author  Edward
*@date  2020年6月28日---下午2:53:52
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface RequestMapping {

	public String value();
}

HandlerMapping:

package core.common;

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

import core.annotation.RequestMapping;

/**
 * 	映射处理器,
 * 	负责提供请求路径与处理器及方法的对应关系
 * 	比如:请求路径为“/hello.do”应该由HelloController的hello方法来处理。
 * 
*@author  Edward
*@date  2020年6月29日---上午9:28:12
*/
public class HandlerMapping {
	/*
	 *  map存放请求路径与处理器及方法的对应关系
	 *  Handler封装有处理器实例及Method对象
	 */
	private Map<String, Handler>map = new HashMap<String, Handler>();
	
	
	
	/**
	 * 依据请求路径返回对应的Handler对象
	 * @param path
	 * @return
	 */
	public Handler getHandler(String path) {
		return map.get(path);
	}
	
	/**
	 * 负责建立请求路径与处理器及方法的对应关系
	 * @param beans
	 */
	public void process(List<Object> beans) {
		System.out.println("HandlerMapping.process()");
		for (Object obj : beans) {
			// 获得加在类前的注解
			RequestMapping rm1 = obj.getClass().getAnnotation(RequestMapping.class);
			String path1 = "";
			if (rm1!=null) {
				path1 = rm1.value();
			}
			
			
			
			Method[]methods = obj.getClass().getDeclaredMethods();
			for (Method method : methods) {
				// 获得加在方法前的注解
				RequestMapping rm = method.getAnnotation(RequestMapping.class);
				// 允许方法前不写注解
				if (rm!=null) {
					// 获得请求路径
					String path = rm.value();
					System.out.println("path:"+path);
					// 将处理器实例及Method对象封装到Handler对象里面
					Handler handler = new Handler();
					handler.setObj(obj);
					handler.setMethod(method);
					// 将对应关系放入map
					map.put(path1+path, handler);
					
				}	
				
			}
		}
		System.out.println("map:"+map);
	}
	
}

Handler:

package core.common;
/**
 * 
 * 该类是为方便利用java反射机制去调用处理器的方法而设计的。
*@author  Edward
*@date  2020年6月29日---上午10:18:57
*/

import java.lang.reflect.Method;

public class Handler {
	// obj是处理器实例
	private Object obj;
	private Method method;
	public Object getObj() {
		return obj;
	}
	public void setObj(Object obj) {
		this.obj = obj;
	}
	public Method getMethod() {
		return method;
	}
	public void setMethod(Method method) {
		this.method = method;
	}
	
	

}

核心就是这四个类,其他自便。
web.xml文件供参考,注意:
1、设置配置文件名
2、设置load on startup
3、设置请求拦截路径

<?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>DispatcherSerlvet</servlet-name>
    <servlet-class>core.web.DispatcherSerlvet</servlet-class>
    <!-- 
    	指定配置文件名
     -->
    <init-param>
    	<param-name>configLocation</param-name>
    	<param-value>smartmvc.xml</param-value>
    </init-param>
    <!-- 
    	配置启动即加载,即容器启动之后,会立即将该Servlet实例化,紧接着初始化 
    	在这里,“1”表示优先级,值是一个大于等于零的整数,越小,优先级越高,
    	也就是说,先被创建。
    -->
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>DispatcherSerlvet</servlet-name>
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>
</web-app>

另外还有很多Springmvc的功能可以实现,比如

		// TODO IOC也是可以做到的,需要开发两个注解,利用反射
		// TODO ModelMap也可以实现,可以在dispatcherSerlvet里面将获得的参数放进request里, addA相当于setA
		// TODO @RequestParam也可以实现,只需要获取方法params前面的注解里的值,再赋给参数
		// TODO 对thymeLeaf的支持...
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章