Administrator
发布于 2022-10-13 / 45 阅读
0
0

SpringMVC源码

前端控制器(DispatcherServlet)的架构

Image

FrameworkServlet中的doService是一个抽象类,具体的实现在DispatcherServlet

protected abstract void doService(HttpServletRequest var1, HttpServletResponse var2) throws Exception;

DispatcherServlet中的doService着重看这一段

 try {
            this.doDispatch(request, response);
        } finally {

doDispatch细节

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
      HttpServletRequest processedRequest = request;
      HandlerExecutionChain mappedHandler = null;
      boolean multipartRequestParsed = false;
      WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

try {
    try {
        ModelAndView mv = null;
        Object dispatchException = null;

        try {
           --
           //1、检查是否文件上传请求
           --
            processedRequest = this.checkMultipart(request);
            multipartRequestParsed = processedRequest != request;
            --
            //2、根据当前的请求地址找到那个类能来处理;
            --
            mappedHandler = this.getHandler(processedRequest);
            --
            //3、如果没有找到哪个处理器(控制器)能处理这个请求就404,或者抛异常
            --
            if (mappedHandler == null) {
                this.noHandlerFound(processedRequest, response);
                return;
            }
	    --
            //4、拿到能执行这个类的所有方法的适配器;(反射工具AnnotationMethodHandlerAdapter)
            --
            HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
            String method = request.getMethod();
            boolean isGet = "GET".equals(method);
            if (isGet || "HEAD".equals(method)) {
                long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
                    return;
                }
            }

            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }
	    --
            // Actually invoke the handler.处理(控制)器的方法被调用
            //控制器(Controller),处理器(Handler)
            //5、适配器来执行目标方法;将目标方法执行完成后的返回值作为视图名,设置保存到ModelAndView中
            //目标方法无论怎么写,最终适配器执行完成以后都会将执行后的信息封装成ModelAndView
            --
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
            if (asyncManager.isConcurrentHandlingStarted()) {
                return;
            }

            this.applyDefaultViewName(processedRequest, mv);
            mappedHandler.applyPostHandle(processedRequest, response, mv);
        } catch (Exception var20) {
            dispatchException = var20;
        } catch (Throwable var21) {
            dispatchException = new NestedServletException("Handler dispatch failed", var21);
        }

	--
    	//转发到目标页面;
        //6、根据方法最终执行完成后封装的ModelAndView;转发到对应页面,
        而且ModelAndView中的数据可以从请求域中获取
        --
        this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
    } catch (Exception var22) {
        this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
    } catch (Throwable var23) {
        this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
    }

} finally {
    if (asyncManager.isConcurrentHandlingStarted()) {
        if (mappedHandler != null) {
            mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
        }
    } else if (multipartRequestParsed) {
        this.cleanupMultipart(processedRequest);
    }

}
}

基本流程:

1. 所有请求过来DispatcherServlet收到请求,
2. 调用doDispatch()方法进行处理
       1.getHandler():根据当前请求地址找到能处理这个请求的目标处理器类(处理器)
                    根据当前请求在HandlerMapping中找到这个请求的映射信息,获取到目标处理器类
       2.getHandlerAdapter():根据当前处理器类获取到能执行这个处理器方法的适配器;
                    根据当前处理器类,找到当前类的HandlerAdapter(适配器)
       3. 使用刚才获取到的适配器(AnnotationMethodHandlerAdapter )执行目标方法;
       4.目标方法执行后会返回一个ModelAndView对象
       5.根据ModelAndView的信息转发到具体的页面,并可以在请求域中取出ModelAndView中的模型数据

 

getHandler细节

SpringMVC怎么根据当前请求就能找到哪个类能来处理?

mappedHandler = this.getHandler(processedRequest);//放行

getHandler()会返回目标处理器类的执行链
image-1665648906947

step into:

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        if (this.handlerMappings != null) {
            Iterator var2 = this.handlerMappings.iterator();

            while(var2.hasNext()) {
                HandlerMapping mapping = (HandlerMapping)var2.next();
                HandlerExecutionChain handler = mapping.getHandler(request);
                if (handler != null) {
                    return handler;
                }
            }
        }

        return null;
    }

 

HandlerMappings有什么?

