【SpringBoot入門から習熟まで】第3章 Springbootスターター、コンテナ、共通アノテーション解析

三、Springboot分析

3.1 インポート方法

3.1.1 親プロジェクトの継承

プロジェクトに継承spring-boot-starter-parentできます。

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>${springboot.version}</version>
</parent>

spring-boot-starter-parentプロジェクト内では、別の親プロジェクトも継承していますspring-boot-dependencies

ここに画像の説明を挿入

3.1.2 依存関係の管理

依存関係管理を通じて Springboot の依存関係を導入しますspring-boot-dependencies

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>${springboot.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

3.2 バージョン番号

3.2.1 デフォルトのバージョン番号

spring-boot-starter-parent親プロジェクトspring-boot-dependenciesで、開発で一般的に使用されるほぼすべての依存関係のバージョン番号を宣言します。したがって、Springboot プロジェクトではバージョン番号に注意する必要はなく、バージョン マッチングは自動的に行われます。

<properties>
    <activemq.version>5.16.5</activemq.version>
    <antlr2.version>2.7.7</antlr2.version>
    <appengine-sdk.version>1.9.97</appengine-sdk.version>
    <artemis.version>2.19.1</artemis.version>
    <aspectj.version>1.9.7</aspectj.version>
    <assertj.version>3.21.0</assertj.version>
    <atomikos.version>4.0.6</atomikos.version>
    <awaitility.version>4.1.1</awaitility.version>
    <build-helper-maven-plugin.version>3.2.0</build-helper-maven-plugin.version>
    <byte-buddy.version>1.11.22</byte-buddy.version>
    <caffeine.version>2.9.3</caffeine.version>
    <cassandra-driver.version>4.13.0</cassandra-driver.version>
    <classmate.version>1.5.1</classmate.version>
    <commons-codec.version>1.15</commons-codec.version>
    <commons-dbcp2.version>2.9.0</commons-dbcp2.version>
    <commons-lang3.version>3.12.0</commons-lang3.version>
    <commons-pool.version>1.6</commons-pool.version>
    <commons-pool2.version>2.11.1</commons-pool2.version>
    <couchbase-client.version>3.2.7</couchbase-client.version>
    <db2-jdbc.version>11.5.7.0</db2-jdbc.version>
    <dependency-management-plugin.version>1.0.11.RELEASE</dependency-management-plugin.version>
    <derby.version>10.14.2.0</derby.version>
    <dropwizard-metrics.version>4.2.10</dropwizard-metrics.version>
    <ehcache.version>2.10.9.2</ehcache.version>
    <ehcache3.version>3.9.9</ehcache3.version>
    <elasticsearch.version>7.15.2</elasticsearch.version>
    <embedded-mongo.version>3.0.0</embedded-mongo.version>
    <flyway.version>8.0.5</flyway.version>
    <freemarker.version>2.3.31</freemarker.version>
    <git-commit-id-plugin.version>4.9.10</git-commit-id-plugin.version>
    <glassfish-el.version>3.0.4</glassfish-el.version>
    <glassfish-jaxb.version>2.3.6</glassfish-jaxb.version>
    <glassfish-jstl.version>1.2.6</glassfish-jstl.version>
    <groovy.version>3.0.11</groovy.version>
    <gson.version>2.8.9</gson.version>
    <h2.version>1.4.200</h2.version>
    <hamcrest.version>2.2</hamcrest.version>
    <hazelcast.version>4.2.5</hazelcast.version>
    <hazelcast-hibernate5.version>2.2.1</hazelcast-hibernate5.version>
    <hibernate.version>5.6.9.Final</hibernate.version>
    <hibernate-validator.version>6.2.3.Final</hibernate-validator.version>
    <hikaricp.version>4.0.3</hikaricp.version>
    <hsqldb.version>2.5.2</hsqldb.version>
    <htmlunit.version>2.54.0</htmlunit.version>
    <httpasyncclient.version>4.1.5</httpasyncclient.version>
    <httpclient.version>4.5.13</httpclient.version>
    <httpclient5.version>5.1.3</httpclient5.version>
    <httpcore.version>4.4.15</httpcore.version>
    <httpcore5.version>5.1.3</httpcore5.version>
    <infinispan.version>12.1.12.Final</infinispan.version>
    <influxdb-java.version>2.22</influxdb-java.version>
    <jackson-bom.version>2.13.3</jackson-bom.version>
    <jakarta-activation.version>1.2.2</jakarta-activation.version>
    <jakarta-annotation.version>1.3.5</jakarta-annotation.version>
    <jakarta-jms.version>2.0.3</jakarta-jms.version>
    <jakarta-json.version>1.1.6</jakarta-json.version>
    <jakarta-json-bind.version>1.0.2</jakarta-json-bind.version>
    <jakarta-mail.version>1.6.7</jakarta-mail.version>
    <jakarta-management.version>1.1.4</jakarta-management.version>
    <jakarta-persistence.version>2.2.3</jakarta-persistence.version>
    <jakarta-servlet.version>4.0.4</jakarta-servlet.version>
    <jakarta-servlet-jsp-jstl.version>1.2.7</jakarta-servlet-jsp-jstl.version>
    <jakarta-transaction.version>1.3.3</jakarta-transaction.version>
    <jakarta-validation.version>2.0.2</jakarta-validation.version>
    <jakarta-websocket.version>1.1.2</jakarta-websocket.version>
    <jakarta-ws-rs.version>2.1.6</jakarta-ws-rs.version>
    <jakarta-xml-bind.version>2.3.3</jakarta-xml-bind.version>
    <jakarta-xml-soap.version>1.4.2</jakarta-xml-soap.version>
    <jakarta-xml-ws.version>2.3.3</jakarta-xml-ws.version>
    <janino.version>3.1.7</janino.version>
    <javax-activation.version>1.2.0</javax-activation.version>
    <javax-annotation.version>1.3.2</javax-annotation.version>
    <javax-cache.version>1.1.1</javax-cache.version>
    <javax-jaxb.version>2.3.1</javax-jaxb.version>
    <javax-jaxws.version>2.3.1</javax-jaxws.version>
    <javax-jms.version>2.0.1</javax-jms.version>
    <javax-json.version>1.1.4</javax-json.version>
    <javax-jsonb.version>1.0</javax-jsonb.version>
    <javax-mail.version>1.6.2</javax-mail.version>
    <javax-money.version>1.1</javax-money.version>
    <javax-persistence.version>2.2</javax-persistence.version>
    <javax-transaction.version>1.3</javax-transaction.version>
    <javax-validation.version>2.0.1.Final</javax-validation.version>
    <javax-websocket.version>1.1</javax-websocket.version>
    <jaxen.version>1.2.0</jaxen.version>
    <jaybird.version>4.0.6.java8</jaybird.version>
    <jboss-logging.version>3.4.3.Final</jboss-logging.version>
    <jdom2.version>2.0.6.1</jdom2.version>
    <jedis.version>3.7.1</jedis.version>
    <jersey.version>2.35</jersey.version>
    <jetty-el.version>9.0.52</jetty-el.version>
    <jetty-jsp.version>2.2.0.v201112011158</jetty-jsp.version>
    <jetty-reactive-httpclient.version>1.1.11</jetty-reactive-httpclient.version>
    <jetty.version>9.4.48.v20220622</jetty.version>
    <jmustache.version>1.15</jmustache.version>
    <johnzon.version>1.2.18</johnzon.version>
    <jolokia.version>1.7.1</jolokia.version>
    <jooq.version>3.14.16</jooq.version>
    <json-path.version>2.6.0</json-path.version>
    <json-smart.version>2.4.8</json-smart.version>
    <jsonassert.version>1.5.0</jsonassert.version>
    <jstl.version>1.2</jstl.version>
    <jtds.version>1.3.1</jtds.version>
    <junit.version>4.13.2</junit.version>
    <junit-jupiter.version>5.8.2</junit-jupiter.version>
    <kafka.version>3.0.1</kafka.version>
    <kotlin.version>1.6.21</kotlin.version>
    <kotlin-coroutines.version>1.5.2</kotlin-coroutines.version>
    <lettuce.version>6.1.8.RELEASE</lettuce.version>
    <liquibase.version>4.5.0</liquibase.version>
    <log4j2.version>2.17.2</log4j2.version>
    <logback.version>1.2.11</logback.version>
    <lombok.version>1.18.24</lombok.version>
    <mariadb.version>2.7.5</mariadb.version>
    <maven-antrun-plugin.version>3.0.0</maven-antrun-plugin.version>
    <maven-assembly-plugin.version>3.3.0</maven-assembly-plugin.version>
    <maven-clean-plugin.version>3.1.0</maven-clean-plugin.version>
    <maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
    <maven-dependency-plugin.version>3.2.0</maven-dependency-plugin.version>
    <maven-deploy-plugin.version>2.8.2</maven-deploy-plugin.version>
    <maven-enforcer-plugin.version>3.0.0</maven-enforcer-plugin.version>
    <maven-failsafe-plugin.version>2.22.2</maven-failsafe-plugin.version>
    <maven-help-plugin.version>3.2.0</maven-help-plugin.version>
    <maven-install-plugin.version>2.5.2</maven-install-plugin.version>
    <maven-invoker-plugin.version>3.2.2</maven-invoker-plugin.version>
    <maven-jar-plugin.version>3.2.2</maven-jar-plugin.version>
    <maven-javadoc-plugin.version>3.3.2</maven-javadoc-plugin.version>
    <maven-resources-plugin.version>3.2.0</maven-resources-plugin.version>
    <maven-shade-plugin.version>3.2.4</maven-shade-plugin.version>
    <maven-source-plugin.version>3.2.1</maven-source-plugin.version>
    <maven-surefire-plugin.version>2.22.2</maven-surefire-plugin.version>
    <maven-war-plugin.version>3.3.2</maven-war-plugin.version>
    <micrometer.version>1.8.7</micrometer.version>
    <mimepull.version>1.9.15</mimepull.version>
    <mockito.version>4.0.0</mockito.version>
    <mongodb.version>4.4.2</mongodb.version>
    <mssql-jdbc.version>9.4.1.jre8</mssql-jdbc.version>
    <mysql.version>8.0.29</mysql.version>
    <nekohtml.version>1.9.22</nekohtml.version>
    <neo4j-java-driver.version>4.4.6</neo4j-java-driver.version>
    <netty.version>4.1.78.Final</netty.version>
    <netty-tcnative.version>2.0.53.Final</netty-tcnative.version>
    <okhttp3.version>3.14.9</okhttp3.version>
    <oracle-database.version>21.3.0.0</oracle-database.version>
    <pooled-jms.version>1.2.4</pooled-jms.version>
    <postgresql.version>42.3.6</postgresql.version>
    <prometheus-client.version>0.12.0</prometheus-client.version>
    <quartz.version>2.3.2</quartz.version>
    <querydsl.version>5.0.0</querydsl.version>
    <r2dbc-bom.version>Arabba-SR13</r2dbc-bom.version>
    <rabbit-amqp-client.version>5.13.1</rabbit-amqp-client.version>
    <rabbit-stream-client.version>0.4.0</rabbit-stream-client.version>
    <reactive-streams.version>1.0.4</reactive-streams.version>
    <reactor-bom.version>2020.0.20</reactor-bom.version>
    <rest-assured.version>4.4.0</rest-assured.version>
    <rsocket.version>1.1.2</rsocket.version>
    <rxjava.version>1.3.8</rxjava.version>
    <rxjava-adapter.version>1.2.1</rxjava-adapter.version>
    <rxjava2.version>2.2.21</rxjava2.version>
    <saaj-impl.version>1.5.3</saaj-impl.version>
    <selenium.version>3.141.59</selenium.version>
    <selenium-htmlunit.version>2.54.0</selenium-htmlunit.version>
    <sendgrid.version>4.7.6</sendgrid.version>
    <servlet-api.version>4.0.1</servlet-api.version>
    <slf4j.version>1.7.36</slf4j.version>
    <snakeyaml.version>1.29</snakeyaml.version>
    <solr.version>8.8.2</solr.version>
    <spring-amqp.version>2.4.6</spring-amqp.version>
    <spring-batch.version>4.3.6</spring-batch.version>
    <spring-data-bom.version>2021.1.5</spring-data-bom.version>
    <spring-framework.version>5.3.21</spring-framework.version>
    <spring-hateoas.version>1.4.4</spring-hateoas.version>
    <spring-integration.version>5.5.13</spring-integration.version>
    <spring-kafka.version>2.8.7</spring-kafka.version>
    <spring-ldap.version>2.3.8.RELEASE</spring-ldap.version>
    <spring-restdocs.version>2.0.6.RELEASE</spring-restdocs.version>
    <spring-retry.version>1.3.3</spring-retry.version>
    <spring-security.version>5.6.6</spring-security.version>
    <spring-session-bom.version>2021.1.3</spring-session-bom.version>
    <spring-ws.version>3.1.3</spring-ws.version>
    <sqlite-jdbc.version>3.36.0.3</sqlite-jdbc.version>
    <sun-mail.version>1.6.7</sun-mail.version>
    <thymeleaf.version>3.0.15.RELEASE</thymeleaf.version>
    <thymeleaf-extras-data-attribute.version>2.0.1</thymeleaf-extras-data-attribute.version>
    <thymeleaf-extras-java8time.version>3.0.4.RELEASE</thymeleaf-extras-java8time.version>
    <thymeleaf-extras-springsecurity.version>3.0.4.RELEASE</thymeleaf-extras-springsecurity.version>
    <thymeleaf-layout-dialect.version>3.0.0</thymeleaf-layout-dialect.version>
    <tomcat.version>9.0.64</tomcat.version>
    <unboundid-ldapsdk.version>4.0.14</unboundid-ldapsdk.version>
    <undertow.version>2.2.18.Final</undertow.version>
    <versions-maven-plugin.version>2.8.1</versions-maven-plugin.version>
    <webjars-locator-core.version>0.48</webjars-locator-core.version>
    <wsdl4j.version>1.6.3</wsdl4j.version>
    <xml-maven-plugin.version>1.0.2</xml-maven-plugin.version>
    <xmlunit2.version>2.8.4</xmlunit2.version>
</properties>

したがって、上記の pom ファイルの依存関係から結論付けることができます。

に存在する依存関係についてdependenciesは、プロジェクトをインポートするときに、依存関係はデフォルトでバージョン番号を記述する必要はありませんが、dependenciesそこで管理されていない依存関係は当然バージョン番号を宣言する必要があります。

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
</dependencies>

ここに画像の説明を挿入

3.2.2 デフォルトのバージョン番号を変更する

現在のプロジェクトに次の構成を導入して、親プロジェクトの構成を上書きし、mysql のバージョン番号を変更します。

<properties>
    <mysql.version>5.1.49</mysql.version>
</properties>

ここに画像の説明を挿入

3.3 ランチャー

Springboot プロジェクトのスターターとは何ですか?

**Springboot はすべての機能シナリオを抽出し、スターター (スターター) を 1 つずつ作成します. これらのスターター関連のシナリオのすべての依存関係をプロジェクトにインポートするだけで済みます. **使用したいシーンのランチャーをインポートします。

すべての公式スターターは、同様の命名規則に従います。spring-boot-starter-*は特定の種類のアプリケーション*です。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

spring-boot-starter-web: Web モジュールが通常の操作に依存するコンポーネントをインポートするのを手伝ってください。

サードパーティのスターター名は、公式の Spring Boot コンポーネントによって予約されているルールであるため、spring-bootで始まる。thirdpartyprojectたとえば、通常は という名前の というサードパーティのスターター プロジェクトがありますthirdpartyproject-spring-boot-starter

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.10</version>
</dependency>

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.2.2</version>
</dependency>

すべてのシーン イネーブラーの中で最も低いレベルの依存関係spring-boot-starter

ここに画像の説明を挿入

公式ドキュメントでのスターターの紹介

3.4 自動設定

ランチャーは 2 つのコアで構成されています。

  1. モジュール関連の依存関係をインポートします。
  2. 自動構成;

自動構成は、Spring ブートのコア機能の 1 つであり、Spring アプリケーションの開発時に必要な、ビジネスに依存しない多くの構成を排除または削減します。

そして、その背後にある考え方は新しいものではなく、convention over configuration (convention over configuration) と呼ばれる原則を使用しており、事前にいくつかの合理的な仮定 (つまり、慣習) を作成しています。追加の構成により、提供される機能を直接使用できるため、明示的な構成が不要になります。たとえば、maven は、ソース コード ディレクトリがデフォルトでプロジェクト ルート ディレクトリの下の src/main/java ディレクトリであると想定します. このディレクトリ構造に従う場合、ソース コード ディレクトリが何であるかを maven に伝える必要はありません. この原則は、さまざまなフレームワークでよく使用されます. フレームワークはこれらの規則に従って対応する構成を作成することが多いためです. これらの構成が私たちのニーズを満たしている場合, 何もする必要はなく, ただ受け入れてすぐに使用できます. これらの構成がそうでない場合のみ.私たちのニーズを満たすには、調整が必要です。

次に、spring-boot-starter-web を例にして、どのような自動構成が行われたかを確認します。

3.4.1 Tomcat コンテナ

  • 組み込みの Tomcat 依存関係を導入します。
  • Tomcat を構成します。

3.4.2 スプリング MVC

  • SpringMVC コンポーネントの完全なセットを導入します。
  • SpringMVC 共通コンポーネントを構成します。
    • フロントコントローラーディスパッチャーサーブレット
    • ハンドラーマッパー requestMappingHandlerMapping
    • ハンドラーアダプター requestMappingHandlerAdapter
    • ビュー リゾルバ viewResolver
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class Springboot02BootApplication {
    
    

    public static void main(String[] args) {
    
    
        //获取SpringIOC容器,ConfigurableApplicationContext是ApplicationContext接口的子接口
        ConfigurableApplicationContext context = SpringApplication.run(Springboot02BootApplication.class, args);

        //获取Spring容器中所有Bean的名称
        String[] beanDefinitionNames = context.getBeanDefinitionNames();
        for (String name : beanDefinitionNames) {
    
    
            System.out.println(name);
        }
    }
}

3.4.3 ウェブ機能

  • 一般的な Web 機能を紹介します。
  • Web 機能を構成します。
    • multipartResolver をアップロードする
    • JSON 処理マッピングJackson2HttpMessageConverter
    • 例外処理 handlerExceptionResolver
    • バックグラウンド データ検証 mvcValidator
    • 文字エンコーディングフィルター characterEncodingFilter

3.4.4 コンポーネントのスキャン

コンポーネント スキャンを構成する必要はありません。デフォルトのコンポーネント スキャン パッケージは、スタートアップ クラスが配置されているパッケージです。つまり、スタートアップ クラスが配置されているパッケージ内のコンポーネントと、その下にあるすべてのサブパッケージがデフォルトでスキャンされます。

スキャン パッケージを変更する必要がある場合は、スタートアップ クラスの @SpringBootApplication アノテーションに scanBasePackages 構成項目を追加するか、@ComponentScan アノテーションを介してスキャン パスを指定します。

@SpringBootApplication(scanBasePackages = "com.newcapec")

3.4.5 デフォルト値の設定

デフォルトの構成は、最終的に次のような特定のクラスにマップされます。

3.4.6 オンデマンドで自動構成アイテムをロードする

Springboot には多くのスターターがあり、それぞれに自動構成アイテムがあり、すべての自動構成機能は spring-boot-autoconfigure パッケージにあります。

ここに画像の説明を挿入

注: Springboot は、起動時にすべての自動構成アイテムをロードするわけではありませんが、導入されたスターターに従って、対応する自動構成アイテムを開きます。

3.5 Springboot コンテナー

3.5.1 @Configuration+@Bean

Bean の構成は、Springboot プロジェクトの @Configuration および @Bean アノテーションを介して実行されます。

3.5.1.1 Bean クラス

顧客クラス:

public class Customers {
    
    

    private String name;
    private Integer age;

    public Customers() {
    
    
    }

    public Customers(String name, Integer age) {
    
    
        this.name = name;
        this.age = age;
    }

    public String getName() {
    
    
        return name;
    }

    public void setName(String name) {
    
    
        this.name = name;
    }

    public Integer getAge() {
    
    
        return age;
    }

    public void setAge(Integer age) {
    
    
        this.age = age;
    }

    @Override
    public String toString() {
    
    
        return "Customers{" +
            "name='" + name + '\'' +
            ", age=" + age +
            '}';
    }
}

注文クラス:

public class Orders {
    
    

    private String name;
    private Customers customers;

    public Orders() {
    
    
    }

    public Orders(String name) {
    
    
        this.name = name;
    }

    public Orders(String name, Customers customers) {
    
    
        this.name = name;
        this.customers = customers;
    }

    public String getName() {
    
    
        return name;
    }

    public void setName(String name) {
    
    
        this.name = name;
    }

    public Customers getCustomers() {
    
    
        return customers;
    }

    public void setCustomers(Customers customers) {
    
    
        this.customers = customers;
    }

    @Override
    public String toString() {
    
    
        return "Orders{" +
            "name='" + name + '\'' +
            ", customers=" + customers +
            '}';
    }
}

3.5.1.2 構成クラス

@Configuration
public class MyConfig {
    
    

    @Bean
    public Customers getCustomers(){
    
    
        return new Customers("Tom", 20);
    }

    @Bean
    public Orders getOrders(){
    
    
        //在@Configuration注解的配置类中,如果直接调用@Bean标注的方法,相当于从IOC容器中获取该bean并依赖注入
        return new Orders("Tom's Order", getCustomers());
    }
}

3.5.1.3 テスト

public static void main(String[] args) {
    
    
    //获取SpringIOC容器,ConfigurableApplicationContext是ApplicationContext接口的子接口
    ConfigurableApplicationContext context = SpringApplication.run(Springboot02BootApplication.class, args);

    //获取Spring中加载的Bean
    String[] beanDefinitionNames = context.getBeanDefinitionNames();
    for (String name : beanDefinitionNames) {
    
    
        System.out.println(name);
    }

    Customers customers = context.getBean(Customers.class);
    System.out.println(customers);

    Orders orders = context.getBean(Orders.class);
    System.out.println(orders);
    
    System.out.println("是否单例:" + (customers == orders.getCustomers()));
}

ここに画像の説明を挿入

  1. @Bean アノテーションのメソッド名はデフォルトの Bean 名であり、@Bean の値設定項目は Bean 名を変更できます
  2. @Bean アノテーションは構成クラスで使用され、コンテナのコンポーネントをメソッドに登録します。これもデフォルトで単一のインスタンスです
  3. @Configuration アノテーションの proxyBeanMethods 構成項目が false の場合、Bean はシングルトン モードではなく、デフォルト値は true です

3.5.2 @インポート

  • @Import アノテーションは、高速インポートによる Spring の IOC コンテナーへのインスタンスの追加を実装します。
  • @Import を使用してサードパーティのパッケージをインポートできます。もちろん @Bean アノテーションも使用できますが、@Import アノテーションをすばやくインポートする方法の方が便利です。
  • @Import の 3 つの使用法には、主に次のようなものがあります。
    1. クラス配列メソッド。
    2. ImportSelector メソッド;
    3. ImportBeanDefinitionRegistrar メソッド;

3.5.2.1 クラス配列

クラス配列に直接入力します。クラス配列には 0 以上を含めることができます。

豆クラス:

public class Dog {
    
    
}

public class Cat {
    
    
}

構成クラスにインポート:

@Configuration
@Import({
    
    Dog.class, Cat.class})
public class MyConfig {
    
    

    @Bean
    public Customers getCustomers(){
    
    
        return new Customers("Tom", 20);
    }

    @Bean
    public Orders getOrders(){
    
    
        return new Orders("Tom's Order", getCustomers());
    }
}

ここに画像の説明を挿入

対応するインポートされた Bean が Spring コンテナーに追加され、コンテナー内の Bean 名はclass の完全なクラス名になります。

3.5.2.2 インポートセレクター

ImportSelector インターフェースの実装クラスは @Import アノテーションを介してインポートされ、登録が必要な Bean オブジェクトはその抽象メソッド selectImports で定義されます。

豆クラス:

public class Student {
    
    
}

public class Teacher {
    
    
}

ImportSelector インターフェースの実装クラス:

public class MyImportSelector implements ImportSelector {
    
    
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
    
    
        return new String[]{
    
    "com.newcapec.bean.Student", "com.newcapec.bean.Teacher"};
    }
}

