SpringBoot-03 | SpringBoot自动配置

news2024/7/6 18:54:17

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
...

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

流畅的 Python 第二版(GPT 重译)(八)

第十五章&#xff1a;关于类型提示的更多内容 我学到了一个痛苦的教训&#xff0c;对于小程序来说&#xff0c;动态类型很棒。对于大型程序&#xff0c;你需要更加纪律严明的方法。如果语言给予你这种纪律&#xff0c;而不是告诉你“嗯&#xff0c;你可以做任何你想做的事情”&…

HTML中的常用标签用法总结

&#x1f31f; 欢迎来到 我的博客&#xff01; &#x1f308; &#x1f4a1; 探索未知, 分享知识 !&#x1f4ab; 本文目录 1. 标题标签2. 段落标签3. 链接标签4. 列表标签5. 图像标签6. 表格标签 1. 标题标签 <h1>至<h6>用于定义标题。<h1>是最大的标题&am…

【GameFramework框架内置模块】13、流程(Procedure)

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址QQ群&#xff1a;398291828 大家好&#xff0c;我是佛系工程师☆恬静的小魔龙☆&#xff0c;不定时更新Unity开发技巧&#xff0c;觉得有用记得一键三连哦。 一、前言 【GameFramework框架】系列教程目录&#xff1a;…

学点Java_Day6_基于Copyright和Actions On Save的IDEA自动更新文件最后编辑时间的方法研究(IDEA2021.2及以上)

0 版本要求 IDEA2021.2及以上&#xff0c;我用的IntelliJ IDEA 2023.2.4 1 操作 1.1 Copyright设置 ① CtrlAltS打开设置&#xff08;Settings&#xff09;→ Editor → Copyright   ② Copyright → Copyright Profiles → → 取名 → OK   ③ 复制下面内容到框里&am…

【数据结构】顺序表和链表详解顺序表和链表的实现

主页&#xff1a;醋溜马桶圈-CSDN博客 专栏&#xff1a;数据结构_醋溜马桶圈的博客-CSDN博客 gitee&#xff1a;mnxcc (mnxcc) - Gitee.com 目录 1.线性表 1.1 顺序表 1.1.1 概念及结构 1.1.2 静态顺序表 1.1.3 动态顺序表 1.2 链表 1.2.1 链表的概念及结构 1.2.2 链表…

上海王梓标准件制造有限公司隆重参加上海紧固件专业展

随着全球工业制造业的高速发展&#xff0c;标准件行业做为基础部件的供应链环节越来越受到重视。标准件&#xff0c;这类微不足道的小零件&#xff0c;在维护工业世界的稳定和发展中是至关重要的。在这样一个紧要关头&#xff0c;上海王梓标准件制造有限公司&#xff08;下称“…

Redis 安装(二)

Redis安装说明 大多数企业都是基于Linux服务器部署项目&#xff0c;而且Redis官网也没有提供Windows版本的安装包&#xff0c;因此课程中我们会基于Linux系统来安装Redis。 此处选择Linux版本为Centos7。 Redis的官方网站地址&#xff1a;https://redis.io/ Redis的安装 切换…

51单片机产生波特率的计算分析,为什么常见11.0592mhz

数据手册中的资料 参考短文&#xff0c;机械周期 对波特率产生的计算分析 经常看到的公式是 b a u d f s o c / 12 / ( 计时器溢出需的数量 ) 2 s m o d ∗ 16 baud \dfrac{f_{soc}/12/(计时器溢出需的数量)}{2^{smod}*16} baud2smod∗16fsoc​/12/(计时器溢出需的数量…

【漏洞复现】1. WebLogic 反序列化漏洞(CVE-2019-2890)复现与分析

文章目录 1. 基础知识2. 复现2.1 漏洞介绍漏洞影响版本&#xff1a; 2.2 漏洞原理分析2.3 漏洞复现2.3.1 环境搭建2.3.2 漏洞验证2.3.3 漏洞利用2.3.4 POC分析 2.4 漏洞修复 1. 基础知识 WebLogic是美国Oracle公司出品的一个application server&#xff0c;确切的说是一个基于J…

麒麟 V10 一键安装 Oracle 19C 19.22 单机版

Oracle 一键安装脚本&#xff0c;演示 麒麟 V10 一键安装 Oracle 19C 19.22 单机版过程&#xff08;全程无需人工干预&#xff09;&#xff1a;&#xff08;脚本包括 ORALCE PSU/OJVM 等补丁自动安装&#xff09; ⭐️ 脚本下载地址&#xff1a;Shell脚本安装Oracle数据库 脚…

