SpringBoot-03 | SpringBoot自动配置

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;@Overridepublic 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;}@Overridepublic void starting(ConfigurableBootstrapContext bootstrapContext) {System.out.println("org.springframework.boot.SpringApplicationRunListener  starting...");}@Overridepublic void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {System.out.println("org.springframework.boot.SpringApplicationRunListener  environmentPrepared...");}@Overridepublic void contextPrepared(ConfigurableApplicationContext context) {System.out.println("org.springframework.boot.SpringApplicationRunListener  contextPrepared...");}@Overridepublic void contextLoaded(ConfigurableApplicationContext context) {System.out.println("org.springframework.boot.SpringApplicationRunListener  contextLoaded...");}@Overridepublic void started(ConfigurableApplicationContext context) {System.out.println("org.springframework.boot.SpringApplicationRunListener  started...");}@Overridepublic void running(ConfigurableApplicationContext context) {System.out.println("org.springframework.boot.SpringApplicationRunListener  running...");}@Overridepublic void failed(ConfigurableApplicationContext context, Throwable exception) {System.out.println("org.springframework.boot.SpringApplicationRunListener  failed...");}/*** Before {@link EventPublishingRunListener}.** @return HIGHEST_PRECEDENCE*/@Overridepublic 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 StandaloneProfileApplicationListenerimplements ApplicationListener<ApplicationEnvironmentPreparedEvent>, PriorityOrdered {private static final Logger LOGGER = LoggerFactory.getLogger(StandaloneProfileApplicationListener.class);String STANDALONE_MODE_PROPERTY_NAME = "standalone";String STANDALONE_SPRING_PROFILE = "standalone";@Overridepublic 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);}}@Overridepublic 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
{@Beanpublic 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 elementsresult.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 {@Overridepublic 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 duplicatesSet<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
...

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/282225.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

58、服务攻防——应用协议设备KibanaZabbix远控向日葵VNCTeamViwer

文章目录 vnc默认端口&#xff1a;5900 or 5902&#xff0c;hydra支持vnc破解。VNC有三种模式&#xff1a;使用vnc密码、windows密码、无密码。 teamviewer、向日葵都是使用之前爆过漏洞进行测试。 zabbix&#xff1a;监控系统&#xff0c;蓝队部署平台。zabbix页面如下&#…

四川宏博蓬达法律咨询有限公司守护您的法律安全

在法治社会中&#xff0c;法律咨询服务的需求日益增长&#xff0c;而四川宏博蓬达法律咨询有限公司正是这一领域中一颗璀璨的明星。公司以其专业的法律服务和严谨的工作态度&#xff0c;为广大客户提供了安全、高效、全面的法律支持&#xff0c;赢得了社会各界的广泛赞誉。 一、…

高德地图——轨迹回放和电子围栏

功能点 地图的初始化显示电子围栏&#xff08;先初始化在调接口显示电子围栏&#xff09;显示定位显示轨迹轨迹回放 &#xff08;回放速度无法控制是因为高德地图的版本问题&#xff0c;不要设置版本&#xff0c;使用默认的即可生效&#xff09;获取当前城市及天气情况设置地图…

【小白成长记】new Error()传递对象数据

new Error()需要传递对象数据时遇到的小问题&#xff0c;简单记录如下。 如下图代码&#xff1a;单个Promise里&#xff0c;表单校验未通过处理为 reject&#xff0c;同时需要将 表单以及未校验通过的错误信息传递以便后续进行处理。经过测试&#xff0c;发现reject只能传递一…

电脑文件msvcp100.dll丢失原因,如何快速修复msvcp100.dll

电脑文件msvcp100.dll丢失原因&#xff0c;最近有朋友在问这个&#xff0c;显然会问这个的人&#xff0c;一般都是遇到了msvcp100.dll丢失的问题了&#xff0c;今天我们就来详细的给大家说说msvcp100.dll这个文件吧&#xff0c;我们只有了解了msvcp100.dll这个文件&#xff0c;…

Docker专题-03 Log-Driver日志转存

Docker专题教程 注&#xff1a; 本教程由羞涩梦整理同步发布&#xff0c;本人技术分享站点&#xff1a;blog.hukanfa.com 转发本文请备注原文链接&#xff0c;本文内容整理日期&#xff1a;2024-03-19 csdn 博客名称&#xff1a;五维空间-影子&#xff0c;欢迎关注 说明 容器…

MySQL 索引:索引为什么使用 B+树?

Hash 索引不支持顺序和范围查询&#xff1b; 二叉查找树(BST)&#xff1a;解决了排序的问题&#xff0c;极端情况下可能会退化成线性链表&#xff0c;查询效率急剧下降&#xff1b; 平衡二叉树(AVL) &#xff1a;通过旋转解决了平衡的问题&#xff0c;但是旋转操作效率太低&am…

Rust之构建命令行程序(五):环境变量

开发环境 Windows 11Rust 1.77.0 VS Code 1.87.2 项目工程 这次创建了新的工程minigrep. 使用环境变量 我们将通过添加一个额外的功能来改进minigrep:一个不区分大小写的搜索选项&#xff0c;用户可以通过环境变量打开该选项。我们可以将此功能设置为命令行选项&#xff0c;…

linux 升级openssl1.1.1w 亲测记录

下载好openssl源码包,解压到指定目录下 tar -xzvf openssl-1.1.1w.tar.gz -C /usr/localcd openssl-1.1.1w/*预编译、编译、安装*/./config --prefix/usr/local/openssl sharedmake && make install备份配置系统中原有的文件、创建软链接、动态库查找路径配置文件 ld.s…

SpringBoot实战(二十七)集成WebFlux

目录 一、WebFlux1.1 定义1.2 WebFlux 与 Spring MVC 区别 二、代码实现2.1 Maven 配置2.2 暴露 RESTful API 接口的方式方式一&#xff1a;基于注解的控制器方式二&#xff1a;函数式路由器&#xff08;Functional Endpoints&#xff09; 2.3 测试Service2.4 测试ServiceImpl2…

【Charles如何对手机APP进行抓包和弱网测试】

一、Charles对APP抓包 1、前提条件&#xff1a; 1&#xff09;电脑上必须安装Charles工具&#xff0c;如没有安装可参考&#xff1a;【Charles抓包工具下载安装详细操作步骤】-CSDN博客 2&#xff09;手机和电脑必须在同一个局域网内&#xff08;连接同一个WiFi&#xff09;…

ng发布静态资源 发布项目 发布数据

描述&#xff1a;把一个项目或者数据发布出来&#xff0c;通过http的形式访问&#xff0c;比如发布一个js文件&#xff0c;用http://localhost:6060/data/jquery/jquery.min.js访问。 步骤&#xff1a;配置nginx.conf文件&#xff0c;nginx.conf位于conf目录下&#xff0c;在se…

P2822 [NOIP2016 提高组] 组合数问题题解

题目 组合数表示的是从n个物品中选出m个物品的方案数。举个例子&#xff0c;从(1,2,3) 三个物品中选择两个物品可以有 (1,2),(1,3),(2,3)这三种选择方法。根据组合数的定义&#xff0c;我们可以给出计算组合数的一般公式&#xff1a; 其中n!12⋯n&#xff1b;特别地&#xff0…

Java 在PDF中插入页眉、页脚

在处理PDF文档时&#xff0c;有时需要为文档中的每一页添加页眉和页脚&#xff0c;以包含一些有用的信息&#xff0c;如文档标题、章节名称、日期、页码等。对于需要自动化处理的场景&#xff0c;或者需要在大量文档中添加一致的页眉和页脚&#xff0c;可以通过编程的方式来实现…

QGraphicsView的使用,view坐标,scene坐标,item坐标

Graphics View绘图构架 QGraphicsScene&#xff08;场景&#xff09;&#xff1a;可以管理多个图形项QGraphicsItem&#xff08;图形项&#xff09;&#xff1a;也就是图元&#xff0c;支持鼠标事件响应。QGraphicsView&#xff08;视图&#xff09;&#xff1a;关联场景可以让…

【数据库系统】数据库完整性和安全性

第六章 数据库完整性和安全性 基本内容 安全性&#xff1b;完整性&#xff1b;数据库恢复技术&#xff1b;SQL Server的数据恢复机制&#xff1b; 完整性 实体完整性、参照完整性、用户自定义完整性 安全性 身份验证权限控制事务日志&#xff0c;审计数据加密 数据库恢复 冗余…

Redis学习二--常见问题及处理

基本概念 Redis基本概念数据结构 机制 持久化机制&#xff1a; RDB(内存快照)&#xff1a;某一时刻的内存快照以二进制的方式写入磁盘&#xff0c;可以手动触发和自动触发。 优点&#xff1a;生成文件小&#xff0c;恢复速度快&#xff0c;适用于灾难恢复。 缺点&#xff1a…

关于Zookeeper分布式锁

背景 之前说到分布式锁的实现有三种 1、基于数据库实现的分布式锁 2、Redis分布式锁 3、Zookeeper分布式锁 前者redis分布式锁博客已具体介绍&#xff0c;此博客最终决定补齐关于Zookeeper分布式锁的实现原理。 简述 Zoopkeeper&#xff0c;它是一个为分布式的协调服务&…

固态继电器(SSR)您需要了解的一切

固态继电器&#xff08;也称SSR&#xff0c;SS继电器或SSR开关&#xff09;是一种集成的非接触式电子开关设备&#xff0c;由集成电路&#xff08;IC&#xff09;和分立组件紧密组装而成。处于现代电气应用的最前沿&#xff0c;与机电同类产品相比&#xff0c;具有许多优势。本…

重学SpringBoot3-Profiles介绍

更多SpringBoot3内容请关注我的专栏&#xff1a;《SpringBoot3》 期待您的点赞&#x1f44d;收藏⭐评论✍ 重学SpringBoot3-Profiles介绍 Profiles简介如何在Spring Boot中使用Profiles定义Profiles激活ProfilesIDEA设置active profile使用Profile-specific配置文件 条件化Bean…