SpringBoot2整合SpringSecurity+Swagger3(源码分析一)


SpringBoot2整合SpringSecurity+Swagger3系列


Spring Boot启动过程中会创建一个WerbServer,对应的代码如下(一般情况下一开始servletContext是不存在的,需要创建Web服务器 )

-- org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#createWebServer
private void createWebServer() {
	WebServer webServer = this.webServer;
	ServletContext servletContext = getServletContext();
	if (webServer == null && servletContext == null) {
		// 从Spring中获取ServletWebServerFactory	
		ServletWebServerFactory factory = getWebServerFactory();
		// 根据ServletWebServerFactory创建Web服务器 并执行启动逻辑
		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();
}
  • 从Spring中获取ServletWebServerFactory
/**
 * Returns the {@link ServletWebServerFactory} that should be used to create the
 * embedded {@link WebServer}. By default this method searches for a suitable bean in
 * the context itself.
 * @return a {@link ServletWebServerFactory} (never {@code null})
 */
protected ServletWebServerFactory getWebServerFactory() {
	// Use bean names so that we don't consider the hierarchy
	String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
	if (beanNames.length == 0) {
		throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing "
				+ "ServletWebServerFactory bean.");
	}
	if (beanNames.length > 1) {
		throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple "
				+ "ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));
	}
	return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
}

具体是哪个Sevlet容器,取决于ServletWebServerFactoryConfiguration自动注入的逻辑,比如当前环境下存在Servlet、Tomcat、UpgradeProtocol而且没有其他ServletWebServerFactory类型Bean的时候就会注入一个TomcatServletWebServerFactory类型的ServletWebServerFactory实现。

@Configuration
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
public static class EmbeddedTomcat {

	@Bean
	public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
		return new TomcatServletWebServerFactory();
	}

}
  • 根据ServletWebServerFactory创建Web服务器

创建Web服务器需要传入ServletContextInitializer列表,根据Servlet 3.0规范,在Servlet容器在启动初始化的过程中会调用注册的ServletContextInitializer的onStartup方法,这是一种SPI机制,让用户参与到Servlet容器的启动中,通常来说,主要是用于注册Servlet的一些组件(比如Servlet、Filter、Listener),当然也包括启动的一些组件。以Tomcat为例,在创建web服务器的时候其实就是创建了一个Tomcat实例。

@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);
	}
	prepareContext(tomcat.getHost(), initializers);
	return getTomcatWebServer(tomcat);
}

首先这里传入的是一个匿名的ServletContextInitializer的实现,而对应的onStartup方法实现则是selfInitialize。在这里selfInitialize并不会真实调用,只有当这个getSelfInitializer返回的ServletContextInitializer的onStartup方法调用时才会触发。

/**
 * Returns the {@link ServletContextInitializer} that will be used to complete the
 * setup of this {@link WebApplicationContext}.
 * @return the self initializer
 * @see #prepareWebApplicationContext(ServletContext)
 */
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);
	for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
		beans.onStartup(servletContext);
	}
}

创建Tomcat主要涉及到连接器(Connector)和容器(Service)、基础目录(baseDir)。至于传入的ServletContextInitializer还有内置的用于管理Session和Cookie的SessionConfiguringInitializer都统一

/**
 * Utility method that can be used by subclasses wishing to combine the specified
 * {@link ServletContextInitializer} parameters with those defined in this instance.
 * @param initializers the initializers to merge
 * @return a complete set of merged initializers (with the specified parameters
 * appearing first)
 */
protected final ServletContextInitializer[] mergeInitializers(ServletContextInitializer... initializers) {
	List<ServletContextInitializer> mergedInitializers = new ArrayList<>();
	mergedInitializers.add((servletContext) -> this.initParameters.forEach(servletContext::setInitParameter));
	mergedInitializers.add(new SessionConfiguringInitializer(this.session));
	mergedInitializers.addAll(Arrays.asList(initializers));
	mergedInitializers.addAll(this.initializers);
	return mergedInitializers.toArray(new ServletContextInitializer[0]);
}

如下图所示
在这里插入图片描述
最后又打包塞到了一个TomcatStarter类当中

TomcatStarter starter = new TomcatStarter(initializers);
if (context instanceof TomcatEmbeddedContext) {
	TomcatEmbeddedContext embeddedContext = (TomcatEmbeddedContext) context;
	embeddedContext.setStarter(starter);
	embeddedContext.setFailCtxIfServletStartFails(true);
}
context.addServletContainerInitializer(starter, NO_CLASSES);

在这里插入图片描述
而这个类又是ServletContainerInitializer。同样也是Servlet规范中的回调SPI接口。

@Override
public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException {
	try {
		for (ServletContextInitializer initializer : this.initializers) {
			initializer.onStartup(servletContext);
		}
	}
	catch (Exception ex) {
		this.startUpException = ex;
		// Prevent Tomcat from logging and re-throwing when we know we can
		// deal with it in the main thread, but log for information here.
		if (logger.isErrorEnabled()) {
			logger.error("Error starting Tomcat context. Exception: " + ex.getClass().getName() + ". Message: "
					+ ex.getMessage());
		}
	}
}
  • 执行最后的启动流程

创建完一个Servlet服务器之后,就会启动容器。

/**
 * Create a new {@link TomcatWebServer} instance.
 * @param tomcat the underlying Tomcat server
 * @param autoStart if the server should be started
 */
public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
	Assert.notNull(tomcat, "Tomcat Server must not be null");
	this.tomcat = tomcat;
	this.autoStart = autoStart;
	initialize();
}

在这里插入图片描述
此过程中就会执行org.springframework.boot.web.embedded.tomcat.TomcatStarteronStartup方法,最后触发所有收集到的ServletContainerInitializer类的onStartup方法。

总结一下:在Spring Boot当中,Spring根据条件获取ServletWebServerFactory,然后通过工厂方式获取ServletWebServer。如果创建的是Tomcat容器,那么会通过TomcatStarter将几个关键的ServletContextInitializer组合到一起。而TomcatStarter本身是一个ServletContainerInitializer,Tomcat在容器初始化过程中,会回调这个类对象的onStartup,对于TomcatStarter对象来说,又在onStartup方法中遍历其中的ServletContextInitializer列表,依次执行每一个元素的onStartup方法,最后完成整个容器的启动方法。理解Servlet规范的话,就会理解ServletContainerInitializer作为Servlet容器的SPI,是外部系统与Servlet容器整合的最关键接口,其实在Spring MVC中,同样是这个接口在起作用的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

lang20150928

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

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

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

打赏作者

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

抵扣说明:

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

余额充值