转载

我所理解的SpringMVC(一)

什么是Spring MVC 其实应该说 什么是 MVC ?

Model 数据, View 视图, Controller 控制器。啪!三个东西合在一起,MVC就出来了。

这么简单? 没错,其实就是这么简单。

当然如果你对MVC不太熟悉的话还是乖乖往下看吧。

其实MVC就是处理Web请求的一种框架模式,我们思考一下用户请求的流程:

  1. 输入url
  2. 发送请求
  3. 接受响应

对于用户来说其实也就这三个步骤,但是对于服务端来说需要做很多,这里我画了一张图供大家理解。这里其实忽略了很多 Tomcat 本身已经为我们做的,而且 Tomcat 并不仅仅只有 Host,Context。

我所理解的SpringMVC(一)

我来解释一下,用户发送请求的 url 其实对应着很多东西。

比如说 localhost ,当然这个就是 ip 地址。这个 ip 地址对应着 Tomcat 里面的 Host (站点) 层。

Context 代表着一个 web 应用,还记得当初写 Servlet 项目的时候有一个 webapp 文件夹(里面还有个WEB-INF,最里面是web.xml)吗?也可以理解为当初写的 servlet 项目就是一个 web 应用,而用户通过 ip 地址的端口映射去找到了这个应用。

这时候我们已经通过 ip 和端口寻找到了指定的 web 应用,我们知道一个 web 应用中存在多个 servlet ,而我们如何去寻找每个请求对应的 servlet 呢? 答案还是 url ,我们通过后面的 /news 去web.xml里面寻找已经注册到应用中的 Servlet 类。

具体我再配合图中解释一下: 找到了指定的 web 应用之后,通过请求的路径 /news 去 web.xml 中寻找是否有对应的 标签,其中这个标签的子标签 标签的值需要匹配到请求的路径,这个时候 标签的值为 /news 正好匹配到了,所以我们获取了上面的标签的值然后再寻找是否有 标签的子标签 和这个值相等,如果有则获取到底下的 对应的类 并通过这个类去解析请求

总结来说就是 通过 url 从 web.xml 文件中寻找到匹配的 servlet 的类

其实这就是原生的 servlet ,那么 MVC 的影子在哪呢?

别急,你要记住的是 MVC 就是对 Servlet 的封装,想要理解 MVC 就必须理解 Servlet 和 MVC 与 Servlet 的关系

SpringMVC中的DispatcherServlet

DispatcherServlet的继承结构

我所理解的SpringMVC(一)

有没有发现这个 DispatcherServlet 其实就是一个 Servlet。也就是说 Spring MVC中最核心的部分其实就是一个 Servlet 。

我来简单解释一下相应的部分(先简单了解一下)

  • FrameworkServlet : 是 DispatcherServlet 的一个抽象父类。其中提供了加载某个对应的 web 应用程序环境的功能,还有将 GET、POST、DELETE、PUT等方法统一交给 DispatcherServlet 处理。
  • Servlet : 一个规范,用来解决 HTTP服务器和业务代码之间的耦合问题
  • GenericServlet : 提升了 ServletConfig 的作用域,在init(servletConfig)方法中调用了init()无参方法,子类可以重写这个无参初始化方法来做一些初始化自定义工作(后面分析源码中会讲到)。
  • HttpServletBean : 可以将 Servlet 配置信息作为 Bean 的属性 自动赋值给 Servlet 的属性。
  • DispatcherServlet:整个继承链中的最后一个也是最重要的一个类,是SpringMVC 实现的核心类。MVC 通过在 web.xml 中配置 DispatcherServlet 来拦截所有请求,然后通过这个 DispatcherServlet 来进行请求的分发,并调用相应的处理器去处理请求和响应消息。

有没有想起来在 SSM 框架配置的时候在 web.xml 中的配置

<servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <!-- 把所以请求都交给DispatcherServlet处理-->
  <servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <!-- 拦截所有 -->
    <url-pattern>/</url-pattern>
  </servlet-mapping>

复制代码

好的,现在我们知道了 springMVC 中使用了一个 DispatcherServlet 去处理所有请求,而我们知道真正处理的肯定不是 DispatcherServlet ,而是具体我们在 Controller 层中写的带有 @Controller @RequestMapping 注解的类和底下的方法。 DispatcherServlet 只是一个为我们分发请求到具体处理器的一个分发Servlet

