自己动手写SpringMVC框架

版权声明:欢迎转载,记得前排留名哦☞ https://blog.csdn.net/qq_31142553/article/details/86582066

上一篇文章中详细讲解了Spring MVC的运行流程源码(点击传送),现在我们自己动手来模仿写一个,主要是为了加深理解。

一、效果预览

1、配置前端控制器,指定配置文件路径

2、配置Controller扫描包和静态资源

3、使用自定义注解声明控制器和访问路径、请求方式

4、成功时

5、 请求路径不存在或者参数不正确时

扫描二维码关注公众号,回复: 5067733 查看本文章

二、整体流程和项目结构

1、请求处理流程

首先不管是什么方式的请求,都会交由前端控制器的doDispatch方法处理。先是根据请求路径找到对应的处理器,然后根据处理器获取对应的适配器,最后使用适配器去调用真正的目标(即我们写的控制器或者静态资源)。

2、项目结构

项目使用个人惯用的Maven多模块结构:一个核心模块,一个测试模块

先看父工程的pom.xml,只摘取了比较重要的两部分。第一部分是两个模块都要依赖的javax.servlet-api;第二部分是引入了编译插件,需要关注的地方是-parameters,这是为了编译时保留方法参数的名称,Java8之后提供的功能(值得注意的是,需要自己手动开启,参考文章https://blog.csdn.net/qq_31142553/article/details/85637154)。

                <dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>servlet-api</artifactId>
			<version>2.5</version>
			<scope>provided</scope>
		</dependency>
        <modules>
		<module>zmvc-core</module>
		<module>zmvc-test</module>
	</modules>
	<build>
		<finalName>zmvc</finalName>
		<plugins>
			<!-- compiler plugin -->
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<source>1.8</source>
					<target>1.8</target>
					<compilerArgs>
						<arg>-parameters</arg>
					</compilerArgs>
				</configuration>
			</plugin>
		</plugins>
	</build>

然后是zmvc-core模块的pom.xml配置,增加了spring-core的依赖,这是为了使用它的路径匹配工具AntPathMatcher。

                <dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>4.3.18.RELEASE</version>
			<exclusions>
				<exclusion>
					<groupId>org.apache.logging.log4j</groupId>
					<artifactId>log4j-api</artifactId>
				</exclusion>
			</exclusions>
		</dependency>

最后是zmvc-core模块的pom.xml配置,测试时再说。

3、核心模块目录结构

三、前端控制器的初始化

我们需要在ZDispatcherServlet的init方法里面加载配置文件、加载处理器映射器、加载处理器适配器。

        /**
	 * 处理器适配器
	 */
	private List<HandlerAdapter> handlerAdapters = new ArrayList<>();

	public void init(ServletConfig config) throws ServletException {
		// 1、加载配置文件
		loadConfig(config.getInitParameter("contextConfigLocation"));
		
		// 2、初始化处理器映射器
		initHandlerMappings();
		
		// 3、初始化处理器适配器
		initHandlerAdapters();
		
	}

1、加载配置文件:将配置文件的属性设置到ZDispatcherServlet的成员变量Properties里面

        /**
	 * 加载配置
	 * @param confLocation
	 * @throws ServletException
	 */
	private void loadConfig(String confLocation) throws ServletException {
		// 把web.xml中的contextConfigLocation对应value值的文件加载到流里面
		if (confLocation.startsWith("classpath:")) {
			confLocation = confLocation.replace("classpath:", "");
		} else if (confLocation.contains("/")) {
			int lastSplitIndex = confLocation.lastIndexOf('/');
			confLocation = confLocation.substring(lastSplitIndex + 1, confLocation.length());
		}
		InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(confLocation);
		try {
			//用Properties文件加载文件里的内容
			properties.load(resourceAsStream);
		} catch (IOException e) {
			throw new ServletException("加载配置文件异常!", e);
		} finally {
			//关流
			if(null != resourceAsStream){
				try {
					resourceAsStream.close();
				} catch (IOException e) {
					throw new ServletException("加载配置文件异常!", e);
				}
			}
		}
	}

2、加载处理器映射器

        /**
	 * 处理器映射器
	 */
	private List<HandlerMapping> handlerMappings = new ArrayList<>();
        /**
	 * 初始化处理器映射器
	 */
	private void initHandlerMappings() {
		// 1、初始化URL处理器映射器
		HandlerMapping urlHandlerMapping = UrlHandlerMapping.init(properties.getProperty("staticResources"));
		handlerMappings.add(urlHandlerMapping);
		
		// 2、初始化方法处理器映射器
		String packageName = Objects.requireNonNull(properties.getProperty("scanPackage"), "scanPackage属性不能为空!");
		
		HandlerMapping methodHandlerMapping = MethodHandlerMapping.init(packageName);
		handlerMappings.add(methodHandlerMapping);
	}

(1)定义一个父类HandlerMapping,提供一个抽象方法getHandler。

/**
 * 处理器映射器
 * @author z_hh
 * @time 2019年1月25日
 */
public abstract class HandlerMapping {

	public abstract Handler getHandler(HttpServletRequest request);
}

(2)定义一个子类MethodHandlerMapping,负责查找控制类方法的处理器。初始化时,扫描指定包下的控制器方法,将它们加到一个自己的Map集合中,并返回一个单例对象。

/**
 * 方法处理器映射器
 * 
 * @author z_hh
 * @date 2019年1月21日
 */
public class MethodHandlerMapping extends HandlerMapping {

	private static final MethodHandlerMapping HANDLER_MAPPING = new MethodHandlerMapping();

	/**
	 * 处理器集合
	 */
	private HashMap<String, MethodHandler> handlers = new HashMap<>();

	private MethodHandlerMapping() {
	}

	/**
	 * 初始化处理器映射器并返回一个实例
	 * 
	 * @param packageName
	 *            处理器扫描包名
	 * @return
	 */
	public static MethodHandlerMapping init(String packageName) {
		HANDLER_MAPPING.doScanHandler(packageName);
		return HANDLER_MAPPING;
	}

	/**
	 * 查找处理器
	 * 
	 * @param request
	 * @return
	 */
	@Override
	public Handler getHandler(HttpServletRequest request) {
		// 1、获取请求路径
		String url = request.getRequestURI(), contextPath = request.getContextPath();
		String path = url.replace(contextPath, "").replaceAll("/+", "/");// 多个斜杠转为单杠

		// 2、根据路径找处理器
		MethodHandler handler = handlers.get(path);
		if (Objects.isNull(handler)) {
			return null;
		}

		// 3、获取请求方式
		String method = request.getMethod();

		// 4、校验请求方式
		List<RequestMethod> methods = handler.getMethods();
		if (method != null && !methods.isEmpty()) {
			boolean anyMatch = methods.parallelStream().anyMatch(m -> method.toUpperCase().equals(m.toString()));
			if (!anyMatch) {
				return null;
			}
		}

		// 5、返回处理器
		return handler;
	}

	/**
	 * 扫描指定包下面的所有类,对符合的方法创建处理器对象
	 * 
	 * @param packageName
	 *            处理器扫描包名
	 */
	private void doScanHandler(String packageName) {
		// 1、递归获取指定包下所有类的全类名
		List<String> classNames = new ArrayList<>();
		doScanClass(packageName, classNames);

		// 2、遍历所有类,对符合的类方法创建处理器对象
		for (String className : classNames) {
			try {
				Class<?> clazz = Class.forName(className);
				// 2.1类上面没有ZController注解,忽略
				if (!clazz.isAnnotationPresent(ZController.class)) {
					continue;
				}
				// 2.2类上面的ZRequestMapping注解的value作为根路径
				String baseUrl = "";
				if (clazz.isAnnotationPresent(ZRequestMapping.class)) {
					baseUrl = clazz.getAnnotation(ZRequestMapping.class).value();
				}
				// 3.3获取所有公开方法,每个带ZRequestMapping注解的都可以作为一个处理器
				Method[] methods = clazz.getMethods();
				for (Method method : methods) {
					// 3.4没有ZRequestMapping注解,忽略
					if (!method.isAnnotationPresent(ZRequestMapping.class)) {
						continue;
					}
					ZRequestMapping zRequestMapping = method.getAnnotation(ZRequestMapping.class);
					// 3.5创建处理器
					MethodHandler handler = new MethodHandler();
					handler.setInstance(clazz.newInstance());
					handler.setMethodName(method.getName());
					setHandlerParameters(handler, method.getParameters());
					handler.setRtType(method.getReturnType());
					handler.setUrl((baseUrl + zRequestMapping.value()).replaceAll("/+", "/"));
					handler.setMethods(Arrays.asList(zRequestMapping.method()));
					// 3.6加入到处理器集合映射中
					handlers.put(handler.getUrl(), handler);
				}
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	/**
	 * 递归扫描指定包下面所有类,并将全类名放进集合
	 * 
	 * @param packageName
	 * @param classNames
	 */
	private void doScanClass(String packageName, List<String> classNames) {
		// 把所有的.替换成/
		URL url = this.getClass().getClassLoader().getResource(packageName.replaceAll("\\.", "/"));
		File dir = new File(url.getFile());
		for (File file : dir.listFiles()) {
			if (file.isDirectory()) {
				// 递归读取包
				doScanClass(packageName + "." + file.getName(), classNames);
			} else {
				if (!file.getName().endsWith(".class")) {
					continue;
				}
				String className = packageName + "." + file.getName().replace(".class", "");
				classNames.add(className);
			}
		}
	}

	/**
	 * 设置处理器方法参数
	 * 
	 * @param handler
	 * @param parameters
	 */
	private void setHandlerParameters(MethodHandler handler, Parameter[] parameters) {
		LinkedHashMap<String, Class<?>> handlerParameters = new LinkedHashMap<>();
		for (Parameter parameter : parameters) {
			String name = parameter.getName();
			Class<?> type = parameter.getType();
			// 存在ZRequestParam注解,设置别名
			if (parameter.isAnnotationPresent(ZRequestParam.class)) {
				String alias = parameter.getAnnotation(ZRequestParam.class).value();
				name = alias;
			}
			handlerParameters.put(name, type);
		}
		handler.setParameters(handlerParameters);
	}

}

对应处理器类MethodHandler

/**
 * 方法处理器
 * @author z_hh
 * @time 2019年1月20日
 */
public class MethodHandler extends Handler {
	
	/** 实例 */
	private Object instance;
	
	/** 方法名 */
	private String methodName;
	
	/** 参数列表(有序):K-类型 V-名称 */
	private LinkedHashMap<String, Class<?>> parameters;
	
	/** 返回值类型 */
	private Class<?> rtType;
	
	/** 可以匹配的url */
	private String url;
	
	/** 可以匹配的请求方式 */
	private List<RequestMethod> methods;

    // 省略getter/setter方法
}

(3)定义一个子类UrlHandlerMapping,负责查找静态资源的处理器。初始化时,将配置的静态资源Pattern加到自己的List集合中,并返回一个单例对象。

/**
 * URL处理器映射器
 * @author z_hh  
 * @date 2019年1月21日
 */
public class UrlHandlerMapping extends HandlerMapping {
	
	private static final UrlHandlerMapping HANDLER_MAPPING = new UrlHandlerMapping();
	
	/**
	 * 处理器集合
	 */
	private List<UrlHandler> handlers = new ArrayList<>();
	
	/**
	 * 路径匹配器
	 */
	private AntPathMatcher antPathMatcher = new AntPathMatcher();
	
	private UrlHandlerMapping() {}
	
	/**
	 * 初始化处理器映射器并返回一个实例
	 * @param packageName 处理器扫描包名
	 * @return
	 */
	public static UrlHandlerMapping init(String resourcesStr) {
		if (resourcesStr != null) {
			// 将配置参数按英文逗号分割,去掉两边空白字符,然后放进集合(Java8Lambda表达式)
			String[] resources = resourcesStr.split(",");
			Arrays.stream(resources)
				.map(String::trim)
				.map(resource -> {
					UrlHandler handler = new UrlHandler();
					handler.setUrlPattern(resource);
					return handler;
				})
				.forEach(HANDLER_MAPPING.handlers::add);
		}
		return HANDLER_MAPPING;
	}
	
	/**
	 * 查找处理器
	 * @param request
	 * @return
	 */
	@Override
	public Handler getHandler(HttpServletRequest request) {
		// 1、获取请求路径
		String url =request.getRequestURI(),
				contextPath = request.getContextPath();
		String path = url.replace(contextPath, "").replaceAll("/+", "/");// 多个斜杠转为单杠
		
		// 2、匹配处理器
		for (UrlHandler urlHandler : handlers) {
			String urlPattern = urlHandler.getUrlPattern();
			boolean match = antPathMatcher.match(urlPattern, path);
			if (match) {
				urlHandler.setPath(url);
				return urlHandler;
			}
		}
		
		return null;
	}
	
}

3、加载处理器适配器

分别创建控制器方法处理器的适配器和静态资源处理器的适配器。

        /**
	 * 初始化处理器适配器
	 */
	private void initHandlerAdapters() {
		// 1、初始化URL处理器适配器
		HandlerAdapter urlHandlerAdapter = UrlHandlerAdapter.getInstance();
		handlerAdapters.add(urlHandlerAdapter);
		
		// 2、初始化方法处理器适配器
		HandlerAdapter methodHandlerAdapter = MethodHandlerAdapter.getInstance();
		handlerAdapters.add(methodHandlerAdapter);
	}
/**
 * 方法处理器适配器,用于反射(方法句柄)调用处理器方法
 * @author z_hh  
 * @date 2019年1月21日
 */
public class MethodHandlerAdapter extends HandlerAdapter {

	private static final HandlerAdapter HANDLER_ADAPTER = new MethodHandlerAdapter();
	
	private MethodHandlerAdapter() {}
	
	/**
	 * 获取单例对象
	 * @return
	 */
	public static HandlerAdapter getInstance() {
		return HANDLER_ADAPTER;
	}
	
	/**
	 * 调用处理器
	 * @param handler 处理器
	 * @param request 方法参数
	 * @return 方法执行结果
	 * @throws Throwable
	 */
	@Override
	public Object invoke(Handler handler, HttpServletRequest request, HttpServletResponse response) throws Throwable {
		MethodHandler methodHandler = (MethodHandler) handler;
		// 参数校验
		check(methodHandler, request);
		
		// 实例对象
		Object instance = methodHandler.getInstance();
		// 方法名
		String methodName = methodHandler.getMethodName();
		// 参数列表
		LinkedHashMap<String, Class<?>> methodParamMap = methodHandler.getParameters();
		// 参数值
		Object[] args = getMethodArgs(methodParamMap, request, response);
		Class<?>[] array = new Class[methodParamMap.size()];
		
		/* 方法句柄的方式,效率更高 */
		/*MethodType methodType = MethodType.methodType(handler.getRtType(), parameters.values().toArray(array));
		MethodHandle methodHandle = MethodHandles.lookup().findVirtual(instance.getClass(), methodName, methodType);
		return methodHandle.invoke(instance, args);*/
		
		// 获取方法
		Method method = instance.getClass().getMethod(methodName, methodParamMap.values().toArray(array));
		
		// 调用方法
		return method.invoke(instance, args);
	}
	
	
	
	private void check(MethodHandler methodHandler, HttpServletRequest request) throws ZmvcException {
		// 1、获取请求参数
		Map<String, String[]> parameterMap = request.getParameterMap();// 指定名称的参数可能值有多个
		
		// 2、校验请求参数,不符合返回400
		LinkedHashMap<String, Class<?>> parameters = methodHandler.getParameters();
		for (Object nameValue : parameterMap.entrySet()) {
			Entry entry = (Entry) nameValue;
			// 2.1指定名称的参数不存在
			Class<?> targetClazz = parameters.get(entry.getKey());
			if (Objects.isNull(targetClazz)) {
				throw new ZmvcException(HttpServletResponse.SC_BAD_REQUEST, "请求参数不正确!");
			}
			// 2.2类型不匹配(这个问题,不会处理)
			/*if (!targetClazz.isAssignableFrom(entry.getKey().getClass())) {
				throw new ZmvcException(HttpServletResponse.SC_BAD_REQUEST, "参数类型不匹配!");
			}*/
		}
		
	}

	private Object[] getMethodArgs(LinkedHashMap<String, Class<?>> methodParamMap, HttpServletRequest request, HttpServletResponse response) {
		// 1、获取请求参数集合
		Map<String, String[]> parameterMap = request.getParameterMap();// 指定名称的参数可能值有多个
		// 2、创建方法参数值数组
		Object[] args = new Object[methodParamMap.size()];
		// 3、按顺序设值,methodParamMap的key顺序就是参数顺序
		int index = 0;
		for (Entry<String, Class<?>> paramNT : methodParamMap.entrySet()) {
			Class<?> paramType = paramNT.getValue();
			// 3.1如果是ServletRequest类型的参数,赋值request
			if (ServletRequest.class.isAssignableFrom(paramType)) {
				args[index] = request;
			// 3.2如果是ServletResponse类型的参数,赋值response
			} else if (ServletResponse.class.isAssignableFrom(paramType)) {
				args[index] = response;
			// 3.3其它,根据名称设值
			} else {
				String paramName = paramNT.getKey();
				// 这个会得到一个数组,这里处理得不是很好,大家有什么建议
				String[] values = parameterMap.get(paramName);
				if (values != null) {
					args[index] = values.length > 1 ? values : values[0];
				}
			}
			index++;
		}
		return args;
	}

	@Override
	public boolean supports(Handler handler) {
		return handler instanceof MethodHandler;
	}
}
/**
 * URL处理器适配器
 * @author z_hh
 * @time 2019年1月25日
 */
public class UrlHandlerAdapter extends HandlerAdapter {

	private static final HandlerAdapter HANDLER_ADAPTER = new UrlHandlerAdapter();
	
	private UrlHandlerAdapter() {}
	
	/**
	 * 获取单例对象
	 * @return
	 */
	public static HandlerAdapter getInstance() {
		return HANDLER_ADAPTER;
	}
	
	@Override
	public Object invoke(Handler handler, HttpServletRequest request, HttpServletResponse response)
			throws Throwable {
		UrlHandler urlHandler = (UrlHandler) handler;
		response.sendRedirect(urlHandler.getPath());
		return null;
	}

	@Override
	public boolean supports(Handler handler) {
		return handler instanceof UrlHandler;
	}

}

四、处理请求

重写doGet、doPost、doPut方法等,将它们统一引入到doService方法,然后交由doDispatch处理并处理抛出的异常。

        /**
	 * 方法入口
	 * @param request
	 * @param response
	 * @throws IOException
	 */
	protected void doService(HttpServletRequest request, HttpServletResponse response) throws IOException {
		try {
			doDispatch(request, response);
		} catch (ZmvcException e) {
			response.sendError(e.getStatus(), e.getMessage());
		} catch (Throwable t) {
			t.printStackTrace();
			response.getWriter().write("500! Server Exception");
		}
	}
	
	protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Throwable {
		// 1、获取处理器
		Handler handler = getHandler(request);
		
		// 2、获取处理器适配器
		HandlerAdapter handlerAdapter = getAdapter(handler);
		
		// 3、处理器适配器调用处理器
		Object result = handlerAdapter.invoke(handler, request, response);
		
		// 4、回写结果
		response.getWriter().println(result);
	}

1、获取处理器

遍历处理器映射器集合,调用它们的getHandler方法查找处理器,没有时抛出404异常。

        /**
	 * 获取匹配的处理器
	 * @param request
	 * @return 处理器
	 * @throws ZmvcException
	 */
	private Handler getHandler(HttpServletRequest request) throws ZmvcException {
		for (HandlerMapping handlerMapping : handlerMappings) {
			Handler handler = handlerMapping.getHandler(request);
			if (handler != null) {
				return handler;
			}
		}
		throw new ZmvcException(HttpServletResponse.SC_NOT_FOUND, "您访问的URL不存在!");
	}

2、获取处理器适配器

遍历处理器适配器集合,调用它们的supports方法判断是否支持调用指定的处理器。

    private HandlerAdapter getAdapter(Handler handler) throws ZmvcException {
		for (HandlerAdapter handlerAdapter : handlerAdapters) {
			if (handlerAdapter.supports(handler)) {
				return handlerAdapter;
			}
		}
		throw new ZmvcException(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "服务器不能处理该请求!");
	}

3、处理器适配器调用处理器

适配器调用invoke方法。如果是控制器方法类型的处理器,使用反射或者方法句柄的方式调用;如果是静态资源,使用请求转发或者重定向访问(循环请求的问题,还没处理)。

五、测试模块

测试模块引入了核心模块,并使用Tomcat插件(可选)。

        <dependencies>
		<dependency>
			<groupId>cn.zhh</groupId>
			<artifactId>zmvc-core</artifactId>
		</dependency>
	</dependencies>

	<build>
		<finalName>zmvc-test</finalName>
		<plugins>
			<!-- tomcat plugin -->
			<plugin>
				<groupId>org.apache.tomcat.maven</groupId>
				<artifactId>tomcat7-maven-plugin</artifactId>
				<version>2.2</version>
				<configuration>
					<port>8080</port>
				</configuration>
			</plugin>
		</plugins>
	</build>

在web.xml里面配置前端控制器,处理所有请求。

  <servlet>
  	<servlet-name>ZDispatcherServlet</servlet-name>
  	<servlet-class>cn.zhh.servlet.ZDispatcherServlet</servlet-class>
  	<init-param>
  		<param-name>contextConfigLocation</param-name>
  		<!-- config file path -->
  		<param-value>classpath:app.properties</param-value>
  	</init-param>
  </servlet>
  
  <servlet-mapping>
  	<servlet-name>ZDispatcherServlet</servlet-name>
  	<url-pattern>/</url-pattern>
  </servlet-mapping>

配置文件写上扫描的包和静态资源。

scanPackage=cn.zhh.controller
staticResources=/*.html

编写测试控制器

/**
 * 测试控制器
 * @author z_hh  
 * @date 2019年1月21日
 */
@ZController
@ZRequestMapping("/test")
public class TestController {
	
	@ZRequestMapping
	public String test(String name, HttpServletResponse response) throws IOException {
		return "Hello " + name;
	}

	@ZRequestMapping(value="/test1")
	public String test1(String name, HttpServletResponse response) throws IOException {
		return "Hello " + name;
	}
	
	@ZRequestMapping(value="/test2", method=RequestMethod.GET)
	public String test2(String name, HttpServletResponse response) throws IOException {
		return "Hello " + name;
	}
	
	@ZRequestMapping(value="/test3", method=RequestMethod.POST)
	public String test3(String name, HttpServletResponse response) throws IOException {
		return "Hello " + name;
	}
	
	@ZRequestMapping(value="/test4")
	public String test4(@ZRequestParam("name") String xxx, HttpServletResponse response) throws IOException {
		return "Hello " + xxx;
	}
	
	@ZRequestMapping(value="/test5")
	public String test5(String name, Long i, HttpServletResponse response) throws IOException {
		return "Hello " + name + i;
	}
	
	@ZRequestMapping(value="/test6")
	public String test6(String name, String i, HttpServletResponse response) throws IOException {
		return "Hello " + name + i;
	}
}

六、完整代码

1、ZDispatcherServlet类

/**
 * ZMVC框架前端控制器
 * @author z_hh  
 * @date 2019年1月21日
 */
public class ZDispatcherServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
    
	/**
	 * 配置文件属性
	 */
	private Properties properties = new Properties();
	
	/**
	 * 处理器映射器
	 */
	private List<HandlerMapping> handlerMappings = new ArrayList<>();
	
	/**
	 * 处理器适配器
	 */
	private List<HandlerAdapter> handlerAdapters = new ArrayList<>();

	public void init(ServletConfig config) throws ServletException {
		// 1、加载配置文件
		loadConfig(config.getInitParameter("contextConfigLocation"));
		
		// 2、初始化处理器映射器
		initHandlerMappings();
		
		// 3、初始化处理器适配器
		initHandlerAdapters();
		
	}

	/**
	 * 方法入口
	 * @param request
	 * @param response
	 * @throws IOException
	 */
	protected void doService(HttpServletRequest request, HttpServletResponse response) throws IOException {
		try {
			doDispatch(request, response);
		} catch (ZmvcException e) {
			response.sendError(e.getStatus(), e.getMessage());
		} catch (Throwable t) {
			t.printStackTrace();
			response.getWriter().write("500! Server Exception");
		}
	}
	
	protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Throwable {
		// 1、获取处理器
		Handler handler = getHandler(request);
		
		// 2、获取处理器适配器
		HandlerAdapter handlerAdapter = getAdapter(handler);
		
		// 3、处理器适配器调用处理器
		Object result = handlerAdapter.invoke(handler, request, response);
		
		// 4、回写结果
		response.getWriter().println(result);
	}

	/**
	 * 加载配置
	 * @param confLocation
	 * @throws ServletException
	 */
	private void loadConfig(String confLocation) throws ServletException {
		// 把web.xml中的contextConfigLocation对应value值的文件加载到流里面
		if (confLocation.startsWith("classpath:")) {
			confLocation = confLocation.replace("classpath:", "");
		} else if (confLocation.contains("/")) {
			int lastSplitIndex = confLocation.lastIndexOf('/');
			confLocation = confLocation.substring(lastSplitIndex + 1, confLocation.length());
		}
		InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(confLocation);
		try {
			//用Properties文件加载文件里的内容
			properties.load(resourceAsStream);
		} catch (IOException e) {
			throw new ServletException("加载配置文件异常!", e);
		} finally {
			//关流
			if(null != resourceAsStream){
				try {
					resourceAsStream.close();
				} catch (IOException e) {
					throw new ServletException("加载配置文件异常!", e);
				}
			}
		}
	}

	/**
	 * 初始化处理器映射器
	 */
	private void initHandlerMappings() {
		// 1、初始化URL处理器映射器
		HandlerMapping urlHandlerMapping = UrlHandlerMapping.init(properties.getProperty("staticResources"));
		handlerMappings.add(urlHandlerMapping);
		
		// 2、初始化方法处理器映射器
		String packageName = Objects.requireNonNull(properties.getProperty("scanPackage"), "scanPackage属性不能为空!");
		
		HandlerMapping methodHandlerMapping = MethodHandlerMapping.init(packageName);
		handlerMappings.add(methodHandlerMapping);
	}

	/**
	 * 初始化处理器适配器
	 */
	private void initHandlerAdapters() {
		// 1、初始化URL处理器适配器
		HandlerAdapter urlHandlerAdapter = UrlHandlerAdapter.getInstance();
		handlerAdapters.add(urlHandlerAdapter);
		
		// 2、初始化方法处理器适配器
		HandlerAdapter methodHandlerAdapter = MethodHandlerAdapter.getInstance();
		handlerAdapters.add(methodHandlerAdapter);
	}

	/**
	 * 获取匹配的处理器
	 * @param request
	 * @return 处理器
	 * @throws ZmvcException
	 */
	private Handler getHandler(HttpServletRequest request) throws ZmvcException {
		for (HandlerMapping handlerMapping : handlerMappings) {
			Handler handler = handlerMapping.getHandler(request);
			if (handler != null) {
				return handler;
			}
		}
		throw new ZmvcException(HttpServletResponse.SC_NOT_FOUND, "您访问的URL不存在!");
	}
	
	private HandlerAdapter getAdapter(Handler handler) throws ZmvcException {
		for (HandlerAdapter handlerAdapter : handlerAdapters) {
			if (handlerAdapter.supports(handler)) {
				return handlerAdapter;
			}
		}
		throw new ZmvcException(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "服务器不能处理该请求!");
	}

	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doService(request, response);
	}

	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doService(request, response);
	}

	@Override
	protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doService(request, response);
	}

	@Override
	protected void doHead(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doService(request, response);
	}

	@Override
	protected void doOptions(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doService(request, response);
	}

	@Override
	protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doService(request, response);
	}

	@Override
	protected void doTrace(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doService(request, response);
	}

}

