フレームとRTTIの関係,RTTIと反射の関係


リード
ソースアドレス
その後のいくつかの文章では、自分のhibernate、spring、beanutilsフレームワークを説明しますが、これらのフレームワークを説明する前に、RTTIと反射を説明する必要があります.
1年近く働いていますが、うちの会社のプロジェクトで使われているフレームワークはSSHです.あるいは、他の会社で使われているフレームワークはSSMです.どのようなフレームであっても、反射に関与します.では、反射とは何ですか.私たちはオブジェクトを生成するとき、どのタイプのオブジェクトを生成するか事前に知らず、プロジェクトが実行されるまでフレームワークが私たちのパラメータに基づいて、私たちが望んでいるオブジェクトを生成します.
例えば、フロントエンドからバックエンドのインタフェースを呼び出して、この人のすべてのプロジェクトを検索して、私たちはこの人のidを伝えるだけでいいです.もちろん、データはデータベースに由来しています.では、問題が来ました.データはどのようにして持続的な状態から私たちが望んでいるタイミングに変換されますか.この中には、反射が含まれています.しかし,反射といえばRTTI,すなわちランタイムタイプ情報(runtime Type Infomation)に言及するに違いない.
RTTI
  • poクラス
  • /**
     * Created By zby on 16:53 2019/3/16
     */
    @AllArgsConstructor
    @NoArgsConstructor
    public class Pet {
    
        private String name;
    
        private String food;
    
        public void setName(String name) {
            this.name = name;
        }
    
        public void setFood(String food) {
            this.food = food;
        }
    
        public String getName() {
            return name;
        }
    
        public String getFood() {
            return food;
        }
    }
    
    /**
     * Created By zby on 17:03 2019/3/16
     */
    public class Cat extends Pet{
    
        @Override
        public void setFood(String food) {
            super.setFood(food);
        }
    }
    
    /**
     * Created By zby on 17:04 2019/3/16
     */
    public class Garfield extends Cat{
    
        @Override
        public void setFood(String food) {
            super.setFood(food);
        }
    }
    
    
    /**
     * Created By zby on 17:01 2019/3/16
     */
    public class Dog extends Pet{
    
        @Override
        public void setFood(String food) {
            super.setFood(food);
        }
    }
    

    以上、persistent objectクラス、すなわちpojoでよく使われるjavabeanクラスについて説明した.継承関係があります.次の図です.
  • 展示情報
  • 次のコードに示すように、メソッドeatWhatTodayには2つのパラメータがあります.この2つのパラメータの1つはインタフェースクラスで、1つは親クラスです.つまり、印刷された情報が何なのか分かりません.印刷された情報は,インタフェースの実装クラスと親クラスの子クラスに基づいてのみ確認される.これが我々が言うランタイムタイプ情報であり,RTTIがあるからこそjavaは動的バインドの概念を持つ.
    /**
     * Created By zby on 17:05 2019/3/16
     */
    public class FeedingPet {
    
        /**
         * Created By zby on 17:05 2019/3/16
         *            
         *
         * @param baseEnum             
         * @param pet        
         */
        public static void eatWhatToday(BaseEnum baseEnum, Pet pet) {
            System.out.println( pet.getName() + "  " + baseEnum.getTitle() + "  " + pet.getFood());
        }
        
    }
  • テストクラス
  •  @Test
    public void testPet(){
        Dog dog=new Dog();
        dog.setName("     ");
        dog.setFood(FoodTypeEnum.FOOD_TYPE_BONE.getTitle());
    
        FeedingPet.eatWhatToday(DateTypeEnum.DATE_TYPE_MORNING,dog);
    
        Garfield garfield=new Garfield();
        garfield.setName("      ");
        garfield.setFood(FoodTypeEnum.FOOD_TYPE_CURRY.getTitle());
        FeedingPet.eatWhatToday(DateTypeEnum.DATE_TYPE_MIDNIGHT_SNACK,garfield);
    }

    印刷された情報は次のとおりです.
    では、これは反射と何の関係があるのでしょうか.
    現在のクラス情報の取得を反射
    前述したランタイムタイプ情報のように、タイプ情報はランタイムでどのように表されますか?この時、Classという特殊な相手を思い浮かべました.属性、メソッド、コンストラクタなど、クラスのすべての情報を含むクラスオブジェクトを参照してください.
    クラスはプログラムの一部であり、各クラスにはClassオブジェクトがあることはよく知られています.新しいクラスが作成され実行されるたびにクラスオブジェクトが生成されます(より適切には、同じ名前の.classファイルに保存されます).このクラスのオブジェクトを生成するために、現在のプログラムを実行するjvmはクラスローダに使用されます.jvmはまずbootstrapクラスローダを呼び出し、コアファイル、jdkのコアファイル、例えばObject、Systemなどのクラスファイルをロードします.次にplateformローダを呼び出し、ファイルに関連するクラスをロードします.例えば圧縮ファイルのクラス、画像のクラスなど.最後に、アプリケーションClassLoaderを使用して、ユーザー定義のクラスをロードします.
    現在のクラス情報のロード
    反射は、Classを使用してオブジェクトの作成、変更、プロパティの値の取得、変更などです.では、反射はどのようにして現在のクラスを作成しますか?
  • の1つ目は、jdbcクラスドライバを記載する場合のように、現在のコンテキストのクラスパスを使用してオブジェクトを作成することができる.
    /**
     * Created By zby on 18:07 2019/3/16
     *               
     */
    public static Class byClassPath(String classPath) {
        if (StringUtils.isBlank(classPath)) {
            throw new RuntimeException("       ");
        }
        classPath = classPath.replace(" ", "");
        try {
            return Class.forName(classPath);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }
  • の2つ目は、クラスの字面定数によって、非常に簡単で、より安全です.コンパイル時にチェックされるため、try catchのコード高速に配置する必要はありません.また、forNameのメソッド呼び出しを根絶するので、より効率的です.これはspring、hibernateなどの主流フレームワークで使われています.

  • フレームワークhibernateの内部でクラス字面定数を使用してオブジェクトを作成した後、下位層はjdbcでデータテーブルのフィールド値を取得し、データテーブルのフィールドに基づいて現在のクラスの属性と一致し、フィールド値を現在のオブジェクトに埋め込む.マッチングが失敗すると、対応するエラーが表示されます.
    クラスフォント定数は、コードに示すようにオブジェクト情報を取得します.以下では、クラスのフォント定数によってオブジェクトを作成します.
     /**
     * Created By zby on 18:16 2019/3/16
     *                
     */
    public static void byClassConstant() {
        System.out.println(Dog.class);
    }
  • の3つ目は、フレームワーク内で使用される現在のクラスをオブジェクトによって作成することです.
  • /**
    * Created By zby on 18:17 2019/3/16
    *              
    */
    public static Class byCurrentObject(Object object) {
        return object.getClass();
    }

    [反射](Reflection)現在のクラスオブジェクトを作成するには
    現在のオブジェクトを作成するには、clazzを通過する2つの方法があります.newInstance();一般的には、パラメトリックコンストラクタがなく、オブジェクトを作成した後、コードに示すように、属性付与とメソッド付与によって属性を取得できます.
  • 第1種はclazzを通過する.新Instance()オブジェクト作成
  • /**
     * Created By zby on 18:26 2019/3/16
     *          
     */
    public static  T byCommonGeneric(Class clazz, String name, BaseEnum baseEnum) {
        if (null == clazz) {
            return null;
        }
        try {
            T t = (T) clazz.newInstance();
            
            //      ,getField      ,      
            Field field = clazz.getDeclaredField("name");
            //    ,  ,           
            field.setAccessible(true);
            field.set(t, name);
            
            //      
            Method method1 = clazz.getDeclaredMethod("setFood", String.class);
            method1.setAccessible(true);
            method1.invoke(t, baseEnum.getTitle());
    
            return t;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    
      :
     @Test
    public void testCommonGeneric() {
        Dog dog= GenericCurrentObject.byCommonGeneric(Dog.class,
                "      ", 
                FoodTypeEnum.FOOD_TYPE_BONE);
        FeedingPet.eatWhatToday(DateTypeEnum.DATE_TYPE_NOON,dog);
    }
    

    おじさんは結果を出しました.
    名前が出力されていないのに、私たちは名前を書いたのに、どうして出力されていないのですか.なぜならdogは親Petを継承しているため、サブクラスオブジェクトを作成するときに、まず、親がロードしていないコンストラクタ、静的コードブロック、静的プロパティ、静的メソッドなどがロードされます.しかし、Dogはここでは無パラメトリックコンストラクタでロードされており、もちろん、無パラメトリックコンストラクタによって親もインスタンス化されている.私たちはdogオブジェクトのnameに値を割り当てるとき、親オブジェクトのnameに値を付けていないので、dogのnameには値がありません.親参照は、子オブジェクトを指します.
    Dogクラスの @Override public void setFood(String food) {super.setFood(food); }のsuperをsetFood(food); メソッドを削除しても、属性foodには値がありません.図に示すように、
  • コンストラクタによるオブジェクト作成
  •     /**
         * Created By zby on 18:26 2019/3/16
         *          
         */
        public static  T byConstruct(Class clazz, String name, BaseEnum baseEnum) {
            if (null == clazz) {
                return null;
            }
    //            ,
            Class paramType[] = {String.class, String.class};
            try {
    //               ,       ,           ,          
                Constructor constructor = clazz.getConstructor(paramType);
    //                  ,               ,  ,   
                return (T) constructor.newInstance(name, baseEnum.getTitle());
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
        
          :
        
       @Test
        public void testConstruct() {
            Dog dog= GenericCurrentObject.byConstruct(Dog.class,
                    "      ",
                    FoodTypeEnum.FOOD_TYPE_BONE);
            System.out.println("       :"+dog.getName()+"
    "); System.out.println(" :"+dog.getFood()+"
    "); FeedingPet.eatWhatToday(DateTypeEnum.DATE_TYPE_MIDNIGHT_SNACK,dog); }

    テスト結果:
    これはコンストラクタによって作成されたオブジェクトです.ただし、パラメータタイプとパラメータ値の桁数は必ず等しくなければなりません.そうしないと、エラーが表示されます.
    まとめ
    なぜこの文章を書いたのか、前にも述べたように、多くのフレームワークが反射とRTTIを使っています.しかし、私たちの普段の仕事は、一般的に業務を主としています.spring、hibernate、mybatis、beanutilsなどの他の人がカプセル化したフレームワークを使用することが多い.だから、私たちは反射にあまり関心を持っていませんが、もっと高い方向に登りたいなら、基礎を引き揚げなければなりません.さもないと、基礎が不安定で、高く登れば登るほど、重く転ぶ.
    私はこれからの章で、私が書いたspring、hibernateのフレームワークを紹介することで、反射をよりよく説明します.