記事ディレクトリ
- 春の面接の質問 (35)
- ベース
- IOC
-
- 5. IOC とは何ですか? DIとは何ですか?
- 6・Spring IOC の実装メカニズムについて簡単に説明していただけますか?
- 7. BeanFactory と ApplicantContext について話しますか?
- 8. Spring コンテナが起動フェーズで何をするか知っていますか?
- 9. Spring Bean のライフサイクルについて話してもらえますか?
- 10.Bean定義と依存関係定義の方法は何ですか?
- 11.依存性注入の方法は何ですか?
- 12.Spring にはどのような自動アセンブリ方法がありますか?
- 13.Spring の Bean の範囲は何ですか?
- 14. Spring のシングルトン Bean にはスレッド セーフティの問題がありますか?
- 15. 循環依存関係について話しますか?
- 16. それでは、Spring は循環依存関係をどのように解決するのでしょうか?
- 17. なぜ 3 レベルキャッシュなのか? レベル2は使えないの?
- 18. @Autowired の実装原理は何ですか?
- AOP
- 事務
- MVC
- スプリングブーツ
- スプリングクラウド
春の面接の質問 (35)
ベース
1.春とは何ですか? 特性?どのようなモジュールがあるのでしょうか?
一言で言えば、Spring は軽量で非侵入的な制御反転 (loC) およびアスペクト指向 (AOP) のフレームワークです。
2003 年に、ロッド ジョンソンというミュージシャンが軽量の Java 開発フレームワークを開発することを決定し、Spring は Java の戦場で竜騎兵として徐々に台頭し、EJB の伝統的な重騎兵を排除しました。
これまで、エンタープライズレベルの開発の標準構成は基本的にSpring5 + Spring Boot 2 + JDK 8でした。
Springにはどのような機能があるのでしょうか?
春には多くの利点があります。
IOC: 制御の反転 制御の反転
DI: 依存関係の注入 依存関係の注入
AOP: アスペクト指向プログラミング スライス指向プログラミング
- IOC および DI のサポート
Spring のコアは、すべてのオブジェクトの作成と依存関係を維持できる大規模なファクトリ コンテナであり、Spring ファクトリは Bean の生成と Bean のライフ サイクルの管理に使用され、高凝集性と低結合性の設計コンセプトを実現します。
- AOP プログラミングのサポート
Springではプログラムのパーミッションインターセプトや動作監視などのアスペクト機能を簡単に実装できるアスペクト指向プログラミングが提供されています。
- 宣言的トランザクションのサポート
ハードコーディングではなく構成によるトランザクション管理をサポートするため、これまで繰り返し行われていたトランザクションの送信とロールバックのための JDBC コードを記述する必要がなくなりました。
- クイックテストサポート
Spring は Junit のサポートを提供しており、アノテーションを通じて Spring プログラムを迅速にテストできます。
- 機能を迅速に統合
Springは様々な優れたオープンソースフレームワークを排除するものではなく、様々な優れたフレームワーク(Struts、Hibernate、MyBatis、Quartzなど)を直接サポートしています。
- 複雑な API テンプレートのカプセル化
Springでは、JavaEE 開発で使用するのが非常に難しい一部の API (JDBC、JavaMail、リモート呼び出しなど) に対してテンプレート化されたカプセル化が提供されており、これらのカプセル化された API の提供により、アプリケーションの難易度が大幅に軽減されます。
2.Spring にはどのようなモジュールがありますか?
Springフレームワークはモジュール化されており、コアにspring Core Container
必要なモジュール以外にも他のモジュールが含まれており可选
、20個程度のモジュールが存在します。
最も重要な 7 つのモジュール:
- Spring Core : Spring コアはフレームワークの最も基本的な部分であり、IOC および依存関係注入 DI 機能を提供します。
- Spring Context : Spring コンテキスト コンテナ。BeanFactory の機能が強化されたサブインターフェイスです。
- Spring Web : Web アプリケーション開発のサポートを提供します。
- Spring MVC : MVC のアイデアを Web アプリケーションに実装することを目的としています。
- Spring DAO : JDBC の抽象化レイヤーを提供し、JDBC コーディングを簡素化すると同時に、コーディングをより堅牢にします。
- Spring ORM : Spring + Hibernate、Spring + iBatis、Spring + JDO 統合などの一般的な ORM フレームワークの統合をサポートします。
- Spring AOP : アスペクト指向プログラミング。AOP Alliance と互換性のあるプログラミング実装を提供します。
3.Spring でよく使用されるアノテーションは何ですか?
Spring には多くのモジュールがあり、汎用化された SpringBoot と SpringCloud さえも Spring の一部とみなされます。モジュールをモジュールに分割し、機能別に一般的に使用されるアノテーションをいくつか見てみましょう。
ウェブ:
- @Controller: 結合アノテーション (@Component アノテーションと結合)、MVC 層 (コントロール層) に適用されます。
- @RestController: このアノテーションは結合されたアノテーションであり、@Controller と @ResponseBody の組み合わせと同等です。このアノテーションはクラスにあります。つまり、コントローラーのすべてのメソッドがデフォルトで @ResponseBody で追加されます。
- @RequestMapping: アクセス パスやパラメータを含む Web リクエストをマッピングするために使用されます。Restful スタイルのインターフェースの場合は、リクエストのタイプに応じて異なるアノテーションを使用することもできます。
- @GetMapping
- @PostMapping
- @PutMapping
- @DeleteMapping
- @ResponseBody: ページではなく応答内に戻り値を配置することをサポートし、通常、ユーザーは json データを返します。
- @RequestBody: リクエスト パラメーターをアドレスの後ろに直接接続するのではなく、リクエスト本文に含めることを許可します。
- @PathVariable: @RequestMapping ("/hello/{name)") で宣言されたパスなどのパス パラメーターを受け取るために使用されます。パラメーターの前にアノテーションを置くことで値を取得できます。通常、Restful として使用されます。インターフェース実装メソッド。
容器:
-
@Component: アノテーション付きクラスが「コンポーネント」であり、Spring 管理 Bean になることを示します。これらのクラスは、アノテーション ベースの構成およびクラスパス スキャンを使用する場合の自動検出の候補とみなされます。同時に、@Component はメタアノテーションでもあります。
-
@Service: 結合アノテーション (@Component アノテーションと結合)、サービス層 (ビジネス ロジック層) に適用されます。
-
@Repository: 結合アノテーション (@Component アノテーションと結合)、dao 層 (データ アクセス層) に適用されます。
-
@Autowired: Spring によって提供されるツール (Spring の依存関係注入ツール (BeanPostProcessor、BeanFactoryPostProcessor) によって自動的に注入されます)。
-
@Qualifier: このアノテーションは通常 @Autowired と一緒に使用されます。注入プロセスをより詳細に制御したい場合、@Qualifier は構成に役立ちます。たとえば、同じタイプの Bean が 2 つ以上ある場合、Spring は選択できませんなので、この注釈を使用します。
-
@Configuration: 現在のクラスが構成クラスであることを宣言します (Spring 構成 XML ファイルに相当)
-
@Value: フィールド、コンストラクター パラメーター、およびメソッド パラメーターでデフォルト値を指定するために使用できます。#{} と ${} の 2 つのメソッドをサポートします。通常、SpringbBootのapplication.propertiesで設定したプロパティ値は変数に代入されます。
-
@Bean: メソッドにアノテーションが付けられ、現在のメソッドの戻り値が Bean であることを宣言します。init() メソッドと destroy() メソッドは、返された Bean に対応するクラス内で定義してから、
定義
@Bean (initMethod="init", destroyMethod="destroy")
、init は構築後に実行され、destroy は破棄前に実行されます。 -
@Scope: Bean の作成に使用するモードを定義します (メソッド、@Bean が必要です) その設定タイプには、Singleton、Prototype、Request、Session、GlobalSession が含まれます。
AOP:
- @Aspect: @After、@Before、@Around を使用してアスペクト (クラス上) を宣言し、アドバイス (アドバイス) を定義します。インターセプト ルール (ポイントカット) をパラメーターとして直接使用できます。
- @After: メソッドの実行後 (メソッド上で) 実行されます。
- @Before: メソッドが実行される前に (メソッド上で) 実行されます。
- @Around: メソッド実行の前後(メソッド上)で実行します。
- @Pointcut: ポイントカットを宣言するには、Java 構成クラスで @EnableAspectJAutoProxy アノテーションを使用して、Spring の Aspectj プロキシ (クラス上) のサポートを有効にします。
取引:
- @Transactional: トランザクションを開始するメソッドに @Transactional アノテーションを使用して、トランザクションを宣言的に開始します。
4.春にはどんなデザインパターンが使われていますか?
Spring フレームワークではさまざまなタイプのデザイン パターンが広く使用されています。
- ファクトリ パターン: Spring コンテナは本質的に大規模なファクトリであり、ファクトリ パターンを使用して、BeanFactory および ApplicationContext を通じて Bean オブジェクトを作成します。
- プロキシ モード: Spring AOP 機能は、動的プロキシと静的プロキシに分けられるプロキシ モードを通じて実装されます。
- シングルトン モード: Spring の Bean はデフォルトでシングルトンであるため、コンテナによる Bean の管理に役立ちます。
- テンプレート モード: JdbcTemplate、RestTemplate、およびデータベースやネットワークなどで動作する Spring の Template で終わるその他のテンプレート クラスは、テンプレート モードを使用します。
- オブザーバー パターン: Spring イベント駆動モデルは、オブザーバー パターンの古典的なアプリケーションです。
- アダプター モード: Spring AOP の拡張または通知 (アドバイス) はアダプター モードを使用し、Spring MVC もコントローラーを適応させるためにアダプター モードを使用します。
- 戦略パターン: Spring には Resource インターフェースがあり、そのさまざまな実装クラスはさまざまな戦略に従ってリソースにアクセスします。
IOC
5. IOC とは何ですか? DIとは何ですか?
Java はオブジェクト指向プログラミング言語です。インスタンス オブジェクトが相互に連携してビジネス ロジックを形成します。私たちは皆、コード内でオブジェクトとオブジェクトの依存関係を作成していることがわかります。
いわゆるIOC (Inversion of Control) :コンテナは、オブジェクトのライフ サイクルとオブジェクト間の関係を制御する責任があります。以前は、必要なものは何でも作成できましたが、今では、必要なものがすべてコンテナから送信されます。
言い換えれば、オブジェクトのライフサイクルを制御するのは、それを参照するオブジェクトではなく、コンテナーになります。以前は特定のオブジェクトに対して他のオブジェクトを制御していましたが、現在はすべてのオブジェクトがコンテナによって制御されるため、これを制御の反転と呼びます。
DI (Dependency Injection) :オブジェクトをインスタンス化するときに依存するクラスを注入するコンテナを指します。IOCとDIは同じものであるという人もいますし、IOCはアイデアでDIはIOCの実装であるという人もいます。
IOC を使用する理由
最も重要なことは2 つの単語を分離することです。ハードコーディングするとオブジェクト間の過剰な結合が発生します。IOC を使用すると、オブジェクト間の依存関係を気にする必要がなくなり、アプリケーションの開発に集中できます。
6・Spring IOC の実装メカニズムについて簡単に説明していただけますか?
PS: または、「単純な Spring を自分で実装したことがありますか?」という質問もあります。
Spring の IOC は本質的に大きな工場です。工場がどのように稼働するかを考えてみましょう。
-
製品の生産:工場の中心的な機能は製品を生産することです。Spring では、Bean 自体をインスタンス化する代わりに、Bean が Spring に渡されます。——答えは間違いなく反省です。
では、この工場の生産管理はどのように行われているのでしょうか?-factory パターンについても知っておく必要があります。
-
在庫品:通常、工場には製品を保管する倉庫があり、生産された製品はすぐに持ち出すことができません。Spring はコンテナであり、そのコンテナの中にオブジェクトが格納されているのは周知の事実ですが、オブジェクトを取得するたびにその場でリフレクションしてオブジェクトを作成することはできず、作成したオブジェクトを格納する必要があります。
-
注文処理:そして最も重要な点は、工場がどのような基準に基づいて製品を供給するかということです。注文。これらの注文にはさまざまな種類があり、オンラインで署名されるもの、工場で署名されるもの、工場の営業担当者が玄関先で署名するものなどがあります。最終的に、注文は処理されて出荷のために工場に送られます。
Spring にもそのような順序があり、これは Bean の定義と依存関係であり、私たちが最もよく知っている XML 形式またはアノテーション形式にすることができます。
Spring IOC のミニバージョンを実装するだけです。
Bean定義:
Bean は構成ファイルを通じて定義され、型に解析されます。
- Beans.properties は遅延です。ここでは、解析するのに最も便利なプロパティを直接使用します。ここでは、<key, value> 型構成を直接使用して、Bean の定義を表します (キーは beanName、値はクラスです)。
userDao:cn.fighter3.bean.UserDao
-
Bean定義.java
Bean 定義クラス、構成ファイル内の Bean 定義に対応するエンティティ
public class BeanDefinition {
private String beanName;
private Class beanClass;
//省略略getter、setter
}
-
リソースローダー.java
リソースローダー。構成ファイル内の構成のロードを完了するために使用されます。
public class ResourceLoader {
public static Map<String, BeanDefinition> getResource() {
Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>(16);
Properties properties = new Properties();
try {
InputStream inputStream =
ResourceLoader.class.getResourceAsStream("/beans.properties");
properties.load(inputStream);
Iterator<String> it = properties.stringPropertyNames().iterator();
while (it.hasNext()) {
String key = it.next();
String className = properties.getProperty(key);
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setBeanName(key);
Class clazz = Class.forName(className);
beanDefinition.setBeanClass(clazz);
beanDefinitionMap.put(key, beanDefinition);
}
inputStream.close();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
return beanDefinitionMap;
}
}
-
BeanRegister.java
オブジェクト レジスタは、シングルトン Bean のキャッシュに使用されます。大幅に簡素化されています。デフォルトでは、すべての Bean がシングルトンです。いわゆるシングルトン登録も非常に単純で、オブジェクトを HashMap に保存するだけであることがわかります。
public class BeanRegister {
//单例Bean缓存
private Map<String, Object> singletonMap = new HashMap<>(32);
/**
* 获取单例Bean
*
* @param beanName bean名称
* @return
*/
public Object getSingletonBean(String beanName) {
return singletonMap.get(beanName);
}
/**
* 注册单例bean
*
* @param beanName
* @param bean
*/
public void registerSingletonBean(String beanName, Object bean) {
if (singletonMap.containsKey(beanName)) {
return;
}
singletonMap.put(beanName, bean);
}
}
- BeanFactory.java
コア クラスの 1 つであるオブジェクト ファクトリは、初期化時に Bean レジスタを作成し、リソースのロードを完了します。
Beanを取得する場合は、まずシングルトンキャッシュからBeanを取得し、取得できない場合はBeanを作成して登録します。
public class BeanFactory {
private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();
private BeanRegister beanRegister;
public BeanFactory() {
//创建bean注册器
beanRegister = new BeanRegister();
//加载资源
this.beanDefinitionMap = new ResourceLoader().getResource();
}
/**
* 获取bean
*
* @param beanName bean名称
* @return
*/
public Object getBean(String beanName) {
//从bean缓存中取
Object bean = beanRegister.getSingletonBean(beanName);
if (bean != null) {
return bean;
}
//根据bean定义,创建bean
return createBean(beanDefinitionMap.get(beanName));
}
/**
* 创建Bean
* @param beanDefinition bean定义
* @return
*/
private Object createBean(BeanDefinition beanDefinition) {
try {
Object bean = beanDefinition.getBeanClass().newInstance();
//缓存bean
beanRegister.registerSingletonBean(beanDefinition.getBeanName(), bean);
return bean;
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
}
- テスト
- UserDao.java 非常に単純な Bean クラス
- 単体テスト
- 演算結果
public class UserDao {
public void queryUserInfo(){
System.out.println("A good man.");
}
}
public class ApiTest {
@Test
public void test_BeanFactory() {
//1.创建bean工厂(同时完成了加载资源、创建注册单例bean注册器的操作)
BeanFactory beanFactory = new BeanFactory();
//2.第一次获取bean(通过反射创建bean,缓存bean)
UserDao userDao1 = (UserDao) beanFactory.getBean("userDao");
userDao1.queryUserInfo();
//3.第二次获取bean(从缓存中获取bean)
UserDao userDao2 = (UserDao) beanFactory.getBean("userDao");
userDao2.queryUserInfo();
}
}
A good man.
A good man.
この時点で、Spring の乞食 + 壊れたバージョンの Spring が完成し、コードも比較的完成しているので、可能であれば実行できます。
7. BeanFactory と ApplicantContext について話しますか?
BeanFactory は Spring の「心臓」であり、ApplicantContext は完全な「本体」であると言えます。
- BeanFactory (Bean ファクトリ) は Spring フレームワークのインフラストラクチャであり、Spring 自体に直面します。
- ApplicantContext (アプリケーション コンテキスト) は BeanFactoty に基づいて構築されており、Spring フレームワークを使用する開発者を対象としています。
BeanFactory インターフェース
BeanFactory は、さまざまなクラスのオブジェクトを作成および管理できるクラスの汎用ファクトリーです。
Spring では BeanFactory の実装が多数提供されています。最も一般的に使用されているのは XmlBeanFactory ですが、Spring 3.2 では廃止されました。XmlBeanDefinitionReader と DefaultListableBeanFactory を使用することをお勧めします。
BeanFactory インタフェースはクラス構造ツリーの最上位にあり、そのメイン メソッドは getBean(String var1) であり、このメソッドはコンテナから特定の名前を持つ Bean を返します。
BeanFactory の機能は他のインターフェースを通じて継続的に拡張されており、例えば、AbstractAutowireCapableBeanFactory では、コンテナ内の Bean を特定のルール (名前による一致、タイプによる一致など) に従って自動的に組み立てるメソッドが定義されています。
XMLBeanFactory (期限切れ) から Bean を取得する例を次に示します。
public class HelloWorldApp{
public static void main(String[] args) {
BeanFactory factory = new XmlBeanFactory (new ClassPathResource("beans.xml"));
HelloWorld obj = (HelloWorld) factory.getBean("helloWorld");
obj.getMessage();
}
}
ApplicationContext インターフェース
ApplicationContext は BeanFactory から派生し、より実用的なアプリケーション指向の機能を提供します。BeanFactory の使用は手動ファイルであり、ApplicationContext の使用は自動ファイルであると言えます。
ApplicationContext は HierachicalBeanFactory インターフェイスと ListableBeanFactory インターフェイスを継承し、これに基づいて、次のような他のインターフェイスを通じて BeanFactory の機能も拡張します。
- Beanのインスタンス化/ワイヤリング
- Beanのインスタンス化/連結
- BeanPostProcessor の自動登録
- BeanFactoryPostProcessor の自動登録
- 便利な MessageSource アクセス (i18n)
- ApplicationEvent のパブリッシュは、BeanFactory の遅延ロードとは異なり、プリロードされるため、ApplicationContext の開始後に各 Bean がインスタンス化されます。
これは ApplicationContext の使用例です。
public class HelloWorldApp{
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
HelloWorld obj = (HelloWorld) context.getBean("helloWorld");
obj.getMessage();
}
}
ApplicationContext には BeanFactory のすべての機能が含まれており、通常は前者を使用することをお勧めします。
8. Spring コンテナが起動フェーズで何をするか知っていますか?
Spring の IOC コンテナの作業プロセスは、実際には、コンテナの起動ステージとBean のインスタンス化ステージの2 つのステージに分けることができます。
コンテナの起動フェーズで行われる主な作業は、構成ファイルをロードして解析し、対応する Bean 定義に保存することです。
コンテナが開始されると、最初に何らかの手段で構成メタデータがロードされます。ほとんどの場合、コンテナは特定のツール クラス (BeanDefinitionReader) に依存して、ロードされた構成メタデータを解析および分析し、分析された情報を対応する BeanDefinition にグループ化する必要があります。
最後に、Bean定義に必要な情報を保存したBeanDefinitionを対応するBeanDefinitionRegistryに登録することでコンテナの起動が完了します。
9. Spring Bean のライフサイクルについて話してもらえますか?
Spring では、基本コンテナ BeanFactory と拡張コンテナ ApplicationContext のインスタンス化のタイミングが異なります。BeanFactory は遅延初期化を使用します。つまり、Bean は、ApplicationContext の開始後、最初の getBean() が実行されたときにのみインスタンス化されます。すべての Bean 定義インスタンス化されます。
Spring IOC における Bean のライフサイクルは、インスタンス化、属性の割り当て (Populate)、初期化、破棄 (Destruction) の4 つの段階に大別されます。
もう少し詳しいプロセスを見てみましょう。
- インスタンス化: ステップ 1、Bean オブジェクトをインスタンス化する
- 属性の割り当て: ステップ 2、Bean の関連属性と依存関係を設定します。
- 初期化: 初期化フェーズには多くのステップがあります。ステップ 5 と 6 が実際の初期化です。ステップ 3 と 4 は初期化前に実行されます。ステップ 7 は初期化後に実行されます。初期化が完了すると、Bean が使用できるようになります。
- 破棄: ステップ 8 ~ 10。ステップ 8 は実際には破棄段階としてカウントできますが、これは本当の意味での破棄ではありません。代わりに、破棄に関連する呼び出しインターフェイスが使用前に登録されるため、その後のステップ 9 と 10 が可能になります。実際にBeanを破棄し、対応するメソッドを実行します。
簡単に要約すると、Bean ライフサイクルの初期化プロセスには、前処理や後処理など、さらに多くのステップがあります。
最後に、例を通して具体的な詳細を見てみましょう。
PersonBean クラス インスタンスの詳細なコード実装については、次の記事を参照してください: Bean コンテナのライフ サイクルは人間の生活に似ています。
10.Bean定義と依存関係定義の方法は何ですか?
直接エンコード方式、設定ファイル方式、アノテーション方式の3つの方式があります。
- 直接エンコード方式: 通常、直接エンコード方式にはアクセスできませんが、実際には、他の方式も最終的には直接エンコードを通じて実装されます。
- 構成ファイル方式: XML およびプロパティ タイプの構成ファイルを通じて対応する依存関係を構成すると、Spring が構成ファイルを読み取って依存関係の注入を完了します。
- アノテーション メソッド: アノテーション メソッドは最も一般的に使用されるメソッドである必要があります。対応する場所でアノテーションの変更を使用すると、Spring がアノテーションをスキャンして依存関係の注入を完了します。
11.依存性注入の方法は何ですか?
Spring では、コンストラクター メソッド インジェクション、プロパティ インジェクション、およびファクトリ メソッド インジェクションがサポートされており、ファクトリ メソッド インジェクションは、静的ファクトリ メソッド インジェクションと非静的ファクトリ メソッド インジェクションに分けられます。
- コンストラクター インジェクション:
クラスのコンストラクターを呼び出すことにより、インターフェイス実装クラスがコンストラクター変数を通じて渡されます。
public CatDaoImpl(String message){
this. message = message;
}
<bean id="CatDaoImpl" class="com.CatDaoImpl">
<constructor-arg value=" message "></constructor-arg>
</bean>
- プロパティ注入は、
Setter メソッドを介して呼び出しクラスに必要な依存関係の注入を完了します。
public class Id {
private int id;
public int getId() {
return id; }
public void setId(int id) {
this.id = id; }
}
<bean id="id" class="com.id ">
<property name="id" value="123"></property>
</bean>
-
ファクトリーメソッドインジェクション
-
静的ツール射出
名前が示すように、静的ファクトリは、静的ファクトリ メソッドを呼び出して必要なオブジェクトを取得することです。すべてのオブジェクトを Spring に管理させるために、「 」を通じてオブジェクトを直接取得することはできませんが、Spring インジェクションを通じて取得します
工程类.静态方法()
。public class DaoFactory { //静态工厂 public static final FactoryDao getStaticFactoryDaoImpl(){ return new StaticFacotryDaoImpl(); } } public class SpringAction { //注入对象 private FactoryDao staticFactoryDao; //注入对象的 set 方法 public void setStaticFactoryDao(FactoryDao staticFactoryDao) { this.staticFactoryDao = staticFactoryDao; } }
<!--factory-method="getStaticFactoryDaoImpl"指定调用哪个工厂方法> <bean name="springAction" class=" SpringAction" > <!--使⽤用静态工厂的方法注入对象,对应下面的配置⽂文件--> <property name="staticFactoryDao" ref="staticFactoryDao"></property> </bean> <!--此处获取对象的方式是从工厂类中获取静态方法--> <bean name="staticFactoryDao" class="DaoFactory" factory-method="getStaticFactoryDaoImpl"></bean>
-
非静的ファクトリーインジェクション
非静的ファクトリ (インスタンス ファクトリとも呼ばれます) は、ファクトリ メソッドが静的ではないことを意味するため、最初に新しいファクトリ インスタンスを作成してから、通常のインスタンス メソッドを呼び出す必要があります。
//非静态工厂 public class DaoFactory { public FactoryDao getFactoryDaoImpl(){ return new FactoryDaoImpl(); } } public class SpringAction { //注入对象 private FactoryDao factoryDao; public void setFactoryDao(FactoryDao factoryDao) { this.factoryDao = factoryDao; } }
<bean name="springAction" class="SpringAction"> <!--使用非静态工厂的方法注入对象,对应下面的配置文件--> <property name="factoryDao" ref="factoryDao"></property> </bean> <!--此处获取对象的方式是从工厂类中获取实例方法--> <bean name="daoFactory" class="com.DaoFactory"></bean> <bean name="factoryDao" factory-bean="daoFactory" factory-method="getFactoryDaoImpl"></bean>
-
12.Spring にはどのような自動アセンブリ方法がありますか?
オートワイヤリングとは何ですか?
Spring IOCコンテナはすべてのBeanの構成情報を知っているほか、コンストラクタメソッドの構造や属性など実装クラスの構造情報もJavaのリフレクション機構を通じて取得できます。すべての Bean に関するこの情報を習得すると、Spring IOC コンテナは依存関係を明示的に構成しなくても、特定のルールに従ってコンテナ内の Bean を自動的に組み立てることができます。
Spring が提供するこのメソッドでは、特定のルールに従って Bean を自動的にアセンブルできます。<bean> 要素は、自動アセンブリ タイプを指定する属性を提供します: autowire="<autowiring type>"
Spring はどのようなタイプの自動配線を提供しますか?
Spring は 4 種類の自動配線を提供します。
- byName: 名前に基づく自動マッチング。Boss に car という名前の別の属性があると仮定します。コンテナ内に car という名前の Bean が存在する場合、Spring は自動的にそれを Boss の car 属性にアセンブルします。
- byType: タイプに基づく自動マッチング。Boss に Car タイプ属性があるとします。コンテナ内に Car タイプ Bean が存在する場合、Spring は自動的にそれを Boss 属性にアセンブルします。
- constructor: byType に似ていますが、コンストラクター注入用である点が異なります。Boss にコンストラクターがある場合、そのコンストラクターには Car タイプの入力パラメーターが含まれます。コンテナー内に Car タイプの Bean がある場合、Spring はこの Bean を Boss コンストラクターの入力パラメーターとして自動的に使用します。コンテナー内にコンストラクターが見つからない場合、入力パラメータが Bean タイプと一致する場合、Spring は例外をスローします。
- autodetect: Bean のイントロスペクション メカニズムに基づいて自動アセンブリに byType を使用するかコンストラクターを使用するかを決定します。Bean がデフォルトのコンストラクターを提供する場合は byType が使用され、それ以外の場合はコンストラクターが使用されます。
13.Spring の Bean の範囲は何ですか?
Spring の Bean は主に 5 つのスコープをサポートします。
- singleton : Spring コンテナ内に Bean インスタンスは 1 つだけあり、Bean は単一インスタンスとして存在し、Bean のデフォルトのスコープになります。
- プロトタイプ: コンテナから Bean が呼び出されるたびに、新しいインスタンスが返されます。
次の 3 つのスコープは、Web アプリケーションにのみ適用されます。
- request : 各 HTTP リクエストは、現在の HTTP リクエスト内でのみ有効な新しい Bean を生成します。
- session : 同じ HTTP セッションは Bean を共有し、異なる HTTP セッションは異なる Bean を使用します。
- globalSession : 同じグローバル セッションは Bean を共有し、Protlet ベースの Web アプリケーションにのみ使用されますが、Spring 5 では存在しません。
14. Spring のシングルトン Bean にはスレッド セーフティの問題がありますか?
まず結論として、Spring のシングルトン Bean はスレッドセーフではありません。
シングルトン Bean にはグローバル Bean が 1 つしかないため、すべてのスレッドで共有されます。
シングルトン Bean がステートレスである場合、つまり、スレッド内の操作がBean 内のメンバークエリ以外の操作を実行しない場合、このシングルトン Bean はスレッドセーフです。たとえば、Spring MVC のコントローラー、サービス、Dao など、これらの Bean のほとんどはステートレスであり、メソッド自体にのみ焦点を当てています。
Bean がステートフルである場合、つまり Bean 内のメンバー変数が書き込まれる場合、スレッド セーフティの問題が発生する可能性があります。
シングルトンBeanのスレッドセーフ問題を解決するにはどうすればよいですか?
一般的なソリューションには次のようなものがあります。
- Bean を複数のインスタンスとして定義する
と、スレッド リクエストごとに新しい Bean が作成されますが、コンテナによる Bean の管理が困難になるため、この方法は実行できません。 - Bean オブジェクト内で変更可能なメンバー変数を定義することは避けてください。
これは、たとえサイズに合わせて切り取ってもできません。 - ThreadLocalの Bean のメンバー変数を
保存します。ThredLoca がマルチスレッド下で変数の分離を保証できることがわかっています。クラスで ThreadLocal メンバー変数を定義し、必要な変数のメンバー変数を ThreadLocal に保存できます。これは推奨される方法です。 . ウェイ。
15. 循環依存関係について話しますか?
循環依存関係とは何ですか?
Spring の循環依存関係: 簡単に言えば、それ自体に依存するか、他の Bean に依存します。
循環依存関係を持つのはシングルトン Bean のみであり、プロトタイプ (Prototype) の場合は Spring が直接例外をスローします。理由は非常に単純で、AB は循環依存しています。A がインスタンス化されると、B に依存していることがわかり、B のインスタンスが作成されます。B が作成されると、A が必要であることがわかり、B のインスタンスが作成されます。 A1 が作成されます...無限のマトリョーシカ人形がシステムを直接ダウンさせます。
Spring はどのような状況で循環依存関係を解決できますか?
Spring はコンストラクター インジェクションに基づく循環依存関係をサポートしていませんが、AB 循環依存関係 (1 つはコンストラクター インジェクション、もう 1 つはセッター インジェクション) の場合はどうなるでしょうか?
いくつかのシナリオを見てみましょう。
4 番目は可能ですが 5 番目は不可能である理由は、Spring が Bean を作成するときにデフォルトで自然な順序に従って Bean を作成するため、A が B の前に作成されるためです。
簡単にまとめると、循環依存関係のインスタンスを setter メソッドで注入した場合は Spring でサポートできるが、コンストラクタで注入した場合はサポートされず、コンストラクタ注入と setter インジェクションが同時に存在する場合は依存する空の上で。
16. それでは、Spring は循環依存関係をどのように解決するのでしょうか?
PS: 実際、正しい答えは開発者が適切に設計し、Bean に循環依存関係を持たせないことですが、方法はありません。面接官はこれを聞きたくないのです。
シングルトン Bean の初期化を完了するには 3 つの手順があることは誰もが知っています。
インジェクションは 2 番目のステップである属性割り当てで発生し、このプロセスと組み合わせることで、Spring は第 3 レベルのキャッシュを通じて循環依存関係を解決します。
- レベル 1 キャッシュ: Map<String, Object> singletonObjects、シングルトン プール。インスタンス化され、プロパティが割り当てられ (注入され)、初期化された Bean インスタンスを保存するために使用されます。
- 第 2 レベルのキャッシュ: Map<String, Object> EarlySingletonObjects、初期公開オブジェクト、インスタンス化された Bean インスタンスの保存に使用されます。
- レベル 3 キャッシュ: Map<String, ObjectFactory<?>> singletonFactories、初期に公開されたオブジェクト ファクトリ。後の拡張でプロキシ オブジェクトを作成できるように、Bean 作成ファクトリを保存するために使用されます。
3 次キャッシュ内の循環依存関係を解決するプロセスを見てみましょう。
2 つのクラス A と B に循環依存関係がある場合:
A インスタンスの初期化プロセス:
- A のインスタンスを作成します。インスタンス化するときに、A オブジェクト ファクトリを 3 次キャッシュに置きます。これは、A のインスタンス化が開始されたことを意味します。私のオブジェクトはまだ完成していませんが、皆さんに知らせるために最初に公開します。
- Aが属性を注入すると、Bに依存していることがわかります。この時点ではまだBが作成されていないため、Bをインスタンス化します。
- 同様に、B が属性を注入し、それが A に依存していることが判明すると、キャッシュから A オブジェクトを見つけます。A を 1 次キャッシュから 3 次キャッシュに順番にクエリし、オブジェクト ファクトリを通じて 3 次キャッシュから A を取得し、A が完全ではないものの存在することを確認し、A を 2 次キャッシュに置きます。を実行し、同時に 3 次キャッシュの A を削除し、B がインスタンス化されて初期化されたら、B を 1 次キャッシュに置きます。
- その後、A は属性の割り当てを続行し、インスタンス化および初期化された B オブジェクトを 1 次キャッシュから正常に取得します。A オブジェクトの作成も完了し、2 次キャッシュ内の A を削除し、1 次キャッシュに A を置きます。 。
- 最後に、第 1 レベルのキャッシュには、インスタンス化および初期化された A オブジェクトと B オブジェクトが格納されます。
Spring がセッターインジェクションの循環依存関係を解決できる理由は、インスタンス化とプロパティの割り当てが分離されているため、操作の余地があることがわかります。全てコンストラクタで注入する場合、インスタンス化ステップで注入を完了する必要があるため、当然サポートできません。
17. なぜ 3 レベルキャッシュなのか? レベル2は使えないの?
いいえ、主にプロキシ オブジェクトを生成するためです。プロキシがない場合は、2 次キャッシュを使用して循環依存関係を解決しても問題ありません。ただし、エージェントがいる場合、レベル 3 では問題ありませんが、レベル 2 では問題ありません。
3 次キャッシュには特定のオブジェクトを生成する匿名の内部クラスが含まれているため、オブジェクトを取得するときにプロキシ オブジェクトを生成したり、通常のオブジェクトを返したりすることができます。3 次キャッシュを使用する主な目的は、常に同じオブジェクトが使用されるようにすることです。
2次キャッシュしかないとすると、2次キャッシュには通常のBeanオブジェクトが配置され、Beanの初期化処理でBeanPostProcessorを介してプロキシオブジェクトが生成された後、通常のBeanオブジェクトが2次キャッシュに配置されます。上書きされるため、取得したBeanオブジェクトが不整合となる可能性があります。
18. @Autowired の実装原理は何ですか?
@Autowired を実装するための鍵は次のとおりです: AutowiredAnnotationBeanPostProcessor
Bean の初期化フェーズ中に、Bean のプリプロセッサおよびポストプロセッサを通じていくつかの前後処理が実行されます。
@Autowired の機能はポストプロセッサーを通じて実装されます。このポストプロセッサは AutowiredAnnotationBeanPostProcessor です。
-
Bean の作成プロセスで、Spring は最終的に
doCreateBean()
メソッドを呼び出します。doCreateBean()
このメソッドはメソッド内で呼び出されpopulateBean()
、Bean のプロパティを入力して自動アセンブリやその他の作業を完了します。 -
populateBean()
メソッドでは合計 2 つのポストプロセッサが呼び出されます。1 回目は、属性の入力が必要かどうかを判断します。属性の入力が必要ない場合は、直接戻ります。属性の入力が必要な場合、メソッドは続行します。実行後、後ほど 2 番目のポストプロセッサが呼び出されますが、このときAutowiredAnnotationBeanPostProcessorのメソッドが呼び出されpostProcessPropertyValues()
、 @Autowired アノテーションが解析され、自動アセンブリが実装されます。 -
postProcessorPropertyValues()
メソッドでは、findAutowiringMetadata()
Bean 内の @Autowired アノテーション、@Inject、および @Value アノテーションを持つプロパティとメソッドを解析するためにメソッドが最初に呼び出されます。次に、metadata.inject()
メソッドを呼び出して属性を入力します。
AOP
19.AOPとは何ですか?
AOP: アスペクト指向プログラミング。簡単に言うと、一部のビジネスロジック内の同じコードを独立したモジュールに抽出して、ビジネスロジックを明確にすることです。
具体的には、CRUD を使用して一連のビジネスを作成したい場合、ビジネス コードの前後でログを出力し、パラメーターを検証するにはどうすればよいでしょうか?
日志记录
再利用可能な機能モジュールを分離し数据校验
、プログラム実行中の適切な場所にこれらのコードを動的に挿入して実行できます。これにより、コードの記述が簡素化されます。
ビジネス ロジック コードには一般的なロジックに関与するコードはなく、ビジネス モジュールはより簡潔であり、コア ビジネス コードのみが含まれています。ビジネス ロジックと一般ロジックの間のコード分離が実現され、メンテナンスとアップグレードが容易になり、ビジネス ロジックと一般ロジックの間の結合が軽減されます。
AOP は、アプリケーション全体に広がる機能を再利用可能なコンポーネントに分離できます。ソース コードを変更せずに、コンパイル、ロード、または実行時にプログラムに機能を動的に追加します。これにより、ビジネス ロジックの分離が実現され、コードのモジュール性が向上します。
AOP のコアは実際にはダイナミック プロキシです。インターフェイスが実装されている場合は、JDK ダイナミック プロキシが使用されます。それ以外の場合は、CGLIB プロキシが使用されます。これは主に、横断的なプロパティを持ついくつかのシステム レベルのサービスを処理するために使用されます。ログ収集、トランザクション管理、セキュリティ検査、キャッシュ、オブジェクトプール管理など。
AOP の中心となる概念は何ですか?
-
アスペクト: クラスはオブジェクトの特性を抽象化したもので、アスペクトは横断的な関心事を抽象化したものです。
-
Joinpoint : インターセプトされたポイント Spring はメソッド タイプのジョイン ポイントのみをサポートするため、Spring のジョイン ポイントはインターセプトされたメソッドを参照します。実際、ジョイン ポイントはフィールドまたはコンストラクターにすることもできます。
-
ポイントカット: 接続点をインターセプトする位置決め
-
アドバイス: いわゆる通知は、接続ポイントをインターセプトした後に実行されるコードを指し、拡張とも呼ばれます。
-
Target : エージェントのターゲットオブジェクト
-
ウィービング: ウィービングは、ターゲット クラスの特定の接続ポイントに拡張機能を追加するプロセスです。
-
コンパイル時のウィービング: ターゲット クラスのコンパイル時にアスペクトが組み込まれます。
-
クラスのロード時間ウィービング: アスペクトは、ターゲット クラスが JVM にロードされるときにウィービングされます。ターゲット クラスをアプリケーションに導入する前にそのバイトコードを拡張するには、特別なクラス ローダーが必要です。
-
ランタイムウィービング: アプリケーションの実行中のある時点でアスペクトが織り込まれます。通常、アスペクトを織り込むとき、AOP コンテナはターゲット オブジェクトのプロキシ オブジェクトを動的に作成します。SpringAOP はこのようにしてさまざまな側面を織り込んでいます。
Spring はランタイム ウィービングを使用しますが、AspectJ はコンパイル時ウィービングとクラス ローダー ウィービングを使用します。
-
-
イントロダクション: イントロダクションは、いくつかのプロパティとメソッドをクラスに動的に追加できる特別な機能拡張です。
AOP にはどのようなラップアラウンド オプションがありますか?
AOP には通常、次の 5 つのラッピング メソッドがあります。
- 事前通知 (@Before)
- 返品通知 (@AfterReturning)
- 例外通知 (@AfterThrowing)
- 投稿通知 (@After)
- アラウンド通知 (@Around)
複数のアスペクトの場合は @Order で順序を指定でき、数値が小さいほど優先順位が高くなります。
20. 普段 AOP を使用しますか?
SpringBoot プロジェクトでは、AOP を使用してインターフェイスの入力および出力パラメーターのログと実行時間を出力するのが比較的高速です。
-
依存関係を導入する: AOP 依存関係を導入する
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
-
カスタム注釈: 注釈をカットポイントとしてカスタマイズします。
@Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.METHOD}) @Documented public @interface WebLog { }
-
AOP アスペクトを構成します。
-
@Aspect: アスペクトを識別します
-
@Pointcut: カット ポイントを設定します。ここでは、カスタム アノテーションがカット ポイントとして使用されます。カット ポイントを定義する方法は他にもたくさんありますが、カスタム アノテーションがより一般的に使用されます。
-
@Before: ポイントカットポイントの前に織り込み、入力パラメータ情報を出力します。
-
@Around: 接点を囲み、戻りパラメータとインターフェイスの実行時間を出力します。
@Aspect @Component public class WebLogAspect { private final static Logger logger = LoggerFactory.getLogger(WebLogAspect.class); /** * 以自定义 @WebLog 注解为切点 **/ @Pointcut("@annotation(cn.fighter3.spring.aop_demo.WebLog)") public void webLog() { } /** * 在切点之前织入 */ @Before("webLog()") public void doBefore(JoinPoint joinPoint) throws Throwable { // 开始打印请求日志 ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); // 打印请求相关参数 logger.info("========================================== Start =========================================="); // 打印请求 url logger.info("URL:{}", request.getRequestURL().toString()); // 打印 Http method logger.info("HTTP Method: {}", request.getMethod()); // 打印调用 controller 的全路径以及执行方法 logger.info("Class Method: {}.{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName()); // 打印请求的 IP logger.info("IP: {}", request.getRemoteAddr()); // 打印请求入参 logger.info("Request Args: {}",new ObjectMapper().writeValueAsString(joinPoint.getArgs())); } /** * 在切点之后织入 * @throws Throwable */ @After("webLog()") public void doAfter() throws Throwable { // 结束后打个分隔线,方便查看 logger.info("=========================================== End ==========================================="); } /** * 环绕 */ @Around("webLog()") public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { //开始时间 long startTime = System.currentTimeMillis(); Object result = proceedingJoinPoint.proceed(); // 打印出参 logger.info("Response Args: {}", new ObjectMapper().writeValueAsString(result)); // 执行耗时 logger.info("Time-Consuming: {} ms", System.currentTimeMillis() - startTime); return result; } }
-
-
使用法: カスタム注釈をインターフェースに追加するだけです
@GetMapping("/hello") @WebLog(desc = "这是一个欢迎接口") public String hello(String name){ return "Hello "+name; }
-
実行結果: ログに入力パラメータ、出力パラメータ、および実行時間が出力されることがわかります。
21. JDK ダイナミック プロキシと CGLIB プロキシについて話しますか?
Spring の AOP は動的プロキシを通じて実装されます。動的プロキシには主に 2 つの方法があります: JDK 動的プロキシとCglib 動的プロキシ。これら 2 つの動的プロキシの使用法と原理は多少異なります。
JDK動的プロキシ
-
インターフェイス: JDK 動的プロキシの場合、ターゲット クラスはインターフェイスを実装する必要があります。
-
InvocationHandler : InvocationHandler はインターフェイスです。このインターフェイスを実装して横断的なロジックを定義し、リフレクション メカニズム (呼び出し) を通じてターゲット クラスのコードを呼び出すことができます。このプロセスでは、ロジックは事前および事後にパッケージ化される場合があります。 -ターゲットメソッドを処理します。
-
Proxy : Proxy は、InvocationHandler を使用して、ターゲット クラスによって実装されたインターフェイスに準拠するインスタンスを動的に作成し、ターゲット クラスのプロキシ ペアを生成します。
一般的な小さなシナリオ、つまりユーザーの問題を解決するための顧客サービスの移転を見てみましょう。
JDK 動的プロキシの実装:
-
インターフェース
public interface ISolver { void solve(); }
-
対象クラス:対応するインターフェースを実装する必要がある
public class Solver implements ISolver { @Override public void solve() { System.out.println("疯狂掉头发解决问题……"); } }
-
動的プロキシ ファクトリ: ProxyFactory は、リフレクションを直接使用してターゲット オブジェクトのプロキシ オブジェクトを生成します。ここでは、匿名の内部クラスを使用して InvocationHandler メソッドを書き換えており、インターフェイスの書き換えは同様です。
public class ProxyFactory { // 维护一个目标对象 private Object target; public ProxyFactory(Object target) { this.target = target; } // 为目标对象生成代理对象 public Object getProxyInstance() { return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("请问有什么可以帮到您?"); // 调用目标对象方法 Object returnValue = method.invoke(target, args); System.out.println("问题已经解决啦!"); return null; } }); } }
-
クライアント: クライアント。プロキシ オブジェクト インスタンスを生成し、プロキシ オブジェクトを通じてターゲット オブジェクト メソッドを呼び出します。
public class Client { public static void main(String[] args) { //目标对象:程序员 ISolver developer = new Solver(); //代理:客服小姐姐 ISolver csProxy = (ISolver) new ProxyFactory(developer).getProxyInstance(); //目标方法:解决问题 csProxy.solve(); } }
Cglib 動的プロキシの実装:
-
ターゲット クラス: ソルバー ここでのターゲット クラスはインターフェイスを実装する必要はありません。
public class Solver { public void solve() { System.out.println("疯狂掉头发解决问题……"); } }
-
動的プロキシファクトリー:
public class ProxyFactory implements MethodInterceptor { //维护一个目标对象 private Object target; public ProxyFactory(Object target) { this.target = target; } //为目标对象生成代理对象 public Object getProxyInstance() { //工具类 Enhancer en = new Enhancer(); //设置父类 en.setSuperclass(target.getClass()); //设置回调函数 en.setCallback(this); //创建子类对象代理 return en.create(); } @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("请问有什么可以帮到您?"); // 执行目标对象的方法 Object returnValue = method.invoke(target, args); System.out.println("问题已经解决啦!"); return null; } }
-
クライアント: クライアント
public class Client { public static void main(String[] args) { //目标对象:程序员 Solver developer = new Solver(); //代理:客服小姐姐 Solver csProxy = (Solver) new ProxyFactory(developer).getProxyInstance(); //目标方法:解决问题 csProxy.solve(); } }
22. Spring AOP と AspectJAOP の違いについて話しますか?
春のAOP
Spring AOP は、次の主な機能を備えたランタイム拡張機能です。
- ダイナミックプロキシをベースに実装されており、デフォルトではインターフェースの場合はJDKが提供するダイナミックプロキシ、メソッドの場合はCGLIBが使用されます。
- Spring AOP は管理のために IOC コンテナに依存する必要があり、Spring コンテナ上でのみ使用でき、純粋な Java コードを使用して実装されます。
- 性能面では、Spring AOPは動的プロキシをベースに実装されているため、コンテナ起動時にプロキシインスタンスを生成する必要があり、メソッド呼び出し時のスタックの深さも増すため、Spring AOPの性能は劣ります。 AspectJ と同様に優れています。
- Spring AOP は、エンタープライズ レベルの開発で最も一般的な AOP (メソッド ウィービング) の解決に取り組んでいます。
アスペクトJ
AspectJ は、使いやすく強力な AOP フレームワークです。コンパイル時の拡張機能であり、単独で使用することも、他のフレームワークに統合することもできます。AOP プログラミングの完全なソリューションです。Aspecty には別のコンパイラ ajc が必要です。
Aspecty は静的ウィービングであり、コードを変更することで実装されます。ウィービングは実際の実行前に完了するため、生成されるクラスには追加のランタイム オーバーヘッドがありません。通常、ウィービングの機会は次のようにいくつかあります。
- コンパイル時のウィービング: クラス A が Aspecty を使用して属性を追加し、クラス B がそれを参照する場合、このシナリオではコンパイル時にウィービングが必要になります。そうでない場合、クラス B はコンパイルできません。
- ポストコンパイル ウィービング: つまり、.class ファイルが生成されているか、jar パッケージにパッケージ化されています。この場合の処理を強化する必要がある場合は、ポストコンパイル ウィービングを使用する必要があります。
- ロード時のウィービング: クラスをロードするときのウィービングを指します。この期間中にウィービングを実現するには、いくつかの一般的な方法があります。
全体的な比較は以下の通りです。
事務
Spring トランザクションの本質は、実際にはデータベースによるトランザクションのサポートであり、データベース トランザクションのサポートがなければ、Spring はトランザクション機能を提供できません。
Spring は統合されたトランザクション管理インターフェイスのみを提供し、特定の実装は各データベース自体によって実装され、データベース トランザクションの送信とロールバックはデータベース独自のトランザクション メカニズムを通じて実装されます。
23. Spring トランザクションにはどのような種類がありますか?
Spring は、编程式事务
管理と声明式
トランザクション管理の 2 つの方法をサポートしています。
プログラマティックトランザクション
- プログラムによるトランザクション管理は TransactionTemplate を使用し、トランザクションの明示的な実行を必要とします。
宣言的トランザクション
-
宣言型トランザクション管理はAOP に基づいて構築されています。その本質は、AOP 関数を通じて前後のメソッドをインターセプトし、インターセプト メソッドにトランザクション処理機能を織り込むことです。つまり、ターゲット メソッドが開始される前にトランザクションを開始し、ターゲット メソッドの実行後にトランザクションがコミットされます。または実行状況に応じてロールバックされます。
-
利点は、ビジネス ロジック コードにトランザクション管理コードを混在させる必要がないことです。構成ファイルで関連するトランザクション ルールを宣言するか、@Transactional アノテーションを使用するだけで、トランザクション ルールをビジネス ロジックに適用し、ビジネス コードを削減できます。汚染の。唯一の欠点は、最も細かい粒度はメソッド レベルにのみ適用でき、プログラム トランザクションのようなコード ブロック レベルには適用できないことです。
24.Spring のトランザクション分離レベルは?
Spring のインターフェイス TransactionDefinition は、分離レベルを表す定数を定義します。もちろん、主にデータベースのトランザクション分離レベルに対応します。
- ISOLATION_DEFAULT: バックエンド データベースのデフォルトの分離境界を使用します。MySQL のデフォルトは反復読み取り、Oracle のデフォルトは読み取りコミットです。
- ISOLATION_READ_UNCOMMITTED: コミットされていない読み取り
- ISOLATION_READ_COMMITTED: 読み取りがコミットされました
- ISOLATION_REPEATABLE_READ: 反復可能な読み取り
- ISOLATION_SERIALIZABLE: シリアル化
25.Spring のトランザクション伝播メカニズム?
Spring トランザクション伝播メカニズムは、複数のトランザクションが同時に存在する場合 (一般に、複数のトランザクション メソッドが相互に呼び出されるときのことを指します) に Spring がこれらのトランザクションの動作をどのように処理するかについて説明します。
トランザクション伝播メカニズムは単純な ThreadLocal を使用して実装されているため、呼び出されたメソッドが新しいスレッドで呼び出される場合、トランザクション伝播は実際には失敗します。
Spring のデフォルトのトランザクション伝播動作は PROPAFATION_REQUIRED であり、ほとんどの状況に適しています。複数のトランザクションがServiceX#methodX()
トランザクション環境で動作しており (すべて Spring トランザクションによって強化されています)、プログラム内に呼び出しチェーンがある場合Service1#method1()->Service2#method2()->Service3#method3()
、これら 3 つのサービス クラスの 3 つのメソッドが実行されます。これらはすべて、Spring のトランザクション伝播メカニズムを通じて同じトランザクション内で動作します。
26.宣言的トランザクション実装の原理を理解していますか?
これは、AOP/動的プロキシを介して行われます。
- Bean 初期化フェーズ中にプロキシ オブジェクトを作成する: Spring コンテナが各シングルトン Bean を初期化すると、コンテナ内のすべての BeanPostProcessor 実装クラスを走査し、その postProcessAfterInitialization メソッドを実行します。コンテナ内の BeanPostProcessor 実装クラス。アスペクトでは、現在インスタンス化されている Bean と一致するアスペクトを見つけます。ここでは、トランザクション属性アスペクトを取得し、@Transactional アノテーションとその属性値を見つけて、取得したアスペクトに基づいてプロキシ オブジェクトを作成します。デフォルトでは、JDK ダイナミック プロキシを使用してプロキシを作成します。ターゲット クラスがインターフェイスの場合は JDK ダイナミック プロキシを使用し、それ以外の場合は Cglib を使用します。
- ターゲット メソッドの実行時にトランザクション拡張操作を実行する: プロキシ オブジェクトを通じて Bean メソッドが呼び出されると、対応する AOP 拡張インターセプターがトリガーされます。宣言型トランザクションは一種のサラウンド拡張です。対応するインターフェイスは、トランザクションの実装です。拡張
MethodInterceptor
インターフェイスはTransactionInterceptor
、クラス図は次のとおりです。
TransactionInterceptor
メソッド内では、トランザクション インターセプタは、invoke
親クラスTransactionAspectSupport
のinvokeWithinTransaction
メソッドを呼び出すことで、トランザクションの開始、トランザクションの送信、例外のロールバックなどのトランザクション処理を実行します。
27. 宣言的トランザクションはどのような状況で失敗しますか?
1. @Transactional は非パブリックの変更されたメソッドに適用されます
非公開の変更メソッドに Transactional アノテーションを適用した場合、Transactional は無効となります。
これは、Spring AOP プロキシにおいて、対象メソッドの実行前後に TransactionInterceptor (トランザクション インターセプター) がインターセプトするためであり、DynamicAdvisedInterceptor (CglibAopProxy の内部クラス) の intercept メソッド、または JdkDynamicAopProxy の invoke メソッドが間接的に AbstractFallbackTransactionAttributeSource の computeTransactionAttribute メソッドを呼び出して取得することになります。トランザクションのアノテーションが付けられたトランザクションの構成情報。
protected TransactionAttribute computeTransactionAttribute(Method method,
Class<?> targetClass) {
// Don't allow no-public methods as required.
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;
}
このメソッドは対象メソッドの修飾子がpublicかどうかを確認し、publicでない場合は@Transactionalのプロパティ構成情報を取得しません。
2. @Transactional アノテーション属性の伝播が正しく設定されていない
- TransactionDefinition.PROPAGATION_SUPPORTS: トランザクションが現在存在する場合はトランザクションに参加し、現在トランザクションが存在しない場合は非トランザクション方式で実行を継続します。
- TransactionDefinition.PROPAGATION_NOT_SUPPORTED: 非トランザクション モードで実行します。現在トランザクションが存在する場合、現在のトランザクションは一時停止されます。
- TransactionDefinition.PROPAGATION_NEVER: 非トランザクション モードで実行され、トランザクションが現在存在する場合は例外をスローします。
3. @Transactional アノテーション属性 rollbackFor が正しく設定されていない
rollbackFor では、トランザクションのロールバックをトリガーできる例外タイプを指定できます。Spring は、デフォルトでチェックされていない例外 (RuntimeException から継承された例外) または Error がスローされた場合にのみトランザクションをロールバックします。その他の例外はトランザクションのロールバックをトリガーしません。
// 希望自定义的异常可以进行回滚
@Transactional(propagation= Propagation.REQUIRED,rollbackFor= MyException.class
ターゲット メソッドでスローされた例外が、rollbackFor で指定された例外のサブクラスである場合、トランザクションもロールバックされます。
4. 同じクラス内のメソッド呼び出しにより、@Transactional が無効になります。
開発中、同じクラス内のメソッドを呼び出すことが避けられません。たとえば、メソッド A を持つ Test クラスがあり、A はこのクラスのメソッド B を呼び出します (メソッド B がパブリックまたはプライベートで変更されているかどうかに関係なく)。 、しかし、メソッド A にはありません。宣言アノテーション トランザクションと、B メソッドにはあります。メソッド A を外部から呼び出した後、メソッド B のトランザクションは有効になりません。ここは間違いが起こりやすい箇所でもあります。
では、なぜこのようなことが起こるのでしょうか? 実際、これは Spring AOP プロキシの使用によって引き起こされます。これは、トランザクション メソッドが現在のクラス外のコードによって呼び出された場合にのみ、 Spring によって生成されたプロキシ オブジェクトによって管理されるためです。
//@Transactional
@GetMapping("/test")
private Integer A() throws Exception {
CityInfoDict cityInfoDict = new CityInfoDict();
cityInfoDict.setCityName("2");
/**
* B 插入字段为 3的数据
*/
this.insertB();
/**
* A 插入字段为 2的数据
*/
int insert = cityInfoDictMapper.insert(cityInfoDict);
return insert;
}
@Transactional()
public Integer insertB() throws Exception {
CityInfoDict cityInfoDict = new CityInfoDict();
cityInfoDict.setCityName("3");
cityInfoDict.setParentCityId(3);
return cityInfoDictMapper.insert(cityInfoDict);
}
この状況は、最も一般的な @Transactional アノテーションの失敗シナリオです。
@Transactional
private Integer A() throws Exception {
int insert = 0;
try {
CityInfoDict cityInfoDict = new CityInfoDict();
cityInfoDict.setCityName("2");
cityInfoDict.setParentCityId(2);
/**
* A 插入字段为 2的数据
*/
insert = cityInfoDictMapper.insert(cityInfoDict);
/**
* B 插入字段为 3的数据
*/
b.insertB();
} catch (Exception e) {
e.printStackTrace();
}
}
メソッド B 内で例外がスローされ、メソッド A がメソッド B の例外をキャッチしようとすると、トランザクションは正常にロールバックできず、例外がスローされます。
org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
MVC
28. Spring MVC のコアコンポーネントは何ですか?
- DispatcherServlet : フロントコントローラーはプロセス制御全体の中核であり、他のコンポーネントの実行を制御し、統一的なスケジューリングを実行し、コンポーネント間の結合を軽減する、最高司令官に相当します。
- ハンドラー: サーブレットまたはアクションに相当する、特定のビジネス ロジックを完了するプロセッサ。
- HandlerMapping : DispatcherServlet はリクエストを受信すると、HandlerMapping を通じてさまざまなリクエストをさまざまなハンドラーにマップします。
- HandlerInterceptor : プロセッサ インターセプタはインターフェイスです。インターセプト処理を完了する必要がある場合は、このインターフェイスを実装できます。
- HandlerExecutionChain : Handler と HandlerInterceptor の 2 つの部分を含むプロセッサ実行チェーン (システムにはデフォルトの HandlerInterceptor があります。追加のインターセプトを設定する必要がある場合は、インターセプターを追加できます)。
- HandlerAdapter : プロセッサ アダプタ。Handler がビジネス メソッドを実行する前に、フォーム データの検証、データ型の変換、フォーム データの JavaBeans へのカプセル化などの一連の操作を実行する必要があります。これらの操作は HandlerApater によって完了します。開発者のみが必要とします。ビジネス ロジックの処理に重点を置く必要があるため、DispatcherServlet は HandlerAdapter を通じてさまざまなハンドラーを実行します。
- ModelAndView : モデルデータとビュー情報を読み込み、Handlerの処理結果としてDispatcherServletに返します。
- ViewResolver : DispatcheServlet が論理ビューを物理ビューに解析し、最終的にレンダリング結果でクライアントに応答するビュー リゾルバー。
29.Spring MVC ワークフロー?
- クライアントはサーバーにリクエストを送信し、このリクエストはまずフロントエンド コントローラー DispatcherServlet (中央コントローラーとも呼ばれます) に送られます。
- DispatcherServlet は、リクエストを受信した後、HandlerMapping プロセッサ マッパーを呼び出します。このことから、どのコントローラーがリクエストを処理すべきかがわかります (コントローラーは呼び出されず、既知であるだけです)
- DispatcherServlet は、HandlerAdapter プロセッサ アダプタを呼び出し、どのコントローラを実行する必要があるかをプロセッサ アダプタに伝えます。
- HandlerAdapter プロセッサ アダプタは、Controller を実行して ModelAndView (データとビュー) を取得し、それを層ごとに DispatcherServlet に返します。
- DispatcherServlet は、解析のために ModelAndView を ViewReslover ビュー パーサーに渡し、実際のビューを返します。
- DispatcherServlet がモデル データをビューに取り込みます
- DispatcherServlet は結果をクライアントに応答します
Spring MVCの全体的なプロセスは複雑ですが、実際の開発は非常にシンプルです。ほとんどのコンポーネントは開発者が作成および管理する必要はありません。構成ファイルを通じて構成するだけで済みます。実際に必要なのはハンドラー (コントローラー) だけです。開発者によって処理される.、View、Model。
もちろん、現在の開発のほとんどはフロントエンドとバックエンドの分離、Restful スタイルのインターフェイスに基づいており、バックエンドは Json データを返すだけで済みます。
30.SpringMVC Restfulスタイルインターフェースのプロセスは何ですか?
PS: これはまったく新しいステレオタイプです。結局のところ、もう誰も ModelAndView メソッドを使用すべきではありませんね。現在では、フロントエンドとバックエンドのインターフェイスが別々になっており、8 つの部分からなるソフトウェアを更新する時期が来ています。
Restful インターフェイスと応答形式は json であり、共通のアノテーション@ResponseBodyを使用することは誰もが知っています。
@GetMapping("/user")
@ResponseBody
public User user(){
return new User(1,"张三");
}
このアノテーションを追加した後のプロセス全体は、ModelAndView を使用する場合とほぼ同じですが、詳細にはいくつかの違いがあります。
-
クライアントがサーバーにリクエストを送信すると、このリクエストはまずフロントエンド コントローラーの DispatcherServlet に送られます。
-
DispatcherServlet は、リクエストを受信した後、HandlerMapping プロセッサ マッパーを呼び出します。これから、どのコントローラーがリクエストを処理すべきかがわかります。
-
DispatcherServlet は、HandlerAdapter プロセッサ アダプタを呼び出し、どのコントローラを実行する必要がある。
-
コントローラーは ServletInvocableHandlerMethod にカプセル化され、HandlerAdapter プロセッサー・アダプターはinvokeAndHandle メソッドを実行してコントローラーの要求処理を完了します。
-
HandlerAdapter が Controller へのリクエストを実行した後、HandlerMethodReturnValueHandler を呼び出して戻り値を処理します。主なプロセスは次のとおりです。
5.1. RequestResponseBodyMethodProcessor を呼び出して、ServletServerHttpResponse (Spring によるネイティブ ServerHttpResponse のカプセル化)のインスタンスを作成します。
5.2. HttpMessageConverter の write メソッドを使用して、戻り値を ServletServerHttpResponse の OutputStream 出力ストリームに書き込みます。
5.3. 書き込みプロセス中に、JsonGenerator (デフォルトでは Jackson フレームワークが使用されます) を使用して戻り値を Json にシリアル化します。
-
リクエストの実行後、返される ModelAndView は null であり、レスポンスは ServletServerHttpResponse に書き込まれているため、View の処理について心配する必要はありません。
スプリングブーツ
31. SpringBoot の紹介とその利点は何ですか?
Spring Boot は Spring に基づいて開発されています。Spring Boot 自体は Spring フレームワークのコア機能や拡張機能を提供しません。Spring フレームワークに基づいて新世代のアプリケーションを迅速かつ機敏に開発するためにのみ使用されます。これは Spring を置き換えるために使用されるソリューションではなく、Spring 開発者のエクスペリエンスを向上させるために Spring フレームワークと密接に統合されるツールです。
Spring Boot は约定大于配置
核となるアイデアに基づいて動作し、Spring に比べて次のような利点があります。
- Spring Boot では、スタンドアロン Spring アプリケーションを迅速に作成できます。
- Spring Bootには Tomcat、Jetty、Undertow などのコンテナーが組み込まれているため、デプロイメント作業を必要とせずに直接実行できます。
- Spring Boot では、Spring のような大量の煩雑な XML ファイル構成を使用する必要がなくなりました。
- Spring Boot は(コア) Spring を自動的に構成できます。SpringBoot は、元の XML 構成を Java 構成に変更し、Bean インジェクションをアノテーション インジェクション (@Autowire) に変更し、複数の XML 構成とプロパティー構成を appliaction.yml 構成ファイルに圧縮します。
- Spring Boot は、測定ツール、フォーム データ検証などのいくつかの既存の機能と、外部構成などのいくつかのサードパーティ機能を提供します。
- Spring Boot は、一般的な依存関係(spring-webmvc、jackson-json、validation-api、tomcat などの開発ライブラリ) を迅速に統合でき、提供される POM により Maven 構成を簡素化できます。コアの依存関係を導入すると、SpringBoot は他の依存関係を自動的に導入します。
32.SpringBoot自動設定の原理は理解できましたか?
自動構成を有効にする SpringBoot のアノテーションは、@EnableAutoConfiguration
スタートアップ クラスのアノテーションが@SpringBootApplication
@EnableAutoConfiguration を含む複合アノテーションであることです。
-
EnableAutoConfiguration
単なる単純なアノテーションであり、自動アセンブリのコア機能は実際にはAutoConfigurationImportSelector
クラスを通じて実装されます。@AutoConfigurationPackage //将main同级的包下的所有组件注册到容器中 @Import({ AutoConfigurationImportSelector.class}) //加载自动装配类 xxxAutoconfiguration public @interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; Class<?>[] exclude() default { }; String[] excludeName() default { }; }
-
AutoConfigurationImportSelector
インターフェイスが実装されていますImportSelector
。このインターフェイスの機能は、インポートする必要がある構成クラスを収集することです。連携することで、@Import()
対応するクラスを Spring コンテナーにインポートできます。 -
挿入されたクラスを取得するメソッドは selectImports() です。実際に呼び出すのは、
getAutoConfigurationEntry
このメソッドが自動アセンブリ クラスを取得するための鍵となります。主なプロセスは次のステップに分けることができます。- 後続の除外のためにアノテーションの属性を取得します
- 自動的にアセンブルする必要があるすべての構成クラスのパスを取得します: このステップが最も重要です META-INF/spring.factories から自動構成クラスのパスを取得します
- 重複した構成クラスと除外する必要がある重複したクラスを削除し、自動的にロードする必要がある構成クラスのパスを保存します。
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
//1.获取到注解的属性
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
//2.获取需要自动装配的所有配置类,读取META-INF/spring.factories,获取自动配置类路径
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
//3.1.移除重复的配置
configurations = this.removeDuplicates(configurations);
//3.2.处理需要排除的配置
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.getConfigurationClassFilter().filter(configurations);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}
33.SpringBoot Srarter をカスタマイズするにはどうすればよいですか?
自動構成の原理を理解していれば、カスタム SpringBoot Starter を作成するのは非常に簡単です。
- demo-spring-boot-starter という名前のプロジェクトを作成し、SpringBoot 関連の依存関係を導入します。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
-
設定ファイルの書き込み
ここでは属性設定のプレフィックスを定義します
@ConfigurationProperties(prefix = "hello") public class HelloProperties { private String name; //省略getter、setter }
-
自動組立
自動構成クラス HelloPropertiesConfigure を作成する
@Configuration @EnableConfigurationProperties(HelloProperties.class) public class HelloPropertiesConfigure { }
-
自動クラスを構成する
自動構成クラスパスをファイル
/resources/META-INF/spring.factories
に追加しますorg.springframework.boot.autoconfigure.EnableAutoConfiguration=\ cn.fighter3.demo.starter.configure.HelloPropertiesConfigure
-
テスト
ここまでで、比較的単純ではありますが、主要な自動アセンブリ機能が完成した、手書きのカスタム SpringBoot-Starter が完成しました。
-
-
プロジェクトを作成し、カスタムのスターター依存関係を導入する
<dependency> <groupId>cn.fighter3</groupId> <artifactId>demo-spring-boot-starter</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency>
-
設定ファイルに設定を追加する
hello.name=张三
-
テストクラス
@RunWith(SpringRunner.class) @SpringBootTest public class HelloTest { @Autowired HelloProperties helloProperties; @Test public void hello(){ System.out.println("你好,"+helloProperties.getName()); } }
-
演算結果
-
34. Springbootの起動原理?
SpringApplication クラスは主に次の 4 つのことを行います。
- アプリケーションの種類が通常のプロジェクトであるか Web プロジェクトであるかを推測します
- 使用可能なすべてのイニシャライザを検索してロードし、それらをイニシャライザ属性に設定します。
- すべてのアプリケーション リスナーを検索し、listeners 属性に設定します。
- 実行するメイン クラスを見つけるために、メイン メソッドの定義クラスを推論して設定します。
SpringBoot 起動の一般的なプロセスは次のとおりです。
スプリングクラウド
35.Spring Cloudについてどれくらい知っていますか?
SpringCloud は、Spring によって正式に開始されたマイクロサービス ガバナンス フレームワークです。
Spring Cloud Alibaba マイクロサービス フレームワーク:
マイクロサービスとは何ですか?
- 2014年にMartin Fowlerによって提案された新しい建築形式。マイクロサービス アーキテクチャは、単一のアプリケーションを一連の小さなサービスに分割することを推奨するアーキテクチャ パターンであり、サービスは相互に調整および連携して、ユーザーに最終的な価値を提供します。各サービスは独自の独立したプロセスで実行され、サービスは軽量の通信メカニズム (HTTP や Dubbo など) を使用して相互に連携します。各サービスは特定のビジネスを中心に構築されており、独立してデプロイできます。実稼働環境では、さらに、統合され集中化されたサービス管理メカニズムはできる限り避けるべきであり、特定のサービスについては、ビジネス コンテキストに従ってそれを構築するために適切な言語とツール (Maven など) を選択する必要があります。
- マイクロサービスの核心は、従来のワンストップ アプリケーションをビジネスに応じて 1 つずつサービスに分割し、完全に分離することであり、各マイクロサービスは単一のビジネス機能のサービスを提供し、各サービスは 1 つのことを実行します。これは、プロセスの概念に似た小規模で独立した処理プロセスであり、独立して開始または破棄でき、独自の独立したデータベースを持ちます。
マイクロサービス アーキテクチャは主にどのような問題を解決しますか?
- 多くのサービスがありますが、クライアントはどのようにそれらにアクセスし、どのように外部ゲートウェイを提供するのでしょうか?
- 非常に多くのサービスがある場合、サービス間でどのように通信するのですか?HTTP ですか、RPC ですか?
- 非常に多くのサービスを管理するにはどうすればよいですか? サービスの登録と検出。
- サービスがダウンした場合はどうすればよいですか? サーキットブレーカーのメカニズム。
主流のマイクロサービス フレームワークは何ですか?
- スプリングクラウド Netflix
- 春の雲 Alibaba
- スプリングブート + ダボ + ZooKeeper
SpringCloud のコアコンポーネントは何ですか?
PS: マイクロサービスを拡張する機会は後ほどありますが、実際、面接は通常プロジェクトに基づいて行われます。