SpringMVC原理解析

147 阅读9分钟

一、引言:

SpringMVC框架是一套基于Spring开发的web框架,他极大的简化了web应用的开发流程,开发时只需要在Controller中编写方法,并给方法指定URL地址,SpringMVC即可将URL路径和方法实现关联起来。当客户端请求指定的URL路径时,他能根据路径匹配到指定的方法并进行调用。

SpringMVC实现的基本思路如下:

  1. 扫描Spring容器中所有的controller(即加了@Controller注解的类);
  2. 遍历controller的方法,读取方法上的URL请求信息并封装到RequestMappingInfo,方法则封装到HandlerMethod中,这种映射关系保存到MappingRegistry中;
  3. 当客户端请求URL时,拿URL到这个MappingRegistry中去查找,如果找不到则返回404,找到则返回对应的方法HandlerMethod;
  4. 将这个HandlerMethod和所有实现了HandlerInterceptor或者MappedInterceptor接口的过滤器封装到HandlerExecutionChain中。
  5. 由于不止@Controller注解一种方式,比如还有实现Controller接口,实现HttpServletRequest接口等方式,因此需要封装一个适配器adapter出来,以一种统一的接口方式调用(当增加一种新的方式时,只需要实现其对应的adapter即可);
  6. 通过对适配器adapter的调用来实现对不同的controller方法的调用;
  7. 对返回值进行处理,比如有的情况下直接将返回值输出到前端,有的情况下需要序列化成json输出到前端,有的情况下需要找到对应的视图文件并返回ModelAndView。

下面的图片是网上找的关于SpringMVC的工作流程图:

image.png

  1. 请求首先进入DispatcherServlet, 由DispatcherServlet 从HandlerMappings中匹配对应的Handler,此时只是获取到了对应的Handler,然后拿着这个Handler去寻找对应的适配器,即:HandlerAdapter;
  2. 对查找到的Handler加上过滤器链封装到HandlerExecutionChain;
  3. 拿到对应HandlerAdapter时,这时候开始调用对应的Handler方法(在调用handler方法之前还会调用过滤器链的相关方法),即执行我们的Controller来处理业务逻辑了, 执行完成之后返回一个ModeAndView;
  4. HandlerAdapter执行完之后,返回一个ModeAndView,把它交给我们的视图解析器ViewResolver,通过视图名称查找出对应的视图然后返回;
  5. 最后,渲染视图 返回渲染后的视图。

二、相关实体类:

  1. HandlerMethod:从controller中搜索出来的方法都封装到HandlerMethod中,其定义包含了bean 的信息、方法的method和参数信息,当调用执行方法时,就是对其method的调用。其定义代码如下:

    public class HandlerMethod {
    	private final Object bean;
    
    	@Nullable
    	private final BeanFactory beanFactory;
    
    	private final Class<?> beanType;
    
    	private final Method method;
    
    	private final Method bridgedMethod;
    
    	private final MethodParameter[] parameters;
    	
    	//...............
    }
    

    派生类有InvocableHandlerMethod和ServletInvocableHandlerMethod。

  2. RequestMappingInfo:存储了controller方法的属性信息,包含URL路径和请求方式(GET,POST, PUT等)。其定义代码如下:

    public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo> {
    	@Nullable
    	private final String name;
    
    	private final PatternsRequestCondition patternsCondition;
    
    	private final RequestMethodsRequestCondition methodsCondition;
    
    	private final ParamsRequestCondition paramsCondition;
    
    	private final HeadersRequestCondition headersCondition;
    
    	private final ConsumesRequestCondition consumesCondition;
    
    	private final ProducesRequestCondition producesCondition;
    
    	private final RequestConditionHolder customConditionHolder;
    	
    	//............
    }
    

    其中patternsCondition存储URL路径,methodsCondition存储请求方式(GET,POST, PUT等)。

  3. MappingRegistry:存储了所有的URL路径和HandlerMethod的对应关系数据,其定义如下:

    class MappingRegistry {
    	private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
    
    	private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
    
    	private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
    		
    	//............
    }
    

    可以看到其存储了多种对应关系的map,有URL路径和和RequestMappingInfo的map,也有RequestMappingInfo和HandlerMethod的map,还有RequestMappingInfo和MappingRegistration的map。MappingRegistration的定义如下:

    private static class MappingRegistration<T> {
    	private final T mapping;
    
    	private final HandlerMethod handlerMethod;
    
    	private final List<String> directUrls;
    
    	@Nullable
    	private final String mappingName;
    		
    	//........
    }
    

    MappingRegistry定义在AbstractHandlerMethodMapping中,如下:

    public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
    	//................
    	private final MappingRegistry mappingRegistry = new MappingRegistry();
    	//...............
    }
    
  4. HandlerMapping:存储了URL和HandlerMethod的对应关系,这个接口仅仅提供了一个getHandler方法来根据URL找到对应的HandlerMethod,然后将HandlerMethod封装成HandlerExecutionChain。上面说的AbstractHandlerMethodMapping就是这个接口的一个实现。

    用注解@RequestMapping定义的Handler,用的是RequestMappingHandlerMapping。用Controller接口和HttpServletRequest定义的handler,用的是BeanNameUrlHandlerMapping。静态资源的请求,用的是SimpleUrlHandlerMapping。

    HandlerMapping的类关系图如下: image.png

  5. HandlerAdapter:相当于是对HandlerMethod的适配,以一种统一的方式对HandlerMethod进行调用,有多少种handler的方式就有多少个HandlerAdapter。其定义如下:

    public interface HandlerAdapter {
    	boolean supports(Object handler);
    	ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
    }
    