2、自定义注解

/**
 * 表明当前类是处理器类
 * @author z_hh
 * @time 2019年1月20日
 */
@Documented
@Retention(RUNTIME)
@Target(TYPE)
public @interface ZController {
}
/**
 * 表明当前方法是处理器方法
 * @author z_hh
 * @time 2019年1月20日
 */
@Documented
@Retention(RUNTIME)
@Target({ TYPE, METHOD })
public @interface ZRequestMapping {

	/**
	 * 请求路径(支持一个)
	 */
	String value() default "";
	
	/**
	 * 请求方式(支持多个,为空时不限制)
	 */
	RequestMethod[] method() default {};
	
	public enum RequestMethod {
		GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE
	}
}
/**
 * 方法参数别名
 * @author z_hh
 * @time 2019年1月20日
 */
@Documented
@Retention(RUNTIME)
@Target(PARAMETER)
public @interface ZRequestParam {

	/**
     * 参数的别名,必填
     */
    String value();
}

3、自定义异常类

/**
 * 自定义异常
 * @author z_hh
 * @time 2019年1月20日
 */
public class ZmvcException extends Exception {

	private static final long serialVersionUID = -5129476850780832850L;

	/** HTTP状态码 */
	private int status;
	
	public ZmvcException(int status) {
		super();
		this.status = status;
	}

	public ZmvcException(int status, String message) {
		super(message);
		this.status = status;
	}
	
	public int getStatus() {
		return status;
	}
	
	public void setStatus(int status) {
		this.status = status;
	}
	
}

放假了,忙着准备回家,所以就介绍的比较粗糙。不过结合上一篇讲解SpringMVC源码的文章的话,应该很容易看懂~

项目完整代码已上传,点击下载

猜你喜欢

转载自blog.csdn.net/qq_31142553/article/details/86582066