1.Spring5ソースコード-IOCコンテナの設計コンセプトとコアアノテーションの役割

何を学ぶことができますか?

0スプリング全体のコンテキスト
たBeanFactory説明1
たBeanFactoryとApplicationContextの間の前記差
SpringIoCのロード処理について説明3.簡単に
4.簡単にビーンのライフサイクルについて説明
Springで利用可能な拡張インターフェイスやメカニズムを呼び出す5.

1.Springソースコードとソースコードコンパイルの全体的なコンテキストの概要

 1.1.IOCとは

iocは制御の反転であり、レイヤー間およびクラス間の結合問題を解決するために使用される設計概念です。

たとえば、現在2つのクラスAとBがあり、クラスBはクラスAで参照されています。したがって、ある日、クラスBを置き換える場合は、どうすればよいですか?クラスBが100回参照される場合、置き換える必要があります。 100回?

ここで、AはBを直接呼び出します。Bを間接的に呼び出す場合は、Bをラップします。将来BをCに変更する場合は、ラッパークラスで置き換えるだけです。クラスAを変更する必要はありません。これはリバースを制御します。

Springはiocを使用し、Spring.ioc(A、B)はAとBの参照をiocに格納します。Springはそれを維持するのに役立つので、心配する必要はありません。

AでBを使用する場合は、Bに対応するインターフェースを使用してから、@ Autowiredアノテーションを使用します。

A {
   @Autowired
   private IB b;  
}

Bを交換するときは、傷ついたりかゆみを感じたりすることはありません。新しいクラスをIoCに配置するだけです。

1.2.Springソースコードの全体的なコンテキスト

Spring IoCはコンテナであり、多くのBeanがSpringIoCで管理されています。

では、これらのBeanはどのようにIoCに登録されますか?言い換えると、カスタムクラスはIoCコンテナによってBeanとしてどのように管理されますか?

最初に、春を開発するときに行った手順を思い出してください。 

第一步: 配置类. 配置类可以使用的方式通常由 
1) xml配置 
2) 注解配置 
3) javaconfig方式配置

第二步: 加载spring上下文 
1) 如果是xml, 则new ClassPathXmlApplicationContext("xml"); 
2) 如果是注解配置: 则new AnnotationConfigApplicationContext(config.class)

第三步: getBean() 
我们会讲自定义的类, 通过xml或者注解的方式注入到ioc容器中.</pre>

このステップでは、xmlまたはアノテーションで指定されたクラスがIoCコンテナーに挿入されます。 

1.2.1では、クラスをiocにどの程度正確に注入しますか? 

以下のプロセス全体を整理しましょう。 

最初の質問:クラスをBeanに生成するための最も重要でコアなクラスは何ですか?

BeanFactoryですか

質問2:BeanFactoryとは何ですか?

BeanFactoryは、Springのトップレベルのコアインターフェイスであり、単純なファクトリパターンを使用します。通常、インスタンスは名前に基づいて生成され、Beanオブジェクトは渡された一意の識別子に従って取得されますが、渡された後に特別に作成されます。パラメータ、またはパラメータCreate beforeの受け渡しは、さまざまなBeanを生成するための名前またはタイプに応じて、特定の状況によって異なります。 

一文の要約:BeanFactoryの責任はBeanを作成することです

次のコードについて考えてみます。 

public static void main( String[] args ) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.getBean("***");
    }

このコードによって実装される機能は、現在のファイルが配置されているディレクトリとそのサブディレクトリ内のファイルを読み取り、指定された名前のBeanを取得することです。プロセス全体を次の図に示します。 

画像

首先, 通过ClassPathXmlApplicationContext或者AnnotationConfigApplicationContext去读取配置,

然后, 将其交给BeanFactory.

第三. BeanFactory调用getBean()方法, 将Bean注入到IoC容器中**</pre>

構成の読み取りはxmlまたはアノテーションである可能性があることがわかりました。 異なる読み取り方法では異なるツールを使用する必要があります。次に、これらのツールの結果を統合してから、BeanFactoryに渡して処理する必要があります。

