21日間でSpring-Supringアーキテクチャ設計原則を解決します(day 1)


次の21日間で、Springアーキテクチャの設計原則に従い、Spring IOC、AOPの実現に行きます.ソース分析の手段を通じて、Springのカーネルハート法をより深く説明します.
アーキテクチャ設計の原則
まず、大きな方向からSpringの構造設計を知る必要があるので、Springのソフトウェアアーキテクチャ設計の原則(共通のソフトウェアアーキテクチャ設計の原則でもある)とSpringでよく使われる設計パターンを詳しく紹介します.
  • 開閉の原則:開閉の原則は、ソフトウェアエンティティ(クラス、モジュール、関数など)が拡張に開放されるべきであり、修正に対してクローズされることを指す.抽象的にフレームを構築し、拡張の詳細を実現することで、ソフトウェアシステムの多重性と維持性を向上させることを強調しています.すなわち、プロジェクトニーズが変更された場合、既存のJava Classを修正する代わりに、新しいJava Classを通じて新たな需要を実現することができる.
  • 依存逆さまの原則:コード構造を設計する時、高層モジュールは下のモジュールに依存してはいけません.両方はその抽象に依存するべきです.抽象は細部に頼るべきではなく、細部は抽象に頼るべきである.
    抽象を基準にして詳細を基準にして構築されたアーキテクチャよりもはるかに安定しているため、需要を手に入れた後、インターフェースに向かってプログラミングし、トップレベルから詳細にコード構造を設計する.
  • 単一職責原則:クラス変更の原因が複数存在しないこと.つまり、クラス、インターフェースまたは方法は一つの役割だけを担当します.
  • インターフェース分離原則:単一の総インターフェースを使用せずに複数の専用インターフェースを使用することを指し、クライアントは不要なインターフェースに依存するべきではない.ただし、注意してください
  • クラスの他のクラスへの依存は、最小のインターフェースの上に構築されるべきである.
  • 単一インターフェースを構築し、膨大で肥大化したインターフェースを構築しないでください.
  • はできるだけインターフェースを細分化して、インターフェースの中の方法はできるだけ少ないです.
  • ディミト原則:1つのオブジェクトが他のオブジェクトに対して最も少ない理解を維持し、クラスとクラスの間の結合度をできるだけ低くすること.
  • リッツ置換の原則:各タイプにT 1のオブジェクトo 1がある場合、T 1で定義された全てのプログラムPがすべてのオブジェクトo 1にo 2に置き換えられた場合、プログラムPの動作が変化しない場合、T 2はT 1のサブタイプである.つまりソフトウェアのエンティティであり、親に適用されるなら、必ずサブクラスにも適用され、すべての引用親類のところは、そのサブクラスのオブジェクトを透明に使用することができる必要があります.
  • 合成多重化原則:できるだけオブジェクト組合せ(has-a)/統合(contain-s)を使用して、ソフトウェア多重化の目的に達するのではなく、オブジェクト同士の結合度を下げることができる.一つのクラスの変化は他のクラスに与える影響が比較的少ない.
  • 注意:上記のソフトウェアアーキテクチャの設計原則は、日常プロジェクト開発の方案設計指導としてのみ可能であり、決して頭を働かせて、強迫症を形成してはならない.業務の複雑な場面に遭遇した時には、臨機応変に、柔軟に応用し、過度の設計を避けるべきである.この近くに興味がありますか?それとも分かりません.自分で関連資料を調べて勉強します.
    デザインモード
    Javaを学んだことがある人はすべて設計モードを理解したことがあるべきで、実際に開発する過程の中で、合理的な利用の設計のモード、効果的なのはコードの可読性を高めることができて、維持性はすぐ拡張性ができて、“抱擁の変化”を実現します.「Springとは、様々なデザインを適用した綿密な枠組みのことです.Springでよく使われている古典的なデザインを一つ一つ分析します.
    工場モード:
    名前の通り、一連のJavaオブジェクトを生産できるインターフェースです.最も古典的なのはSpringのBeanFactoryです.
    工場モードの利点:工場モードを通じて、私達は上の階に応答する工場を保留するだけで、具体的な種類の実現の細部を暴露する必要がなく、需要が変化する時、上の階のコードを変えなくてもいいです.
    工場モードを細分化していくと、以下の3つの種類に分けられます.工場モードは比較的簡単であり、また、戦略モード、テンプレート方法モードなどの他の設計モードと一緒に使用されることが多いので、ここでは多すぎる説明はしません.
  • シンプル工場モデル
  • 工場方法モデル
  • 抽象工場モデル
  • プロキシモード:
    動的エージェントは、バイト配列を採用して、元のオブジェクトの代わりにオブジェクトを再生成し、動的エージェントの目的を達成しました.SpringのコアAOPは、動的エージェントによって実現されます.
  • JDK動的エージェント生成オブジェクトのステップは、以下の通りである.
  • は、プロキシ対象の参照を取得し、そのすべてのインターフェースを取得し、反射によって取得する.
  • JDK動的エージェントクラスは、新しいクラスを再生成し、同時に新しいクラスは、エージェントクラスによって実現されるすべてのインターフェースを実現する.
  • は、Javaコードを動的に生成し、新たに追加されたトラフィック論理法は、論理コードによって呼び出される.
  • によって再生成されたJavaコード.classファイル
  • は、JVMに再ロードして実行します.JDK動的エージェントのターゲットクラスは、あるインターフェースを実現しなければならないので、プロキシオブジェクトを生成することができません.
    //     1
    public interface OrderService {
         
        void buy();
    }
    //     2
    public interface OrderService2 {
         
        void refund();
    }
    //      :     1   2
    public class OrderServiceImpl implements OrderService, OrderService2 {
         
        private String name;
        public OrderServiceImpl(String name){
         
            this.name = name;
        }
        @Override
        public void buy() {
         
            System.out.println(this.name + " want to buy a macbook pro...");
        }
        @Override
        public void refund() {
         
            System.out.println(this.name + " want to refund");
        }
    }
    //      :    InvocationHandler  ,     invoke  
    //         ,         InvocationHandler invoke  
    public class OrderServiceProxy implements InvocationHandler {
         
    
    //       :      
        private Object target;
    
        public  <T> T getProxy(T target){
         
            this.target = target;
            Class<?> cls = target.getClass();
            Class<?>[] interfaces = cls.getInterfaces();
            if (interfaces== null || interfaces.length <= 0){
         
                throw new RuntimeException("Not implement a interface");
            }
    //   JDK     ,      ,          ,   InvocationHandler invoke    
            return (T) Proxy.newProxyInstance(cls.getClassLoader(), interfaces, this);
        }
    
    //   invoke  
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
         
       		 //     
            System.out.println("=======before========");
            //       , target   method  
            Object response = method.invoke(this.target, args);
            //     
            System.out.println("=======After==========");
    
            return response;
        }
    }
    
    /**
    =======before========
    xiaoming want to buy a macbook pro...
    =======After==========
    */
    	@Test
        public void testProxy(){
         
            OrderService orderService = new OrderServiceImpl("xiaoming");
    
            OrderServiceProxy proxy = new OrderServiceProxy();
            OrderService instance = proxy.getProxy(orderService);
    
            instance.buy();
        }
    
  • Cglib動的エージェント:Cglib動的エージェントの大きな利点は、エージェントによってインターフェースを実現する必要がなく、バイトコードを解析して再構成することによって動的エージェントを実現することである.
    //               :    
    public class NonInterfaceObject {
         
        public void refund(){
         
            System.out.println("I am not implement any interface...");
        }
    
    }
    //   Cglib        ,    MethodInterceptor  。   JDK      InvocationHandler。
    public class CglibProxy implements MethodInterceptor {
         
    
        public <T> T getInstance(Class<? extends T> cls){
         
    		// Cglib    ,                
            Enhancer enhancer = new Enhancer();
            //             
            //                
            enhancer.setSuperclass(cls);
            //           ,     (   MethodInterceptor   intercept  )  
            enhancer.setCallback(this);
    
            return (T) enhancer.create();
        }
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
         
            System.out.println("===============Before=============");
    //        Object response = method.invoke(o, objects);   //           ,  invoke       intercept  
            Object response = methodProxy.invokeSuper(o, objects);   System.out.println("===============After=============");
            return response;
        }
    }
    
        @Test
        public void testCglibProxy() {
         
    
            //   Cglib    ,       .class        
          	System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D://cglib");
    
            CglibProxy cglibProxy = new CglibProxy();
            NonInterfaceObject instance = cglibProxy.getInstance(NonInterfaceObject.class);
            instance.refund();
        }
    
    逆コンパイルにより、JDKダイナミックエージェントとCglibダイナミックエージェントが生成したプロキシオブジェクトの実装をそれぞれ確認することができます.
  • CglibとJDK動的エージェントの比較
  • JDK動的エージェントは、代理対象のインターフェースを実現し、Cglibエージェントは、代理対象を継承した.
  • JDK動的エージェントとCglibエージェントは、実行中にバイトコードを生成し、JDK動的エージェントは直接にClassバイトコードを書き込み、CglibエージェントはASMフレームを使用してClassバイトコードを書き込み、Cglibエージェントはより複雑になり、JDK動的エージェントより生成が低くなります.
  • JDK動的エージェント呼び出しプロキシ方法は反射機構によって呼び出され、CglibプロキシはFastClass機構によって直接的に呼び出されるので、Cglibプロキシの実行効率がより高く、ここでは実行効率が強調される.
  • Springにおけるプロキシモード:AOPを実現する.
  • Springは、動的エージェントを利用してAOPを実現する場合、2つの非常に重要なクラスがあります.
  • ビーンがインターフェースを実現するとき、SpringはJDK動的エージェント、すなわちJDKDynamicAopProxyを使用する.
  • ビーンがインターフェースを実装していない場合、SpringはCglibエージェント、すなわちCglibAopProxyを選択する.
  • Springは、Cglibエージェントを強制的に構成することにより、Springのプロファイルに以下のコードを追加するだけでよい.
    <aop:aspectj-autoproxy proxy-target-class="true">
    
  • プロキシモードの長所と短所:
  • 利点:
  • プロキシモードは、プロキシオブジェクトを実際に呼び出されたオブジェクトから分離することができる.
  • は、ある程度システムの結合性を低下させ、拡張性が良い.
  • は、ターゲットを保護する役割を果たすことができる.
  • は、ターゲットオブジェクトの機能を強化することができる.欠点:
  • プロキシモードは、システム設計におけるクラス数の増加をもたらす.
  • クライアントとターゲットオブジェクトにプロキシオブジェクトを追加すると、要求処理速度が遅くなります.
  • はシステムの複雑さを増しました.
  • 他にもいくつかのエッグを残しています.興味のある人は自分で資料を調べたり試したりします.
  • Spring 5.0 xのAOPデフォルトは依然としてJDKダイナミック世代
  • を使用しています.
  • SpringBoot 2.xを開始し、JDK動的エージェントの使用によるタイプ変換異常を解決するためにCGLOIBがデフォルトで使用されている.
  • はSpringBoot 2.xにおいて、デフォルトでJDK動的エージェントを使用するなら、設定項目spring.aop.proxy-targt class=falseによって修正することができ、proxyTarget Class構成は無効となりました.