Javaリフレクションメカニズムの理解を整理する

ここに画像の説明を挿入
まず最初に、JVM、JVM、Java仮想マシンとは何かを理解しましょう。javaがクロスプラットフォーム化できるのはこのためです。プロセス、プログラムとして理解できますが、彼の役割はコードを実行することです。上の画像はJavaメモリモデルです。ここでは、メソッド領域、スタック、ヒープに焦点を当てています。最初に学習したときに先生が深く理解しない場合、Javaメモリはヒープとスタックに分割されていることだけがわかります。理解しやすいです!

コードを記述した場合:Object o = new Object();

走っている!

まず、JVMが起動し、コードが.classファイルにコンパイルされてから、クラスローダーによってjvmのメモリに読み込まれ、クラスオブジェクトがメソッド領域に読み込まれ、オブジェクトクラスのクラスオブジェクトがヒープ上に作成されます。これは新しいオブジェクトではなく、クラス型オブジェクトです。各クラスには、メソッド領域クラスのデータ構造へのインターフェースとして機能するクラスオブジェクトが1つだけあります。オブジェクトを作成する前に、jvmはまずクラスがロードされているかどうかを確認し、クラスに対応するクラスオブジェクトを探します。ロードされている場合は、オブジェクトにメモリを割り当てます。初期化はコードです:new Object()。

上記のプロセスは、jvmに書き込んだコードを記述して実行することです。実行後、コードは終了し、jvmが閉じ、プログラムが停止します。

なぜこれについて話すのですか?反射を理解するには、それがどのシーンで使用されているかを知っている必要があります。

サブジェクトは、上記のプログラムオブジェクトは新しいものであると考えており、プログラムは実行するjvmに書き込むことと同等です。リクエストがサーバーで突然発生した場合は、クラスが必要ですが、jvmにロードされていません。停止して独自のコードを記述しますか、サーバーを起動します(新しい)。

反射とは何ですか?プログラムの実行中、一部のクラスを動的にロードする必要があります。これらのクラスは以前に使用されていない可能性があるため、jvmにロードする必要はありませんが、実行時に必要に応じてロードされます。このような利点はサーバーにとって自明です。私たちのプロジェクトの下部の例は、mysqlを使用することもあれば、oracleを使用することもあります。実際の状況に応じてドライバークラスを動的にロードする必要があります。このクラスを使用する必要があります。現時点では、プログラムはClass tc = Class.forName( "com.java.dbtest.TestConnection");を介してより動的に記述されています。クラスの完全なクラス名を使用すると、jvmがサーバーで見つかり、このクラスをロードし、それがoracleの場合、着信パラメーターは別のものになります。現時点では、リフレクションの利点を確認できます。このダイナミックはJavaの特性を反映しています。複数の例を引用すると、Springに連絡したことがある場合、さまざまなBeanを構成すると、構成ファイルの形式で構成されます。使用する必要のあるBeanを構成する必要があり、Springコンテナーは、動的にロードする必要があるため、プログラムを確実に実行できます。

反射とは何ですか?
リフレクション(リフレクション)はJavaの特性の1つです。これにより、実行中のJavaプログラムが独自の情報を取得し、クラスまたはオブジェクトの内部プロパティを操作できます。

オラクルの公式な反射の解釈は次のとおりです。

リフレクションを使用すると、Javaコードは、ロードされたクラスのフィールド、メソッド、およびコンストラクターに関する情報を検出し、リフレクトされたフィールド、メソッド、およびコンストラクターを使用して、セキュリティ制限内で対応するフィールドを操作できます。
APIは、(ランタイムクラスに基づく)ターゲットオブジェクトのパブリックメンバーまたは特定のクラスによって宣言されたメンバーのいずれかにアクセスする必要があるアプリケーションに対応します。また、プログラムでデフォルトのリフレクトアクセス制御を抑制することもできます。

