Spring源码分析第四弹 - MVC分析

153 阅读11分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

到本篇文章spring的IOC DI AOP原理就分析完了!但是仅仅是容器内的的调用。 今天继续来分析下spring MVC框架的源码,看下是怎么实现发送一个请求返回数据或者页面的。

MVC原理分析

预先准备

  • 看过手写的应该都知道,MVC基于tomcat容器,依赖servlet包。这里直接采用springboot,引入web包就好了
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
  • 准备一个Controller
@RequestMapping("/v1")
public class HelloController{

 @RequestMapping("/index")
 @ResponseBody
 public String hello() {
  return "hello world";
 }

 @RequestMapping("/home.html")
 @ResponseBody
 public String home() {
  return "home";
 }
}

MVC handleMapping初始化时序图

  • 入口在IOC容器的回调 handleMapping初始化时序图

MVC初始化时序图

  • HttpServlet包下init方法 MVC初始化时序图

MVC调用阶段时序图

MVC调用阶段时序图 本章源码比较开散,请自备好晕车药!

MVC源码分析

1.HandlerMapping 初始化阶段

  • 上篇文章有提到过MVC的入口,找到AbstractAutowireCapableBeanFactory.doCreateBean依赖注入完之后的这行代码
exposedObject = initializeBean(beanName, exposedObject, mbd);

//初始化之后
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
 ......

 try {
  //mvc在这里实现 接下来走这里
  invokeInitMethods(beanName, wrappedBean, mbd);
 }
 catch (Throwable ex) {
  throw new BeanCreationException(
    (mbd != null ? mbd.getResourceDescription() : null),
    beanName, "Invocation of init method failed", ex);
 }
 if (mbd == null || !mbd.isSynthetic()) {
  //aop在这里实现
  wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
 }
 return wrappedBean;
}


//初始化完bean之后对实现了InitializingBean的类进行通知
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
  throws Throwable {

 ......
    //多余的去掉
    //重点看这里
 if (System.getSecurityManager() != null) {
   try {
    AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {     //发送通知
     ((InitializingBean) bean).afterPropertiesSet();
     return null;
    }, getAccessControlContext());
   }
   catch (PrivilegedActionException pae) {
    throw pae.getException();
   }
  }
  else {
               //发送回调通知
   ((InitializingBean) bean).afterPropertiesSet();
  }
 }
}
  • 既然是MVC,那么就去MVC包里找实现了InitializingBean接口的类,于是在spring-mvc包下找到 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping 这个类,因为现在是基于springboot架子分析,为什么到这个类就不多说了,看完springboot自动注入篇就懂了
@Override
public void afterPropertiesSet() {
 //初始化一些配置 RequestMappingInfo.BuilderConfiguration config
 this.config = new RequestMappingInfo.BuilderConfiguration();
 this.config.setTrailingSlashMatch(useTrailingSlashMatch());
 this.config.setContentNegotiationManager(getContentNegotiationManager());

 if (getPatternParser() != null) {
  this.config.setPatternParser(getPatternParser());
  Assert.isTrue(!this.useSuffixPatternMatch && !this.useRegisteredSuffixPatternMatch,
    "Suffix pattern matching not supported with PathPatternParser.");
 }
 else {
  this.config.setSuffixPatternMatch(useSuffixPatternMatch());
  this.config.setRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch());
  this.config.setPathMatcher(getPathMatcher());
 }
 //调用父类的方法
 super.afterPropertiesSet();
}
  • 接下来到org.springframework.web.servlet.handler.AbstractHandlerMethodMapping 这个类
@Override
public void afterPropertiesSet() {
 initHandlerMethods(); //初始化mapping的入口
}

protected void initHandlerMethods() {
 for (String beanName : getCandidateBeanNames()) {
  //beanName 不是已 ‘scopedTarget.’ 开头的
  if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
   //走这里
   processCandidateBean(beanName);
  }
 }
 handlerMethodsInitialized(getHandlerMethods());
}