ImportSelector インターフェイスの selectImports メソッド:

  • 戻り値 String[]:コンテナに登録されているコンポーネントの完全なクラス名を表す文字列配列。
  • パラメータ AnnotationMetadata: 現在 @Import によって注釈が付けられているすべての注釈情報を示します。

構成クラスにインポート:

@Configuration
@Import({
    
    Dog.class, Cat.class, MyImportSelector.class})
public class MyConfig {
    
    

    @Bean
    public Customers getCustomers(){
    
    
        return new Customers("Tom", 20);
    }

    @Bean
    public Orders getOrders(){
    
    
        return new Orders("Tom's Order", getCustomers());
    }
}

ここに画像の説明を挿入

selectImports メソッドは空の配列を返すことができますが、null を返すことはできません。そうしないと、null ポインター例外が報告されます。

3.5.2.3 ImportBeanDefinitionRegistrar

ImportBeanDefinitionRegistrar インターフェースの実装クラスは @Import アノテーションを介してインポートされ、登録が必要な Bean オブジェクトはその抽象メソッド registerBeanDefinitions で定義されます。ImportSelector の使用法と似ていますが、この使用法では登録された Bean をカスタマイズできます。

豆クラス:

public class Users {
    
    
}

ImportBeanDefinitionRegistrar インターフェースの実装クラス:

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    
    

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    
    

        //指定Bean定义信息
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Users.class);
        //注册一个Bean指定bean名称
        registry.registerBeanDefinition("users", rootBeanDefinition);
    }
}

