SpringMVC原理(设计原理+启动原理+工作原理)

前言

本系列文章基本环境如下:

  • java8
  • springboot2.7

创建项目,使用阿里的源:https://start.aliyun.com
创建前,请先设置好自己的maven环境,java版本。保持网络正常。
在这里插入图片描述
选择spring-web的2.7.6 版本:
在这里插入图片描述在这里插入图片描述
使用阿里的源创建好项目后,会自动生成的有控制器Controller,启动类,以及一个页面。
如此,准备工作就做好了。

正文

在Java还没有SpringMvc时,使用的是servlet + jsp 的方式,对外提供接口,以及和页面进行数据交互等操作。
但是,这种操作,毕竟还是不方便,功能也不够强大。
曾经的写法,需要配置xml文件,如果页面够多,光配置就是一大堆。

发展到后来,servlet3.0的时候,出现了完全注解版的写法。

关于servlet的描述这里不做过多解释,本文将对对springmvc中使用到的servlet特征进行阐述,继而分析它的设计原理,springmvc启动原理,以及工作原理

等到在Spring框架中的时候,就已经是DispatcherServlet了。

而它本身就是一个servlet,其类关系图如下:
在这里插入图片描述

一、设计原理

1.1 servlet生命周期简述

Servlet生命周期分为三个阶段:

  1. 初始化阶段:调用init()方法实现初始化工作。
  2. 运行阶段:处理请求,容器创建代表HTTP请求的ServletRequest对象和代表HTTP响应的ServletResponse对象,并将它们作为参数传递给Servletservice()方法。
  3. 销毁阶段:Servlet将被销毁,生命周期结束。

Servlet本身只是一个接口,在HttpServlet实现类中,对service()方法进行了实现。
而这里的实现是套用了模版方法设计模式,将service的职责拆分了,按照请求方法的类型不同划分。

比如,如果请求方法是 GET请求,则会执行到 HttpServletdoGet方法;如果是POST请求,则会执行到 HttpServletdoPost方法。

FrameworkServlet 又对HttpServlet中的service方法进行了重写:

protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
    if (httpMethod != HttpMethod.PATCH && httpMethod != null) {
        super.service(request, response);
    } else {
        this.processRequest(request, response);
    }

}

这里判断了请求方法,默认执行 HttpServletservice方法。
但是实际调用的是 doGetdoPost这类方法。而同时,这类方法也被FrameworkServlet重写了

也就是说,servlet 会触发执行到 FrameworkServletprocessRequest 方法。如下图所示:在这里插入图片描述
这里会执行FrameworkServletdoService方法。而这是一个抽象方法。其子类DispatcherServlet 对其进行了实现。如此便贯通了。

1.2 设计原理小结

在1.1小节中的分析中,Servlet继承&实现的关系如下:
在这里插入图片描述
也就是说,在servlet处理请求时,对于springmvc而言,就是执行 doService方法。

二、启动原理

这一小节,主要分析SpringBoot项目启动时,对SpringMvc部分的处理。

2.1 AbstractHandlerMethodMapping 初始化 --RequestMapping注解解析

谈起SpringMvc,最先想起来的俩注解应该是 ControllerRequestMapping
而关于启动项目时,框架对这俩注解的处理,基本都体现在AbstractHandlerMethodMapping中。

public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
	// ...省略其他方法
	
	public void afterPropertiesSet() {
        this.initHandlerMethods();
    }
}

首先,AbstractHandlerMethodMapping是个抽象类,它的子类会放到Spring容器中。
而在它的子类 RequestMappingHandlerMapping 中,对初始化方法进行了重写,具体内容如下:

public void afterPropertiesSet() {
    this.config = new RequestMappingInfo.BuilderConfiguration();
    this.config.setTrailingSlashMatch(this.useTrailingSlashMatch());
    this.config.setContentNegotiationManager(this.getContentNegotiationManager());
    if (this.getPatternParser() != null) {
        this.config.setPatternParser(this.getPatternParser());
        Assert.isTrue(!this.useSuffixPatternMatch && !this.useRegisteredSuffixPatternMatch, "Suffix pattern matching not supported with PathPatternParser.");
    } else {
        this.config.setSuffixPatternMatch(this.useSuffixPatternMatch());
        this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch());
        this.config.setPathMatcher(this.getPathMatcher());
    }

    super.afterPropertiesSet();
}

