Sécurité
Les accès à l'API de reflexion sont contrôlés
par un gestionnaire de sécurité. Une
application complètement sécurisée a accès
à toutes les fonctionnalités vues
précédemment ; elle peut accéder aux
membres de classes au niveau de restriction
accordé au code compris dans sa portée. Il
est toutefois possible de fournir des accès
privilégiés au code, afin qu'il puisse utiliser
l'API de réflexion et accéder à des
memebres protégés et privés d'autres
classes, ce qui est normalement interdit par
le langage Java.
Les classes Field,Method et Constructor sont
toutes des extensions de la classe de base
AccessibleObject. La classe AccessibleObject
possède une méthode fondamentale,
appelée setAccessible(), qui permet de
désactiver la sécurité d'accès. à un membre
particulier d'une classe. cela semble trop
facile. C'est effectivement facile, mais le fait
que cette méthode vous permettent ou pas
de désactiver la sécurité est une
fonctionnalité du gestionnaire de sécurité
Java et des règles associées. Vous pouvez le
faire uniquement dans une application Java
qui fonctionne sans aucune règle de
sécurité.
Récupération des valeurs primitives
La classe java.lang.reflect.Field représente
les attributs des objets. Field possède un jeu
complet de méthode d'accès surchargées
pour tous les types de base, par exemple
getInt() et setInt(),getBoolean() et
setBoolean() et des méthodes get() et set()
pour accéder à des attributs qui font
références à des objets.
import java.lang.reflect.*;
import static java.lang.System.*;
class Banque {
public int balance = 25;
}
public class Main {
public static void main(String[] args)
throws Exception {
Banque compte = new CompteBancaire
();
Field at = Banque.class.getField
("balance");
int balance = at.getInt(compte);
out.println("Balance = "+balance);
at.setInt(compte, 42);
out.println("Balance = "+at.getInt
(compte));
}
}
Dans cet exemple, nous sommes supposés
déjà connaître la structure de l'objet Banque.
En règle général, nous devrions pouvoir
récupérer cette information à partir de
l'objet même.
Toutes les données des méthodes d'accès de
Field prennent une référence sur l'objet
auquel nous voulons accéder. Dans le code
ci-dessus, la méthode getField() renvoie un
objet Field représentant l'attribut balance de
la classe Banque ; cet objet Field ne fait pas
référence à un objet Banque particulier. Par
conséquent, pour lire ou modifier un objet de
type Banque spécifique, nous appelons
getInt() et setInt() avec une référence à
compte, qui est le compte précis sur lequel
nous désirons travailler.
Bien entendu, il est facile de lire le contenu d'un champ spécifique d'un objet dont le nom et le type sont connus
lors de l'écriture du programme. Mais la réflexion permet de lire les attributs des objets qui n'étaient pas connus
au moment de la compilation.
Récupération des valeurs des attributs par la méthode get()
A cet égard, la méthode essentielle est la méthode get() de la classe Field. Si attribut est un objet de type Field
obtenu au moyen de la méthode getDeclaredFields() et moi un objet de la classe dont attribut est un attribut,
alors attribut.get(moi)renvoie un objet dont la valeur est la valeur courante de l'attribut de l'objet moi :
package test;
import java.lang.reflect.*;
public class Main {
public static void main(String[] args) throws Exception {
Personne moi = new Personne("REMY", "Emmanuel", 46);
Class classe = moi.getClass();
Field attribut = classe.getDeclaredField("nom");
Object valeur = attribut.get(moi);
System.out.println(valeur);
}
}
class Personne {
private String nom, prénom;
private int âge;
public Personne(String nom, String prénom, int âge) {
this.nom = nom;
this.prénom = prénom;
this.âge = âge;
}
}
// Résultat : Exception de type IllegalAccessException
En réalité, ce code pose un problème. Comme l'attribut moi est privé, la méthode get() déclenche une exception
IllegalAccessException. Cette méthode ne peut être employée que pour obtenir les valeurs des champs
accessibles. Le mécanisme de sécurité Java vous permet de connaître les champs d'un objet, mais pas de lire la
valeur de ces champs si vous n'avez pas une autorisation d'accès.
Accessibilité des attributs
Par défaut, le mécanisme de réflexion respecte le contrôle des accès. Néanmoins, si un programme Java n'est pas
contrôlé par un gestionnaire de sécurité qui le lui interdit, il peut outrepasser son droit d'accès. Pour cela, il faut
invoquer la méthode setAccessible() d'un objet Field,Method ou Constructor :
attribut.setAccessible(true);
La méthode setAccessible() se trouve dans la classe AccessibleObject, qui est la superclasse commune des
classes Field,Method ou Constructor. Cette fonctionnalité est destinée au débogage, au stockage permanent et à
des mécanismes similaires.
package test;
import java.lang.reflect.*;
public class Main {
public static void main(String[] args) throws Exception {
Personne moi = new Personne("REMY", "Emmanuel", 46);
Class classe = moi.getClass();
Field attribut = classe.getDeclaredField("nom");
attribut.setAccessible(true);
Object valeur = attribut.get(moi);
System.out.println(valeur);
}
}
class Personne {
private String nom, prénom;
private int âge;
public Personne(String nom, String prénom, int âge) {
this.nom = nom;
this.prénom = prénom;
this.âge = âge;
}
}
// Résultat : REMY
Récupération des valeurs de type primitif
La méthode get() pose un second problème. Dans notre exemple, l'attribut nom est de type String, il est donc
possible de récupérer la valeur en tant que Object. Mais supposons que nous désirions étudier l'attribut âge.
Celui-ci est de type primitif int, et les nombres ne sont pas des objets en Java. Il existe deux solutions :
1. La première consiste à utiliser la méthode getInt() de la classe Field ;
2. la seconde est un appel à get(), car le mécanisme de réflexion enveloppe automatiquement la valeur du champ
dans la classe enveloppe appropriée, en l'occurence Integer.
L'exemple ci-dessous permet d'exploiter ces deux possibilités, ou même de travailler avec la classe de base
Object :
package test;
import java.lang.reflect.*;
public class Main {
public static void main(String[] args) throws Exception {
Personne moi = new Personne("REMY", "Emmanuel", 46);
Class classe = moi.getClass();
Field attribut = classe.getDeclaredField("âge");
attribut.setAccessible(true);
Object objet = attribut.get(moi); // première solution
System.out.println(objet);
int entier = (Integer) attribut.get(moi); // deuxième solution
System.out.println(entier);
int valeur = attribut.getInt(moi); // troisième solution