スプリングブート起動の自動ロード構成の原則は関連しています

    スプリングブーツ自動ローディングの原理

概要:

Spring-Bootプロジェクトでは、スタートアップクラスを作成し、@ SpringBootApplicationアノテーションをマークするだけで、自動構成が完了します。これの原則は、主に@SpringBootApplicationアノテーションの役割です。このアノテーションを詳細に分析してみましょう。

 1つは、SpringBootApplicationアノテーションです。

@SpringBootApplicationコメントプライマリパッケージング3つのサブアノテーションは次のとおりです:
@ SpringBootConfiguration、@ EnableAutoConfiguration、@ ComponentScan

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class))
public @interface SpringBootApplication {
   
   

その中で、@ ComponentScanはパッケージの役割をスキャンするために使用され、@ SpringBootConfigurationは@Configurationのラッパーであり、@ Configurationは@Componentのラッパーです。つまり、このクラスは構成クラスです。
        
@EnableAutoConfigurationアノテーションは、メインの分析オブジェクトです。

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

このアノテーションには2つの主要な機能があります。1つは@AutoConfigurationPackageで、もう1つは@Import(キー分析)です。

@Importは、指定されたクラスのクラスをロードするだけでなく、ImportSelector実装のサブクラスも指定できることがわかっています。インターフェイスメソッドselectImports、返されるリストは、この@Importアノテーションがロードする必要のあるクラスです。

EnableAutoConfigurationImportSelectorの具体的な実現。

public String[] selectImports(AnnotationMetadata metadata) {
			try {
				AnnotationAttributes attributes = getAttributes(metadata);
				List<String> configurations = getCandidateConfigurations(metadata,
						attributes); // 加载候选的配置类
				// ....略
				return configurations.toArray(new String[configurations.size()]);
			}
			catch (IOException ex) {
				throw new IllegalStateException(ex);
			}
		}

selectImportsメソッドはgetCandidateConfigurationsメソッドを呼び出して候補クラスをロードし、このメソッドは実際にはSpringFactoriesLoaderのloadFactoryNamesメソッドを使用してさまざまな自動構成クラスをロードします。

		protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
			AnnotationAttributes attributes) {
			List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
					getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
			
			return configurations;
		}

SpringFactoriesLoaderのloadFactoryNamesメソッドは、EnableAutoConfigurationクラスの完全なクラス名を渡し、主にMETA-INF / spring.factoriesパスからリソースファイルをロードしてトラバースし、の完全なクラス名キーに一致する値を見つけます。分析と処理。

public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
			String factoryClassName = factoryClass.getName();
			try {
				Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
						ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); // 加载资源文件
				List<String> result = new ArrayList<String>();
				while (urls.hasMoreElements()) {
					URL url = urls.nextElement();
					Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url)); // 解析成prop
					String factoryClassNames = properties.getProperty(factoryClassName); // 得到factoryClass的属性
					result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
				}
				return result;
			}
			catch (IOException ex) {
			}
		}

        以下に、spring-boot-autoconfigureのjarパッケージにあるMETA-INFからのこのリソースファイルの構成を示します。

  org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
        org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
        org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
        org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\

    2.いくつかのデフォルト構成クラスの構成の説明

1.DataSourceAutoConfigurationの分析

1)クラスでの注釈の構成

@Configuration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ Registrar.class, DataSourcePoolMetadataProvidersConfiguration.class })

このアノテーションは合計4つのサブアノテーションをパックします。@ Configurationはアノテーション構成クラスであり、@ ConditionalOnClassはDataSourceクラスが存在する必要があることを示し、@ EnableConfigurationPropertiesは構成ファイルをロードするためのものです。
 @Importによって導入された2つのクラスはすべて、クラスのロードを直接指定するメソッドを使用します。レジストラの1つは、データソースBeanの初期化後にDataSourceInitializerをロードする目的を実現するためにBeanPostProcessorを生成するために使用されます。DataSourcePoolMetadataProvidersConfigurationは、データソースプロバイダークラスの構成を提供します。DBCP、TOMCATなどのデータソース提供クラス。

2)メソッドとサブクラスの構成

方法1)dataSourceInitializer構成は、主にパブリックDataSourceInitializer Beanを初期化することです。このBeanの主な機能
            は、構成クラス構成のURLとパスワードを作成されたデータソースにデフォルトで割り当てることです。

        @Bean
            @ConditionalOnMissingBean // 表示
            public DataSourceInitializer dataSourceInitializer() {
                return new DataSourceInitializer();
            }

