【Spring】原來SpringBoot是這樣玩的
阿新 • • 發佈:2020-06-30
菜瓜:我自己去調Mvc的原始碼差點沒給Spring的邏輯秀死。。。難受
水稻:那今天咱們看一個簡單易用的SpringBoot吧
菜瓜:可以,這個我熟悉
水稻:熟悉?
菜瓜:當我沒說,請開始你的表演
水稻:我沒有別的意思,就是單純的反問(手動狗頭)。平時工作中用多了SpringBoot。咱們今天帶著幾個問題來看看它的操作吧
- 如何啟動Spring容器
- 如何內嵌Tomcat容器
- 如何完成自動裝配,就是0配置
菜瓜:你確定這是我熟悉的SpringBoot???
水稻:。。。看過來
- 啟動類點進去
-
public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ... listeners.starting(); try { ... // ①建立Spring上下文容器物件 - 預設Servlet容器 context = createApplicationContext(); ... // ②呼叫refresh方法 - 回到熟悉的容器啟動流程 refreshContext(context); afterRefresh(context, applicationArguments); ... ... return context; }
- ① 建立上下文物件
-
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) { ... } } return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass); }
-
- ②啟動容器
-
@Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { prepareRefresh(); ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { ... // ①springboot 內嵌tomcat容器 onRefresh(); ... } @Override protected void onRefresh() { super.onRefresh(); try { // ②建立Servlet容器 預設tomcat createWebServer(); } ... } private void createWebServer() { WebServer webServer = this.webServer; ServletContext servletContext = getServletContext(); if (webServer == null && servletContext == null) { ServletWebServerFactory factory = getWebServerFactory(); // ③看進去 回到mvc整合tomcat的場景 this.webServer = factory.getWebServer(getSelfInitializer()); } ... initPropertySources(); } @Override public WebServer getWebServer(ServletContextInitializer... initializers) { Tomcat tomcat = new 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); return getTomcatWebServer(tomcat); }
-
水稻:好了,第一步和第二步完成了
菜瓜:就這???
水稻:是不是極其簡單,令人髮指。重頭戲是後面的自動裝配
- 回到咱們啟動類的註解上
-
... // 標記自身被掃描 @SpringBootConfiguration // 下一步 - 自動裝配入口 @EnableAutoConfiguration // 掃描bean路徑 - 約定是啟動類所在的包:所以沒事別把啟動類挪走(都是淚) @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication -> @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration -> 重要 public class AutoConfigurationImportSelector ... { @Override public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) { ... // 獲取以EnableAutoConfiguration命名的/META-INF/Spring.factories檔案中的value去重 AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector).getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata); // 啟動的時候斷點可以看到
this.autoConfigurationEntries.add(autoConfigurationEntry); for (String importClassName : autoConfigurationEntry.getConfigurations()) { this.entries.putIfAbsent(importClassName, annotationMetadata); } } } -
AutoConfigurationImportSelector 中的process是被ConfigurationClassPostProcessor通過processConfigBeanDefinitions方法呼叫(呼叫鏈如下) 1. this.processConfigBeanDefinitions(registry); 2. parser.parse(candidates); 3. this.parse(((AnnotatedBeanDefinition)bd).getMetadata(), holder.getBeanName()); 4. sourceClass = this.doProcessConfigurationClass(configClass, sourceClass); 5. this.processImports(configClass, sourceClass, this.getImports(sourceClass), true); 6. this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector)selector); 7. handler.processGroupImports(); 8. grouping.getImports().forEach... 9. this.group.process(...); -- 蒐集到需要自動裝配的類,封裝成BeanDefinition後續例項化,實現自動裝配功能 譬如引入WebMvcAutoConfiguration類 - webmvc功能自動整合 org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
菜瓜:原來如此。你把呼叫鏈拎出來就簡單了很多。自動裝配就是通過SPI載入org.springframework.boot.autoconfigure包下的class,封裝成BeanDefinition後交給容器載入
總結:SpringBoot只需要一行程式碼便能啟動一個Java應用。完全解放開發者複雜的配置
- 內嵌Servlet容器,預設tomcat
- 啟動SpringWeb容器
- 自動裝配了簡單web應用需要的工具和組建
&n