三、映射关系的建立:

由于AbstractHandlerMethodMapping类实现了InitializingBean接口,因此在bean初始化后必然会调用其afterPropertiesSet方法,在这个方法里面完成映射关系的建立:

public void afterPropertiesSet() {
	initHandlerMethods();
}

protected void initHandlerMethods() {
	//拿到所有的bean的名称
	String[] beanNames = obtainApplicationContext().getBeanNamesForType(Object.class));
	for (String beanName : beanNames) {
		Class<?> beanType = obtainApplicationContext().getType(beanName);
		//isHandler判断是否加了controller注解或者RequestMapping注解
		if (beanType != null && isHandler(beanType)) {
			//搜索所有的controller,建立URL路径和HandlerMethod的映射
			detectHandlerMethods(beanName);
		}
	}
	//...........
}

protected boolean isHandler(Class<?> beanType) {
	return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
		AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}

可以看到,initHandlerMethods首先在容器中过滤出所有的bean名称,然后遍历循环,判断是否加了controller注解或者RequestMapping注解,如果是controller,则遍历其方法,通过detectHandlerMethods建立URL路径和HandlerMethod的映射。detectHandlerMethods的代码如下:

protected void detectHandlerMethods(Object handler) {
	Class<?> handlerType = handler.getClass();
	if (handlerType != null) {
		Class<?> userType = ClassUtils.getUserClass(handlerType);
		//遍历controller的方法,封装成RequestMappingInfo对象
		Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
				(MethodIntrospector.MetadataLookup<T>) method -> {
					return getMappingForMethod(method, userType);
				});		
		methods.forEach((method, mapping) -> {
			Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
			//将RequestMappingInfo和method的映射注册到mappingRegistry中
			registerHandlerMethod(handler, invocableMethod, mapping);
		});
	}
}

protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
	//将method封装成RequestMappingInfo
	RequestMappingInfo info = createRequestMappingInfo(method);	
	return info;
}

protected void registerHandlerMethod(Object handler, Method method, T mapping) {
	this.mappingRegistry.register(mapping, handler, method);
}

public void register(T mapping, Object handler, Method method) {
	HandlerMethod handlerMethod = createHandlerMethod(handler, method);	
	//先添加到mappingLookup中
	this.mappingLookup.put(mapping, handlerMethod);
	
	//再添加到urlLookup中
	List<String> directUrls = getDirectUrls(mapping);
	for (String url : directUrls) {
		this.urlLookup.add(url, mapping);
	}

	//添加到addMappingName中
	String name = null;
	if (getNamingStrategy() != null) {
		name = getNamingStrategy().getName(handlerMethod, mapping);
		addMappingName(name, handlerMethod);
	}
	
	//添加到registry中
	this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));	
}

首先遍历controller的method,封装成RequestMappingInfo对象,最后调用registerHandlerMethod注册到mappingRegistry。

四、调用的流程

