【springboot源码】嵌入式Servlet容器底层原理分析


注:本文阅读前提,对springboot基本使用有所了解,此外,其他一些源码解读,如果有需要,可以参考:

1.springboot嵌入式Servlet容器基础使用

  • SpringBoot默认使用Tomcat作为嵌入式的Servlet容器;
    在这里插入图片描述

1.1 SpringBoot定制和修改Servlet容器的相关配置

  • 第一种方法,是在配置文件application.Properties中修改和server有关的配置(源码底层:ServerProperties类)
#1.tomcat端口
server.port=8009
#2.项目访问路径
server.servlet.context-path=/demo
#指定错误页面
server.error.path=/error
#设置session最大超时时间,默认为30分钟
#server.servlet.session.timeout=
#该服务绑定的IP地址
#server.address=Ip
#tomcat的编码
server.tomcat.uri-encoding=UTF-8
#tomcat最大连接数,默认200
server.tomcat.max-connections=250
#存放临时文件
#server.tomcat.basedir=路径
#打开tomcat 的access日志
#server.tomcat.accesslog.enabled=true
#设置access格式
#server.tomcat.accesslog.pattern=commen
#设置access路径
#server.tomcat.accesslog.directory=路径
#日志存放位置
#logging.path=路径
#日志名称
#logging.file=my.log
  • 第二种方法,是在配置类中编写一个:WebServerFactoryCustomizer嵌入式的Servlet容器的定制器bean;来修改Servlet容器的配置
    //配置嵌入式的Servlet容器
    @Bean
    public WebServerFactoryCustomizer<ConfigurableWebServerFactory> webServerFactoryCustomizer(){
        return new WebServerFactoryCustomizer<ConfigurableWebServerFactory>() {
            @Override
            public void customize(ConfigurableWebServerFactory factory) {
                factory.setPort(8081);
            }
        };
    }
  • 其实第一种配置方式底层原理跟第二种一样,具体会在下面聊底层原理时讲。

1.2 springboot注册Servlet三大组件【Servlet、Filter、Listener】方法

  • 由于SpringBoot默认是以jar包的方式启动嵌入式的Servlet容器来启动SpringBoot的web应用,没有web.xml文件,所以无法以传统方式注入。
  • springboot中注册三大组件用以下方式:
@Bean
public ServletRegistrationBean myServlet(){
    ServletRegistrationBean registrationBean = new ServletRegistrationBean(new MyServlet(),"/myServlet");
    return registrationBean;
}

@Bean
public FilterRegistrationBean myFilter(){
    FilterRegistrationBean registrationBean = new FilterRegistrationBean();
    registrationBean.setFilter(new MyFilter());
    registrationBean.setUrlPatterns(Arrays.asList("/hello","/myServlet"));
    return registrationBean;
}
@Bean
public ServletListenerRegistrationBean myListener(){
    ServletListenerRegistrationBean<MyListener> registrationBean = new ServletListenerRegistrationBean<>(new MyListener());
    return registrationBean;
}

2.springboot嵌入式Servlet容器自动配置原理

  • springboot的自动装配功能会在项目启动的时候进行自动配置,具体原理分析可见:
  • 【springboot源码】springboot自动配置原理分析
  • 在整个自动装配过程中,嵌入式Servlet容器自动配置也包含在其中。
  • 而关于Servlet容器自动配置类就是:org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration
@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 {

	@Bean
	public 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);
	}

	/**
	 * Registers a {@link WebServerFactoryCustomizerBeanPostProcessor}. Registered via
	 * {@link ImportBeanDefinitionRegistrar} for early registration.
	 */
	public static class BeanPostProcessorsRegistrar
			implements ImportBeanDefinitionRegistrar, BeanFactoryAware {

		private ConfigurableListableBeanFactory beanFactory;

		@Override
		public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
			if (beanFactory instanceof ConfigurableListableBeanFactory) {
				this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
			}
		}

		@Override
		public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
				BeanDefinitionRegistry registry) {
			if (this.beanFactory == null) {
				return;
			}
			registerSyntheticBeanIfMissing(registry,
					"webServerFactoryCustomizerBeanPostProcessor",
					WebServerFactoryCustomizerBeanPostProcessor.class);
			registerSyntheticBeanIfMissing(registry,
					"errorPageRegistrarBeanPostProcessor",
					ErrorPageRegistrarBeanPostProcessor.class);
		}

		private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry,
				String name, Class<?> beanClass) {
			if (ObjectUtils.isEmpty(
					this.beanFactory.getBeanNamesForType(beanClass, true, false))) {
				RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
				beanDefinition.setSynthetic(true);
				registry.registerBeanDefinition(name, beanDefinition);
			}
		}

	}

}

  • @Configuration,标注在哪个类上就代表它是个配置类,会在包扫描时被识别。
  • @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)装配先后排序
  • @ConditionalOnClass(ServletRequest.class)表示这个配置类只有在项目中存在ServletRequest.class这个类时才会生效
  • @ConditionalOnWebApplication(type = Type.SERVLET)代表这个配置类只有本项目是web项目时才生效
  • @EnableConfigurationProperties(ServerProperties.class)在本配置类装配前,先装配ServerProperties这个配置类,那我们来打开这个ServerProperties看看:
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {

	private Integer port;

	private InetAddress address;

	@NestedConfigurationProperty
	private final ErrorProperties error = new ErrorProperties();
	
	private Boolean useForwardHeaders;
	
	private String serverHeader;
    ...}
  • 这个配置类就是读取我们配置文件中像server.port=8009这样的server配置的。

  • @Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
    ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
    ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
    ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })这个注解是在向容器中添加组件:

  • ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class注册一个后置处理器,这里先打个记号,我们稍后再说它的作用,

  • ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,配置Tomcat的

  • ServletWebServerFactoryConfiguration.EmbeddedJetty.class,配置Jetty的

  • ServletWebServerFactoryConfiguration.EmbeddedUndertow.class,配置Undertow的

  • 我们来打开ServletWebServerFactoryConfiguration这个类看看:

@Configuration
class ServletWebServerFactoryConfiguration {

	@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();
		}

	}

	@Configuration
	@ConditionalOnClass({ Servlet.class, Server.class, Loader.class,
			WebAppContext.class })
	@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
	public static class EmbeddedJetty {

		@Bean
		public JettyServletWebServerFactory JettyServletWebServerFactory() {
			return new JettyServletWebServerFactory();
		}

	}

	@Configuration
	@ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
	@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
	public static class EmbeddedUndertow {

		@Bean
		public UndertowServletWebServerFactory undertowServletWebServerFactory() {
			return new UndertowServletWebServerFactory();
		}

	}

}
  • 上面我们讲过@ConditionalOnClass这个注解标注在配置类上,那被标注的配置类生效的前提就是项目中必须有@ConditionalOnClass括号中标记的类。
  • 我来截一张我看源码时的图片,大家可以一起看看:
    在这里插入图片描述
  • 看到我们标注的@ConditionalOnClass注解了吗?里面的类在我的IDE里是红色的,代表我的IDE没有在项目里找个相应的jar包,相应的类,所以默认情况下EmbeddedJetty ,EmbeddedUndertow 这两个组件注入都不会成功。
  • 成功的只有一个,就是:
  • EmbeddedTomcat :Tomcat的配置,它向容器中注入了一个TomcatServletWebServerFactory,Tomcat工厂,它里面有段代码,我们可以看看:
@Override
	public WebServer getWebServer(ServletContextInitializer... initializers) {
	   //创建一个Tomcat
		Tomcat tomcat = new Tomcat();
	   //配置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);
		//将配置好的Tomcat传入进去,返回一个EmbeddedServletContainer;并且启动Tomcat服务器 
		return getTomcatWebServer(tomcat);
	}
  • 创建一个Tomcat,配置并返回,这个方法的调用,我们会在下个章节,聊嵌入式Servlet容器启动原理的时候讲
  • 现在我们回过头,再聊一下我们之前的那个标记:ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class
  • 我们打开它看看它都做了什么:
public static class BeanPostProcessorsRegistrar
			implements ImportBeanDefinitionRegistrar, BeanFactoryAware {

		private ConfigurableListableBeanFactory beanFactory;

		@Override
		public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
			if (beanFactory instanceof ConfigurableListableBeanFactory) {
				this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
			}
		}

		@Override
		public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
				BeanDefinitionRegistry registry) {
			if (this.beanFactory == null) {
				return;
			}
			//重点
			registerSyntheticBeanIfMissing(registry,
					"webServerFactoryCustomizerBeanPostProcessor",
					WebServerFactoryCustomizerBeanPostProcessor.class);
			registerSyntheticBeanIfMissing(registry,
					"errorPageRegistrarBeanPostProcessor",
					ErrorPageRegistrarBeanPostProcessor.class);
		}

		private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry,
				String name, Class<?> beanClass) {
			if (ObjectUtils.isEmpty(
					this.beanFactory.getBeanNamesForType(beanClass, true, false))) {
				RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
				beanDefinition.setSynthetic(true);
				registry.registerBeanDefinition(name, beanDefinition);
			}
		}

	}
  • 重点在registerSyntheticBeanIfMissing(registry,“webServerFactoryCustomizerBeanPostProcessor”,WebServerFactoryCustomizerBeanPostProcessor.class);它往容器注入了一个WebServerFactoryCustomizerBeanPostProcessor
  • WebServerFactoryCustomizerBeanPostProcessor从名字看就知道这是个后置处理器,至于后置处理器的作用,它可以在所有bean初始化之前做一些操作,详细可见:
  • 【Spring源码】 后置处理器BeanPostProcessor底层原理分析
  • 这里我们打开它:
public class WebServerFactoryCustomizerBeanPostProcessor
		implements BeanPostProcessor, BeanFactoryAware {

	private ListableBeanFactory beanFactory;

	private List<WebServerFactoryCustomizer<?>> customizers;
    //给WebServerFactoryCustomizerBeanPostProcessor设置BeanFactory 的,这里我们不看
	@Override
	public void setBeanFactory(BeanFactory beanFactory) {
		Assert.isInstanceOf(ListableBeanFactory.class, beanFactory,
				"WebServerCustomizerBeanPostProcessor can only be used "
						+ "with a ListableBeanFactory");
		this.beanFactory = (ListableBeanFactory) beanFactory;
	}
   //初始化之前,重点
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName)
			throws BeansException {
		//如果当前初始化的是一个WebServerFactory类型的组件
		if (bean instanceof WebServerFactory) {
			postProcessBeforeInitialization((WebServerFactory) bean);
		}
		return bean;
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName)
			throws BeansException {
		return bean;
	}

	@SuppressWarnings("unchecked")
	private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
	    //获取所有的定制器,调用每一个定制器的customize方法来给Servlet容器进行属性赋值;
		LambdaSafe
				.callbacks(WebServerFactoryCustomizer.class, getCustomizers(),
						webServerFactory)
				.withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)
				.invoke((customizer) -> customizer.customize(webServerFactory));
	}

	private Collection<WebServerFactoryCustomizer<?>> getCustomizers() {
		if (this.customizers == null) {
			// Look up does not include the parent context
			this.customizers = new ArrayList<>(getWebServerFactoryCustomizerBeans());
			this.customizers.sort(AnnotationAwareOrderComparator.INSTANCE);
			this.customizers = Collections.unmodifiableList(this.customizers);
		}
		return this.customizers;
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	private Collection<WebServerFactoryCustomizer<?>> getWebServerFactoryCustomizerBeans() {
	   //从容器中获取所有这葛类型的组件:WebServerFactoryCustomizer
            //定制Servlet容器,给容器中可以添加一个WebServerFactoryCustomizer类型的组件
		return (Collection) this.beanFactory
				.getBeansOfType(WebServerFactoryCustomizer.class, false, false).values();
	}

}
  • 在容器初始化所有bean之前先判断是不是WebServerFactory类型的,如果是的话就找到容器中所有的WebServerFactoryCustomizer,依次调用customize()方法把定制配置加载到这个WebServerFactory类型的Servlet容器中。
  • 至于都那些bean是WebServerFactory类型的,像上面的TomcatServletWebServerFactory就是,跟踪到底层就会发现它是继承于WebServerFactory的。
  • 对后置处理器熟悉的,看我代码中的注释应该就可以明白了,还记得上面第一个章节中讲的两种可以修改配置的方法吗?
  • 第二种,是在配置类中编写一个:WebServerFactoryCustomizer嵌入式的Servlet容器的定制器bean;来修改Servlet容器的配置,到这里应该明白了吧
  • 然后,第一种修改配置的方法的底层原理,让我们回到最开始的那个入口配置类ServletWebServerFactoryAutoConfiguration,里面有注入这么个bean :
   @Bean
	public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(
			ServerProperties serverProperties) {
		return new ServletWebServerFactoryCustomizer(serverProperties);
	}
  • 我们打开ServletWebServerFactoryCustomizer:
public class ServletWebServerFactoryCustomizer implements
		WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered {

	private final ServerProperties serverProperties;

	public ServletWebServerFactoryCustomizer(ServerProperties serverProperties) {
		this.serverProperties = serverProperties;
	}

	@Override
	public int getOrder() {
		return 0;
	}

	@Override
	public void customize(ConfigurableServletWebServerFactory factory) {
		PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
		map.from(this.serverProperties::getPort).to(factory::setPort);
		map.from(this.serverProperties::getAddress).to(factory::setAddress);
		map.from(this.serverProperties.getServlet()::getContextPath)
				.to(factory::setContextPath);
		map.from(this.serverProperties.getServlet()::getApplicationDisplayName)
				.to(factory::setDisplayName);
		map.from(this.serverProperties.getServlet()::getSession).to(factory::setSession);
		map.from(this.serverProperties::getSsl).to(factory::setSsl);
		map.from(this.serverProperties.getServlet()::getJsp).to(factory::setJsp);
		map.from(this.serverProperties::getCompression).to(factory::setCompression);
		map.from(this.serverProperties::getHttp2).to(factory::setHttp2);
		map.from(this.serverProperties::getServerHeader).to(factory::setServerHeader);
		map.from(this.serverProperties.getServlet()::getContextParameters)
				.to(factory::setInitParameters);
	}

}
  • 同样的实现了WebServerFactoryCustomizer
  • 总结一下步骤:

1,SpringBoot根据导入的jar依赖情况,给容器中添加相应的Servlet容器工厂【TomcatServletWebServerFactory】

2,容器中某个组件要创建对象就会惊动后置处理器WebServerFactoryCustomizerBeanPostProcessor,如果这个组件是Servlet容器工厂,比如TomcatServletWebServerFactory,那这个后置处理器就会开始工作。

3,这个后置处理器会把容器里的所有WebServerFactoryCustomizer找出来,调用定制器的定制方法,把定制配置加载到Servlet容器工厂中。

4,最后在Servlet容器工厂初始化后创建Servlet容器(tomcat)时使用那些配置。

3.springboot嵌入式Servlet容器启动原理

  • 上面聊了嵌入式Servlet容器是怎样自动配置的,现在我们接着聊一下它的启动原理:
@SpringBootApplication
public class ApplicationStart {
    public static void main(String[] args) {
        SpringApplication.run(ApplicationStart.class, args);
    }
}
  • 从springboot启动类入手,进入SpringApplication.run(ApplicationStart.class, args);方法一直跟进去:
	public static ConfigurableApplicationContext run(Class<?>[] primarySources,
			String[] args) {
		return new SpringApplication(primarySources).run(args);
	}
	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		//保存主配置类 
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		//判断当前是否一个web应用 
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		//从类路径下找到META‐INF/spring.factories配置的所有ApplicationContextInitializer;然后保存起 来 
		setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));
	    //从类路径下找到ETA‐INF/spring.factories配置的所有ApplicationListener
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		 //从多个配置类中找到有main方法的主配置类 
		this.mainApplicationClass = deduceMainApplicationClass();
	}
  • 这段会加载一些配置,创建SpringApplication对象
  • 创建SpringApplication对象后,会进入一个run方法:
public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		configureHeadlessProperty();
		//获取SpringApplicationRunListeners;从类路径下META‐INF/spring.factories 
		SpringApplicationRunListeners listeners = getRunListeners(args);
		//回调所有的获取SpringApplicationRunListener.starting()方法 
		listeners.starting();
		try {
		   //封装命令行参数 
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
		    //准备环境
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
			//创建环境完成后回调SpringApplicationRunListener.environmentPrepared();表示环境准 备完成 
			configureIgnoreBeanInfo(environment);
			//打印Banner 图就是springboot项目启动时那个springboot标志
			Banner printedBanner = printBanner(environment);
		   //创建ApplicationContext;决定创建web的ioc还是普通的ioc 
			context = createApplicationContext();
			exceptionReporters = getSpringFactoriesInstances(
					SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
             //准备上下文环境;将environment保存到ioc中;而且applyInitializers();                                 
             //applyInitializers():回调之前保存的所有的ApplicationContextInitializer的initialize方法        
             //回调所有的SpringApplicationRunListener的contextPrepared(); 
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
			//prepareContext运行完成以后回调所有的SpringApplicationRunListener的contextLoaded(); 


            //刷新容器;ioc容器初始化(如果是web应用还会创建嵌入式的Tomcat);Spring注解版        
            //扫描,创建,加载所有组件的地方;(配置类,组件,自动配置) 
			refreshContext(context);
            //从ioc容器中获取所有的ApplicationRunner和CommandLineRunner进行回调                 
            //ApplicationRunner先回调,CommandLineRunner再回调 
			afterRefresh(context, applicationArguments);
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass)
						.logStarted(getApplicationLog(), stopWatch);
			}
			//所有的SpringApplicationRunListener回调started方法
			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;
	}
   
  • 代码中我写的注释已经很详细了,其中涉及到嵌入式Servlet容器启动的是refreshContext(context);这行代码,刷新IOC容器,也就是IOC加载所有bean的过程,具体spring IOC底层可参考:
  • 【spring源码】spring IOC容器底层源码分析

下面整个底层原理的剖析建立在对spring IOC容器熟悉的情况下

  • 现在我们回过头,首先,这是个web项目所以在context = createApplicationContext();这行代码的时候创建的IOC容器是web容器,就是这个:AnnotationConfigServletWebServerApplicationContext
  • AnnotationConfigServletWebServerApplicationContext比之正常IOC容器重写了onRefresh方法:
@Override
	protected void onRefresh() {
		super.onRefresh();
		try {
			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) {
			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 (比如我们上面聊的TomcatServletWebServerFactory),然后创建它(初始化它之前会调用相关加载定制配置的后置处理器),最后调用它的getWebServer()方法启动Servlet容器。
  • 我们可以再回顾一下TomcatServletWebServerFactory的getWebServer方法
@Override
	public WebServer getWebServer(ServletContextInitializer... initializers) {
	   //创建一个Tomcat
		Tomcat tomcat = new Tomcat();
	   //配置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);
		//将配置好的Tomcat传入进去,返回一个EmbeddedServletContainer;并且启动Tomcat服务器 
		return getTomcatWebServer(tomcat);
	}

4.springboot使用外置的Servlet容器

  • 嵌入式Servlet容器:应用打成可执行的jar

​优点:简单、便携

缺点:默认不支持JSP、优化定制比较复杂

  • 外置的Servlet容器:外面安装Tomcat—应用要打成war包;

4.1 步骤

  • 1,必须创建一个springboot war项目;

  • 2, 将嵌入式的Tomcat指定为provided;

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-tomcat</artifactId>
   <scope>provided</scope>
</dependency>
  • 3,必须编写一个SpringBootServletInitializer的子类,并调用configure方法
public class ServletInitializer extends SpringBootServletInitializer {

   @Override
   protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
       //传入SpringBoot应用的主程序
      return application.sources(SpringBoot04WebJspApplication.class);
   }

}
  • 4, 启动服务器就可以使用;

4.2 原理

  • servlet3.0 规则:

1、服务器启动(web应用启动)会创建当前web应用里面每一个jar包里面ServletContainerInitializer实例:

2、ServletContainerInitializer的实现放在jar包的META-INF/services文件夹下,有一个名为javax.servlet.ServletContainerInitializer的文件,内容就是ServletContainerInitializer的实现类的全类名

​ 3、还可以使用@HandlesTypes,在应用启动的时候加载我们感兴趣的类;

  • 流程:

1、启动Tomcat

2、org\springframework\spring-web\4.3.14.RELEASE\spring-web-4.3.14.RELEASE.jar!\META-INF\services\javax.servlet.ServletContainerInitializer:
Spring的web模块里面有这个文件:org.springframework.web.SpringServletContainerInitializer

3、SpringServletContainerInitializer将@HandlesTypes(WebApplicationInitializer.class)标注的所有这个类型的类都传入到onStartup方法的Set<Class<?>>;为这些WebApplicationInitializer类型的类创建实例;

4、每一个WebApplicationInitializer都调用自己的onStartup;

5、相当于我们的SpringBootServletInitializer的类会被创建对象,并执行onStartup方法

6、SpringBootServletInitializer实例执行onStartup的时候会createRootApplicationContext;创建SpringApplication

protected WebApplicationContext createRootApplicationContext(
      ServletContext servletContext) {
    //1、创建SpringApplicationBuilder
   SpringApplicationBuilder builder = createSpringApplicationBuilder();
   StandardServletEnvironment environment = new StandardServletEnvironment();
   environment.initPropertySources(servletContext, null);
   builder.environment(environment);
   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));
   builder.contextClass(AnnotationConfigEmbeddedWebApplicationContext.class);
    
    //调用configure方法,子类重写了这个方法,将SpringBoot的主程序类传入了进来
   builder = configure(builder);
    
    //使用builder创建一个Spring应用
   SpringApplication application = builder.build();
   if (application.getSources().isEmpty() && AnnotationUtils
         .findAnnotation(getClass(), Configuration.class) != null) {
      application.getSources().add(getClass());
   }
   Assert.state(!application.getSources().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.getSources().add(ErrorPageFilterConfiguration.class);
   }
    //启动Spring应用
   return run(application);
}

7、Spring的应用就启动并且创建IOC容器

public ConfigurableApplicationContext run(String... args) {
   StopWatch stopWatch = new StopWatch();
   stopWatch.start();
   ConfigurableApplicationContext context = null;
   FailureAnalyzers analyzers = null;
   configureHeadlessProperty();
   SpringApplicationRunListeners listeners = getRunListeners(args);
   listeners.starting();
   try {
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(
            args);
      ConfigurableEnvironment environment = prepareEnvironment(listeners,
            applicationArguments);
      Banner printedBanner = printBanner(environment);
      context = createApplicationContext();
      analyzers = new FailureAnalyzers(context);
      prepareContext(context, environment, listeners, applicationArguments,
            printedBanner);
       
       //刷新IOC容器
      refreshContext(context);
      afterRefresh(context, applicationArguments);
      listeners.finished(context, null);
      stopWatch.stop();
      if (this.logStartupInfo) {
         new StartupInfoLogger(this.mainApplicationClass)
               .logStarted(getApplicationLog(), stopWatch);
      }
      return context;
   }
   catch (Throwable ex) {
      handleRunFailure(context, listeners, analyzers, ex);
      throw new IllegalStateException(ex);
   }
}

【完】

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值