//是否满足解析的条件
protected void processCandidateBean(String beanName) {
    Class<?> beanType = null;
    try {
     //通过getType去容器中取出实例对象
        beanType = obtainApplicationContext().getType(beanName);
    }
    catch (Throwable ex) {
        // An unresolvable bean type, probably from a lazy bean - let's ignore it.
        if (logger.isTraceEnabled()) {
            logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
        }
    }
    //取出类型并是handler的才走下面 先看isHandler
    if (beanType != null && isHandler(beanType)) {
        detectHandlerMethods(beanName);
    }
}

//先看下 isHandler 只有加了这两个注解的才解析
protected boolean isHandler(Class<?> beanType) {
 //@Controller || @RequestMapping
 return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
   AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}

//接下来detectHandlerMethods 找到对应关系并注册
protected void detectHandlerMethods(Object handler) {
 //用beanName 通过type获取class
 Class<?> handlerType = (handler instanceof String ?
   obtainApplicationContext().getType((String) handler) : handler.getClass());

 if (handlerType != null) {
  Class<?> userType = ClassUtils.getUserClass(handlerType);
        //获的class方法与路径的对应关系 存储的是class类方法和url请求路径
        //详细Debugger图在下面
  Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
    (MethodIntrospector.MetadataLookup<T>) method -> {
     try {
      //这里面的代码就不贴了 获取注解里的值组成请求路径
      //封装成 RequestMappingInfo
      return getMappingForMethod(method, userType);
     }
     catch (Throwable ex) {
      throw new IllegalStateException("Invalid mapping on handler class [" +
        userType.getName() + "]: " + method, ex);
     }
    });
  if (logger.isTraceEnabled()) {
   logger.trace(formatMappings(userType, methods));
  }
  methods.forEach((method, mapping) -> {
   Method invocableMethod = AopUtils.selectInvocableMethod(method, userType); 
            //遍历map进行注册 注意此时的handler还是beanName
   registerHandlerMethod(handler, invocableMethod, mapping);
  });
 }
}

protected void registerHandlerMethod(Object handler, Method method, T mapping) {
    //内部类 保存在Map<T, MappingRegistration<T>> registry 里面
    this.mappingRegistry.register(mapping, handler, method);
}
  • MethodIntrospector.selectMethods() 断点调试图 方法对应路径图

  • 接下来来到内部类MappingRegistry.register()

public void register(T mapping, Object handler, Method method) {
 // Assert that the handler method is not a suspending one.
 if (KotlinDetector.isKotlinType(method.getDeclaringClass()) && KotlinDelegate.isSuspend(method)) {
  throw new IllegalStateException("Unsupported suspending handler method detected: " + method);
 }
 //加写锁
 this.readWriteLock.writeLock().lock();
 try {
        //进行封装,跟到底就是new HandlerMethod对象,里面保存类beanName和方法相关
  HandlerMethod handlerMethod = createHandlerMethod(handler, method);
  validateMethodMapping(handlerMethod, mapping);
        //保存到map
  this.mappingLookup.put(mapping, handlerMethod);

  List<String> directUrls = getDirectUrls(mapping);
  for (String url : directUrls) {
            //保存到list 其实是一个map List
            //Map<K, List<V>> targetMap key是请求的url 值是bean 方法 容器的封装
   this.urlLookup.add(url, mapping);
  }

  String name = null;
  if (getNamingStrategy() != null) {
   name = getNamingStrategy().getName(handlerMethod, mapping);
   //加到 name -> list
   addMappingName(name, handlerMethod);
  }

  CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
  if (corsConfig != null) {
   this.corsLookup.put(handlerMethod, corsConfig);
  }
  //存储注册信息
  this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
 }
 finally {
  this.readWriteLock.writeLock().unlock();
 }
}
  • 内部成员变量 mappingRegistry,里面有ReentrantReadWriteLock 读写锁,是保证线程安全的
private final MappingRegistry mappingRegistry = new MappingRegistry();
class MappingRegistry {
 //保存注册信息的MAP
 //this.registry.put(mapping,
 //new MappingRegistration<>(mapping, handlerMethod, directPaths, name, corsConfig != null));
 private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
 //this.pathLookup.add(path, mapping); 路径对应mapping
 private final MultiValueMap<String, T> pathLookup = new LinkedMultiValueMap<>();
 //this.nameLookup.put(name, newList); List<HandlerMethod> newList
 //名称 #(方法名中的大写字符) list
 private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();
 //this.corsLookup.put(handlerMethod, corsConfig);
 //跨域配置Map
 private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();
 //读写锁
 private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
}

