JDK 1.1 was released on February 18, 1997 and JDK 1.1.1 was released on March 27, 1997. This lesson was originally written on April 10, 1997 using the software and documentation in the JDK 1.1.1 download package along with the April 97 release of the BDK 1.0 download package.
java.beans.Introspector. |
The Introspector class contains two overloaded versions of a single method that can be used to analyze the target bean's class and superclasses looking either for explicit or implicit information. The information discovered is used to build and return an object of type BeanInfo that describes the target bean. Once the object of type BeanInfo is available, a variety of methods are available to extract specific information about the bean from that object.
The programmer can elect provide explicit information about a bean or can rely on automatic low-level reflection and the recognition of design patterns. The explicit approach will be briefly covered here, and discussed in more detail in a subsequent lesson.
The methods of the Introspector class use low-level reflection techniques in the analysis of the bean. Low-level reflection techniques were studied in an earlier lesson.
The primary method of the Introspector class used to analyze a bean is the getBeanInfo() method. Simply put, this method takes a target Class object as a parameter and returns a BeanInfo object containing information about the target class. The BeanInfo class contains a number of methods that can be used to extract the different elements of information from the BeanInfo object.
There are two versions of the getBeanInfo() method. The method which accepts only one parameter returns information about the target class and all its superclasses.
Another version accepts a second Class object as a parameter and uses that class as a ceiling for introspection up the inheritance hierarchy. For example, if this second class is the direct superclass of the primary target class, only information about the primary target class is returned.
One of the goals of the Java designers was to avoid the requirement for the use of a separate specification language for defining the behavior of a bean. Their goal was to make its behavior completely specifiable in Java.
According to JavaSoft:
"A key goal of Java Beans is to make it very easy to write simple components and to provide default implementations for most common tasks." |
Remember these three: properties, events, and methods. The object of introspection is to gather information about these the exposed properties, events, and methods of a bean.
This has resulted in a composite mechanism. The default case is to use low-level reflection to analyze the methods supported by a bean and then to apply design patterns to determine from the methods the specific events, properties, and public methods that are supported.
However, they have also made it possible for a component developer to provide a class that implements the BeanInfo interface and to use that class to explicitly describe the bean. This BeanInfo class is then used to discover the beans behavior.
The Introspector class is provided to allow application builders and other tools to analyze beans in a uniform manner. The Introspector class understands the various design patterns and standard interfaces and extracts the pertinent information from the bean.
"conventional names and type signatures for sets of methods and/or interfaces that are used for standard purposes." |
public void set<PropertyName>(<PropertyType> a); public <PropertyType> get<PropertyName>(); |
JavaSoft has settled on the use of design patterns for at least
two reasons.
|
public void set<PropertyName>(<PropertyType> a); public <PropertyType> get<PropertyName>(); |
The two methods are used to get and set the property values as the names of the method imply.
Both methods in the pair may be located either in the same class, or one may be in a subclass and the other may be in a superclass.
If only one of the methods from the pair exists, then it is regarded either as a read-only or a write-only property.
The default assumption is that the property is neither bound nor constrained. As mentioned earlier, this will be discussed in more detail in a subsequent lesson.
Reflecting the above general description in more concrete terms might
result in the following
public void setMyProperty(int a){//...} public int getMyProperty(){//...} |
public boolean is<PropertyName>(){//...} |
An example for a boolean property named ready might be:
public boolean isReady(){//...} public void setReady(boolean m){//...} |
public <PropertyElementType> get<PropertyName>(int a){//...} public void set<PropertyName>( int a, < PropertyElementType> b){//...} |
public <PropertyType>[] get<PropertyName>(){//...} public void set<PropertyName>(<PropertyType> a[]) |
//return an element public MyType getMyProperty(int a){//...} //set an element public void setMyProperty(int a, MyType b){//...} public MyType[] getMyProperty(){//...} //return an array public void setMyProperty(MyType a[]){//...} //set an array |
public void add<EventListenerType>(<EventListenerType> a) public void remove<EventListenerType>(<EventListenerType> a) |
A pair of example methods that define a multicast event source might
look like the following:
public void addMyTypeOfListener(MyTypeOfListener t){//...} public void removeMyTypeOfListener(MyTypeOfListener t){//...} |
public void addMyTypeOfListener(MyTypeOfListener t) throws java.util.TooManyListenersException{//...} public void removeMyTypeOfListener(MyTypeOfListener t){//...} |
As mentioned earlier, a more detailed discussion of this topic will be deferred to a subsequent lesson. For the meantime, suffice it to say that application tools should always use the Introspector interface which combines the information from a variety of potential sources, including explicit specifications, to construct a BeanInfo descriptor for a target bean.
The Introspector examines each class in the inheritance chain of the target class. It checks at each level to determine if there is a matching BeanInfo class providing explicit information about the bean. If it finds such a class, it uses that explicit information.
If it does not find such a class it uses low-level reflection APIs to study the target class and then uses design patterns to analyze its behavior. It then moves on up the inheritance chain.
This multi-level analysis allows component developers to deliver complex beans with explicitly specified behavior. These beans can then be subclassed and extended by end-user customers without a requirement to provide explicit behavior information. The Introspector can then combine the explicit behavior information provided by the bean's developer with information gained by reflection and design patterns on the behavior introduced when the bean is subclassed.
When the name is extracted from the middle of a normal mixedCase Java name, the first character in the name will normally be converted to a lower case letter. For example the property accessor method named setMyProp() would normally result in a property name of myProp.
However, since it is sometimes desirable to use all uppercase names, the case will not be changed if the first two characters of the name are both uppercase. For example, the property accessor method named setRGB() would result in a property name of RGB.
Once the information about the bean is encapsulated in the BeanInfo object, a variety of other methods are used to extract specific types of information about the bean from the object.
In order to illustrate the behavior of the different methods being used, the application was applied to the skeleton bean named Beans01 (which we discussed in an earlier lesson) and the output from the program for each section of code was included as comments in that section.
To use the program, enter the command
java Introspect01 beanName |
As a sidelight, this program also illustrates the use of the fileWriter and printWriter classes which are new to JDK 1.1. However, since they are to be discussed in another lesson dealing with I/O upgrades in JDK 1.1, they are not discussed in this lesson.
The program was tested using JDK 1.1.1 and Win95.
Class myBeanClassObject = Class.forName(myBeanClassName); |
The second version that has two parameters uses the second parameter
to determine the point in the tree at and above which information
is not needed. In this case, we make the second parameter be the superclass
of the bean, thus causing the getBeanInfo() method to extract information
only on the class of the bean.
BeanInfo beanInfo = Introspector.getBeanInfo( myBeanClassObject,myBeanClassObject.getSuperclass()); |
Once the BeanDescriptor object is available, other methods are applied to the object to extract specific information from the object and write it to the output file.
When this application was applied to the skeleton bean named Beans01
that we studied in an earlier lesson, the output shown in the comments
was produced by this section of code.
/* Name of bean: Beans01 Class of bean: class Beans01 */ BeanDescriptor beanDescriptor = beanInfo.getBeanDescriptor(); printWriter.println("Name of bean: " + beanDescriptor.getName()); printWriter.println("Class of bean: " + beanDescriptor.getBeanClass()); printWriter.println(""); |
Each object describes one property that a Java Bean exports via a pair of accessor methods. Once that array of objects is available, other methods are used to extract specific information about each of the properties and to write that information into the output file.
When this program was applied to the skeleton bean named Beans01,
the output for this section of code was as shown in the comments. Note
that the preferredSize property is a read-only property because
its set method is null (there is no set method for
this property). Note that manual breaks have been inserted to make the
material fit the page.
/* ==== Properties: ==== Name: color Type: class java.awt.Color Get method: public synchronized java.awt.Color Beans01.getColor() Set method: public synchronized void Beans01.setColor(java.awt.Color) Name: preferredSize Type: class java.awt.Dimension Get method: public synchronized java.awt.Dimension Beans01.getPreferredSize() Set method: null Name: myBooleanProperty Type: boolean Get method: public synchronized boolean Beans01.isMyBooleanProperty() Set method: public synchronized void Beans01.setMyBooleanProperty(boolean) */ printWriter.println("==== Properties: ===="); PropertyDescriptor[] propertyDescriptor = beanInfo.getPropertyDescriptors(); for (int i=0; i<propertyDescriptor.length; i++) { printWriter.println("Name: " + propertyDescriptor[i].getName()); printWriter.println(" Type: " + propertyDescriptor[i].getPropertyType()); printWriter.println(" Get method: " + propertyDescriptor[i].getReadMethod()); printWriter.println(" Set method: " + propertyDescriptor[i].getWriteMethod()); }//end for-loop printWriter.println(""); |
When this program was applied to the skeleton bean named Beans01,
the output for this section of code was as shown in the comments. (Note
that the line breaks were inserted during the editing of this document
to make it all fit on the screen.) In this case, only one multicast
event was exposed by the bean.
==== Events: ==== Event Name: action Add Method: public synchronized void Beans01. addActionListener(java.awt.event.ActionListener) Remove Method: public synchronized void Beans01. removeActionListener( java.awt.event.ActionListener) Event Type: actionPerformed */ printWriter.println("==== Events: ===="); EventSetDescriptor[] eventSetDescriptor = beanInfo.getEventSetDescriptors(); for (int i=0; i<eventSetDescriptor.length; i++) { printWriter.println("Event Name: " + eventSetDescriptor[i].getName()); printWriter.println(" Add Method: " + eventSetDescriptor[i].getAddListenerMethod()); printWriter.println(" Remove Method: " + eventSetDescriptor[i].getRemoveListenerMethod()); MethodDescriptor[] methodDescriptor = eventSetDescriptor[i]. getListenerMethodDescriptors(); for (int j=0; j<methodDescriptor.length; j++) { printWriter.println(" Event Type: " + methodDescriptor[j].getName()); }//end for-loop }//end for-loop printWriter.println(""); |
This program was applied to the skeleton bean named Beans01,
and the output for this section of code is shown in the comments. You should
note that the list of methods includes property accessor methods, methods
that expose multicast event support, and "ordinary" methods that are not
intended to expose properties or events.
/* ==== Methods: ==== makeRed setMyBooleanProperty removeActionListener addActionListener setColor getColor getPreferredSize makeBlue isMyBooleanProperty */ printWriter.println("==== Methods: ===="); MethodDescriptor[] methodDescriptor = beanInfo.getMethodDescriptors(); for (int i=0; i<methodDescriptor.length; i++) { printWriter.println(methodDescriptor[i].getName()); }//end for-loop printWriter.println("");. |
/*File Introspect01.java Copyright 1997, R.G.Baldwin This program was designed to be compiled and executed under JDK 1.1.1. The purpose of this program is to illustrate the use of the static getBeanInfo() method of the Introspector class to encapsulate information about a bean in an object of type BeanInfo. Once the information about the bean is encapsulated in the BeanInfo object, a variety of other methods are used to extract specific types of information about the bean from the object. In order to illustrate the behavior of the different methods being used, the application was applied to the skeleton bean named Beans01 (discussed in an earlier lesson) and the output from the program for each section of code was included as comments in that section. The program was tested using JDK 1.1.1 and Win95. **********************************************************/ import java.io.*; import java.beans.*; import java.lang.reflect.*; public class Introspect01 { //name of bean class file to be analyzed static String myBeanClassName; FileWriter fileWriter; PrintWriter printWriter; //Start the program and get the name of the class file // for the bean in the String myBeanClassName public static void main(String args[]) throws Exception { myBeanClassName = args[0]; Introspect01 x = new Introspect01(); }//end main public Introspect01() throws Exception {//constructor //Open an output file to store the report in. fileWriter = new FileWriter("junk.txt"); printWriter = new PrintWriter(fileWriter); //Create an object of type Class that describes the // class of the bean. The static method // Introspector.getBeanInfo() requires either one or // two objects of type Class as parameters. The // forName() method of the Class class returns such an // object, given the name of a class as a parameter. Class myBeanClassObject = Class.forName( myBeanClassName); //Given the Class object that describes the bean's // class, use the static getBeanInfo() method of the // Introspector class to obtain information about the // class of the bean. Save this information in an // object of type BeanInfo. The second parameter // passed to getBeanInfo() prevents introspection from // going further up the inheritance hierarchy. BeanInfo beanInfo = Introspector.getBeanInfo( myBeanClassObject,myBeanClassObject.getSuperclass()); //A BeanDescriptor object provides global information // about a bean, including its Java class, its // displayName, etc. Use the getBeanDescriptor() // method to extract information of that type from // the beanInfo object and store it in a new // BeanDescriptor object. Store the information in the // output file using methods designed to extract the // name and class of the bean from the beanDescriptor // object. When this application was applied to the // skeleton bean named Beans01, the following output // was produced by this section of code. /* Name of bean: Beans01 Class of bean: class Beans01 */ BeanDescriptor beanDescriptor = beanInfo.getBeanDescriptor(); printWriter.println("Name of bean: " + beanDescriptor.getName()); printWriter.println("Class of bean: " + beanDescriptor.getBeanClass()); printWriter.println(""); //A PropertyDescriptor object describes one property // that a Java Bean exports via a pair of accessor // methods. Use the getPropertyDescriptors() method // to create an array of PropertyDescriptor objects, // one for each exported property. Then store that // information in the output file using methods // designed to extract the name of the property, the // type of the property, the name of the get method, // and the name of the set method. When this // application was applied to Beans01, the following // output was produced by this section of code. Manual // line breaks were inserted to make it fit in the // available space. /* ==== Properties: ==== Name: color Type: class java.awt.Color Get method: public synchronized java.awt.Color Beans01.getColor() Set method: public synchronized void Beans01.setColor(java.awt.Color) Name: preferredSize Type: class java.awt.Dimension Get method: public synchronized java.awt.Dimension Beans01.getPreferredSize() Set method: null Name: myBooleanProperty Type: boolean Get method: public synchronized boolean Beans01.isMyBooleanProperty() Set method: public synchronized void Beans01.setMyBooleanProperty(boolean) */ printWriter.println("==== Properties: ===="); PropertyDescriptor[] propertyDescriptor = beanInfo.getPropertyDescriptors(); for (int i=0; i<propertyDescriptor.length; i++) { printWriter.println("Name: " + propertyDescriptor[i].getName()); printWriter.println(" Type: " + propertyDescriptor[i].getPropertyType()); printWriter.println(" Get method: " + propertyDescriptor[i].getReadMethod()); printWriter.println(" Set method: " + propertyDescriptor[i].getWriteMethod()); }//end for-loop printWriter.println(""); //An EventSetDescriptor object describes a group of // events that a given Java bean fires. Information can // be extracted from each object of the type. // When this application was applied to the Beans01 // bean, the following output was produced by this // section of code (note that line breaks were // inserted during editing). /* ==== Events: ==== Event Name: action Add Method: public synchronized void Beans01. addActionListener(java.awt.event.ActionListener) Remove Method: public synchronized void Beans01. removeActionListener(java.awt.event.ActionListener) Event Type: actionPerformed */ printWriter.println("==== Events: ===="); EventSetDescriptor[] eventSetDescriptor = beanInfo.getEventSetDescriptors(); for (int i=0; i<eventSetDescriptor.length; i++) { printWriter.println("Event Name: " + eventSetDescriptor[i].getName()); printWriter.println(" Add Method: " + eventSetDescriptor[i].getAddListenerMethod()); printWriter.println(" Remove Method: " + eventSetDescriptor[i].getRemoveListenerMethod()); MethodDescriptor[] methodDescriptor = eventSetDescriptor[i]. getListenerMethodDescriptors(); for (int j=0; j<methodDescriptor.length; j++) { printWriter.println(" Event Type: " + methodDescriptor[j].getName()); }//end for-loop }//end for-loop printWriter.println(""); //A MethodDescriptor describes a particular method that // a Java Bean supports for external access from other // components. The getMethodDescriptors() method // returns an array of MethodDescriptor objects where // each object describes one of the methods. When this // application was applied to the Beans01 bean, the // following output was produced by this section // of code. /* ==== Methods: ==== makeRed setMyBooleanProperty removeActionListener addActionListener setColor getColor getPreferredSize makeBlue isMyBooleanProperty */ printWriter.println("==== Methods: ===="); MethodDescriptor[] methodDescriptor = beanInfo.getMethodDescriptors(); for (int i=0; i<methodDescriptor.length; i++) { printWriter.println(methodDescriptor[i].getName()); }//end for-loop printWriter.println(""); printWriter.close(); }//end constructor }//end class Introspect01 |