これらの類似点と相違点は BeanFactoryでは処理されないためです。BeanFactoryには、本番Beanという1つの役割しかありません。 

1.2.2では、さまざまなツールの読み取り構成はどのように統合されていますか?

構成を読み取るための別の実装が必要であることがわかっています。xmlメソッドとアノテーションメソッドを統合されたものに読み取り、それをbeanFactoryに配置します。これは誰ですか?BeanDefinition(Bean定義)です。 

それはどういう意味ですか?以下に示すように:

画像

 緑色のボックスで囲まれた部分を見てください。この意味は、さまざまなツールを介して、xmlApplicationContextであり、annotationApplicationContextツールで読み取られた構成であり、最終的にBeanDefinitionオブジェクトに構築されます。次にBeanDefinitionがBeanFactoryに渡されます。 、およびBeanFactoryはBeanDefinitionオブジェクトを均一に処理し、getBean()メソッドを呼び出して、それをIoCコンテナーに配置します。

1.2.3では、読み取り構成はどのようにBeanDefinitionに統合されますか?

例を挙げましょう。今、人がいます。たとえば、家を買ったばかりで、飾りたいです。ワードローブが必要です。このとき、ワードローブの店を見つけます。次に、私のニーズと色を教えてください。キャビネット、スタイルフォーマットどんな種類。それからワードローブ店は私のニーズを記録します。このとき、彼は自分で生産するのではなく、工場に通知して工場に生産させます。工場が生産するものに応じて、ワードローブ店内にはデザイナーがいて、そのデザイナーがついてきて、自分のニーズに合わせて絵を描いて、工場に渡します。工場では、その絵に基づいて豆を作っています。 

全体のプロセスは次のとおりです。

画像

 入り口は「私」です

1.必要があります。キャビネットを開けて、ワードローブ店を見つけます

2.ワードローブストアに私のニーズ、キャビネットの色とスタイルを伝え、ワードローブストアのデザイナーが私の要件に従って図面をデザインします

3.ワードローブショップが図面を工場に送り、工場は図面に従ってキャビネットを製造します

これがワードローブの作り方です。図面を描​​くとき、​​工場に1枚の図面が渡されます。これは非効率的です。n個を描いて一緒に工場に渡すことができるので、設計図には1枚あります。複数の図面を保管するためのコンテナ

後で、キャビネットショップをカスタマイズしたい場合は、デザイナーにキャビネットの色とスタイルを伝えれば問題ありません。プロセスは上記と同じです。 

全体のプロセスは私たちの豆の生産プロセスに似ています

1. @Componentアノテーションを使用してクラスを定義します。ワードローブストアが見つかりました。ワードローブストアは、ApplicationContextに似ています。

2. ApplicationContextにニーズを伝え、@ Lazyを遅延ロードし、シングルトンモードまたはマルチケースモード@Scopeを設定します。対応するのは、キャビネットの色とスタイルをカスタマイズすることです。次に、ワードローブストアのデザイナーBeanDefinitionRegistryがそれに応じてデザインします。私のニーズに合わせてBeanDefinitionに構築される図面。さまざまなBeanDefinitionRegistryがさまざまなBeanDefinitionを設計し、それらをすべてコンテナに入れます。

3.ワードローブストアのApplicationContextは、図面のスタックBeanDefinitionMapをファクトリに均一に配信し、ファクトリは必要に応じてBeanを生成し、生成されたBeanをIoCコンテナに配置します。

これは、@ Componentを持つクラスがロードされるプロセスです。 

ワードローブ店が良いビジネスをしたいのなら、彼はライブに行かなければならないので、それは良い売り上げが必要です。売り上げは不動産をスキャンし、装飾が必要な人に連絡する必要があります。1つずつ尋ねてください。 

でも100人に聞いてみたら、10人くらいで飾る必要があるので、レセプションもあるので、お客さんに連絡して、興味のあるお客さんを見て、フィルターで外して、家具をカスタマイズします。