至此,MVC的AbstractHandlerMethodMapping类已经通过Bean的初始化回调接口InitializingBean#afterPropertiesSet()将所有符合条件的类解析,并将方法和请求路径的对应关系存储到成员变量MappingRegistry中,接下来就开始MVC的初始化了 但是注意此时的handler还是beanName

2.MVC初始化阶段

  • 看过前面手写的MVC版本,入口应不用多说了,这里还是贴下
<servlet>
     <servlet-name>mvc</servlet-name>
     //调用哪个类
     <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
     //init方法 传参
     <init-param>
      //参数名称
         <param-name>contextConfigLocation</param-name>
         //参数值
         <param-value>application.properties</param-value>
     </init-param>
     <load-on-startup>1</load-on-startup>
</servlet>
  • 接下来就直接找到DispatcherServlet类里找init方法,结果没找到,那就只能去父类了。最终在父类找到GenericServlet#init()
public void init(ServletConfig config) throws ServletException {
    //保存了配置文件
    this.config = config;
    //当前类的init 子类实现
    this.init();
}
  • 接下来到HttpServletBean.init()
public final void init() throws ServletException {

 // Set bean properties from init parameters.
 //把init参数封装一下
 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;
  }
 }

 // Let subclasses do whatever initialization they like.
 //接下来走这个 又是子类实现的方法
 initServletBean();
}
  • 接下来到FrameworkServlet.initServletBean()
//找到这行
this.webApplicationContext = initWebApplicationContext();

//创建web容器 实际上也是继承了Application 这个容器用来存储一些web的东西
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) {
  wac = findWebApplicationContext();
 }
 if (wac == null) {
  wac = createWebApplicationContext(rootContext);
 }

 if (!this.refreshEventReceived) {
  synchronized (this.onRefreshMonitor) {
            //容器初始完成走这里 分析完 配置 & 刷新容器 等下分析这里
   onRefresh(wac);
  }
 }

 if (this.publishContext) {
  // Publish the context as a servlet context attribute.
  String attrName = getServletContextAttributeName();
  getServletContext().setAttribute(attrName, wac);
 }
 return wac;
}
  • 配置 & 刷新容器configureAndRefreshWebApplicationContext
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
 if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
  // The application context id is still set to its original default value
  // -> assign a more useful id based on available information
  if (this.contextId != null) {
   wac.setId(this.contextId);
  }
  else {
   // Generate default id...
   wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
     ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
  }
 }
 //web容器的一些参数
 wac.setServletContext(getServletContext());
 wac.setServletConfig(getServletConfig());
 wac.setNamespace(getNamespace());
 wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));

 // The wac environment's #initPropertySources will be called in any case when the context
 // is refreshed; do it eagerly here to ensure servlet property sources are in place for
 // use in any post-processing or initialization that occurs below prior to #refresh
 //在任何情况下,当上下文要先刷新;
 //在这里这样做的原因,以确保servlet属性源已准备就绪
 //在以下刷新之前发生的任何后处理或初始化中使用
 ConfigurableEnvironment env = wac.getEnvironment();
 if (env instanceof ConfigurableWebEnvironment) {
  ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
 }
 //回调后置处理
 postProcessWebApplicationContext(wac);
 //保存web容器
 applyInitializers(wac);
 //重点 点进去到了 AbstractApplicationContext.refresh
 wac.refresh();
}
  • wac.refresh() 接下来到了AbstractApplicationContext.refresh(),是不是很熟悉了,这不是SpringIOC容器的入口吗
