プライベートメソッドを呼び出す方法はありますか?
-
22-08-2019 - |
質問
XML とリフレクションを使用して返すクラスがあります。 Object
別のクラスに移動します。
通常、これらのオブジェクトは外部オブジェクトのサブフィールドですが、場合によっては、その場で生成したい場合もあります。このようなことを試しましたが、役に立ちませんでした。それはJavaがアクセスを許可していないためだと思います private
反省の方法。
Element node = outerNode.item(0);
String methodName = node.getAttribute("method");
String objectName = node.getAttribute("object");
if ("SomeObject".equals(objectName))
object = someObject;
else
object = this;
method = object.getClass().getMethod(methodName, (Class[]) null);
提供されたメソッドが private
, 、で失敗します。 NoSuchMethodException
. 。メソッドを作ることで解決できました public
, 、または派生元の別のクラスを作成します。
簡単に言うと、私はただ、 private
リフレクションによるメソッド。
解決
あなたは反射でプライベートメソッドを呼び出すことができます。投稿コードの最後のビットを変更します:
Method method = object.getClass().getDeclaredMethod(methodName);
method.setAccessible(true);
Object r = method.invoke(object);
注意点がいくつかあります。まず、getDeclaredMethod
は現在のClass
で宣言されたメソッドは、スーパータイプから継承されないでしょう。必要であれば、具体的なクラス階層をトラバース。第二に、SecurityManager
はsetAccessible
メソッドの使用を防ぐことができます。だから、それは(PrivilegedAction
またはAccessController
を使用して)Subject
として実行する必要があります。
他のヒント
プライベートメソッドオブジェクトを取得し、実際にそれを呼び出すことができるようにgetDeclaredMethod()
を使用するmethod.setAccessible()
を使用してください。
は、この方法は、次のメソッドは、任意のクラスのプライベートメソッドを呼び出すために使用することができる非プリミティブデータタイプを受け入れる場合:
public static Object genericInvokeMethod(Object obj, String methodName,
Object... params) {
int paramCount = params.length;
Method method;
Object requiredObj = null;
Class<?>[] classArray = new Class<?>[paramCount];
for (int i = 0; i < paramCount; i++) {
classArray[i] = params[i].getClass();
}
try {
method = obj.getClass().getDeclaredMethod(methodName, classArray);
method.setAccessible(true);
requiredObj = method.invoke(obj, params);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return requiredObj;
}
受け入れたパラメータは、OBJのmethodNameおよびパラメータです。たとえば、
public class Test {
private String concatString(String a, String b) {
return (a+b);
}
}
方法concatStringは次のように呼び出すことができる
Test t = new Test();
String str = (String) genericInvokeMethod(t, "concatString", "Hello", "Mr.x");
Spring の ReflectionTestUtils を使用してこれを行うことができます (org.springframework.test.util.ReflectionTestUtils)
ReflectionTestUtils.invokeMethod(instantiatedObject,"methodName",argument);
例 :プライベートメソッドを持つクラスがある場合 square(int x)
Calculator calculator = new Calculator();
ReflectionTestUtils.invokeMethod(calculator,"square",10);
私は反射を経由して実行保護されたメソッドの完全なコードを提供してみましょう。これは、ジェネリック医薬品、autoboxedのparamsとnull値を含むのparamsのいずれかの種類をサポートしています。
@SuppressWarnings("unchecked")
public static <T> T executeSuperMethod(Object instance, String methodName, Object... params) throws Exception {
return executeMethod(instance.getClass().getSuperclass(), instance, methodName, params);
}
public static <T> T executeMethod(Object instance, String methodName, Object... params) throws Exception {
return executeMethod(instance.getClass(), instance, methodName, params);
}
@SuppressWarnings("unchecked")
public static <T> T executeMethod(Class clazz, Object instance, String methodName, Object... params) throws Exception {
Method[] allMethods = clazz.getDeclaredMethods();
if (allMethods != null && allMethods.length > 0) {
Class[] paramClasses = Arrays.stream(params).map(p -> p != null ? p.getClass() : null).toArray(Class[]::new);
for (Method method : allMethods) {
String currentMethodName = method.getName();
if (!currentMethodName.equals(methodName)) {
continue;
}
Type[] pTypes = method.getParameterTypes();
if (pTypes.length == paramClasses.length) {
boolean goodMethod = true;
int i = 0;
for (Type pType : pTypes) {
if (!ClassUtils.isAssignable(paramClasses[i++], (Class<?>) pType)) {
goodMethod = false;
break;
}
}
if (goodMethod) {
method.setAccessible(true);
return (T) method.invoke(instance, params);
}
}
}
throw new MethodNotFoundException("There are no methods found with name " + methodName + " and params " +
Arrays.toString(paramClasses));
}
throw new MethodNotFoundException("There are no methods found with name " + methodName);
}
メソッドautoboxedのparamsの互換性をチェックするためにApache ClassUtilsを使用する
もう一つの変形は非常に強力なJOORライブラリを使用している https://github.com/jOOQ/jOOR
MyObject myObject = new MyObject()
on(myObject).get("privateField");
これは、最終的な静的定数のような任意のフィールドを変更し、継承hierarhyに具象クラスを指定せずにYNE保護されたメソッドを呼び出すことができます。
<!-- https://mvnrepository.com/artifact/org.jooq/joor-java-8 -->
<dependency>
<groupId>org.jooq</groupId>
<artifactId>joor-java-8</artifactId>
<version>0.9.7</version>
</dependency>
あなたは直接、タイプのための @Jailbreak のマニホールドのに使用することができますJavaリフレクション-safeます:
@Jailbreak Foo foo = new Foo();
foo.callMe();
public class Foo {
private void callMe();
}
@Jailbreak
はfoo
の階層内のすべてのメンバーに直接アクセスするためのコンパイラでFoo
ローカル変数のロックを解除します。
同様に、あなたは一回限りの使用のための脱獄()の拡張メソッドを使用することができます
foo.jailbreak().callMe();
jailbreak()
方法を通じて、あなたはFoo
の階層内の任意のメンバーにアクセスすることができます。
両方の場合において、コンパイラは、メソッド呼び出しを解決します。
タイプが静的に知られていない場合は、あるいは、あなたは構造型付けを使用することができます型は、その実装を宣言しなくても満足させることができるインタフェースを定義します。この戦略は、型の安全性を維持し、反射やプロキシコードに関連するパフォーマンスとアイデンティティの問題を回避できます。
は、マニホールドの詳細を発見します。