SpringMVC源码解析-DispatcherServlet启动流程和初始化

 在使用springmvc框架,会在web.xml文件配置一个DispatcherServlet,这正是web容器开始初始化,同时会在建立自己的上下文来持有SpringMVC的bean对象。

先从DispatcherServlet入手,从名字来看,它是一个Servlet。它的定义如下:

public class DispatcherServlet extends FrameworkServlet {

    它是继承FrameworkServlet,来看一下整个的继承关系。

从继承关系来看,DispatcherServlet继承FrameworkServlet和HttpServletBean而继承HttpServlet,通过使用Servlet API 来对HTTP请求进行响应,成为SpringMVC的前端处理器。

先看一个时序图

注:作为Servlet,DispatcherServlet的启动和Servlet的启动相关联的。在Servlet初始化过程中,Servlet的init方法会被调用,以进行初始化,然而DispatcherServlet的基类,所以从HttpServletBean中的初始化过程开始。      

        DispatcherServlet的工作分为2部分,一部分是初始化(也就是图的上半部分),有initServletBean()启动,通过initWebApplicationContext()方法最终调用DispatcherServlet中的initStrategies()方法。另一部分(也就是图的下半部分),是对HTTP请求进行响应,作为Servlet,Web容器会调用Servlet的doGet()和doPost()方法,在经过FrameworkServlet的processRequest()简单处理后,会调用DispatcherServlet的doService方法,在这个方法调用中封装了doDispatch(),继续调用processDispatchResult方法返回调用信息。

接下来看看初始化的源码的流程:

HttpServletBean.init方法

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

/**

     * 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 {

        if (logger.isDebugEnabled()) {

            logger.debug("Initializing servlet '" + getServletName() + "'");

        }

          //获取Servlet初始化参数

        // Set bean properties from init parameters.

        try {

            PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);

            BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);

            ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());

            bw.registerCustomEditor(Resource.classnew 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;

        }

                //调用子类的方法

        // Let subclasses do whatever initialization they like.

        initServletBean();

 

        if (logger.isDebugEnabled()) {

            logger.debug("Servlet '" + getServletName() + "' configured successfully");

        }

    }

  例如web.xml中配置参数:

1

2

3

4

5

6

7

8

9

10

11

12

13

<servlet> 

    <servlet-name>TestServlet</servlet-name> 

    <servlet-class>com.lzyer.TestServlet</servlet-class

    <!--配置参数,可以通过ServletConfig获取参数-->

    <init-param> 

        <param-name>servlet-name</param-name> 

        <param-value>TestServlet</param-value> 

    </init-param> 

 </servlet> 

 <servlet-mapping> 

    <servlet-name>TestServlet</servlet-name> 

    <url-pattern>/TestServlet</url-pattern> 

  </servlet-mapping>

  FrameworkServlet.initServletBean方法

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

/**

     * Overridden method of {@link HttpServletBean}, invoked after any bean properties

     * have been set. Creates this servlet's WebApplicationContext.

     */

    @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");

        }

    }

  FrameworkServlet.initWebApplicationContext()

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

/**

     * 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) {

                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) {

                        // 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.

            onRefresh(wac);

        }

 

        if (this.publishContext) {

            // Publish the context as a servlet context attribute.

            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;

    }

  先看WebApplicationContextUtils.getWebApplicationContext(getServletContext()),,主要是从ServletContext获取WebApplicationContext

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

public static WebApplicationContext getWebApplicationContext(ServletContext sc, String attrName) {

        Assert.notNull(sc, "ServletContext must not be null");<br>                //从ServletContext中获取

        Object attr = sc.getAttribute(attrName);

        if (attr == null) {

            return null;

        }

        if (attr instanceof RuntimeException) {

            throw (RuntimeException) attr;

        }

        if (attr instanceof Error) {

            throw (Error) attr;

        }

        if (attr instanceof Exception) {

            throw new IllegalStateException((Exception) attr);

        }

        if (!(attr instanceof WebApplicationContext)) {

            throw new IllegalStateException("Context attribute is not of type WebApplicationContext: " + attr);

        }

        return (WebApplicationContext) attr;

    }

 findWebApplicationContext和上面一样从ContextServlet中查找,如果不存在就调用下面的createWebApplicationContext方法。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {<br>          //获取contextClass

        Class<?> contextClass = getContextClass();

        if (this.logger.isDebugEnabled()) {

            this.logger.debug("Servlet with name '" + getServletName() +

                    "' will try to create custom WebApplicationContext context of class '" +

                    contextClass.getName() + "'" ", using parent context [" + parent + "]");

        }

        if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {

            throw new ApplicationContextException(

                    "Fatal initialization error in servlet with name '" + getServletName() +

                    "': custom WebApplicationContext class [" + contextClass.getName() +

                    "] is not of type ConfigurableWebApplicationContext");

        }<br>                //通过反射获取实例

        ConfigurableWebApplicationContext wac =

                (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

 

        wac.setEnvironment(getEnvironment());<br>                //设置双亲上下文

        wac.setParent(parent);

        wac.setConfigLocation(getContextConfigLocation());

                //配置和刷新应用

        configureAndRefreshWebApplicationContext(wac);

 

        return wac;

    }

  看一下getContextClass()到底是哪个类? XmlWebApplicationContext.class

最后configureAndRefreshWebApplicationContext调用refresh方法启动容器。

回到initWebApplicationContext方法中

1

2

3

4

5

6

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);

        }

  这个会触发SpringMVC初始化策略

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

/**

     * This implementation calls {@link #initStrategies}.

     */

    @Override

    protected void onRefresh(ApplicationContext context) {

        initStrategies(context);

    }

 

    /**

     * Initialize the strategy objects that this servlet uses.

     * <p>May be overridden in subclasses in order to initialize further strategy objects.

     */

    protected void initStrategies(ApplicationContext context) {

        initMultipartResolver(context);

        initLocaleResolver(context);

        initThemeResolver(context);<br>                //映射关系

        initHandlerMappings(context);

        initHandlerAdapters(context);

        initHandlerExceptionResolvers(context);

        initRequestToViewNameTranslator(context);

        initViewResolvers(context);

        initFlashMapManager(context);

    }

  到此,SpringMVC的初始化的流程大概就是这样,下篇就是SpringMVC请求流程。

   

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值