内部クラス2)PooledDataSourceConfigurationこの内部クラスは、主に、@ Importによって直接提供されるいくつかのデータソースのクラスを提供します。

        @Configuration
            @Conditional(PooledDataSourceCondition.class)
            @ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
            @Import({ DataSourceConfiguration.Tomcat.class, DataSourceConfiguration.Hikari.class,
                    DataSourceConfiguration.Dbcp.class, DataSourceConfiguration.Dbcp2.class,
                    DataSourceConfiguration.Generic.class })
            protected static class PooledDataSourceConfiguration {
            }

     この内部クラスにはdbcp2データソースクラスが必要であり、spring.datasource.typeが正しく指定されているか、指定されていない必要があります(matchIfMissing = true)。データソースを作成するにはどうすればよいですか。その作成メソッドの最終的な実行は、DataSourceBuilder.create()..... type()。build()です。

   @ConditionalOnClass(org.apache.commons.dbcp2.BasicDataSource.class) 
            @ConditionalOnProperty(name = "spring.datasource.type", havingValue = "org.apache.commons.dbcp2.BasicDataSource", matchIfMissing = true)
            static class Dbcp2 extends DataSourceConfiguration {
        
                @Bean
                @ConfigurationProperties("spring.datasource.dbcp2")
                public org.apache.commons.dbcp2.BasicDataSource dataSource(
                        DataSourceProperties properties) {
                    return createDataSource(properties,
                            org.apache.commons.dbcp2.BasicDataSource.class);
                }
            }

 最後に、自分で作成し、DataSourceBuilderで作成したデータソースを一覧表示し、構成ファイルを使用してプロパティを指定します。

       @ConfigurationProperties(prefix = "spring.datasource.xxx")
            @Bean(destroyMethod = "close", name = "xxx")
            public DataSource dataSource1() {
                return DataSourceBuilder.create().type(dataSourceType).build();
            }

    3.条件付き一連の注釈のソースコード分析

    上記の構成クラスでは、さまざまなConditionalOnClass条件使用します。以下では、ConditionalOnClassアノテーションからこのタイプのアノテーションの原理を分析します。まず、Conditionalアノテーションの値は、Conditionを実装するインターフェースです。このインターフェースのmatchesメソッドの実装がtrueを返すと、Beanが読み込まれ、ConditionalOnClassのアノテーションが実際にConditionalと、を実装するクラスをラップします。条件インターフェースはOnClassConditionです

1. OnClassCondition
            OnClassConditionはSpringBootConditionを継承し、SpringBootConditionはmatchメソッドを実装しますが、実際には、matchesはサブクラステンプレートメソッド getMatchOutcomeを呼び出します。メソッドは次のとおりです(ConditionOutcomeはブール型のマークと情報をラップするpoクラスであり、matchメソッドはtrueであり、noMatchはfalseであることに注意してください)。

  public ConditionOutcome getMatchOutcome(ConditionContext context,
            AnnotatedTypeMetadata metadata) {
            ConditionMessage matchMessage = ConditionMessage.empty();
            MultiValueMap<String, Object> onClasses = getAttributes(metadata,ConditionalOnClass.class); // 得到注解元素
            if (onClasses != null) {
                List<String> missing = getMatchingClasses(onClasses, MatchType.MISSING,context);  // 判断缺少的类
                if (!missing.isEmpty()) {
                    return ConditionOutcome
                            .noMatch(ConditionMessage.forCondition(ConditionalOnClass.class)
                                    .didNotFind("required class", "required classes")
                                    .items(Style.QUOTE, missing));
                }
            }
            //....
            return ConditionOutcome.match(matchMessage);
        }
private List<String> getMatchingClasses(MultiValueMap<String, Object> attributes,
				MatchType matchType, ConditionContext context) {
			List<String> matches = new LinkedList<String>();
			addAll(matches, attributes.get("value"));
			addAll(matches, attributes.get("name"));
			Iterator<String> iterator = matches.iterator();
			while (iterator.hasNext()) {
				if (!matchType.matches(iterator.next(), context)) {
					iterator.remove(); // matchType传入的枚举是不符合,这里!就是符合的移除,剩余的就是没有提供的。
				}
			}
			return matches;
		}
private enum MatchType {
			PRESENT { // 提供了class
				public boolean matches(String className, ConditionContext context) {
					return ClassUtils.isPresent(className, context.getClassLoader());
				}
			},
			MISSING {  // 没有提供calss
				public boolean matches(String className, ConditionContext context) {
					return !ClassUtils.isPresent(className, context.getClassLoader());
				}
			};
			public abstract boolean matches(String className, ConditionContext context);
		}

上記のコードから、OnClassConditionの判断は非常に単純であり、アノテーションによって提供される値のクラスタイプに基づいており、class.forNameの反映がtrueである、つまりクラスが提供されることがわかります。 、そうでない場合、それは提供されません。クラス、そして最後にgetMatchOutcomeメソッドは一致を返します。

    コンプリート!

 

おすすめ

転載: blog.csdn.net/shuixiou1/article/details/113276641