HandlerMappings:处理器映射,他里面保存了每一个处理器能处理哪些请求的映射信息
image-1665649478773

与Spring4.0的对比
Image [2]


 

SpringMVC如何存储映射信息?

mappingLookup:ioc容器启动创建Controller对象的时候扫描每个处理器都能处理什么请求,保存在HandlerMapping的mappingLookup属性中;
下一次请求过来,就来看哪个HandlerMapping中有这个请求映射信息就行了;

image-1665650644213
 

与Spring4.0的对比
Image [3]

个人补充:
与4版本相比,同样是使用注解的方式编写controller,但4版本中的DefaultAnnotationHandlerMapping
在5版本中是没有的了,如下图:
被替换成RequestMappingHandlerMapping
由前面还可以知道BeanNameUrlHandlerMapping的顺序也放在了后面。

留个坑:在看SpringBoot源码的时候貌似SimpleUrlHandlerMapping也会出现在HandlerMappings中,
==
这里却没有
--

image-1665651095810


 

getHandlerAdapter细节

如何找到目标处理器类的适配器。要拿适配器才去执行目标方法?

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

step into:

 protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
        if (this.handlerAdapters != null) {
            Iterator var2 = this.handlerAdapters.iterator();

            while(var2.hasNext()) {
                HandlerAdapter adapter = (HandlerAdapter)var2.next();
                
                //查看哪个适配器支持传入得处理器
                
                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");
    }

 
SpringMVC 5.0版本
image-1665652137234
 

与SpringMVC4.0的对比:
Image [4]
 

与HandlerMapping一样,在Annotationxxx什么的换成了RequestMappingHandlerAdapter
image-1665652502872

 

结合Spring流程看SpringMVC

1. Spring在启动的时候,进行WebApplicationContext的初始化,首先会逐个加载你配置的xml文件,然后第二个。。。

	@Override
	public void contextInitialized(ServletContextEvent event) {
		initWebApplicationContext(event.getServletContext());
	}

2.进入ContextLoader的initWebApplicationContext方法,节约空间源码只留一部分

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {

  try {
--
//传入的servletContext的context为空,直接创建一个WebApplicationContext
//至于servletContext的context为什么会有东西,可以追溯下源码,
//public ServletContext getServletContext () { return (ServletContext) super.getSource(); }
//这是一个事件类,用于通知web应用程序的servlet上下文的更改.自: Servlet 2.3 参见: ServletContextListener
//其从源文件中getSource返回一个ServletContext,其中带有context,再深入的东西暂时不研究
--
      if (this.context == null) {
          this.context = createWebApplicationContext(servletContext);
      }
      --
      如果类型是ConfigurableWebApplicationContext的,将这个类型的context进行向上转型,
      this.context此时的类型是XmlWebApplicationContext
      --
      if (this.context instanceof ConfigurableWebApplicationContext) {
          ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
          if (!cwac.isActive()) {

              if (cwac.getParent() == null) {

                  ApplicationContext parent = loadParentContext(servletContext);
                  cwac.setParent(parent);
              }
              --
              配置和刷新Web应用程序上下文,step into 
              --
              configureAndRefreshWebApplicationContext(cwac, servletContext);
          }
      }
      servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

      ClassLoader ccl = Thread.currentThread().getContextClassLoader();
      if (ccl == ContextLoader.class.getClassLoader()) {
          currentContext = this.context;
      }
      else if (ccl != null) {
          currentContextPerThread.put(ccl, this.context);
      }

      if (logger.isInfoEnabled()) {
          long elapsedTime = System.currentTimeMillis() - startTime;
          logger.info("Root WebApplicationContext initialized in " + elapsedTime + " ms");
      }

      return this.context;
  }
  catch (RuntimeException | Error ex) {
      logger.error("Context initialization failed", ex);
      servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
      throw ex;
  }
}

image-1665715030086


3.configureAndRefreshWebApplicationContext

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
    if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
        String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
        if (idParam != null) {
            wac.setId(idParam);
        }
        else {
            // Generate default id...
            wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
                    ObjectUtils.getDisplayString(sc.getContextPath()));
        }
    }

    wac.setServletContext(sc);
    --
    //获取配置文件位置参数
    //首先获取的是applicationContext.xml
    //
    --
    String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
    if (configLocationParam != null) {
        wac.setConfigLocation(configLocationParam);
    }

