web-dev-qa-db-fra.com

Comment accéder aux méthodes privées et aux membres de données privées via la réflexion?

Je sais que nous pouvons accéder à un constructeur privé via une réflexion sous la forme @Sanjay T. Sharma mentionné dans sa réponse à ma question: “instanceof Void” renvoie-t-il toujours la valeur false?

Cependant, @duffymodit :

vous pouvez accéder à tout ce qui est privé avec réflexion: méthodes, constructeurs, membres de données, tout.

  1. Comment puis-je accéder aux méthodes privées et aux membres de données privées?
  2. Est-il possible d'accéder à une variable locale via la réflexion?
  3. Existe-t-il un moyen d'empêcher toute personne d'accéder aux constructeurs, méthodes et membres de données privés?
25
Eng.Fouad

1)Comment puis-je accéder aux méthodes privées et aux membres de données privées?

Vous pouvez le faire avec un peu d'aide de la méthode setAccessible(true):

class Dummy{
    private void foo(){
        System.out.println("hello foo()");
    }
    private int i = 10;
}

class Test{
    public static void main(String[] args) throws Exception {
        Dummy d = new Dummy();

        /*---  [INVOKING PRIVATE METHOD]  ---*/
        Method m = Dummy.class.getDeclaredMethod("foo");
        //m.invoke(d); // Exception Java.lang.IllegalAccessException
        m.setAccessible(true);//Abracadabra
        m.invoke(d); // Now it's OK

        /*---  [GETING VALUE FROM PRIVATE FIELD]  ---*/
        Field f = Dummy.class.getDeclaredField("i");
        //System.out.println(f.get(d)); // Not accessible now
        f.setAccessible(true); // Abracadabra
        System.out.println(f.get(d)); // Now it's OK

        /*---  [SETTING VALUE OF PRIVATE FIELD]  ---*/
        Field f2 = Dummy.class.getDeclaredField("i");
        //f2.set(d,20); // Not accessible now
        f2.setAccessible(true); // Abracadabra
        f2.set(d, 20); // Now it's OK
        System.out.println(f2.get(d));
    }
}

2)Est-il possible d'accéder à une variable locale via réflexion?

Les variables locales ne sont pas accessibles en dehors du bloc dans lequel elles ont été créées (quelqu'un pourrait dire que vous pouvez affecter une telle variable à un champ tel que field = localVariable; et accéder ultérieurement à un tel champ par réflexion, mais nous accéderons ainsi à lavaleur, pas le variable).

3)Y a-t-il un moyen d'empêcher qui que ce soit d'accéder aux constructeurs, méthodes et membres de données privés?

Je pense que pour constructors ou methods, vous pouvez utiliser stacktrace pour vérifier si elle a été invoquée par Reflection.
Pour les champs, je ne trouve pas de solution pour empêcher leur accès via la réflexion.

[AVERTISSEMENT: cela n’a été approuvé par personne. Je viens de l'écrire inspiré par votre question.]

class Dummy {
    private void safeMethod() {
        StackTraceElement[] st = new Exception().getStackTrace();
        // If a method was invoked by reflection, the stack trace would be similar
        // to something like this:
        /*
        Java.lang.Exception
            at package1.b.Dummy.safeMethod(SomeClass.Java:38)
            at Sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
            at Sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.Java:57)
            at Sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.Java:43)
        ->    at Java.lang.reflect.Method.invoke(Method.Java:601)
            at package1.b.Test.main(SomeClass.Java:65)
        */
        //5th line marked by "->" is interesting one so I will try to use that info

        if (st.length > 5 &&
            st[4].getClassName().equals("Java.lang.reflect.Method"))
            throw new RuntimeException("safeMethod() is accessible only by Dummy object");

        // Now normal code of method
        System.out.println("code of safe method");
    }

    // I will check if it is possible to normally use that method inside this class
    public void trySafeMethod(){
        safeMethod();
    }

    Dummy() {
        safeMethod();
    }
}

class Dummy1 extends Dummy {}

class Test {
    public static void main(String[] args) throws Exception {
        Dummy1 d1 = new Dummy1(); // safeMethod can be invoked inside a superclass constructor
        d1.trySafeMethod(); // safeMethod can be invoked inside other Dummy class methods
        System.out.println("-------------------");

        // Let's check if it is possible to invoke it via reflection
        Method m2 = Dummy.class.getDeclaredMethod("safeMethod");
        // m.invoke(d);//exception Java.lang.IllegalAccessException
        m2.setAccessible(true);
        m2.invoke(d1);
    }
}

Sortie de la méthode principale Test:

code of safe method
code of safe method
-------------------
Exception in thread "main" Java.lang.reflect.InvocationTargetException
    at Sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at Sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.Java:57)
    at Sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.Java:43)
    at Java.lang.reflect.Method.invoke(Method.Java:601)
    at package1.b.Test.main(MyClass2.Java:87)
Caused by: Java.lang.RuntimeException: method safeMethod() is accessible only by Dummy object
    at package1.b.Dummy.safeMethod(MyClass2.Java:54)
    ... 5 more
65
Pshemo
  1. En utilisant la méthode indiquée dans la réponse à laquelle vous êtes lié: setAccessible (true) , méthode de la superclasse Field, Constructor et Method.
  2. Non.
  3. Non, sauf si le code est exécuté dans une machine virtuelle que vous contrôlez, où vous installez un gestionnaire de sécurité. Mais si vous donnez un fichier jar à quelqu'un et qu'il utilise les classes de ce fichier jar, il pourra accéder à tout.
