SpringBoot的一大优点就是Starter,因为SpringBoot有不少开箱即用的Starter依赖,使得咱们开发变得简单,咱们不须要过多的关注框架的配置。java
在平常开发中,咱们也会自定义一些Starter,特别是如今微服务框架,咱们一个项目分红了多个单体项目,而这些单体项目中会引用公司的一些组件,这个时候咱们定义Starter,可使这些单体项目快速搭起,咱们只须要关注业务开发。web
在此以前咱们再深刻的了解下SpringBoot启动原理。然后再将如何自定义starter。spring
apache
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); }
这里是建立一个SpringApplication对象,并调用了run方法缓存
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); //保存主配置类 this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); //肯定web应用类型 this.webApplicationType = WebApplicationType.deduceFromClasspath(); //从类路径下找到META-INF/spring.factories配置的全部ApplicationContextInitializer;而后保存起来 setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)); //从类路径下找到ETA-INF/spring.factories配置的全部ApplicationListener setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); //从多个配置类中找到有main方法的主配置类 this.mainApplicationClass = deduceMainApplicationClass(); }
从这个方法中能够看出,这个springboot
第一步:保存主配置类。mybatis
第二步:肯定web应用类型。app
第三步:setInitializers方法,这个方法走咱们看带入的参数是getSpringFactoriesInstances(ApplicationContextInitializer.class),咱们再往下查看getSpringFactoriesInstances框架
再进入这个方法:less
这里就是从类路径下找到META-INF/spring.factories配置的全部ApplicationContextInitializer,而后再保存起来,放开断点,咱们能够看到这个时候获取到的
public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); configureHeadlessProperty(); //从类路径下META-INF/spring.factories获取SpringApplicationRunListeners SpringApplicationRunListeners listeners = getRunListeners(args); //回调全部的获取SpringApplicationRunListener.starting()方法 listeners.starting(); try { //封装命令行参数 ApplicationArguments applicationArguments = new DefaultApplicationArguments( args); //准备环境 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);//建立环境完成后回调SpringApplicationRunListener.environmentPrepared();表示环境准备完成 configureIgnoreBeanInfo(environment); //打印Banner图 Banner printedBanner = printBanner(environment); //建立ApplicationContext,决定建立web的ioc仍是普通的ioc context = createApplicationContext(); //异常分析报告 exceptionReporters = getSpringFactoriesInstances( SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); //准备上下文环境,将environment保存到ioc中 //applyInitializers():回调以前保存的全部的ApplicationContextInitializer的initialize方法 //listeners.contextPrepared(context) //prepareContext运行完成之后回调全部的SpringApplicationRunListener的contextLoaded() prepareContext(context, environment, listeners, applicationArguments, printedBanner); //刷新容器,ioc容器初始化(若是是web应用还会建立嵌入式的Tomcat) //扫描,建立,加载全部组件的地方,(配置类,组件,自动配置) refreshContext(context); afterRefresh(context, applicationArguments); stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch); } //全部的SpringApplicationRunListener回调started方法 listeners.started(context); //从ioc容器中获取全部的ApplicationRunner和CommandLineRunner进行回调, //ApplicationRunner先回调,CommandLineRunner再回调 callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try { //全部的SpringApplicationRunListener回调running方法 listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } //整个SpringBoot应用启动完成之后返回启动的ioc容器 return context; }
前面的代码不用分析,主要是准备对象,咱们从 SpringApplicationRunListeners listeners = getRunListeners(args)开始分析,
第一步:是从类路径下META-INF/spring.factories获取SpringApplicationRunListeners,
这个方法跟前面分析的两个获取配置方法相似。
第二步:回调全部的获取SpringApplicationRunListener.starting()方法。
第三步: 封装命令行参数。
第四步:准备环境,调用prepareEnvironment方法。
第五步:打印Banner图(就是启动时的标识图)。
第六步:建立ApplicationContext,决定建立web的ioc仍是普通的ioc。
第七步:异常分析报告。
第八步:准备上下文环境,将environment保存到ioc中,这个方法须要仔细分析下,咱们再进入这个方法
这里面有一个applyInitializers方法,这里是回调以前保存的全部的ApplicationContextInitializer的initialize方法
还有一个listeners.contextPrepared(context),这里是回调全部的SpringApplicationRunListener的contextPrepared(),
最后listeners.contextLoaded(context) 是prepareContext运行完成之后回调全部的SpringApplicationRunListener的contextLoaded()。
第九步:刷新容器,ioc容器初始化(若是是web应用还会建立嵌入式的Tomcat),这个就是扫描,建立,加载全部组件的地方,(配置类,组件,自动配置)。
第十步:全部的SpringApplicationRunListener回调started方法。
第十一步:从ioc容器中获取全部的ApplicationRunner和CommandLineRunner进行回调,ApplicationRunner先回调,CommandLineRunner再回调。
第十二步:全部的SpringApplicationRunListener回调running方法。
第十三步:整个SpringBoot应用启动完成之后返回启动的ioc容器。
这就是run的所有过程,想要更详细的了解还需本身去看源码。
自定义starter(场景启动器),咱们要作的事情是两个:肯定依赖和编写自动配置。咱们重点要作的就是编写自动配置,咱们以前写过一些自动配置,主要是注解配置的使用,主要的注解有:
@Configuration :指定这个类是一个配置类
@ConditionalOnXXX :在指定条件成立的状况下自动配置类生效
@AutoConfigureAfter:指定自动配置类的顺序
@Bean:给容器中添加组件
@ConfigurationPropertie:结合相关xxxProperties类来绑定相关的配置
@EnableConfigurationProperties:让xxxProperties生效加入到容器中
按照这些注解写好自动配置类后,咱们还须要进行自动配置的加载,加载方式是将须要启动就加载的自动配置类,配置在META-INF/spring.factories,启动器的大体原理是如此,而启动器的实际设计是有必定模式的,就是启动器模块是一个空 JAR 文件,仅提供辅助性依赖管理,而自动配置模块应该再从新设计一个,而后启动器再去引用这个自动配置模块。Springboot就是如此设计的:
另外还有一个命名规则:
官方命名空间
– 前缀:“spring-boot-starter-”
– 模式:spring-boot-starter-模块名
– 举例:spring-boot-starter-web、spring-boot-starter-actuator、spring-boot-starter-jdbc
自定义命名空间
– 后缀:“-spring-boot-starter”
– 模式:模块-spring-boot-starter
– 举例:mybatis-spring-boot-starter
具体的建立过程就不赘述了,就是最简单的项目,去掉不须要的文件,建立完成结构以下:
第三步:咱们先将自动配置模块导入starter中,让启动模块依赖自动配置模块
启动模块的POM文件加入依赖
<dependencies> <!--引入自动配置模块--> <dependency> <groupId>com.yuanqinnan-starter</groupId> <artifactId>yuanqinnan-springboot-starter-autoconfigurer</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> </dependencies>
自动配置模块的完整POM文件:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.4.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.yuanqinnan-starter</groupId> <artifactId>yuanqinnan-springboot-starter-autoconfigurer</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <properties> <java.version>1.8</java.version> </properties> <dependencies> <!--引入spring-boot-starter;全部starter的基本配置--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> </dependencies> </project>
至此,两个项目基本建立完成,如今咱们实现简单的配置。
第五步:对自动配置类进行自动配置代码编写
先编写一个配置类,用于配置:
@ConfigurationProperties(prefix = "yuanqinnan.hello") public class HelloProperties { //前缀 private String prefix; //后缀 private String suffix; public String getPrefix() { return prefix; } public void setPrefix(String prefix) { this.prefix = prefix; } public String getSuffix() { return suffix; } public void setSuffix(String suffix) { this.suffix = suffix; } }
再编写一个服务
public class HelloService { HelloProperties helloProperties; public HelloProperties getHelloProperties() { return helloProperties; } public void setHelloProperties(HelloProperties helloProperties) { this.helloProperties = helloProperties; } public String sayHello(String name) { return helloProperties.getPrefix() + "-" + name + helloProperties.getSuffix(); } }
而后再将这个服务注入组件:
@Configuration @ConditionalOnWebApplication //web应用才生效 @EnableConfigurationProperties(HelloProperties.class) public class HelloServiceAutoConfiguration { @Autowired HelloProperties helloProperties; @Bean public HelloService helloService(){ HelloService service = new HelloService(); service.setHelloProperties(helloProperties); return service; } }
这个时候咱们的自动配置以及写完,还差最后一步,由于SpringBoot读取自动配置是在META-INF的spring.factories文件中,因此咱们还要将咱们的自动配置类写入其中
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.yuanqinnan.starter.HelloServiceAutoConfiguration
最后的结构以下:
至此,代码以及编写完成,这个时候咱们将其装入仓库中,让其余项目引用
建立一个web项目,而后在项目中引入依赖
<!--引入自定义starter--> <dependency> <groupId>com.yuanqinnan.starter</groupId> <artifactId>yuanqinnan-springboot-starter</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
在application.properties 配置中加上配置:
yuanqinnan.hello.prefix=早安
yuanqinnan.hello.suffix=晚安
加入测试:
@Autowired HelloService helloService; @Test public void contextLoads() { System.out.println(helloService.sayHello("世界")); }
这样自定义Starter和引用自定义都已完成,Springboot的核心知识已经总结完成,后面再进行Springboot的一些高级场景整合,如缓存、消息、检索、分布式等。