03.SpringMVC 初始化 - FrameworkServlet

基本概念

FrameWrokServlet 继承自 HttpServletBean ,继承关系如下:

这里写图片描述

它实现了 HttpServletBean 的 initServletBean 方法。说明它也承担着 SpringMVC 的部分初始化工作。


原理分析

首先来看 FrameWrokServlet 类的 initServletBean 方法:

protected final void initServletBean() throws ServletException {
    // 省略部分代码...
    try {
        // 关键 -> 初始化容器
        this.webApplicationContext = initWebApplicationContext();

        // 空方法
        initFrameworkServlet();
    }catch (ServletException ex) {
        // 抛出异常...
    }catch (RuntimeException ex) {
        // 抛出异常...
    }
}

继承追踪到它的 initWebApplicationContext 方法:

// 表示 SpringMVC 容器
private WebApplicationContext webApplicationContext;

// 表示是否发布上下文属性
private boolean publishContext = true;

// 表示 onRefresh 是否执行过
private boolean refreshEventReceived = false;

protected WebApplicationContext initWebApplicationContext() {

    // 1.取得根容器
    WebApplicationContext rootContext= 
        WebApplicationContextUtils.getWebApplicationContext(getServletContext());

    WebApplicationContext wac = null;

    // 2.取得 SpringMVC 容器
    if (this.webApplicationContext != null) {
        wac = this.webApplicationContext;
        if (wac instanceof ConfigurableWebApplicationContext) {
            ConfigurableWebApplicationContext cwac = 
                (ConfigurableWebApplicationContext) wac;
            if (!cwac.isActive()) {
                if (cwac.getParent() == null) {
                    cwac.setParent(rootContext);
                }
                configureAndRefreshWebApplicationContext(cwac);
            }
        }
    }

    // 3.寻找 SpringMVC 容器
    if (wac == null) {
        wac = findWebApplicationContext();
    }

    // 4.创建 SpringMVC 容器
    if (wac == null) {
        wac = createWebApplicationContext(rootContext);
    }

    // 5.重刷新 SpringMVC 容器
    if (!this.refreshEventReceived) {
        // 空方法
        onRefresh(wac);
    }

    // 6.发布 SpringMVC 容器
    if (this.publishContext) {
        String attrName = getServletContextAttributeName();
        getServletContext().setAttribute(attrName, wac);
        // 省略部分代码...
    }

    return wac;
}

分析代码,可以知道 FrameWrokServlet 的初始化过程主要与 SpringMVC 容器有关,具体步骤为:

  • 取得 根容器
  • 取得 SpringMVC 容器
  • 寻找 SpringMVC 容器
  • 创建 SpringMVC 容器
  • 重刷新 SpringMVC 容器
  • 发布 SrpingMVC 容器

流程探究

1.取得根容器

根容器,这里其实指的是 Spring 容器,Spring 容器在初始化后会被添加进 ServletContext。因此这里需要从 ServletContext 中寻找。

String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE =     
    WebApplicationContext.class.getName() + ".ROOT";

public static WebApplicationContext getWebApplicationContext(ServletContext sc) {

    return getWebApplicationContext(sc,
        WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
}

public static WebApplicationContext getWebApplicationContext(ServletContext sc, String attrName) {
    // 省略代码...

    Object attr = sc.getAttribute(attrName);

    return (WebApplicationContext) attr;
}

2.取得 SpringMVC 容器

该步骤具体是若当前 Servlet 已经存在 SpringMVC 容器,则直接取得。并对其进行初始化。

是否存在,则通过该类的成员变量 webApplicationContext 来判断。


3.寻找 SpringMVC 容器

若当前 Servlet 不存在 SpringMVC 容器,则需要去 ServletContext 中去取得,与获取根容器的原理一致。

protected WebApplicationContext findWebApplicationContext() {
    // 省略部分代码...

    String attrName = getContextAttribute();    
    if (attrName == null) {
        return null;
    }

    // 从 sc 当前获取。
    WebApplicationContext wac = 
        WebApplicationContextUtils.getWebApplicationContext(getServletContext(), attrName);

    return wac;
}

public String getContextAttribute() {
    return this.contextAttribute;
}

4.创建 SpringMVC 容器

若在 Servlet、ServletContext 中均没有找到 SpringMVC 容器,说明它还没被创建。下面来看它的创建过程:

protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {

    // 省略部分代码...

    // 默认为 XmlWebApplicationContext 类
    Class<?> contextClass = getContextClass();

    // 关键 -> 创建容器
    ConfigurableWebApplicationContext wac =
        (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);

    // 初始化容器
    wac.setEnvironment(getEnvironment());
    wac.setParent(parent);
    wac.setConfigLocation(getContextConfigLocation());
    configureAndRefreshWebApplicationContext(wac);

    return wac;
}

继续来看 BeanUtils.instantiateClass 方法,该方法负责容器的真正创建过程:

public static <T> T instantiateClass(Class<T> clazz) 
    throws BeanInstantiationException {

    // 省略部分代码...

    try {
        // 利用反射实例化对象,完成容器创建
        return instantiateClass(clazz.getDeclaredConstructor());

    }catch (NoSuchMethodException ex) {
        // 抛出异常...
    }
}

public static <T> T instantiateClass(Constructor<T> ctor, Object... args) 
    throws BeanInstantiationException {

    // 省略部分代码...

    try {
        // 设置访问权限为 public
        ReflectionUtils.makeAccessible(ctor);

        // 利用构造函数实例化对象
        return ctor.newInstance(args);

    }catch (InstantiationException ex) {
        // 抛出异常...
    }catch (IllegalAccessException ex) {
        // 抛出异常...
    }catch (IllegalArgumentException ex) {
        // 抛出异常...
    }catch (InvocationTargetException ex) {
        // 抛出异常...
    }
}

5.重刷新 SpringMVC 容器

该类的 onRefresh 方法为空方法,具体的实现交给它的子类 DispatcherServlet 来实现。


6.发布 SpringMVC 容器

发布 SpringMVC 容器,实际就是将其封装进 ServletContext 的属性中。

属性名的定义如下:

public static final String SERVLET_CONTEXT_PREFIX = 
    FrameworkServlet.class.getName() + ".CONTEXT.";

public String getServletContextAttributeName() {
    return SERVLET_CONTEXT_PREFIX + getServletName();
}

总结

FrameworkServlet 在 SpringMVC 的初始化的过程中主要负责其容器的初始化。

  • 它取得 Spring 根容器的目的是将其设为 SpringMVC 的父容器

  • 它对于 SpringMVC 容器的初始化过程,主要经历了以下几个动作:

从 Servlet 获取 -> 从 ServletContext 获取 -> 创建 -> 重刷新 -> 发布

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

oxf

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值