9
JB Nizet

Pour accéderun champ privé vous devrez appeler la méthode Class.getDeclaredField(String name) ou enter code here. Cochez ce code simple:

public class PrivateObject {

  private String privateString = null;

  public PrivateObject(String privateString) {
    this.privateString = privateString;
  }
}

PrivateObject privateObject = new PrivateObject("The Private Value");

Field privateStringField = PrivateObject.class.
            getDeclaredField("privateString");

privateStringField.setAccessible(true);

String fieldValue = (String) privateStringField.get(privateObject);
System.out.println("fieldValue = " + fieldValue

Pour accéder à une méthode privée, vous devrez appeler la méthode Class.getDeclaredMethod (nom de chaîne, Class [] paramètreTypes) ou Class.getDeclaredMethods ().

Cochez ce code simple: _

public class PrivateObject {

  private String privateString = null;

  public PrivateObject(String privateString) {
    this.privateString = privateString;
  }

  private String getPrivateString(){
    return this.privateString;
  }
}
PrivateObject privateObject = new PrivateObject("The Private Value");

Method privateStringMethod = PrivateObject.class.
        getDeclaredMethod("getPrivateString", null);

privateStringMethod.setAccessible(true);

String returnValue = (String)
        privateStringMethod.invoke(privateObject, null);

System.out.println("returnValue = " + returnValue);

Lisez les détails sur http://tutorials.jenkov.com/Java-reflection/private-fields-and-methods.html

3
VdeX

Exemple comme ci-dessous:

import Java.lang.reflect.Constructor;

import Java.lang.reflect.Field;

import Java.lang.reflect.InvocationTargetException;

import Java.lang.reflect.Method;

class Test
{
    private int a = 5;   // Private data member

    private void call(int n) // Private method
    {
        System.out.println("in call()  n: " + n);
    }
}

public class Sample
{
    public static void main(String args[]) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchFieldException
    {
        Class c = Class.forName("Test");

        Object obj = c.newInstance();

        //---- Accessing a private method
        Method m=c.getDeclaredMethod("call",new Class[]{int.class});
        m.setAccessible(true);
        m.invoke(obj,7);

       //---- Accessing a private data member
       Field d = c.getDeclaredField("a");
       d.setAccessible(true);
       System.out.println(d.getInt(obj));
    }
}
1
 Area s=(Area)c.newInstance();  
 s.setRadius(10);
 System.out.println("Area: "+s.calculateArea(4));

 Method m[]=c.getDeclaredMethods(); 
  Constructor c1[]=c.getConstructors();  

 for(int i=0;i<m.length;i++)
     System.out.println(""+m[i]);

 for(int i=0;i<c1.length;i++)
     System.out.println(""+c1[i]);
1
Om Deshmukh

Pour répondre à votre troisième question:

  1. Existe-t-il un moyen d'empêcher toute personne d'accéder aux constructeurs, méthodes et membres de données privés?

Réponse:

Oui, vous pouvez restreindre l'accès (vous pouvez générer une exception lorsque quelqu'un tente d'accéder à votre constructeur/méthode/données privé)

Reportez-vous à l'exemple ci-dessous:

******JavaSingleton Class******

package server;

public class JavaSingleton {

  private static final JavaSingleton INSTANCE = new JavaSingleton();

  private JavaSingleton() {
    if (INSTANCE != null) {
      throw new IllegalStateException("Inside JavaSingleton(): JavaSingleton " +
                                                        "instance already created.");
    }
    System.out.println("Inside JavaSingleton(): Singleton instance is being created.");
  }

  public static final JavaSingleton getInstance() {
    return INSTANCE;
  }
}


***Listing 2: JavaSingleton client***

import server.JavaSingleton;
import Java.lang.reflect.*;

public class TestSingleton {

  public static void main(String[] args) throws ReflectiveOperationException {
    System.out.println("Inside main(): Getting the singleton instance using getInstance()...");
    JavaSingleton s = JavaSingleton.getInstance();

    System.out.println("Inside main(): Trying to use reflection to get another instance...");
    Class<JavaSingleton> clazz = JavaSingleton.class;
    Constructor<JavaSingleton> cons = clazz.getDeclaredConstructor();
    cons.setAccessible(true);
    JavaSingleton s2 = cons.newInstance();
  }
}

Output:

C:\singleton>Java TestSingleton
Inside main(): Getting the singleton instance using getInstance()...
Inside JavaSingleton(): Singleton instance is being created.
Inside main(): Trying to use reflection to get another instance...
Exception in thread "main" Java.lang.reflect.InvocationTargetException
  at Sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
  at Sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
  at Sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
  at Java.lang.reflect.Constructor.newInstance(Unknown Source)
  at TestSingleton.main(TestSingleton.Java:13)
Caused by: Java.lang.IllegalStateException: Inside JavaSingleton(): JavaSingleton instance already created.
  at server.JavaSingleton.<init>(JavaSingleton.Java:7)
  ... 5 more

Cet exemple concernait une classe singleton (enregistrement du constructeur), mais vous pouvez toujours implémenter cette logique pour les méthodes privées que vous souhaitez empêcher l'accès à partir d'autres classes.

Dans ce cas, vous déclarerez également une instance statique, en vérifier la valeur dans la méthode privée et émettez une erreur en cas de valeur non souhaitée.

0
Onkar Nevgi