public void refresh() throws BeansException, IllegalStateException {
 synchronized (this.startupShutdownMonitor) {
  // Prepare this context for refreshing.
  prepareRefresh();

  // Tell the subclass to refresh the internal bean factory.
  ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

  // Prepare the bean factory for use in this context.
  prepareBeanFactory(beanFactory);

  ......
}
  • 无缝整合!创建web容器的同时,也创建好了IOC容器。容器创建完成,接下来我们再回到前面继续看onRefresh方法,到DispatcherServlet.onRefresh()方法
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);
}
  • 9大组件只挑几个我们关注的分析,先分析initHandlerMappings()
private void initHandlerMappings(ApplicationContext context) {
 //保存HandlerMapping的list private List<HandlerMapping> handlerMappings;
 this.handlerMappings = null;
 //找到所有的映射器 默认为true
 if (this.detectAllHandlerMappings) {
  // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
         //翻译过来就是找到所有的HandleMapping 映射器
  Map<String, HandlerMapping> matchingBeans =
    BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
  if (!matchingBeans.isEmpty()) {
            //然后保存到本地的handlerMapping List
   this.handlerMappings = new ArrayList<>(matchingBeans.values());
   // We keep HandlerMappings in sorted order.
   AnnotationAwareOrderComparator.sort(this.handlerMappings);
  }
 }
 .....
}
  • 经过断点调试,springboot中映射器截图就这么多。然后保存到本地变量List<HandlerMapping> handlerMappings handlerMaping映射器

其他组件都是去容器中取出对应类型的bean,代码就不贴了。 初始化阶段至此结束,接下来开始调用阶段!

3.MVC调用阶段

用过servlet都知道,需要自己实现doGet/doPost方法,那么直接从DispatcherServlet类开始找,最终在父类FrameworkServlet找到

//还有其他的例如 doPut、doDelete这里就不贴了
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
  throws ServletException, IOException {

 processRequest(request, response);
}

/**
 * Delegate POST requests to {@link #processRequest}.
 * @see #doService
 */
@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
  throws ServletException, IOException {

 processRequest(request, response);
}

FrameworkServletdoGet/doPost方法下processRequest() -> DispatcherServlet#doService() -> DispatcherServlet#doDispatch()一直到这里,中间初始化一些配置和本地线程变量。

DispatcherServlet#doDispatch这个方法有四个关注点,一个一个来

3.1 getHandler()根据当前请求找到对应的请求Bean和管道

  • DispatcherServlet#doDispatch 找到这段代码
//根据当前请求找到对应的请求地址管道
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
 //没找到路径 返回404
 noHandlerFound(processedRequest, response);
 return;
}
  • getHandler一直往下,到AbstractHandlerMapping
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    //拿到HandlerMethod对象 我们接下来看一下这个方法 
 Object handler = getHandlerInternal(request);
 if (handler == null) {
  handler = getDefaultHandler();
 }
 if (handler == null) {
  return null;
 }
 // Bean name or resolved handler?
 if (handler instanceof String) {
  String handlerName = (String) handler;
  handler = obtainApplicationContext().getBean(handlerName);
 }
 //可能会存在多个请求地址 组成封装好的管道链 
 HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

 ......
 //返回管道链
 return executionChain;
}

接下来到AbstractHandlerMethodMapping#getHandlerInternal,这里分为两步,第一步是从注册的信息中获取对应的HandlerMethod bean,先看方法initLookupPath()

3.1.1 initLookupPath()从注册的信息中获取对应的HandlerMethod

//getHandlerInternal到抽象类 AbstractHandlerMethodMapping#getHandlerInternal
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
 //获取请求的url路径
 String lookupPath = initLookupPath(request);
 //加读锁
 this.mappingRegistry.acquireReadLock();
 try {
  //根据请求路径取出对应的handlerMethod beanName和Method
  HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
  //如果不等于null 就去容器中根据beanName获取bean
  return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
 }
 finally {
  this.mappingRegistry.releaseReadLock();
 }
}
  • 获取请求的url路径 AbstractHandlerMapping#initLookupPath()->UrlPathHelper#resolveAndCacheLookupPath()->getLookupPathForRequest()->getPathWithinApplication(),这个代码应该很熟悉,返回请求路径
