这是个人的一个学习的经历。。。。。。
spring mvc的版本是5.x的,sping mvc的入口是 DispatcherServlet 类 ,在之前的ssm项目中,我们是通过配置在web.xml中的servlet把请求的入口设置为DispatcherServlet,现在的版本官方不提倡编写web.xml,而是如下:
public class MyWebApplicationInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletCxt) {
// Load Spring web application configuration
AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
ac.register(AppConfig.class);
ac.refresh();
// Create and register the DispatcherServlet
DispatcherServlet servlet = new DispatcherServlet(ac);
ServletRegistration.Dynamic registration = servletCxt.addServlet("app", servlet);
registration.setLoadOnStartup(1);
registration.addMapping("/app/*");
}
}
原理是在spring-web项目下(spring-webmvc依赖于spring-web) 资源文件中 META-INF下services下的javax.servlet.ServletContainerInitializer文件中配置了org.springframework.web.SpringServletContainerInitializer类,
这个类在这个固定的路径文件下就意味着服务器比如tomcat启动的时候会去运行这个类,当然这个类继承了ServletContainerInitializer接口,固定会去运行startup方法。ServletContainerInitializer 是在javax.servlet中的,这个差不多就是一个规范一个标准,具体实现是在服务器例如tomcat中的。
而SpringServletContainerInitializer类配置了一行注解
@HandlesTypes(WebApplicationInitializer.class)
这个handlestypes的注解意思是会查找到继承了WebApplicationInitializer接口的类会被当成startup方法的参数传入,而SpringServletContainerInitializer的startup中会用反射调用实现了WebApplicationInitializer类的startup方法
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
try {
initializers.add((WebApplicationInitializer);
ReflectionUtils.accessibleConstructor(waiClass).newInstance());
}
catch (Throwable ex) {
throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
}
}
回到最开始的官方代码,代码中注册了DispatcherServlet基本等同于xml中这样的一个效果。
app
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
1
请求到了这里之后,就可以来看DispatcherServlet的实现了,首先上类图
DispatcherServlet继承了servlet,而servlet类请求执行都是通过调用doGet和doPost方法,所以查看这两个方法的实现。具体在FrameworkServlet类中,两个方法的代码都是调用了processRequest(request, response)方法,在processRequest中先看这段代码
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
先重点说明当前线程的RequestAttributes对象是在 RequestContextFilter中初始化的,主要之前一直疑惑为什么这里要重新将request和response对象放入本地线程变量中,后来发现会有这么个情况,那就是在RequestContextFilter和执行到DispatcherServlet这个mvc的sevlet中间我们可能对request或response对象进行封装修改操作,所以这里是一个同步最新的request和response对象的操作。
protected ServletRequestAttributes buildRequestAttributes(HttpServletRequest request,
@Nullable HttpServletResponse response, @Nullable RequestAttributes previousAttributes) {
if (previousAttributes == null || previousAttributes instanceof ServletRequestAttributes) {
return new ServletRequestAttributes(request, response);
}
else {
return null; // preserve the pre-bound RequestAttributes instance
}
}
接下来会运行doService(request, response)方法,而doService方法由子类DispatcherServlet继承执行,所以请求最后会执行doDispatch(request, response)方法。
doDispatch(HttpServletRequest request, HttpServletResponse response)
在方法中第一个重点是获取handler处理器
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
进入getHandler方法,是这样一段代码,循环一个已存在的HandlerMapping的List查找本次请求所需要的HandlerMapping,这个List以及getHandler方法都是在DispatcherServlet类中的,至于HandlerMapping列表什么时候存在的,怎么创建的?首先肯定是初始化的时候,具体情况先放一边。
(ps:此处是一个策略模式)
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
点开getHandler方法,getHandler方法并不是由具体实现类去实现的,而是通过AbstractHandlerMapping这个抽象类,而在AbstractHandlerMapping的getHandler方法中,调用了getHandlerInternal方法,这个方法由子类去实现 (模板方法模式)。
先看RequestMappingHandlerMapping实现类,这个类继承自 AbstractHandlerMethodMapping,主要实现也是AbstractHandlerMethodMapping的getHandlerInternal方法,getHandlerInternal方法中调用了lookupHandlerMethod方法
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
request.setAttribute(LOOKUP_PATH, lookupPath);
this.mappingRegistry.acquireReadLock();
try {
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
lookupHandlerMethod方法中从mappingRegistry这个map中查找对应的匹配结果,并最终得到 HandlerMethod,在mappingRegistry中,有多个map缓存,最终的HandlerMethod也是通过多次从这几个map中查找才获得,这其中的具体操作先放一边。
而在获取到HandlerMethod后,此时的method对象是一个最初的method,这个method将会生成最终的bean
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
在createWithResolvedBean方法中,将会执行this.beanFactory.getBean(beanName),从spring容器中获取一个完整的bean,最终返回一个全新的HandlerMethod
public HandlerMethod createWithResolvedBean() {
Object handler = this.bean;
if (this.bean instanceof String) {
Assert.state(this.beanFactory != null, "Cannot resolve bean name without BeanFactory");
String beanName = (String) this.bean;
handler = this.beanFactory.getBean(beanName);
}
return new HandlerMethod(this, handler);
}
拿到handler之后,这时候handler从一个原始的java对象到spring bean到一个完整的spring mvc中的controller对象仅仅还是处于spring bean这个步骤,再回到AbstractHandlerMapping中的getHandler方法中
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
在这个方法中,spring mvc的拦截器会被添加进来,最后会得到一个完整的HandlerExecutionChain
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, LOOKUP_PATH);
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;
}
再回到 DispatcherServlet类中,此时得到的HandlerExecutionChain对象就是一个完整的有拦截器并且被spring管理的一个handler对象。
拿到handler后,往下看,查找一个适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
RequestMappingHandlerMapping对应的适配器是RequestMappingHandlerAdapter,在适配器中,通过supports去判断是否是传入的handle的适配器,这样的好处就是避免了大量的ifelse,而这里在拿到了对应的适配器后,再回到DispatcherServlet类中,先是执行了之前获取到的HandlerExecutionChain中的HandlerInterceptor拦截器链。
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
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;
}
在这里,如果interceptor.preHandle方法返回的是false的话,会去触发triggerAfterCompletion也就是interceptor.afterCompletion方法。而在依次正常执行完interceptor拦截器之后,回到DispatcherServlet,就会去执行适配器的handle方法。
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
这里再进到RequestMappingHandlerAdapter中,RequestMappingHandlerAdapter继承的是AbstractHandlerMethodAdapter,所以handle方法先调用AbstractHandlerMethodAdapter中的handle方法。
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
而handleInternal方法被RequestMappingHandlerAdapter实现,handleInternal方法中最重要是这一行代码
mav = invokeHandlerMethod(request, response, handlerMethod);
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
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);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
LogFormatUtils.traceDebug(logger, traceOn -> {
String formatted = LogFormatUtils.formatValue(result, !traceOn);
return "Resume with async result [" + formatted + "]";
});
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
先看getDataBinderFactory(handlerMethod),方法名称可以看出,这个方法是获取initBinder的
private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
Class> handlerType = handlerMethod.getBeanType();
Set methods = this.initBinderCache.get(handlerType);
if (methods == null) {
methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
this.initBinderCache.put(handlerType, methods);
}
List initBinderMethods = new ArrayList<>();
// Global methods first
this.initBinderAdviceCache.forEach((clazz, methodSet) -> {
if (clazz.isApplicableToBeanType(handlerType)) {
Object bean = clazz.resolveBean();
for (Method method : methodSet) {
initBinderMethods.add(createInitBinderMethod(bean, method));
}
}
});
for (Method method : methods) {
Object bean = handlerMethod.getBean();
initBinderMethods.add(createInitBinderMethod(bean, method));
}
return createDataBinderFactory(initBinderMethods);
}
首先,从initBinderCache中获取,如果当前是项目启动这个HandlerMethod的第一次请求,那么initBinderCache这个map中肯定是空的,就会查找注解了InitBinder的方法并放入缓存中,再来请求的话就会直接跳过,从缓存中取。然后再从initBinderAdviceCache中获取,initBinderAdviceCache中存放的是注解了@ControllerAdvice中的类,不过initBinderMethods添加的顺序是先initBinderAdviceCache,然后再是initBinderCache,最后获取WebDataBinderFactory返回。
回到invokeHandlerMethod方法,接下来就是getModelFactory(handlerMethod, binderFactory)方法
private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
Class> handlerType = handlerMethod.getBeanType();
Set methods = this.modelAttributeCache.get(handlerType);
if (methods == null) {
methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
this.modelAttributeCache.put(handlerType, methods);
}
List attrMethods = new ArrayList<>();
// Global methods first
this.modelAttributeAdviceCache.forEach((controllerAdviceBean, methodSet) -> {
if (controllerAdviceBean.isApplicableToBeanType(handlerType)) {
Object bean = controllerAdviceBean.resolveBean();
for (Method method : methodSet) {
attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
}
}
});
for (Method method : methods) {
Object bean = handlerMethod.getBean();
attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
}
return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
}
getModelFactory中的做法和之前的getDataBinderFactory方法类似,modelAttributeCache中存放的是对应的handlerMethod中存在@ModelAttribute(不能有@RequestMapping)注解的方法,而modelAttributeAdviceCache存放的就是有@ControllerAdvice注解的类中具有@ModelAttribute注解(不能有@RequestMapping)的方法,最后创建一个ModelFactory并返回
然后再回到invokeHandlerMethod方法,接下来创建一个createInvocableHandlerMethod,设置argumentResolvers,returnValueHandlers,binderFactory,parameterNameDiscoverer,总之就是初始化了一个ServletInvocableHandlerMethod,ServletInvocableHandlerMethod是HandlerMethod类的一个扩展,具备了参数解析,返回值解析等功能, 然后初始化一个ModelAndViewContainer
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
initModel方法中会执行注解了@ModelAttribute的方法的流程,不怎么用模板,因此带过。
跳过异步的代码,往下看,
invocableMethod.invokeAndHandle(webRequest, mavContainer)
进入invokeAndHandle方法中的第一句代码
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
return doInvoke(args);
}
invokeForRequest方法中,getMethodArgumentValues是获取当前方法所需的参数,细节先放一边,往下看doInvoke方法
return getBridgedMethod().invoke(getBean(), args);
这一行代码是目标方法最终执行的地方,getBridgedMethod()是父类HandlerMethod的方法,其最终构建过程在之前说的AbstractHandlerMethodMapping中createWithResolvedBean方法中。此时获取的是当前的Controller对象,也就是当前请求的类的原始对象,而getBean()方法获取到的就是spring容器中取出的Bean,这个Bean是包含了spring中各种aop的spring bean。最终该方法执行得到结果返回。回到invokeAndHandle方法中
try {
this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
if (handler == null) {
throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
}
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
这里selectHandler方法明显是获取对应的Handler方法,方式和之前获取对应的适配器类似
private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
boolean isAsyncValue = isAsyncReturnValue(value, returnType);
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
continue;
}
if (handler.supportsReturnType(returnType)) {
return handler;
}
}
return null;
}
所以这里对应不同的类型,有不同的returnHandler处理器,这里看RequestResponseBodyMethodProcessor处理器
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
mavContainer.setRequestHandled(true);
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
// Try even with null return value. ResponseBodyAdvice could get involved.
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
重点是writeWithMessageConverters方法
try {
message = new EmptyBodyCheckingHttpInputMessage(inputMessage);
for (HttpMessageConverter> converter : this.messageConverters) {
Class> converterType = (Class>) converter.getClass();
GenericHttpMessageConverter> genericConverter =
(converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter>) converter : null);
if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
(targetClass != null && converter.canRead(targetClass, contentType))) {
if (message.hasBody()) {
HttpInputMessage msgToUse =
getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
((HttpMessageConverter) converter).read(targetClass, msgToUse));
body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
}
else {
body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
}
break;
}
}
}
方法中选择了对应的HttpMessageConverter,并且调用了getAdvice()的方法,这个Advice就是实现了ResponseBodyAdvice的类,最后write输出。
if (body != null) {
Object theBody = body;
LogFormatUtils.traceDebug(logger, traceOn ->
"Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]");
addContentDispositionHeader(inputMessage, outputMessage);
if (genericConverter != null) {
genericConverter.write(body, targetType, selectedMediaType, outputMessage);
}
else {
((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
}
最后再回到RequestMappingHandlerAdapter,接下来获取ModelAndView,当前主要说rest请求,所以这里跳过。
return getModelAndView(mavContainer, modelFactory, webRequest);
接着就回到了DispatchServlet中
mappedHandler.applyPostHandle(processedRequest, response, mv);
这里会去调用拦截器的postHandle方法,再往下
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
在这个方法中,回去调用拦截器的afterCompletion方法。
到此为止,spring mvc的一个请求的执行周期就已经基本结束了。以上说的只是一个大概的过程,具体的细节放到后续的博客里面