Spring Boot默認(rèn)使用Tomcat作為嵌入式的Servlet容器汛蝙,只要引入了spring-boot-start-web依賴豺撑,則默認(rèn)是用Tomcat作為Servlet容器:
1.1质帅、定制和修改Servlet容器的相關(guān)配置
1)适揉、修改和server有關(guān)的配置(ServerProperties,它其實(shí)也是EmbeddedServletContainerCustomizer的子類):
server.port=8080
server.context-path=/
# tomcat相關(guān)設(shè)置
server.tomcat.uri-encoding=UTF-8
2)煤惩、編寫EmbeddedServletContainerCustomizer(嵌入式的Servlet容器的定制器)來(lái)修改Servlet容器的配置嫉嘀,返回一個(gè)自定義的定制器Bean:
@Bean
public EmbeddedServletContainerCustomizer embeddedServletContainerCustomizer(){
//定制嵌入式的Servlet容器相關(guān)的屬性配置
return container -> container.setPort(8083);
}
1.2、注冊(cè)Servlet容器的三大組件(Servlet魄揉、Filter剪侮、Listener)
? 由于Spring Boot默認(rèn)是以jar包的形式啟動(dòng)嵌入式的Servlet容器,從而來(lái)啟動(dòng)Spring Boot的web應(yīng)用什猖,沒(méi)有web.xml文件票彪。
? 以前編寫三大組件大多都需要在web.xml文件中進(jìn)行配置(使用注解除外,@WebServlet不狮,@WebListener降铸,@WebFilter),而現(xiàn)在使用SpringBoot作為框架摇零,如果需要編寫三大組件推掸,則需要使用配置的方式進(jìn)行注冊(cè)。
要注冊(cè)三大組件:
- ServletRegistrationBean:注冊(cè)Servlet
//Servlet定義
public class MyServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("這是一個(gè)servlet請(qǐng)求...");
}
}
//Servlet注冊(cè)
@Configuration
public class MyServletConfig {
//注冊(cè)Servlet
@Bean
public ServletRegistrationBean myServlet(){
return new ServletRegistrationBean(new MyServlet(), "/myServlet");
}
}
- FilterRegistrationBean:注冊(cè)Filter
//Filter定義
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("MyFilter process...");
//放行
chain.doFilter(request, response);
}
@Override
public void destroy() {
}
}
//Filter注冊(cè)
@Bean
public FilterRegistrationBean myFilter(){
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setFilter(new MyFilter());
bean.setUrlPatterns(Arrays.asList("/hello", "/myServlet"));
return bean;
}
- ServletListenerRegistrationBean:注冊(cè)Listener
//Listener定義
public class MyListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("contextInitialized...web啟動(dòng)");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("contextDestroyed...web銷毀");
}
}
//Listener注冊(cè)
@Bean
public ServletListenerRegistrationBean myListener(){
return new ServletListenerRegistrationBean<>(new MyListener());
}
? 最熟悉的莫過(guò)于驻仅,在Spring Boot在自動(dòng)配置SpringMVC的時(shí)候谅畅,會(huì)自動(dòng)注冊(cè)SpringMVC前端控制器:DispatcherServlet,該控制器主要在DispatcherServletAutoConfiguration自動(dòng)配置類中進(jìn)行注冊(cè)的噪服。
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@ConditionalOnWebApplication
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(EmbeddedServletContainerAutoConfiguration.class)
public class DispatcherServletAutoConfiguration {
//other code...
public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";
private String servletPath = "/";
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public ServletRegistrationBean dispatcherServletRegistration(
DispatcherServlet dispatcherServlet) {
ServletRegistrationBean registration = new ServletRegistrationBean(
dispatcherServlet, this.serverProperties.getServletMapping());
//默認(rèn)攔截 / 所有請(qǐng)求毡泻,包括靜態(tài)資源,但是不攔截jsp請(qǐng)求粘优;/*會(huì)攔截jsp
//可以通過(guò)修改server.servlet-path來(lái)修改默認(rèn)的攔截請(qǐng)求
registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
registration.setLoadOnStartup(
this.webMvcProperties.getServlet().getLoadOnStartup());
if (this.multipartConfig != null) {
registration.setMultipartConfig(this.multipartConfig);
}
return registration;
}
}
? 正如源碼中提到仇味,它使用ServletRegistrationBean將dispatcherServlet進(jìn)行注冊(cè),并將urlPattern設(shè)為了/雹顺,這樣就類似原來(lái)的web.xml中配置的dispatcherServlet丹墨。
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
1.3、其他Servlet容器
Spring Boot默認(rèn)支持Tomcat嬉愧,Jetty贩挣,和Undertow作為底層容器。如圖:
而Spring Boot默認(rèn)使用Tomcat,一旦引入spring-boot-starter-web模塊王财,就默認(rèn)使用Tomcat容器卵迂。
切換其他Servlet容器:
? 1)、將tomcat依賴移除掉
? 2)搪搏、引入其他Servlet容器依賴
引入jetty:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<artifactId>spring-boot-starter-tomcat</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
引入undertow:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<artifactId>spring-boot-starter-tomcat</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
切換了Servlet容器后狭握,只是以server開(kāi)頭的配置不一樣外,其他都類似疯溺。
1.4论颅、嵌入式Servlet容器自動(dòng)配置原理
? 其中EmbeddedServletContainerAutoConfiguration是嵌入式Servlet容器的自動(dòng)配置類,該類在spring-boot-autoconfigure-xxx.jar中的web模塊可以找到囱嫩。
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@ConditionalOnWebApplication
@Import(BeanPostProcessorsRegistrar.class)
public class EmbeddedServletContainerAutoConfiguration {
@Configuration
@ConditionalOnClass({ Servlet.class, Tomcat.class })
@ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
public static class EmbeddedTomcat {
@Bean
public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
return new TomcatEmbeddedServletContainerFactory();
}
}
@Configuration
@ConditionalOnClass({ Servlet.class, Server.class, Loader.class,
WebAppContext.class })
@ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
public static class EmbeddedJetty {
@Bean
public JettyEmbeddedServletContainerFactory jettyEmbeddedServletContainerFactory() {
return new JettyEmbeddedServletContainerFactory();
}
}
@Configuration
@ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
@ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
public static class EmbeddedUndertow {
@Bean
public UndertowEmbeddedServletContainerFactory undertowEmbeddedServletContainerFactory() {
return new UndertowEmbeddedServletContainerFactory();
}
}
//other code...
}
? 在這個(gè)自動(dòng)配置類中配置了三個(gè)容器工廠的Bean恃疯,分別是:
TomcatEmbeddedServletContainerFactory
JettyEmbeddedServletContainerFactory
-
UndertowEmbeddedServletContainerFactory
?
這里以大家熟悉的Tomcat為例,首先Spring Boot會(huì)判斷當(dāng)前環(huán)境中是否引入了Servlet和Tomcat依賴墨闲,并且當(dāng)前容器中沒(méi)有自定義的EmbeddedServletContainerFactory的情況下今妄,則創(chuàng)建Tomcat容器工廠。其他Servlet容器工廠也是同樣的道理鸳碧。
1)盾鳞、EmbeddedServletContainerFactory:嵌入式Servlet容器工廠
public interface EmbeddedServletContainerFactory {
EmbeddedServletContainer getEmbeddedServletContainer(
ServletContextInitializer... initializers);
}
內(nèi)部只有一個(gè)方法,用于獲取嵌入式的Servlet容器瞻离。
該工廠接口主要有三個(gè)實(shí)現(xiàn)類腾仅,分別對(duì)應(yīng)三種嵌入式Servlet容器的工廠類,如圖所示:
2)套利、EmbeddedServletContainer:嵌入式Servlet容器
同樣道理推励,對(duì)應(yīng)三種嵌入式Servlet容器,如圖所示:
3)肉迫、以Tomcat容器工廠TomcatEmbeddedServletContainerFactory類為例:
public class TomcatEmbeddedServletContainerFactory
extends AbstractEmbeddedServletContainerFactory implements ResourceLoaderAware {
//other code...
@Override
public EmbeddedServletContainer getEmbeddedServletContainer(
ServletContextInitializer... initializers) {
//創(chuàng)建一個(gè)Tomcat
Tomcat tomcat = new Tomcat();
//配置Tomcat的基本環(huán)節(jié)
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對(duì)象验辞,返回一個(gè)嵌入式Tomcat容器,內(nèi)部會(huì)啟動(dòng)該tomcat容器
return getTomcatEmbeddedServletContainer(tomcat);
}
}
看看TomcatEmbeddedServletContainerFactory#getTomcatEmbeddedServletContainer函數(shù):
protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(
Tomcat tomcat) {
return new TomcatEmbeddedServletContainer(tomcat, getPort() >= 0);
}
該函數(shù)很簡(jiǎn)單喊衫,就是來(lái)創(chuàng)建Tomcat容器并返回跌造。
看看TomcatEmbeddedServletContainer類定義:
public class TomcatEmbeddedServletContainer implements EmbeddedServletContainer {
public TomcatEmbeddedServletContainer(Tomcat tomcat, boolean autoStart) {
Assert.notNull(tomcat, "Tomcat Server must not be null");
this.tomcat = tomcat;
this.autoStart = autoStart;
//初始化嵌入式Tomcat容器,并啟動(dòng)Tomcat
initialize();
}
private void initialize() throws EmbeddedServletContainerException {
TomcatEmbeddedServletContainer.logger
.info("Tomcat initialized with port(s): " + getPortsDescription(false));
synchronized (this.monitor) {
try {
addInstanceIdToEngineName();
try {
final Context context = findContext();
context.addLifecycleListener(new LifecycleListener() {
@Override
public void lifecycleEvent(LifecycleEvent event) {
if (context.equals(event.getSource())
&& Lifecycle.START_EVENT.equals(event.getType())) {
// Remove service connectors so that protocol
// binding doesn't happen when the service is
// started.
removeServiceConnectors();
}
}
});
// Start the server to trigger initialization listeners
//啟動(dòng)tomcat
this.tomcat.start();
// We can re-throw failure exception directly in the main thread
rethrowDeferredStartupExceptions();
try {
ContextBindings.bindClassLoader(context, getNamingToken(context),
getClass().getClassLoader());
}
catch (NamingException ex) {
// Naming is not enabled. Continue
}
// Unlike Jetty, all Tomcat threads are daemon threads. We create a
// blocking non-daemon to stop immediate shutdown
startDaemonAwaitThread();
}
catch (Exception ex) {
containerCounter.decrementAndGet();
throw ex;
}
}
catch (Exception ex) {
stopSilently();
throw new EmbeddedServletContainerException(
"Unable to start embedded Tomcat", ex);
}
}
}
}
到這里就啟動(dòng)了嵌入式的Servlet容器族购,其他容器類似鼻听。
那么問(wèn)題來(lái)了,我們對(duì)嵌入式容器的修改配置是如何生效的联四?
? 之前講過(guò)可以通過(guò)修改ServerProperties相關(guān)配置或者自定義EmbeddedServletContainerCustomizer定制器兩種方式來(lái)修改默認(rèn)配置。而ServerProperties其實(shí)就是EmbeddedServletContainerCustomizer的子類撑教,所以說(shuō)到底還是EmbeddedServletContainerCustomizer起了修改的作用朝墩。
? 其實(shí)在EmbeddedServletContainerAutoConfiguration類上導(dǎo)入了一個(gè)BeanPostProcessorsRegistrar類:
@Import(BeanPostProcessorsRegistrar.class)
該類主要用于給容器導(dǎo)入組件,看定義:
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;
}
//注冊(cè)了一個(gè)EmbeddedServletContainerCustomizerBeanPostProcessor后置處理器的Bean
registerSyntheticBeanIfMissing(registry,
"embeddedServletContainerCustomizerBeanPostProcessor",
EmbeddedServletContainerCustomizerBeanPostProcessor.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);
}
}
}
后置處理器:在bean初始化前(創(chuàng)建完成,還未屬性賦值)收苏,會(huì)執(zhí)行初始化工作亿卤。
所以重點(diǎn)是在registerBeanDefinitions方法中向容器中導(dǎo)入了EmbeddedServletContainerCustomizerBeanPostProcessor類型的Bean:
public class EmbeddedServletContainerCustomizerBeanPostProcessor
implements BeanPostProcessor, BeanFactoryAware {
//初始化之前
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof ConfigurableEmbeddedServletContainer) {
postProcessBeforeInitialization((ConfigurableEmbeddedServletContainer) bean);
}
return bean;
}
private void postProcessBeforeInitialization(
ConfigurableEmbeddedServletContainer bean) {
//獲取所有的定制器,調(diào)用每個(gè)定制器的customize方法鹿霸,給Servlet容器進(jìn)行屬性賦值
for (EmbeddedServletContainerCustomizer customizer : getCustomizers()) {
customizer.customize(bean);
}
}
//從IOC容器中獲取所有類型為EmbeddedServletContainerCustomizer的定制器
private Collection<EmbeddedServletContainerCustomizer> getCustomizers() {
if (this.customizers == null) {
// Look up does not include the parent context
this.customizers = new ArrayList<EmbeddedServletContainerCustomizer>(
//類型為EmbeddedServletContainerCustomizer的Bean
this.beanFactory
.getBeansOfType(EmbeddedServletContainerCustomizer.class,
false, false)
.values());
Collections.sort(this.customizers, AnnotationAwareOrderComparator.INSTANCE);
this.customizers = Collections.unmodifiableList(this.customizers);
}
return this.customizers;
}
//other code...
}
所以之前介紹可以向容器中添加一個(gè)自定義的EmbeddedServletContainerCustomizer類型的組件排吴,用于自定義屬性配置,然后在導(dǎo)入的后置處理器中獲取到該組件懦鼠,并調(diào)用該自定義組件的customize方法钻哩,來(lái)修改默認(rèn)的屬性配置。
總結(jié):
? (1)肛冶、Spring Boot根據(jù)導(dǎo)入容器類型的依賴情況街氢,會(huì)給容器中添加相應(yīng)的EmbeddedServletContainerFactory嵌入式Servlet容器工廠;
? (2)睦袖、應(yīng)用程序一旦導(dǎo)入了嵌入式Servlet容器依賴珊肃,就會(huì)觸動(dòng)后置處理器EmbeddedServletContainerCustomizerBeanPostProcessor;
? (3)馅笙、后置處理器從IOC容器中獲取所有的EmbeddedServletContainerCustomizer類型的嵌入式Servlet容器定制器伦乔,并調(diào)用每個(gè)定制器的定制方法customize,從而修改默認(rèn)屬性配置董习。
1.5烈和、嵌入式Servlet容器啟動(dòng)原理
過(guò)程:
? 1)、Spring Boot應(yīng)用啟動(dòng)運(yùn)行run方法阱飘;
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);
//創(chuàng)建一個(gè)ApplicationContext容器
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);
}
}
? 2)斥杜、createApplicationContext();創(chuàng)建IOC容器,如果是web應(yīng)用沥匈,則創(chuàng)建AnnotationConfigEmbeddedWebApplicationContext的IOC容器蔗喂;如果不是,則創(chuàng)建AnnotationConfigApplicationContext的IOC容器高帖;
public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
+ "annotation.AnnotationConfigApplicationContext";
public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework."
+ "boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext";
//SpringApplication#createApplicationContext
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
//根據(jù)應(yīng)用環(huán)境缰儿,創(chuàng)建不同的IOC容器
contextClass = Class.forName(this.webEnvironment
? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, "
+ "please specify an ApplicationContextClass",
ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
}
?
? 3)、refreshContext(context);Spring Boot刷新IOC容器【創(chuàng)建IOC容器對(duì)象散址,并初始化容器乖阵,創(chuàng)建容器中每一個(gè)組件】;
//SpringApplication#refreshContext
private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);
//Other code...
}
?
? 4)预麸、refresh(context);刷新剛才創(chuàng)建的IOC容器瞪浸;
//SpringApplication#refresh
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
((AbstractApplicationContext) applicationContext).refresh();
}
?
? 5)、調(diào)用抽象父類的refresh()方法吏祸;
//AbstractApplicationContext#refresh
@Override
public 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) {
//...
}
finally {
//...
}
}
}
?
? 6)对蒲、抽象父類AbstractApplicationContext類的子類EmbeddedWebApplicationContext的onRefresh方法;
//EmbeddedWebApplicationContext#onRefresh
@Override
protected void onRefresh() {
super.onRefresh();
try {
createEmbeddedServletContainer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start embedded container",
ex);
}
}
?
? 7)、在createEmbeddedServletContainer方法中會(huì)獲取嵌入式的Servlet容器工廠蹈矮,并通過(guò)工廠來(lái)獲取Servlet容器:
//EmbeddedWebApplicationContext#createEmbeddedServletContainer
private void createEmbeddedServletContainer() {
EmbeddedServletContainer localContainer = this.embeddedServletContainer;
ServletContext localServletContext = getServletContext();
if (localContainer == null && localServletContext == null) {
//獲取嵌入式Servlet容器工廠
EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();
//根據(jù)容器工廠來(lái)獲取對(duì)應(yīng)的嵌入式Servlet容器
this.embeddedServletContainer = containerFactory
.getEmbeddedServletContainer(getSelfInitializer());
}
else if (localServletContext != null) {
try {
getSelfInitializer().onStartup(localServletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context",
ex);
}
}
initPropertySources();
}
?
? 8)砰逻、從IOC容器中獲取嵌入式Servlet容器工廠:
//EmbeddedWebApplicationContext#getEmbeddedServletContainerFactory
protected EmbeddedServletContainerFactory getEmbeddedServletContainerFactory() {
// Use bean names so that we don't consider the hierarchy
String[] beanNames = getBeanFactory()
.getBeanNamesForType(EmbeddedServletContainerFactory.class);
if (beanNames.length == 0) {
throw new ApplicationContextException(
"Unable to start EmbeddedWebApplicationContext due to missing "
+ "EmbeddedServletContainerFactory bean.");
}
if (beanNames.length > 1) {
throw new ApplicationContextException(
"Unable to start EmbeddedWebApplicationContext due to multiple "
+ "EmbeddedServletContainerFactory beans : "
+ StringUtils.arrayToCommaDelimitedString(beanNames));
}
return getBeanFactory().getBean(beanNames[0],
EmbeddedServletContainerFactory.class);
}
在上文中解釋到,如果加入了嵌入式的Servlet容器依賴泛鸟,Spring Boot就會(huì)自動(dòng)配置一個(gè)嵌入式Servlet容器工廠蝠咆。接著就使用后置處理器,來(lái)獲取所有的定制器來(lái)定制配置北滥,所以上述源碼中會(huì)從IOC容器中獲取EmbeddedServletContainerFactory類型的容器工廠刚操,并返回。
?
? 9)碑韵、使用Servlet容器工廠獲取嵌入式的Servlet容器赡茸,具體使用哪個(gè)容器工廠需要看配置環(huán)境依賴:
this.embeddedServletContainer = containerFactory
.getEmbeddedServletContainer(getSelfInitializer());
? 獲取嵌入式的Servlet容器后,會(huì)自動(dòng)啟動(dòng)Servlet容器祝闻。
? 10)占卧、上述過(guò)程是首先啟動(dòng)IOC容器,接著啟動(dòng)嵌入式的Servlet容器联喘,再接著將IOC容器中剩下沒(méi)有創(chuàng)建的對(duì)象獲取出來(lái)华蜒,比如自己創(chuàng)建的controller等等。
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
看看finishBeanFactoryInitialization方法:
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
//other code...
// Instantiate all remaining (non-lazy-init) singletons.
beanFactory.preInstantiateSingletons();
}
大概看看preInstantiateSingletons方法:
@Override
public void preInstantiateSingletons() throws BeansException {
List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);
// Trigger initialization of all non-lazy singleton beans...
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
if (isFactoryBean(beanName)) {
final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
@Override
public Boolean run() {
return ((SmartFactoryBean<?>) factory).isEagerInit();
}
}, getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
}
else {
//注冊(cè)bean
getBean(beanName);
}
}
}
// Trigger post-initialization callback for all applicable beans...
for (String beanName : beanNames) {
Object singletonInstance = getSingleton(beanName);
if (singletonInstance instanceof SmartInitializingSingleton) {
final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
if (System.getSecurityManager() != null) {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
smartSingleton.afterSingletonsInstantiated();
return null;
}
}, getAccessControlContext());
}
else {
smartSingleton.afterSingletonsInstantiated();
}
}
}
}
使用getBean方法來(lái)通過(guò)反射將所有未創(chuàng)建的實(shí)例創(chuàng)建出來(lái)豁遭,并加入到IOC容器中叭喜。
9、使用外置的Servlet容器
嵌入式Servlet容器:
? 優(yōu)點(diǎn):簡(jiǎn)單蓖谢,便攜捂蕴;
? 缺點(diǎn):默認(rèn)不支持jsp,優(yōu)化定制比較復(fù)雜闪幽;
使用外置Servlet容器的步驟:
? 1)啥辨、必須創(chuàng)建一個(gè)war項(xiàng)目,需要建立好web項(xiàng)目的目錄結(jié)構(gòu)盯腌,特別是webapp/WEB-INF/web.xml溉知;
? 2)、嵌入式的Tomcat依賴的scope指定為provided腕够;
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
? 3)级乍、必須編寫一個(gè)SpringBootServletInitializer類的子類,并重寫configure方法帚湘;
public class ServletInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(SpringBoot04WebJspApplication.class);
}
}
? 4)玫荣、啟動(dòng)服務(wù)器。
jar包和war包啟動(dòng)區(qū)別:
? jar包:執(zhí)行SpringBootApplication的run方法大诸,啟動(dòng)IOC容器崇决,然后創(chuàng)建嵌入式的Servlet容器材诽。
? war包:?jiǎn)?dòng)Servlet服務(wù)器,服務(wù)器啟動(dòng)SpringBoot應(yīng)用【SpringBootServletInitializer】恒傻,然后才啟動(dòng)IOC容器。
Servlet3.0+規(guī)則:
? 1)建邓、服務(wù)器啟動(dòng)(web應(yīng)用啟動(dòng))會(huì)創(chuàng)建當(dāng)前web應(yīng)用里面每一個(gè)jar包里面ServletContainerInitializer實(shí)例盈厘;
** 2)、ServletContainerInitializer的實(shí)現(xiàn)放在jar包的META-INF/services文件夾下官边,有個(gè)名為javax.servlet.ServletContainerInitializer的文件沸手,內(nèi)容就是ServletContainerInitializer實(shí)現(xiàn)類的全類名;**
** 3)注簿、還可以使用@HandlesTypes注解契吉,在應(yīng)用啟動(dòng)的時(shí)候加載指定的類。**
流程&原理:
? 1)诡渴、啟動(dòng)Tomcat捐晶;
? 2)、根據(jù)上述描述的Servlet3.0+規(guī)則妄辩,可以在Spring的web模塊里面找到有個(gè)文件名為javax.servlet.ServletContainerInitializer的文件惑灵,而文件的內(nèi)容為org.springframework.web.SpringServletContainerInitializer,用于加載SpringServletContainerInitializer類眼耀;
? 3)英支、看看SpringServletContainerInitializer類定義:
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
/**
* Delegate the {@code ServletContext} to any {@link WebApplicationInitializer}
* implementations present on the application classpath.
* <p>Because this class declares @{@code HandlesTypes(WebApplicationInitializer.class)},
* Servlet 3.0+ containers will automatically scan the classpath for implementations
* of Spring's {@code WebApplicationInitializer} interface and provide the set of all
* such types to the {@code webAppInitializerClasses} parameter of this method.
* <p>If no {@code WebApplicationInitializer} implementations are found on the classpath,
* this method is effectively a no-op. An INFO-level log message will be issued notifying
* the user that the {@code ServletContainerInitializer} has indeed been invoked but that
* no {@code WebApplicationInitializer} implementations were found.
* <p>Assuming that one or more {@code WebApplicationInitializer} types are detected,
* they will be instantiated (and <em>sorted</em> if the @{@link
* org.springframework.core.annotation.Order @Order} annotation is present or
* the {@link org.springframework.core.Ordered Ordered} interface has been
* implemented). Then the {@link WebApplicationInitializer#onStartup(ServletContext)}
* method will be invoked on each instance, delegating the {@code ServletContext} such
* that each instance may register and configure servlets such as Spring's
* {@code DispatcherServlet}, listeners such as Spring's {@code ContextLoaderListener},
* or any other Servlet API componentry such as filters.
* @param webAppInitializerClasses all implementations of
* {@link WebApplicationInitializer} found on the application classpath
* @param servletContext the servlet context to be initialized
* @see WebApplicationInitializer#onStartup(ServletContext)
* @see AnnotationAwareOrderComparator
*/
@Override
public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {
List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>();
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 {
//為所有的WebApplicationInitializer類型的類創(chuàng)建實(shí)例,并加入到集合中
initializers.add((WebApplicationInitializer) 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);
//調(diào)用每一個(gè)WebApplicationInitializer實(shí)例的onStartup方法
for (WebApplicationInitializer initializer : initializers) {
initializer.onStartup(servletContext);
}
}
}
? 在上面一段長(zhǎng)長(zhǎng)的注釋中可以看到哮伟,SpringServletContainerInitializer將@HandlesTypes(WebApplicationInitializer.class)標(biāo)注的所有WebApplicationInitializer這個(gè)類型的類都傳入到onStartup方法的Set參數(shù)中干花,并通過(guò)反射為這些WebApplicationInitializer類型的類創(chuàng)建實(shí)例;
? 4)楞黄、函數(shù)最后池凄,每一個(gè)WebApplicationInitializer實(shí)例都調(diào)用自己的onStartup方法;
public interface WebApplicationInitializer {
void onStartup(ServletContext servletContext) throws ServletException;
}
? 5)谅辣、而WebApplicationInitializer有個(gè)抽象實(shí)現(xiàn)類SpringBootServletInitializer(記住我們繼承了該抽象類)修赞,則會(huì)調(diào)用每一個(gè)WebApplicationInitializer實(shí)例(包括SpringBootServletInitializer)的onStartup方法:
public abstract class SpringBootServletInitializer implements WebApplicationInitializer {
//other code...
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
// Logger initialization is deferred in case a ordered
// LogServletContextInitializer is being used
this.logger = LogFactory.getLog(getClass());
//創(chuàng)建IOC容器
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) {
//創(chuàng)建Spring應(yīng)用構(gòu)建器,并進(jìn)行相關(guān)屬性設(shè)置
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);
//調(diào)用configure方法桑阶,創(chuàng)建war類型的web項(xiàng)目后柏副,由于編寫SpringBootServletInitializer的子類重寫configure方法,所以此處調(diào)用的是我們定義的子類重寫的configure方法
builder = configure(builder);
//通過(guò)構(gòu)建器構(gòu)建了一個(gè)Spring應(yīng)用
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);
}
//啟動(dòng)Spring應(yīng)用
return run(application);
}
//Spring應(yīng)用啟動(dòng)蚣录,創(chuàng)建并返回IOC容器
protected WebApplicationContext run(SpringApplication application) {
return (WebApplicationContext) application.run();
}
}
SpringBootServletInitializer實(shí)例執(zhí)行onStartup方法的時(shí)候會(huì)通過(guò)createRootApplicationContext方法來(lái)執(zhí)行run方法割择,接下來(lái)的過(guò)程就同以jar包形式啟動(dòng)的應(yīng)用的run過(guò)程一樣了,在內(nèi)部會(huì)創(chuàng)建IOC容器并返回萎河,只是以war包形式的應(yīng)用在創(chuàng)建IOC容器過(guò)程中荔泳,不再創(chuàng)建Servlet容器了蕉饼。