ここには、営業と受付の2種類の人がいます。具体的なタスクは次のとおりです。

画像

売上高は、BeanDefinitionReaderと同等です。彼の役割は、不動産をスキャンして潜在的な顧客を見つけることです。対応するのは、xml構成または注釈注釈を読み取るBeanDefinitionReaderです。 

xmlには多くの構成と注釈がありますが、それがすべての目標ではありません。

レセプションでは、すべての潜在的な顧客をスキャンする必要があります。関心のある顧客をスキャンします。これは、BeanDefinitionScannerと同様に、潜在的な顧客をスキャンし、最後に@Componentアノテーションが付けられたクラスを除外します。

これは後でカスタム家具が必要なお客様です

BeanDefinitionReader对应的就去读取配置类, 看看有哪些需求需要搞装修.

它本身也是一个抽象类, 可以看到他有AnnotationBeanDefinitionReader和XmlBeanDefinitionReader

我们配置了配置包, 去扫描这个包下所有的类, **然后将扫描到的所有的类交给BeanDefinitionScanner, 它会去过滤带有@Component的类.** </pre>

上記のプロセスに関連して、構成ファイル全体がIoCにロードされるプロセスです。 

1.3.ApplicationContextとFactoryBeanの違い

1. FactoryBeanの機能は、Beanを生成することです。これは、BeanDefinitionに基づいてBeanを生成するため、一度に1つしか生成できません。

2. ApplicationContextには2種類あります。1つはxmlApplicationContextで、もう1つはannotationApplicationContextです。彼が渡すパラメータは構成ファイルです。つまり、ディレクトリに@Componentを持つすべてのクラスをロードできます。

どちらにも独自の使用シナリオがあり、ほとんどがApplicationContextを使用しています。

もう1つの違い:後で説明するように、ApplicationContextには、部外者との統合に使用できる2つの拡張インターフェイスがあります。たとえば、MyBatisとの統合です。

1.4.Beanのライフサイクル

画像

上の図に示すように、beanFactoryはBeanDefinitionを取得し、getBean()を直接呼び出してBean?を生成します。 

いいえ、Beanを作成するプロセスがあります。Beanのライフサイクルを見てみましょう。

第一步: 实例化. bean实例化的时候从BeanDefinition中得到Bean的名字, 然后通过反射机制, 将Bean实例化. 实例化以后, 这是还只是个壳子, 里面什么都没有.

第二步: 填充属性. 经过初始化以后, bean的壳子就有了, bean里面有哪些属性呢? 在这一步填充

第三步: 初始化. 初始化的时候, 会调用initMethod()初始化方法, destory()初始化结束方法

这个时候, 类就被构造好了.

第四步: 构造好了的类, 会被放到IoC的一个Map中. Map的key是beanName, value是bean实例. 这个Map是一个单例池, 也就是我们说的一级缓存

第五步: 我们就可以通过getBean("user"), 从单例池中获取雷鸣是user的类了.

 Beanを構築する過程で、循環依存関係など、多くの詳細な問題が発生します。

 画像

 クラスBはクラスAで呼び出されるため、BeanFactoryはAを構築するときにBを構築します。次にBを構築するときに、BもAに依存していることがわかります。このように、循環依存です。これは不可能です。 

Springは循環依存の問題をどのように解決しますか? 

出口を設定します。たとえば、Aは建設中、マークを設定して建設中です。次にBを建設し、建設中にAを適用します。このとき、建設するのは興味深いことです。 A、そしてAが構築されていることを見つけると、Aは再び構築されません。 

後で、Springが循環参照を解決する方法について詳しく説明します。ここで知っておく必要があるの は、Springは3レベルのキャッシュを使用して循環参照の問題を解決することです。

 実際、Beanは第1レベルのキャッシュに格納され、循環参照は第3レベルのキャッシュによって解決されます。実際、第1、第2、および第3レベルのキャッシュはマップです。

1.5.Springの拡張インターフェース