.....

    customizeContext(sc, wac);
    --
    //调用refresh方法进行容器的刷新
    --
    wac.refresh();
}


4.refresh方法

wac的类型被转成是ConfigurableWebApplicationContext(接口)
但原本类型还是XmlWebApplicationContext
其继承关系:


image-1665717667489
 

refresh方法在AbstractApplicationContext有实现,所以跳到AbstractApplicationContext中执行以下代码。
其他部分解析在Spring源码中有,这里着重了解HandlerMapping什么时候注入的。

 

@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // Prepare this context for refreshing.
        prepareRefresh();

        // Tell the subclass to refresh the internal bean factory.
        --
        //告诉子类刷新内部bean工厂。
        --
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

5.obtainFreshBeanFactory

	protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
		refreshBeanFactory();
		return getBeanFactory();
	}

5.1 refreshBeanFactory (AbstractRefreshableApplicationContext类下)

	@Override
	protected final void refreshBeanFactory() throws BeansException {
    --
    //是否有beanfactory,有就清理掉
    --
		if (hasBeanFactory()) {
			destroyBeans();
			closeBeanFactory();
		}
		try {
        --
        //创建一个新的beanFactory
        --
          DefaultListableBeanFactory beanFactory = createBeanFactory();
          beanFactory.setSerializationId(getId());
            --
            //定制beanFactory
            --
          customizeBeanFactory(beanFactory);
          --
          重点
          --
          loadBeanDefinitions(beanFactory);
          synchronized (this.beanFactoryMonitor) {
              this.beanFactory = beanFactory;
          }
      }
      catch (IOException ex) {
          throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
      }
  }

5.2 loadBeanDefinitions

@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
    // Create a new XmlBeanDefinitionReader for the given BeanFactory.
    --
    //为给定的BeanFactory创建一个新的XmlBeanDefinitionReader
    --
    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

    // Configure the bean definition reader with this context's
    // resource loading environment.
    beanDefinitionReader.setEnvironment(getEnvironment());
    beanDefinitionReader.setResourceLoader(this);
    beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

    // Allow a subclass to provide custom initialization of the reader,
    // then proceed with actually loading the bean definitions.
    --
    //初始化用于加载此上下文的bean定义的bean定义读取器。默认实现为空。 
    //可以在子类中重写,例如关闭XML验证或使用不同的XmlBeanDefinitionParser实现。
    --
    initBeanDefinitionReader(beanDefinitionReader);
    --
    //加载bean的定义信息
    --
    loadBeanDefinitions(beanDefinitionReader);
}

5.3loadBeanDefinitions

--
使用给定的XmlBeanDefinitionReader加载bean定义。
bean工厂的生命周期由refreshBeanFactory方法处理;
因此,该方法仅用于加载和/或注册bean定义。 
委托给ResourcePatternResolver将位置模式解析到资源实例中

--
  protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
  --
  //获取配置文件的路径信息
  --
      String[] configLocations = getConfigLocations();
      if (configLocations != null) {
          for (String configLocation : configLocations) {
          --
          加载配置文件信息
          --
              reader.loadBeanDefinitions(configLocation);
          }
      }
  }

5.4 loadBeanDefinitions

	@Override
	public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
		return loadBeanDefinitions(location, null);
	}
    
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
    ResourceLoader resourceLoader = getResourceLoader();
    if (resourceLoader == null) {
        throw new BeanDefinitionStoreException(
                "Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
    }

    if (resourceLoader instanceof ResourcePatternResolver) {
        // Resource pattern matching available.
        try {
            Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
            --
            加载bean
            重点就这一个,此方法后beanfactory内就包含有很多HandleMapping等信息
            ---
            int count = loadBeanDefinitions(resources);
            ---
            
            if (actualResources != null) {
                Collections.addAll(actualResources, resources);
            }
            if (logger.isTraceEnabled()) {
                logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
            }
            return count;
        }
        catch (IOException ex) {
            throw new BeanDefinitionStoreException(
                    "Could not resolve bean definition resource pattern [" + location + "]", ex);
        }
    }
    else {
        // Can only load single resources by absolute URL.
        Resource resource = resourceLoader.getResource(location);
        int count = loadBeanDefinitions(resource);
        if (actualResources != null) {
            actualResources.add(resource);
        }
        if (logger.isTraceEnabled()) {
            logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
        }
        return count;
    }
}



	@Override
	public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
      Assert.notNull(resources, "Resource array must not be null");
      int count = 0;
      for (Resource resource : resources) {
          count += loadBeanDefinitions(resource);
      }
      return count;
  }
  

