一、引言:
SpringMVC框架是一套基于Spring开发的web框架,他极大的简化了web应用的开发流程,开发时只需要在Controller中编写方法,并给方法指定URL地址,SpringMVC即可将URL路径和方法实现关联起来。当客户端请求指定的URL路径时,他能根据路径匹配到指定的方法并进行调用。
SpringMVC实现的基本思路如下:
- 扫描Spring容器中所有的controller(即加了@Controller注解的类);
- 遍历controller的方法,读取方法上的URL请求信息并封装到RequestMappingInfo,方法则封装到HandlerMethod中,这种映射关系保存到MappingRegistry中;
- 当客户端请求URL时,拿URL到这个MappingRegistry中去查找,如果找不到则返回404,找到则返回对应的方法HandlerMethod;
- 将这个HandlerMethod和所有实现了HandlerInterceptor或者MappedInterceptor接口的过滤器封装到HandlerExecutionChain中。
- 由于不止@Controller注解一种方式,比如还有实现Controller接口,实现HttpServletRequest接口等方式,因此需要封装一个适配器adapter出来,以一种统一的接口方式调用(当增加一种新的方式时,只需要实现其对应的adapter即可);
- 通过对适配器adapter的调用来实现对不同的controller方法的调用;
- 对返回值进行处理,比如有的情况下直接将返回值输出到前端,有的情况下需要序列化成json输出到前端,有的情况下需要找到对应的视图文件并返回ModelAndView。
下面的图片是网上找的关于SpringMVC的工作流程图:
- 请求首先进入DispatcherServlet, 由DispatcherServlet 从HandlerMappings中匹配对应的Handler,此时只是获取到了对应的Handler,然后拿着这个Handler去寻找对应的适配器,即:HandlerAdapter;
- 对查找到的Handler加上过滤器链封装到HandlerExecutionChain;
- 拿到对应HandlerAdapter时,这时候开始调用对应的Handler方法(在调用handler方法之前还会调用过滤器链的相关方法),即执行我们的Controller来处理业务逻辑了, 执行完成之后返回一个ModeAndView;
- HandlerAdapter执行完之后,返回一个ModeAndView,把它交给我们的视图解析器ViewResolver,通过视图名称查找出对应的视图然后返回;
- 最后,渲染视图 返回渲染后的视图。
二、相关实体类:
-
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。
-
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等)。
-
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(); //............... } -
HandlerMapping:存储了URL和HandlerMethod的对应关系,这个接口仅仅提供了一个getHandler方法来根据URL找到对应的HandlerMethod,然后将HandlerMethod封装成HandlerExecutionChain。上面说的AbstractHandlerMethodMapping就是这个接口的一个实现。
用注解@RequestMapping定义的Handler,用的是RequestMappingHandlerMapping。用Controller接口和HttpServletRequest定义的handler,用的是BeanNameUrlHandlerMapping。静态资源的请求,用的是SimpleUrlHandlerMapping。
HandlerMapping的类关系图如下:
-
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。