likes
comments
collection
share

浅尝SpringMVC原理

作者站长头像
站长
· 阅读数 33

MVC原理

如何使用mvc

实现WebApplicationInitializer,然后自己创建容器

 public class MyWebApplicationInitializer implements WebApplicationInitializer {
 ​
     @Override
     public void onStartup(ServletContext servletContext) {
 ​
         // Load Spring web application configuration
         AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
         context.register(AppConfig.class);
 ​
         // Create and register the DispatcherServlet
         DispatcherServlet servlet = new DispatcherServlet(context);
         ServletRegistration.Dynamic registration = servletContext.addServlet("app", servlet);
         registration.setLoadOnStartup(1);
         registration.addMapping("/app/*");
     }
 }

下面是超快使用教程

 public class QuickStarter  extends AbstractAnnotationConfigDispatcherServletInitializer {
     @Override
     protected Class<?>[] getRootConfigClasses() {
         return new Class[0];//ioc容器的配置
     }
 ​
     @Override
     protected Class<?>[] getServletConfigClasses() {
         return new Class[0];//mvc容器的配置
     }
 ​
     @Override
     protected String[] getServletMappings() {
         return new String[0];//映射路径
     }
 }

原理分析

     /**
      将 委托 ServletContext 给应用程序类路径上存在的任何 WebApplicationInitializer 实现。
 因为这个类声明了 @HandlesTypes(WebApplicationInitializer.class),所以 Servlet 容器会自动扫描类路径以查找 Spring WebApplicationInitializer 接口的实现,并将所有此类类型的 webAppInitializerClasses 集合提供给此方法的参数。
 如果在类路径上找不到 WebApplicationInitializer 任何实现,则此方法实际上是无操作的。将发出 INFO 级日志消息,通知用户确实已调用, ServletContainerInitializer 但未 WebApplicationInitializer 找到任何实现。
 假设检测到一种或多种WebApplicationInitializer类型,它们将被实例化(如果存在 @@Order 注解或Ordered接口已实现,则对其进行排序)。然后,将在每个实例上调用该WebApplicationInitializer.onStartup(ServletContext)方法,ServletContext委托每个实例可以注册和配置 Servlet(如 Spring)、DispatcherServlet侦听器(如 Spring)ContextLoaderListener或任何其他 Servlet API 功能(如过滤器)。
 指定者:
 onStartup 在界面中 ServletContainerInitializer
 参数:
 webAppInitializerClasses – 在应用程序类路径上找到的所有实现WebApplicationInitializer
 servletContext – 要初始化的 servlet 上下文
 抛出:
 ServletException
 另请参见:
 WebApplicationInitializer.onStartup(ServletContext), AnnotationAwareOrderComparator
      */
     @Override
     public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
             throws ServletException {
 ​
         List<WebApplicationInitializer> initializers = Collections.emptyList();
 ​
         if (webAppInitializerClasses != null) {
             initializers = new ArrayList<>(webAppInitializerClasses.size());
             for (Class<?> waiClass : webAppInitializerClasses) {
                 // Be defensive: Some servlet containers provide us with invalid classes,
                 // no matter what @HandlesTypes says...
                 if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
                         WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
                     try {
                         initializers.add((WebApplicationInitializer)
                                 ReflectionUtils.accessibleConstructor(waiClass).newInstance());
                     }
                     catch (Throwable ex) {
                         throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
                     }
                 }
             }
         }
 ​
         if (initializers.isEmpty()) {
             servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
             return;
         }
 ​
         servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
         AnnotationAwareOrderComparator.sort(initializers);
         for (WebApplicationInitializer initializer : initializers) {
             initializer.onStartup(servletContext);//调用客户自定义方法
         }
     }
 ​
  1. 利用spi机制加载SpringServletContainerInitializer
  2. 调用实现了WebApplicationInitializer的方法

