SpringBoot
脚手架
自动装配
通过@Import导入AutoConfigurationImportSelector.class 实现DeffredImportSelector,然后在process方法扫描所有jar包中的spring.factories,
把扫描到的完整包名的字符串放到list里面,然后排序返回给spring
其中使用DeffredImportSelector是必要的,它在@bean,@Component等加载完成后加载,spring加载前会判断是否加载了这个bean,有才加载(conditionalOnBean)所以先来的先加载,DeffredImportSelector最后加载保证用户自定义的先加载
编写starter
编写装配类
- 自动装配类
@Configuration // 让ConfigurationProperties注解生效, @EnableConfigurationProperties(IndexProperties.class) // 配置文件配置了com.zwq.index.words这个属性,才让这个配置类生效 @ConditionalOnProperty("com.zwq.index.words") public class IndexAutoConfiguration { @Autowired IndexProperties indexProperties; @Bean public IndexController indexController() { return new IndexController(indexProperties); } }
- 配置文件读取到类
@ConfigurationProperties(prefix = "com.zwq.index") public class IndexProperties { private String words; public String getWords() { return words; } public void setWords(String words) { this.words = words; } }
- 配个接口
@RestController public class IndexController { IndexProperties indexProperties; public IndexController(IndexProperties indexProperties) { this.indexProperties = indexProperties; } @RequestMapping("/") public String index() { return indexProperties.getWords(); } }
编写spring.factories
在resources下新建META-INF文件夹 -> spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.zwq.test.IndexAutoConfiguration
引用
在其它项目引用
<dependency>
<groupId>com.zwq</groupId>
<artifactId>index-stater</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
启动流程
new SpringApplication(初始化一些信息)
this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); // 启动类放入primarySources this.primarySources = new LinkedHashSet(Arrays.asList(primarySources)); // 根据classpath,推算当前应用类型是servle还是webFlux this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 获取引导器 去spring.factories 文件中找 org.springframework.boot.Bootstrapper this.bootstrapRegistryInitializers = this.getBootstrapRegistryInitializersFromSpringFactories(); // 去spring.factories获取所有key(ApplicationContextInitializer)对外扩展 this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class)); // 去spring.factories获取所有key(ApplicationListener)监听器,解耦,例:读取全局配置文件 this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class)); // 根据main推算所在类 this.mainApplicationClass = this.deduceMainApplicationClass(); 1. 获取启动类,根据启动类加载IOC 2. 获取web应用类型 3. 在spring.factories读取对外扩展的ApplicationContextInitializer,ApplicationListener扩展,解耦(全局配置文件,热部署插件) 4. 根据main推算所在类
run
// 记录开始时间 StopWatch stopWatch = new StopWatch(); stopWatch.start(); // 创建引导上下文环境 DefaultBootstrapContext bootstrapContext = this.createBootstrapContext(); // 任何spring上下文接口 ConfigurableApplicationContext context = null; // 开启HeadLess模式 this.configureHeadlessProperty(); // 用来发布事件,运行监听器 SpringApplicationRunListeners listeners = this.getRunListeners(args); // 发布事件:容器加载开始 listeners.starting(bootstrapContext, this.mainApplicationClass); try { // 封装命令行参数ApplicationArguments ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // 读取环境配置信息,例:读取全局配置文件 ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments); // 忽略beaninfo的bean this.configureIgnoreBeanInfo(environment); Banner printedBanner = this.printBanner(environment); // 根据webApplicationType创建Spring上下文 context = this.createApplicationContext(); context.setApplicationStartup(this.applicationStartup); // 预初始化spring上下文(读取注册配置类) this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); // 同spring的refresh方法:加载ioc容器,注册bean定义,加载bean,这里在onRefresh创建了servlet容器 this.refreshContext(context); // 留给用户自定义容器刷新完成后的处理逻辑 this.afterRefresh(context, applicationArguments); stopWatch.stop(); if (this.logStartupInfo) { (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch); } listeners.started(context); this.callRunners(context, applicationArguments); } catch (Throwable var10) { this.handleRunFailure(context, var10, listeners); throw new IllegalStateException(var10); }
内嵌Tomcat
this.refreshContext(context); -> this.onRefresh(); -> this.createWebServer(); // 去容器获取ServletWebServerFactory.class bean ServletWebServerFactory factory = this.getWebServerFactory(); // new tomcat 启动tomcat 挂起tomcat this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()}); // 回调getSelfInitializer private void selfInitialize(ServletContext servletContext) throws ServletException { this.prepareWebApplicationContext(servletContext); this.registerApplicationScope(servletContext); WebApplicationContextUtils.registerEnvironmentBeans(this.getBeanFactory(), servletContext); // 能够拿到DispatcherServlet Iterator var2 = this.getServletContextInitializerBeans().iterator(); while(var2.hasNext()) { ServletContextInitializer beans = (ServletContextInitializer)var2.next(); // 注册servlet beans.onStartup(servletContext); } }
this.createWebServer():
判断servletContext是否存在,不存在就说明使用内嵌servlet容器,就去创建servlet容器外置Tomcat
SPI:Service Provider Interface,服务发现机制,在ClassPath的META-INF/services查找文件,自动加载文件里定义的类
打包
当使用spring-boot-maven-plugin打包的时候,
会把jar包打好,生成一个MANIFEST.MF
运行java -jar会找manifest文件,找到启动类Main-Class,使用JarLauncher自定义加载器去加载jar文件,实现fat jar(jar里面放着jar)启动
之后拿到Start-Class属性值:springboot启动类,然后通过反射执行main方法