Rechercher
 

Améliorations / Corrections

Vous avez des améliorations (ou des corrections) à proposer pour ce document : je vous remerçie par avance de m'en faire part, cela m'aide à améliorer le site.

Emplacement :

Description des améliorations :

JavaBeans et l'introspection

Coder un moteur de sérialisation Introduction à l'utilisation d'annotations



Accès rapide :
Qu'est-ce que l'introspection Java ?
Les conventions JavaBeans
Exemple d'utilisation du moteur d'introspection
Lister les propriétés supportées par la classe
Lister les méthodes des différents gestionnaires d'événements (les listeners) supportées par la classe
Création d'un composant Swing d'affichage de propriétés et de listeners pour un Java Bean
Mise en place des deux composants de types JTables
Intégration des deux tables dans un conteneur d'onglets
Intégration du composant dans une fenêtre graphique

Qu'est-ce que l'introspection Java ?

En Java, l'introspection complète les possibilités offertes par le moteur de réflexion. Ce principe d'introspection est aussi en lien avec le modèle de composants JavaBeans. Un composant JavaBean est une classe Java qui expose des propriétés et des gestionnaires d'événements (aussi appelés « listeners ») en respectant les conventions de codages « JavaBeans ».

De nombreux frameworks Java sont basés sur cette notion d'introspection. Citons, entre autres, Spring Framework, JSF (Java Server Faces) et plus généralement la plate-forme Jakarta EE (anciennement Java EE).

Le moteur d'introspection est disponible à partir du package java.beans. Il s'appui sur le moteur de réflexion, lui-même localisé dans le package java.lang.reflect.

Les conventions JavaBeans