リフレクションの核心は、JVMが動的にクラスをロードしたり、メソッドを呼び出したり、プロパティにアクセスしたりすることです。実行中のオブジェクトが誰であるかを事前に(コードやコンパイル時に)知る必要はありません。

Javaリフレクションは主に次の機能を提供します。


実行時に任意のオブジェクトが属するクラス
を判断し、実行時に任意のクラスのオブジェクトを作成し、実行時に任意のクラスのメンバー変数とメソッドを判断します(リフレクションを通じてプライベートメソッドを
呼び出すこともできます)、実行時に任意を呼び出すオブジェクトのメソッドの
焦点:コンパイル時ではなく実行時

第二に、リフレクションの主な目的
多くの人々は、リフレクションは実際のJava開発アプリケーションでは広く使用されていないと考えていますが、そうではありません。IDE(Eclipse、IDEAなど)を使用している場合、オブジェクトまたはクラスを入力し、そのプロパティまたはメソッドを呼び出すと、ドットを押すと、コンパイラーはそのプロパティまたはメソッドを自動的にリストします。リフレクションを使用します。

リフレクションの最も重要な用途は、さまざまな一般的なフレームワークを開発することです。多くのフレームワーク(Springなど)は構成可能です(XMLファイルを介してBeanを構成するなど)。フレームワークの普遍性を確保するために、異なるオブジェクトまたはクラスをロードし、構成ファイルに従って異なるメソッドを呼び出す必要がある場合があります。今回は、リフレクションのために、ロードする必要があるオブジェクトは、実行時に動的にロードされます。

例を挙げると、Struts 2フレームワークを使用した開発では、一般的に次のようにstruts.xmlでアクションを構成します。

<action name="login"
class="org.ScZyhSoft.test.action.SimpleLoginAction"
method="execute">
<result>/shop/shop-index.jsp</result>
<result name="error">login.jsp</result>
</action>

構成ファイルは、アクションとのマッピング関係を確立します。ビューレイヤーがリクエストを送信すると、そのリクエストはStrutsPrepareAndExecuteFilterによってインターセプトされ、StrutsPrepareAndExecuteFilterが動的にActionインスタンスを作成します。たとえば、login.actionをリクエストすると、StrutsPrepareAndExecuteFilterはstruts.xmlファイルを解析し、アクション内で名前がloginであるアクションを取得し、クラス属性に基づいてSimpleLoginActionインスタンスを作成し、invokeメソッドを使用してexecuteメソッドを呼び出します。このプロセスはリフレクションと切り離せません。

フレームワークを扱う開発者にとって、リフレクションは小さいですが非常に便利であり、さまざまなコンテナー実装の中核です。平均的な開発者にとって、フレームワークの開発に深く入り込まない場合は、リフレクションを少し少なくしますが、フレームワークの基本的なメカニズムを理解してプログラミングのアイデアを豊かにすることも非常に役立ちます。

3番目に、
リフレクションの基本的なアプリケーション前述のように、リフレクションを使用して、任意のオブジェクトが属するクラスを決定し、Classオブジェクトを取得し、任意のオブジェクトを作成し、オブジェクトを呼び出すことができます。ここでは、基本的なリフレクション関数の使用と実装を紹介します(リフレクション関連のクラスは通常java.lang.relfectパッケージにあります)。

1. Classオブジェクト
取得するには3つの方法があります

(1)ClassクラスのforName静的メソッドを使用します。

public static Class<?> forName(String className)

たとえば、JDBC開発では、このメソッドはデータベースドライバをロードするためによく使用されます。

Class.forName(driver);

(2)オブジェクトのクラスを直接取得します。例:

	
Class<?> klass = int.class;
Class<?> classInt = Integer.TYPE;

(3)次のようなオブジェクトのgetClass()メソッドを呼び出します。

	
StringBuilder str = new StringBuilder("123");
Class<?> klass = str.getClass();

2.クラス
のインスタンスかどうかを判断する一般に、instanceofキーワードを使用して、クラスのインスタンスかどうかを判断します。同時に、リフレクションでClassオブジェクトのisInstance()メソッドを使用して、それがクラスのインスタンスであるかどうかを判断することもできます。これはネイティブメソッドです。