首先看DispatcherServlet#doDispatch()的代码:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) {
	.........
	//1、根据URL(当然不一定非得是URL)匹配到一个处理器
	mappedHandler = getHandler(processedRequest);
	if (mappedHandler == null) {
		// 若匹配不到Handler处理器,就404了
		noHandlerFound(processedRequest, response);
		return;
	}

	//2、从HandlerExecutionChain里拿出Handler(注意是Object类型哦~ )然后找到属于它的适配器
	HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
	.........
	//3、执行作用在此Handler上的所有拦截器的Pre方法
	if (!mappedHandler.applyPreHandle(processedRequest, response)) {
		return;
	}
	//4、真正执行handle方法(也就是你自己书写的逻辑方法),得到一个ModelAndView
	mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

	//5、视图渲染
	applyDefaultViewName(processedRequest, mv);
	
	//6、执行拦截器的post方法(可见它是视图渲染完成了才会执行的哦~)
	mappedHandler.applyPostHandle(processedRequest, response, mv);
	.........
	//7、执行拦截器的afterCompletion方法(不管抛出与否)
}

1、匹配HandlerMethod:

这里以@Controller注解的方式来分析源码。

其实现代码在AbstractHandlerMethodMapping.java中。代码如下:

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
	//从request中解析出URL路径
	String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
	//根据URL路径解析出HandlerMethod
	HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
	return handlerMethod;
}

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
	List<Match> matches = new ArrayList<>();
	//根据URL路径在mappingRegistry中匹配
	List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
	if (directPathMatches != null) {
		addMatchingMappings(directPathMatches, matches, request);
	}
	if (matches.isEmpty()) {
		//如果没有匹配到则返回null
		return null;
	} else {
		return matches.get(0).handlerMethod;
	}
}

public List<T> getMappingsByUrl(String urlPath) {
	return this.urlLookup.get(urlPath);
}

首先从request中解析出URL路径,然后根据URL路径到mappingRegistry中查找匹配,从mappingRegistry的定义可知,urlLookup存储的是URL路径和RequestMappingInfo的映射关系。

2、封装HandlerExecutionChain:

在DispatcherServlet的getHandler中查找HandlerMethod并封装成HandlerExecutionChain:

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
	for (HandlerMapping hm : this.handlerMappings) {	
    	//调用AbstractHandlerMapping.getHandler查找HandlerMethod并封装成HandlerExecutionChain
		HandlerExecutionChain handler = hm.getHandler(request);
		if (handler != null) {
			return handler;
		}
	}
	return null;
}

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
	//..........省略部分代码.........
	
	//上面的匹配HandlerMethod就是这里的getHandlerInternal
	Object handler = getHandlerInternal(request);
	
	//如果handler是bean名称,则从Spring容器中查找其bean对象
	if (handler instanceof String) {
		String handlerName = (String) handler;
		handler = obtainApplicationContext().getBean(handlerName);
	}

	//将HandlerMethod封装成HandlerExecutionChain并返回
	HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);	
	return executionChain;
}

protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
	HandlerExecutionChain chain = new HandlerExecutionChain(handler));

	String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
	//添加过滤器到HandlerExecutionChain的过滤器链
	for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
		if (interceptor instanceof MappedInterceptor) {
			MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
			if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
				chain.addInterceptor(mappedInterceptor.getInterceptor());
			}
		} else {
			chain.addInterceptor(interceptor);
		}
	}
	return chain;
}

这里只分析@Controller注解方式的。首先getHandlerInternal调用AbstractHandlerMethodMapping的getHandlerInternal方法根据URL路径匹配到HandlerMethod,接着判断如果但会的handler是bean的名称,则根据这个名称从Spring容器中找到bean对象。最后调用getHandlerExecutionChain将HandlerMethod封装成HandlerExecutionChain并返回。

getHandlerExecutionChain首先根据HandlerMethod创建HandlerExecutionChain对象,然后添加过滤器到HandlerExecutionChain。this.adaptedInterceptors中包含了系统中所有实现了MappedInterceptor接口的过滤器,其初始化代码如下:

protected void initApplicationContext() throws BeansException {
	extendInterceptors(this.interceptors);
	//初始化this.adaptedInterceptors
	detectMappedInterceptors(this.adaptedInterceptors);
	initInterceptors();
}

protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
	mappedInterceptors.addAll(BeanFactoryUtils.beansOfTypeIncludingAncestors(obtainApplicationContext(), MappedInterceptor.class, true, false).values());
}

3、匹配HandlerAdapter:

首先是系统启动时将所有实现了HandlerAdapter接口的bean放到this.handlerAdapters变量中,其初始化是在DispatcherServlet.java中:

protected void onRefresh(ApplicationContext context) {
	initStrategies(context);
}

protected void initStrategies(ApplicationContext context) {
	initMultipartResolver(context);
	initLocaleResolver(context);
	initThemeResolver(context);
	initHandlerMappings(context);
	//初始化HandlerAdapter
	initHandlerAdapters(context);
	initHandlerExceptionResolvers(context);
	initRequestToViewNameTranslator(context);
	initViewResolvers(context);
	initFlashMapManager(context);
}