ImportBeanDefinitionRegistrar インターフェースの registerBeanDefinitions メソッド:

  • パラメータ AnnotationMetadata: 現在 @Import によって注釈が付けられているすべての注釈情報を示します。
  • パラメータ BeanDefinitionRegistry: Bean が登録用に定義されていることを示します。

構成クラスにインポート:

@Configuration
@Import({
    
    Dog.class, Cat.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})
public class MyConfig {
    
    

    @Bean
    public Customers getCustomers(){
    
    
        return new Customers("Tom", 20);
    }

    @Bean
    public Orders getOrders(){
    
    
        return new Orders("Tom's Order", getCustomers());
    }
}

3.5.3 @条件付き

@Conditional アノテーションは、任意の型またはメソッドで使用できます. @Conditional アノテーションを介していくつかの条件判断を構成できます. すべての条件が満たされた場合、 @Conditional でマークされたターゲットはSpringコンテナによって処理されます.

3.5.3.1 派生アノテーション

ここに画像の説明を挿入

3.5.3.2 使用分析

@Conditional 派生アノテーション 機能(ともに指定条件を満たしているかを判定)
@ConditionalOnJava システムの Java バージョンが要件を満たしているかどうか
@ConditionalOnBean 指定された Bean クラスがある
@ConditionalOnMissingBean Bean クラスが指定されていません
@ConditionalOnExpression 指定された SpEL 式に一致します
@ConditionalOnClass 指定されたクラスがある
@ConditionalOnMissingClass クラスが指定されていません
@ConditionalOnSingleCandidate コンテナには指定された Bean が 1 つしかないか、この Bean が優先 Bean です
@ConditionalOnProperty 指定されたプロパティ属性には、指定された値があります
@ConditionalOnResource 指定されたリソースはパスの下に存在します
@ConditionalOnWebApplication システム環境はWeb環境
@ConditionalOnNotWebApplication システム環境は Web 環境ではありません
@ConditionalOnjndi 指定された項目は JNDI に存在します