public String getPathWithinApplication(HttpServletRequest request) {
 //获取项目路径
 String contextPath = getContextPath(request);
 //获取请求路径
 String requestUri = getRequestUri(request);
 //合并 下面有注释 正常情况下 URI会包含项目路径
 String path = getRemainingPath(requestUri, contextPath, true);
 if (path != null) {
  // Normal case: URI contains context path.
  return (StringUtils.hasText(path) ? path : "/");
 }
 else {
  return requestUri;
 }
}

上面已经拿到请求路径了,再回到AbstractHandlerMapping#getHandlerInternal根据请求路径取出对应的handlerMethod,在lookupHandlerMethod()方法里

3.1.2 lookupHandlerMethod()

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
 List<Match> matches = new ArrayList<>();
 //去本地变量中拿到对应的mapping
 //this.pathLookup.get(urlPath); 上面有提过的一个封装的Map
 //key是路径,value是List RequestMappingInfo
 List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
 if (directPathMatches != null) {
  //用上面的mapping 校验request,最终封装成RequestMappingInfo添加到matches list
  //继续往下看
  addMatchingMappings(directPathMatches, matches, request);
 }
 if (matches.isEmpty()) {
  addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
 }
 if (!matches.isEmpty()) {
  Match bestMatch = matches.get(0);
  if (matches.size() > 1) {
   Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
   matches.sort(comparator);
   bestMatch = matches.get(0);
   
   //不关注的去掉了
   。。。
  }
  request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.getHandlerMethod());
  handleMatch(bestMatch.mapping, lookupPath, request);
  //返回bean的封装信息
  return bestMatch.getHandlerMethod();
 }
 else {
  return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
 }
}
  • AbstractHandlerMethodMapping#addMatchingMappings()添加有效的mapping
private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
 for (T mapping : mappings) {
  //通过mapping中的一下本地变量校验request
  T match = getMatchingMapping(mapping, request);
  if (match != null) {
   //添加到list
   matches.add(new Match(match, 
   this.mappingRegistry.getRegistrations().get(mapping)));
  }
 }
}
  • 获取mapping的注册信息,
this.mappingRegistry.getRegistrations().get(mapping);

public Map<T, MappingRegistration<T>> getRegistrations() {
 return this.registry;
}
//前面添加 this.mappingRegistry.register(mapping, handler, method);
//上面有提过此时的handler还是beanName
private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
  • 再次回到AbstractHandlerMethodMapping#getHandlerInternal() 此时已经拿到对应的handlerMethod了。但此时的handler还是beanName
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
 String lookupPath = initLookupPath(request);
 this.mappingRegistry.acquireReadLock();
 try {
  //根据请求路径取出对应的handlerMethod beanName和Method
  HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
  //如果不等于null 就去容器中根据beanName获取bean
  //先看createWithResolvedBean方法
  return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
 }
 finally {
  this.mappingRegistry.releaseReadLock();
 }
}
  • handlerMethod.createWithResolvedBean()HandlerMethod#createWithResolvedBean()
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;
  //还是熟悉的味道 getBean就不用多说了吧
  handler = this.beanFactory.getBean(beanName);
 }
 return new HandlerMethod(this, handler);
}

至此,handler才从真正的实例化,得到的就是我们要调用的bean了

3.2 getHandlerAdapter()获取处理器

HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

//返回此处理程序对象的HandlerAdapter。
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");
}


//随便看了一个 AbstractHandlerMethodAdapter 其实就是类型匹配
public final boolean supports(Object handler) {
 return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}

3.3 handle()进行调用

  • 上面通过校验已经拿到请求的bean了,回到DispatcherServlet#doDispatch
//通过封装的bean 拿到执行器 Adapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Actually invoke the handler.
//开始执行 往这里走
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
  • 接下来最终会到RequestMappingHandlerAdapter#handle()
//父类抽象类 AbstractHandlerMethodAdapter
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
   throws Exception {
 return handleInternal(request, response, (HandlerMethod) handler);
}