private void initHandlerAdapters(ApplicationContext context) {
	//.......省略部分代码..........
	
	Map<String, HandlerAdapter> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
	this.handlerAdapters = new ArrayList<>(matchingBeans.values());
}

从initHandlerAdapters中可以看到,this.handlerAdapters的初始化是从Spring容器中查找所有的实现了HandlerAdapter接口的bean。

接下来看看getHandlerAdapter是如何匹配HandlerAdapter的:

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
	for (HandlerAdapter ha : this.handlerAdapters) {			
		if (ha.supports(handler)) {
			return ha;
		}
	}
}

他的实现原理很简单,就是遍历刚才初始化的this.handlerAdapters,分别调用其supports,如果支持就返回该HandlerAdapter。

HandlerAdapter的实现类有三个:SimpleControllerHandlerAdapter是用于Controller接口的适配器,HttpRequestHandlerAdapter是用于HttpRequestHandler接口的适配器,RequestMappingHandlerAdapter则是用于HandlerMethod的适配器,也就是我们使用的最多的这种。

public class SimpleControllerHandlerAdapter implements HandlerAdapter {
	@Override
	public boolean supports(Object handler) {
		return (handler instanceof Controller);
	}

	@Override
	@Nullable
	public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) {
		return ((Controller) handler).handleRequest(request, response);
	}
}

public class HttpRequestHandlerAdapter implements HandlerAdapter {
	@Override
	public boolean supports(Object handler) {
		return (handler instanceof HttpRequestHandler);
	}

	@Override
	@Nullable
	public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) {
		((HttpRequestHandler) handler).handleRequest(request, response);
		return null;
	}
}

public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware, InitializingBean {
	@Override
	public final boolean supports(Object handler) {
		return (handler instanceof HandlerMethod);
	}
	
	@Override
	@Nullable
	public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) {
		return invokeHandlerMethod(request, response, handlerMethod);
	}
}

4、对HandlerAdapter方法的调用:

if (!mappedHandler.applyPreHandle(processedRequest, response)) {
	return;
}

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

mappedHandler.applyPostHandle(processedRequest, response, mv);

---------------------

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
	HandlerInterceptor[] interceptors = getInterceptors();
	if (!ObjectUtils.isEmpty(interceptors)) {
		for (int i = 0; i < interceptors.length; i++) {
			HandlerInterceptor interceptor = interceptors[i];
			if (!interceptor.preHandle(request, response, this.handler)) {
				triggerAfterCompletion(request, response, null);
				return false;
			}
			this.interceptorIndex = i;
		}
	}
	return true;
}

void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) {
	HandlerInterceptor[] interceptors = getInterceptors();
	if (!ObjectUtils.isEmpty(interceptors)) {
		for (int i = interceptors.length - 1; i >= 0; i--) {
			HandlerInterceptor interceptor = interceptors[i];
			interceptor.postHandle(request, response, this.handler, mv);
		}
	}
}

1)、applyPreHandle和applyPostHandle分别调用所有过滤器的前置处理和后置处理,也就是HTTP请求调用之前的处理和之后的处理,那么我们可以自己编写bean实现HandlerInterceptor或者MappedInterceptor接口来扩展,以便在请求处理之前和之后加入我们自己的处理代码。

2)、ha.handle则是调用handlerMethod(也即Controller中的接口方法),其调用代码如下:

public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) {
	return handleInternal(request, response, (HandlerMethod) handler);
}

protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
	//.......省略部分代码.........
	
	ModelAndView mav = invokeHandlerMethod(request, response, handlerMethod);
	return mav;
}

protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) {
	//.......省略部分代码.........
	
	ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
	if (this.argumentResolvers != null) {
		invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
	}
	if (this.returnValueHandlers != null) {
		invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
	}
	invocableMethod.setDataBinderFactory(binderFactory);
	invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
	//调用HandlerMethod
	invocableMethod.invokeAndHandle(webRequest, mavContainer);	

	return getModelAndView(mavContainer, modelFactory, webRequest);
}

我们这里只分析@Controller注解的方式的,因此对适配器HandlerAdapter的调用就进入了ServletInvocableHandlerMethod.handle,他先是调用handleInternal,接着再调用invokeHandlerMethod,在invokeHandlerMethod中,首先封装handlerMethod为ServletInvocableHandlerMethod,最后通过invocableMethod.invokeAndHandle来调用调用HandlerMethod。