SpringMvc (1) - main process

foreword

The focus of this chapter is to understand what the main process of springMvc does. Some details are not very detailed, and its source code is not obscure.

SpringMvc startup instructions

A process of Tomcat startup (simple may not be accurate, mainly to understand what Tomcat does, in fact, the initialization of springMvc is linked to Tomcat startup, so it is still necessary to understand):

  1. Tomcat startup will parse server.xml, generate servletContext(servlet context),
  2. Parse web.xml, then merge Tomcat's web.xml and application's web.xml, and then read the configuration ContextLoaderListenerand DispatcherServletcontent into the container
  3. Then look for the jar package under Tomcat, and look for ServletContainerInitializerthe interface implementation class. This interface is provided for the application to initialize. SpringMvc has implemented this interface (springMVC is such META-INF/services/javax.servlet.ServletContainerInitializera file under the classpath), but it does not do other operations. Through Implement this interface and configure it /META-INF/services/below , it can be read and executed by Tomcat. The specific loading logic is inWebappServiceLoader
  4. ContextLoaderListenerInitialize the spring container through the listener in web.xml
  5. Finally, by DisapatcherServletinitializing the servlet container, web.xml provides the default DefaultServlet``JspServletand executes here

There is another knowledge point:

The priority of servlet-mapping is as follows:

