spring mvc 之 FrameworkServlet 源码解析

 2023-02-15
原文作者:从入门到入狱 原文地址:https://juejin.cn/post/7012147385174327327

FrameworkServlet是spring mvc下一个setvlet的基类,它继承了HttpServletBean,HttpServletBean又继承了HttpServlet
通过 HttpServletBean 源码解析中我们了解到HttpServletBean重写了servlet的init()方法,在init()方法中调用了initServletBean(), initServletBean()是个空方法需要子类去实现它

FrameworkServlet做了一些什么事?

  • 实现HttpServletBean中的initServletBean(),在initServletBean()创建或者刷新WebApplicationCentext容器
    protected final void initServletBean() throws ServletException {
        this.getServletContext().log("Initializing Spring " + this.getClass().getSimpleName() + " '" + this.getServletName() + "'");
        if (this.logger.isInfoEnabled()) {
            this.logger.info("Initializing Servlet '" + this.getServletName() + "'");
        }
    
        long startTime = System.currentTimeMillis();
    
        try {
        
            //初始化或者刷新WebApplicationContext容器
            this.webApplicationContext = this.initWebApplicationContext();
            this.initFrameworkServlet();
        } catch (RuntimeException | ServletException var4) {
            this.logger.error("Context initialization failed", var4);
            throw var4;
        }
    
        if (this.logger.isDebugEnabled()) {
            String value = this.enableLoggingRequestDetails ? "shown which may lead to unsafe logging of potentially sensitive data" : "masked to prevent unsafe logging of potentially sensitive data";
            this.logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails + "': request parameters and headers will be " + value);
        }
    
        if (this.logger.isInfoEnabled()) {
            this.logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
        }
    
    }
    protected WebApplicationContext initWebApplicationContext() {
        WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
        WebApplicationContext wac = null;
        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);
                    }
                     //这个方法可以刷新WebApplicationContext中配置,
                    this.configureAndRefreshWebApplicationContext(cwac);
                }
            }
        }
    
        if (wac == null) {
          //从ContextAttribute中查找WebApplicationContext
            wac = this.findWebApplicationContext();
        }
    
        if (wac == null) {
            //如果还是没有找到则会去创建一个WebApplicationContext
            wac = this.createWebApplicationContext(rootContext);
        }
    
       
        if (!this.refreshEventReceived) {
           //刷新WebApplicationContext配置,onRefresh需要子类去实现
            synchronized(this.onRefreshMononRefresh需要子类去实现itor) {
                this.onRefresh(wac);
            }
        }
    
        if (this.publishContext) {
        
            //将当前容器保存到ServletContext中
            String attrName = this.getServletContextAttributeName();
            this.getServletContext().setAttribute(attrName, wac);
        }
    
        return wac;
    }

WebApplicationContext寻找以及创建包含以下几个步骤

  1. this.webApplicationContext不等于null,则判断当前webApplicationContext容器是否可用,不可用的情况下,判断是否设置了父容器,最后调用this.configureAndRefreshWebApplicationContext(cwac)刷新webApplicationContext配置
  2. webApplicationContext为空,则通过从web.xml配置中servlet参数查到对应的WebApplicationContext
  3. 如果依旧没有找到则创建webApplicationContext

configureAndRefreshWebApplicationContext

不管是单纯创建还是通过构造器注入,最终都会调用到configureAndRefreshWebApplicationContext()方法,configureAndRefreshWebApplicationContext()中调用了AbstractApplicationContext.refresh()刷新上下文环境,

AbstractApplicationContext.refresh()在最后一步调用finishRefresh()发布了ContextRefreshedEvent事件,则调用事件,从而FrameworkServlet监听到了ContextRefreshedEvent事件,则调用onRefresh()方法。

onRefresh()最后被DispatcherServlet所实现,初始化了它的九大组件

总结

  • HttpServletBean 覆盖 GenericServlet 的 init() 方法,该方法调用 initServletBean() 方法,该方法被 FrameworkServlet 覆盖
  • 在 FrameworkServlet 的 initServletBean() 方法中,它调用 initWebApplicationContext() 方法
  • 在该方法中,它检查 webApplicationContext 是否是 ConfigurableWebApplicationContext 的实例
  • 如果是,则调用 configureAndRefreshWebApplicationContext() 方法
  • 因为它在 webApplicationContext 上调用 refresh 方法
  • 查看 AbstractApplicationContext 中 refresh() 方法的实现,最后调用了 finishRefresh() 方法
  • 在该方法中它发布 ContextRefreshedEvent
  • 在 FrameworkServlet 中有实现 ApplicationListener 的私有类 ContextRefreshListener
  • 此类上的 onApplicationEvent() 方法调用 FrameworkServlet 的 onApplicationEvent() 方法
  • 在该方法中,它调用由 DispatcherServlet 覆盖的 onRefresh 方法