那么,这个 DispatcherServlet 具体怎么工作的呢?它是如何分发请求的呢? 且听我慢慢道来。

和 DispatcherServlet 一起工作的一些组件

首先我先将这些组件简单化,并把一些不必要的先省略为了便于理解。

其实要 分发请求处理请求并相应 ,我们可以肯定的是 我们需要使用一个映射关系Mapping来表示 url 和对应的 处理器,使用一个处理器Handler来处理对应的请求。这样,我们就出来了两个 最根本 的角色: HandlerMapping Handler

我们再来强调一下这两者的工作。

  • HandlerMapping : 建立请求和处理器的映射关系,即我们可以通过请求去获取对应的 handler。
  • Handler : 处理请求的处理器。

这样,我们就可以再画出一个简单的流程图了。

我所理解的SpringMVC(一)

有没有疑惑, 这个 HandlerMapping 集合从哪来?HandlerMapping 的类结构是啥样的?Handler的类结构又是什么样的?

如果有,那么就带着这些问题往下看。

首先,这个 handlerMapping 的集合从哪来的?甭说集合,连单个你都不知道从哪来。 那么我们就从源码中找答案吧。为了你省力,我直接告诉你,DispatcherServlet 中的 doDispatch 方法中进行了 分发的主要流程。

这里我给出了简化版的 doDispatch 方法

public void doDispatcher(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        // 通过request在处理器映射HandlerMapping中获取相应处理器
        Object handler = getHandler(req);
        if (handler != null) {
            ... 调用handler的处理方法
        }
    }
复制代码

那么这个 getHandler(request) 方法又是什么样的呢?这里我直接放 DispatcherServlet 类的源码

@Nullable
// 这里返回的是 HandlerExecutionChain 
// 其实这是一个处理器和拦截器链的组合
// 现在你就理解为返回的是一个 handler
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
	if (this.handlerMappings != null) {
	        // 遍历 handlerMapping 调用它的getHanlder获取处理器
	        // 如果不为空直接返回
		for (HandlerMapping mapping : this.handlerMappings) {
			HandlerExecutionChain handler = mapping.getHandler(request);
			if (handler != null) {
				return handler;
			}
		}
	}
	return null;
}
复制代码

我们继续追踪 HandlerMapping 的getHandler(request) 方法。

其实进入源码你会发现,HandlerMapping 是一个接口,故这里给出一个简单的 HandlerMapping 接口代码,如果有能力可以去看源码。

public interface HandlerMapping {

    /**
     * 获取请求对应的处理器
     * @param request 请求
     * @return 处理器
     * @throws Exception 异常
     */
    Object getHandler(HttpServletRequest request) throws Exception;

}
复制代码

那么,具体的实现类又是什么呢?我们思考一下,这个mapping是一个请求和处理器的映射,它是如何存的?我们当初怎么做的?

想必,你已经有答案了,在我们使用 SSM 框架的时候我们是通过 给类和方法 配置相应的注解(@Controller,@ReqeustMapping)来建立相应的 url 和 处理器方法的映射关系的。

我们再回来看源码 在idea中 可以使用 ctrl+alt+B 来查看方法实现和类实现继承。我们查看 HandlerMapping 接口的 getHandler 方法的实现,我们会发现直接跳到了 AbstractHandlerMapping 这个抽象类的方法,我们查看该方法的源码

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        // 获取 handler 这其实是一个抽象方法 
        // 子类可以通过不同实现来获取handler
        // 例如通过 url 和 name的映射逻辑
        // 通过 requestMapping 中的 url 和对应方法的映射逻辑
	Object handler = getHandlerInternal(request);
	if (handler == null) {
	        // 如果获取为null 的获取默认的处理器
	        // 这里子类也可以设置自己的默认处理器
		handler = getDefaultHandler();
	}
	// 如果还是没有则返回 这时候 DispatcherServlet会返回 404
	if (handler == null) {
		return null;
	}
	// Bean name or resolved handler?
	// 如果返回的处理器是字符串 则认为它是一个beanName
	if (handler instanceof String) {
		String handlerName = (String) handler;
		// 通过beanName从IOC容器中获取相应的处理器
		handler = obtainApplicationContext().getBean(handlerName);
	}
	// 下面是将处理器 和 拦截器封装成处理器执行链
	HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

	if (logger.isTraceEnabled()) {
		logger.trace("Mapped to " + handler);
	}
	else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
		logger.debug("Mapped to " + executionChain.getHandler());
	}

	if (CorsUtils.isCorsRequest(request)) {
		CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request);
		CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
		CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
		executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
	}
	// 返回处理器执行链
	return executionChain;
}
复制代码