THM学习笔记—Bounty Hacker

nmap扫描&#xff0c;扫了一大堆但只有三个端口是开放的 试试ftp是否可以匿名登录 可以匿名登录&#xff0c;把里面的文件下载下来 查看里面的内容&#xff0c;猜lin为用户名&#xff0c;locks.txt为密码列表&#xff0c;使用hydra进行ssh登录。 找到密码了&#xff0c;进行ssh…

LoRa模块在野外科研与环境保护中的角色:科技守护自然之宝

随着科技的不断发展&#xff0c;LoRa&#xff08;低功耗广域网&#xff09;模块在野外科研与环境保护中正发挥着越来越重要的作用。其卓越的通信能力、低功耗特性以及良好的穿透能力&#xff0c;为科学家和环保人士提供了一种先进的技术手段&#xff0c;有助于更深入、更全面地…

LLM—Transformer作用及信息流

一、Transformer的作用 Transformer架构的精髓在于其创新性地采用了编码器与解码器的堆叠设计&#xff0c;这一设计巧妙地融合了多头自注意力机制&#xff08;Multi-Head Attention&#xff09;和位置前馈网络&#xff08;Position-wise Feed Forward Network&#xff09;两大核…

数据指标体系搭建指南:让数据说话,让决策更明智

如今数据已经成为企业运营中不可或缺的重要资源。无论是产品研发、市场营销还是决策制定&#xff0c;数据都发挥着至关重要的作用。因此&#xff0c;搭建一个科学、合理的数据指标体系&#xff0c;对于企业的长远发展具有重要意义。一个完善的数据指标体系&#xff0c;可以帮助…

基于爬虫对山西省人口采集+机器学习的可视化平台

文章目录 数据来源一、研究背景与意义二、研究目标三、研究内容与方法四、预期成果五、代码讲解六、全文总结 数据来源 1.所有原数据均来自&#xff1a;国家统计局-政府的数据网站 2.涉及到的一些预测数据是根据现有数据进行预测而来。 本文从数据来源&#xff0c;研究意义&am…

ideaSSM 高校公寓交流员管理系统bootstrap开发mysql数据库web结构java编程计算机网页源码maven项目

一、源码特点 idea 开发 SSM 高校公寓交流管理系统是一套完善的信息管理系统&#xff0c;结合SSM框架和bootstrap完成本系统&#xff0c;对理解JSP java编程开发语言有帮助系统采用SSM框架&#xff08;MVC模式开发&#xff09;&#xff0c;系统具有完整的源代码和数据库&…

3.7 RK3399项目开发实录-板载OpenWRT系统的使用(wulianjishu666)

STM32F103单片机从零到项目开发程序实例 下载链接&#xff1a;https://pan.baidu.com/s/1dWNskNinrMk4bxaE-jgHhQ?pwdymn3 1. OpenWRT 手册 1.1. 支持设备列表 主控板卡型号RK3568ROC-RK3568-PC/Station-P2 1.2. 登录 IP 、登录密码和 WIFI 名称 固件默认登录 IP 为 192.1…

数据结构—稀疏多项式相加

利用链表实现两个稀疏多项式相加。 代码 #include <iostream> using namespace std;// 定义多项式项结构体 typedef struct {int x; // 系数int y; // 指数 } Elemtype;// 定义链表节点结构体 typedef struct Node {Elemtype data;struct Node* next; } *LinkList, N…

如何使用 ArcGIS Pro 制作好看的高程渲染图

虽然 ArcGIS Pro 已经提供了很多好看的配色方案&#xff0c;但是如果直接对高程DEM进行渲染效果不是很理想&#xff0c;我们可以结合山体阴影让高程渲染图看起来更加立体&#xff0c;这里为大家介绍一下制作方法&#xff0c;希望能对你有所帮助。 数据来源 教程所使用的数据是…

C#,精巧实用的代码,调用GDI32.DLL绘制图形的源程序

C#画图既可以使用 System.Drawing 命名空间的各种基础类。在某些情况下,也可以直接调用 Windows 的公共基础链接库 GDI32.DLL。 1 GDI32.DLL图形设备接口 意图 Microsoft Windows图形设备界面(GDI)使应用程序能够在视频显示器和打印机上使用图形和格式化文本。基于Window…