@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
@SpringBootApplication
@SpringBootApplication
標註在某個類,說明這個類是SpringBoot的主配置類。
檢視@SpringBootApplication
註解程式碼
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
......
}
@SpringBootConfiguration
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
@AliasFor(
annotation = Configuration.class
)
boolean proxyBeanMethods() default true;
}
就是一個@Configuration
@EnableAutoConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
......
}
@EnableAutoConfiguration
由下面兩個註解組成
@AutoConfigurationPackage
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
用來自動配置包,透過@Import
註解,指定使用AutoConfigurationPackages.Registrar.class
往容器中註冊元件。
// AutoConfigurationPackages
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
......
// AnnotationMetadata metadata為主配置類的註解資訊
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
// 該方法主要是往容器中注入了一個元件, 元件儲存了主配置類所在的包名, 這個包名為自動掃描的包名
register(registry, new PackageImport(metadata).getPackageName());
}
......
}
@AutoConfigurationImportSelector
@AutoConfigurationImportSelector
註解也是往容器中註冊元件。
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
/**
* 返回值就是匯入到容器中的全類名, 這些類就會被註冊到容器中
*/
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
// 這一步會獲取到所有的自動配置類
// 它會從類路徑中拿到所有名為META-INF/spring.factories的配置檔案,獲取EnableAutoConfiguration指定的值。
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
annotationMetadata);
// 返回獲取到的自動配置類
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
例如,檢視spring-boot-autoconfigure包下的META-INF/spring.factories
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
......
此時SpringApplicationAdminJmxAutoConfiguration
和AopAutoConfiguration
等元件就會被註冊到容器中。
這些XxxAutoConfiguration是用來自動往容器中注入元件的。
自動配置類
從上面的分析我們可以知道,SpringBoot會收集META-INF/spring.factories中的自動配置類,下面來分析一下自動配置類的作用。
自動配置常用註解
@Configuration :指定這個類是一個配置類
@ConditionalOnClass :當類路徑classpath下有指定的類的情況下進行自動配置
@ConditionalOnMissingClass:當類路徑下沒有指定的類的條件下
@ConditionalOnBean:當容器(Spring Context)中有指定的Bean的條件下
@ConditionalOnMissingBean:當容器(Spring Context)中沒有指定Bean的情況下進行自動配置
@ConfigurationProperties :結合相關XxxProperties來繫結相關配置, 主要用來把properties配置檔案轉化為對應的XxxProperties來使用的, 並不會把該類放入到IOC容器中
@EnableConfigurationProperties(XxxProperties.class) :註解的作用是使@ConfigurationProperties註解生效並且將XxxProperties注入到IOC容器中。
如果在每個Properties上都使用@Component來標註,那麼也不需要使用@EnableConfigurationProperties({XxxProperties.class})來標註了,同樣也可以在Spring上下文容器中也能獲取到XxxProperties對應的Bean。
@ConditionalOnProperty(prefix = “spring.person”, value = “enabled”, matchIfMissing = true)
當配置檔案中spring.person.enabled=true時進行自動配置,如果沒有設定此值就預設使用matchIfMissing對應的值。如果不設定matchIfMissing = true, 預設為false。
以HttpEncodingAutoConfiguration
為例,當滿足一些條件時,它會自動往ioc容器中注入元件。
// 表示這是一個配置類
@Configuration(proxyBeanMethods = false)
// 將HttpProperties中的屬性與配置檔案進行繫結, 並注入ioc容器中
@EnableConfigurationProperties(HttpProperties.class)
// web應用型別是SERVLET
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
// 當前類路徑不存在CharacterEncodingFilter.class
@ConditionalOnClass(CharacterEncodingFilter.class)
// 當配置檔案中spring.http.encoding.enabled=true時進行自動配置,如果沒有設定此值就預設使用matchIfMissing對應的值。如果不設定matchIfMissing = true, 預設為false。
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {
private final HttpProperties.Encoding properties;
public HttpEncodingAutoConfiguration(HttpProperties properties) {
this.properties = properties.getEncoding();
}
@Bean
// 容器中不存在CharacterEncodingFilter型別的bean時,這個bean才會生效
@ConditionalOnMissingBean
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE));
return filter;
}
}
run()
準備環境
- 執行ApplicationContextInitializer.initialize()方法
- 監聽器回撥SpringApplicationRunListener.contextPrepared()方法
- 載入主配置類定義資訊
- 監聽器回撥SpringApplicationRunListener.contextLoaded()方法
重新整理IOC容器
- 掃描載入所有容器中的元件
- 包括META-INF/spring.factories下的EnableAutoConfiguration元件
回撥容器中所有的ApplicationRunner、CommandLineRunner的run方法
回撥所有SpringApplicationRunListener.running(context)方法
建立SpringApplication物件
檢視啟動類的run方法,將會看到新建一個SpringApplication
, 並將配置類傳遞進去
// SpringApplication.java
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return (new SpringApplication(primarySources)).run(args);
}
檢視建立SpringApplication
物件過程
// SpringApplication.java
public SpringApplication(Class... primarySources) {
this((ResourceLoader)null, primarySources);
}
public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
this.sources = new LinkedHashSet();
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.addConversionService = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = new HashSet();
this.isCustomEnvironment = false;
this.lazyInitialization = false;
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
// 儲存配置類
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
// 獲取當前應用型別: NONE, SERVLET, REACTIVE;
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 從類路徑下尋找META-INF/spring.factories中的所有ApplicationContextInitializer並儲存起來
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 從類路徑下尋找META-INF/spring.factories中的所有ApplicationListener並儲存起來
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
// 從多個配置類中找到有main方法的主配置類
this.mainApplicationClass = this.deduceMainApplicationClass();
}
執行SpringApplication.run()
// SpringApplication.java
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
this.configureHeadlessProperty();
// 獲取SpringApplicationRunListener
// 從類路徑下尋找META-INF/spring.factories中的所有SpringApplicationRunListener並儲存起來
SpringApplicationRunListeners listeners = this.getRunListeners(args);
// 回撥所有的SpringApplicationRunListener.starting()方法
listeners.starting();
Collection exceptionReporters;
try {
// 封裝命令列引數
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 根絕應用型別建立不同的ConfigurableEnvironment
// 建立ConfigurableEnvironment後會回撥
// SpringApplicationRunListener.environmentPrepared(ConfigurableEnvironment environment), 表示環境準備完成
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
// 列印控制檯Banner
Banner printedBanner = this.printBanner(environment);
// 建立ApplicationContext, 根據NONE,SERVLET,REACTIVE型別建立不同的ApplicationContext
// 如果使用預設的Tomcat內嵌伺服器, web型別為SERVLET
// SERVLET => AnnotationConfigServletWebServerApplicationContext
// REACTIVE => AnnotationConfigReactiveWebServerApplicationContext
// NONE => AnnotationConfigApplicationContext
context = this.createApplicationContext();
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
// 1. 準備上下文環境
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 2. 重新整理IOC容器,
// 載入所有容器中的元件,
// 包括META-INF/spring.factories下的EnableAutoConfiguration元件
// 如果是web應用, 還會啟動web容器,後面將會分析web容器是如何啟動的
this.refreshContext(context);
// 這是一個空方法
this.afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
// 回撥所有SpringApplicationRunListener.started(context)方法
listeners.started(context);
// 從ioc容器中獲取ApplicationRunner、CommandLineRunner, 並回撥相關方法
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}
try {
// 回撥所有SpringApplicationRunListener.running(context)方法
listeners.running(context);
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
prepareContext
// SpringApplication.java
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {’
// 為ioc容器設定環境資訊
context.setEnvironment(environment);
postProcessApplicationContext(context);
// 回撥所有 ApplicationContextInitializer.initialize(context)方法
applyInitializers(context);
// 回撥所有 SpringApplicationRunListener.contextPrepared(context)方法
listeners.contextPrepared(context);
// 列印一些日誌資訊
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// 往ioc容器中註冊一些元件
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// 獲取配置類資訊
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0])); // 將配置類資訊註冊到容器中
// 回撥所有SpringApplicationRunListener.contextLoaded(context)方法
listeners.contextLoaded(context);
}
refreshContext
refreshContext方法最終會呼叫AbstractApplicationContext.refresh()
方法,分析Spring原始碼的時候已經分析過該方法了,這個方法會載入所有元件到容器中並例項化,需要注意的是這個方法裡面還呼叫了onRefresh()
方法,這個方法是留給子類實現的。根據前面的分析我們可以知道,若web應用型別為SERVLET,此時的應用上下文為AnnotationConfigServletWebServerApplicationContext
,AnnotationConfigServletWebServerApplicationContext
繼承了ServletWebServerApplicationContext
,
ServletWebServerApplicationContext
實現了AbstractApplicationContext.refresh()
方法,web容器就是在這裡啟動的。
容器自動配置
SpringBoot預設使用Tomcat容器作為內嵌容器。檢視web容器的自動配置類ServletWebServerFactoryAutoConfiguration
。
@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
// 僅在當前應用是 Servlet Web 應用時才生效
@ConditionalOnWebApplication(type = Type.SERVLET)
// 將配置檔案中字首為server的配置引數繫結到ServerProperties, 並將其匯入到容器中
@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);
}
// Tomcat 定製器,
// WebServerFactoryCustomizerBeanPostProcessor會獲取TomcatServletWebServerFactoryCustomizer對 TomcatServletWebServerFactor 進行定製
@Bean
@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(
ServerProperties serverProperties) {
return new TomcatServletWebServerFactoryCustomizer(serverProperties);
}
......
}
ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class
往容器中註冊了兩個BeanPostProcessor
,分別是WebServerFactoryCustomizerBeanPostProcessor
和ErrorPageRegistrarBeanPostProcessor
,這兩個元件用來對WebServerFactory
進行定製化。
1.ServletWebServerFactoryConfiguration.EmbeddedTomcat.class |
---|
往容器中注入TomcatServletWebServerFactory |
2.ServletWebServerFactoryConfiguration.EmbeddedJetty.class |
往容器中注入JettyServletWebServerFactory |
3.ServletWebServerFactoryConfiguration.EmbeddedUndertow.class |
往容器中注入UndertowServletWebServerFactory |
TomcatServletWebServerFactory
、JettyServletWebServerFactory
、UndertowServletWebServerFactory
繼承自抽象基類AbstractServletWebServerFactory
,實現了介面WebServerFactory
,ErrorPageRegistry
。
這些類將分別會被WebServerFactoryCustomizerBeanPostProcessor
和ErrorPageRegistrarBeanPostProcessor
使用。由於程式碼比較簡單,只看WebServerFactoryCustomizerBeanPostProcessor
。
// WebServerFactoryCustomizerBeanPostProcessor.java
public class WebServerFactoryCustomizerBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
private ListableBeanFactory beanFactory;
private List<WebServerFactoryCustomizer<?>> customizers;
@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 {
// 判斷bean的型別是否為WebServerFactory
// 主要對 TomcatServletWebServerFactory、JettyServletWebServerFactory、UndertowServletWebServerFactory進行處理定製化處理
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) {
// 對WebServerFactory進行定製化操作
LambdaSafe.callbacks(WebServerFactoryCustomizer.class, getCustomizers(), webServerFactory)
.withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)
.invoke((customizer) -> customizer.customize(webServerFactory));
}
// 獲取型別為WebServerFactoryCustomizer的元件
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;
}
// 從容器中獲取所有型別為WebServerFactoryCustomizer的元件
@SuppressWarnings({ "unchecked", "rawtypes" })
private Collection<WebServerFactoryCustomizer<?>> getWebServerFactoryCustomizerBeans() {
return (Collection) this.beanFactory.getBeansOfType(WebServerFactoryCustomizer.class, false, false).values();
}
}
建立容器
前面已經分析過,web容器的建立是在ServletWebServerApplicationContext.refresh()
方法裡面。
// ServletWebServerApplicationContext.java
// AnnotationConfigServletWebServerApplicationContext繼承了ServletWebServerApplicationContext,ServletWebServerApplicationContext實現了AbstractApplicationContext.refresh()方法
public class ServletWebServerApplicationContext extends GenericWebApplicationContext
implements ConfigurableWebServerApplicationContext {
@Override
protected void onRefresh() {
super.onRefresh();
try {
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
// 建立web服務
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
// 1. 獲取 ServletWebServerFactory
// 如果使用的是Tomcat, 那麼返回的是 TomcatServletWebServerFactory
ServletWebServerFactory factory = getWebServerFactory();
// 2. 建立web服務, 啟動Tomcat
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();
}
}
1-獲取 ServletWebServerFactory
// ServletWebServerApplicationContext.java
// 獲取 ServletWebServerFactory
protected ServletWebServerFactory getWebServerFactory() {
// 從容器中獲取型別為ServletWebServerFactory的元件名
// 常見的ServletWebServerFactory的實現類有TomcatServletWebServerFactory、UndertowServletWebServerFactory、JettyServletWebServerFactory
String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
if (beanNames.length == 0) {
throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing "
+ "ServletWebServerFactory bean.");
}
// 不能同時存在多個ServletWebServerFactory
if (beanNames.length > 1) {
throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple "
+ "ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));
}
// 從容器中獲取ServletWebServerFactory例項
return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
}
2-建立web服務
// ServletWebServerApplicationContext.java
this.webServer = factory.getWebServer(getSelfInitializer());
getSelfInitializer()
這裡需要注意的是,getSelfInitializer()
這個方法返回了一個ServletContextInitializer
,這裡使用了回撥的方式。
// ServletWebServerApplicationContext.java
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
return this::selfInitialize;
}
ServletContainerInitializer
介面只有一個方法
// ServletContainerInitializer.java
public interface ServletContainerInitializer {
void onStartup(Set<Class<?>> var1, ServletContext var2) throws ServletException;
}
接著往下看
// 當ServletContextInitializer 類被 TomcatStarter 的#onStartup()方法呼叫時才會觸發這裡
private void selfInitialize(ServletContext servletContext) throws ServletException {
// 1. 判斷 ServletContext 裡面是否存在 WebApplicationContext.class.getName() + ".ROOT"
// 2. 如果不存在則往 ServletContext 裡面註冊一個根容器, 如果web型別為SERVLET,那就是註冊了一個AnnotationConfigServletWebServerApplicationContext
prepareWebApplicationContext(servletContext);
// 設定作用域
registerApplicationScope(servletContext);
// 往環境中註冊bean
WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
// 從容器中獲取型別為ServletContextInitializer元件並執行他們的onStartup方法
// 一般使用這個給容器動態地新增Servlet、Filter、Listener
// 注意 ServletContextInitializer 和 servlet3.0 中的 ServletContainerInitializer長得特別像,因為SpringBoot使用內嵌web容器啟動的時候並沒有遵守 servlet 的規範,所以我們不能使用ServletContainerInitializer的來實現動態新增元件,但是可以透過實現ServletContextInitializer介面來動態新增Servlet、Filter、Listener
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
beans.onStartup(servletContext);
}
}
這裡主要做了兩件事情:
- 往
ServletContext中
注入當前的ApplicationContext
,這個就相當於是一個ROOT容器。學習SpringMVC的時候我們知道,SpringMVC共有兩個ioc容器,一個是ServletContextListener
中的ROOT容器,一個是在DispatcherServlet
中的WEB容器,WEB容器的parent為ROOT容器。 - 從ioc容器中獲取所有型別為
ServletContextInitializer
型別的元件,並執行他們的onStartup(ServletContext servletContext)
方法。在SpringBoot中,如果想要實現動態新增Servlet
、Filter
、Listener
元件,需要實現ServletContextInitializer
介面來動態新增,例如:
@Component
public class MyServletContextInitializer implements ServletContextInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
ServletRegistration.Dynamic dynamic = servletContext.addServlet("MyServlet",new MyServlet());
dynamic.addMapping("/myServlet");
}
}
跳過這裡繼續往後看就會發現,這裡返回的ServletContextInitializer 會在 TomcatStarter 的 #onStartup()
方法裡面呼叫它的ServletContextInitializer#onStartup(ServletContext servletContext)
,那時才會觸發這裡的selfInitialize(ServletContext servletContext)
方法。
getWebServer()
建立Tomcat
// TomcatServletWebServerFactory.java
// 這裡就是Tomcat的啟動
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
if (this.disableMBeanRegistry) {
Registry.disableRegistry();
}
Tomcat tomcat = new Tomcat();
File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
connector.setThrowOnFailure(true);
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);
}
// 這裡傳入了 initializers(getSelfInitializer())
prepareContext(tomcat.getHost(), initializers);
return getTomcatWebServer(tomcat);
}
// 這裡只要為了看清楚 initializers 引數被 TomcatStarter 呼叫,因此省略了很多程式碼
protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
// 省略很多程式碼........
ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
// 省略......
configureContext(context, initializersToUse);
// 省略......
}
protected void configureContext(Context context, ServletContextInitializer[] initializers) {
// 建立 TomcatStarter 例項並傳入了 initializers
TomcatStarter starter = new TomcatStarter(initializers);
}
檢視TomcatStarter.onStartup()方法
// TomcatStarter.java
@Override
public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException {
try {
for (ServletContextInitializer initializer : this.initializers) {
// 這裡會觸發 前面提到的selfInitialize(ServletContext servletContext) 方法
initializer.onStartup(servletContext);
}
}
catch (Exception ex) {
// ......
}
}
DispatcherServlet
是在自動配置類DispatcherServletAutoConfiguration
中被建立的。
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
public class DispatcherServletAutoConfiguration {
// The bean name for a DispatcherServlet that will be mapped to the root URL "/"
public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";
// The bean name for a ServletRegistrationBean for the DispatcherServlet "/"
public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";
@Configuration(proxyBeanMethods = false)
@Conditional(DefaultDispatcherServletCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties({ HttpProperties.class, WebMvcProperties.class })
protected static class DispatcherServletConfiguration {
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet(HttpProperties httpProperties, WebMvcProperties webMvcProperties) {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
dispatcherServlet.setEnableLoggingRequestDetails(httpProperties.isLogRequestDetails());
return dispatcherServlet;
}
}
@Configuration(proxyBeanMethods = false)
@Conditional(DispatcherServletRegistrationCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties(WebMvcProperties.class)
@Import(DispatcherServletConfiguration.class)
protected static class DispatcherServletRegistrationConfiguration {
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
webMvcProperties.getServlet().getPath());
registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
multipartConfig.ifAvailable(registration::setMultipartConfig);
return registration;
}
}
}
從上面的程式碼可以看出DispatcherServlet
例項被封裝成DispatcherServletRegistrationBean
注入到ioc容器的,而DispatcherServletRegistrationBean
實現了ServletContextInitializer
介面,因此最終這個DispatcherServlet
會被注入到ServletContext
中。
本作品採用《CC 協議》,轉載必須註明作者和本文連結