//RequestMappingHandlerAdapter
protected ModelAndView handleInternal(HttpServletRequest request,
   HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
 //注意这里 返回的是一个ModelAndView 
 ModelAndView mav;
 checkRequest(request);

 // Execute invokeHandlerMethod in synchronized block if required.
 //服务器session存在需要加锁 现在能分清session和cookie的区别了吧
 if (this.synchronizeOnSession) {
  HttpSession session = request.getSession(false);
  if (session != null) {
   Object mutex = WebUtils.getSessionMutex(session);
   synchronized (mutex) {
    mav = invokeHandlerMethod(request, response, handlerMethod);
   }
  }
  else {
   // No HttpSession available -> no mutex necessary
   //直接看调用吧 走这里
   mav = invokeHandlerMethod(request, response, handlerMethod);
  }
 }
 else {
  // No synchronization on session demanded at all...
  mav = invokeHandlerMethod(request, response, handlerMethod);
 }

 if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
  if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
   applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
  }
  else {
   prepareResponse(response);
  }
 }

 return mav;
}
  • invokeHandlerMethod()方法关注两行
 //调用 先看调用
 invocableMethod.invokeAndHandle(webRequest, mavContainer);
 if (asyncManager.isConcurrentHandlingStarted()) {
  return null;
 }
 //封装成ModelAndView 
 return getModelAndView(mavContainer, modelFactory, webRequest);

 //getModelAndView里面 有一段这个代码 如果已经处理返回值了 这里返回的就是null了
 if (mavContainer.isRequestHandled()) {
  return null;
 }

3.4 invokeAndHandle()调用

  • ServletInvocableHandlerMethod#invokeAndHandle
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
  Object... providedArgs) throws Exception {
 //调用以及获取返回值 先看这个 
 Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
 setResponseStatus(webRequest);

 //无返回值直接return
 if (returnValue == null) {
  if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
   disableContentCachingIfNecessary(webRequest);
   mavContainer.setRequestHandled(true);
   return;
  }
 }
 else if (StringUtils.hasText(getResponseStatusReason())) {
  mavContainer.setRequestHandled(true);
  return;
 }
 //设置还没有处理返回结果
 mavContainer.setRequestHandled(false);
 Assert.state(this.returnValueHandlers != null, "No return value handlers");
 try {
  //调用返回值的实现 例如返回页面回到ModelAndViewMethodReturnValueHandler
  //返回字符串数据到RequestResponseBodyMethodProcessor
  //最后一点返回值时分析
  this.returnValueHandlers.handleReturnValue(
    returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
 }
 catch (Exception ex) {
  if (logger.isTraceEnabled()) {
   logger.trace(formatErrorForReturnValue(returnValue), ex);
  }
  throw ex;
 }
}
  • InvocableHandlerMethod#invokeForRequest()
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 method.invoke(getBean(), args);
  return doInvoke(args);
 }
  • getMethodArgumentValues()看过手写的MVC,这个代码应该不陌生,这里简单过一下
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
  Object... providedArgs) throws Exception {
 //获取方法的参数数组
 MethodParameter[] parameters = getMethodParameters();
 if (ObjectUtils.isEmpty(parameters)) {
  return EMPTY_ARGS; //无参
 }

 Object[] args = new Object[parameters.length];
 for (int i = 0; i < parameters.length; i++) {
  MethodParameter parameter = parameters[i];
  parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
  args[i] = findProvidedArgument(parameter, providedArgs);
  if (args[i] != null) {
   continue;
  }
  if (!this.resolvers.supportsParameter(parameter)) {
   throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
  }
  try {
   //通过转换器赋值
   args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
  }
  catch (Exception ex) {
   // Leave stack trace for later, exception may actually be resolved and handled...
   if (logger.isDebugEnabled()) {
    String exMsg = ex.getMessage();
    if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
     logger.debug(formatArgumentError(parameter, exMsg));
    }
   }
   throw ex;
  }
 }
 return args;
}
  • 再回到ServletInvocableHandlerMethod#invokeAndHandle 分析下返回参数的封装
