SpringBoot-03 | SpringBoot自动配置

SpringBoot-03 | SpringBoot自动配置

网上盗一个图,请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
...
相关推荐
Bald Baby10 分钟前
JWT的使用
java·笔记·学习·servlet
刘大浪11 分钟前
后端数据增删改查基于Springboot+mybatis mysql 时间根据当时时间自动填充,数据库连接查询不一致,mysql数据库连接不好用
数据库·spring boot·mybatis
魔道不误砍柴功15 分钟前
实际开发中的协变与逆变案例:数据处理流水线
java·开发语言
Rverdoser35 分钟前
RabbitMQ的基本概念和入门
开发语言·后端·ruby
dj244294570738 分钟前
JAVA中的Lamda表达式
java·开发语言
攻心的子乐41 分钟前
shell脚本启动springboot项目
spring boot
工业3D_大熊1 小时前
3D可视化引擎HOOPS Luminate场景图详解:形状的创建、销毁与管理
java·c++·3d·docker·c#·制造·数据可视化
szc17671 小时前
docker 相关命令
java·docker·jenkins
程序媛-徐师姐1 小时前
Java 基于SpringBoot+vue框架的老年医疗保健网站
java·vue.js·spring boot·老年医疗保健·老年 医疗保健