文章中涉及到的代码仓库地址:demo-springBoot
SpringBoot的启动流程:
① 通过程序主入口进入;
② 创建SpringApplication对象;
③ 调用SpringApplication对象中的run方法启动SpringBoot;
(一)创建SpringApplication对象
可以在主配置类上打断点,然后单步调试查看SpringBoot启动的运行过程
① 在主配置类上打断点
② 通过Debug的方式启动应用
③ 使用调试工具进入到创建SpringApplication对象的方法内部
获取到的 initializers 的值,此处获取到的值会存储起来,后面的run方法会调用。
获取到的 listenters 的值,此处获取到的值会存储起来,后面的run方法会调用。
SpringApplication类的构成
public class SpringApplication {
......
// run方法通过调用此方法创建SpringApplication对象
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
// 真正创建Application对象的构造方法
@SuppressWarnings({ "unchecked", "rawtypes" })
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));
// 在类路径下查找META-INF/spring.factories配置所有的ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 在多个配置类中找到包含main方法的主程序类
this.mainApplicationClass = deduceMainApplicationClass();
}
......
}
SpringApplication创建过程:
① 判断当前的应用类型;
② 加载并保存所有的ApplicationContextInitializer(META-INF/spring.factoties);
③ 加载并保存所有的ApplicationLisenter(META-INF/spring.factories);
④ 从多个配置类中得到包含main方法的主程序类;
(二)run方法的运行机制
作用:运行Spring 应用,创建并刷新IoC容器
SpringApplication类中 run 方法的构成
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
// 1)调用getRunListeners方法获取SpringApplicationRunListeners
SpringApplicationRunListeners listeners = getRunListeners(args);
// 回调所有的SpringApplicationRunListener对象的starting方法
listeners.starting();
try {
// 将参数进行封装
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 2)调用prepareEnvironment方法准备IoC容器运行所需的环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
// 在控制台打印banner图标
Banner printedBanner = printBanner(environment);
// 3)调用createApplicationContext方法创建IoC容器,并决定IoC容器的类型
context = createApplicationContext();
// 异常报告,出现异常时打印的报告
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 4)调用prepareContext方法准备上下文环境,将environment(IoC环境配置信息)保存到IoC中
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 5)调用refreshContext方法刷新容器,即初始化IoC容器。如果是web应用的话还会创建嵌入式的Servlet容器
refreshContext(context);
// 调用afterRefresh方法,这是个空方法,可能为SpringBoot后期扩展使用
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
// 回调所有的SpringApplicationRunListener对象的started方法
listeners.started(context);
// 调用callRunners方法进行事件回调
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
// 发生异常时调用SpringApplicationRunListener对象的failed方法
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
// 回调所有SpringApplicationRunListener对象的running方法,IoC容器创建完成
listeners.running(context);
}
catch (Throwable ex) {
// 发生异常时调用SpringApplicationRunListener对象的failed方法
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
// 至此整个SpringBoot应用启动完成并返回IoC容器
return context;
}
// 通过我们书写的主配置类调用的run方法,实际上返回的是下面的run方法
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
// 进入到此方法中,创建对象然后调用真正的run方法
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
1)SpringApplication类中的 getRunListeners 方法
// 在类路径META-INF/spring.factories中获取监听器SpringApplicationRunListeners
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
// 获取监听器SpringApplicationRunListeners
return new SpringApplicationRunListeners(logger,
getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
2)SpringApplication类中的 prepareEnvironment 方法,用于准备IoC容器运行所需的环境
// 准备当前IoC容器运行所需的环境
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
// 回调所有SpringApplicationRunListener对象的environmentPrepared方法;此时环境准备完成
listeners.environmentPrepared(environment);
bindToSpringApplication(environment);
// 如果容器是web容器则进行转化
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
// 返回创建好的环境
return environment;
}
3)SpringApplication类中的 createApplicationContext 方法,用于创建IoC容器
// 此方法用于创建IoC容器
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
// 根据项目的类型创建不同的IoC容器
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) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
}
}
// 利用反射创建IoC容器
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
4)SpringApplication类中的 prepareContext 方法用于准备上下文环境
// 此方法用于准备上下文环境,将前准备好的信息添加到IoC容器中
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
// 调用applyInitializers方法获取所有ApplicationContextInitializer对象并调用initialize方法
applyInitializers(context);
// 回调所有的SpringApplicationRunListener对象的contextPrepared()方法
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
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());
}
// Load the sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
// prepareContext运行完成以后回调所有的SpringApplicationRunListener的contextLoaded方法
listeners.contextLoaded(context);
}
4-1)SpringApplication类中的 applyInitializers 方法,获取所有 ApplicationContextInitializer 对象并调用 initialize 方法
// 此方法是回调之前保存的所有的ApplicationContextInitializer的initialize方法。
// ApplicationContextInitializer对象是SpringBoot在启动时获取到并保存的
@SuppressWarnings({ "rawtypes", "unchecked" })
protected void applyInitializers(ConfigurableApplicationContext context) {
for (ApplicationContextInitializer initializer : getInitializers()) {
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
initializer.initialize(context);
}
}
5)SpringApplication类中 refreshContext 方法用于刷新容器,即初始化IoC容器,是创建、扫描、加载所有组建的地方
private void refreshContext(ConfigurableApplicationContext context) {
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
// 调用刷新方法,刷新IoC容器
refresh((ApplicationContext) context);
}
6)SpringApplication类中 callRunners 方法,从IoC容器中获取所有的 ApplicationRunner 和 CommandLineRunner 进行回调
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<>();
// 在IoC容器中获取ApplicationRunner
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
// 在IoC容器中获取CommandLineRunner
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
AnnotationAwareOrderComparator.sort(runners);
for (Object runner : new LinkedHashSet<>(runners)) {
// 先回调ApplicationRunner
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
// 再回调CommandLineRunner
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}
SpringApplication类中run方法的运行过程:
① 回调所有SpringApplicationRunListener对象的 starting 方法;
② 将参数封装到 ApplicationArguments 对象中;
③ 准备IoC容器运行所需的环境,回调所有SpringApplicationRunListener对象的 environmentPrepare 方法;
④ 向控制台输出banner信息(SpringBoot的logo);
⑤ 创建IoC容器对象;
org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext(web环境容器)org.springframework.context.annotation.AnnotationConfigApplicationContext(默认的环境容器)
⑥ 准备上下文环境;此时回调SpringApplicationRunListener对象的 contextPrepared 方法和 contextLoaded 方法
⑦ 刷新容器,即初始化IoC容器,是创建、扫描、加载所有组建的地方;
⑧ 回调所有的SpringApplicationRunListener对象的 started 方法;
⑨ 调用SpringApplication对象的 callRunner 方法;此时回调 ApplicationRunner 和 CommandLineRunner 的 run 方法;
⑩ 回调所有SpringApplicationRunListener对象的 running 方法;
至此整个SpringBoot应用启动完成并返回IoC容器;
run方法的核心:
- 各种监听机制的使用,我们需要在什么时候干预,在对应的时机调用回调即可
refreshContext方法:容器的刷新,即扫描、创建、加载所有组建的地方。我们配置加载到IoC容器中的所有组件都是在此处加载的。
(三)事件监听机制
通过前面对SpringBoot应用启动和run方法运行过程的分析我们可以注意到,SpringBoot的启动和IoC容器的创建依赖于监听器的回调,在合适的时机调用相应的监听器即可达到干预SpringBoot应用启动的目的。
SpringBoot中几个重要的事件回调:
- 配置在META-INF/spring.factories中
- ApplicationContextInitializer
- SpringApplicationRunListener
- 配置在ioc容器中的
- ApplicationRunner
- CommandLineRunner
1. 配置在META-INF/spring.factories中的监听器
1.1 ApplicationContextInitializer接口:
作用:用于初始化Spring的回调接口
ApplicationContextInitializer接口的构成:
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
/**
* Initialize the given application context.
* @param applicationContext the application to configure
*/
void initialize(C applicationContext);
}
ApplicationContextInitializer接口继承关系图:
我们可以实现此接口查看 ApplicationContextInitializer 监听器的回调时机
public class TestApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("ApplicationContextInitializer对象的initialize方法运行了......容器类型为:" + applicationContext);
}
}
1.2 SpringApplicationRunListener接口
作用:SpringApplication对象 run 方法运行时监听器
SpringApplicationRunListener接口的构成:
public interface SpringApplicationRunListener {
/**
* Called immediately when the run method has first started. Can be used for very
* early initialization.
*/
default void starting() {
}
/**
* Called once the environment has been prepared, but before the
* {@link ApplicationContext} has been created.
* @param environment the environment
*/
default void environmentPrepared(ConfigurableEnvironment environment) {
}
/**
* Called once the {@link ApplicationContext} has been created and prepared, but
* before sources have been loaded.
* @param context the application context
*/
default void contextPrepared(ConfigurableApplicationContext context) {
}
/**
* Called once the application context has been loaded but before it has been
* refreshed.
* @param context the application context
*/
default void contextLoaded(ConfigurableApplicationContext context) {
}
/**
* The context has been refreshed and the application has started but
* {@link CommandLineRunner CommandLineRunners} and {@link ApplicationRunner
* ApplicationRunners} have not been called.
* @param context the application context.
* @since 2.0.0
*/
default void started(ConfigurableApplicationContext context) {
}
/**
* Called immediately before the run method finishes, when the application context has
* been refreshed and all {@link CommandLineRunner CommandLineRunners} and
* {@link ApplicationRunner ApplicationRunners} have been called.
* @param context the application context.
* @since 2.0.0
*/
default void running(ConfigurableApplicationContext context) {
}
/**
* Called when a failure occurs when running the application.
* @param context the application context or {@code null} if a failure occurred before
* the context was created
* @param exception the failure
* @since 2.0.0
*/
default void failed(ConfigurableApplicationContext context, Throwable exception) {
}
}
SpringApplicationRunListener接口的继承关系图:
我们可以实现此接口查看 ApplicationContextInitializer 监听器的回调时机
public class TestSpringApplicationRunListener implements SpringApplicationRunListener {
// 此构造器必须书写,否则会报错
public TestSpringApplicationRunListener(SpringApplication springApplication, String[] args) {
}
@Override
public void starting() {
System.out.println("SpringApplicationRunListener类中的starting方法被调用......");
}
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
Object osName = environment.getSystemProperties().get("os.name");
System.out.println("SpringApplicationRunListener类中的environmentPrepared方法被调用......" + osName);
}
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
System.out.println("SpringApplicationRunListener类中的contextPrepared方法被调用......");
}
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
System.out.println("SpringApplicationRunListener类中的contextLoaded方法被调用......");
}
@Override
public void started(ConfigurableApplicationContext context) {
System.out.println("SpringApplicationRunListener类中的started方法被调用......");
}
@Override
public void running(ConfigurableApplicationContext context) {
System.out.println("SpringApplicationRunListener类中的running方法被调用......");
}
@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) {
System.out.println("SpringApplicationRunListener类中的failed方法被调用......");
}
}
我们需要在类路径下创建 META-INF/spring.factories 文件配置我们自定义的监听器
org.springframework.context.ApplicationContextInitializer=\
cn.bruce.springboot06_theory.listener.TestApplicationContextInitializer
org.springframework.boot.SpringApplicationRunListener=\
cn.bruce.springboot06_theory.listener.TestSpringApplicationRunListener
2. 配置在ioc容器中的监听器
2.1 ApplicationRunner
作用:
ApplicationRunner接口的构成:
@FunctionalInterface
public interface ApplicationRunner {
/**
* Callback used to run the bean.
* @param args incoming application arguments
* @throws Exception on error
*/
void run(ApplicationArguments args) throws Exception;
}
ApplicationRunner接口继承关系图:
我们可以实现 ApplicationRunner 接口查看他的调用时机
// 注入到容器中
@Component
public class TestApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("ApplicationRunner对象的run方法执行了......");
}
}
2.2 CommandLineRunner
作用:可以用于在Spring应用IoC容器
CommandLineRunner接口的构成:
@FunctionalInterface
public interface CommandLineRunner {
/**
* Callback used to run the bean.
* @param args incoming main method arguments
* @throws Exception on error
*/
void run(String... args) throws Exception;
}
实现 CommandLineRunner 接口查看调用时机
// 添加到容器中
@Component
public class TestCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("CommandLineRunner对象的run方法执行了......参数:"+ Arrays.asList(args));
}
}
3. SpringBoot应用启动后监听器回调时机
实现以上接口后控制台的输出内容
SpringApplicationRunListener类中的starting方法被调用......
SpringApplicationRunListener类中的environmentPrepared方法被调用......Windows 10
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.3.5.RELEASE)
ApplicationContextInitializer对象的initialize方法运行了......容器类型为:org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@2ef3eef9, started on Thu Jan 01 08:00:00 CST 1970
SpringApplicationRunListener类中的contextPrepared方法被调用......
2020-11-07 11:43:47.376 INFO 18792 --- [ main] c.b.s.Springboot06TheoryApplication : Starting Springboot06TheoryApplication on YOGA-S740 with PID 18792 (E:\workspace\workspace_idea03\demo-springBoot\springboot06_theory\target\classes started by Bruce in E:\workspace\workspace_idea03\demo-springBoot)
2020-11-07 11:43:47.389 INFO 18792 --- [ main] c.b.s.Springboot06TheoryApplication : No active profile set, falling back to default profiles: default
SpringApplicationRunListener类中的contextLoaded方法被调用......
2020-11-07 11:43:48.165 INFO 18792 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2020-11-07 11:43:48.171 INFO 18792 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2020-11-07 11:43:48.171 INFO 18792 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.39]
2020-11-07 11:43:48.230 INFO 18792 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2020-11-07 11:43:48.231 INFO 18792 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 785 ms
2020-11-07 11:43:48.345 INFO 18792 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2020-11-07 11:43:48.470 INFO 18792 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2020-11-07 11:43:48.477 INFO 18792 --- [ main] c.b.s.Springboot06TheoryApplication : Started Springboot06TheoryApplication in 1.378 seconds (JVM running for 2.064)
SpringApplicationRunListener类中的started方法被调用......
ApplicationRunner对象的run方法执行了......
CommandLineRunner对象的run方法执行了......参数:[]
SpringApplicationRunListener类中的running方法被调用......
通过控制台输出的信息,我们能看到SpringBoot应用启动的过程和回调监听器的时机:
① 创建 SpringApplication 对象;
1) 判断当前的应用类型;
2) 加载并保存所有的ApplicationContextInitializer(META-INF/spring.factoties);
3) 加载并保存所有的ApplicationLisenter(META-INF/spring.factories);
4) 从多个配置类中得到包含main方法的主程序类;
② 运行 SpringApplication 对象中的 run 方法;
1) 回调所有SpringApplicationRunListener对象的 starting 方法;
2) 将参数封装到 ApplicationArguments 对象中;
3) 准备IoC容器运行所需的环境,回调所有SpringApplicationRunListener对象的 environmentPrepare 方法;
4) 向控制台输出banner信息(SpringBoot的logo);
5) 创建IoC容器对象;
org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext(web环境容器)org.springframework.context.annotation.AnnotationConfigApplicationContext(默认的环境容器)
6) 准备上下文环境,此时回调SpringApplicationRunListener对象的 contextPrepared 方法和 contextLoaded 方法
7) 刷新容器,即初始化IoC容器,是创建、扫描、加载所有组建的地方;
8) 回调所有的SpringApplicationRunListener对象的 started 方法;
9)调用SpringApplication对象的 callRunner 方法;此时回调 ApplicationRunner 和 CommandLineRunner 的 run 方法;
10) 回调所有SpringApplicationRunListener对象的 running 方法;
至此整个SpringBoot应用启动完成并返回配置好的IoC容器;
(四)自定义starters
starter书写的基本原则:
① 确定应用场景所需的依赖;
② 编写自动配置,配置相关的场景;
③ 配置模式,引入Starters导入自动配置;
1. Starter编写的模式
① 启动器模块是空的Jar文件,仅提供辅助性的依赖管理,这些依赖可能用于自动装配或其他类库;
② 专门书写一个自动配置模块 xxxAutoConfiguration;
③ 启动器依赖自动配置;使用时仅需引入启动器(Starter)即可自动配置;
starter命名规范:
- 官方的命名方式:
前缀-模块名- 前缀:
spring-boot-starter- - 模式:
spring-boot-starter-模块名 - 举例:
spring-boot-starter-web、spring-boot-starter-jdbc
- 前缀:
- 自定义命名方式:
模块名-后缀- 后缀:
-spring-boot-starter - 模式:
模块-spring-boot-starter - 举例:
mybatis-spring-boot-starter
- 后缀:
2. 书写自动配置:
编写自动配置类
@configuration // 指定此类是一个配置类
@ConditationOnxxx // 在指定的条件成立的情况下自动配置类生效
@AutoConfigureAfter // 指定自动配置类的顺序
@Bean //给容器中添加组件
@EnableConfigurationProperties // 引入相关的xxxProperties配置类,使其生效并加入到容器中
编写配置属性类
@ConfigurationProperties // 书写相关的xxxProperties配置类绑定相关的配置
自动配置类的加载
将需要启动时加载的自动配置类,配置在META-INF/spring.factories中。如:
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
3. 自定义Starters并测试使用
3.1 书写启动器模块
新建模块 demo-spring-boot-starter,启动模块仅作为依赖导入。
书写 pom.xml 文件
<?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>
<groupId>cn.bruce.starter</groupId>
<artifactId>demo-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!-- 引入自动配置模块 -->
<dependency>
<groupId>cn.bruce.starter</groupId>
<artifactId>demo-spring-boot-starter-autoconfigurer</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
3.2 书写自动配置模块
书写自动配置模块 demo-spring-boot-starter-configurer
① 修改 pom.xml 文件
<?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 https://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.3.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>cn.bruce.starter</groupId>
<artifactId>demo-spring-boot-starter-autoconfigurer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>demo-spring-boot-starter-configurer</name>
<description>Demo project for Spring Boot</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<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 = "demo.test")
public class TestProperties {
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 TestService {
TestProperties testProperties;
public TestProperties getTestProperties() {
return testProperties;
}
public void setTestProperties(TestProperties testProperties) {
this.testProperties = testProperties;
}
public String testPrintString(String name) {
return testProperties.getPrefix() + "-" + name + testProperties.getSuffix();
}
}
④ 书写自动配置服务类
@Configuration
@ConditionalOnWebApplication // web应用生效
@EnableConfigurationProperties(TestProperties.class )
public class TestServiceAutoConfiguration {
@Autowired
TestProperties testProperties;
@Bean
public TestService testService() {
TestService testService = new TestService();
testService.setTestProperties(testProperties);
return testService;
}
}
⑤ 书写spring配置文件,配置自动导入
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
cn.bruce.starter.TestServiceAutoConfiguration
⑥ 通过Maven将启动器模块和自动配置模块打包成jar包
1)将 demo-spring-boot-starter-configurer 模块打包成jar包安装到仓库
2)将 demo-spring-boot-starter 模块打包成jar包安装到仓库
demo-spring-boot-starter模块依赖demo-spring-boot-starter-configurer模块,因此需要先打包demo-spring-boot-starter-configurer模块
3.3 使用自定义starter
① 导入自定义的starter依赖
<!-- 引入自定义starter -->
<dependency>
<groupId>cn.bruce.starter</groupId>
<artifactId>demo-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
② 书写配置文件
demo.test.prefix=this is
demo.test.suffix=good
③ 书写controller调用
@RestController
public class TestController {
@Autowired
TestService testService;
@GetMapping("/test")
public String Test() {
return testService.testPrintString("cat");
}
}