La convention de codage « JavaBeans » définie les règles suivantes :

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
JTextField txtFirstName = new JTextField();
txtFirstName.addFocusListener( new FocusListener() {

    @Override
    public void focusGained( FocusEvent event ) {
        System.out.println( "Prise de focus pour la zone de saisie de texte." );
    }
    
    @Override
    public void focusLost( FocusEvent event ) {
        System.out.println( "Perte de focus pour la zone de saisie de texte." );
    } 

} );
Exemple d'enregistrement d'un listener.
la dernière partie sur les listeners (les gestionnaires d'événements) est peu plus compliqué. Si vous souhaitez mieux maîtriser ce sujet, je vous conseille de consulter les premiers chapitres du tutoriel sur la mise en oeuvre d'IHM via l'API Swing.

Le fait de respecter ces conventions permettra à des outils (ou des framework) de trouver automatiquement, par introspection, la liste des propriétés exposées ainsi que la liste des gestionnaires d'événements supportés. Imaginez par exemple un panneau latéral permettant de configurer des composants graphiques dans un éditeur d'interfaces graphiques.

Exemple d'utilisation du moteur d'introspection

Les deux principaux types du moteur d'introspection sont la classe java.beans.Introspector et l'interface java.beans.BeanInfo. La première classe proposée correspond au point d'entrée sur le moteur d'introspection alors de la seconde interface correspond à un ensemble de méta-données associées à un type de JavaBean.

Voici un premier exemple de code permettant de récupérer une instance de BeanInfo associée à un JavaBean.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
package fr.koor.introspection;

import java.beans.BeanInfo;
import java.beans.Introspector;

import javax.swing.JButton;

public class BeanInfoSample {

    public static void main( String[] args ) throws Exception {
        
        BeanInfo beanInfo = Introspector.getBeanInfo( JButton.class );
        System.out.println( beanInfo );
        
    }
    
}
Récupération d'un BeanInfo à partir d'un composant JavaBean.

Une instance de BeanInfo permet de lister l'ensemble des propriétés et des listeners supportés par la classe.

Lister les propriétés supportées par la classe

Lister les propriétés d'une classe respectant les conventions « JavaBeans » pourrait se faire via le moteur de réflexion Java : il suffirait de lister toutes les méthodes publiques et de ne garder que celles commençant par get, set ou is. Ensuite, il faudrait chercher à regrouper les éventuelles paires de méthodes. Même si cela reste facile à réaliser, l'emploi du moteur d'introspection sera plus simple, car il réalise ce travail pour vous.

le moteur d'introspection Java recherche bien les propriétés disponibles dans la classe considérée, mais aussi dans ses classes parentes.

Voici un exemple de code qui parcourt l'ensemble des propriétés disponibles sur la classe java.swing.JButton.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
 22 
 23 
 24 
 25 
 26 
package fr.koor.introspection;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;

import javax.swing.JButton;

public class BeanInfoSample {

    public static void main( String[] args ) throws Exception {
        
        BeanInfo beanInfo = Introspector.getBeanInfo( JButton.class );
        PropertyDescriptor [] properties = beanInfo.getPropertyDescriptors();
        for ( PropertyDescriptor propertyDescriptor : properties ) {
            System.out.printf( "%-50s %-30s %c/%c\n",
                propertyDescriptor.getPropertyType(),
                propertyDescriptor.getName(),
                propertyDescriptor.getReadMethod() != null ? 'R' : '-',
                propertyDescriptor.getWriteMethod() != null ? 'W' : '-'        
            );
        }
        
    }
    
}
Parcourts des propriétés de la classe JButton.

Et voici les résultats produits par cet exemple :

class javax.swing.plaf.ButtonUI                    UI                             R/W
class java.lang.String                             UIClassID                      R/-
class javax.accessibility.AccessibleContext        accessibleContext              R/-
interface javax.swing.Action                       action                         R/W
class java.lang.String                             actionCommand                  R/W
class [Ljava.awt.event.ActionListener;             actionListeners                R/-
class javax.swing.ActionMap                        actionMap                      R/W
float                                              alignmentX                     R/W
float                                              alignmentY                     R/W
class [Ljavax.swing.event.AncestorListener;        ancestorListeners              R/-
boolean                                            autoscrolls                    R/W
class java.awt.Color                               background                     R/W
class java.awt.Component$BaselineResizeBehavior    baselineResizeBehavior         R/-
interface javax.swing.border.Border                border                         R/W
boolean                                            borderPainted                  R/W
class [Ljavax.swing.event.ChangeListener;          changeListeners                R/-
null                                               component                      -/-
int                                                componentCount                 R/-
class javax.swing.JPopupMenu                       componentPopupMenu             R/W
class [Ljava.awt.Component;                        components                     R/-
class [Ljava.awt.event.ContainerListener;          containerListeners             R/-
boolean                                            contentAreaFilled              R/W
int                                                debugGraphicsOptions           R/W
boolean                                            defaultButton                  R/-
boolean                                            defaultCapable                 R/W
interface javax.swing.Icon                         disabledIcon                   R/W
interface javax.swing.Icon                         disabledSelectedIcon           R/W
int                                                displayedMnemonicIndex         R/W
boolean                                            doubleBuffered                 R/W
boolean                                            enabled                        R/W
boolean                                            focusCycleRoot                 R/W
boolean                                            focusPainted                   R/W
null                                               focusTraversalKeys             -/-
class java.awt.FocusTraversalPolicy                focusTraversalPolicy           R/W
boolean                                            focusTraversalPolicyProvider   R/W
boolean                                            focusTraversalPolicySet        R/-
boolean                                            focusable                      R/W
class java.awt.Font                                font                           R/W
class java.awt.Color                               foreground                     R/W
class java.awt.Graphics                            graphics                       R/-
int                                                height                         R/-
boolean                                            hideActionText                 R/W
int                                                horizontalAlignment            R/W
int                                                horizontalTextPosition         R/W
interface javax.swing.Icon                         icon                           R/W
int                                                iconTextGap                    R/W
boolean                                            inheritsPopupMenu              R/W
class javax.swing.InputMap                         inputMap                       R/-
class javax.swing.InputVerifier                    inputVerifier                  R/W
class java.awt.Insets                              insets                         R/-
class [Ljava.awt.event.ItemListener;               itemListeners                  R/-
class java.lang.String                             label                          R/W
interface java.awt.LayoutManager                   layout                         R/W
boolean                                            managingFocus                  R/-
class java.awt.Insets                              margin                         R/W
class java.awt.Dimension                           maximumSize                    R/W
class java.awt.Dimension                           minimumSize                    R/W
int                                                mnemonic                       R/W
interface javax.swing.ButtonModel                  model                          R/W
long                                               multiClickThreshhold           R/W
class java.lang.String                             name                           R/W
class java.awt.Component                           nextFocusableComponent         R/W
boolean                                            opaque                         R/W
boolean                                            optimizedDrawingEnabled        R/-
boolean                                            paintingForPrint               R/-
boolean                                            paintingTile                   R/-
class java.awt.Dimension                           preferredSize                  R/W
interface javax.swing.Icon                         pressedIcon                    R/W
class [Ljavax.swing.KeyStroke;                     registeredKeyStrokes           R/-
boolean                                            requestFocusEnabled            R/W
boolean                                            rolloverEnabled                R/W
interface javax.swing.Icon                         rolloverIcon                   R/W
interface javax.swing.Icon                         rolloverSelectedIcon           R/W
class javax.swing.JRootPane                        rootPane                       R/-
boolean                                            selected                       R/W
interface javax.swing.Icon                         selectedIcon                   R/W
class [Ljava.lang.Object;                          selectedObjects                R/-
class java.lang.String                             text                           R/W
class java.lang.String                             toolTipText                    R/W
class java.awt.Container                           topLevelAncestor               R/-
class javax.swing.TransferHandler                  transferHandler                R/W
boolean                                            validateRoot                   R/-
boolean                                            verifyInputWhenFocusTarget     R/W
int                                                verticalAlignment              R/W
int                                                verticalTextPosition           R/W
class [Ljava.beans.VetoableChangeListener;         vetoableChangeListeners        R/-
boolean                                            visible                        R/W
class java.awt.Rectangle                           visibleRect                    R/-
int                                                width                          R/-
int                                                x                              R/-
int                                                y                              R/-

Lister les méthodes des différents gestionnaires d'événements (les listeners) supportées par la classe

De même, la recherche des gestionnaires d'événements supportés par une classe respectant les conventions « JavaBeans » pourrait être réalisée via le moteur de réflexion Java en filtrant, dans les méthodes publiques, celles commençant par le préfixe add et se terminant par le suffixe Listener. Mais, encore une fois, le moteur d'introspection fait cela pour vous : il serait dommage de ne pas y faire appel.

Voici un exemple de code listant toutes les méthodes de gestion d'événements. Ces méthodes sont portées par les différentes interfaces d'écoute. On manipule ces méthodes via le moteur de réflexion.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
 22 
 23 
 24 
 25 
 26 
 27 
 28 
package fr.koor.introspection;

import java.beans.BeanInfo;
import java.beans.EventSetDescriptor;
import java.beans.Introspector;
import java.lang.reflect.Method;

import javax.swing.JButton;

public class BeanInfoSample {

    public static void main( String[] args ) throws Exception {
        
        BeanInfo beanInfo = Introspector.getBeanInfo( JButton.class );
        EventSetDescriptor [] eventSetDescriptors = beanInfo.getEventSetDescriptors();
        for ( EventSetDescriptor eventSetDescriptor : eventSetDescriptors ) {
            String addMethodName = eventSetDescriptor.getAddListenerMethod().getName();
            String associatedInterface = eventSetDescriptor.getListenerType().toString();
            System.out.printf( "%s [with %s]\n", addMethodName, associatedInterface );
            Method [] methods = eventSetDescriptor.getListenerMethods();
            for( Method method : methods ) {
                System.out.println( "\t" + method.toString() );
            }
        }
        
    }
    
}
Parcourt des différents gestionnaires d'événements (les listeners) supportées par la classe JButton.

Et voici les résultats produits par cet exemple :

addContainerListener [with interface java.awt.event.ContainerListener]
    public abstract void java.awt.event.ContainerListener.componentAdded(java.awt.event.ContainerEvent)
    public abstract void java.awt.event.ContainerListener.componentRemoved(java.awt.event.ContainerEvent)
addItemListener [with interface java.awt.event.ItemListener]
    public abstract void java.awt.event.ItemListener.itemStateChanged(java.awt.event.ItemEvent)
addHierarchyListener [with interface java.awt.event.HierarchyListener]
    public abstract void java.awt.event.HierarchyListener.hierarchyChanged(java.awt.event.HierarchyEvent)
addAncestorListener [with interface javax.swing.event.AncestorListener]
    public abstract void javax.swing.event.AncestorListener.ancestorAdded(javax.swing.event.AncestorEvent)
    public abstract void javax.swing.event.AncestorListener.ancestorRemoved(javax.swing.event.AncestorEvent)
    public abstract void javax.swing.event.AncestorListener.ancestorMoved(javax.swing.event.AncestorEvent)
addChangeListener [with interface javax.swing.event.ChangeListener]
    public abstract void javax.swing.event.ChangeListener.stateChanged(javax.swing.event.ChangeEvent)
addMouseMotionListener [with interface java.awt.event.MouseMotionListener]
    public abstract void java.awt.event.MouseMotionListener.mouseMoved(java.awt.event.MouseEvent)
    public abstract void java.awt.event.MouseMotionListener.mouseDragged(java.awt.event.MouseEvent)
addFocusListener [with interface java.awt.event.FocusListener]
    public abstract void java.awt.event.FocusListener.focusGained(java.awt.event.FocusEvent)
    public abstract void java.awt.event.FocusListener.focusLost(java.awt.event.FocusEvent)
addMouseWheelListener [with interface java.awt.event.MouseWheelListener]
    public abstract void java.awt.event.MouseWheelListener.mouseWheelMoved(java.awt.event.MouseWheelEvent)
addHierarchyBoundsListener [with interface java.awt.event.HierarchyBoundsListener]
    public abstract void java.awt.event.HierarchyBoundsListener.ancestorMoved(java.awt.event.HierarchyEvent)
    public abstract void java.awt.event.HierarchyBoundsListener.ancestorResized(java.awt.event.HierarchyEvent)
addMouseListener [with interface java.awt.event.MouseListener]
    public abstract void java.awt.event.MouseListener.mousePressed(java.awt.event.MouseEvent)
    public abstract void java.awt.event.MouseListener.mouseReleased(java.awt.event.MouseEvent)
    public abstract void java.awt.event.MouseListener.mouseClicked(java.awt.event.MouseEvent)
    public abstract void java.awt.event.MouseListener.mouseExited(java.awt.event.MouseEvent)
    public abstract void java.awt.event.MouseListener.mouseEntered(java.awt.event.MouseEvent)
addComponentListener [with interface java.awt.event.ComponentListener]
    public abstract void java.awt.event.ComponentListener.componentResized(java.awt.event.ComponentEvent)
    public abstract void java.awt.event.ComponentListener.componentMoved(java.awt.event.ComponentEvent)
    public abstract void java.awt.event.ComponentListener.componentShown(java.awt.event.ComponentEvent)
    public abstract void java.awt.event.ComponentListener.componentHidden(java.awt.event.ComponentEvent)
addInputMethodListener [with interface java.awt.event.InputMethodListener]
    public abstract void java.awt.event.InputMethodListener.inputMethodTextChanged(java.awt.event.InputMethodEvent)
    public abstract void java.awt.event.InputMethodListener.caretPositionChanged(java.awt.event.InputMethodEvent)
addActionListener [with interface java.awt.event.ActionListener]
    public abstract void java.awt.event.ActionListener.actionPerformed(java.awt.event.ActionEvent)
addPropertyChangeListener [with interface java.beans.PropertyChangeListener]
    public abstract void java.beans.PropertyChangeListener.propertyChange(java.beans.PropertyChangeEvent)
addKeyListener [with interface java.awt.event.KeyListener]
    public abstract void java.awt.event.KeyListener.keyTyped(java.awt.event.KeyEvent)
    public abstract void java.awt.event.KeyListener.keyPressed(java.awt.event.KeyEvent)
    public abstract void java.awt.event.KeyListener.keyReleased(java.awt.event.KeyEvent)
addVetoableChangeListener [with interface java.beans.VetoableChangeListener]
    public abstract void java.beans.VetoableChangeListener.vetoableChange(java.beans.PropertyChangeEvent) throws java.beans.PropertyVetoException

Création d'un composant Swing d'affichage de propriétés et de listeners pour un Java Bean

Bon, pour cette section, je m'aventure un peu loin : si vous avez suivi ce tuto dans l'ordre de lecture, vous n'avez pas encore vu la programmation graphique via la librairie Swing. Pour autant, je vais tenter de proposer un code, basé sur cette librairie, qui affiche les propriétés et les événements supportés par un composant graphique quelconque. Il faut savoir que par défaut, les composants graphiques Swing respectent la convention « JavaBeans ». Bien entendu, nous allons nous baser sur les codes présentés précédemment. Voici une capture d'écran montrant le panneau de propriétés d'un composant de type JButton.

Mise en place des deux composants de types JTables

Pour afficher les propriétés et les gestionnaires d'événements, nous allons utiliser deux composants de type javax.swing.JTable. Un JTable permet d'afficher des données dans une table en utilisant un pattern MVC (Model-View-Controller). Le modèle de données de la table doit être compatible avec l'interface TableModel : la classe abstraite AbstractTableModel étant compatible avec cette interface. Un modèle de données permet de retrouver le nombre de colonnes et de lignes de la table ainsi que les valeurs de chaque cellule (y compris pour les cellules de titres).

Chaque table pourra recevoir un nouvel objet à introspecter : à chaque changement d'instance, un nouveau modèle de données sera produit. Voici le code pour la table d'affichage du panneau de propriétés.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
 22 
 23 
 24 
 25 
 26 
 27 
 28 
 29 
 30 
 31 
 32 
 33 
 34 
 35 
 36 
 37 
 38 
 39 
 40 
 41 
 42 
 43 
 44 
 45 
 46 
 47 
 48 
 49 
 50 
 51 
 52 
 53 
 54 
 55 
 56 
 57 
 58 
 59 
 60 
 61 
 62 
 63 
 64 
 65 
 66 
 67 
 68 
 69 
 70 
 71 
 72 
 73 
 74 
 75 
 76 
 77 
 78 
 79 
 80 
 81 
 82 
 83 
 84 
 85 
 86 
 87 
 88 
 89 
 90 
 91 
package fr.koor.introspection;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;

import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;

// Cette classe est un JScrollPane, pour avoir des barres de scrolling,
// qui contient un composant JTable.
public class BeanPropertiesTable extends JScrollPane {

    // Le composant JTable affichant les propriétés
    private JTable table;
    // Le bean sur lequel réaliser l'introspection
    private Object instance;
    
    public BeanPropertiesTable() {
        // On crée le JScrollPane par-dessus un JTable
        super( new JTable() );
        // On récupère le composant JTable
        this.table = (JTable) this.getViewport().getView();
    }
    
    public Object getInstance() {
        return instance;
    }
    
    // Si on change l'instance à introspecter alors on produit un nouveau modèle.
    public void setInstance( Object instance ) {
        this.instance = instance;
        // Le modèle (la source de données) utilisé par la table 
        // doit être compatible avec le type AbstractTableModel.
        this.table.setModel( new AbstractTableModel() {

            private Class<?> metadata;
            private PropertyDescriptor [] propertyDescriptors;
            
            // Le constructeur de la classe anonyme
            {
                try {
                    metadata = instance.getClass();
                    BeanInfo beanInfo = Introspector.getBeanInfo( metadata );
                    propertyDescriptors = beanInfo.getPropertyDescriptors();
                } catch( Exception exception ) {
                    System.err.println( "Cannot access bean properties" );
                }
            }
            
            @Override public int getRowCount() {
                return propertyDescriptors.length;
            }
            
            @Override public int getColumnCount() {
                return 2;       // property name / property value
            }

            @Override public Object getValueAt( int rowIndex, int columnIndex ) {
                switch( columnIndex ) {
                    case 0:
                        return propertyDescriptors[rowIndex].getName();
                    case 1:
                        try {
                            return "" + propertyDescriptors[rowIndex].getReadMethod().invoke( instance );
                        } catch ( Exception exception ) {
                            return "";
                        }
                    default:
                        throw new RuntimeException( "Bad column index" );
                    
                }
            }
            
            @Override
            public String getColumnName( int columnIndex ) {
                switch( columnIndex ) {
                    case 0:
                        return "Property name";
                    case 1:
                        return "Value";
                    default:
                        throw new RuntimeException( "Bad column index" );
                    
                }
            }
            
        } );
    }
}
Le code de la table associée au panneau de propriétés.

Et voici le code de la table associée aux gestionnaires d'événements.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
 22 
 23 
 24 
 25 
 26 
 27 
 28 
 29 
 30 
 31 
 32 
 33 
 34 
 35 
 36 
 37 
 38 
 39 
 40 
 41 
 42 
 43 
 44 
 45 
 46 
 47 
 48 
 49 
 50 
 51 
 52 
 53 
 54 
 55 
 56 
 57 
 58 
 59 
 60 
 61 
 62 
 63 
 64 
 65 
 66 
 67 
 68 
 69 
 70 
 71 
 72 
 73 
 74 
 75 
 76 
 77 
 78 
 79 
 80 
 81 
 82 
 83 
 84 
 85 
 86 
 87 
 88 
 89 
 90 
 91 
 92 
 93 
 94 
 95 
 96 
 97 
 98 
 99 
package fr.koor.introspection;

import java.beans.BeanInfo;
import java.beans.EventSetDescriptor;
import java.beans.Introspector;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;

//Cette classe est un JScrollPane, pour avoir des barres de scrolling,
//qui contient un composant JTable.
public class BeanEventsTable extends JScrollPane {

    // Le composant JTable affichant les gestionnaires d'événements
    private JTable table;
    // Le bean sur lequel réaliser l'introspection
    private Object instance;
    
    public BeanEventsTable() {
        // On crée le JScrollPane par-dessus un JTable
        super( new JTable() );
        // On récupère le composant JTable
        this.table = (JTable) this.getViewport().getView();
    }
    
    public Object getInstance() {
        return instance;
    }
    
    // Si on change l'instance à introspecter alors on produit un nouveau modèle.
    public void setInstance( Object instance ) {
        this.instance = instance;
        // Le modèle (la source de données) utilisé par la table 
        // doit être compatible avec le type AbstractTableModel.
        this.table.setModel( new AbstractTableModel() {

            private Class<?> metadata;
            private List<String> eventListenerMethods = new ArrayList<>();
            
            // Le constructeur de la classe anonyme
            {
                try {
                    metadata = instance.getClass();
                    BeanInfo beanInfo = Introspector.getBeanInfo( metadata );
                    EventSetDescriptor [] eventSetDescriptors = beanInfo.getEventSetDescriptors();
                    for ( EventSetDescriptor eventSetDescriptor : eventSetDescriptors ) {
                        Method [] methods = eventSetDescriptor.getListenerMethods();
                        for( Method method : methods ) {
                            eventListenerMethods.add( method.getName() );
                        }
                    }
                    // On veut les méthodes présentées dans l'ordre alphabétique.
                    eventListenerMethods.sort( (l1, l2) -> l1.compareTo( l2 ) );
                } catch( Exception exception ) {
                    System.err.println( "Cannot access bean listeners" );
                }
            }
            
            @Override public int getRowCount() {
                return eventListenerMethods.size();
            }
            
            @Override public int getColumnCount() {
                return 2;       // property name / property value
            }

            @Override public Object getValueAt( int rowIndex, int columnIndex ) {
                switch( columnIndex ) {
                    case 0:
                        return eventListenerMethods.get( rowIndex );
                    case 1:
                        return "";
                    default:
                        throw new RuntimeException( "Bad column index" );
                    
                }
            }
            
            @Override
            public String getColumnName( int columnIndex ) {
                switch( columnIndex ) {
                    case 0:
                        return "Event";
                    case 1:
                        return "Method";
                    default:
                        throw new RuntimeException( "Bad column index" );
                    
                }
            }
            
        } );
    
    }
}
Le code de la table associée aux gestionnaires d'événements.
vous pouvez poursuivre l'implémentation de cette classe afin d'afficher les gestionnaires d'événements effectivement connectés à l'objet en cours d'introspection. Pour ma part, j'ai botté en touche.

Intégration des deux tables dans un conteneur d'onglets

Pour afficher des onglets en Swing, on utilise la classe javax.swing.JTabbedPane. On y ajoute un composant Swing par onglet : dans notre cas nos tables précédemment développées. Voici le code du composant assemblé.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
 22 
 23 
 24 
 25 
 26 
 27 
 28 
 29 
 30 
 31 
package fr.koor.introspection;

import javax.swing.JTabbedPane;

public class BeanViewer extends JTabbedPane {
    
    private BeanPropertiesTable propertiesTable = new BeanPropertiesTable();
    private BeanEventsTable eventsTable = new BeanEventsTable();
    private Object instance;
    
    public BeanViewer() {
        // On place les onglets en bas
        this.setTabPlacement( JTabbedPane.BOTTOM );
        
        // On ajoute les deux JTable
        this.add( "Properties", propertiesTable );
        this.add( "Event listeners", eventsTable );
    }
    
    public Object getInstance() {
        return instance;
    }
    
    // En cas de changement d'instance à introspecter, on la transfert aux sous-tables.
    public void setInstance( Object instance ) {
        this.instance = instance;
        this.propertiesTable.setInstance( instance );
        this.eventsTable.setInstance( instance );
    }
    
}
Le composant assemblé (avec les deux composants JTable)

Intégration du composant dans une fenêtre graphique

Pour finir, on crée une fenêtre de test qui intègre notre composant. On connecte notre explorateur de beans sur une instance de JButton. Voici le code de cette fenêtre.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
 22 
 23 
 24 
 25 
 26 
 27 
 28 
 29 
 30 
 31 
 32 
 33 
 34 
 35 
 36 
 37 
 38 
package fr.koor.introspection;

import java.awt.Dimension;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
import javax.swing.UIManager;
import javax.swing.plaf.nimbus.NimbusLookAndFeel;

public class Start extends JFrame {
    
    private BeanViewer beanViewer = new BeanViewer();
    private JLabel rightPart = new JLabel( "Imagine a graphical editor here!" );
    
    public Start() {
        super( "Bean viewer" );
        
        JPanel contentPane = (JPanel) this.getContentPane();
        contentPane.add( new JSplitPane( JSplitPane.HORIZONTAL_SPLIT, beanViewer, rightPart ) );

        beanViewer.setPreferredSize( new Dimension( 340, 0 ) );
        beanViewer.setInstance( new JButton( "Click me!" ) );
        rightPart.setHorizontalAlignment( JLabel.CENTER );
        
        this.setSize( 800, 400 );
        this.setLocationRelativeTo( null );
        this.setDefaultCloseOperation( DISPOSE_ON_CLOSE );
        this.setVisible( true );
    }

    public static void main( String[] args ) throws Exception {
        UIManager.setLookAndFeel( new NimbusLookAndFeel() );
        new Start();
    }
}
On intègre notre explorateur de beans dans une fenêtre.
pour de plus amples informations sur la librairie Swing, je vous renvoie sur mon tutoriel Swing.


Coder un moteur de sérialisation Introduction à l'utilisation d'annotations