3.5.3.3 ケース

豆クラス:

public class Color {
    
    
}

public class Red {
    
    
}

public class Green {
    
    
}

構成クラス:

@Configuration
@Import({
    
    Dog.class, Cat.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})
public class MyConfig {
    
    

    @Bean
    public Customers getCustomers(){
    
    
        return new Customers("Tom", 20);
    }

    @Bean
    public Orders getOrders(){
    
    
        return new Orders("Tom's Order", getCustomers());
    }

    @Bean("color")
    public Color getColor(){
    
    
        return new Color();
    }

    @ConditionalOnBean(name = "color")
    @Bean("red")
    public Red getRed(){
    
    
        return new Red();
    }

    @ConditionalOnMissingBean(name = "color")
    @Bean("green")
    public Green getGreen(){
    
    
        return new Green();
    }
}

テスト:

public static void main(String[] args) {
    
    
    //获取SpringIOC容器,ConfigurableApplicationContext是ApplicationContext接口的子接口
    ConfigurableApplicationContext context = SpringApplication.run(Springboot02BootApplication.class, args);

    //获取Spring中加载的Bean
    String[] beanDefinitionNames = context.getBeanDefinitionNames();
    for (String name : beanDefinitionNames) {
    
    
        System.out.println(name);
    }

    Customers customers = context.getBean(Customers.class);
    System.out.println(customers);

    Orders orders = context.getBean(Orders.class);
    System.out.println(orders);

    System.out.println("是否单例:" + (customers == orders.getCustomers()));

    boolean color = context.containsBean("color");
    System.out.println("容器中是否包含color组件:" + color);

    boolean red = context.containsBean("red");
    System.out.println("容器中是否包含red组件:" + red);

    boolean green = context.containsBean("green");
    System.out.println("容器中是否包含green组件:" + green);
}

