基本概念
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 获取 -> 创建 -> 重刷新 -> 发布