public native boolean isInstance(Object obj);

3.インスタンスの作成
リフレクションを介してオブジェクトを生成するには、主に2つの方法があります

ClassオブジェクトのnewInstance()メソッドを使用して、Classオブジェクトに対応するクラスのインスタンスを作成します。

Class<?> c = String.class;
Object str = c.newInstance();

まずClassオブジェクトを通じて指定されたConstructorオブジェクトを取得してから、ConstructorオブジェクトのnewInstance()メソッドを呼び出してインスタンスを作成します。このメソッドは、指定されたコンストラクターを使用して、クラスのインスタンスを構築できます。

/获取String所对应的Class对象
Class<?> c = String.class;
//获取String类带一个String参数的构造器
Constructor constructor = c.getConstructor(String.class);
//根据构造器创建实例
Object obj = constructor.newInstance("23333");
System.out.println(obj);

4.メソッドの
取得Classオブジェクトのメソッドのコレクションを取得するには、主に次のメソッドが含まれます。

getDeclaredMethodsメソッドは、パブリック、保護、デフォルト(パッケージ)アクセス、プライベートメソッドを含む、クラスまたはインターフェースによって宣言されたすべてのメソッドを返しますが、継承されたメソッドは返しません。

public Method[] getDeclaredMethods() throws SecurityException

getMethodsメソッドは、継承されたクラスのパブリックメソッドを含む、クラスのすべてのパブリックメソッドを返します。

public Method[] getMethods() throws SecurityException

getMethodメソッドは特定のメソッドを返します。最初のパラメーターはメソッド名で、次のパラメーターはClassオブジェクトに対応するメソッドのオブジェクトです。

public Method getMethod(String name, Class<?>... parameterTypes)

このように説明されている内容を理解するのは難しいかもしれませんが、これらの3つの方法を理解するために例を使用します。

package org.ScZyhSoft.common;
 
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
 
public class test1 {
public static void test() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class<?> c = methodClass.class;
Object object = c.newInstance();
Method[] methods = c.getMethods();
Method[] declaredMethods = c.getDeclaredMethods();
//获取methodClass类的add方法
Method method = c.getMethod("add", int.class, int.class);
//getMethods()方法获取的所有方法
System.out.println("getMethods获取的方法:");
for(Method m:methods)
System.out.println(m);
//getDeclaredMethods()方法获取的所有方法
System.out.println("getDeclaredMethods获取的方法:");
for(Method m:declaredMethods)
System.out.println(m);
}
}
class methodClass {
 
public final int fuck = 3;
public int add(int a,int b) {
return a+b;
}
public int sub(int a,int b) {
return a+b;
}
}

プログラムの実行結果は次のとおりです。

getMethods获取的方法:
public int org.ScZyhSoft.common.methodClass.add(int,int)
public int org.ScZyhSoft.common.methodClass.sub(int,int)
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
getDeclaredMethods获取的方法:
public int org.ScZyhSoft.common.methodClass.add(int,int)
public int org.ScZyhSoft.common.methodClass.sub(int,int)

getMethods()で取得したメソッドは、java.lang.Objectで定義されているメソッドなど、親クラスのメソッドを取得できることがわかります。
5.コンストラクター情報
の取得取得するクラスコンストラクターの使用方法は、上記の取得方法の使用方法と同様です。主にClassクラスのgetConstructorメソッドを使用して、Constructorクラスのインスタンスを取得します。Constructorクラスには、オブジェクトインスタンスを作成できるnewInstanceメソッドがあります。

public T newInstance(Object ... initargs)

このメソッドは、対応するコンストラクターを呼び出して、渡されたパラメーターに基づいてオブジェクトインスタンスを作成できます。

6.クラスのメンバー変数(フィールド)の情報を取得する
これらは主にこれらのメソッドであり、ここでは繰り返されません。

