Spring Factores

12759 ワード

Spring BootにはSpring Factoresという非常に結合した拡張機構がある。この拡張機構は実際にJava中のSPI拡張機構を模倣して実現された。
 
SPIメカニズムとは
SPIのフルネームはService Provider Interfaceです。ほとんどの開発者は知らないかもしれません。これはメーカーやプラグインのためです。java.util.ServiceLoaderの文書には比較的詳しい紹介があります。
簡単な総括の下でjava SPIの構造の思想。私たちのシステムには抽象的なモジュールがたくさんあります。例えば、ログモジュールの方案、xml解析モジュール、jdbcモジュールの方案などです。向きの対象のデザインでは、モジュール間はインターフェースに基づいてプログラムし、モジュール間は実装類に対してハードコードをしないように勧めます。コードの中で具体的な実現類に関連すると、抜き差し可能な原則に違反します。もし代替が必要なら、コードの修正が必要です。モジュール組み立て時にプログラム内で動的に指定しないためには、サービス発見メカニズムが必要です。
java SPIは、あるインターフェースのためのサービスの実現の仕組みを提供しています。IOCの思想に似ています。組立の制御権をプログラムの外に移すということです。モジュール設計においては、このメカニズムが特に重要です。
 
Spring BootにおけるSPIメカニズム
SpringにはJava SPIと同様のローディング機構もある。META-INF/spring.factoresファイルにインターフェースの実装クラス名を設定して、プログラムでこれらのプロファイルを読み込み、実行します。
このカスタムSPI機構はSpring Boot Starterの実現の基礎である。
 
Spring Factores実現原理
spring-coreパッケージにSpring Factores Loader類が定義されていますが、このクラスはMETA-INF/spring.factoresファイルを検索し、指定インターフェースの配置を取得する機能を実現しています。このクラスでは、2つの対外的な方法が定義されている。
  • loadFactoresは、インターフェースクラスからその実装クラスの例を取得し、この方法はオブジェクトリストを返します。
  • loadFactoryNamesは、インターフェースからそのインターフェースクラスの名前を取得し、この方法ではクラス名のリストを返します。
  • 上の二つの方法の鍵は指定されたクラスLoaderからspring.factoresファイルを取得し、クラス名のリストを解析します。具体的なコードは以下の通りです。
    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        MultiValueMap<String, String> result = cache.get(classLoader);
        if (result != null) {
            return result;
        }
    
        try {
            Enumeration<URL> urls = (classLoader != null ?
                                     classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                                     ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
            result = new LinkedMultiValueMap<>();
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                UrlResource resource = new UrlResource(url);
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                for (Map.Entry<?, ?> entry : properties.entrySet()) {
                    String factoryClassName = ((String) entry.getKey()).trim();
                    for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                        result.add(factoryClassName, factoryName.trim());
                    }
                }
            }
            cache.put(classLoader, result);
            return result;
        }
        catch (IOException ex) {
            throw new IllegalArgumentException("Unable to load factories from location [" +
                                               FACTORIES_RESOURCE_LOCATION + "]", ex);
        }
    }
    
    コードから分かるように、この方法ではClass Loaderのすべてのjarに包まれたspring.factoresファイルを巡回します。つまり、私達は自分のjarにspring.factoresファイルを配置してもいいです。他のところの配置に影響しないし、他の人の配置にも覆われないです。
    spring.factoresはProptiesによって解析されたものです。だから私たちはファイルを書く時の内容は以下のように構成されています。
    com.xxx.interface=com.xxx.classname
    
    一つのインターフェースが複数のインプリメンテーションクラスを構成したいなら、’を使用して分割してもいいです。
     
    spring-bootカバンの中のspring.factoresファイル
    Spring Bootの多くのカバンの中にspring.factoresファイルがあります。下にはspring-bootカバンの中のspring.factoresファイルがあります。
    # PropertySource Loaders
    org.springframework.boot.env.PropertySourceLoader=\
    org.springframework.boot.env.PropertiesPropertySourceLoader,\
    org.springframework.boot.env.YamlPropertySourceLoader
    
    # Run Listeners
    org.springframework.boot.SpringApplicationRunListener=\
    org.springframework.boot.context.event.EventPublishingRunListener
    
    # Error Reporters
    org.springframework.boot.SpringBootExceptionReporter=\
    org.springframework.boot.diagnostics.FailureAnalyzers
    
    # Application Context Initializers
    org.springframework.context.ApplicationContextInitializer=\
    org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
    org.springframework.boot.context.ContextIdApplicationContextInitializer,\
    org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
    org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
    
    # Application Listeners
    org.springframework.context.ApplicationListener=\
    org.springframework.boot.ClearCachesApplicationListener,\
    org.springframework.boot.builder.ParentContextCloserApplicationListener,\
    org.springframework.boot.context.FileEncodingApplicationListener,\
    org.springframework.boot.context.config.AnsiOutputApplicationListener,\
    org.springframework.boot.context.config.ConfigFileApplicationListener,\
    org.springframework.boot.context.config.DelegatingApplicationListener,\
    org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
    org.springframework.boot.context.logging.LoggingApplicationListener,\
    org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
    
    # Environment Post Processors
    org.springframework.boot.env.EnvironmentPostProcessor=\
    org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
    org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\
    org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor
    
    # Failure Analyzers
    org.springframework.boot.diagnostics.FailureAnalyzer=\
    org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer,\
    org.springframework.boot.diagnostics.analyzer.BeanNotOfRequiredTypeFailureAnalyzer,\
    org.springframework.boot.diagnostics.analyzer.BindFailureAnalyzer,\
    org.springframework.boot.diagnostics.analyzer.BindValidationFailureAnalyzer,\
    org.springframework.boot.diagnostics.analyzer.UnboundConfigurationPropertyFailureAnalyzer,\
    org.springframework.boot.diagnostics.analyzer.ConnectorStartFailureAnalyzer,\
    org.springframework.boot.diagnostics.analyzer.NoUniqueBeanDefinitionFailureAnalyzer,\
    org.springframework.boot.diagnostics.analyzer.PortInUseFailureAnalyzer,\
    org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer,\
    org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyNameFailureAnalyzer,\
    org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyValueFailureAnalyzer
    
    # FailureAnalysisReporters
    org.springframework.boot.diagnostics.FailureAnalysisReporter=\
    org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter
    
    spring-boot-2.0.RELEASE.jar/META-INF/spring.factores