如果其他的你看不懂,你只要理解我注释区域的代码就行了。我们从上面得到最重要的信息就是: 真正的handler获取是在子类实现的getHandlerInternal(request)中 ,那我们来看一下有哪些子类。

我所理解的SpringMVC(一)

我们可以看到其中有 AbstractHandlerMethodMapping、AbstractUrlHandlerMapping、WelcomeHandlerMapping。

我们主要关注 AbstractHandlerMethodMapping (提供方法处理器) 和 AbstractUrlHandlerMapping(提供url对应处理器映射),这里为了不耽误时间,我们直接分析 AbstractHandlerMethodMapping ,它是注解方法的映射的一个抽象类。

@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
    // 获取请求的路径
    String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
    this.mappingRegistry.acquireReadLock();
    try {
        // 通过 lookupPath 来从中获取 HandlerMethod 
        // 这个HandlerMethod 又是什么?
        // 先不用管 我们继续看lookupHandlerMethod源码
    	HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
    	return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
    }
    finally {
    	this.mappingRegistry.releaseReadLock();
    }
}

// 这里的逻辑稍微有些复杂 
// 你只要知道它通过请求来匹配返回处理器方法
// 如果有多个处理器方法可以处理当前Http请求 那么返回最佳匹配的处理器
@Nullable
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
	List<Match> matches = new ArrayList<>();
	List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
	if (directPathMatches != null) {
		addMatchingMappings(directPathMatches, matches, request);
	}
	if (matches.isEmpty()) {
		// No choice but to go through all mappings...
		addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
	}

	if (!matches.isEmpty()) {
		Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
		matches.sort(comparator);
		Match bestMatch = matches.get(0);
		if (matches.size() > 1) {
			if (logger.isTraceEnabled()) {
				logger.trace(matches.size() + " matching mappings: " + matches);
			}
			if (CorsUtils.isPreFlightRequest(request)) {
				return PREFLIGHT_AMBIGUOUS_MATCH;
			}
			Match secondBestMatch = matches.get(1);
			if (comparator.compare(bestMatch, secondBestMatch) == 0) {
				Method m1 = bestMatch.handlerMethod.getMethod();
				Method m2 = secondBestMatch.handlerMethod.getMethod();
				String uri = request.getRequestURI();
				throw new IllegalStateException(
						"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
			}
		}
		request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
		handleMatch(bestMatch.mapping, lookupPath, request);
		// 返回最佳匹配
		return bestMatch.handlerMethod;
	}
	else {
		return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
	}
}
复制代码

到现在逻辑慢慢变得复杂起来,我们做一个小结: 在 DispatcherServlet 中我们通过遍历 handlerMapping 集合并调用它的 getHandler 方法来获取handler ,这个handler 是一个 Object (因为spring会整合其他框架的处理器,并使用这些处理器处理请求 所以这里选择Object)。而 HandlerMapping 仅仅是一个接口 为了方便 抽象类 AbstractHandlerMapping 实现了这个方法并且为子类提供了自定义获取handler的 getHandlerInternal(request) 方法。 对于我们通用方式注解来标识控制器方法和url请求路径的映射是通过 AbstractHandlerMethodMapping 来获取请求对应的HandlerMethod的

我所理解的SpringMVC(一)

那么,疑问又来了,HandlerMethod是什么?

还记得刚刚上面的问题么, 这个 HandlerMapping 集合从哪来?HandlerMapping 的类结构是啥样的?Handler的类结构又是什么样的

我们现在可以来回答一下Handler的类结构了,Handler是一个Object,为了第三方框架的处理器能够接入来处理请求,spring使用了Object,而对于注解形式来说 一个处理器是一个 HandlerMethod 。这里我给出 HandlerMethod 的简单实现形式,如果有能力可以查看源码。

@Data
public class HandlerMethod {
    // bean 其实这个是标识 Controller 注解的类的对象
    private Object bean;
    // 该对象的类型
    private Class<?> beanType;
    // 该类上被标识RequestMapping注解的方法
    private Method method;
}
复制代码

在 HandlerMethod 中存放了控制器类和对应的方法。为什么要存放他们?你想一下,我们用@RequestMapping注解标识的方法不就是处理方法吗,HandlerMethod 中存放他们,到时候调用处理方法只需要通过反射调用这个bean的method就行了。如果不理解可以看一下我下面写的代码。