BeanFactoryPostProcessor(Beanファクトリのポストプロセッサ)とBeanDefinitionRegistryPostProcessorの2つの非常に重要な拡張インターフェイスがあります。

これらの2つのインターフェースは何をしますか? 

画像

この写真では、設計者が図面を設計し、それを工場に渡して製造することを望んでいることがわかりました。それでは、設計者が設計した図面は変更される可能性がありますか?

もちろん変更可能ですが、工場に引き渡されていない限り変更可能です。

BeanFactoryPostProcessor(Beanファクトリのポストプロセッサ)の役割は、BeanDefinitionを変更することです。

1. BeanFactoryPostProcessor:BeanDefinitionを変更します。

これはインターフェースであり、クラスはこのインターフェースを実装して、内部のメソッドを書き直すことができます

public class DefinedPost implements BeanFactoryPostProcessor {
    /**
     * 重写Bean工厂的后置处理器
     * @param beanFactory
     * @throws BeansException
     */
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // beanFactory 拿到工厂了, 就可以获取某一个Bean定义了
        GenericBeanDefinition car = (GenericBeanDefinition) beanFactory.getBeanDefinition("Car");
        // 拿到了car, 然后修改了Car的类名为com.example.tulingcourse.Tank. 那么后面在获取的Bean里面, 将其转换为Car, 就会报错了
        car.setBeanClassName("com.example.tulingcourse.Tank");
    }
}

ステップ1:BeanFactoryPostProcessorインターフェースを実装してから、内部のメソッドを書き直す必要があります

ステップ2:書き換えメソッドにより、beanFactory、beanfactoryが直接得られることがわかりました

ステップ3:Beanファクトリを取得します。Bean定義である名前に基づいてBeanDefinitionを取得できます。 

ステップ4:Bean定義のクラス名をTankに変更しました。 

このときどうなりますか?豆工場で製造された車は、取り出された後、Carオブジェクトに変換され、エラーが報告されます。

public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Application.class);
    
        Car car = context.getBean("car", Car.class); // 这里会报错, 因为已经被修改
        System.out.println(car.getName());
    }

実行プロセス:Springが開始すると、AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TulingCourseApplication.class);が実行されます。

次に、ApplicationContextは、BeanFactoryPostProcessorオブジェクトを実装するすべてのクラスをスキャンするために戻り、postProcessBeanFactoryメソッドを実行します。 

BeanFactoryPostProcessorは、mybatisの統合など、他のコンポーネントを統合するときに多くのシナリオで使用されます

2.BeanDefinitionRegistryPostProcessorがBeanDefinitionを登録しました

これは、Bean定義登録用のポストプロセッサーです。BeanDefinitionRegistryPostProcessorは実際にBeanFactoryPostProcessorインターフェースを実装しています。

画像

 デモを見てみましょう

public class DefinedPost implements BeanDefinitionRegistryPostProcessor {

    /**
     * 重写Bean工厂的后置处理器
     * @param beanFactory
     * @throws BeansException
     */
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // beanFactory 拿到工厂了, 就可以获取某一个Bean定义了
        GenericBeanDefinition car = (GenericBeanDefinition) beanFactory.getBeanDefinition("Car");
        // 拿到了car, 然后修改了Car的类名为com.example.tulingcourse.Tank. 那么后面在获取的Bean里面, 将其转换为Car, 就会报错了
        car.setBeanClassName("com.example.tulingcourse.Tank");
    }

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {

    }
}

クラスはBeanDefinitionRegistryPostProcessorを実装し、postProcessBeanDefinitionRegistryメソッドをオーバーライドする必要があります。これにより、BeanDefinitionRegistryが直接提供されます。 

次に、beanDefinitionRegistry.registerBeanDefinition();を使用して図面を追加します。

ここで、新しいBeanを登録したり、登録したBeanを削除したりできます。もう1つ登録すると、Beanファクトリがもう1つビルドします。 

総括する:

    BeanFactoryPostProcessorとBeanDefinitionRegistryPostProcessorの2つの拡張クラスは非常に重要なクラスであり、次のように外部への拡張に大きな役割を果たします。mybatisの統合