カラー Bean が存在する場合:

容器中是否包含color组件:true
容器中是否包含red组件:true
容器中是否包含green组件:false

カラー Bean が存在しない場合:

容器中是否包含color组件:false
容器中是否包含red组件:false
容器中是否包含green组件:true

3.6 Springboot 起動解析

3.6.1 メインプログラム

通常の Java クラスに @SpringBootApplication アノテーションを追加すると、Springboot アプリケーションとして自動的に識別され、このクラスのメイン メソッドは Springboot プログラムのエントリ メソッドでもあります。

@SpringBootApplication
public class MainApplication {
    
    

    public static void main(String[] args) {
    
    
        SpringApplication.run(MainApplication.class, args);
    }
}

3.6.2 @SpringBoot アプリケーション

SpringBoot プログラムのメイン プログラム アノテーションである現在のクラスは、SpringBoot のメイン構成クラスでもあります。

@SpringBootApplication アノテーションのソースコード:

@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、@ComponentScan、および @EnableAutoConfiguration の 3 つの注釈が含まれています。

3.6.3 @SpringBootConfiguration

@SpringBootConfiguration は、Springboot プログラムの構成クラスのアノテーションです。

@SpringBootConfiguration アノテーションのソースコード:

@Target({
    
    ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
    
    

}

@Configuration アノテーションのソースコード:

@Target({
    
    ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
    
    

}

分析:

  • @Configuration アノテーションは、クラスを構成クラスとして識別するために Spring フレームワークによって提供されるアノテーションです。
  • @SpringBootConfiguration は、Springboot が提供するアノテーションであり、これが Springboot 構成クラスであることを示します。
  • 構成クラスもコンテナー内のコンポーネントであり、これは @Component アノテーションの役割です。

3.6.4 @ComponentScan

@ComponentScan アノテーションのソースコード:

@Retention(RetentionPolicy.RUNTIME)
@Target({
    
    ElementType.TYPE})
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
    
    

}

分析:

  • パッケージをスキャンし、パッケージとそのサブパッケージの下にあるクラスをスキャンするように指定します。
  • @Repeatable(ComponentScans.class): @Repeatable アノテーションのないアノテーションで、同じアノテーションを同じ場所で使用するとエラーが報告されることを示します。このメタアノテーションでマークされたアノテーションを使用すると、同じアノテーションを同じ場所で使用できます。場所。@ComponentScan アノテーションは、@ComponentScans アノテーション内で複数回使用できます。

3.6.5 @EnableAutoConfiguration

自動構成機能をオンにします。

Spring の前の時代では、xml コンテンツを構成する必要がありましたが、現在は Springboot によって自動的に構成され、@EnableAutoConfiguration によって SpringBoot に自動構成機能を有効にするように指示されます。

@EnableAutoConfiguration アノテーションのソースコード:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    
    

}

