序文
数日前、読者の顔であるAliに質問がありました。Springフレームワークはどのようなデザインパターンを使用していますか?、答えはあまり良くないので、これについての記事を書くつもりです!
プロキシモード
いわゆるプロキシとは、プロキシオブジェクトと同じインターフェイスを実装することを意味します。クライアントはプロキシを使用してプロキシターゲットクラスと対話する必要があり、プロキシは通常、対話プロセス中(対話の前後)に特定の処理を実行します。 。、たとえば、このメソッドを呼び出す前に前処理を実行し、このメソッドを呼び出した後に後処理を実行します。
プロキシは静的プロキシと動的プロキシに分けられ、SpringのAOPは動的プロキシ方式を採用しています。
Springは、動的プロキシを介してクラスのメソッドレベルの側面を強化し、ターゲットオブジェクトのプロキシクラスを動的に生成し、プロキシクラスのメソッドにインターセプターを設定し、インターセプター、それによってAOPを実現します。
動的プロキシについては、私の以前の記事を読むことができます。これは非常に詳細です。動的プロキシの概要、知っておく必要があるのはここだけです。意味がありません。
戦略モード
前述したように、SpringAOPは動的プロキシを介して実装されます。
コード実装に固有のSpringは、2つの動的プロキシ実装をサポートしています。1つはJDKが提供する動的プロキシ実装で、もう1つはCglibが提供する動的プロキシ実装です。
Springは、実行時に別の動的プロキシ実装を動的に選択します。このアプリケーションシナリオは、実際には戦略パターンの典型的なアプリケーションシナリオです。
戦略インターフェースを定義するだけで、さまざまな戦略クラスにこの戦略インターフェースを実装させることができます。Springソースコードに対応して、AopProxyはストラテジーインターフェイスであり、JdkDynamicAopProxyとCglibAopProxyはAopProxyインターフェイスを実装する2つのストラテジークラスです。
その中で、AopProxyインターフェースの定義は次のとおりです。
ストラテジーパターンでは、ストラテジーの作成は通常、ファクトリメソッドを介して行われます。Springソースコードに対応して、AopProxyFactoryはファクトリクラスインターフェイスであり、DefaultAopProxyFactoryはAopProxyオブジェクトの作成に使用されるデフォルトのファクトリクラスです。
ソースコードは次のとおりです。
戦略パターンの典型的なアプリケーションシナリオは、環境変数、状態値、計算結果などを通じて、使用する戦略を動的に決定することです。
Springソースコードに対応して、上記のDefaultAopProxyFactoryクラスのcreateAopProxy()関数のコード実装を参照できます。
その中で、コードの10行目は、戦略が動的に選択される判断条件です。
デコレータパターン
キャッシュは通常、データベースと組み合わせて使用されることを私たちは知っています。書き込みキャッシュは成功したが、データベーストランザクションがロールバックされた場合、キャッシュにはダーティデータがあります。
この問題を解決するには、キャッシュ書き込み操作とデータベース書き込み操作を同じトランザクションに入れる必要があります。両方とも成功するか、両方とも失敗します。
このような機能を実現するために、Springはデコレータパターンを使用します。
TransactionAwareCacheDecoratorは、トランザクションのサポートを追加し、トランザクションがコミットおよびロールバックされるときにキャッシュデータを個別に処理します。
TransactionAwareCacheDecoratorは、キャッシュインターフェイスを実装し、実装のためにすべての操作をtargetCacheに委任して、書き込み操作にトランザクション関数を追加します。これは、デコレータパターンの典型的なアプリケーションシナリオとコード実装です。
シングルトンパターン
シングルトンパターンは、システム操作全体でクラスの1つのインスタンスのみを生成できることを意味します。
Springでは、Beanはプロトタイプ(複数のインスタンス)とシングルトン(シングルトン)の2つのモードで定義できます。SpringBeanはデフォルトでシングルトンモードです。
では、Springはどのようにシングルトンパターンを実装するのでしょうか?
答えは、シングルトンレジストリ、特にHashMapの使用によるものです。簡略化されたコードは次のとおりです。
public class DefaultSingletonBeanRegistry {
//使用了线程安全容器ConcurrentHashMap,保存各种单实例对象
private final Map singletonObjects = new ConcurrentHashMap;
protected Object getSingleton(String beanName) {
//先到HashMap中拿Object
Object singletonObject = singletonObjects.get(beanName);
//如果没拿到通过反射创建一个对象实例,并添加到HashMap中
if (singletonObject == null) {
singletonObjects.put(beanName,
Class.forName(beanName).newInstance());
}
//返回对象实例
return singletonObjects.get(beanName);
}
}
复制代码
上記のコードのロジックは比較的明確です。まず、HashMapに移動して単一のインスタンスオブジェクトを取得します。取得できない場合は、オブジェクトを作成してHashMapに追加します。
シンプルなファクトリパターン
そのようなシーンがあります:
オブジェクトAがオブジェクトBのメソッドを呼び出す必要がある場合、AにBの新しいインスタンスを作成する必要があります。その欠点は、たとえば、Bの代わりにクラスCを使用する必要がある場合など、要件が変更されると、クラスAは書き直す必要があります。
ある意味でBを結合するクラスがアプリケーションに100個ある場合、変更は困難になります。
単純なファクトリパターンを使用します。
単純なファクトリパターンは静的ファクトリメソッドとも呼ばれます。その本質は、ファクトリクラスが、入力パラメータに従って作成する製品クラスを動的に決定することです。
SpringのBeanFactoryは単純なファクトリパターンの実施形態であり、BeanFactoryはSpringIOCコンテナのコアインターフェイスです。その定義は次のとおりです。
Beanは、具体的な実装クラス(ClassPathXmlApplicationContextなど)を介して取得できます。
BeanFactory bf = new ClassPathXmlApplicationContext("spring.xml");
FlyFish flyFishBean = (FlyFish) bf.getBean("flyfishBean");
复制代码
上記のコードからわかるように、ユーザーは自分で新しいオブジェクトを作成する必要はありませんが、ファクトリクラスのメソッドgetBeanを介してオブジェクトインスタンスを取得します。これは典型的な単純なファクトリパターンですが、Springはリフレクションメカニズムを使用してBeanを作成します。
ファクトリメソッドパターン
単純なファクトリでは、すべての論理的な判断とインスタンスの作成はファクトリクラスによって実行されます。ファクトリで判断を行いたくない場合は、製品ごとに異なるファクトリを提供できます。ファクトリごとに異なる製品が生成され、各ファクトリはに対応します。対応するオブジェクトは1つだけです。これはファクトリメソッドパターンです。
SpringのFactoryBeanは、このアイデアの具体化です。FactoryBeanは、ファクトリBeanとして理解できます。最初に、その定義を見てみましょう。
クラスFlyFishFactoryBeanを定義して、FactoryBeanインターフェイスを実装します。主に、getObjectメソッドで新しいFlyFishオブジェクトを作成します。このように、getBean(id)を介して取得するのは、次のように、FlyFishFactoryBean自体のインスタンスではなく、ファクトリによって生成されたFlyFishのインスタンスです。
BeanFactory bf = new ClassPathXmlApplicationContext("spring.xml");
FlyFish flyFishBean = (FlyFish) bf.getBean("flyfishBean");
复制代码
オブザーバーパターン
Springに実装されているオブザーバーパターンは、イベントイベント(メッセージに相当)、リスナーリスナー(オブザーバーに相当)、パブリッシャー送信者(オブザーバーに相当)の3つの部分で構成されています。
Springが提供するオブザーバーパターンがどのように使用されるかを例で見てみましょう。
// Event事件
public class DemoEvent extends ApplicationEvent {
private String message;
public DemoEvent(Object source, String message) {
super(source);
}
public String getMessage() {
return this.message;
}
}
// Listener监听者
@Component
public class DemoListener implements ApplicationListener {
@Override
public void onApplicationEvent(DemoEvent demoEvent) {
String message = demoEvent.getMessage();
System.out.println(message);
}
}
// Publisher发送者
@Component
public class DemoPublisher {
@Autowired
private ApplicationContext applicationContext;
public void publishEvent(DemoEvent demoEvent) {
this.applicationContext.publishEvent(demoEvent);
}
}
复制代码
コードから、主に3つの部分が含まれていることがわかります。
- ApplicationEventを継承するイベント(DemoEvent)を定義します。
- ApplicationListenerを実装するリスナー(DemoListener)を定義します。
- 送信者(DemoPublisher)を定義すると、送信者はApplicationContextを呼び出してイベントメッセージを送信します。
Springの実装では、オブザーバーはどこに登録されていますか?どのように登録されていますか?
SpringはオブザーバーをApplicationContextオブジェクトに登録します。
実際、ソースコードに関する限り、ApplicationContextは単なるインターフェイスであり、特定のコード実装はその実装クラスAbstractApplicationContextに含まれています。オブザーバーパターンに関連するコードを次のように配置します。イベントの送信方法とリスナーの登録方法に注意を払う必要があります。
上記のコードから、実際のメッセージ送信は実際にはApplicationEventMulticasterクラスを介して行われることがわかりました。
次のクラスのソースコードの最も重要な部分、つまり、2種類のオブザーバーモードをサポートするmulticastEvent()のメッセージ送信関数(スレッドプールを介した非同期非ブロッキングと同期ブロッキング)のみを抜粋しました。
Springが提供するオブザーバーモードのスケルトンコードの助けを借りて、Springでのイベントの送信と監視を実現したい場合は、少しの作業を行い、イベントを定義し、リスナーを定義し、 ApplicationContextへのイベント。残りの作業はSpringフレームワークによって行われます。
実際、これはSpringフレームワークの拡張性、つまりコードを変更せずに新しいイベントとリスナーを拡張することも反映しています。
テンプレートモード
インタビューでよく聞かれる質問は次のとおりです。
SpringBeanの作成プロセスの主な手順を教えてください。
これには、テンプレートパターンが含まれます。また、春の拡張性を反映しています。Springでは、テンプレートパターンを使用して、ユーザーがBean作成プロセスをカスタマイズできます。
以下は、SpringBeanのライフサイクル全体です。
詳細については、前の記事「Springの拡張ポイントの適用」を参照してください。
ソースコードを注意深く見ると、実際、ここでのテンプレートパターンの実装は、標準の抽象クラスの実装ではなく、コールバックコールバックの実装にいくぶん似ていることがわかります。実行される関数はオブジェクトにカプセル化され(たとえば、初期化メソッドはInitializingBeanオブジェクトにカプセル化されます)、実行のためにテンプレート(BeanFactory)に渡されます。
オブザーバーモードとテンプレートモード。これら2つのモードは、拡張ポイントの作成に役立ち、フレームワークのユーザーがソースコードを変更せずに拡張ポイントに基づいてフレームワーク機能をカスタマイズできるようにします。
アダプターモード
Spring MVCでは、コントローラーを定義する最も一般的な方法は、@ Controllerアノテーションを使用してクラスをコントローラークラスとしてマークし、@RequesMappingアノテーションを使用して関数に対応するURLをマークすることです。
ただし、クラスにControllerインターフェイスまたはServletインターフェイスを実装させることでControllerを定義することもできます。
これらの3つの定義について、次のように3つのサンプルコードを記述しました。
// 方法一:通过@Controller、@RequestMapping来定义
@Controller
public class DemoController {
@RequestMapping("/FlyFish")
public ModelAndView getEmployeeName() {
ModelAndView model = new ModelAndView("FlyFish");
model.addObject("message", "FlyFish");
return model;
}
}
// 方法二:实现Controller接口 + xml配置文件:配置DemoController与URL的对应关系
public class DemoController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest req, HttpServletResponse resp) throws Exception {
ModelAndView model = new ModelAndView("FlyFish");
model.addObject("message", "FlyFish");
return model;
}
}
// 方法三:实现Servlet接口 + xml配置文件:配置DemoController类与URL的对应关系
public class DemoServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("Hello World.");
}
}
复制代码
アプリケーションが起動すると、SpringコンテナはこれらのControllerクラスをロードし、URLに対応するハンドラー関数を解析してHandlerオブジェクトにカプセル化し、HandlerMappingオブジェクトに格納します。リクエストが到着すると、DispatcherServletはHanderMappingからリクエストURLに対応するハンドラーを探し、ハンドラーに対応する関数コードを呼び出して実行し、最後に実行結果をクライアントに返します。
ただし、さまざまな方法で定義されたコントローラーの場合、それらの関数(関数名、入力パラメーター、戻り値など)の定義は統一されていません。
DispatcherServletはservice()メソッドを呼び出します。DispatcherServletは、さまざまなタイプのコントローラーに応じてさまざまな関数を呼び出す必要があります。
Springはアダプタモードを使用し、さまざまな方法で定義されたControllerクラスの関数を統一された関数定義に適合させます。
Springのコード実装を詳しく見てみましょう。
Springは、統合インターフェースHandlerAdapterを定義し、各コントローラーに対応するアダプタークラスを定義します。
これらのアダプタクラスには、AnnotationMethodHandlerAdapter、SimpleControllerHandlerAdapter、SimpleServletHandlerAdapterなどが含まれます。
DispatcherServletクラスでは、異なるControllerオブジェクトを異なる方法で処理する必要はなく、HandlerAdapterのhandle()関数を均一に呼び出すだけです。
著者:Programmer Duan Fei
リンク:https
://juejin.cn/post/7066266639355871268出典:RareEarthNuggets
著作権は著者に帰属します。商用の再版については、著者に連絡して許可を求め、非商用の再版については、出典を示してください。