画像

BeanFactoryPostProcessorとBeanDefinitionRegistryPostProcessorは、ApplicationContextの2つの拡張インターフェースです。これは、ApplicationContextとBeanFactoryの違いの1つでもあります。これは、これら2つの拡張ノードを使用すると、外部と統合できるためです。たとえば、Mybatis統合です。例:スキャン構成クラス、つまり、 これらの2つの拡張ポイントによって実現されます。

この拡張ポイントの役割:

1. IoCに加えて、AOPやMyBatis統合などの他の拡張機能はすべて、これら2つの拡張ポイントを使用します。Springを多くの外部コンポーネントと整然と中断することなく統合できる理由は、これら2つの拡張ポイントの機能です。

1.6Bean拡張ポイント

ApplicationContextに拡張ポイントがあることに加えて、Spring IoCのBeanにも拡張ポイントがあります。BeanPostProcessor(Beanのポストプロセッサ)。getBean()の前に使用すると、Beanの構築を防ぐことができ、Beanの構築をカスタマイズすることもできます。 。

BeanPostProcessorは、多くのシナリオで使用されます。Beanがインスタンス化される前後に呼び出されます。また、プロパティが入力される前後に呼び出され、初期化の前後にも呼び出されます。一部のプロシージャは複数回呼び出されます。 。プロセス全体が合計9回呼び出されます。Beanはすべてのプロセスで拡張できます。

思考:SpringはどのようにしてAOPに参加しましたか?

統合されたAOPは間違いなくIoCと統合されません。AOPはBeanPostProcessor(Beanポストプロセッサー)を介して統合されます。

AOPを実装する方法は2つあります。1つはCGLIBで、もう1つはJDKです。 

統合する場合、どのステップで継承されますか?たとえば、ログを追加するには、AOPを使用して追加します。通常、初期化後にAOPを追加します。AOPはここに統合されます。

上に示したように:インタビューするとき、インタビュアーはBeanのライフサイクルについて尋ねます。インスタンス化->プロパティの入力->初期化について話すだけではありません。一連の認識がある場合は、初期化についても話す必要があります。 。

1.7.SpringIOCのロードプロセス

画像

上図と比較して、iocの読み込みプロセスを簡単に説明しましょう 

クラスをBeanにロードします。1つのステップではなく、プロセスを実行する必要があります。 

1.まず、クラスをBeanDefinition(Bean定義)としてロードする必要があります。

  Bean定義にロードするには、いくつかのステップがあります。

  1)BeanDefinitionReaderを使用して構成クラスをロードします。この時点で、プロジェクト内のすべてのxmlファイルまたはアノテーションをスキャンします。これらの一部はターゲットクラスであり、一部はそうではありません。

  2)BeanDefinitionScannerを使用して、ターゲットクラスをスキャンします。 

  3)BeanDefinitionRegistryを使用して、BeanDefinitionMapにBeanを登録します。

2.次に、ApplicationContextはBeanFactoryPostProcessorを呼び出してBean定義を変更できます。また、BeanDefinitionRegistryPostProcessorを呼び出してBean定義を登録することもできます。

3. BeanDefinitionは処理のためにBeanFactoryに渡され、BeanFactoryはgetBean()を呼び出してBeanを生成するか、Beanを呼び出します(getBean()には2つの関数があります)。 

4. Beanが生成されると、最初にインスタンス化され、次にプロパティが入力されます(主に、@ Autowire、@ Value、およびその他のアノテーションを読み取るため)。Beanが初期化されると、initMethod()メソッドと初期化の破棄が行われます。メソッドdestroy()が呼び出されます。初期化Awareの束もいつ呼び出され、Bean生成プロセス中に拡張するための多くの拡張ポイントがあります。

5.生成されたBeanをマップに配置します。マップは第1レベルのキャッシュプールです。後で、getBean( "user")を使用してキャッシュプールからBeanを取得できます。

おすすめ

転載: blog.51cto.com/15091061/2609370