getFiled:パブリックメンバー変数にアクセスします
getDeclaredField:宣言されたすべてのメンバー変数ですが、その親クラスのメンバー変数を取得できません
getFiledsおよびgetDeclaredFieldsメソッドは上記と同じです(メソッドを参照)

7.メソッドの呼び出し
クラスからメソッドを取得したら、invoke()メソッドを使用してこのメ​​ソッドを呼び出すことができます。invokeメソッドのプロトタイプは次のとおりです。

public Object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException

次に例を示します。

public class test1 {
 
public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class<?> klass = methodClass.class;
//创建methodClass的实例
Object obj = klass.newInstance();
//获取methodClass类的add方法
Method method = klass.getMethod("add",int.class,int.class);
//调用method对应的方法 => add(1,4)
Object result = method.invoke(obj,1,4);
System.out.println(result);
}
 
}
 
class methodClass {
 
public final int fuck = 3;
public int add(int a,int b) {
return a+b;
}
public int sub(int a,int b) {
return a+b;
}
}

invokeメソッドの詳細な説明については、後で記事を書いて、invokeプロセスを詳しく分析します。
8.リフレクションを使用して配列を作成する
配列はJavaの特別な型であり、オブジェクト参照に割り当てることができます。リフレクションを使用して配列を作成する例を見てみましょう。

public static void testArray() throws ClassNotFoundException {
Class<?> cls = Class.forName("java.lang.String");
Object array = Array.newInstance(cls,25);
//往数组里添加内容
Array.set(array,0,"hello");
Array.set(array,1,"Java");
Array.set(array,2,"fuck");
Array.set(array,3,"Scala");
Array.set(array,4,"Clojure");
//获取某一项的内容
System.out.println(Array.get(array,3));
}

Arrayクラスはjava.lang.reflect.Arrayクラスです。Array.newInstance()を通じて配列オブジェクトを作成します。そのプロトタイプは次のとおりです。

public static Object newInstance(Class<?> componentType, int length)
throws NegativeArraySizeException {
return newArray(componentType, length);
}

newArrayメソッドはネイティブメソッドであり、HotSpot JVMでの特定の実装については後で検討します。ここでは、ソースコードを最初に投稿します。

private static native Object newArray(Class<?> componentType, int length)
throws NegativeArraySizeException;

ソースディレクトリ:openjdk \ hotspot \ src \ share \ vm \ runtime \ Reflection.cpp

arrayOop Reflection::reflect_new_array(oop element_mirror, jint length, TRAPS) {
if (element_mirror == NULL) {
THROW_0(vmSymbols::java_lang_NullPointerException());
}
if (length < 0) {
THROW_0(vmSymbols::java_lang_NegativeArraySizeException());
}
if (java_lang_Class::is_primitive(element_mirror)) {
Klass* tak = basic_type_mirror_to_arrayklass(element_mirror, CHECK_NULL);
return TypeArrayKlass::cast(tak)->allocate(length, THREAD);
} else {
Klass* k = java_lang_Class::as_Klass(element_mirror);
if (k->oop_is_array() && ArrayKlass::cast(k)->dimension() >= MAX_DIM) {
THROW_0(vmSymbols::java_lang_IllegalArgumentException());
}
return oopFactory::new_objArray(k, length, THREAD);
}
}

さらに、Arrayクラスのsetメソッドとgetメソッドはどちらもネイティブメソッドであり、HotSpot JVMのReflection :: array_setメソッドとReflection :: array_getメソッドに対応していますが、ここでは詳しく分析しません。

**
リフレクションに関する4つの考慮事項** リフレクションは追加のシステムリソースを消費するため、オブジェクトを動的に作成する必要がない場合は、リフレクションを使用する必要はありません。

また、リフレクションによってメソッドが呼び出された場合、権限チェックは無視できるため、カプセル化が解除され、セキュリティ上の問題が発生する可能性があります。

元の記事を17件公開 Likes0 訪問数224

おすすめ

転載: blog.csdn.net/weixin_42531204/article/details/105328505