//挑了页面的返回 ModelAndViewMethodReturnValueHandler
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
  ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

 if (returnValue == null) {
  //空的设置返回已处理
  mavContainer.setRequestHandled(true);
  return;
 }
 //返回值是modelAndView的 里面是html路径和参数
 ModelAndView mav = (ModelAndView) returnValue;
 if (mav.isReference()) {
  String viewName = mav.getViewName();
  //保存到mavContainer容器里面来
  mavContainer.setViewName(viewName);
  if (viewName != null && isRedirectViewName(viewName)) {
   mavContainer.setRedirectModelScenario(true);
  }
 }
 else {
  View view = mav.getView();
  mavContainer.setView(view);
  if (view instanceof SmartView && ((SmartView) view).isRedirectView()) {
   mavContainer.setRedirectModelScenario(true);
  }
 }
 mavContainer.setStatus(mav.getStatus());
 mavContainer.addAllAttributes(mav.getModel());
}

至此调用业务类已经完成,并且拿到了返回值。接下来分析返回

3.5 返回值处理

3.5.1 非页面返回值分析

  • 找到这里ServletInvocableHandlerMethod#invokeAndHandle返回值这里也分情况,加了@ResponseBody的才能找到
this.returnValueHandlers.handleReturnValue(
     returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
//handleReturnValue一直往下到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.
 //即使返回值为空,也要尝试。ResponseBodyAdvice可能会参与进来
    //到最后通过 response.write写出去
 writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}

3.5.2 如果是页面返回的

  • 非页面的在这里其实已经写出去了,这里看到是返回页面的,我们以127.0.0.1/error页面为例

  • 再次回到DispatcherServlet#doDispatch,找到这段代码

//处理返回结果
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);


private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
  @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
  @Nullable Exception exception) throws Exception {

 ...

 // Did the handler return a view to render?
    //返回去的页面是否要显示视图 为什么说非页面的在这里其实已经写出去了
    //前面getModelAndView里面如果不是页面返回的会标识已经处理了 也就是MV是空就不会走这里了
 if (mv != null && !mv.wasCleared()) {
        //渲染 多余的去掉 这里重点关注
  render(mv, request, response);
  if (errorView) {
   WebUtils.clearErrorRequestAttributes(request);
  }
 }
 
 ....
}

 protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
 ...... 
           
       //不关注的判断先去掉
 try {
  if (mv.getStatus() != null) {
   response.setStatus(mv.getStatus().value());
  }
           //视图的渲染 请求/error 断点看到接下来走ErrorMvcAutoConfiguration.render
  view.render(mv.getModelInternal(), request, response);
 }
 catch (Exception ex) {
  if (logger.isDebugEnabled()) {
   logger.debug("Error rendering view [" + view + "]", ex);
  }
  throw ex;
 }
}
  • 接下来到ErrorMvcAutoConfiguration.render
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response)
   throws Exception {
  if (response.isCommitted()) {
   String message = getMessage(model);
   logger.error(message);
   return;
  }
  response.setContentType(TEXT_HTML_UTF8.toString());
  StringBuilder builder = new StringBuilder();
  Date timestamp = (Date) model.get("timestamp");
  Object message = model.get("message");
  Object trace = model.get("trace");
  if (response.getContentType() == null) {
   response.setContentType(getContentType());
  }
  builder.append("<html><body><h1>Whitelabel Error Page</h1>").append(
    "<p>This application has no explicit mapping for /error, so you are seeing this as a fallback.</p>")
    .append("<div id='created'>").append(timestamp).append("</div>")
    .append("<div>There was an unexpected error (type=").append(htmlEscape(model.get("error")))
    .append(", status=").append(htmlEscape(model.get("status"))).append(").</div>");
  if (message != null) {
   builder.append("<div>").append(htmlEscape(message)).append("</div>");
  }
  if (trace != null) {
   builder.append("<div style='white-space:pre-wrap;'>").append(htmlEscape(trace)).append("</div>");
  }
  builder.append("</body></html>");
     //通过write写到页面上
  response.getWriter().append(builder.toString());
 }
  • 在页面上看到就是这样的 error

至此,整个MVC的源码分析结束了。多看多断点就会发生一切很简单,只是想的太复杂

以上就是本章的全部内容了。

上一篇:Spring源码分析第三弹 - AOP切面编程分析 下一篇:Spring源码分析第五弹 - 神级的spring还有其他什么功效?

问渠那得清如许,为有源头活水来