Java Reflectionを使用して特定のフィールドに値を設定する

16145 ワード

JAva replicationの定義


実行中のJavaプログラムからクラス情報を動的に取得して処理できるJava基本API
また、特定のクラスで定義されたフィールド、メソッド、および作成者情報、およびメソッドで宣言されたパラメータ情報を表示することもできます.
これらのクエリーのフィールド、メソッド、作成者などの情報により、単純値クエリー/設定からメソッド実行までの操作を処理できます.
この文書では、Javaが提供するReplication APIを使用して、クラスで宣言されたフィールドをチェックし、値の変更/クエリーを試みます.
例:ドメインクラスを使用して、次のように一般的な値を格納します.
直接アクセスフィールドを回避するために、フィールドにプライベートアクセス制御者を指定し、getter/setterを作成して値を設定および取得します.
lombokのようなライブラリを使用するとgetter/setterを簡単に作成できますが、例を明確にするために1つずつ作成されています.
package dy.sample.reflection.domain;

public class NumberingItemInfo {
	private int item1;
	private int item2;
	private int item3;
	
	public int getItem1() {
		return item1;
	}
	
	public void setItem1(int item1) {
		this.item1 = item1;
	}
	
	public int getItem2() {
		return item2;
	}
	
	public void setItem2(int item2) {
		this.item2 = item2;
	}
	
	public int getItem3() {
		return item3;
	}
	
	public void setItem3(int item3) {
		this.item3 = item3;
	}
	
}

クラスで定義されたフィールド情報を取得します。