ここでは、@AutoConfigurationPackage と @Import(AutoConfigurationImportSelector.class) の 2 つの注釈が関係しています。

3.6.6 @AutoConfigurationパッケージ

デフォルトのパッケージ ルールを指定する自動構成パッケージ。

@AutoConfigurationPackage アノテーションのソース コード:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
    
    

}

ここでも @Import(AutoConfigurationPackages.Registrar.class) アノテーションが関係しています。

3.6.7 @Import(AutoConfigurationPackages.Registrar.class)

@Import アノテーションを使用して AutoConfigurationPackages.Registrar クラスをインポートし、一連のコンポーネントをコンテナーに登録します。

AutoConfigurationPackages.Registrar のソース コード:

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
    
    

    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
    
    
        register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
    }

    @Override
    public Set<Object> determineImports(AnnotationMetadata metadata) {
    
    
        return Collections.singleton(new PackageImports(metadata));
    }
}

ブレークポイントのデバッグ機能を使用すると、@SpringBootApplication アノテーションが付けられたクラスが配置されているパッケージとそのサブパッケージ配下のすべてのクラスを把握し、Spring コンテナーに自動的にスキャンして登録できます。以前Springを学習する際に使用したxmlファイルで、コンポーネントスキャンベースパッケージの機能は同じです。