//在这个类中,会将你编写的配置文件转换成流的形式
--
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
      Assert.notNull(encodedResource, "EncodedResource must not be null");
      if (logger.isTraceEnabled()) {
          logger.trace("Loading XML bean definitions from " + encodedResource);
      }

      Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
      if (currentResources == null) {
          currentResources = new HashSet<>(4);
          this.resourcesCurrentlyBeingLoaded.set(currentResources);
      }
      if (!currentResources.add(encodedResource)) {
          throw new BeanDefinitionStoreException(
                  "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
      }
      try {
      --
      //将配置文件转成字节流
      --
          InputStream inputStream = encodedResource.getResource().getInputStream();
          try {
              InputSource inputSource = new InputSource(inputStream);
              if (encodedResource.getEncoding() != null) {
                  inputSource.setEncoding(encodedResource.getEncoding());
              }
              --
              //字节流文件传入
              --
              return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
          }



protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {

		try {
        --
        //加载成Document
        --
			Document doc = doLoadDocument(inputSource, resource);
            --
            //注册bean定义信息并返回结果数量
            --
			int count = registerBeanDefinitions(doc, resource);
			if (logger.isDebugEnabled()) {
				logger.debug("Loaded " + count + " bean definitions from " + resource);
			}
			return count;
		}

中间跳过两步方法的调用


5.5 解析xml配置文件

最后会来到这里

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
      if (delegate.isDefaultNamespace(root)) {
          NodeList nl = root.getChildNodes();
          --
          //一重循环,将转成字节流的文件取出
          //在跳过的两步中,有对传入的字节流进行处理,然后转成Node类型
          //每一个Node就是你编写的配置文件的空行、注释、配置的bean组件信息
          //将每个Node和Element类型进行匹配
          //像mvc ,context,bean这样的名称空间都是适配的
          --
          for (int i = 0; i < nl.getLength(); i++) {
              Node node = nl.item(i);
              if (node instanceof Element) {
                  Element ele = (Element) node;
                  if (delegate.isDefaultNamespace(ele)) {
                      parseDefaultElement(ele, delegate);
                  }
                  else {
                  --
                  //一般会进入这个方法,因为一般isDefaultNamespace都是false
                  --
                      delegate.parseCustomElement(ele);
                  }
              }
          }

 

5.6parseCustomElement

public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
--
//获取解析到的组件的名称空间uri
//如:http://www.springframework.org/schema/mvc
--
      String namespaceUri = getNamespaceURI(ele);
      if (namespaceUri == null) {
          return null;
      }
      
      --
      解析名称空间的uri,得到名称空间里附带的组件:如HandlerMapping;HandlerAdapter等
      或者是自己标注的组件
      --
      NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
      if (handler == null) {
          error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
          return null;
      }
      --
      将解析得到的组件加入到BeanDefinitionMap中
      --
      return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
  }

解析名称空间的uri
image-1665748598283

5.7 自己写的组件是如何扫描进来的?

public BeanDefinition parse(Element element, ParserContext parserContext) {
--
得到你标记的要扫描的包路径
--
      String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
      basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
      String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
              ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);

      // Actually scan for bean definitions and register them.
      --
      扫描组件bean并注册他们
      --
      ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
      --
      扫描注册你标记了注解的组件或者说你注册的组件,如Controller等
      --
      Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
      --
      扫描component-scan中有的组件注册到beanDefinitions中,附图:
      --
      registerComponents(parserContext.getReaderContext(), beanDefinitions, element);

      return null;
  }

 
标记注解的组件加入:
image-1665751115407

5.8 某些HandlerMapping等是如何加进来的?

在AnnotationDrivenBeanDefinitionParser类下,
有public BeanDefinition parse(Element element, ParserContext context) {}这么一个方法
里面有一大段很长的一段实现。
里面要注册那些HandlerMapping;HandlerAdapter都是写好了的。
类似调用者样的方法:

RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
readerContext.getRegistry().registerBeanDefinition(HANDLER_MAPPING_BEAN_NAME, handlerMappingDef);
这里是注册RequestMappingHandlerMapping

如果你单纯写了一个mvc的组件,里面会注册些什么,底层已经是写好了的。

 
** mvc:annotation-driven组件扫描注册后**
image-1665751232488
 

另附我的xml配置文件:

以上步骤只在我只有三个组件配置的情况下得出,其他原理应该大差不差
dispatch-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">


    <!--扫描所有组件-->
    <context:component-scan base-package="com.lly"/>
    <!--注解生效-->
    <mvc:annotation-driven/>

    <!--配置一个视图解析器;能帮我们拼接页面地址-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/pages/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

</beans>

组件注册流程总结:

  1. Spring在启动的时候会逐个加载配置的xml文件,如applicationContext.xml;dispatcher-servlet.xml
    1. 如果你在web.xml中配置了组件,但建的文件的名字没写对应
    2. Spring会使用默认的名字(如:)并找对应目录下的的文件,如果你配置文件的组件名字和你的配置的文件名不一样,且你的组件文件命名又和官方的默认命名不一样,报错
  2. 在加载组件的时候会将组件转成字节流信息
  3. 解析字节流信息,并将信息封装成一个Node类型的数组
  4. 遍历这个数组,得到适合的类型
    1. 解析该类型得到uri地址,根据uri解析出该命名空间含有的组件
    2. 根据解析出的组件,选择出你配置文件有的组件进行组件注册
      eg:localName=annotation-driven
  5. 至此,Spring启动的时候,组件的扫描完成,但此时只是加入到BeanDefinitionMap,后续还需通过initStrategies方法初始化到handlerMappings等List集合中。
  6. 即BeanDefinitionMap中保存的是所有的bean组件,后续还得通过初始化加入到对应的类型中。是handlerMappings或者是HandlerAdapter或者是其他。。。

 
image-1665752277661

补充:如果你配置文件里的什么组件都没有,怎么办?

image-1665814909163

 

	/**
	 * Initialize the strategy objects that this servlet uses.
	 * <p>May be overridden in subclasses in order to initialize further strategy objects.
	 */
     以下方法会实现组件的初始化,以initHandlerMappings举例
	protected void initStrategies(ApplicationContext context) {
		initMultipartResolver(context);
		initLocaleResolver(context);
		initThemeResolver(context);
		initHandlerMappings(context);
		initHandlerAdapters(context);
		initHandlerExceptionResolvers(context);
		initRequestToViewNameTranslator(context);
		initViewResolvers(context);
		initFlashMapManager(context);
	}
initHandlerMappings
  private void initHandlerMappings(ApplicationContext context) {
      this.handlerMappings = null;
--
//detectAllHandlerMappings默认true,探查ApplicationContext里的HandlerMappings
--
      if (this.detectAllHandlerMappings) {
          // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
          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) {
      --
      //使用默认的策略配置
      --
          this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
          if (logger.isTraceEnabled()) {
              logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
                      "': using default strategies from DispatcherServlet.properties");
          }
      }
  }

getDefaultStrategies
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
--
//获取strategyInterface的名字handlerMapping,
//去配置文件中找默认的策略实现,这个配置文件在和DispatcherServlet类同级目录下的
//DispatcherServlet.properties中
      String key = strategyInterface.getName();
      String value = defaultStrategies.getProperty(key);
      --
      //以下进行策略装配
      --
      if (value != null) {
          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<>();
      }
  }

image-1665815672541

DispatcherServlet.properties

image-1665815696308

但最后发送请求的时候也是会报错的
因为虽然有了这些组件,但在doDispatch里进行getHandler或者getHandlerAdapter时也会报错
除非进行一些别的配置文件配置,比如注册bean组件


<bean name="hello" class="com.lly.controller.MyFirstController">

</bean>

MyFirstController


@Controller
public class MyFirstController{

    @RequestMapping("/hello")
    public String hello() {
        System.out.println("处理hello请求....");
        return "/WEB-INF/pages/success.jsp";
    }

评论