在设置了一堆配置之后,最终调用的还是 AbstractHandlerMethodMappinginitHandlerMethods 方法。


    protected void initHandlerMethods() {
    	// 获取spring容器中的beanName
        String[] var1 = this.getCandidateBeanNames();
        int var2 = var1.length;

        for(int var3 = 0; var3 < var2; ++var3) {
            String beanName = var1[var3];
            if (!beanName.startsWith("scopedTarget.")) {
            	// 通过beanName映射出methodHandler
                this.processCandidateBean(beanName);
            }
        }

		// 初始化handlerMethods
        this.handlerMethodsInitialized(this.getHandlerMethods());
    }

  
    protected void processCandidateBean(String beanName) {
        Class<?> beanType = null;

        try {
        	// 通过beanName获取当前的类型
            beanType = this.obtainApplicationContext().getType(beanName);
        } catch (Throwable var4) {
            if (this.logger.isTraceEnabled()) {
                this.logger.trace("Could not resolve type for bean '" + beanName + "'", var4);
            }
        }

		// 当前beanName对应的类定义不为空,并且带有Controller 或 RequestMapping注解时,对其进行处理
        if (beanType != null && this.isHandler(beanType)) {
            this.detectHandlerMethods(beanName);
        }

    }

可以看到,最终处理控制器时,是调用了detectHandlerMethods 方法。具体内容如下:

protected void detectHandlerMethods(Object handler) {
	// 通过beanName获取到对应的类型
    Class<?> handlerType = handler instanceof String ? this.obtainApplicationContext().getType((String)handler) : handler.getClass();
    if (handlerType != null) {
    	// 获取你自己定义的控制器类型
        Class<?> userType = ClassUtils.getUserClass(handlerType);
        // 将类中符合条件(标注了RequestMapping注解)的 method 映射为 RequestMappingInfo 对象,并放入map中;这一步的实现,在其子类中。并且聚合它们的请求路径。
        Map<Method, T> methods = MethodIntrospector.selectMethods(userType, (method) -> {
            try {
                return this.getMappingForMethod(method, userType);
            } catch (Throwable var4) {
                throw new IllegalStateException("Invalid mapping on handler class [" + userType.getName() + "]: " + method, var4);
            }
        });
        
		// 记录日志
        if (this.logger.isTraceEnabled()) {
            this.logger.trace(this.formatMappings(userType, methods));
        } else if (this.mappingsLogger.isDebugEnabled()) {
            this.mappingsLogger.debug(this.formatMappings(userType, methods));
        }

		// 方法注册
        methods.forEach((method, mapping) -> {
            Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
            this.registerHandlerMethod(handler, invocableMethod, mapping);
        });
    }

}

这里对方法的处理分了两步,第一步,根据类型简单处理注解,主要是聚合了请求路径。聚合后的结果如下:

可以看到,这里的路径属性已经有值了。
然后就是注册方法了。
而真正注册的方法registerHandlerMethod 是在其子类中实现的。具体内容如下:
在这里插入图片描述

在其父级的实现中,注册的结果如下:
在这里插入图片描述

最后,简单处理RequestBody注解(如果使用了该注解,参数必填)。

2.2 DispatcherServlet 的初始化

框架中定义了自动配置类 DispatcherServletAutoConfiguration。其有个内部类 DispatcherServletConfiguration 对 DispatcherServlet 进行了配置。具体如下:

@Conditional({DefaultDispatcherServletCondition.class})
@ConditionalOnClass({ServletRegistration.class})
@EnableConfigurationProperties({WebMvcProperties.class})
protected static class DispatcherServletConfiguration {
    protected DispatcherServletConfiguration() {
    }

    @Bean(
        name = {"dispatcherServlet"}
    )
    public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {
        DispatcherServlet dispatcherServlet = new DispatcherServlet();
        dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
        dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
        dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
        dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
        dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());
        return dispatcherServlet;
    }

    @Bean
    @ConditionalOnBean({MultipartResolver.class})
    @ConditionalOnMissingBean(
        name = {"multipartResolver"}
    )
    public MultipartResolver multipartResolver(MultipartResolver resolver) {
        return resolver;
    }
}

在第一节设计原理的时候,提到过DispatcherServlet 也是一个servlet 。那么它的初始化,也就包含在servlet的生命周期中。

在servlet生命周期中,有 init 方法,进行初始化。FrameworkServlet 有一个父类HttpServletBean,其中定义了初始化方法。虽然不是原生的servlet 初始化方法,但是也是会间接调用到的(通过模版方法设计模式,由子类代为实现)

