300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > Spring Boot 集成undertow作为web容器分析

Spring Boot 集成undertow作为web容器分析

时间:2020-03-30 12:14:05

相关推荐

Spring Boot 集成undertow作为web容器分析

背景

项目中使用了undertow作为web容器,以获得更好的性能,这里先不谈undertow相比其他容器(tomcat,jetty等)的优缺点,只是单纯分析下Spring Boot 是如何集成web容器的

项目中使用undertow

Spring Boot默认使用tomcat作为内嵌web容器,可以通过修改pom更改为undertow

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><exclusions><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-tomcat</artifactId></exclusion></exclusions></dependency><!-- 使用undertow 作为web服务器 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-undertow</artifactId></dependency>

原理分析

通过上面简单的修改就可以使用undertow, 那么Spring Boot内部是如何实现的呢? 带着这个疑问,走了下源码(本人使用Spring Boot版本为2.0.6)。

@SpringBootApplicationpublic class UsBridgeApp {public static void main( String[] args ) {SpringApplication.run(UsBridgeApp.class, args);}}

上面是一个简单的入口程序,主要是使用了SpringBootApplication注解和main方法中运行SpringApplication 的静态run方法,进入SpringApplication不难发现,静态run方法最终也是调用SpringApplication类实例run方法

/*** Run the Spring application, creating and refreshing a new* {@link ApplicationContext}.* @param args the application arguments (usually passed from a Java main method)* @return a running {@link ApplicationContext}*/public ConfigurableApplicationContext run(String... args) {StopWatch stopWatch = new StopWatch();stopWatch.start();ConfigurableApplicationContext context = null;Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();configureHeadlessProperty();SpringApplicationRunListeners listeners = getRunListeners(args);listeners.starting();try {ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);configureIgnoreBeanInfo(environment);Banner printedBanner = printBanner(environment);context = createApplicationContext();exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,new Class[] { ConfigurableApplicationContext.class }, context);prepareContext(context, environment, listeners, applicationArguments,printedBanner);refreshContext(context);afterRefresh(context, applicationArguments);stopWatch.stop();if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);}listeners.started(context);callRunners(context, applicationArguments);}catch (Throwable ex) {handleRunFailure(context, ex, exceptionReporters, listeners);throw new IllegalStateException(ex);}try {listeners.running(context);}catch (Throwable ex) {handleRunFailure(context, ex, exceptionReporters, null);throw new IllegalStateException(ex);}return context;}

代码先是通过createApplicationContext()创建了ApplicationContext,之后调refreshContext方法,它们做了什么呢?

protected ConfigurableApplicationContext createApplicationContext() {Class<?> contextClass = this.applicationContextClass;if (contextClass == null) {try {switch (this.webApplicationType) {case SERVLET:contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);break;case REACTIVE:contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);break;default:contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);}}catch (ClassNotFoundException ex) {throw new IllegalStateException("Unable create a default ApplicationContext, "+ "please specify an ApplicationContextClass",ex);}}return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);}

通过判断webApplicationType值来选择加载哪个类, WebApplicationType 通过静态方法获得

static WebApplicationType deduceFromClasspath() {if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null)&& !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {return WebApplicationType.REACTIVE;}for (String className : SERVLET_INDICATOR_CLASSES) {if (!ClassUtils.isPresent(className, null)) {return WebApplicationType.NONE;}}return WebApplicationType.SERVLET;}

可以看出默认使用WebApplicationType.SERVLET, 所以默认加载的spring context 是 DEFAULT_SERVLET_WEB_CONTEXT_CLASS,

/*** The class name of application context that will be used by default for web* environments.*/public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";

回到refreshContext,实际是调了AbstractApplicationContext 的refresh方法