ここに画像の説明を挿入

3.6.8 @Import(AutoConfigurationImportSelector.class)

@Import アノテーションを使用して AutoConfigurationImportSelector クラスをインポートし、一連のコンポーネントをコンテナーに登録します。

AutoConfigurationImportSelector のソース コードの一部:

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());
    }
            
    protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    
    
        if (!isEnabled(annotationMetadata)) {
    
    
            return EMPTY_ENTRY;
        }
        AnnotationAttributes attributes = getAttributes(annotationMetadata);
        List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
        configurations = removeDuplicates(configurations);
        Set<String> exclusions = getExclusions(annotationMetadata, attributes);
        checkExcludedClasses(configurations, exclusions);
        configurations.removeAll(exclusions);
        configurations = getConfigurationClassFilter().filter(configurations);
        fireAutoConfigurationImportEvents(configurations, exclusions);
        return new AutoConfigurationEntry(configurations, exclusions);
    }
    
    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    
    
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
                getBeanClassLoader());
        Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
                + "are using a custom packaging, make sure that file is correct.");
        return configurations;
    }
}

3.6.8.1 selectImports メソッド

ソース コードの 99 行目では、getAutoConfigurationEntry(annotationMetadata);いくつかのコンポーネントがバッチでコンテナーにインポートされます。

3.6.8.2 getAutoConfigurationEntry方法

ソース コードの 123 行目で、呼び出しにより、List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);コンテナーにインポートする準備ができているすべてのコンポーネントが取得されます。これらのコンポーネントは自動構成クラスであり、特定のシーン (スターター) に必要なすべてのコンポーネントをコンテナーにインポートして、これらのコンポーネントを構成します。自動構成クラスを使用すると、構成注入関数コンポーネントなどを手動で作成する作業が省略されます。

ここに画像の説明を挿入

3.6.8.3 ファクトリーローダー

ソースコードの 178 行目では、SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());ファクトリをロードしてすべてのコンポーネントを取得しています。

3.6.8.4 spring.factories ファイル

SpringFactoriesLoader クラスの loadFactoryNames() メソッドのソース コード:

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
    
    
    ClassLoader classLoaderToUse = classLoader;
    if (classLoader == null) {
    
    
        classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
    }

    String factoryTypeName = factoryType.getName();
    return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}

private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
    
    
    Map<String, List<String>> result = (Map)cache.get(classLoader);
    if (result != null) {
    
    
        return result;
    } else {
    
    
        HashMap result = new HashMap();

        try {
    
    
            Enumeration urls = classLoader.getResources("META-INF/spring.factories");

            while(urls.hasMoreElements()) {
    
    
                URL url = (URL)urls.nextElement();
                UrlResource resource = new UrlResource(url);
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                Iterator var6 = properties.entrySet().iterator();

                while(var6.hasNext()) {
    
    
                    Entry<?, ?> entry = (Entry)var6.next();
                    String factoryTypeName = ((String)entry.getKey()).trim();
                    String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                    String[] var10 = factoryImplementationNames;
                    int var11 = factoryImplementationNames.length;

                    for(int var12 = 0; var12 < var11; ++var12) {
    
    
                        String factoryImplementationName = var10[var12];
                        ((List)result.computeIfAbsent(factoryTypeName, (key) -> {
    
    
                            return new ArrayList();
                        })).add(factoryImplementationName.trim());
                    }
                }
            }

            result.replaceAll((factoryType, implementations) -> {
    
    
                return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
            });
            cache.put(classLoader, result);
            return result;
        } catch (IOException var14) {
    
    
            throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);
        }
    }
}

ソースコードを確認して、Springboot がデフォルトでクラスパスの下に META-INF/spring.factories ファイルをロードすることを確認してください。

ここに画像の説明を挿入

spring-boot-autoconfigure-2.6.9.jar パッケージで META-INF/spring.factories ファイルを見つけます。

25 行目から 158 行目までに、インポートするコンポーネントが合計 133 個あります。これは、デバッグで表示される数と一致しています。

