El Interface BeanInfo
¿Cólo examinan las herramientas de desarrollo a un Bean para exponer sus caracterísitcas (propiedades, eventos y métodos) en un hoja de propiedades? Utilizando la clase
java.beans.Introspector. Esta clase utiliza el
corazón de reflexión del API del JDK para descubrir los métodos del Bean, y luego aplica los patrones de diseño de los JavaBeans para descubrir sus caracterísitcas. Este proceso de descubrimiento se llama
introspección.
De forma alternativa, se pueden exponer explícitamente las caractericas del Bean en una clase asociada separada que implemente el interface BeanInfo. Asociando una clase BeanInfo con un Bean se puede:
- Exponer solo aquellas características que queremos exponer.
- Relegar en BeanInfo la exposición de algunas caracterísitcas del Bean, mientras se deja el resto para la reflexión de bajo nivel.
- Asociar un icono con el Bean fuente.
- Especificar una clase personaliza.
- Segregar las caracterísitcas entre normales y expertas.
- Proporcionar un nombre más descriptivo, información adicional sobre la caracterísitca del Bean.
BeanInfo define métodos que devuelven descriptores para cada propiedad, método o evento que se quiere exponer. Aquí tienes los prototipos de estos métodos:
PropertyDescriptor[] getPropertyDescriptors();
MethodDescriptor[] getMethodDescriptors();
EventSetDescriptor[] getEventSetDescriptors();
Cada uno de estos métodos devuelve un array de descriptores para cada caracterísitca.
Descriptores de Caracterisitcas
Las clases
BeanInfo contienen
descriptores que precisamente describen las características del Bean fuente. El BDK implementa las siguientes clases:
- FeatureDescriptor es la clase base para las otras clases de descriptores. Declara los aspectos comunes a todos los tipos de descriptores.
- BeanDescriptor describe el tipo de la clase y el nombre del Bean fuente y describe la clase personalizada del Bean fuente si existe.
- PropertyDescriptor describe las propiedades del Bean fuente.
- IndexedPropertyDescriptor es una subclase de PropertyDescriptor, y describe las propiedades indexadas del Bean fuente.
- EventSetDescriptor describe los eventos lanzados por el Bean fuente.
- MethodDescriptor describe los métodos del Bean fuente.
- ParameterDescriptor describe los parámetros de métodos.
El interface BeanInfo declara métodos que devuelven arrays de los descriptores anteriores.
Crear una Clase BeanInfo
Utilizaremos la clase
ExplicitButtonBeanInfo para ilustrar la creación de una clase
BeanInfo. Aquí están los pasos generales para crear una clase
BeanInfo:
- Nombrar la clase BeanInfo. Se debe añadir el estring "BeanInfo" al nombre de la clase fuente. Si el nombre de la clase fuente es ExplicitButton, la clase BeanInfo asociada se debe llamar ExplicitButtonBeanInfo
- Subclasificar SimpleBeanInfo. Esta es una clase de conveniencia que implementa los métodos de BeanInfo para que devuelvan null o un valor nulo equivalente.
public class ExplicitButtonBeanInfo extends SimpleBeanInfo {
Utilizando SimpleBeanInfo nos ahorramos tener que implementar todos los métodos de BeanInfo; solo tenemos que sobreescribir aquellos métodos que necesitemos.
- Sobreescribir los métodos apropiados para devolver las propiedades, los métodos o los eventos que queremos exponer. ExplicitButtonBeanInfo sobreescribe el método getPropertyDescriptors() para devolver cuatro propiedades:
public PropertyDescriptor[] getPropertyDescriptors() {
try {
PropertyDescriptor background =
new PropertyDescriptor("background", beanClass);
PropertyDescriptor foreground =
new PropertyDescriptor("foreground", beanClass);
PropertyDescriptor font =
new PropertyDescriptor("font", beanClass);
PropertyDescriptor label =
new PropertyDescriptor("label", beanClass);
background.setBound(true);
foreground.setBound(true);
font.setBound(true);
label.setBound(true);
PropertyDescriptor rv[] =
{background, foreground, font, label};
return rv;
} catch (IntrospectionException e) {
throw new Error(e.toString());
}
}
Existen dos cosas importantes que observar aquí:
- Si se deja fuera algún descriptor, la propiedad, evento o método no descrito no se expondrá. En otras palabras, se puede exponer selectivamente las propiedades, eventos o métodos, dejando fuera las que no queramos exponer.
- Si un método obtenedor de características (por ejemplo getMethodDescriptor()) devuelve Null, se utilizará la reflexión de bajo nivel para esa caracterísitca. Esto significa, por ejemplo, que se pueden expecificar explicitamente propiedades, y dejar que la reflexión de bajo nivel descubra los métodos. Si no se sobreescribe el método por defecto de SimpleBeanInfo que devuelve null, la reflexión de bajo nivel se utilizará para esta característica.
- Optionalmente, asociar un icono con el Bean fuente.
public java.awt.Image getIcon(int iconKind) {
if (iconKind == BeanInfo.ICON_MONO_16x16 ||
iconKind == BeanInfo.ICON_COLOR_16x16 ) {
java.awt.Image img = loadImage("ExplicitButtonIcon16.gif");
return img;
}
if (iconKind == BeanInfo.ICON_MONO_32x32 ||
iconKind == BeanInfo.ICON_COLOR_32x32 ) {
java.awt.Image img = loadImage("ExplicitButtonIcon32.gif");
return img;
}
return null;
}
El BeanBox muestra el icono junto al nombre del Bean en el ToolBox. Se puede esperar que las herramientas de desarrollo hagan algo similar.
- Especificar la clase del Bean fuente, y , si el Bean está personalizado, especificarlo también.
public BeanDescriptor getBeanDescriptor() {
return new BeanDescriptor(beanClass, customizerClass);
}
...
private final static Class beanClass = ExplicitButton.class;
private final static Class customizerClass = OurButtonCustomizer.class;
Guarda la clase BeanInfo en el mismo directorio que la clase fuente. El BeanBox busca primero la clase BeanInfo de un Bean en el path del paquete del Bean. Si no se encuentra el BeanInfo, entonces la información del Bean busca en el path (mantenido por el Introspector). La información del Bean se busca por defecto en el path sun.beans.infos. Si no se encuentra la clase BeanInfo, se utiliza la reflexión de bajo nivel para descrubrir las características del Bean.
Utilizar BeanInfo para Controlar las Características a Exponer
Si relegamos en la reflexión del bajo nivel para descubrir las características del Bean, todas aquellas propiedades, métodos y eventos que conformen el patrón de diseño apropiado serán expuestas en una herramienta de desarrollo. Esto incluye cualquier característica de la clase base. Si el BeanBox encuentra una clase
BeanInfo asociada, entonces la información es utiliza en su lugar, y no se examinan más clases base utilizando la reflexión. En otras palabras, la información del
BeanInfo sobreescribe la información de la reflexión de bajo nivel, y evita el examen de la clase base.
Mediante la utilización de una clase BeanInfo, se pueden exponer subconjuntos de una característica particular del Bean. Por ejemplo, mediante la no devolución de un método descriptor para un método particular, ese método no será expuesto en una herramienta de desarrollo.
Cuando se utiliza la clase BeanInfo
- Las características de la clase base no serán expuestas. Se pueden recuperar las características de la clase base utilizando el método BeanInfo.getAdditionalBeanInfo().
- Las propiedades, eventos o métodos que no tengan descriptor no serán expuestos. Para una característica particular, sólo aquellos ítems devueltos en el array de descriptores serán expuestos. Por ejemplo, si devolvemos descriptores para todos los métodos de un Bean excepto foo(), entonces foo() no será expuesto.
- La reflexión de bajo nivel será utilizada para las características cuyos metodos obtenedores devuelvan null. Por ejemplo su nuestra clase BeanInfo contiene esta implementación de métodos:
public MethodDescriptor[] getMethodDescriptors() {
return null;
}
Entonces la reflexión de bajo nivel se utilizará para descubrir los métodos públicos del Bean.
Localizar las clases BeanInfo
Antes de examinar un Bean, el
Introspector intentará encontrar una clase
BeanInfo asociada con el bean. Por defecto, el
Introspector toma el nombre del paquete del Bean totalmente cualificado, y le añade "BeanInfo" para formar un nuevo nombre de clase. Por ejemplo, si el Bean fuente es
sunw.demo.buttons.ExplicitButton, el
Introspector intentará localizar
sunw.demo.buttons.ExplicitButtonBeanInfo.
Si esto falla, se buscará en todos los paquetes en el path de BeanInfo. El path de búsqueda de BeanInfo es mantenido por Introspector.setBeanInfoSearchPath() y
Introspector.getBeanInfoSearchPath().
Ozito