public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware 

FrameworkServlet 触发初始化时,会执行到 initServletBean 方法。其中有两个比较关键初始化方法,具体如下:
在这里插入图片描述
篇幅原因,这里只拿出关键代码。initWebApplicationContext()内,有这样一段,在处理请求时,会执行到:

if (!this.refreshEventReceived) {
      synchronized(this.onRefreshMonitor) {
            this.onRefresh(wac);
      }
}

onRefresh 由子类DispatcherServlet 重写后,就成了这样:

protected void onRefresh(ApplicationContext context) {
 	this.initStrategies(context);
}

protected void initStrategies(ApplicationContext context) {
    // 初始化上传文件解析器
    initMultipartResolver(context);
   // 初始化本地解析器
    initLocaleResolver(context);
   // 主题处理器
    initThemeResolver(context);
   // 映射处理器
    initHandlerMappings(context);
   // 处理适配器
    initHandlerAdapters(context);
   // 异常处理器
    initHandlerExceptionResolvers(context);
   // 请求到视图名的翻译器
    initRequestToViewNameTranslator(context);
   // 视图解析器
    initViewResolvers(context);
   // 初始化FlashManager
    initFlashMapManager(context);
}

如果大家想看看这里执行的内容,以及初始化后的结果,可以自行打断点查看。这里因为东西较多,我就不截图了。

PS: 下一小节以 HandlerMappings 为例,进行说明

2.3 DispatcherServlet#initHandlerMappings(…) 初始化示例说明

private void initHandlerMappings(ApplicationContext context) {
    this.handlerMappings = null;
    if (this.detectAllHandlerMappings) {
    	// 获取所有的handlerMapping
        Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
        // 对handlerMapping进行排序
        if (!matchingBeans.isEmpty()) {
            this.handlerMappings = new ArrayList(matchingBeans.values());
            AnnotationAwareOrderComparator.sort(this.handlerMappings);
        }
    } else {
        try {
        	// 获取名字是 handlerMapping 的handlerMapping
            HandlerMapping hm = (HandlerMapping)context.getBean("handlerMapping", HandlerMapping.class);
            this.handlerMappings = Collections.singletonList(hm);
        } catch (NoSuchBeanDefinitionException var4) {
        }
    }
	
	// handlerMappings为空,需要设置默认的handlerMapping
    if (this.handlerMappings == null) {
        this.handlerMappings = this.getDefaultStrategies(context, HandlerMapping.class);
        if (this.logger.isTraceEnabled()) {
            this.logger.trace("No HandlerMappings declared for servlet '" + this.getServletName() + "': using default strategies from DispatcherServlet.properties");
        }
    }

    Iterator var6 = this.handlerMappings.iterator();

    while(var6.hasNext()) {
        HandlerMapping mapping = (HandlerMapping)var6.next();
        if (mapping.usesPathPatterns()) {
            this.parseRequestPath = true;
            break;
        }
    }
}

默认情况下,如果配置了使用全部,会有以下handlerMapping:
在这里插入图片描述
另外补充一句,如果使用默认的handlerMapping,需要配置DispatcherServlet.properties

三、工作原理

这一小节,以DispatcherServlet 为起点,分析SpringMvc的工作原理。

3.1 DispatcherServlet处理请求流程图

官方的示意图如下:
在这里插入图片描述

个人理解:
在这里插入图片描述

3.2 请求分发

依据模版方法设计模式,在类FrameworkServlet 中,重写了 doPostdoGet 等方法。
其内部调用了processRequest 方法,这是一个 final 的方法,内部主要调用了 doService(request, response) 方法。其在FrameworkServlet 中的定义为:

protected abstract void doService(HttpServletRequest request, HttpServletResponse response)
			throws Exception;

doService 请求分发方法由 DispatcherServlet 实现。也就是本文要说明核心的处理方法。而 doService 的核心处理是 doDispatch(request, response)

3.3 获取HandlerMapping

DispatcherServlet#getHandler(...) 方法,获取HandlerMapping 并生成 HandlerExecutionChain实例。
在这里插入图片描述

