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中

posted @ 2018-01-25 16:02  坏~牧羊人  阅读(389)  评论(0编辑  收藏  举报