SpringBoot-03 | SpringBoot自动配置
- 原理分析
- 代码示例
- 源码剖析
- @SpringBootConfiguration:组合注解,标记当前类为配置类
- @ComponentScan
- @EnableAutoConfiguration
- @Import加载spring.factories
- run初始化加载spring.factories
- spring.factories中的钩子类
网上盗一个图,请call 666
原理分析
SpringBoot自动配置也使用到了SPI的思想。和JDK中的原理相同。
工具类不同:
- JDK使用的工具类是ServiceLoader
- SpringBoot中使用的类是SpringFactoriesLoader
文件路径不同: - JDK配置在 META-INF/services文件夹,然后创建以接口全限定名为名字的文件,文件内容为实现类的全路径名
- SpringBoot配置放在 META-INF/spring.factories中
代码示例
spring.factories
org.springframework.context.ApplicationListener=\
com.tope365.config.profile.StandaloneProfileApplicationListener
org.springframework.boot.SpringApplicationRunListener=\
com.tope365.config.profile.SpringApplicationRunListener
org.springframework.context.ApplicationContextInitializer=\
com.tope365.config.profile.ApplicationUtils
**.ApplicationContextInitializer
package com.tope365.config.profile;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
/**
* @author yewenhai
*/
@SuppressWarnings("all")
public class ApplicationUtils implements ApplicationContextInitializer<ConfigurableApplicationContext> {
private static ApplicationContext applicationContext;
@Override
public void initialize(ConfigurableApplicationContext context) {
System.out.println("org.springframework.context.ApplicationContextInitializer initialize...");
applicationContext = context;
}
}
**.SpringApplicationRunListener
package com.tope365.config.profile;
import org.springframework.boot.ConfigurableBootstrapContext;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.context.event.EventPublishingRunListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.Ordered;
import org.springframework.core.env.ConfigurableEnvironment;
import java.util.Collection;
/**
* {@link org.springframework.boot.SpringApplicationRunListener} before {@link EventPublishingRunListener} execution.
*
* @author yewenhai
* @since 0.2.2
*/
public class SpringApplicationRunListener implements org.springframework.boot.SpringApplicationRunListener, Ordered {
private final SpringApplication application;
private final String[] args;
public SpringApplicationRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
}
@Override
public void starting(ConfigurableBootstrapContext bootstrapContext) {
System.out.println("org.springframework.boot.SpringApplicationRunListener starting...");
}
@Override
public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
System.out.println("org.springframework.boot.SpringApplicationRunListener environmentPrepared...");
}
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
System.out.println("org.springframework.boot.SpringApplicationRunListener contextPrepared...");
}
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
System.out.println("org.springframework.boot.SpringApplicationRunListener contextLoaded...");
}
@Override
public void started(ConfigurableApplicationContext context) {
System.out.println("org.springframework.boot.SpringApplicationRunListener started...");
}
@Override
public void running(ConfigurableApplicationContext context) {
System.out.println("org.springframework.boot.SpringApplicationRunListener running...");
}
@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) {
System.out.println("org.springframework.boot.SpringApplicationRunListener failed...");
}
/**
* Before {@link EventPublishingRunListener}.
*
* @return HIGHEST_PRECEDENCE
*/
@Override
public int getOrder() {
return HIGHEST_PRECEDENCE;
}
}
**.ApplicationListener
package com.tope365.config.profile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Profile;
import org.springframework.core.PriorityOrdered;
import org.springframework.core.env.ConfigurableEnvironment;
import java.util.Arrays;
public class StandaloneProfileApplicationListener
implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, PriorityOrdered {
private static final Logger LOGGER = LoggerFactory.getLogger(StandaloneProfileApplicationListener.class);
String STANDALONE_MODE_PROPERTY_NAME = "standalone";
String STANDALONE_SPRING_PROFILE = "standalone";
@Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
System.out.println("org.springframework.context.ApplicationListener onApplicationEvent...");
ConfigurableEnvironment environment = event.getEnvironment();
if (environment.getProperty(STANDALONE_MODE_PROPERTY_NAME, boolean.class, false)) {
environment.addActiveProfile(STANDALONE_SPRING_PROFILE);
}
}
@Override
public int getOrder() {
return HIGHEST_PRECEDENCE;
}
}
启动后输出:
F:\develop\jdk-17.0.9\bin\java.exe -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:64926,suspend=y,server=n -XX:TieredStopAtLevel=1 -Dspring.output.ansi.enabled=always -Dcom.sun.management.jmxremote -Dspring.jmx.enabled=true -Dspring.liveBeansView.mbeanDomain -Dspring.application.admin.enabled=true "-Dmanagement.endpoints.jmx.exposure.include=*" -javaagent:C:\Users\Administrator\AppData\Local\JetBrains\IntelliJIdea2023.1\captureAgent\debugger-agent.jar=file:/C:/Users/Administrator/AppData/Local/Temp/capture1.props -Dfile.encoding=UTF-8 -classpath "F:\iframework\ai-coder\ai-java\web\target\classes;F:\iframework\ai-coder\ai-java\service\target\classes;F:\iframework\ai-coder\ai-java\api\target\classes;F:\iframework\ai-coder\ai-java\qdrant\http\target\classes;C:\.m2\repository\com\squareup\okhttp3\okhttp\4.11.0\okhttp-4.11.0.jar;C:\.m2\repository\com\squareup\okio\okio\3.2.0\okio-3.2.0.jar;C:\.m2\repository\com\squareup\okio\okio-jvm\3.2.0\okio-jvm-3.2.0.jar;C:\.m2\repository\org\jetbrains\kotlin\kotlin-stdlib\1.6.20\kotlin-stdlib-1.6.20.jar;C:\.m2\repository\org\jetbrains\kotlin\kotlin-stdlib-common\1.6.20\kotlin-stdlib-common-1.6.20.jar;C:\.m2\repository\org\jetbrains\annotations\13.0\annotations-13.0.jar;C:\.m2\repository\org\jetbrains\kotlin\kotlin-stdlib-jdk8\1.6.20\kotlin-stdlib-jdk8-1.6.20.jar;C:\.m2\repository\org\jetbrains\kotlin\kotlin-stdlib-jdk7\1.6.20\kotlin-stdlib-jdk7-1.6.20.jar;C:\.m2\repository\com\fasterxml\jackson\core\jackson-databind\2.15.0\jackson-databind-2.15.0.jar;F:\iframework\ai-coder\ai-java\qdrant\common\target\classes;C:\.m2\repository\org\springframework\retry\spring-retry\2.0.5\spring-retry-2.0.5.jar;C:\.m2\repository\org\aspectj\aspectjrt\1.9.7\aspectjrt-1.9.7.jar;C:\.m2\repository\org\aspectj\aspectjweaver\1.9.7\aspectjweaver-1.9.7.jar;C:\.m2\repository\org\apache\commons\commons-text\1.9\commons-text-1.9.jar;C:\.m2\repository\org\apache\commons\commons-lang3\3.11\commons-lang3-3.11.jar;C:\.m2\repository\com\huaban\jieba-analysis\1.0.2\jieba-analysis-1.0.2.jar;C:\.m2\repository\org\springframework\boot\spring-boot-starter-web\2.7.11\spring-boot-starter-web-2.7.11.jar;C:\.m2\repository\org\springframework\boot\spring-boot-starter\2.7.11\spring-boot-starter-2.7.11.jar;C:\.m2\repository\org\springframework\boot\spring-boot-starter-logging\2.7.11\spring-boot-starter-logging-2.7.11.jar;C:\.m2\repository\ch\qos\logback\logback-classic\1.2.12\logback-classic-1.2.12.jar;C:\.m2\repository\ch\qos\logback\logback-core\1.2.12\logback-core-1.2.12.jar;C:\.m2\repository\org\apache\logging\log4j\log4j-to-slf4j\2.17.2\log4j-to-slf4j-2.17.2.jar;C:\.m2\repository\org\apache\logging\log4j\log4j-api\2.17.2\log4j-api-2.17.2.jar;C:\.m2\repository\org\slf4j\jul-to-slf4j\1.7.36\jul-to-slf4j-1.7.36.jar;C:\.m2\repository\jakarta\annotation\jakarta.annotation-api\1.3.5\jakarta.annotation-api-1.3.5.jar;C:\.m2\repository\org\springframework\spring-core\5.3.27\spring-core-5.3.27.jar;C:\.m2\repository\org\springframework\spring-jcl\5.3.27\spring-jcl-5.3.27.jar;C:\.m2\repository\org\yaml\snakeyaml\1.30\snakeyaml-1.30.jar;C:\.m2\repository\org\springframework\boot\spring-boot-starter-json\2.7.11\spring-boot-starter-json-2.7.11.jar;C:\.m2\repository\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.13.5\jackson-datatype-jdk8-2.13.5.jar;C:\.m2\repository\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.13.5\jackson-datatype-jsr310-2.13.5.jar;C:\.m2\repository\com\fasterxml\jackson\module\jackson-module-parameter-names\2.13.5\jackson-module-parameter-names-2.13.5.jar;C:\.m2\repository\org\springframework\boot\spring-boot-starter-tomcat\2.7.11\spring-boot-starter-tomcat-2.7.11.jar;C:\.m2\repository\org\apache\tomcat\embed\tomcat-embed-core\9.0.74\tomcat-embed-core-9.0.74.jar;C:\.m2\repository\org\apache\tomcat\embed\tomcat-embed-el\9.0.74\tomcat-embed-el-9.0.74.jar;C:\.m2\repository\org\apache\tomcat\embed\tomcat-embed-websocket\9.0.74\tomcat-embed-websocket-9.0.74.jar;C:\.m2\repository\org\springframework\spring-web\5.3.27\spring-web-5.3.27.jar;C:\.m2\repository\org\springframework\spring-beans\5.3.27\spring-beans-5.3.27.jar;C:\.m2\repository\org\springframework\spring-webmvc\5.3.27\spring-webmvc-5.3.27.jar;C:\.m2\repository\org\springframework\spring-aop\5.3.27\spring-aop-5.3.27.jar;C:\.m2\repository\org\springframework\spring-context\5.3.27\spring-context-5.3.27.jar;C:\.m2\repository\org\springframework\spring-expression\5.3.27\spring-expression-5.3.27.jar;C:\.m2\repository\org\springframework\boot\spring-boot-starter-webflux\2.7.11\spring-boot-starter-webflux-2.7.11.jar;C:\.m2\repository\org\springframework\boot\spring-boot-starter-reactor-netty\2.7.11\spring-boot-starter-reactor-netty-2.7.11.jar;C:\.m2\repository\io\projectreactor\netty\reactor-netty-http\1.0.31\reactor-netty-http-1.0.31.jar;C:\.m2\repository\io\netty\netty-resolver-dns-native-macos\4.1.91.Final\netty-resolver-dns-native-macos-4.1.91.Final-osx-x86_64.jar;C:\.m2\repository\io\projectreactor\netty\reactor-netty-core\1.0.31\reactor-netty-core-1.0.31.jar;C:\.m2\repository\org\springframework\spring-webflux\5.3.27\spring-webflux-5.3.27.jar;C:\.m2\repository\org\springframework\boot\spring-boot-configuration-processor\2.7.11\spring-boot-configuration-processor-2.7.11.jar;C:\.m2\repository\org\springframework\boot\spring-boot-autoconfigure\2.7.11\spring-boot-autoconfigure-2.7.11.jar;C:\.m2\repository\org\springframework\boot\spring-boot\2.7.11\spring-boot-2.7.11.jar;C:\.m2\repository\org\springframework\boot\spring-boot-starter-jdbc\2.7.11\spring-boot-starter-jdbc-2.7.11.jar;C:\.m2\repository\com\zaxxer\HikariCP\4.0.3\HikariCP-4.0.3.jar;C:\.m2\repository\org\springframework\spring-jdbc\5.3.27\spring-jdbc-5.3.27.jar;C:\.m2\repository\org\springframework\spring-tx\5.3.27\spring-tx-5.3.27.jar;C:\.m2\repository\mysql\mysql-connector-java\8.0.19\mysql-connector-java-8.0.19.jar;C:\.m2\repository\com\google\protobuf\protobuf-java\3.6.1\protobuf-java-3.6.1.jar;C:\.m2\repository\com\alibaba\druid-spring-boot-starter\1.1.10\druid-spring-boot-starter-1.1.10.jar;C:\.m2\repository\com\alibaba\druid\1.1.10\druid-1.1.10.jar;C:\.m2\repository\org\slf4j\slf4j-api\1.7.25\slf4j-api-1.7.25.jar;C:\.m2\repository\com\baomidou\mybatis-plus-boot-starter\3.4.1\mybatis-plus-boot-starter-3.4.1.jar;C:\.m2\repository\com\github\pagehelper\pagehelper-spring-boot-starter\1.3.0\pagehelper-spring-boot-starter-1.3.0.jar;C:\.m2\repository\org\mybatis\spring\boot\mybatis-spring-boot-starter\2.1.3\mybatis-spring-boot-starter-2.1.3.jar;C:\.m2\repository\org\mybatis\spring\boot\mybatis-spring-boot-autoconfigure\2.1.3\mybatis-spring-boot-autoconfigure-2.1.3.jar;C:\.m2\repository\org\mybatis\mybatis\3.5.5\mybatis-3.5.5.jar;C:\.m2\repository\org\mybatis\mybatis-spring\2.0.5\mybatis-spring-2.0.5.jar;C:\.m2\repository\com\github\pagehelper\pagehelper-spring-boot-autoconfigure\1.3.0\pagehelper-spring-boot-autoconfigure-1.3.0.jar;C:\.m2\repository\com\github\pagehelper\pagehelper\5.2.0\pagehelper-5.2.0.jar;C:\.m2\repository\com\github\jsqlparser\jsqlparser\3.2\jsqlparser-3.2.jar;C:\.m2\repository\com\baomidou\mybatis-plus\3.4.1\mybatis-plus-3.4.1.jar;C:\.m2\repository\com\baomidou\mybatis-plus-extension\3.4.1\mybatis-plus-extension-3.4.1.jar;C:\.m2\repository\com\baomidou\mybatis-plus-core\3.4.1\mybatis-plus-core-3.4.1.jar;C:\.m2\repository\com\baomidou\mybatis-plus-annotation\3.4.1\mybatis-plus-annotation-3.4.1.jar;C:\.m2\repository\com\baomidou\mybatis-plus-generator\3.4.1\mybatis-plus-generator-3.4.1.jar;C:\.m2\repository\org\freemarker\freemarker\2.3.29\freemarker-2.3.29.jar;C:\.m2\repository\org\springframework\boot\spring-boot-starter-data-redis\2.7.11\spring-boot-starter-data-redis-2.7.11.jar;C:\.m2\repository\org\springframework\data\spring-data-redis\2.7.11\spring-data-redis-2.7.11.jar;C:\.m2\repository\org\springframework\data\spring-data-keyvalue\2.7.11\spring-data-keyvalue-2.7.11.jar;C:\.m2\repository\org\springframework\data\spring-data-commons\2.7.11\spring-data-commons-2.7.11.jar;C:\.m2\repository\org\springframework\spring-oxm\5.3.27\spring-oxm-5.3.27.jar;C:\.m2\repository\org\springframework\spring-context-support\5.3.27\spring-context-support-5.3.27.jar;C:\.m2\repository\io\lettuce\lettuce-core\6.1.10.RELEASE\lettuce-core-6.1.10.RELEASE.jar;C:\.m2\repository\org\redisson\redisson\3.27.0\redisson-3.27.0.jar;C:\.m2\repository\io\netty\netty-common\4.1.107.Final\netty-common-4.1.107.Final.jar;C:\.m2\repository\io\netty\netty-codec\4.1.107.Final\netty-codec-4.1.107.Final.jar;C:\.m2\repository\io\netty\netty-buffer\4.1.107.Final\netty-buffer-4.1.107.Final.jar;C:\.m2\repository\io\netty\netty-transport\4.1.107.Final\netty-transport-4.1.107.Final.jar;C:\.m2\repository\io\netty\netty-resolver\4.1.107.Final\netty-resolver-4.1.107.Final.jar;C:\.m2\repository\io\netty\netty-resolver-dns\4.1.107.Final\netty-resolver-dns-4.1.107.Final.jar;C:\.m2\repository\io\netty\netty-codec-dns\4.1.107.Final\netty-codec-dns-4.1.107.Final.jar;C:\.m2\repository\io\netty\netty-handler\4.1.107.Final\netty-handler-4.1.107.Final.jar;C:\.m2\repository\io\netty\netty-transport-native-unix-common\4.1.107.Final\netty-transport-native-unix-common-4.1.107.Final.jar;C:\.m2\repository\javax\cache\cache-api\1.1.1\cache-api-1.1.1.jar;C:\.m2\repository\io\projectreactor\reactor-core\3.6.2\reactor-core-3.6.2.jar;C:\.m2\repository\org\reactivestreams\reactive-streams\1.0.4\reactive-streams-1.0.4.jar;C:\.m2\repository\io\reactivex\rxjava3\rxjava\3.1.6\rxjava-3.1.6.jar;C:\.m2\repository\org\jboss\marshalling\jboss-marshalling\2.0.11.Final\jboss-marshalling-2.0.11.Final.jar;C:\.m2\repository\org\jboss\marshalling\jboss-marshalling-river\2.0.11.Final\jboss-marshalling-river-2.0.11.Final.jar;C:\.m2\repository\com\esotericsoftware\kryo\5.6.0\kryo-5.6.0.jar;C:\.m2\repository\com\esotericsoftware\reflectasm\1.11.9\reflectasm-1.11.9.jar;C:\.m2\repository\org\objenesis\objenesis\3.3\objenesis-3.3.jar;C:\.m2\repository\com\esotericsoftware\minlog\1.3.1\minlog-1.3.1.jar;C:\.m2\repository\com\fasterxml\jackson\core\jackson-annotations\2.16.1\jackson-annotations-2.16.1.jar;C:\.m2\repository\com\fasterxml\jackson\dataformat\jackson-dataformat-yaml\2.16.1\jackson-dataformat-yaml-2.16.1.jar;C:\.m2\repository\com\fasterxml\jackson\core\jackson-core\2.16.1\jackson-core-2.16.1.jar;C:\.m2\repository\net\bytebuddy\byte-buddy\1.14.5\byte-buddy-1.14.5.jar;C:\.m2\repository\org\jodd\jodd-bean\5.1.6\jodd-bean-5.1.6.jar;C:\.m2\repository\org\jodd\jodd-core\5.1.6\jodd-core-5.1.6.jar;C:\.m2\repository\org\eclipse\jgit\org.eclipse.jgit\5.13.2.202306221912-r\org.eclipse.jgit-5.13.2.202306221912-r.jar;C:\.m2\repository\com\googlecode\javaewah\JavaEWAH\1.1.13\JavaEWAH-1.1.13.jar;C:\.m2\repository\commons-io\commons-io\2.15.1\commons-io-2.15.1.jar;C:\.m2\repository\com\github\javaparser\javaparser-core\3.25.8\javaparser-core-3.25.8.jar;C:\.m2\repository\com\tope365\tope-netty-sdk-server\1.0.4-SNAPSHOT\tope-netty-sdk-server-1.0.4-SNAPSHOT.jar;C:\.m2\repository\com\tope365\tope-netty-sdk-common\1.0.4-SNAPSHOT\tope-netty-sdk-common-1.0.4-SNAPSHOT.jar;C:\.m2\repository\io\netty\netty-all\4.1.74.Final\netty-all-4.1.74.Final.jar;C:\.m2\repository\io\netty\netty-codec-haproxy\4.1.74.Final\netty-codec-haproxy-4.1.74.Final.jar;C:\.m2\repository\io\netty\netty-codec-memcache\4.1.74.Final\netty-codec-memcache-4.1.74.Final.jar;C:\.m2\repository\io\netty\netty-codec-mqtt\4.1.74.Final\netty-codec-mqtt-4.1.74.Final.jar;C:\.m2\repository\io\netty\netty-codec-redis\4.1.74.Final\netty-codec-redis-4.1.74.Final.jar;C:\.m2\repository\io\netty\netty-codec-smtp\4.1.74.Final\netty-codec-smtp-4.1.74.Final.jar;C:\.m2\repository\io\netty\netty-codec-socks\4.1.74.Final\netty-codec-socks-4.1.74.Final.jar;C:\.m2\repository\io\netty\netty-codec-stomp\4.1.74.Final\netty-codec-stomp-4.1.74.Final.jar;C:\.m2\repository\io\netty\netty-codec-xml\4.1.74.Final\netty-codec-xml-4.1.74.Final.jar;C:\.m2\repository\org\jctools\jctools-core\3.1.0\jctools-core-3.1.0.jar;C:\.m2\repository\io\netty\netty-tcnative-classes\2.0.48.Final\netty-tcnative-classes-2.0.48.Final.jar;C:\.m2\repository\io\netty\netty-transport-rxtx\4.1.74.Final\netty-transport-rxtx-4.1.74.Final.jar;C:\.m2\repository\io\netty\netty-transport-sctp\4.1.74.Final\netty-transport-sctp-4.1.74.Final.jar;C:\.m2\repository\io\netty\netty-transport-udt\4.1.74.Final\netty-transport-udt-4.1.74.Final.jar;C:\.m2\repository\io\netty\netty-transport-classes-epoll\4.1.74.Final\netty-transport-classes-epoll-4.1.74.Final.jar;C:\.m2\repository\io\netty\netty-transport-classes-kqueue\4.1.74.Final\netty-transport-classes-kqueue-4.1.74.Final.jar;C:\.m2\repository\io\netty\netty-resolver-dns-classes-macos\4.1.74.Final\netty-resolver-dns-classes-macos-4.1.74.Final.jar;C:\.m2\repository\com\google\code\gson\gson\2.8.6\gson-2.8.6.jar;C:\.m2\repository\cn\hutool\hutool-all\5.7.13\hutool-all-5.7.13.jar;C:\.m2\repository\commons-lang\commons-lang\2.6\commons-lang-2.6.jar;C:\.m2\repository\com\googlecode\protobuf-java-format\protobuf-java-format\1.2\protobuf-java-format-1.2.jar;C:\.m2\repository\org\reflections\reflections\0.9.12\reflections-0.9.12.jar;C:\.m2\repository\org\javassist\javassist\3.26.0-GA\javassist-3.26.0-GA.jar;C:\.m2\repository\com\auth0\java-jwt\3.3.0\java-jwt-3.3.0.jar;C:\.m2\repository\commons-codec\commons-codec\1.11\commons-codec-1.11.jar;C:\.m2\repository\com\squareup\retrofit2\retrofit\2.9.0\retrofit-2.9.0.jar;C:\.m2\repository\com\squareup\retrofit2\adapter-rxjava2\2.9.0\adapter-rxjava2-2.9.0.jar;C:\.m2\repository\io\reactivex\rxjava2\rxjava\2.0.0\rxjava-2.0.0.jar;C:\.m2\repository\com\squareup\retrofit2\converter-jackson\2.9.0\converter-jackson-2.9.0.jar;C:\.m2\repository\io\swagger\swagger-annotations\1.5.21\swagger-annotations-1.5.21.jar;C:\.m2\repository\io\swagger\swagger-models\1.5.21\swagger-models-1.5.21.jar;C:\.m2\repository\io\springfox\springfox-swagger2\2.9.2\springfox-swagger2-2.9.2.jar;C:\.m2\repository\io\springfox\springfox-spi\2.9.2\springfox-spi-2.9.2.jar;C:\.m2\repository\io\springfox\springfox-core\2.9.2\springfox-core-2.9.2.jar;C:\.m2\repository\io\springfox\springfox-schema\2.9.2\springfox-schema-2.9.2.jar;C:\.m2\repository\io\springfox\springfox-swagger-common\2.9.2\springfox-swagger-common-2.9.2.jar;C:\.m2\repository\io\springfox\springfox-spring-web\2.9.2\springfox-spring-web-2.9.2.jar;C:\.m2\repository\com\google\guava\guava\20.0\guava-20.0.jar;C:\.m2\repository\com\fasterxml\classmate\1.4.0\classmate-1.4.0.jar;C:\.m2\repository\org\springframework\plugin\spring-plugin-core\1.2.0.RELEASE\spring-plugin-core-1.2.0.RELEASE.jar;C:\.m2\repository\org\springframework\plugin\spring-plugin-metadata\1.2.0.RELEASE\spring-plugin-metadata-1.2.0.RELEASE.jar;C:\.m2\repository\org\mapstruct\mapstruct\1.2.0.Final\mapstruct-1.2.0.Final.jar;C:\.m2\repository\io\springfox\springfox-bean-validators\2.9.2\springfox-bean-validators-2.9.2.jar;C:\.m2\repository\io\springfox\springfox-swagger-ui\2.9.2\springfox-swagger-ui-2.9.2.jar;C:\.m2\repository\com\github\xiaoymin\swagger-bootstrap-ui\1.9.1\swagger-bootstrap-ui-1.9.1.jar;C:\.m2\repository\org\projectlombok\lombok\1.18.24\lombok-1.18.24.jar;C:\.m2\repository\org\jsoup\jsoup\1.14.3\jsoup-1.14.3.jar;C:\.m2\repository\org\json\json\20211205\json-20211205.jar;C:\.m2\repository\com\azure\azure-ai-openai\1.0.0-beta.6\azure-ai-openai-1.0.0-beta.6.jar;C:\.m2\repository\com\azure\azure-core\1.45.1\azure-core-1.45.1.jar;C:\.m2\repository\com\azure\azure-json\1.1.0\azure-json-1.1.0.jar;C:\.m2\repository\com\azure\azure-core-http-netty\1.13.11\azure-core-http-netty-1.13.11.jar;C:\.m2\repository\io\netty\netty-handler-proxy\4.1.101.Final\netty-handler-proxy-4.1.101.Final.jar;C:\.m2\repository\io\netty\netty-codec-http\4.1.101.Final\netty-codec-http-4.1.101.Final.jar;C:\.m2\repository\io\netty\netty-codec-http2\4.1.101.Final\netty-codec-http2-4.1.101.Final.jar;C:\.m2\repository\io\netty\netty-transport-native-epoll\4.1.101.Final\netty-transport-native-epoll-4.1.101.Final-linux-x86_64.jar;C:\.m2\repository\io\netty\netty-transport-native-kqueue\4.1.101.Final\netty-transport-native-kqueue-4.1.101.Final-osx-x86_64.jar;C:\.m2\repository\io\netty\netty-tcnative-boringssl-static\2.0.62.Final\netty-tcnative-boringssl-static-2.0.62.Final.jar;C:\.m2\repository\io\netty\netty-tcnative-boringssl-static\2.0.62.Final\netty-tcnative-boringssl-static-2.0.62.Final-linux-x86_64.jar;C:\.m2\repository\io\netty\netty-tcnative-boringssl-static\2.0.62.Final\netty-tcnative-boringssl-static-2.0.62.Final-linux-aarch_64.jar;C:\.m2\repository\io\netty\netty-tcnative-boringssl-static\2.0.62.Final\netty-tcnative-boringssl-static-2.0.62.Final-osx-x86_64.jar;C:\.m2\repository\io\netty\netty-tcnative-boringssl-static\2.0.62.Final\netty-tcnative-boringssl-static-2.0.62.Final-osx-aarch_64.jar;C:\.m2\repository\io\netty\netty-tcnative-boringssl-static\2.0.62.Final\netty-tcnative-boringssl-static-2.0.62.Final-windows-x86_64.jar;C:\.m2\repository\org\springframework\boot\spring-boot-starter-validation\2.3.7.RELEASE\spring-boot-starter-validation-2.3.7.RELEASE.jar;C:\.m2\repository\org\glassfish\jakarta.el\3.0.3\jakarta.el-3.0.3.jar;C:\.m2\repository\org\hibernate\validator\hibernate-validator\6.1.6.Final\hibernate-validator-6.1.6.Final.jar;C:\.m2\repository\jakarta\validation\jakarta.validation-api\2.0.2\jakarta.validation-api-2.0.2.jar;C:\.m2\repository\org\jboss\logging\jboss-logging\3.3.2.Final\jboss-logging-3.3.2.Final.jar;C:\.m2\repository\com\alibaba\fastjson\1.2.80\fastjson-1.2.80.jar;C:\Program Files\JetBrains\IntelliJ IDEA 2023.1.1\lib\idea_rt.jar" com.tope365.SpringAppRun
Connected to the target VM, address: '127.0.0.1:64926', transport: 'socket'
11:09:42.415 [main] INFO com.tope365.SpringAppRun - openai web启动中....
org.springframework.boot.SpringApplicationRunListener starting...
org.springframework.boot.SpringApplicationRunListener environmentPrepared...
org.springframework.context.ApplicationListener onApplicationEvent...
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.7.11)
org.springframework.context.ApplicationContextInitializer initialize...
org.springframework.boot.SpringApplicationRunListener contextPrepared...
org.springframework.boot.SpringApplicationRunListener contextLoaded...
********
_ _ |_ _ _|_. ___ _ | _
| | |\/|_)(_| | |_\ |_)||_|_\
/ |
3.4.1
********
org.springframework.boot.SpringApplicationRunListener started...
org.springframework.boot.SpringApplicationRunListener running...
2024-03-13 11:09:51.045 INFO 18344 --- [ main] com.tope365.SpringAppRun : openai web启动成功!
源码剖析
@SpringBootConfiguration:组合注解,标记当前类为配置类
@EnableAutoConfiguration:开启自动配置
@ComponentScan:扫描主类所在的同级包以及子级包里的Bean
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
...
}
@SpringBootConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
可以看到@Configuration为@Component注解的子实现,他同样支持被@ComponentScan扫描到。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
@ComponentScan
1、包扫描路径范围问题:如果是引入的其他jar包,需要加载的bean组件的包路径与配置类的扫描包路径相同则可以扫描到,否则无法扫描到容器。(编译成jar时,同包合并。),一般只有我们自己的maven聚合工程项目才会按照规则创建相同的包,引入第三方jar包时,往往都不一样。
2、包扫描路径覆盖问题:@ComponentScan指定第三方jar包的组件路径,但是@ComponentScan 和@SpringBootApplication注解的包扫描有冲突,@ComponentScan注解包扫描会覆盖掉@SpringBootApplication的包扫描。解决办法就是在@ComponentScan(basePackages={“com.ruoyi.common.swagger.config”,“com.ruoyi.system”})的基础上加上@SpringBootApplication扫描的包。
3、使用 @Configuration与@Bean 注解,必须在com.ruoyi.system包下创建,保证被启动类包扫描到。
@Configuration
public class SwaggerAutoConfiguration
{
@Bean
public Docket api(SwaggerProperties swaggerProperties)
{
。。。
@EnableAutoConfiguration
@Import加载spring.factories
通过@Import(AutoConfigurationImportSelector.class)导入Selector类,@Import 是 Spring 基于 Java 注解配置的主要组成部分,@Import 注解提供了类似 @Bean 注解的功能,向Spring容器中注入bean,也对应实现了与Spring XML中的元素相同的功能。
其一扫描入口,springboot1.5 低版本使用,高版本已经在run时直接加载spring.factories文件了,spring核心方法invokeBeanFactoryPostProcessors(beanFactory);内部会扫描到并触发AutoConfigurationImportSelector类的selectImports方法。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
...
}
AutoConfigurationImportSelector中的方法selectImports()调用到loadFactoryNames(),得到待配置的class的类名集合,这个集合就是所有需要进行自动配置的类,而是否自动配置的关键在于META-INF/spring.factories文件中是否存在该配置信息。
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
Map<String, List<String>> result = cache.get(classLoader);
if (result != null) {
return result;
}
result = new HashMap<>();
try {
Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
String[] factoryImplementationNames =
StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
for (String factoryImplementationName : factoryImplementationNames) {
result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
.add(factoryImplementationName.trim());
}
}
}
// Replace all lists with unmodifiable lists containing unique elements
result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
cache.put(classLoader, result);
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
return result;
}
@Import(AutoConfigurationImportSelector.class)自动配置调用链:(spring低版本使用)
@SpringBootApplication
—>@EnableAutoConfiguration
—>@Import(AutoConfigurationImportSelector.class)
—>selectImports(AnnotationMetadata annotationMetadata)
—>getAutoConfigurationEntry(AnnotationMetadata annotationMetadata)
—>getCandidateConfigurations(metadata, attributes)
—>loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()))
—>loadSpringFactories(ClassLoader classLoader)
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
run初始化加载spring.factories
public class SpringApplication {
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
}
第一次的初始化加载:
SpringApplication.run(SpringAppRun.class, args);
—>new SpringApplication(primarySources).run(args)
—>this(null, primarySources);
—>getSpringFactoriesInstances(Class type)
—>getSpringFactoriesInstances(type, new Class<?>[] {})
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
以下是全是调用返回已经初始化好的factories cache。
static final Map<ClassLoader, Map<String, List>> cache = new ConcurrentReferenceHashMap<>();
public ConfigurableApplicationContext run(String... args) {
...
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
...
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
...
spring.factories中的钩子类
以下是factories 缓存集合内容,可以看到自定义的factories文件内容已经加载进去了。
根据Spring Boot 2.6.x版本中的启动代码步骤,以下是上述22个钩子类在运行时执行的顺序:
// Spring 应用程序上下文初始化器接口。
org.springframework.context.ApplicationContextInitializer
// Spring Boot 日志系统工厂接口。
org.springframework.boot.logging.LoggingSystemFactory
// Spring Boot 属性源加载器接口。
org.springframework.boot.env.PropertySourceLoader
// Spring Boot 环境后置处理器接口。
org.springframework.boot.env.EnvironmentPostProcessor
// Spring Boot 自动配置导入监听器接口。
org.springframework.boot.autoconfigure.AutoConfigurationImportListener
// Spring Boot 自动配置注解。
org.springframework.boot.autoconfigure.EnableAutoConfiguration
// Spring Bean 信息工厂接口。
org.springframework.beans.BeanInfoFactory
// Spring Boot 数据库初始化器检测器。
org.springframework.boot.sql.init.dependency.DatabaseInitializerDetector
// Spring Boot 数据库初始化器依赖检测器。
org.springframework.boot.sql.init.dependency.DependsOnDatabaseInitializationDetector
// Spring Boot 自动配置导入过滤器接口。
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter
// Spring Boot 失败分析器接口。
org.springframework.boot.diagnostics.FailureAnalyzer
// Spring Boot 失败分析报告器接口。
org.springframework.boot.diagnostics.FailureAnalysisReporter
// Spring Boot 异常报告器接口。
org.springframework.boot.SpringBootExceptionReporter
// Spring Data 定制集合注册器接口。
org.springframework.data.util.CustomCollectionRegistrar
// Spring Boot 配置数据位置解析器。
org.springframework.boot.context.config.ConfigDataLocationResolver
// Spring Boot 配置数据加载器。
org.springframework.boot.context.config.ConfigDataLoader
// Spring Boot 应用程序上下文工厂接口。
org.springframework.boot.ApplicationContextFactory
// Spring Boot 应用程序运行监听器接口。
org.springframework.boot.SpringApplicationRunListener
// Spring 应用程序事件监听器接口。
org.springframework.context.ApplicationListener
// Spring Data 仓库工厂支持类。
org.springframework.data.repository.core.support.RepositoryFactorySupport
// Spring Data Jackson 模块配置类。
org.springframework.data.web.config.SpringDataJacksonModules
// Spring Boot JSON 解析器工厂类。
org.springframework.boot.json.JsonParserFactoryorg.springframework.boot.json.JsonParserFactoryorg.springframework.boot.json.JsonParserFactory:Spring Boot JSON 解析器工厂类。
以上是GPT给的调用顺序,其实不是特别准确,如下是自行验证的效果:
org.springframework.boot.SpringApplicationRunListener starting...
org.springframework.boot.SpringApplicationRunListener environmentPrepared...
org.springframework.context.ApplicationListener onApplicationEvent...
org.springframework.context.ApplicationContextInitializer initialize...
org.springframework.boot.SpringApplicationRunListener contextPrepared...
org.springframework.boot.SpringApplicationRunListener contextLoaded...
org.springframework.boot.SpringApplicationRunListener started...
org.springframework.boot.SpringApplicationRunListener running...
spring.factories中的钩子类的框架实现
下图表示有不少的实现类
比如以下不同包中的实现类:
spring-boot-2.3.12.RELEASE\spring-boot-project\spring-boot-autoconfigure\build\resources\main\META-INF\spring.factories
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
...
spring-boot-2.3.12.RELEASE\spring-boot-project\spring-boot-devtools\src\main\resources\META-INF\spring.factories
# Application Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.devtools.restart.RestartScopeInitializer
...