spring boot学习之tomcat与spring boot

本文探讨了Spring Boot如何与Tomcat集成,包括Spring Boot内置Tomcat的启动过程,以及如何在不使用web.xml的情况下进行外置Tomcat部署。通过ServletWebServerApplicationContext的生命周期方法和SpringServletContainerInitializer的实现,解释了Spring Boot支持基于注解的应用启动模式。

tomcat是Servlet容器,spring boot是开发框架,可开发基于Servlet规范的应用
即spring boot应用需要放在Servlet容器上运行
而spring boot可以使用内置Servlet容器启动,也可以发布在外置Servlet容器容器上

内置tomcat之tomcat启动
1.容器构建时期ServletWebServerApplicationContext的onRefresh()

	//在父类AbstractApplicationContext的refresh()中调用
	protected void onRefresh() {
		super.onRefresh();
		try {
		   //创建web容器
			createWebServer();
		}
		catch (Throwable ex) {
			throw new ApplicationContextException("Unable to start web server", ex);
		}
	}
	private void createWebServer() {
		WebServer webServer = this.webServer;
		ServletContext servletContext = getServletContext();
		if (webServer == null && servletContext == null) {
			//main方法启动时会找对应的jar包,没有则报错
			ServletWebServerFactory factory = getWebServerFactory();
			this.webServer = factory.getWebServer(getSelfInitializer());
		}
		else if (servletContext != null) {
			//外置tomcat启动,初始化selvlet context
			try {
				getSelfInitializer().onStartup(servletContext);
			}
			catch (ServletException ex) {
				throw new ApplicationContextException("Cannot initialize servlet context", ex);
			}
		}
		initPropertySources();
	}
	

2.容器启动时期ServletWebServerApplicationContext的finishRefresh()

	protected void finishRefresh() {
		super.finishRefresh();
		WebServer webServer = startWebServer();
		if (webServer != null) {
			publishEvent(new ServletWebServerInitializedEvent(webServer, this));
		}
	}

外置tomcat之不采用web.xml配置
大部分传统的JavaWeb应用启动入口一般在web.xml文件中,而在新的servlet规范中可支持不使用web.xml文件,提供了基于注解的启动模式,因此提供了ServletContainerInitializer接口和HandlesTypes注解等。而spring boot也对此规范进行了实现,从而可以发布应用时无需构建web.xml文件

package javax.servlet;

import java.util.Set;
public interface ServletContainerInitializer {
    void onStartup(Set<Class<?>> var1, ServletContext var2) throws ServletException;
}

ServletContainerInitializer的实现SpringServletContainerInitializer

	public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
			throws ServletException {
			
		//实现WebApplicationInitializer该接口进行扩展
		List<WebApplicationInitializer> initializers = new LinkedList<>();

		if (webAppInitializerClasses != null) {
			for (Class<?> waiClass : webAppInitializerClasses) {
				// Be defensive: Some servlet containers provide us with invalid classes,
				// no matter what @HandlesTypes says...
				if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
						WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
					try {
						initializers.add((WebApplicationInitializer)
								ReflectionUtils.accessibleConstructor(waiClass).newInstance());
					}
					catch (Throwable ex) {
						throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
					}
				}
			}
		}

		if (initializers.isEmpty()) {
			servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
			return;
		}

		servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
		AnnotationAwareOrderComparator.sort(initializers);
		for (WebApplicationInitializer initializer : initializers) {
			initializer.onStartup(servletContext);
		}
	}

WebApplicationInitializer的实现SpringBootServletInitializer

	public void onStartup(ServletContext servletContext) throws ServletException {
		// Logger initialization is deferred in case an ordered
		// LogServletContextInitializer is being used
		this.logger = LogFactory.getLog(getClass());
		WebApplicationContext rootAppContext = createRootApplicationContext(servletContext);
		if (rootAppContext != null) {
			servletContext.addListener(new ContextLoaderListener(rootAppContext) {
				@Override
				public void contextInitialized(ServletContextEvent event) {
					// no-op because the application context is already initialized
				}
			});
		}
		else {
			this.logger.debug("No ContextLoaderListener registered, as " + "createRootApplicationContext() did not "
					+ "return an application context");
		}
	}
	protected WebApplicationContext createRootApplicationContext(ServletContext servletContext) {
	
		//构建SpringApplication
		SpringApplicationBuilder builder = createSpringApplicationBuilder();
		builder.main(getClass());
		ApplicationContext parent = getExistingRootWebApplicationContext(servletContext);
		if (parent != null) {
			this.logger.info("Root context already created (using as parent).");
			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null);
			builder.initializers(new ParentContextApplicationContextInitializer(parent));
		}
		builder.initializers(new ServletContextApplicationContextInitializer(servletContext));
		
		//指定加载applicationContext的类为ServletWebServerApplicationContext的子类AnnotationConfigServletWebServerApplicationContext
		builder.contextClass(AnnotationConfigServletWebServerApplicationContext.class);
		
		//需要继承此类并实现该方法,定义spring boot的配置如扫描包目录等
		builder = configure(builder);
		builder.listeners(new WebEnvironmentPropertySourceInitializer(servletContext));
		SpringApplication application = builder.build();
		if (application.getAllSources().isEmpty()
				&& AnnotationUtils.findAnnotation(getClass(), Configuration.class) != null) {
			application.addPrimarySources(Collections.singleton(getClass()));
		}
		Assert.state(!application.getAllSources().isEmpty(),
				"No SpringApplication sources have been defined. Either override the "
						+ "configure method or add an @Configuration annotation");
		// Ensure error pages are registered
		if (this.registerErrorPageFilter) {
			application.addPrimarySources(Collections.singleton(ErrorPageFilterConfiguration.class));
		}
		//main方法启动的形式也调用过该方法
		return run(application);
	}

参考博文:
SpringBoot 中的 ServletInitializer
spring boot 内置tomcat和外置tomcat部署总结
在Spring boot中加入web.xml

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值