• spring mvc之启动过程源码分析


    • 简介

    这两个星期都在看spring mvc源码,看来看去还是还是很多细节没了解清楚,在这里把看明白的记录下,欢迎在评论中一起讨论。

    一、铺垫

    spring mvc是基于servlet的,在正式分析之前,我们来看一下servlet的知识。servlet的生命周期通过三个方法init、service、destory来构建的。

    • init():

    在Servlet的生命周期中,仅执行一次init()方法。它是在服务器装入Servlet时执行的,负责初始化Servlet对象。可以配置服务器,以在启动服务器或客户机首次访问Servlet时装入Servlet。无论有多少客户机访问Servlet,都不会重复执行init()。

    • service():

    它是Servlet的核心,负责响应客户的请求。每当一个客户请求一个HttpServlet对象,该对象的Service()方法就要调用,而且传递给这个方法一个“请求”(ServletRequest)对象和一个“响应”(ServletResponse)对象作为参数。在HttpServlet中已存在Service()方法。默认的服务功能是调用与HTTP请求的方法相应的do功能。

    • destroy():

    仅执行一次,在服务器端停止且卸载Servlet时执行该方法。当Servlet对象退出生命周期时,负责释放占用的资源。一个Servlet在运行service()方法时可能会产生其他的线程,因此需要确认在调用destroy()方法时,这些线程已经终止或完成。

    二、spring mvc启动过程

    spring mcv的入口是DispatcherServlet,顾名思义就是调度servlet是服务请求调度,它的继承结构如下:

     

    在整合spring mvc时,web.xml有这样配置,相信用过的都知道

    <servlet>
        <servlet-name>mvc-dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
          <param-name>contextConfigLocation</param-name>
          <param-value>classpath:mvc-dispatcher.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
      </servlet>
      <servlet-mapping>
        <servlet-name>mvc-dispatcher</servlet-name>
        <url-pattern>*.do</url-pattern>
      </servlet-mapping>

    那根据servlet的知识,在服务器启动,创建dispatcherServlet对象,会执行init方法,根据DispaterServlet的继承关系,找到init方法在HttpServletBean中,下面我们来看一下这个

    方法

    public final void init() throws ServletException {
            if (logger.isDebugEnabled()) {
                logger.debug("Initializing servlet '" + getServletName() + "'");
            }
    
            // Set bean properties from init parameters.
            try {
                /*PropertyValuesz是封装在web.xml配置servlet参数信息
                 * <init-param>
                    <param-name>contextConfigLocation</param-name>
                    <param-value>classpath:mvc-dispatcher.xml</param-value>
                  </init-param>
                */
                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);
                //将配置的初始化值设置到DispatcherServlet
                bw.setPropertyValues(pvs, true);
            }
            catch (BeansException ex) {
                logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
                throw ex;
            }
    
            //初始化spring mvc容器方法webApplicationContext,由子类的FrameworkServlet来实现
            initServletBean();
    
            if (logger.isDebugEnabled()) {
                logger.debug("Servlet '" + getServletName() + "' configured successfully");
            }
            }

    2.1、接下来看FrameworkServlet的initServletBean(),主要调了initWebApplicationContext()方法

    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 {
                //初始化spring mcv容器
                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");
            }
        }
    2.2、initWebApplicationContext方法
     protected WebApplicationContext initWebApplicationContext() {
            /*
              在spring启动过程中,ContextLoaderListener回监听到,实例化IoC容器,并将此容器实例注册到ServletContext中,现在把IOC容器取出来
             */
            WebApplicationContext rootContext =
                    WebApplicationContextUtils.getWebApplicationContext(getServletContext());
            WebApplicationContext wac = null;
    
            if (wac == null) {
                //把IOC容器传进去,创建spring mvc自己的容器
                wac = createWebApplicationContext(rootContext);
            }
            return wac;
            if (this.publishContext) {
                
                String attrName = getServletContextAttributeName();
                //把创建好的spring mcv自己的容器设置到ServletContext容器中
                getServletContext().setAttribute(attrName, wac);
                
            }
        }
    2.3、createWebApplicationContext方法
     protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
            /*根据web.xml中的配置决定使用何种WebApplicationContext。默认情况下使用XmlWebApplicationContext
               web.xml中相关的配置context-param的名称“contextClass” */
            Class<?> contextClass = getContextClass();
            //获得spring mvc自己容器
            ConfigurableWebApplicationContext wac =
                    (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
    
            wac.setEnvironment(getEnvironment());
            //指定父容器为IOC容器
            wac.setParent(parent);
            //指定spring mcv 核心配置文件的位置classpath:mvc-dispatcher.xml
            wac.setConfigLocation(getContextConfigLocation());
            //spring mcv 容器继续初始化
            configureAndRefreshWebApplicationContext(wac);
    
            return wac;
        }

     2.4、配置和刷新spring mvc自己容器方法configureAndRefreshWebApplicationContext(wac),就是针对spring mcv容器对象一些属性,然后初始化

    protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
            //设置ServletContext
            wac.setServletContext(getServletContext());
            //设置ServletConfig
            wac.setServletConfig(getServletConfig());

    postProcessWebApplicationContext(wac);
            applyInitializers(wac);
            wac.refresh(); }

    2.5、接下来,我们看看刷新方法,做了什么事

    public void refresh() throws BeansException, IllegalStateException {
            //完成Bean工厂初始化
            finishBeanFactoryInitialization(beanFactory);
    //完成刷新spring mvc自己的容器功能
            finishRefresh();
    }

    在这里主要看完成BeanFactory初始化工作

    2.6、接下来我们看看初始化BeanFactory工作finishBeanFactoryInitialization,主要看到实例化单例对象

    protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
            //实例化单例对象
            beanFactory.preInstantiateSingletons();
        }

    2.7、接下来实例化单例对象方法preInstantiateSingletons()

        public void preInstantiateSingletons() throws BeansException {
            //获得所有定义好beanNames名称
            List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);
            for (String beanName : beanNames) {
                RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
                if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
                    if (isFactoryBean(beanName)) {
                        final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
                        boolean isEagerInit;
                        if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
                            isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
                                @Override
                                public Boolean run() {
                                    return ((SmartFactoryBean<?>) factory).isEagerInit();
                                }
                            }, getAccessControlContext());
                        }
                        else {
                            isEagerInit = (factory instanceof SmartFactoryBean &&
                                    ((SmartFactoryBean<?>) factory).isEagerInit());
                        }
                        if (isEagerInit) {
                            getBean(beanName);
                        }
                    }
                    else {
                        getBean(beanName);
                    }
                }
            }
            
            for (String beanName : beanNames) {
                Object singletonInstance = getSingleton(beanName);
                if (singletonInstance instanceof SmartInitializingSingleton) {
                    final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
                    if (System.getSecurityManager() != null) {
                        AccessController.doPrivileged(new PrivilegedAction<Object>() {
                            @Override
                            public Object run() {
                                smartSingleton.afterSingletonsInstantiated();
                                return null;
                            }
                        }, getAccessControlContext());
                    }
                    else {
                        smartSingleton.afterSingletonsInstantiated();
                    }
                }
            }
        }
    这个方法beanNames有以下

    是通过getBean(beanName)来实例化对象的,在这里我主要讲spring mvc实例化RequestMappingHandlerMapping做了什么事,不要急,我们先来看一下,RequestMappingHandlerMapping的继承结构



     
     可以看到RequestMappingHandleMapping继承了AbstractHandlerMethodMapping,在初始化的时候,会执行到AbstractHandlerMethodMapping的构造方法,看看
    AbstractHandlerMethodMapping这个

     实现了InitializingBean,所以这个类在初始化完成时会执行到afterPropertiesSet()方法

    2.8、接下来我们看看initHandlerMethods方法到底做了什么事

     protected void initHandlerMethods() {
            //获得所有的beanNames
            String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
                    BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
                    getApplicationContext().getBeanNamesForType(Object.class));
            //遍历取出每个beanName
            for (String beanName : beanNames) {
                if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
                    Class<?> beanType = null;
                    //获得BeanName对应的Class对象
                    beanType = getApplicationContext().getType(beanName);
                    }
                    //判断这个Class时候否有@Controller和@RequestMapping()
                    if (beanType != null && isHandler(beanType)) {
                        //如果类上含有@Controller和@RequestMapping()注解,执行探测处理器方法
    detectHandlerMethods(beanName); } } } handlerMethodsInitialized(getHandlerMethods()); }

    2.9、接下来我们看看detectHandlerMethods(beanName)这个方法到底干了什么事

    protected void detectHandlerMethods(final Object handler) {
            //传进来的handle是个字符串类型的BeanName,首先根据Bean名称创建Class对象
            Class<?> handlerType = (handler instanceof String ?
                    getApplicationContext().getType((String) handler) : handler.getClass());
            final Class<?> userType = ClassUtils.getUserClass(handlerType);
            //获得Controller类对应methods对应的Map
            Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
                    new MethodIntrospector.MetadataLookup<T>() {
                        @Override
                        public T inspect(Method method) {
                            return getMappingForMethod(method, userType);
                        }
                    });
    
            //遍历Map,取出Controller类的每一个方法和对应的Mapping
            for (Map.Entry<Method, T> entry : methods.entrySet()) {
                Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType);
                T mapping = entry.getValue();
                registerHandlerMethod(handler, invocableMethod, mapping);
            }
        }
    下图是Map<Method, T> methods的值

    可以看到T封装了Controller类的请求方式,和匹配条件等等数据

    2.10、接下来我们来看看spring mvc是怎么注册Controller方法

    registerHandlerMethod(handler, invocableMethod, mapping)
    protected void registerHandlerMethod(Object handler, Method method, T mapping) {
            this.mappingRegistry.register(mapping, handler, method);
        }

    2.11、接下来我们看看mappingRegistry的register方法

    public void register(T mapping, Object handler, Method method) {
            //拿到写锁
            this.readWriteLock.writeLock().lock();
            try {
                //根据handler(是你的controller类的类名)和controller的method方法创建handlerMethod对象
                HandlerMethod handlerMethod = createHandlerMethod(handler, method);
                //存储到名称为mappingLookup的Map中
                this.mappingLookup.put(mapping, handlerMethod);
                //然后从mapping中取出请求url
                List<String> directUrls = getDirectUrls(mapping);
                for (String url : directUrls) {
                    //然后我们再把url和mapping存储到MultiValueMap类型的urlLookup中,所以我们可以通过url找到mapping,再通过mapping找到handleMethod
                    this.urlLookup.add(url, mapping);
                }
                 //把mapping和appingRegistration存储到HashMap类型的registry中
                this.registry.put(mapping, new AbstractHandlerMethodMapping.MappingRegistration<T>(mapping, handlerMethod, directUrls, name));
            }
            finally {
                //释放写锁
                this.readWriteLock.writeLock().unlock();
            }
        }

    这里解释下传进来3个参数 mapping封装了匹配条件,请求方式等数据,handler是我们的controller类名(字符串),method(controller类的方法对象)

    2.12、接下来我们回到的2.8的initHandlerMethods方法

    protected void initHandlerMethods() {
            //获得所有的beanNames
            String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
                    BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
                    getApplicationContext().getBeanNamesForType(Object.class));
            //遍历取出每个beanName
            for (String beanName : beanNames) {
                if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
                    Class<?> beanType = null;
                    //获得BeanName对应的Class对象
                    beanType = getApplicationContext().getType(beanName);
                    }
                    //判断这个Class时候否有@Controller和@RequestMapping()
                    if (beanType != null && isHandler(beanType)) {
                        //如果类上含有@Controller和@RequestMapping()注解,执行探测处理器方法
    
                        detectHandlerMethods(beanName);
                    }
                }
            }
            handlerMethodsInitialized(getHandlerMethods());
        }

    执行完了detectHandlerMethods(beanName);方法,继续往下执行handlerMethodsInitialized(getHandlerMethods())方法,这个方法是个 是个模板 方法,什么都没执行,到这里

    public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
            @Override
            public void afterPropertiesSet() {
                initHandlerMethods();
            }
        }

    afterPropertiesSet方法就算执行完了。

    2.13、接下来执行继续回到2.7的实例化单例对象方法preInstantiateSingletons();

              接下没什么什么好说,preInstantiateSingletons()结束后,finishBeanFactoryInitialization()方法也就结束了,回到了refresh(),继续完成刷新spring mvc自己的容器功能

    public void refresh() throws BeansException, IllegalStateException {
            //完成Bean工厂初始化
            finishBeanFactoryInitialization(beanFactory);
             //完成刷新spring mvc自己的容器功能
            finishRefresh();
    }

    2.14、finishRefresh();这一步会触发一个ApplicationEvent: 

      protected void finishRefresh() {
           //其中this是指XmlWebApplicationContext对象
            publishEvent(new ContextRefreshedEvent(this));
            
        

    2.15、接下来解析初始化DispatcherServlet类的各种成员变量

    protected void onRefresh(ApplicationContext context) {
            initStrategies(context);
        }
       protected void initStrategies(ApplicationContext context) {
            initMultipartResolver(context);
            initLocaleResolver(context);
            initThemeResolver(context);
            //初始化handlerMappings
            initHandlerMappings(context);
            //handlerAdapters
            initHandlerAdapters(context);
            initHandlerExceptionResolvers(context);
            initRequestToViewNameTranslator(context);
            //初始化视图解析器
            initViewResolvers(context);
            initFlashMapManager(context);
        }

    2.16、接下来回到2.1的FrameworkServlet的initServletBean()方法中,然后initServletBean()执行完之后,HttpServletBean中的init初始化方法也就执行完了,到这里spring mvc启动的源码已经分析完成,

    希望你看会有收获。

    三、总结

    其实分析完spring mvc自己启动的源码会发现,大部分内容都在构建spring mcv自己的容器,构建完成之后将容器放到SevletContext中

  • 相关阅读:
    2018牛客网暑期ACM多校训练营(第九场)A -Circulant Matrix(FWT)
    ZOJ
    BZOJ 4318 OSU!(概率DP)
    POJ
    POJ
    Linux安装及管理程序
    Linux目录及文件管理
    linux账号管理操作
    linux系统命令总结
    linux目录及文件管理操作
  • 原文地址:https://www.cnblogs.com/520playboy/p/8351696.html
Copyright © 2020-2023  润新知