// 这里先不用管 ModelAndView
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    ModelAndView modelAndView = null;
    HandlerMethod handlerMethod = ((HandlerMethod) handler);
    // 获取HandlerMethod的method
    Method method = handlerMethod.getMethod();
    if (method != null) {
        // 通过反射调用方法并返回视图对象(这就是处理方法)
        modelAndView = (ModelAndView)method.invoke(BeanFactory.getBean(handlerMethod.getBeanType()));
    }
    return modelAndView;
}
复制代码

再来看看上面的问题。 这个 HandlerMapping 集合从哪来?HandlerMapping 的类结构是啥样的?Handler的类结构又是什么样的

第三个问题解决了,第二个问题上面也解决了,那么第一个问题来了。

我们从一开始就只讨论了如何在HandlerMapping中取出handler 并且调用handler的处理方法,那么我们一开始遍历的这个handlerMappings集合到底从哪儿来,或者说 它是什么时候被初始化的

这个时候,我们又得回到根源。我再来放这张图,不知道你们是否还记得

我所理解的SpringMVC(一)

你能想到什么呢?我这里假设你对servlet还是有一些了解的。

我们知道 DispatcherServlet 是一个 servlet 。一个 servlet 肯定有init()方法 (还记得我上面讲的GenericServlet的作用吗?现在来了,如果不是很懂init(),建议去了解一下servlet的生命周期)。

我们可以大胆的猜测,对 handlerMappings 的初始化就是在 servlet 的初始化方法中进行的。

很遗憾我们没有能在 DispatcherServlet 中找到 init 方法,那么就找他爹,找不到再找他爷爷,曾爷爷。我们知道因为 DispatcherServlet 继承了 GenericServlet 所以我们需要找到 实现的 init() 无参方法。所以我们找到了 HttpServletBean 中重写的 init() 方法了

@Override
// 这家伙还不允许被重写 final
public final void init() throws ServletException {

	// Set bean properties from init parameters.
	// 将servlet配置信息存入bean的属性中
	PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
	if (!pvs.isEmpty()) {
		try {
			BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
			ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
			bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
			initBeanWrapper(bw);
			bw.setPropertyValues(pvs, true);
		}
		catch (BeansException ex) {
			if (logger.isErrorEnabled()) {
				logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
			}
			throw ex;
		}
	}
	// 重点在这里 这里子类才可以自由发挥
	// 该方法不能被重写是因为 上面的步骤是必须的
	// 别忘了上面的步骤是 HttpServletBean 的职责
	// 接下去继续看
	// Let subclasses do whatever initialization they like.
	initServletBean();
}