@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
	// 当前初始化的handlerMappings不为空,没有特殊配置的情况下会有以下5个实例:
	// RequestMappingHandlerMapping、WelcomePageHandlerMapping、BeanNameHandlerMapping、RouterFunctionMapping、ResourceHandlerMapping
    if (this.handlerMappings != null) {
        Iterator var2 = this.handlerMappings.iterator();

        while(var2.hasNext()) {
        	// 获取RequestMappingHandlerMapping 实例
            HandlerMapping mapping = (HandlerMapping)var2.next();
            // 获取执行器链,内部依据request的路径,获取HandlerMethod实例,最终获得执行器链HandlerExecutionChain 实例
            HandlerExecutionChain handler = mapping.getHandler(request);
            if (handler != null) {
                return handler;
            }
        }
    }

    return null;
}

获取执行器链的具体方法是:AbstractHandlerMapping#getHandler(request)。这里不做过多分析。感兴趣的可以自行查看。

3.4 获取处理器适配器

DispatcherServlet 中的 getHandlerAdapter(...) 用于获取处理器适配器。

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
	// 获取当前的所有适配器
    if (this.handlerAdapters != null) {
        Iterator var2 = this.handlerAdapters.iterator();

        while(var2.hasNext()) {
            HandlerAdapter adapter = (HandlerAdapter)var2.next();
            // 适配器支持当前的HandlerMethod
            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");
    }

handlerAdapters 有4个值:RequestMappingHandlerAdapterHandlerFunctionAdapterHttpRequestHandlerAdapterSimpleControllerHandlerAdapter

3.5 拦截器预处理请求

执行器链HandlerExecutionChain执行 applyPreHandle。具体如下:

    boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
        for(int i = 0; i < this.interceptorList.size(); this.interceptorIndex = i++) {
            HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i);
            // 拦截器预处理
            if (!interceptor.preHandle(request, response, this.handler)) {
                this.triggerAfterCompletion(request, response, (Exception)null);
                return false;
            }
        }

        return true;
    }

3.6 处理器适配器执行handl(…)

这里调用的是 HandlerAdapterhandle 方法。AbstractHandlerMethodAdapter 重写了该方法,具体如下:

    @Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    return this.handleInternal(request, response, (HandlerMethod)handler);
}

@Nullable
protected abstract ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception;

RequestMappingHandlerAdapter 为例,它实现了handleInternal,如下:

protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    this.checkRequest(request);
    ModelAndView mav;
    if (this.synchronizeOnSession) {
        HttpSession session = request.getSession(false);
        if (session != null) {
            Object mutex = WebUtils.getSessionMutex(session);
            synchronized(mutex) {
                mav = this.invokeHandlerMethod(request, response, handlerMethod);
            }
        } else {
            mav = this.invokeHandlerMethod(request, response, handlerMethod);
        }
    } else {
        mav = this.invokeHandlerMethod(request, response, handlerMethod);
    }

    if (!response.containsHeader("Cache-Control")) {
        if (this.getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
            this.applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
        } else {
            this.prepareResponse(response);
        }
    }

    return mav;
}

3.7 执行器链执行拦截器

HandlerExecutionChain 执行applyPostHandle

void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {
    for(int i = this.interceptorList.size() - 1; i >= 0; --i) {
        HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i);
        interceptor.postHandle(request, response, this.handler, mv);
    }

}

3.8 执行 render()渲染视图

这个方法是 doDispatch 核心代码的最后一环。
执行,DispatcherServlet 定义的私有方法processDispatchResult,触发 render 方法。

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception {
    boolean errorView = false;
    if (exception != null) {
        if (exception instanceof ModelAndViewDefiningException) {
            this.logger.debug("ModelAndViewDefiningException encountered", exception);
            mv = ((ModelAndViewDefiningException)exception).getModelAndView();
        } else {
            Object handler = mappedHandler != null ? mappedHandler.getHandler() : null;
            mv = this.processHandlerException(request, response, handler, exception);
            errorView = mv != null;
        }
    }

    if (mv != null && !mv.wasCleared()) {
    	// 渲染视图
        this.render(mv, request, response);
        if (errorView) {
            WebUtils.clearErrorRequestAttributes(request);
        }
    } else if (this.logger.isTraceEnabled()) {
        this.logger.trace("No view rendering, null ModelAndView returned.");
    }

    if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
        if (mappedHandler != null) {
            mappedHandler.triggerAfterCompletion(request, response, (Exception)null);
        }

    }
}

3.9 工作原理小结

这里做一个DispatcherServlet 中的 doDIspatch 方法的执行流程小结。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你家宝宝

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值