まずターゲットクラス情報を取得するには、3つの方法があります.
  • ターゲットクラスを指定することによりクラス情報を直接取得する方法
  • .
    Class targetClass = NumberingItemInfo.class;
  • パッケージ名とクラス名のインポート方法(例外処理のキャプチャを試みる必要がある)
  • Class targetClass = Class.forName("dy.sample.reflection.domain.NumberingItemInfo");
  • 作成したインスタンスから
  • をインポートする方法
    NumberingItemInfo numberingItemInfo = new NumberingItemInfo();
    numberingItemInfo.getClass();
    これらのインポートされたクラス・オブジェクトには、次の方法を使用してクラスで宣言されたフィールド情報をクエリーできます.
  • getDeclaredFields()
    クラスで定義されたすべてのフィールド情報を取得し、Fieldタイプの配列に戻ります.この場合、フィールドのアクセス制御者が誰であるかにかかわらず、クエリーが行われます.
  • getFields()
    クラスで定義されたすべてのフィールド情報を取得し、Fieldタイプの配列に戻ります.この場合、アクセス制御者によっては外部からアクセス可能な場合のみクエリーが行われます.
  • getDeclaredField(String name)
    クラスで定義されたフィールドの指定した名前と同じフィールド情報をFieldタイプのオブジェクトに返します.指定した名前のフィールドが見つからない場合は、アクセス制御者が誰であってもクエリーが可能なNoSuchFieldExceptionが表示されます.
  • getField(String name)
    クラスで定義されたフィールドの指定した名前と同じフィールド情報をFieldタイプのオブジェクトに返します.指定した名前のフィールドが見つからない場合は、「NoSuchFieldException」と表示され、アクセス制御者によっては外部からアクセス可能なフィールドしか見つかりません.
    Fieldオブジェクトの大まかな内容を簡単に撮りましょう.
  • NumberingItemInfo numberingItemInfo = new NumberingItemInfo();
    Arrays.stream(numberingItemInfo.getClass().getDeclaredFields())
    		.forEach(field -> System.out.println("field = " + field.toString()));
    結果
    field = private java.lang.String dy.sample.reflection.domain.NumberingItemInfo.item1
    field = private java.lang.String dy.sample.reflection.domain.NumberingItemInfo.item2
    field = private java.lang.String dy.sample.reflection.domain.NumberingItemInfo.item3
    Fieldクラスで再定義されたtoStringメソッドによって指定されたクラス名{アクセス制御者}{タイプ}{パッケージパスを含む}です.フォーマットで出力します.また,Fieldオブジェクトには,フィールドに適した説明情報,制御者(static,finalなど)などの情報が含まれている.
    getFieldsメソッドを使用すると、何も出力されません.各フィールドのアクセス制御者はプライベートなので.

    コードの例


    いよいよ返信で価格設定.item 2フィールドに迷ったことを書きます.プライベートフィールドであることを知っているため、getDeclaredFieldメソッドを使用します.
    NumberingItemInfo numberingItemInfo = new NumberingItemInfo();
    Field targetField = numberingItemInfo.getClass().getField("item2");
    targetField.set(numberingItemInfo, "리플렉션으로 수정한 값-item2");
    こうして作成して実行すると...このようなエラーが発生する可能性があります.
    java.lang.IllegalAccessException: Class dy.sample.reflection.service.NumberingService can not access a member of class dy.sample.reflection.domain.NumberingItemInfo with modifiers "private"
    	at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
    	at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)
    	at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288)
    	at java.lang.reflect.Field.set(Field.java:761)
    	at dy.sample.reflection.service.NumberingService.main(NumberingService.java:15)
    このフィールドはprivateとして指定されているため、アクセスできません.
    getDeclaredFieldメソッドを使用してprivateフィールドの情報を取得できますが、このように簡単に変更することは防止されます.
    しかし、これは完全に不可能ではありません.コードを修正して、次のように実行します.
    NumberingItemInfo numberingItemInfo = new NumberingItemInfo();
    Field targetField = numberingItemInfo.getClass().getField("item2");
    targetField.setAccessible(true);
    targetField.set(numberingItemInfo, "리플렉션으로 수정한 값-item2");
    前述したように、setアクセスメソッドを使用してフィールドをtrueとして指定して再実行した場合、エラーは発生しません.
    今回も値段を撮ってみましょう
    NumberingItemInfo numberingItemInfo = new NumberingItemInfo();
    Field targetField = numberingItemInfo.getClass().getDeclaredField("item2");
    targetField.setAccessible(true);        // 필드 값에 접근 가능하도록 허용하기
    targetField.set(numberingItemInfo, "리플렉션으로 수정한 값-item2");       // 값 세팅하기
    		
    System.out.println("Value of item2 : " + targetField.get(numberingItemInfo));   // 값 조회하기
    結果
    Value of item2 : 리플렉션으로 수정한 값-item2
    
    Process finished with exit code 0
    クエリー値にプライベートアクセス制限も指定されている場合は、setアクセス性で変更してアクセスを許可する必要があります.
    また、2行目のFieldオブジェクトを取得するコードには、NumberingItemInfoインスタンスという名前のフィールド情報はありません.NumberingItemInfoのソースクラスで定義されているitem 2というフィールドメタデータ情報を取得しました.したがって、targetFieldオブジェクトにはNumberingItemInfoに格納されている値は含まれません.

    に報いる


    運用中のサービスでは、最大15個の番号フィールドを処理する必要がある場合があります.
    これらのフィールド値を処理する論理にレプリケーションが適用されていない場合、
  • フィールドの数でコードを記述する必要があります.これは面倒ですが、コードは毒性に悪影響を及ぼします.
  • のフィールド値を保存する前に、処理ロジックなどがあり、一部のロジックを変更する必要がある場合は、フィールド数に等しいすべてのコード
  • を変更する必要がある.
    オブジェクト(または同じ仕様テーブル)を使用する他の機能がある場合は、その機能を実装するコードに同じフィールド数のコードを記述する必要がある場合があります.上記の問題があります.
    しかし、これは無条件ではなく、プライベート範囲に指定されたフィールドに直接アクセスし、値を勝手に変更することで、いくつかの大きなルールを破ったようです.
    最終的には個人の判断でこれらの機能を使うかどうかを決めますが、私の場合のように、効率の面で大きな違いがある場合は、適切に使うのも悪くありません.

    それ以外は


    この文書では、Fieldのみをチェックしていますが、クラスで定義されているメソッド、作成者、デモンストレーション者、制御者、インタフェース、サブクラスなどの多くの情報を表示し、必要に応じて多くの操作(たとえば、特定のクラスで定義されているメソッド情報をクエリーする)を実行することもできます.「フィールド名を簡単に動的に指定して値を設定する方法はないか」という好奇心から、返信を知り、関連情報を最大限に簡略化して分析し、学ぶべきことが多く、開発に際して考慮すべきことも多いと感じました.
    リファレンスサイト
    https://happyitpark.tistory.com/6
    https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/package-summary.html
    https://docs.oracle.com/javase/tutorial/reflect/