ここに画像の説明を挿入

3.6.8.5 オンデマンドで自動構成を有効にする

デフォルトでは 130 個の自動構成がロードされますが、Springboot は引き続き条件に従ってルール @Conditional を組み立て、最終的にそれらをオンデマンドで構成します。コードが 129 行目まで実行されると、インポートする必要がある自動構成クラスは 24 個だけであることがわかります。

ここに画像の説明を挿入

3.6.8.6 デフォルト構成の変更

@Bean
@ConditionalOnBean(MultipartResolver.class)
@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
public MultipartResolver multipartResolver(MultipartResolver resolver) {
    
    
    // Detect if the user has created a MultipartResolver but named it incorrectly
    return resolver;
}
  • Springboot はデフォルトで一番下のすべてのコンポーネントを構成しますが、ユーザーが自分で構成すると、ユーザーの優先順位が与えられます。
  • @Bean アノテーション付きメソッドの仮パラメーターは、コンテナー内のコンポーネントを検索できます。

3.6.8.5 例 1

1つを探してください。ここにRedisAutoConfigurationがあります。redisがどのように構成されているかを確認してください

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({
    
     LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
    
    

    @Bean
    @ConditionalOnMissingBean(name = "redisTemplate")
    @ConditionalOnSingleCandidate(RedisConnectionFactory.class)
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
    
    
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnSingleCandidate(RedisConnectionFactory.class)
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
    
    
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }
}

分析:

  • @Configuration: このクラスが構成クラスであることを示します。
  • @ConditionalOnClass(RedisOperations.class): この構成は、RedisOperations クラスが存在する場合に有効になります。
  • @EnableConfigurationProperties(RedisProperties.class): Redis の一部の構成パラメーターは、R​​edisProperties クラスで初期化されます。yml またはプロパティに記述された構成は、このクラスの対応するロジックです。
  • @Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class }) : redis 接続のインポートの構成を示します。

3.6.8.6 例 2

DispatcherServletAutoConfiguration は、DispatcherServlet がどのように構成されているかを確認するためのものです。

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
public class DispatcherServletAutoConfiguration {
    
    

	/**
	 * The bean name for a DispatcherServlet that will be mapped to the root URL "/".
	 */
	public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";

	/**
	 * The bean name for a ServletRegistrationBean for the DispatcherServlet "/".
	 */
	public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";

	@Configuration(proxyBeanMethods = false)
	@Conditional(DefaultDispatcherServletCondition.class)
	@ConditionalOnClass(ServletRegistration.class)
	@EnableConfigurationProperties(WebMvcProperties.class)
	protected static class DispatcherServletConfiguration {
    
    

	}

	@Configuration(proxyBeanMethods = false)
	@Conditional(DispatcherServletRegistrationCondition.class)
	@ConditionalOnClass(ServletRegistration.class)
	@EnableConfigurationProperties(WebMvcProperties.class)
	@Import(DispatcherServletConfiguration.class)
	protected static class DispatcherServletRegistrationConfiguration {
    
    

	}

	@Order(Ordered.LOWEST_PRECEDENCE - 10)
	private static class DefaultDispatcherServletCondition extends SpringBootCondition {
    
    

	}

	@Order(Ordered.LOWEST_PRECEDENCE - 10)
	private static class DispatcherServletRegistrationCondition extends SpringBootCondition {
    
    

	}

}

分析:

  • @Configuration構成クラスも Bean ですが、構成クラスについては、いくつかのシナリオでの実行順序が必要であり、保証する必要があります。Springと比較すると、Spring Bootの下での自動構成の管理はブラックボックスであり、コンテナ内の状況に応じて、自動構成クラスがロードされているかどうか、およびロードする順序を動的に判断するため、 : Spring Boot の自動構成には、順序に関する強い要件があります。需要に応じて、Spring Boot は @AutoConfigureBefore、@AutoConfigureAfter、@AutoConfigureOrder (これら 3 つのアノテーションを総称して「3 つのアノテーション」と呼びます) を提供して、この需要を解決するのに役立てます。
  • @AutoConfigureOrderバージョン 1.3.0 で追加され、絶対的な順序を示します (数値が小さいほど優先度が高くなります)。
  • @AutoConfigureBefore構成の順序を制御し@AutoConfigureAfter特定の構成の後または前にロードされることを示します。ロードされる前提は、初期化が完了したことです。DispatcherServletAutoConfigurationServletWebServerFactoryAutoConfiguration
  • DispatcherServletAutoConfiguration には 4 つの内部クラスが定義されており、DispatcherServletAutoConfiguration の設定が有効になった場合にのみ、内部クラスの設定条件を判断できます。

要約:

  • Springboot は、最初にすべての自動構成クラス xxxxxAutoConfiguration をロードします。
  • 各自動構成クラスは、条件に従って有効になります。デフォルトでは、構成ファイルで指定された値がバインドされ、xxxxProperties クラスから取得され、xxxProperties が構成ファイルにバインドされます。
  • 有効な構成クラスは、コンテナー内に多くのコンポーネントを組み立てます。これらのコンポーネントがコンテナー内に存在する限り、これらの機能と同等です。
  • カスタマイズされた構成:
    • ユーザーは、基礎となるコンポーネントを直接 @Bean に置き換えます。
    • ユーザーは、このコンポーネントによって取得された構成ファイルの値を確認すると、それを変更します。

おすすめ

転載: blog.csdn.net/ligonglanyuan/article/details/126125992