サードパーティに設計が不十分なクラスがあり、JAR
そのプライベートクラスの1つにアクセスする必要があるフィールドのます。たとえば、プライベートフィールドを選択する必要があるのはなぜですか?
class IWasDesignedPoorly {
private Hashtable stuffIWant;
}
IWasDesignedPoorly obj = ...;
リフレクションを使用して値を取得するにはどうすればよいstuffIWant
ですか?
サードパーティに設計が不十分なクラスがあり、JAR
そのプライベートクラスの1つにアクセスする必要があるフィールドのます。たとえば、プライベートフィールドを選択する必要があるのはなぜですか?
class IWasDesignedPoorly {
private Hashtable stuffIWant;
}
IWasDesignedPoorly obj = ...;
リフレクションを使用して値を取得するにはどうすればよいstuffIWant
ですか?
回答:
プライベートフィールドにアクセスするには、クラスの宣言済みフィールドからフィールドを取得して、アクセス可能にする必要があります。
Field f = obj.getClass().getDeclaredField("stuffIWant"); //NoSuchFieldException
f.setAccessible(true);
Hashtable iWantThis = (Hashtable) f.get(obj); //IllegalAccessException
EDIT:としてでコメントされていaperkinsにアクセスし、値を取得するとして、それを設定すると投げることができ、両方のフィールドにアクセスする、Exception
唯一ものの、Sをチェックし、あなたがに留意する必要がある例外が上にコメントしています。
NoSuchFieldException
あなたが宣言したフィールドに対応していませんでした名前でフィールドを求めた場合にスローされます。
obj.getClass().getDeclaredField("misspelled"); //will throw NoSuchFieldException
IllegalAccessException
それはプライベートであり、逃し経由でアクセス可能にされていない場合、フィールドには、例えば(アクセスできなかった場合にスローされますf.setAccessible(true)
行を。
RuntimeException
スローされる可能性があり、SのいずれかですSecurityException
(JVMのであれば、S SecurityManager
は、フィールドのアクセシビリティを変更することはできません)、またはIllegalArgumentException
あなたがいないフィールドのクラスの型のオブジェクトのフィールドを試してみて、アクセスすると、(S)
f.get("BOB"); //will throw IllegalArgumentException, as String is of the wrong type
getDeclaredField()
が親クラスで定義されている場合、フィールドは見つかりません- getDeclaredField()
一致が見つかる(その後を呼び出すことができるsetAccessible(true)
)か、に到達するまで、親クラスの階層全体を反復して、それぞれを呼び出す必要がありますObject
。
private
フィールドに違いをもたらしました。偶発的なアクセスを防ぎます。
FieldUtils
Apache commons-lang3から試してください:
FieldUtils.readField(object, fieldName, true);
リフレクションは、問題を解決する唯一の方法ではありません(クラス/コンポーネントのプライベートな機能/動作にアクセスすることです)
別の解決策は、.jarからクラスを抽出し、(たとえば)JodeまたはJadを使用して逆コンパイルし、フィールドを変更して(またはアクセサを追加して)、元の.jarに対して再コンパイルすることです。次に、新しい.classを.jar
クラスパスのの前に置くか、に再度挿入し.jar
ます。(jarユーティリティを使用すると、既存の.jarを抽出して再挿入できます)
以下に述べるように、これは単にフィールドにアクセス/変更するのではなく、プライベート状態にアクセス/変更するというより広い問題を解決します。
.jar
もちろん、これは署名されていないことが必要です。
まだ言及されていないもう1つのオプション:Groovyを使用します。Groovyでは、言語の設計の副作用としてプライベートインスタンス変数にアクセスできます。フィールドにゲッターがあるかどうかに関係なく、
def obj = new IWasDesignedPoorly()
def hashTable = obj.getStuffIWant()
Javaのリフレクションを使用するprivate/public
と、あるクラスのすべてのフィールドとメソッドに別のクラスにアクセスできます。ただし 、セクションの欠点のOracle ドキュメントに従って彼らはそれを推奨:
「リフレクションを使用すると、プライベートフィールドやメソッドへのアクセスなど、非リフレクトコードでは違法となる操作をコードで実行できるため、リフレクションを使用すると、予期しない副作用が発生し、コードが機能しなくなり、移植性が損なわれる場合があります。リフレクティブコード抽象化に違反しているため、プラットフォームのアップグレードにより動作が変わる可能性があります」
以下は、リフレクションの基本的な概念を示すためのコードスナップです。
Reflection1.java
public class Reflection1{
private int i = 10;
public void methoda()
{
System.out.println("method1");
}
public void methodb()
{
System.out.println("method2");
}
public void methodc()
{
System.out.println("method3");
}
}
Reflection2.java
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Reflection2{
public static void main(String ar[]) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
{
Method[] mthd = Reflection1.class.getMethods(); // for axis the methods
Field[] fld = Reflection1.class.getDeclaredFields(); // for axis the fields
// Loop for get all the methods in class
for(Method mthd1:mthd)
{
System.out.println("method :"+mthd1.getName());
System.out.println("parametes :"+mthd1.getReturnType());
}
// Loop for get all the Field in class
for(Field fld1:fld)
{
fld1.setAccessible(true);
System.out.println("field :"+fld1.getName());
System.out.println("type :"+fld1.getType());
System.out.println("value :"+fld1.getInt(new Reflaction1()));
}
}
}
それがお役に立てば幸いです。
oxbow_lakesが言及しているように、リフレクションを使用してアクセス制限を回避できます(SecurityManagerで許可されている場合)。
とは言っても、このクラスの設計があまりにも悪く、そのようなハッカーに頼るようになっている場合は、別の方法を探す必要があります。確かに、この小さなハックによって数時間は節約できるかもしれませんが、将来、どれくらいの費用がかかりますか?
Soot Java最適化フレームワークを使用して、バイトコードを直接変更します。 http://www.sable.mcgill.ca/soot/
すすは完全にJavaで書かれており、新しいJavaバージョンで動作します。
次のことを行う必要があります。
private static Field getField(Class<?> cls, String fieldName) {
for (Class<?> c = cls; c != null; c = c.getSuperclass()) {
try {
final Field field = c.getDeclaredField(fieldName);
field.setAccessible(true);
return field;
} catch (final NoSuchFieldException e) {
// Try parent
} catch (Exception e) {
throw new IllegalArgumentException(
"Cannot access field " + cls.getName() + "." + fieldName, e);
}
}
throw new IllegalArgumentException(
"Cannot find field " + cls.getName() + "." + fieldName);
}
Springを使用する場合、ReflectionTestUtilsは、最小限の労力でここで役立ついくつかの便利なツールを提供します。「単体および統合テストシナリオで使用するため」と説明されています。ReflectionUtilsという名前の同様のクラスもありますが、これは「内部使用のみを目的としています」と説明されています。これが何を意味するかについては、この回答を参照してください。
投稿された例に対処するには:
Hashtable iWantThis = (Hashtable)ReflectionTestUtils.getField(obj, "stuffIWant");
ReflectionTestUtils
し続けましたが、テスト状況でこの種のことを実行する必要があっただけです。)
リフレクションに関する追加の注記:いくつかの特殊なケースで、同じ名前のクラスが異なるパッケージに複数存在する場合、トップアンサーで使用されているリフレクションがオブジェクトから正しいクラスを選択できない場合があることを確認しました。したがって、オブジェクトのpackage.classが何であるかがわかっている場合は、次のようにプライベートフィールドの値にアクセスすることをお勧めします。
org.deeplearning4j.nn.layers.BaseOutputLayer ll = (org.deeplearning4j.nn.layers.BaseOutputLayer) model.getLayer(0);
Field f = Class.forName("org.deeplearning4j.nn.layers.BaseOutputLayer").getDeclaredField("solver");
f.setAccessible(true);
Solver s = (Solver) f.get(ll);
(これは私にとってうまくいかなかったクラスの例です)
マニフォールドの @JailBreakを使用して、直接型保証されたJavaリフレクションを実行できます。
@JailBreak Foo foo = new Foo();
foo.stuffIWant = "123;
public class Foo {
private String stuffIWant;
}
@JailBreak
の階層foo
内Foo
のすべてのメンバーに直接アクセスできるように、コンパイラーのローカル変数のロックを解除します。
同様に、jailbreak()拡張メソッドを1回限りの使用に使用できます。
foo.jailbreak().stuffIWant = "123";
jailbreak()
メソッドを通じて、Foo
の階層内の任意のメンバーにアクセスできます。
どちらの場合も、コンパイラーはフィールドアクセスをタイプセーフにパブリックフィールドのように解決しますが、マニフォールドは内部で効率的なリフレクションコードを生成します。
マニホールドの詳細をご覧ください。
XrayInterfaceツールを使用すると、非常に簡単です。不足しているゲッター/セッターを定義するだけです。
interface BetterDesigned {
Hashtable getStuffIWant(); //is mapped by convention to stuffIWant
}
そして、あなたの貧弱に設計されたプロジェクトをX線で撮る:
IWasDesignedPoorly obj = new IWasDesignedPoorly();
BetterDesigned better = ...;
System.out.println(better.getStuffIWant());
内部的には、これは反射に依存しています。
ケースの問題を回避してみてください。データを設定/取得したいカラスは、独自のクラスの1つです。
ただ、作成public setter(Field f, Object value)
してpublic Object getter(Field f)
いるため。これらのメンバー関数内で独自にセキュリティチェックを行うこともできます。たとえば、セッターの場合:
class myClassName {
private String aString;
public set(Field field, Object value) {
// (A) do some checkings here for security
// (B) set the value
field.set(this, value);
}
}
もちろん、ここでフィールド値を設定java.lang.reflect.Field
するsString
前にfor を見つける必要があります。
この手法は、一般的なResultSet-to-and-from-model-mapperで使用します。