继承AbstractAnnotationConfigDispatcherServletInitializer的原理:

  1. 注册监听器registerContextLoaderListener(servletContext);

  2. 创建一个AnnotationConfigWebApplicationContext容器(Spring的ioc

    1. 准备一个监听器->初始化web容器
  3. 注册DispatchServlet

    1. 又一个容器AnnotationConfigWebApplicationContext(mvc配置类)
    2. new一个DispatcherServlet
  4. 在Tomcat的流程中继续进行

    1. Spring容器会在监听器中进行初始化
    2. web容器会在Dispatchservlet初始化函数中进行初始化(设置父子容器

DispatcherServlet

继承树: 浅尝SpringMVC原理

父子容器

浅尝SpringMVC原理

 /**
  * Initialize and publish the WebApplicationContext for this servlet.
  * <p>Delegates to {@link #createWebApplicationContext} for actual creation
  * of the context. Can be overridden in subclasses.
  * @return the WebApplicationContext instance
  * @see #FrameworkServlet(WebApplicationContext)
  * @see #setContextClass
  * @see #setContextConfigLocation
  */
 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 cwac && !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) {
         // No context instance was injected at construction time -> see if one
         // has been registered in the servlet context. If one exists, it is assumed
         // that the parent context (if any) has already been set and that the
         // user has performed any initialization such as setting the context id
         wac = findWebApplicationContext();
     }
     if (wac == null) {
         // No context instance is defined for this servlet -> create a local one
         wac = createWebApplicationContext(rootContext);
     }
 ​
     if (!this.refreshEventReceived) {
         // Either the context is not a ConfigurableApplicationContext with refresh
         // support or the context injected at construction time had already been
         // refreshed -> trigger initial onRefresh manually here.
         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;
 }

核心源码

doService->doDispatch

八大核心组件

Servlet 栈的 Web 应用 何时初始化: 在spring容器刷新完成之后,有个事件,在此初始化各大组件 flashAttribute 重定向还携带数据====

doDispatch分析
 @SuppressWarnings("deprecation")
     protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
         HttpServletRequest processedRequest = request;
         HandlerExecutionChain mappedHandler = null;
         boolean multipartRequestParsed = false;//文件上传标志位(判断是不是进行文件上传
 ​
         WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
 ​
         try {
             ModelAndView mv = null;
             Exception dispatchException = null;
 ​
             try {
                 processedRequest = checkMultipart(request);
                 multipartRequestParsed = (processedRequest != request);
                 // 获取所有handler 来源于controller(并且里面封装了interceptors)
                 // Determine handler for the current request.
                 mappedHandler = getHandler(processedRequest);
                 if (mappedHandler == null) {
                     noHandlerFound(processedRequest, response);
                     return;
                 }
 //   获取处理 request 的处理器适配器 HandlerAdapter
                 // Determine handler adapter for the current request.
                 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
 ​
                 // Process last-modified header, if supported by the handler.
                 String method = request.getMethod();
                 boolean isGet = HttpMethod.GET.matches(method);
                 if (isGet || HttpMethod.HEAD.matches(method)) {
                     long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                     if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                         return;
                     }
                 }
                  // 4.拦截器的预处理方法
                 if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                     return;
                 }
             // 5.实际的处理器处理请求,返回结果视图对象
                 // Actually invoke the handler.
                 mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
 ​
                 if (asyncManager.isConcurrentHandlingStarted()) {
                     return;
                 }
 ​
               // 结果视图对象的处理
                 applyDefaultViewName(request, mv);
                 // 6.拦截器的后处理方法
                 mappedHandler.applyPostHandle(processedRequest, response, mv);
             }
             catch (Exception ex) {
                 dispatchException = ex;
             }
             catch (Throwable err) {
                 // As of 4.3, we're processing Errors thrown from handler methods as well,
                 // making them available for @ExceptionHandler methods and other scenarios.
                 dispatchException = new ServletException("Handler dispatch failed: " + err, err);
             }
             processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
         }
         catch (Exception ex) {
             triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
         }
         catch (Throwable err) {
             triggerAfterCompletion(processedRequest, response, mappedHandler,
                     new ServletException("Handler processing failed: " + err, err));
         }
         finally {
             if (asyncManager.isConcurrentHandlingStarted()) {
                 // Instead of postHandle and afterCompletion
                 if (mappedHandler != null) {
                     mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                 }
             }
             else {
                 // Clean up any resources used by a multipart request.
                 if (multipartRequestParsed) {
                     cleanupMultipart(processedRequest);
                 }
             }
         }
     }

获取handler

 @Nullable
 protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
     if (this.handlerMappings != null) {
         for (HandlerMapping mapping : this.handlerMappings) {
             HandlerExecutionChain handler = mapping.getHandler(request);
             if (handler != null) {
                 return handler;
             }
         }
     }
     return null;
 }

spring自带的三个映射器(bean的名字,;@requestMapping注解 还有响应式编程的) 浅尝SpringMVC原理

然后找适配器 真正执行请求方法(分离) 浅尝SpringMVC原理

核心方法: 配置参数解析器和返回值处理器

 @SuppressWarnings("deprecation")
     @Nullable
     protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
             HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
 ​
         ServletWebRequest webRequest = new ServletWebRequest(request, response);
         WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);//用于表单等数据的绑定 数据类型转换等
         ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
 ​
         ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
         if (this.argumentResolvers != null) {//参数解析器
             invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
         }
         if (this.returnValueHandlers != null) {//返回值解析器
             invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
         }
         invocableMethod.setDataBinderFactory(binderFactory);
         invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
 ​
         ModelAndViewContainer mavContainer = new ModelAndViewContainer();
         mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
         modelFactory.initModel(webRequest, mavContainer, invocableMethod);
         mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
 ​
         AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
         asyncWebRequest.setTimeout(this.asyncRequestTimeout);
 ​
         WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
         asyncManager.setTaskExecutor(this.taskExecutor);
         asyncManager.setAsyncWebRequest(asyncWebRequest);
         asyncManager.registerCallableInterceptors(this.callableInterceptors);
         asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
 ​
         if (asyncManager.hasConcurrentResult()) {
             Object result = asyncManager.getConcurrentResult();
             mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
             asyncManager.clearConcurrentResult();
             LogFormatUtils.traceDebug(logger, traceOn -> {
                 String formatted = LogFormatUtils.formatValue(result, !traceOn);
                 return "Resume with async result [" + formatted + "]";
             });
             invocableMethod = invocableMethod.wrapConcurrentResult(result);
         }
 //真正执行目标方法
         invocableMethod.invokeAndHandle(webRequest, mavContainer);
         if (asyncManager.isConcurrentHandlingStarted()) {
             return null;
         }
 ​
         return getModelAndView(mavContainer, modelFactory, webRequest);
     }

最后执行 浅尝SpringMVC原理

附上一张我自己整理的图

浅尝SpringMVC原理

转载自:https://juejin.cn/post/7386360803039805467
评论
请登录