美文网首页
SpringMVC的实现与Web环境

SpringMVC的实现与Web环境

作者: appreciate_2018 | 来源:发表于2019-04-17 16:28 被阅读0次
    • IOC容器在web环境初始化过程

    SpringMVC是建立在IOC容器的基础上的,SpringIOC是一个独立的模块,需要在web环境中引入SpringIOC并启动。通常我们会在web.xml配置。

    <servlet>
            <servlet-name>spring</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <load-on-startup>1</load-on-startup>
    </servlet>
    
    <servlet-mapping>
            <servlet-name>spring</servlet-name>
            <url-pattern>/</url-pattern>
    </servlet-mapping>
    
    <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath*:applicationContext.xml</param-value>
    </context-param>
    
    <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    

    DispatcherServlet:Springmvc的前端控制器:初始化mvc组件与分发请求。
    ContextLoaderListener:负责监听IOC容器在web环境初始化的整个过程。
    context-param:SpringIOC读取bean的路径。

    ContextLoaderListener创建的上下文为根上下文,同时还有一个输入MVC的子上下文。

    public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
        public ContextLoaderListener() {
        }
    
        public ContextLoaderListener(WebApplicationContext context) {
            super(context);
        }
    
        public void contextInitialized(ServletContextEvent event) {
            this.initWebApplicationContext(event.getServletContext());
        }
    
        public void contextDestroyed(ServletContextEvent event) {
            this.closeWebApplicationContext(event.getServletContext());
            ContextCleanupListener.cleanupAttributes(event.getServletContext());
        }
    }
    

    ContextLoaderListener继承了ContextLoader并实现了ServletContextListener,这个监听器是启动根IOC容器并把它载入web容器的主要模块,首先从servlet事件中得到servletContext,然后可以读取配置在web.xml中的各个相关值,接着Contextloader会实例化WebapplicationContext,并完成载入跟初始化过程,这个被初始化的上下文为根上下文。获取根上下文的方法:

     WebApplicationContext w = WebApplicationContextUtils.getWebApplicationContext(ServletContext sc);
    

    ServletContextListener是ServlettListener的监听者,当服务启动或结束时,会调用相对应的回调方法

    public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
        public ContextLoaderListener() {
        }
    
        public ContextLoaderListener(WebApplicationContext context) {
            super(context);
        }
    
        public void contextInitialized(ServletContextEvent event) {
            this.initWebApplicationContext(event.getServletContext());
        }
    
        public void contextDestroyed(ServletContextEvent event) {
            this.closeWebApplicationContext(event.getServletContext());
            ContextCleanupListener.cleanupAttributes(event.getServletContext());
        }
    }
    

    在initWebApplicationContext中,根上下文创建成功时,会存在web容器的ServletContext中供需要时使用。而当根上下文创建好时,Web容器开始初始化DispatcherServlet,DispatcherServlet会建立自己当上下文来持有SpringMVC的bean对象。

    • DispatcherServlet的初始化过程

    DispatcherServlet的继承关系(引用spring技术内幕).png DispatcherServlet的处理过程(引用spring技术内幕).png

    通过时序图可看到DispatcherServlet初始化是在httpServletBean完成的

    public final void init() throws ServletException {
            if (logger.isDebugEnabled()) {
                logger.debug("Initializing servlet '" + getServletName() + "'");
            }
    
            //从初始化参数,配置bean属性  
            try {
                PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
                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) {
                logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
                throw ex;
            }
    
            //调用子类来初始化bean
            initServletBean();
    
            if (logger.isDebugEnabled()) {
                logger.debug("Servlet '" + getServletName() + "' configured successfully");
            }
        }
    
    @Override
        protected final void initServletBean() throws ServletException {
            getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
            if (this.logger.isInfoEnabled()) {
                this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
            }
            long startTime = System.currentTimeMillis();
    
            try {
                    //初始化web上下文
                this.webApplicationContext = initWebApplicationContext();
                initFrameworkServlet();
            }
            catch (ServletException ex) {
                this.logger.error("Context initialization failed", ex);
                throw ex;
            }
            catch (RuntimeException ex) {
                this.logger.error("Context initialization failed", ex);
                throw ex;
            }
    
            if (this.logger.isInfoEnabled()) {
                long elapsedTime = System.currentTimeMillis() - startTime;
                this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
                        elapsedTime + " ms");
            }
        }
    
    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) {
                            //使用根上下文为mvc上下文的双亲上下文
                            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.
                onRefresh(wac);
            }
    
            if (this.publishContext) {
                // 把当前上下文放到servletContext中
                String attrName = getServletContextAttributeName();
                getServletContext().setAttribute(attrName, wac);
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
                            "' as ServletContext attribute with name [" + attrName + "]");
                }
            }
    
            return wac;
        }
    

    springMVC的上下文则是在initFrameworkServlet()初始化的,主要是将根上下文传递给它,然后利用反射来实例化上下文对象。最终调用IOC容器的refresh()方法来完成容器的初始化。

    • DispatcherServlet具体做了什么事?

    由上面的时序图中可看到在DispatcherServlet中,主要做了两件事:
    1.initStrategies():初始化MVC。
    2.doDispatch():对http请求进行分发。

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

    主要对initHandlerMapping初始化进行分析,HandlerMapping主要是为http请求找到相对应的Controller控制器,在initHandlerMappings中把bean配置文件中配置好的HandlerMapping载入到IOC容器中。

    private void initHandlerMappings(ApplicationContext context) {
            this.handlerMappings = null;
    
            if (this.detectAllHandlerMappings) {
                // 从所有的上下文中获取HandlerMapping
                Map<String, HandlerMapping> matchingBeans =
                        BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
                if (!matchingBeans.isEmpty()) {
                          //保存HandlerMapping到list中
                    this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
                    //对HandlerMapping并排序
                    AnnotationAwareOrderComparator.sort(this.handlerMappings);
                }
            }
            else {
                try {
                          //从上下文信息里根据handler名称来获取HandlerMapping
                    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.
                }
            }
    
            
            //通过注册确保我们至少有一个HandlerMapping
           //如果未找到其他映射,则为默认的HandlerMapping。
            if (this.handlerMappings == null) {
                this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
                if (logger.isDebugEnabled()) {
                    logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
                }
            }
        }
    

    HandlerMapping接口主要维护着handler跟url的映射关系,我们来分析下HandlerMapping的主要实现。

    public interface HandlerMapping {
        HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
    }
    

    HandlerMapping里只有一个方法getHandler(),这个方法返回了一个HandlerExecutionChain,这个HandlerExecutionChain持有一个拦截器链跟handler,通过拦截器链来对handler进行增强功能。HandlerExecutionChain里定义对handler跟interceptors需要在定义HandlerMapping时配置好,那么我们来分析一下这个注册的过程。

    @Override
        public void initApplicationContext() throws BeansException {
            super.initApplicationContext();
            registerHandlers(this.urlMap);
    }
    protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {
            if (urlMap.isEmpty()) {
                logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");
            }
            else {
                for (Map.Entry<String, Object> entry : urlMap.entrySet()) {
                    String url = entry.getKey();
                    Object handler = entry.getValue();
                    // Prepend with slash if not already present.
                    if (!url.startsWith("/")) {
                        url = "/" + url;
                    }
                    // Remove whitespace from handler bean name.
                    if (handler instanceof String) {
                        handler = ((String) handler).trim();
                    }
                                  //调用基类来完成注册
                    registerHandler(url, handler);
                }
            }
        }
    

    具体的注册是在基类AbstractUrlHandlerMapping里完成的

    protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
            Assert.notNull(urlPath, "URL path must not be null");
            Assert.notNull(handler, "Handler object must not be null");
            Object resolvedHandler = handler;
    
            // 如果是用bean名称定义,那么直接从容器中获取
            if (!this.lazyInitHandlers && handler instanceof String) {
                String handlerName = (String) handler;
                if (getApplicationContext().isSingleton(handlerName)) {
                    resolvedHandler = getApplicationContext().getBean(handlerName);
                }
            }
    
            Object mappedHandler = this.handlerMap.get(urlPath);
            if (mappedHandler != null) {
                if (mappedHandler != resolvedHandler) {
                    throw new IllegalStateException(
                            "Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +
                            "]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");
                }
            }
            else {
                if (urlPath.equals("/")) {
                    if (logger.isInfoEnabled()) {
                        logger.info("Root mapping to " + getHandlerDescription(handler));
                    }
                    setRootHandler(resolvedHandler);
                }
                else if (urlPath.equals("/*")) {
                    if (logger.isInfoEnabled()) {
                        logger.info("Default mapping to " + getHandlerDescription(handler));
                    }
                    setDefaultHandler(resolvedHandler);
                }
                else {
                              //将url跟controller设置到handlerMap中
                    this.handlerMap.put(urlPath, resolvedHandler);
                    if (logger.isInfoEnabled()) {
                        logger.info("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription(handler));
                    }
                }
            }
        }
      //存放url跟controller
    private final Map<String, Object> handlerMap = new LinkedHashMap<String, Object>();
    

    这个时候已经为SpringMVC分发http请求准备好了基础数据,根据handlerMap中的url映射到具体的handler,我们来看一下HandlerMapping是如何完成请求映射的。是如何找到handler的。

    Override
        public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
            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 = getApplicationContext().getBean(handlerName);
            }
    
            HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
            if (CorsUtils.isCorsRequest(request)) {
                CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);
                CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
                CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
                executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
            }
            return executionChain;
        }
    //具体实现在AbstractUrlHandlerMapping类的getHandlerInternal()里面,
    @Override
        protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
              //从request中获取url
            String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
              //将url跟handler进行匹配
            Object handler = lookupHandler(lookupPath, request);
            if (handler == null) {
                // We need to care for the default handler directly, since we need to
                // expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.
                Object rawHandler = null;
                if ("/".equals(lookupPath)) {
                    rawHandler = getRootHandler();
                }
                if (rawHandler == null) {
                    rawHandler = getDefaultHandler();
                }
                if (rawHandler != null) {
                    // Bean name or resolved handler?
                    if (rawHandler instanceof String) {
                        String handlerName = (String) rawHandler;
                        rawHandler = getApplicationContext().getBean(handlerName);
                    }
                    validateHandler(rawHandler, request);
                    handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
                }
            }
            if (handler != null && logger.isDebugEnabled()) {
                logger.debug("Mapping [" + lookupPath + "] to " + handler);
            }
            else if (handler == null && logger.isTraceEnabled()) {
                logger.trace("No handler mapping found for [" + lookupPath + "]");
            }
            return handler;
        }
    
    protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
            // Direct match?
            Object handler = this.handlerMap.get(urlPath);
            if (handler != null) {
                // Bean name or resolved handler?
                if (handler instanceof String) {
                    String handlerName = (String) handler;
                    handler = getApplicationContext().getBean(handlerName);
                }
                validateHandler(handler, request);
                return buildPathExposingHandler(handler, urlPath, urlPath, null);
            }
            // Pattern match?
            List<String> matchingPatterns = new ArrayList<String>();
            for (String registeredPattern : this.handlerMap.keySet()) {
                if (getPathMatcher().match(registeredPattern, urlPath)) {
                    matchingPatterns.add(registeredPattern);
                }
                else if (useTrailingSlashMatch()) {
                    if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) {
                        matchingPatterns.add(registeredPattern +"/");
                    }
                }
            }
            String bestPatternMatch = null;
            Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
            if (!matchingPatterns.isEmpty()) {
                Collections.sort(matchingPatterns, patternComparator);
                if (logger.isDebugEnabled()) {
                    logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);
                }
                bestPatternMatch = matchingPatterns.get(0);
            }
            if (bestPatternMatch != null) {
                handler = this.handlerMap.get(bestPatternMatch);
                if (handler == null) {
                    Assert.isTrue(bestPatternMatch.endsWith("/"));
                    handler = this.handlerMap.get(bestPatternMatch.substring(0, bestPatternMatch.length() - 1));
                }
                // Bean name or resolved handler?
                if (handler instanceof String) {
                    String handlerName = (String) handler;
                    handler = getApplicationContext().getBean(handlerName);
                }
                validateHandler(handler, request);
                String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestPatternMatch, urlPath);
    
                // There might be multiple 'best patterns', let's make sure we have the correct URI template variables
                // for all of them
                Map<String, String> uriTemplateVariables = new LinkedHashMap<String, String>();
                for (String matchingPattern : matchingPatterns) {
                    if (patternComparator.compare(bestPatternMatch, matchingPattern) == 0) {
                        Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);
                        Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
                        uriTemplateVariables.putAll(decodedVars);
                    }
                }
                if (logger.isDebugEnabled()) {
                    logger.debug("URI Template variables for request [" + urlPath + "] are " + uriTemplateVariables);
                }
                return buildPathExposingHandler(handler, bestPatternMatch, pathWithinMapping, uriTemplateVariables);
            }
            // No handler found...
            return null;
        }
    

    具体的http分发是由dispatcherServlet的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 {
                 //先设置视图模型
               ModelAndView mv = null;
               Exception dispatchException = null;
    
               try {
                   processedRequest = checkMultipart(request);
                   multipartRequestParsed = (processedRequest != request);
    
                   // 根据请求获取Handler.
                   mappedHandler = getHandler(processedRequest);
                   if (mappedHandler == null || mappedHandler.getHandler() == null) {
                       noHandlerFound(processedRequest, response);
                       return;
                   }
    
                   // 利用HandlerAdapter检查handler的合法性
                   HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    
                   // Process last-modified header, if supported by the handler.
                   String method = request.getMethod();
                   boolean isGet = "GET".equals(method);
                   if (isGet || "HEAD".equals(method)) {
                       long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                       if (logger.isDebugEnabled()) {
                           logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
                       }
                       if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                           return;
                       }
                   }
    
                   if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                       return;
                   }
    
                   // 真正完成handler的调用
                   mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    
                   if (asyncManager.isConcurrentHandlingStarted()) {
                       return;
                   }
    
                   applyDefaultViewName(processedRequest, mv);
                   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 NestedServletException("Handler dispatch failed", err);
               }
               processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
           }
           catch (Exception ex) {
               triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
           }
           catch (Throwable err) {
               triggerAfterCompletion(processedRequest, response, mappedHandler,
                       new NestedServletException("Handler processing failed", 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);
                   }
               }
           }
       }
    
    springMVC的流程(图片引用spring技术内幕)

    相关文章

      网友评论

          本文标题:SpringMVC的实现与Web环境

          本文链接:https://www.haomeiwen.com/subject/wbuziqtx.html