美文网首页
SpringMVC源码分析一

SpringMVC源码分析一

作者: 吾问无为谓123 | 来源:发表于2019-07-15 21:37 被阅读0次
    入口分析

    tomcat启动的时候会加载web.xml,对于spring项目来说一个简单的web.xml通常为如下格式:

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
             version="4.0">
    
        <!-- Spring mvc分发servlet -->
        <servlet>
            <servlet-name>dispatcherServlet</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:applicationContext.xml</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>
    
        <servlet-mapping>
            <servlet-name>dispatcherServlet</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    
        <!-- 欢迎页,默认进入index.jsp -->
        <welcome-file-list>
            <welcome-file>index.jsp</welcome-file>
        </welcome-file-list>
    </web-app>
    

    文件中有一个Servlet,所以Servlet 即为程序的入口。

    DispatcherServlet 分析

    通过下图可以看出DispatcherServlet 继承了 FrameworkServlet,HttpServletBean,HttpServlet,GenericServlet


    DispatcherServlet继承实现关系.png DispatcherServlet Diagrams.png
    我们思考下一个Servlet是如何加载的?
    Servlet Structure.png

    调用顺序 init() ---> service() ---> destory() , 由于DispatcherServlet方法是Servlet的实现类,所以其必然有对应的init方法,但是经过分析发现DispatcherServlet方法中以及其父类FrameworkServlet方法中都没有init方法,直在HttpServletBean中才发现init方法。代码如下:

      /**
        * Map config parameters onto bean properties of this servlet, and
        * invoke subclass initialization.
        * @throws ServletException if bean properties are invalid (or required
        * properties are missing), or if subclass initialization fails.
        */
       @Override
       public final void init() throws ServletException {
    
           // Set bean properties from init parameters.
           // 第一步:根据bean属性设置,设置初始化参数。getServletConfig为Tomcat帮我们实现,主要是Servlet的信息,如Servlet名字等,requiredProperties为new HashSet表示必然要出现的属性
           // 这个步骤的主要目的是为了获取设置的init-param中的name 和 value, 并将其狗造成PropertyValue 放至MutablePropertyValues 的成员变量propertyValueList 中
           PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
    
           if (!pvs.isEmpty()) {
               try {
                   // 第二步:创建一个BeanWrapper对象,里面包装了一个DispatcherServlet对象
                   BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
                   //  第三步:获取资源加载对象
                   ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
                   // 第四步:给bw注册一个客户自定义的属性编辑器,主要是做类型转换
                   bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
                   // 第五步:initBeanWrapper,spring 默认没有实现
                   initBeanWrapper(bw);
                   // 第六步:为bean设置属性值,底层是利用的反射
                   bw.setPropertyValues(pvs, true);
               }
               catch (BeansException ex) {
                   if (logger.isErrorEnabled()) {
                       logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
                   }
                   throw ex;
               }
           }
    
           // Let subclasses do whatever initialization they like.
           // 第七步:调用子类去实现spring 容器初始化(关键步骤!)
           initServletBean();
       }
    

    我们进行逐步分析:
    第一步:根据bean属性设置,设置初始化参数。getServletConfig为Tomcat帮我们实现,主要是Servlet的信息,如Servlet名字等,requiredProperties为new HashSet

            PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
    
    /**
             * Create new ServletConfigPropertyValues.
             * 创建一个新的ServletConfig属性值
             * @param config the ServletConfig we'll use to take PropertyValues from
             * @param requiredProperties set of property names we need, where
             * we can't accept default values
             * @throws ServletException if any required properties are missing
             */
            public ServletConfigPropertyValues(ServletConfig config, Set<String> requiredProperties)
                    throws ServletException {
    
                Set<String> missingProps = (!CollectionUtils.isEmpty(requiredProperties) ?
                        new HashSet<>(requiredProperties) : null);
    
                // 获取到配置的初始化参数
                /*
                    config 可以理解为,web.xml中的配置信息
                    <servlet>
                        <servlet-name>dispatcherServlet</servlet-name>
                        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
                        <init-param>
                            <param-name>contextConfigLocation</param-name>
                            <param-value>classpath:applicationContext.xml</param-value>
                        </init-param>
                        <load-on-startup>1</load-on-startup>
                    </servlet>
    
                    此处就为获取到  <init-param> 下的信息 为tomcat实现,我们暂时不做分析。
                 */
                Enumeration<String> paramNames = config.getInitParameterNames();
    
                while (paramNames.hasMoreElements()) {
                    // 获取参数名字
                    String property = paramNames.nextElement();
                    // 获取参数value
                    Object value = config.getInitParameter(property);
                    // 将name和value构成PropertyValue,放至MutablePropertyValues 的成员变量propertyValueList 中,但是并未使用其返回值
                    addPropertyValue(new PropertyValue(property, value));
                    if (missingProps != null) {
                        missingProps.remove(property);
                    }
                }
    
                // Fail if we are still missing properties.
                // 如果丢失了必然出现的属性则抛出异常。
                if (!CollectionUtils.isEmpty(missingProps)) {
                    throw new ServletException(
                            "Initialization from ServletConfig for servlet '" + config.getServletName() +
                            "' failed; the following required properties were missing: " +
                            StringUtils.collectionToDelimitedString(missingProps, ", "));
                }
            }
    

    第二步:创建一个BeanWrapper对象,里面包装了一个DispatcherServlet对象

    第三步:获取资源加载对象

    第四步:给bw注册一个客户自定义的属性编辑器,主要是做类型转换

    第五步:initBeanWrapper,spring 默认没有实现

    第六步:为bean设置属性值,底层是利用的反射

    第七步:调用子类去实现spring 容器初始化(关键步骤!)

    相关文章

      网友评论

          本文标题:SpringMVC源码分析一

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