@Overridepublic void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// Prepare this context for refreshing.prepareRefresh();// Tell the subclass to refresh the internal bean factory.ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// Prepare the bean factory for use in this context.prepareBeanFactory(beanFactory);try {// Allows post-processing of the bean factory in context subclasses.postProcessBeanFactory(beanFactory);// Invoke factory processors registered as beans in the context.invokeBeanFactoryPostProcessors(beanFactory);// Register bean processors that intercept bean creation.registerBeanPostProcessors(beanFactory);// Initialize message source for this context.initMessageSource();// Initialize event multicaster for this context.initApplicationEventMulticaster();// Initialize other special beans in specific context subclasses.onRefresh();// Check for listener beans and register them.registerListeners();// Instantiate all remaining (non-lazy-init) singletons.finishBeanFactoryInitialization(beanFactory);// Last step: publish corresponding event.finishRefresh();}catch (BeansException ex) {if (logger.isWarnEnabled()) {logger.warn("Exception encountered during context initialization - " +"cancelling refresh attempt: " + ex);}// Destroy already created singletons to avoid dangling resources.destroyBeans();// Reset 'active' flag.cancelRefresh(ex);// Propagate exception to caller.throw ex;}finally {// Reset common introspection caches in Spring's core, since we// might not ever need metadata for singleton beans anymore...resetCommonCaches();}}}

这个方法做了很多事情,其中调用的onRefresh 就是调用了AnnotationConfigServletWebServerApplicationContext的父类ServletWebServerApplicationContext方法

@Overrideprotected void onRefresh() {super.onRefresh();try {createWebServer();}catch (Throwable ex) {throw new ApplicationContextException("Unable to start web server", ex);}}

终于看到了想看到的,createWebServer()似乎就是我要找的了,继续...

private void createWebServer() {WebServer webServer = this.webServer;ServletContext servletContext = getServletContext();if (webServer == null && servletContext == null) {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();}

ServletWebServerFactory 创建了webServer , ServletWebServerFactory 通过从spring容器中获得

protected ServletWebServerFactory getWebServerFactory() {// Use bean names so that we don't consider the hierarchyString[] 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);}

现在只要弄清楚Spring Boot 此时到底使用的哪个factory 便可,Spring Boot 基本配置都在spring-boot-autoconfigure 这个jar中,首先看org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration

@Configuration@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)@ConditionalOnClass(ServletRequest.class)@ConditionalOnWebApplication(type = Type.SERVLET)@EnableConfigurationProperties(ServerProperties.class)@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,ServletWebServerFactoryConfiguration.EmbeddedJetty.class,ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })public class ServletWebServerFactoryAutoConfiguration {@Beanpublic ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties) {return new ServletWebServerFactoryCustomizer(serverProperties);}@Bean@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(ServerProperties serverProperties) {return new TomcatServletWebServerFactoryCustomizer(serverProperties);}...}

通过Import注解加入了 Tomcat, Jetty,Undertow三种支持,

/** Copyright - the original author or authors.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package org.springframework.boot.autoconfigure.web.servlet;import javax.servlet.Servlet;import io.undertow.Undertow;import org.apache.catalina.startup.Tomcat;import org.apache.coyote.UpgradeProtocol;import org.eclipse.jetty.server.Server;import org.eclipse.jetty.util.Loader;import org.eclipse.jetty.webapp.WebAppContext;import org.xnio.SslClientAuthMode;import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;import org.springframework.boot.autoconfigure.condition.SearchStrategy;import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory;import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory;import org.springframework.boot.web.servlet.server.ServletWebServerFactory;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;/*** Configuration classes for servlet web servers* <p>* Those should be {@code @Import} in a regular auto-configuration class to guarantee* their order of execution.** @author Phillip Webb* @author Dave Syer* @author Ivan Sopov* @author Brian Clozel* @author Stephane Nicoll*/@Configurationclass ServletWebServerFactoryConfiguration {@Configuration@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)public static class EmbeddedTomcat {@Beanpublic TomcatServletWebServerFactory tomcatServletWebServerFactory() {return new TomcatServletWebServerFactory();}}/*** Nested configuration if Jetty is being used.*/@Configuration@ConditionalOnClass({ Servlet.class, Server.class, Loader.class,WebAppContext.class })@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)public static class EmbeddedJetty {@Beanpublic JettyServletWebServerFactory JettyServletWebServerFactory() {return new JettyServletWebServerFactory();}}/*** Nested configuration if Undertow is being used.*/@Configuration@ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)public static class EmbeddedUndertow {@Beanpublic UndertowServletWebServerFactory undertowServletWebServerFactory() {return new UndertowServletWebServerFactory();}}}

最开始的pom配置因为排除了Tomcat.class 类的依赖而引入了Undertow.class类,所有项目实际加载的ServletWebServerFactory 为UndertowServletWebServerFactory;

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。