做者:koalajava
https://blog.csdn.net/believer123/java/article/details/70196572web
經過SpringBoot整合各個框架是愈來愈方便了,整合SpringMVC只須要添加對應的starer依賴便可。
spring
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
並且還配備了Tomcat的starter編程
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
這樣,只須要根據自身需求,設置配置文件。啓動web服務器只須要運行java application就能夠了,再也不須要部署到tomcat服務了。tomcat
以前一直很好奇,使用SpringMVC時須要在web.xml上配置DispatcherServlet。而整合了SpringBoot後爲何就不須要配置了,下面就進行完整的分析。服務器
看着累?能夠直接看步驟7,核心分析。微信
一、尋找入口,找到WebServlet自動配置類:app
EmbeddedServletContainerAutoConfiguration框架
org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@ConditionalOnWebApplication
@Import(BeanPostProcessorsRegistrar.class)
public class EmbeddedServletContainerAutoConfiguration{
...省略代碼
}
SpringBoot 自動配置功能類都以AutoConfiguration結尾koa
二、注入須要的Bean
從類上的註解能夠看出,導入了BeanPostProcessorsRegistrar,來添加EmbeddedServletContainerCustomizerBeanPostProcessor。首先會查看工程是否有自定的EmbeddedServletContainerCustomizerBeanPostProcessor,若是沒有,則注入默認的EmbeddedServletContainerCustomizerBeanPostProcessor。代碼以下:
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitionRegistry registry) {
if (this.beanFactory == null) {
return;
}
if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(
EmbeddedServletContainerCustomizerBeanPostProcessor.class, true,
false))) {
registry.registerBeanDefinition( "embeddedServletContainerCustomizerBeanPostProcessor",
new RootBeanDefinition(
EmbeddedServletContainerCustomizerBeanPostProcessor.class));
}
if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(
ErrorPageRegistrarBeanPostProcessor.class, true, false))) {
registry.registerBeanDefinition("errorPageRegistrarBeanPostProcessor",
new RootBeanDefinition(
ErrorPageRegistrarBeanPostProcessor.class));
}
}
實現ImportBeanDefinitionRegistrar接口,實現注入須要的Bean到Spring容器中,Mybatis(MapperScannerRegistrar)也是經過此接口來完成Mapper類的定義。
三、步驟2注入了bean:
EmbeddedServletContainerCustomizerBeanPostProcessor,該類實現了在ConfigurableEmbeddedServletContainer對象初始化前,進行行必要的參數配置。
獲取全部EmbeddedServletContainerCustomizer對象
調用EmbeddedServletContainerCustomizer.customize方法
EmbeddedServletContainerCustomizer實現類根據自身需求設置WebServlet容器參數(如:端口號、鏈接數等等)
核心代碼以下:
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)throws BeansException {
if (bean instanceof ConfigurableEmbeddedServletContainer) { postProcessBeforeInitialization((ConfigurableEmbeddedServletContainer) bean);
}
return bean;
}
private void postProcessBeforeInitialization(ConfigurableEmbeddedServletContainer bean) {
for (EmbeddedServletContainerCustomizer customizer : getCustomizers()) {
customizer.customize(bean);
}
}
ConfigurableEmbeddedServletContainer:是Web容器的接口,默認注入的有
BeanPostProcessor :是Spring容器的回調接口,在全部Bean初始化以前和以後分別回調此接口的postProcessBeforeInitialization,postProcessAfterInitialization方法。這樣就能夠根據需求在Bean初始化先後配置設置須要的功能。
經過步驟1-3完成了Web容器啓動前的參數配置功能。
四、EmbeddedWebApplicationContext入場Spring容器配置加載完成後,會回調EmbeddedWebApplicationContext.refresh方法。
EmbeddedWebApplicationContext在執行refresh方法中,調用了onRefresh方法進行ServletContainer配置。代碼以下:
@Override
public final void refresh() throws BeansException, IllegalStateException {
...省略
super.refresh();
...省略
}
@Override
protected void onRefresh() {
...省略
//建立ServletContainer
createEmbeddedServletContainer();
...省略
}
EmbeddedWebApplicationContext實現了接口ConfigurableApplicationContext, Spring容器配置加載完成後會回調全部的ConfigurableApplicationContext對象的refresh方法。
1.在onRefresh方法中,獲取EmbeddedServletContainerFactory對象,由於工程上使用Tomcat,因此這裏就是TomcatEmbeddedServletContainerFactory
2.執行EmbeddedServletContainerFactory.getEmbeddedServletContainer方法
五、TomcatEmbeddedServletContainerFactory.getEmbeddedServletContainer這裏是Tomcat容器核心功能完的地方。主要完成了對Tomcat配置(不是這篇重點,省略代碼),在configureContext方法添加Tomcat容器啓動回調接口(重點)。
protected void configureContext(Context context,
ServletContextInitializer[] initializers) {
TomcatStarter starter = new TomcatStarter(initializers);
if (context instanceof TomcatEmbeddedContext) {
// Should be true
((TomcatEmbeddedContext) context).setStarter(starter);
}
...省略
}
ServletContainerInitializer是Tomcat容器啓動的一個回調接口。
在Tomcat啓動前,SpringBoot經過TomcatStarter完成Servlet,Filter等Web組件的組注入
六、TomcatStarter,在Tomcat啓動後回調onStartup。
七、EmbeddedWebApplicationContext在onStartup回調中完成SpringMvc功能注入
7.一、在selfInitialize方法中獲取到全部ServletContextInitializer對象,並調用其onStartup方法
7.二、ServletContextInitializer實現類以下:
7.三、經過上圖就很清楚的說明了Servlet,Filter等Web組件實現類
7.四、在ServletRegistrationBean向ServletContainer添加Servlet
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
Assert.notNull(this.servlet, "Servlet must not be null");
String name = getServletName();
if (!isEnabled()) {
logger.info("Servlet " + name + " was not registered (disabled)");
return;
}
logger.info("Mapping servlet: '" + name + "' to " + this.urlMappings);
Dynamic added = servletContext.addServlet(name, this.servlet);
if (added == null) {
logger.info("Servlet " + name + " was not registered "
+ "(possibly already registered?)");
return;
}
configure(added);
}
7.五、這裏也就解釋了SpringBoot官方文檔70.1節上爲何是經過RegistrationBean添加Servlet與Filter的緣由了。
7.六、DispatcherServletAutoConfiguration.DispatcherServletRegistrationConfiguration此處添加SpringMVC核心功能類DispatcherServlet
@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());
registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
registration.setLoadOnStartup(
this.webMvcProperties.getServlet().getLoadOnStartup());
if (this.multipartConfig != null) {
registration.setMultipartConfig(this.multipartConfig);
}
return registration;
}
以上只是將重要點抽出來講明,貼上所有源碼也是無心義的。要理解其中過程還須要自行查看源碼。
經過以上步驟分析了SpringBoot集成SpringMVC和Tomcat功能簡要步驟。其實只要找到了入口,便可Debug一步一步的走下去,來查看內部實現。
總結
經過以上分析和Mybatis功能分析,發現滿滿的都是套路。在SpringBoot上實現自定義Starter功能應該都是以下套路:
一、在自定義的XXAutoConfiguration上Import一個ImportBeanDefinitionRegistrar來注入指定Bean
二、添加自定義的BeanPostProcessor在Bean初始化以前或以後完成配置功能或初始化某些依賴功能
END
我是武哥,最後給你們 免費分享我寫的 10 萬字 Spring Boot 學習筆記(帶完整目錄)以及對應的源碼 。這是我以前在 CSDN 開的一門課,因此筆記很是詳細完整,我準備將資料分享出來給你們免費學習,相信你們看完必定會有所收穫( 下面有下載方式 )。
能夠看出,我當時備課很是詳細,目錄很是完整,讀者能夠手把手跟着筆記,結合源代碼來學習。如今免費分享出來,有須要的讀者能夠下載學習,就在我公衆號回覆:筆記,就行。
若有文章對你有幫助,
在看和轉發是對我最大的支持!
關注Java開發寶典
天天學習技術乾貨
點贊是最大的支持
本文分享自微信公衆號 - 武哥聊編程(eson_15)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。