Spring MVC 核心类
类的继承关系
Spring MVC前端控制器DispatcherServlet
-->FrameworkServlet
-->HttpServletBean
-->HttpServlet
HttpServlet
HttpServlet是Servlet规范中的核心类,实现Servlet接口,继承此类用于处理用户请求。
HttpServletBean
HttpServletBean主要配置servlet中初始化参数。继承HttpServlet,并实现无参的init()方法,用于设置在web.xml中配置的contextConfigLocation属性,此属性指定Spring MVC的配置文件地址,默认为WEB-INF/[servlet-name]-servlet.xml,源码如下:
/**
* DispatcherServlet第一次加载时调用init方法
*/
@Override
public final void init() throws ServletException {
// 省略日志...
// 获取在web.xml配置的初始化参数<init-param>,并将其设置到DispatcherServlet中
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
if (logger.isErrorEnabled()) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
}
throw ex;
}
}
// 调用子类(FrameworkServlet)进行初始化
// 模版方法,此方法在HttpServletBean本身是空的,但是因为调用方法的对象是DispatcherServlet
// 所以优先在DispatcherServlet找,找不到再去父类找,最后在FrameworkServlet找到
initServletBean();
if (logger.isDebugEnabled()) {
logger.debug("Servlet '" + getServletName() + "' configured successfully");
}
}
总结HttpServletBean的作用:
- 获取web.xml的中配置DispatcherServlet的初始化参数,存放到一个参数容器ServletConfigPropertyValues中
- 根据传进来的this创建BeanWrapper,本质上它就是DispatcherServlet
- 通过bw.setPropertyValues(pvs, true),把参数设置到bw(即DispatcherServlet)里面去
- 最后调用子类的initServletBean()
FrameworkServlet
FrameworkServlet主要创建WebApplicationContext上下文,重写了HttpServletBean的initServletBean()方法。
1、initServletBean
该方法只有两句关键代码,其作用一个是调用initWebApplicationContext()方法初始化WebApplicationContext上下文,另一个是调用子类方法initFrameworkServlet()方法,源码如下:
@Override
protected final void initServletBean() throws ServletException {
getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
// 省略日志...
long startTime = System.currentTimeMillis();
try {
// 初始化WebApplicationContext,并调用子类(DispatcherServlet)的onRefresh(wac)方法
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
}
catch (ServletException | 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、initWebApplicationContext
protected WebApplicationContext initWebApplicationContext() {
// 获取root WebApplicationContext,即web.xml中配置的listener(ContextLoaderListener)
WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
// 判断容器是否由编程式传入(即是否已经存在了容器实例),存在的话直接赋值给wac,给springMVC容器设置父容器
// 最后调用刷新函数configureAndRefreshWebApplicationContext(wac),作用是把Spring MVC配置文件的配置信息加载到容器中去
if (this.webApplicationContext != null) {
// context上下文在构造是注入
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
// context没有被refreshed,提供一些诸如设置父context、设置应用context id等服务
if (cwac.getParent() == null) {
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
// 在ServletContext中寻找是否有Spring MVC容器,初次运行是没有的,Spring MVC初始化完毕ServletContext就有了Spring MVC容器
if (wac == null) {
wac = findWebApplicationContext();
}
// 当wac既没有没被编程式注册到容器中的,也没在ServletContext找得到,此时就要新建一个Spring MVC容器
if (wac == null) {
// 如果没有WebApplicationContext则创建
wac = createWebApplicationContext(rootContext);
}
// 到这里Spring MVC容器已经创建完毕,接着真正调用DispatcherServlet的初始化方法onRefresh(wac)
// 此处仍是模板模式的应用
if (!this.refreshEventReceived) {
onRefresh(wac);
}
// 将Spring MVC容器存放到ServletContext中去,方便下次取出来
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;
}
3、createWebApplicationContext
protected WebApplicationContext createWebApplicationContext(@Nullable WebApplicationContext parent) {
return createWebApplicationContext((ApplicationContext) parent);
}
protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
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");
}
// 实例化容器
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
// 设置容器环境
wac.setEnvironment(getEnvironment());
// 设置父容器
wac.setParent(parent);
// 加载Spring MVC的配置信息,如:bean注入、注解、扫描等等
String configLocation = getContextConfigLocation();
if (configLocation != null) {
wac.setConfigLocation(configLocation);
}
// 刷新容器,根据Spring MVC配置文件完成初始化操作
configureAndRefreshWebApplicationContext(wac);
return wac;
}
总结FrameworkServlet的作用:
- 创建Spring MVC的容器,根据配置文件实例化里面各种bean,并将之与spring的容器进行关联
- 把创建出来的Spring MVC容器存放到ServletContext中
- 通过模板方法模式调用子类DispatcherServlet的onRefresh()方法
DispatcherServlet
DispatcherServlet是Spring MVC核心,它是J2EE规范前端控制器的实现,负责拦截用户请求,并解析请求进行转发。
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context); // 文件上传解析
initLocaleResolver(context); // 本地解析
initThemeResolver(context); //主题解析
initHandlerMappings(context); // URL请求映射
initHandlerAdapters(context); // 初始化Controller类
initHandlerExceptionResolvers(context); // 异常解析
initRequestToViewNameTranslator(context);
initViewResolvers(context); // 视图解析
initFlashMapManager(context);
}
总结
- 容器启动时,加载web.xml部署描述文件,扫描到并找到DispatcherServlet核心控制器
- 调用HttpServletBean的init()方法,把DispatcherServlet初始化参数设置到DispatcherServlet中,并调用子类FrameworkServlet的initServletBean()方法
- FrameworkServlet的initServletBean()创建Spring MVC容器并初始化,并且和Spring父容器进行关联,使得Spring MVC容器能访问Spring容器中定义的bean,之后调用子类DispatcherServlet的onRefresh()方法
- DispatcherServlet的onRefresh(ApplicationContext context)对DispatcherServlet的策略组件进行初始化
最后创建了qq群方便大家交流,可扫描加入,同时也可加我qq:276420284,共同学习、共同进步,谢谢!