/* > / > *.jsp

There is a default web.xml in Tomcat, which is configured with two processors defaultServletand JspServletones for processing static resources and jsp respectively.

These two defaults are used as the default configuration. When there is no configuration in our project, Tomcat's default configuration will connect it.

Therefore, in the configuration of the project, it is necessary to pay attention not to configure the servlet-mapper as /and /*, because the configuration in our project is DispatcherServletto handle requests of the controller type. If it is configured /*, the priority is high, and the default DefualtServletone will also be intercepted.

The following configuration, interception /*, in the controller hasRequestMapping("/index")

    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>

The page request xxx/index will go DispatcherServlet. When the resource cannot be found, it will not go DefaultServlet. It is equivalent to missing a default servlet.

And Tomcat also has a servlet-mapping, which intercepts the suffix. As long as the request has the suffix, it will go.

Therefore, the general configuration in the project is configuration *.htmlor *.doas a distinction, so that independent

springMvc main process

[SpringMVC process architecture diagram_gmvc diagram](https://blog.csdn.net/menglixiazhiweizhi/article/details/85318012?ops_request_misc=%7B%22request%5Fid%22%3A%22166495705016782417032134%22%2C%22scm% 22 %3A%2220140713.130102334…%22%7D&request_id=166495705016782417032134&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2 all sobaiduend~default-2-853180 12-null-null.142 v51 new_blog_pos_by_title,201 v3 control_1 & utm_term = springmvc graph &spm= 1018.2226.3001.4187)

such as container initialization

There is a listener ContextLoaderListenerTomcat in springMvc that will call this initialization containerinitWebApplicationContext

位置:org.springframework.web.context.ContextLoader#initWebApplicationContext

 public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
    
    
     // 1. 判断是否存在父容器,因为他需要初始化,当然已经存在父容器就是有问题的
        if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
    
    
            throw new IllegalStateException("Cannot initialize context because there is already a root application context present - check whether you have multiple ContextLoader* definitions in your web.xml!");
        } else {
    
    
            // 记录日志
            servletContext.log("Initializing Spring root WebApplicationContext");
            Log logger = LogFactory.getLog(ContextLoader.class);
            if (logger.isInfoEnabled()) {
    
    
                logger.info("Root WebApplicationContext: initialization started");
            }
			// 记录时间
            long startTime = System.currentTimeMillis();

            try {
    
    
                // 创建父容器,也可以说是spring容器
                if (this.context == null) {
    
    
                    this.context = this.createWebApplicationContext(servletContext);
                }

                if (this.context instanceof ConfigurableWebApplicationContext) {
    
    
                    ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)this.context;
                    if (!cwac.isActive()) {
    
    
                        if (cwac.getParent() == null) {
    
    
                            ApplicationContext parent = this.loadParentContext(servletContext);
                            cwac.setParent(parent);
                        }
                        // 接下来看这个方法,设置并初始化spring容器(bean扫描、实例化、后置处理器、国际化等)
                        this.configureAndRefreshWebApplicationContext(cwac, servletContext);
                    }
                }
				// 将如容器设置到servlet上下文
                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 (Error | RuntimeException var8) {
    
    
                logger.error("Context initialization failed", var8);
                servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, var8);
                throw var8;
            }
        }
    }


The method of creating a spring container createWebApplicationContextis as follows

    protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
    
    
        // 读取容器类
        Class<?> contextClass = this.determineContextClass(sc);
        if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
    
    
            throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
        } else {
    
    
            // 实例化
            return (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);
        }
    }

See how the bottom layer reads the container class

    protected Class<?> determineContextClass(ServletContext servletContext) {
    
    
        // 从servlet上下文获取容器类全名
        String contextClassName = servletContext.getInitParameter("contextClass");
        if (contextClassName != null) {
    
    
            try 
                // 不等于空就反射
                return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
            } catch (ClassNotFoundException var4) {
    
    
                throw new ApplicationContextException("Failed to load custom context class [" + contextClassName + "]", var4);
            }
        } else {
    
    
        // 等于空,就从默认配置中获取
            contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());

            try {
    
    
                return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
            } catch (ClassNotFoundException var5) {
    
    
                throw new ApplicationContextException("Failed to load default context class [" + contextClassName + "]", var5);
            }
        }
    }
  1. First, find the container class from the spring container,

  2. Get it reflectively

  3. If not, read from the default location, see the following sentence

    java contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());

    It has a default, look for all references, it is staticdirectly loaded under the block, you can see that it is under the bytecode file directoryContextLoader.properties

        static {
          
          
            try {
          
          
                ClassPathResource resource = new ClassPathResource("ContextLoader.properties", ContextLoader.class);
                defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
            } catch (IOException var1) {
          
          
                throw new IllegalStateException("Could not load 'ContextLoader.properties': " + var1.getMessage());
            }
    
            currentContextPerThread = new ConcurrentHashMap(1);
        }
    

Locate the file location, there is really one

image-20221006210352430

The file content is:

org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext

The class configured in this file is XmlWebApplicationContext, we directly locate the past

back toorg.springframework.web.context.ContextLoader#initWebApplicationContext

                if (this.context instanceof ConfigurableWebApplicationContext) {
    
    
                    ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)this.context;
                    if (!cwac.isActive()) {
    
    
                        if (cwac.getParent() == null) {
    
    
                            ApplicationContext parent = this.loadParentContext(servletContext);
                            cwac.setParent(parent);
                        }
						// 接下来看这个方法,设置并初始化spring容器
                        this.configureAndRefreshWebApplicationContext(cwac, servletContext);
                    }
                }
 protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
    
    
        String configLocationParam;
     // 设置id
        if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
    
    
            configLocationParam = sc.getInitParameter("contextId");
            if (configLocationParam != null) {
    
    
                wac.setId(configLocationParam);
            } else {
    
    
                wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getContextPath()));
            }
        }
	 // 将spring容器作为父容器,将servlet容器作为子容器设置
        wac.setServletContext(sc);
     // 这里的`contextConfigLocation`就是web.xml配置里的那个`contextConfigLocation`,下面给了截图
        configLocationParam = sc.getInitParameter("contextConfigLocation");
        if (configLocationParam != null) {
    
    
            wac.setConfigLocation(configLocationParam);
        }

     // 初始化spring容器的配置到servlet容器
        ConfigurableEnvironment env = wac.getEnvironment();
        if (env instanceof ConfigurableWebEnvironment) {
    
    
            ((ConfigurableWebEnvironment)env).initPropertySources(sc, (ServletConfig)null);
        }
	 // 初始化操作
        this.customizeContext(sc, wac);
     // spring 的启动时调用的初始方法,这个在spring篇章讲过的AnnotationConfigApplicationContext类里
     // 就是做了扫描bean,创建bean,postProcesser,国际化等操作
        wac.refresh();
    }

image-20221006211528757

Child container initialization

DispatcherServletInheritance HttpServletBean, container initialization is done by the init() method.

Location: org.springframework.web.servlet.HttpServletBean#init

public final void init() throws ServletException {
    
    
    // 这里是将servlet的配置信息设置的pvs,往细的看扯到了Tomcat的过程, ̄□ ̄||,之后在仔细看Tomcat
        PropertyValues pvs = new ServletConfigPropertyValues(this.getServletConfig(), this.requiredProperties);
        if (!pvs.isEmpty()) {
    
    
            try {
    
    
                // beanWrapper包装该类
                BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
                // 创建servlet资源加载器
                ResourceLoader resourceLoader = new ServletContextResourceLoader(this.getServletContext());
                // 根据名字看是一个编辑器,在这里的作用是将变量替换,环境加载等操作
                // ResourceEditor 资源编辑器,它是对如:file:E:/xxx/xxx,classpaht:xxxx, ${xxx}等这样的资源进行处理
                bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.getEnvironment()));
                // 这里并没有实现
                this.initBeanWrapper(bw);
                // 这里的方法在spring中也有,就是将属性值设置进去
                bw.setPropertyValues(pvs, true);
            } catch (BeansException var4) {
    
    
                if (this.logger.isErrorEnabled()) {
    
    
                    this.logger.error("Failed to set bean properties on servlet '" + this.getServletName() + "'", var4);
                }

                throw var4;
            }
        }
		// 初始化改servletBean 重点看这个方法,这个是让子类去实现的
        this.initServletBean();
    }

initServletBean()realize, that isFrameworkServlet

	protected final void initServletBean() throws ServletException {
    
    
        // 打日志
		getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");
		if (logger.isInfoEnabled()) {
    
    
			logger.info("Initializing Servlet '" + getServletName() + "'");
		}
        // 时间记录
		long startTime = System.currentTimeMillis();

		try {
    
    
            // 初始化web容器,也就是servlet容器,上文我说的是servlet容器,一样的
			this.webApplicationContext = initWebApplicationContext();
            // 这里是空实现
			initFrameworkServlet();
		}
        
        // 下面就是日志一堆
		catch (ServletException | RuntimeException ex) {
    
    
			logger.error("Context initialization failed", ex);
			throw ex;
		}

		if (logger.isDebugEnabled()) {
    
    
			String value = this.enableLoggingRequestDetails ?
					"shown which may lead to unsafe logging of potentially sensitive data" :
					"masked to prevent unsafe logging of potentially sensitive data";
			logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails +
					"': request parameters and headers will be " + value);
		}

		if (logger.isInfoEnabled()) {
    
    
			logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
		}
	}

位置:org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext

	protected WebApplicationContext initWebApplicationContext() {
    
    
        // 查找当前servlet上下文对应的跟容器(spring容器)
		WebApplicationContext rootContext =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
        // 暂定servlet容器对象为wac
		WebApplicationContext wac = null;

		if (this.webApplicationContext != null) {
    
    
            // 走这里是构造器实例化就已经传入的
			wac = this.webApplicationContext;
			if (wac instanceof ConfigurableWebApplicationContext) {
    
    
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
				if (!cwac.isActive()) {
    
    
                    // 这里它有一个标志 active 这个标志着实例化完,并设置好了web容器需要的环境及工具
					if (cwac.getParent() == null) {
    
    
					// 如果目前获取到的servlet容器是没有父容器的,那么就把刚刚获取到的设置进去
						cwac.setParent(rootContext);
					}
                    // 开始刷新(设置各种容器环境:bean的扫描、注册、国际化等)
                    // 注意,这里在刷新完(设置完)会发布一个`ContextRefreshedEvent`事件
					configureAndRefreshWebApplicationContext(cwac);
				}
			}
		}
        
		if (wac == null) {
    
    
		// 构造器没有传入,它就会到servlet 上下文中找,然后返回
			wac = findWebApplicationContext();
		}
		if (wac == null) {
    
    
			// 如果到这里还没有,就自己创建一个,并刷新(设置容器环境)
            // 注意,这里在刷新完(设置各种容器环境:bean的扫描、注册、国际化等)会发布一个`ContextRefreshedEvent`事件
			wac = createWebApplicationContext(rootContext);
		}

		if (!this.refreshEventReceived) {
    
    
            // 这里判断如果没有刷新,就会在刷新一次,这里再刷新,和`ContextRefreshedEvent`事件监听器里的一样,都是调用子类`DispatcherServlet`的实现方法
            // 事件监听器位置在:org.springframework.web.servlet.FrameworkServlet.ContextRefreshListener#onApplicationEvent
			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;
	}

onRefresh(wac);The actual execution is the following code

位置:org.springframework.web.servlet.DispatcherServlet#initStrategies

You can see that it initializes many parsers: multi-file upload, internationalization, dynamic style, mapping processor, mapping processor adapter, exception handler, view parser...

	protected void initStrategies(ApplicationContext context) {
    
    
		initMultipartResolver(context);
		initLocaleResolver(context);
		initThemeResolver(context);
		initHandlerMappings(context);
		initHandlerAdapters(context);
		initHandlerExceptionResolvers(context);
		initRequestToViewNameTranslator(context);
		initViewResolvers(context);
		initFlashMapManager(context);
	}

In the above demo we simulated HandlerMappingsand handlerAdapters, we focus on it.

request process

When the request comes, tomcat will call DispatcherServletto process, the location is: org.apache.catalina.core.ApplicationFilterChain#internalDoFilter

image-20221012215153251

It also corresponds to: javax.servlet.http.HttpServlet#service(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)

There is no need to post the method code. Its specific function is to request verification and distribution, get method calls doGet, post calls doPostlike this

map processor

In the springMvc flowchart, it can be known that the HandlerAdapter obtains the corresponding mapping processor according to the request, but it is an interface,

Create a request handler

In springMvc, the interface processor:

  1. @Controller @RequestMapping
  2. Controller interface
  3. HttpRequestHandler

Create servlet request

Method 1: Inheritance HttpServlet, rewrite doGet or doPost to process requests, but this is a Tomcat servlet directly used, so you need to configure servlet-mapping in web.xml

public class IndexController2 extends HttpServlet {
    
    
    private static final long serialVersionUID = -2964194399437247271L;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        System.out.println("in httpServlet");
        super.doGet(req, resp);
    }
}
    <servlet>
        <servlet-name>indexController2</servlet-name>
        <servlet-class>com.liry.controller.IndexController2</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>indexController2</servlet-name>
        <url-pattern>/index2.html</url-pattern>
    </servlet-mapping>

Method 2: Implement Controllerthe interface

public class IndexController3 implements Controller {
    
    
    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
    
    
        System.out.println("in controller");
        ModelAndView result = new ModelAndView();
        result.setViewName("index");
        return result;
    }
}

Then configure the mapping

There are also two ways to configure the mapping:

  1. @Component("/index3.html"): Annotation method defines beanName not url
  2. <bean id="/index3.html" class="com.liry.controller.IndexController3"/>This method is the classic xml configuration

Although there are two types, it is actually one type, the two are the same, and the controller is saved as a bean

Method 3:@Controller@RequestMapping

The most common and convenient way is to use annotations. Multiple requests can be processed in one class

HandlerMapping

In the SpringMvc process, this is the request -> DisaptcherServlet -> HandlerAdapter -> HandlerMapping -> hander

It is not difficult to see that after going to the DispatcherServlet, or adapt the corresponding HandlerMapping through the HandlerAdapter, and then use the HandlerMapping to process the request. Here, the HandlerMapping can be regarded as a controller, so we can reverse it through the Adapter.

image-20221013231700422

As you can see from the figure, it has obtained 3 types of HandlerMapping:

  1. RequestMappingHandlerMapping
  2. BeanNameUrlHandlerMapping
  3. SimpleUrlHandlerMapping

RequestMappingHandlerMapping

When we use @Controllerit, it will be scanned by it.

When starting the application, initialize the spring container and go to the callback function:

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#afterPropertiesSet

Then go to the parent class:

org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#initHandlerMethods

image-20221013235759653

getCandidateBeanNames()This method will get the beanName in the IOC container, and then processCandidateBean(beanName)analyze the controller to see the internal method:

	protected void processCandidateBean(String beanName) {
    
    
		Class<?> beanType = null;
		try {
    
    
            // 获取class
			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);
			}
		}
        // 这个是判断是否是一个Controller
		if (beanType != null && isHandler(beanType)) {
    
    
			detectHandlerMethods(beanName);
		}
	}

The judgment condition is existence: @Controller@RequestMapping, here it should be noted that the following judgment is based on ||the premise that @RequestMappingthe marked bean can be scanned by spring, and there is no problem in judging between @Controllerthe current package and one .@Component

[External link picture transfer failed, the source site may have an anti-theft link mechanism, it is recommended to save the picture and upload it directly (img-VkzN9wCB-1665802757031)(E:/ALI/Documents/%E5%BE%85%E5%8F%91 %E5%B8%83%E6%96%87%E7%AB%A0/Typora/typora/images/image-20221014000036670.png)]

Then look at the process of parsing the processor:

protected void detectHandlerMethods(Object handler) {
    
    
   Class<?> handlerType = (handler instanceof String ?
         obtainApplicationContext().getType((String) handler) : handler.getClass());

   if (handlerType != null) {
    
    
      Class<?> userType = ClassUtils.getUserClass(handlerType);
      Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
            // 匿名函数,处理指定class和method对象
            (MethodIntrospector.MetadataLookup<T>) method -> {
    
    
               try {
    
    
                  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);
         registerHandlerMethod(handler, invocableMethod, mapping);
      });
   }
}

The logic of finding the method marked with RequestMapping is as follows:

image-20221014001340091

image-20221014001349850

What it finally found was RequestMapping, but we also used GetMapping, PostMapping, DeleteMappingthese annotations, is there any other way to find it?

Unfortunately not, look at GetMappingthe annotation definition, GetMapping is RequestMappingmarked, then the method marked GetMapping is also marked RequestMapping, and the properties of GetMapping are @AliasForassociated through annotations, so this is why using GetMapping has the same effect as using RequestMapping.

image-20221014001618706

You can still take a look at its internal method logic. It uses the object parsed by the method RequestMappingInfoas the value and the method as the key, and stores it in the map.

public static <T> Map<Method, T> selectMethods(Class<?> targetType, final MetadataLookup<T> metadataLookup) {
    
    
   final Map<Method, T> methodMap = new LinkedHashMap<>();
   Set<Class<?>> handlerTypes = new LinkedHashSet<>();
   Class<?> specificHandlerType = null;

    // 判断是否是代理类
   if (!Proxy.isProxyClass(targetType)) {
    
    
      specificHandlerType = ClassUtils.getUserClass(targetType);
      handlerTypes.add(specificHandlerType);
   }
    // 获取所有的接口的class
   handlerTypes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetType));

   for (Class<?> currentHandlerType : handlerTypes) {
    
    
// 这里还不太懂,代理对象要这样处理
      final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType);

      ReflectionUtils.doWithMethods(currentHandlerType, method -> {
    
    
         Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
         T result = metadataLookup.inspect(specificMethod);
         if (result != null) {
    
    
            Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
            if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) {
    
    
               methodMap.put(specificMethod, result);
            }
         }
      }, ReflectionUtils.USER_DECLARED_METHODS);
   }

   return methodMap;
}

Most have mapping (url) as the key, registered in the mapperRegistry, the internal division pathLookup, nameLookup, corsLookup, the only way to get the handler is to get it pathLookUpfrom

image-20221014005302132

SimpleUrlHandlerMapping

Here is one more thing to mention. At the beginning, we briefly explained tomcat: Tomcat starts to read web.xml, first reads the application's web.xml, and then merges Tomcat's web. Two Servlets are configured in it, one is DefaultServlet, the other is JspServlt, and the two handle static resources and jsp pages respectively.

And here SimpleUrlHandlerMapping is for processing static resources, here are 4, namely:

  1. /css/**
  2. /js/**
  3. /image/**
  4. /

The first three are corresponding to the static resource mapping configured in spring-mvc.xml:

    <!--静态资源映射-->
    <!--本项目把静态资源放在了webapp的statics目录下,资源映射如下-->
    <mvc:resources mapping="/css/**" location="/statics/css/"/>
    <mvc:resources mapping="/js/**" location="/statics/js/"/>
    <mvc:resources mapping="/image/**" location="/statics/images/"/>
    <mvc:default-servlet-handler/>  <!--这句要加上,要不然可能会访问不到静态资源,具体作用自行百度-->

And the last one is the DefaultServlet interception configured in web.xml in tomcat, as the last interception.

BeanNameUrlHandlerMapping

As the name suggests, this is a processor that uses beanName as url mapping. The configuration method of creating a processor above is already understandable. It uses beanName as the url interception address.

The parent classes of SimpleURLHandlerMapping and BeanNameURLHandlerMapping are bothorg.springframework.web.servlet.handler.AbstractDetectingUrlHandlerMapping

They all belong to the method of matching processors through url addresses, but the final processed objects are different, resulting in different implementations

Summarize

HandlerMapping can be classified into two ways,

  1. Scan the specified annotation as the request handler's identifier
  2. Scan for beans and /use beans whose beanName starts with

HandlerAdapter

Corresponding to HandlerMapping, it also has 3 Adapters

  1. RequestMappingHandlerAdapter

  2. HttpRequestHandlerAdapter

  3. SimpleControllerHandlerAdapter

位置:org.springframework.web.servlet.DispatcherServlet#getHandlerAdapter

It will traverse the Adapter, and then adapter.supports()judge whether the Adapter supports the handler by calling. supportsThe method is no longer suspenseful.

image-20221014011305206

After that, reflection execution is called HandlerAdatper.handler, which is one of its main processes.

demo - Simulate the main process of SpringMvc

Based on what we know above, write a springMvc based on spring container + Tomcat, just try it

demo address

Guess you like

Origin blog.csdn.net/qq_28911061/article/details/127332585