Spring Boot SprigAplicationスタートクラス(一)

14518 ワード

目次
  • ディレクトリ
  • クライテリア
  • 、起源
  • 、SpringApple準備段階
  • 2.1、Webアプリケーションタイプを推定する
  • 2.2、アプリケーションコンテキスト初期器を読み込むAppliation Controtext Initializer
  • 2.3、アプリケーションイベントモニタを読み込みます.
  • 2.4、推論アプリケーションガイド
  • 、SpringApple構成
  • 、まとめ
  • 目次
    前言
            最近はSpring Bootに関する授業を勉強していますが、その過程でメモを取って、便利になったら思い出してください.同時にここで皆さんと検討しています.文章の中に漏れがあったり、補足があったり、間違ったものがあったりしたら、早速提出してください.ここで感謝します.
    始まる前に、いくつかの問題を持って勉強してほしいです.2、どうやって実現しましたか?3、長所と短所は何ですか?4、Springとどうやって繋がりましたか?これは自己に対する質問です.問題を持って勉強するのはより良い学習方法であり、理解を深めるのに役立つと思います.はい、これからテーマに入ります.
    1、起源
            上記の記事では、Spring Bootの自動組立に関する知識を説明しましたが、Spring BootはどうしてSpringApplication.run()を実行して起動できるのか分かりません.これはどのようにSpringアプリケーションと文脈を結び、どのようにSpringApplicationを使用してSpringアプリケーションを駆動しますか?次にSpringApplicationについての議論が展開される.
    注:本記事で使用されているSpring Bootバージョンは2.1.6.BUILD-NAPSHOTです.
    2、SprigAplication準備段階
             SpringApplicationは、Webアプリケーションタイプを推定し、Springをロードするコンテキスト初期化器やイベントモニター、およびデフォルト属性を設定するなど、動作前に一連の準備を行っている.続いて、ソース方式で勉強します.
    まずスタートクラスを見てみます.
    @SpringBootApplication
    public class DiveInSpringBootApplication {
        public static void main(String[] args) {
            SpringApplication.run(DiveInSpringBootApplication.class, args);
        }
    }
    @SpringBootApplicationこの注釈は前の文章で述べたように、自動組立及びスキャンを行うことであり、ここではもはや説明しなく、私達は主にrun方法に注目している.
    public static ConfigurableApplicationContext run(Class> primarySource, String... args) {
        return run(new Class>[] { primarySource }, args);
    }
    
    public static ConfigurableApplicationContext run(Class>[] primarySources, String[] args) {
        return new SpringApplication(primarySources).run(args);
    }
    primarySourcesクラスに入ることによって、SpringApplicationクラスを構成し、その後、run方法を起動することができ、ここで、準備段階の動作はすべてSpringApplicationのコンストラクタで処理されることが分かる.
    public class SpringApplication {
    
        ...
    
        private Set> primarySources;
        
        private WebApplicationType webApplicationType;
        
        private List> initializers;
        
        private List> listeners;
        
        private Class> mainApplicationClass;
    
        public SpringApplication(ResourceLoader resourceLoader, Class>... primarySources) {
        
            // resourceLoader        Resource   ClassLoader。     null
            this.resourceLoader = resourceLoader;
            
            //              null,    
            Assert.notNull(primarySources, "PrimarySources must not be null");
            
            // primarySources SpringApplication.run   ,        
            this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
            
            //   Web       
            this.webApplicationType = WebApplicationType.deduceFromClasspath();
            
            //             initializer
            setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
            
            //           listener
            setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
            
            //      ,        
            this.mainApplicationClass = deduceMainApplicationClass();
        }
        
        ...
        
    }
    次に、コンストラクタにおける重点部分について詳細に説明する.
    2.1、Webアプリケーションの種類を推定するSpringApplicationは、アプリケーションのタイプを指定することを許可し、Webアプリケーションおよび非Webアプリケーションを実質的に含む.Spring Boot 2.0から始まって、Webアプリケーションはまた Servlet WebReactive Webに分けることができます.準備段階では、現在のClassPathの下にあるクラスが存在するかどうかを確認することによって、アプリケーションのタイプを導出する.WebApplicationType.deduceFromClasspath()に入ります.
    public enum WebApplicationType {
    
        /**
         *   web   
         */
        NONE,
    
        /**
         * servlet web   
         */
        SERVLET,
        
        /**
         * reactive web   
         */
        REACTIVE;
    
        private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
                "org.springframework.web.context.ConfigurableWebApplicationContext" };
    
        private static final String WEBMVC_INDICATOR_CLASS = "org.springframework." + "web.servlet.DispatcherServlet";
    
        private static final String WEBFLUX_INDICATOR_CLASS = "org." + "springframework.web.reactive.DispatcherHandler";
    
        private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
    
        private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext";
    
        private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext";
    
        static WebApplicationType deduceFromClasspath() {
            if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
                    && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
                return WebApplicationType.REACTIVE;
            }
            for (String className : SERVLET_INDICATOR_CLASSES) {
                if (!ClassUtils.isPresent(className, null)) {
                    return WebApplicationType.NONE;
                }
            }
            return WebApplicationType.SERVLET;
        }
    
        ...
    
    }
    ClassUtils.isPresentが存在し、DispatcherHandlerが存在しない場合には、本方法でDispatcherServletを用いて判断することができる.ServletContainerおよびReactive webが存在しない場合、現在のアプリケーションは非Webタイプである.他はServletタイプです.
    Reactive:Reactive応答式プログラミングは、非同期または同時、イベント駆動、プッシュPUSH機構、および観察者モードの派生である新しいプログラミングスタイルです.
    2.2、アプリケーションコンテキスト初期器を読み込むApplication Controtext Initializer
    次に、Springアプリケーションのコンテキスト初期器をロードするプロセスConfigurableWebApplicationContextに進む.
    public class SpringApplication {
    
        ...
        
        private List> initializers;
        
        public void setInitializers(Collection extends ApplicationContextInitializer>> initializers) {
            this.initializers = new ArrayList<>();
            this.initializers.addAll(initializers);
        }
        
        private  Collection getSpringFactoriesInstances(Class type) {
            return getSpringFactoriesInstances(type, new Class>[] {});
        }
        
        private  Collection getSpringFactoriesInstances(Class type, Class>[] parameterTypes, Object... args) {
            ClassLoader classLoader = getClassLoader();
            // Use names and ensure unique to protect against duplicates
            Set names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
            List instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
            AnnotationAwareOrderComparator.sort(instances);
            return instances;
        }
    }
    ここではSpring工場負荷機構Servlet Webによって取得されることがわかる.上記の記事に関連して説明したように、本方法はsetInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class))のすべてのリソースからkey SpringFactoriesLoader.loadFactoryNames(type, classLoader)の実装クラスのセットを取得することであり、以下のようにMETA-INF/spring.factoriesのパケットの下のApplicationContextInitializerファイルであることを知っている.
    # Initializers
    org.springframework.context.ApplicationContextInitializer=\
    org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
    org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
    ここで取得されたのはspring-boot-autoconfigureおよびspring.factoriesのコンテキスト初期化器であり、次いでSharedMetadataReaderFactoryContextInitializerの方法によってこれらの実施形態を初期化する.
    private  List createSpringFactoriesInstances(Class type, Class>[] parameterTypes,
                ClassLoader classLoader, Object[] args, Set names) {
        List instances = new ArrayList<>(names.size());
        for (String name : names) {
            try {
                Class> instanceClass = ClassUtils.forName(name, classLoader);
                Assert.isAssignable(type, instanceClass);
                Constructor> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
                T instance = (T) BeanUtils.instantiateClass(constructor, args);
                instances.add(instance);
            }
            catch (Throwable ex) {
                throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
            }
        }
        return instances;
    }
    ここでは、まずConditionEvaluationReportLoggingListenerによってこれらのクラスを初期化し、その後、初期化されたクラスをListに保存して戻ってきて、順序付け動作を行い、最後にcreateSpringFactoriesInstances(type, parameterTypes, classLoader, args, names)BeanUtils.instantiateセットの変数に追加する.これでこのプロセスは終了します.
    例えば、SpringApplicationのような初期装置の内容を見てみよう.
    class SharedMetadataReaderFactoryContextInitializer
            implements ApplicationContextInitializer, Ordered {
    
        ...
        
        @Override
        public void initialize(ConfigurableApplicationContext applicationContext) {
            applicationContext.addBeanFactoryPostProcessor(new CachingMetadataReaderFactoryPostProcessor());
        }
    
        ...
    
    }
    このクラスは、Springを実装したinitializersインターフェースと、SharedMetadataReaderFactoryContextInitializer方法を書き換えることができる.同じように、他のInitializerインターフェースも同様に実現されます.ここでは、コンテキストでApplicationContextInitializer bean工場のバックプロセッサに加入している.
    Application Contect tInitializerインターフェースの主な役割はConfigrable Application Controtext((zhi refsh)メソッドが呼び出す前にいくつかの初期化作業をすることです.
    2.3、アプリケーションイベントモニタApplication Listenerをロードする
    次いで、アプリケーションイベントモニタinitialize()をロードし、プロセスは「ローディングアプリケーションコンテキスト初期器」と基本的に一致し、同様にCachingMetadataReaderFactoryPostProcessorを呼び出す方法であるが、ここで取得されるのは、key setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class))のオブジェクトセットであり、以下のようにgetSpringFactoriesInstancesのパケットの下のApplicationListenerファイルである.
    # Application Listeners
    org.springframework.context.ApplicationListener=\
    org.springframework.boot.autoconfigure.BackgroundPreinitializer
    最後に、spring-boot-autoconfigureオブジェクトをspring.factories方式でBackgroundPreinitializer属性変数に入れる.
    public void setListeners(Collection extends ApplicationListener>> listeners) {
        this.listeners = new ArrayList<>();
        this.listeners.addAll(listeners);
    }
    私たちも例を挙げて、setListenersのようなモニターの内容を見てみましょう.
    public class BackgroundPreinitializer implements ApplicationListener {
    
        ...
        
        @Override
        public void onApplicationEvent(SpringApplicationEvent event) {
            if (!Boolean.getBoolean(IGNORE_BACKGROUNDPREINITIALIZER_PROPERTY_NAME)
                    && event instanceof ApplicationStartingEvent && preinitializationStarted.compareAndSet(false, true)) {
                
                ...
                
            }
            if ((event instanceof ApplicationReadyEvent || event instanceof ApplicationFailedEvent)
                    && preinitializationStarted.get()) {
                    
                ...
                
            }
        }
        
        ...
    }
    このクラスは、Springのlistenersインターフェースを実装し、書き換えBackgroundPreinitializer方法で対応するイベントをトリガして動作することが分かる.同じように、他のListenerも同様に実現します.このインターフェースの主な機能は、ベリファイア、メッセージ変換器などを含む、他のバックグラウンドスレッドを起動することである.
    現在のspring bootでサポートされているイベントの種類は以下の通りです.
  • Apple FailedEvent:このイベントは、spring bootの起動に失敗したときの動作
  • である.
  • Apple PreparedEvent:コンテキストcontext準備時にトリガ
  • Application ReadyEvent:コンテキストが準備されたときにトリガされる
  • Apple StartEvent:spring boot起動傍受クラス
  • SprigAplication Event:SprigAplicationを取得する
  • Aplication Evironment PreparedEvent:環境事前準備
  • 2.4、推論応用ガイド類
    準備段階の最後のステップは、アプリケーションのブートクラスを推定することであり、すなわち、起動main方法のクラスを取得することであり、実行するのはApplicationListener方法である.
    private Class> deduceMainApplicationClass() {
        try {
            StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
            for (StackTraceElement stackTraceElement : stackTrace) {
                if ("main".equals(stackTraceElement.getMethodName())) {
                    return Class.forName(stackTraceElement.getClassName());
                }
            }
        }
        catch (ClassNotFoundException ex) {
            // Swallow and continue
        }
        return null;
    }
    onApplicationEvent方法で現在のスレッドの実行スタックを取得し、deduceMainApplicationClass()を介して方法名を取得し、main方法かどうかを判断し、最後にmainメソッドの所在クラスに戻ることが見られます.
    3、SprigAplicationの配置
             getStackTrace()準備段階が終わったら、当然運行段階に入るべきですが、運行段階の前にもう一つの操作があります.つまり、getMethodName()のデフォルト構成を変更することができます.冒頭のコード例は、アプリケーションのメインクラスのmainメソッドに書かれているのは全部SpringApplicationです.このような書き方は私たちの要求を満たしていないかもしれません.以下は、SpringApplicationを用いて構成を追加する.
    @SpringBootApplication
    public class DiveInSpringBootApplication {
        public static void main(String[] args) {
            new SpringApplicationBuilder(DiveInSpringBootApplication.class)
                    
                    //         
                    .web(WebApplicationType.SERVLET)
                    
                    //    banner       、   、  、   
                    .bannerMode(Banner.Mode.OFF)
                    
                    //        banner
                    .banner()
                    
                    //        initializer      
                    .initializers()
                    
                    //        listeners     
                    .listeners()
                    .run(args);
        }
    }
    
    このように実装されたSpringApplication.run(xx.class)は、カスタム構成を追加することができることが分かる.もちろん配置はこれだけではないです.他のソースも自分で見てください.
    4、まとめ
            これにより、SpringApplicationの準備作業が終了し、これは主にBannerSpringApplicationBuilderSpringApplicationSpringApplicationprimarySourcesのこれらの属性を初期化し、webApplicationTypeをカスタマイズした構成である.次の記事では、initializersの動作段階について説明します.これらのlistenersの構成とアプリケーション初期化器、モニターなどは、どのように使用されていますか?
    以上がこの章の内容です.もし文章の中に間違いがあったり、補充が必要なものがあったら、直ちに提出してください.参考:
    『Spring Bootプログラミング思想』