// 进入FrameworkServlet 查看实现的initServletBean方法
@Override
protected final void initServletBean() throws ServletException {
        // log不用管
	getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");
	if (logger.isInfoEnabled()) {
		logger.info("Initializing Servlet '" + getServletName() + "'");
	}
	long startTime = System.currentTimeMillis();
	// 重点来了
	try {
	        // 初始化容器和上下文
	        // 我们要记得现在在 FrameworkServlet中执行呢
	        // 我们进入initWebApplicationContext方法
		this.webApplicationContext = initWebApplicationContext();
		// 初始化FrameworkServlet 这里没给实现 子类也没给
		// 所以不用管
		initFrameworkServlet();
	}
	// log不用管
	catch (ServletException | RuntimeException ex) {
		logger.error("Context initialization failed", ex);
		throw ex;
	}

	if (logger.isDebugEnabled()) {
		String value = this.enableLoggingRequestDetails ?
				"shown which may lead to unsafe logging of potentially sensitive data" :
				"masked to prevent unsafe logging of potentially sensitive data";
		logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails +
				"': request parameters and headers will be " + value);
	}

	if (logger.isInfoEnabled()) {
		logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
	}
}
// 初始化容器和上下文
protected WebApplicationContext initWebApplicationContext() {
        // 查找是否有专门的根环境 先不用管
	WebApplicationContext rootContext =
			WebApplicationContextUtils.getWebApplicationContext(getServletContext());
	WebApplicationContext wac = null;
	// 如果不存在专用根环境 通常我们不会走到这 先不用管
	if (this.webApplicationContext != null) {
		// A context instance was injected at construction time -> use it
		wac = this.webApplicationContext;
		if (wac instanceof ConfigurableWebApplicationContext) {
			ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
			if (!cwac.isActive()) {
				// The context has not yet been refreshed -> provide services such as
				// setting the parent context, setting the application context id, etc
				if (cwac.getParent() == null) {
					// The context instance was injected without an explicit parent -> set
					// the root application context (if any; may be null) as the parent
					cwac.setParent(rootContext);
				}
				configureAndRefreshWebApplicationContext(cwac);
			}
		}
	}
	// 如果为空 
	if (wac == null) {
	// 查看是否在servlet中已经注册
		// No context instance was injected at construction time -> see if one
		// has been registered in the servlet context. If one exists, it is assumed
		// that the parent context (if any) has already been set and that the
		// user has performed any initialization such as setting the context id
		wac = findWebApplicationContext();
	}
	if (wac == null) {
	// 自己创建一个
		// No context instance is defined for this servlet -> create a local one
		wac = createWebApplicationContext(rootContext);
	}
	// 判断这个环境是否支持刷新 如果不支持 下面手动刷新 
	// 如果支持则前面已经刷新了
	if (!this.refreshEventReceived) {
		// Either the context is not a ConfigurableApplicationContext with refresh
		// support or the context injected at construction time had already been
		// refreshed -> trigger initial onRefresh manually here.
		synchronized (this.onRefreshMonitor) {
		    // !!!!!!!!!!!!!!!!!!!!!重点
		    // DispacherServlet 就是在这里实现的
			onRefresh(wac);
		}
	}

	if (this.publishContext) {
		// Publish the context as a servlet context attribute.
		String attrName = getServletContextAttributeName();
		getServletContext().setAttribute(attrName, wac);
	}

	return wac;
}
// DispatcherServlet 重写了该方法
@Override
protected void onRefresh(ApplicationContext context) {
	initStrategies(context);
}

// 一系列的初始化工作
protected void initStrategies(ApplicationContext context) {
        // 前面一些不用管
	initMultipartResolver(context);
	// 地域
	initLocaleResolver(context);
	// 主题
	initThemeResolver(context);
	// 重点来了!!!!
	// 初始化HandlerMapping
	initHandlerMappings(context);
	// 初始化适配器
	initHandlerAdapters(context);
	// 初始化异常处理
	initHandlerExceptionResolvers(context);
	initRequestToViewNameTranslator(context);
	initViewResolvers(context);
	initFlashMapManager(context);
}
复制代码

我们先暂停一下,理一下思路。

HttpServletBean 中重写了 GenericServletinit() 无参方法开始初始化动作,其中HttpServletBean中先实现了 servlet 配置信息到 bean 属性信息的赋值,然后调用 initServletBean() 该方法是子类进行自定义初始化的方法。 FrameworkServlet 实现了该方法并且调用了 initWebApplicationContext() 方法进行了容器和上下文的初始化工作,并且其中调用了 onRefresh(ApplicationContext context) 方法。 这里FrameworkServlet没有做任何操作而是子类 DispatcherServlet 在其中调用了 initStrategies(context) 进行初始化工作。

好了我们继续看初始化 handlerMappings方法。

private void initHandlerMappings(ApplicationContext context) {
	this.handlerMappings = null;

	if (this.detectAllHandlerMappings) {
		// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
		// 在应用上下文中寻找 handlerMappings
		Map<String, HandlerMapping> matchingBeans =
				BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
		if (!matchingBeans.isEmpty()) {
			this.handlerMappings = new ArrayList<>(matchingBeans.values());
			// We keep HandlerMappings in sorted order.
			AnnotationAwareOrderComparator.sort(this.handlerMappings);
		}
	}
	else {
		try {
			HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
			this.handlerMappings = Collections.singletonList(hm);
		}
		catch (NoSuchBeanDefinitionException ex) {
			// Ignore, we'll add a default HandlerMapping later.
		}
	}
	
	// Ensure we have at least one HandlerMapping, by registering
	// a default HandlerMapping if no other mappings are found.
	// 前面不用管 其实一般我们使用默认的
	if (this.handlerMappings == null) {
	        // 这里是获取默认的handlerMappings
		this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
		if (logger.isTraceEnabled()) {
			logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
					"': using default strategies from DispatcherServlet.properties");
		}
	}
}

protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
	String key = strategyInterface.getName();
	// 获取defaultStrategies的内容
	String value = defaultStrategies.getProperty(key);
	if (value != null) {
	        // 解析相应内容并初始化 handlerMappings
	        // 获取内容中的类名数组
		String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
		List<T> strategies = new ArrayList<>(classNames.length);
		for (String className : classNames) {
			try {
			     //通过反射创建并加入数组中取返回给上面
				Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
				Object strategy = createDefaultStrategy(context, clazz);
				strategies.add((T) strategy);
			}
			catch (ClassNotFoundException ex) {
				throw new BeanInitializationException(
						"Could not find DispatcherServlet's default strategy class [" + className +
						"] for interface [" + key + "]", ex);
			}
			catch (LinkageError err) {
				throw new BeanInitializationException(
						"Unresolvable class definition for DispatcherServlet's default strategy class [" +
						className + "] for interface [" + key + "]", err);
			}
		}
		return strategies;
	}
	else {
		return new LinkedList<>();
	}
}
复制代码

事情马上明了了,我们现在已经知道了 handlerMapping 是怎么加入队列中了( 获取到 defaultStrategies 的资源内容 遍历内容获取类名 并通过反射创建对象加入队列 ),所以我们可以大胆猜测 defaultStrategies 中藏着秘密,它肯定已经定义好了默认的 handlerMapping 的类名。

果不其然,我们来看代码

private static final Properties defaultStrategies;
	// 在静态块中已经加载了defaultStrategies
	static {
		// Load default strategy implementations from properties file.
		// This is currently strictly internal and not meant to be customized
		// by application developers.
		try {
		    // 通过资源初始化defaultStrategies
		    // 这里的资源路径 很重要!!!!
			ClassPathResource resource = new
			ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
			defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
		}
		catch (IOException ex) {
			throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
		}
	}
	// 在这里呀 DispatcherServlet.properties
	private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties";
复制代码

我们去寻找一下 DispatcherServlet.properties 这个文件, 原来都给我们定义好了,我们可以看见默认的handlerMapping有两个。 BeanNameUrlHandlerMapping 和 RequestMappingHandlerMapping。

我所理解的SpringMVC(一)

好了,我们现在终于可以总结一下了。

在我定义的简单的 DispatcherServlet 的 “同事”中,主要有 HandlerMapping 和 Handler。HandlerMapping 会在 DispatcherServlet 初始化的时候被在加载 ,然后在 DispatcherServlet 调用到执行方法 doDispatch() 的时候,会遍历 handlerMappings 集合获取对应的 handler。handler 是一个 Object(因为需要适配其他框架的处理器),在注解方式中是一个 HandlerMethod (里面存了Controller类的实例和method,在处理方法的时候使用反射调用该实例的method方法)。获取完 handler之后通过 处理器的处理方法返回一个视图对象并渲染页面。

再来一个组件 Adapter

其实对于“正宗”的MVC流程中,在遍历 handlerMappings 获取到相应的 handler 之后,其实并不是直接通过 handler 来执行处理方法的,而是通过 HandlerAdapter 来执行处理方法的。

这里我写了一个简单的适配器接口,源码也不复杂 你可以直接看源码

public interface HandlerAdapter {
    ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
    boolean support(Object handler);
}
复制代码

handleRequest 不必说,用来执行处理的,里面传进去一个 handler 肯定最终调用的 handler的执行方法。这是典型的适配器模式。

support 判断该handler是否被该适配器支持。

其实理解上面的过程了之后再加入一个适配器就不难了,我们主要思考一下为什么要加入适配器,我们知道 handler 是一个 Object 它的处理方法是不固定的,如果我们要在 DispatcherServlet 中通过 Handler 执行处理方法,那么就要做很多类型判断,这对于 DispatcherServlet 是非常难受的,所以需要通过适配器扩展。

这样我们可以写出一个简单的 doDispatch 方法了,有能力的可以查看源码

public void doDispatcher(HttpServletRequest req, HttpServletResponse resp) throws Exception {
    // 通过request在处理器映射HandlerMapping中获取相应处理器
    Object handler = getHandler(req);
    if (handler != null) {
        // 通过handler获取对应的适配器
        HandlerAdapter handlerAdapter = getHandlerAdapter(handler);
        if (handlerAdapter != null) {
            // 通过适配器调用处理器的处理方法并返回ModelAndView视图对象
            ModelAndView modelAndView = handlerAdapter.handleRequest(req, resp, handler);
            ... 处理视图并渲染
        }
    }
}
复制代码
原文  https://juejin.im/post/5da18c746fb9a04e37316a9e
正文到此结束
Loading...