Spring Boots Filter的加载过程

Spring Boot Filter,Servlet,Listener的加载过程

这里在说明这个过程之前需要先列出来几个比较重要的类

  • ServletContainerInitializer 这是一个关键接口,任何程序可以通过该接口向Tomcat添加Filter,Servlet,Listener
  • ServletContextInitializer 这个接口是Spring中定义的可以看到它和ServletContainerInitializer 接口几乎完全一样,也就是说在Spring Boot中实现ServletContextInitializer 就可以把对应的Filter,Servlet,Listener 配置到Tomcat,而Spring Boot只有一个ServletContextInitializer 的实现类(TomcatStarter)这样就保证了对外的接口尽量单一,降低了耦合度。而Filter,Servlet,Listener对应的RegistrationBean都实现了ServletContextInitializer 接口
  • ServletWebServerApplicationContext 这个类是Servlet应用的上下文类,而Spring Boot默认使用的是它的子类 AnnotationConfigServletWebServerApplicationContext,这个上下文管理了所有的Spring 容器中的类
  • TomcatStarter 该类实现了ServletContainerInitializer 并且持有容器中的所有ServletContextInitializer实现类的实例,向Tomcat中注入Filter,Servlet,Listener都是通过该类完成的

那么好了有了以上的几个类那Spring是怎么把这几个类联合使用的呢?

Spring Boot 和Tomcat的纽带

ServletWebServerApplicationContext 这个上下文类同时持有WebServer实例(也就是Tomcat实现),和所有的ServletContextInitializer 实现类,并在创建webServer实例时将这些ServletContextInitializer 的实现类注入到了TomcatStarter 类中

private void createWebServer() {
		WebServer webServer = this.webServer;
		ServletContext servletContext = getServletContext();
		if (webServer == null && servletContext == null) {
            //创建 WebServer并把getSelfInitializer 注入到了TomcatStarter中
			ServletWebServerFactory factory = getWebServerFactory();
			this.webServer = factory.getWebServer(getSelfInitializer());
		}
		else if (servletContext != null) {
			try {
				getSelfInitializer().onStartup(servletContext);
			}
			catch (ServletException ex) {
				throw new ApplicationContextException("Cannot initialize servlet context",
						ex);
			}
		}
		initPropertySources();
	}

再来看一下getSelfInitializer()的实现

/**
这个方法只是调用了一个私有方法来做ServletContextInitializer的实现
*/
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
		return this::selfInitialize;
}

private void selfInitialize(ServletContext servletContext) throws ServletException {
		prepareWebApplicationContext(servletContext);
		registerApplicationScope(servletContext);
		WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(),
				servletContext);
    //getServletContextInitializerBeans() 会将所有实现了ServletContextInitializer 接口的类全部从Spring容器中加载出来
		for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
			beans.onStartup(servletContext);
		}
}

getWebServer的实现

@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
   Tomcat tomcat = new Tomcat();
   File baseDir = (this.baseDirectory != null) ? this.baseDirectory
         : createTempDir("tomcat");
   tomcat.setBaseDir(baseDir.getAbsolutePath());
   Connector connector = new Connector(this.protocol);
   tomcat.getService().addConnector(connector);
   customizeConnector(connector);
   tomcat.setConnector(connector);
   tomcat.getHost().setAutoDeploy(false);
   configureEngine(tomcat.getEngine());
   for (Connector additionalConnector : this.additionalTomcatConnectors) {
      tomcat.getService().addConnector(additionalConnector);
   }
   //配置Tomcat的上下文
   prepareContext(tomcat.getHost(), initializers);
   return getTomcatWebServer(tomcat);
}

prepareContext 方法的定义

protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
   File documentRoot = getValidDocumentRoot();
    //实例化Tomcat上下文类
   TomcatEmbeddedContext context = new TomcatEmbeddedContext();
   if (documentRoot != null) {
      context.setResources(new LoaderHidingResourceRoot(context));
   }
   context.setName(getContextPath());
   context.setDisplayName(getDisplayName());
   context.setPath(getContextPath());
   File docBase = (documentRoot != null) ? documentRoot
         : createTempDir("tomcat-docbase");
   context.setDocBase(docBase.getAbsolutePath());
   context.addLifecycleListener(new FixContextListener());
   context.setParentClassLoader(
         (this.resourceLoader != null) ? this.resourceLoader.getClassLoader()
               : ClassUtils.getDefaultClassLoader());
   resetDefaultLocaleMapping(context);
   addLocaleMappings(context);
   context.setUseRelativeRedirects(false);
   configureTldSkipPatterns(context);
   WebappLoader loader = new WebappLoader(context.getParentClassLoader());
   loader.setLoaderClass(TomcatEmbeddedWebappClassLoader.class.getName());
   loader.setDelegate(true);
   context.setLoader(loader);
   if (isRegisterDefaultServlet()) {
      addDefaultServlet(context);
   }
   if (shouldRegisterJspServlet()) {
      addJspServlet(context);
      addJasperInitializer(context);
   }
   context.addLifecycleListener(new StaticResourceConfigurer(context));
   //加载到所有的ServletContextInitializer实例
   ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
   host.addChild(context);
   //将配置写入到Tomcat上下文 
   configureContext(context, initializersToUse);
   postProcessContext(context);
}

configureContext 方法的定义

protected void configureContext(Context context,
      ServletContextInitializer[] initializers) {
   //创建TomcatStarter
   TomcatStarter starter = new TomcatStarter(initializers);
   
   if (context instanceof TomcatEmbeddedContext) {
      TomcatEmbeddedContext embeddedContext = (TomcatEmbeddedContext) context;
      embeddedContext.setStarter(starter);
      embeddedContext.setFailCtxIfServletStartFails(true);
   }
   //将TomcatStarter加入到上下文
   context.addServletContainerInitializer(starter, NO_CLASSES);
   for (LifecycleListener lifecycleListener : this.contextLifecycleListeners) {
      context.addLifecycleListener(lifecycleListener);
   }
   for (Valve valve : this.contextValves) {
      context.getPipeline().addValve(valve);
   }
   for (ErrorPage errorPage : getErrorPages()) {
      new TomcatErrorPage(errorPage).addToContext(context);
   }
   for (MimeMappings.Mapping mapping : getMimeMappings()) {
      context.addMimeMapping(mapping.getExtension(), mapping.getMimeType());
   }
   configureSession(context);
   new DisableReferenceClearingContextCustomizer().customize(context);
   for (TomcatContextCustomizer customizer : this.tomcatContextCustomizers) {
      customizer.customize(context);
   }
}

Spring boot中添加Filter,Servlet,Listener

通过上面的代码分析可知Spring Boot 会把容器中所有实现了ServletContextInitializer的实例加载出来并尝试将它们注册到Tomcat中而 RegistrationBean就实现了ServletContextInitializer并且该类为Filter,Servlet,Listener对应RegistrationBean的父类所以我们只需要把对应的RegistrationBean 加入到Spring容器就可以实现向Tomcat中注册对应的Filter,Servlet,Listener。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值