El paquete org.springframework.beans sigue el estándar de clase JavaBeans. Un JavaBean es una clase con un constructor predeterminado sin argumentos que sigue una convención de nomenclatura donde (por ejemplo) una propiedad llamada bingoMadness tendrá un definidor setBingoMadness(...) y un captador getBingoMadness(). Para obtener más información sobre JavaBeans y la especificación, consulte javabeans.

Una de las clases importantes en el paquete bean es la interfaz BeanWrapper y su correspondiente implementación (BeanWrapperImpl). Como se indica en el javadoc, BeanWrapper ofrece funcionalidad para configurar y obtener valores de propiedades (individualmente o en masa), obtener identificadores de propiedades y consultar propiedades para determinar si se pueden leer o escribir. Además, BeanWrapper ofrece soporte para propiedades anidadas, lo que le permite establecer propiedades en subpropiedades con una profundidad ilimitada. BeanWrapper también admite la capacidad de agregar JavaBeans estándar PropertyChangeListeners y VetoableChangeListeners sin la necesidad de código adicional en la clase de destino. Por último, pero no menos importante, BeanWrapper proporciona soporte para configurar propiedades indexadas. BeanWrapper normalmente no se usa directamente en el código de la aplicación, pero sí en DataBinder y BeanFactory.

Así es como BeanWrapper funciona queda algo claro por su nombre: envuelve un bean para realizar ciertas acciones en él, como configurar y obtener propiedades.

Configurar y obtener propiedades base y anidadas

La configuración y obtención de propiedades se realiza utilizando los métodos sobrecargados setPropertyValue y getPropertyValue en BeanWrapper. Consulte su Javadoc para obtener más detalles. La siguiente tabla muestra algunos ejemplos de estas convenciones:

Tabla 11. Ejemplos de propiedades
Expression Explanation

name

Especifica la propiedad name que coincide métodos getName() o isName() y setName(..).

account.name

Especifica el nombre de la account propiedad anidada, que corresponde (por ejemplo) a los métodos getAccount().setName() o getAccount().getName().

account[2]

Especifica el tercer elemento de la propiedad indexada account propiedad. Las propiedades indexadas pueden ser de tipo array, list u otra colección ordenada naturalmente.

account[COMPANYNAME]

Indica el valor de la entrada de matriz asociativa indexada por la clave COMPANYNAME del Account Map propiedad.

(La siguiente sección no es extremadamente importante a menos que planee trabajar con BeanWrapper directamente. Si solo usa DataBinder y BeanFactory y sus implementaciones predeterminadas, entonces debe ir directamente a PropertyEditors.)

Las siguientes dos clases de ejemplo usan BeanWrapper para obtener y establecer propiedades:

Java

public class Company {
    private String name;
    private Employee managingDirector;
    public String getName() {
        return this.name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Employee getManagingDirector() {
        return this.managingDirector;
    }
    public void setManagingDirector(Employee managingDirector) {
        this.managingDirector = managingDirector;
    }
}
Kotlin

            class Company {
    var name: String? = null
    var managingDirector: Employee? = null
}
Java

           public class Employee {
    private String name;
    private float salary;
    public String getName() {
        return this.name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public float getSalary() {
        return salary;
    }
    public void setSalary(float salary) {
        this.salary = salary;
    }
}        
Kotlin

            class Employee {
    var name: String? = null
    var salary: Float? = null
}

Los siguientes fragmentos de código demuestran ejemplos de cómo obtener y manipular algunas propiedades de la Company y los Employeesinstancias creados:

Java

BeanWrapper company = new BeanWrapperImpl(new Company());
// setting the company name...
company.setPropertyValue("name", "Some Company Inc.");
// ... can also be done like this:
PropertyValue value = new PropertyValue("name", "Some Company Inc.");
company.setPropertyValue(value);
// Okay, let's create a director and bind him to the company:
BeanWrapper jim = new BeanWrapperImpl(new Employee());
jim.setPropertyValue("name", "Jim Stravinsky");
company.setPropertyValue("managingDirector", jim.getWrappedInstance());
// getting salary for managingDirector via company
Float salary = (Float) company.getPropertyValue("managingDirector.salary");
Kotlin

val company = BeanWrapperImpl(Company())
// setting the company name...
company.setPropertyValue("name", "Some Company Inc.")
// ... can also be done like this:
val value = PropertyValue("name", "Some Company Inc.") company.setPropertyValue(value)
// Okay, let's create a director and bind him to the company:
val jim = BeanWrapperImpl(Employee())
jim.setPropertyValue("name" , "Jim Stravinsky")
company.setPropertyValue("managingDirector", jim.wrappedInstance)
// getting salary for managingDirector via company
val salary = company.getPropertyValue("managingDirector.salary") as Float?

Implementaciones integradas de PropertyEditor

Spring utiliza el concepto PropertyEditor para realizar la conversión entre Object y String. Puede resultar conveniente representar las propiedades de forma diferente al objeto mismo. Por ejemplo, Date se puede representar en forma legible por humanos (como String: '2007-14-09'), mientras que todos nosotros También puede convertir el formato legible por humanos a la fecha original (o, mejor aún, convertir cualquier fecha ingresada en formato legible por humanos nuevamente en objetos Fecha). Esta lógica operativa se puede lograr registrando editores especiales como java.beans.PropertyEditor. Registrar editores especiales para BeanWrapper, o alternativamente en un contenedor IoC específico (como se mencionó en el capítulo anterior), le indica cómo convertir las propiedades al tipo deseado. Para obtener más información sobre PropertyEditor, consulte javadoc para el paquete java.beans de Oracle.

Algunos ejemplos del uso de la función de edición de propiedades en Spring:

  • La configuración de las propiedades del bean se realiza mediante implementaciones de PropertyEditor. Si usa String como valor de propiedad de cualquier bean que declare en un archivo XML, Spring (si el definidor de la propiedad correspondiente tiene un parámetro Class) usa un ClassEditor para intentar resolver el parámetro en un objeto Class.

  • El análisis de los parámetros de solicitud HTTP en el marco Spring MVC se realiza utilizando varias implementaciones PropertyEditor, que se pueden vincular manualmente en todas las subclases de CommandController.

Spring tiene una serie de implementaciones integradas. en PropertyEditor implementaciones, que facilitan su trabajo. Todos están en el paquete org.springframework.beans.propertyeditors. La mayoría de ellos (pero no todos, como se enumera en la siguiente tabla) están registrados por BeanWrapperImpl de forma predeterminada. En los casos en que el editor de propiedades esté configurado de una manera particular, puede registrar su propia variación para anular la variación predeterminada. La siguiente tabla describe las diversas implementaciones de PropertyEditor que proporciona Spring:

Tabla 12. Incorporada implementaciones PropertyEditor
Class Explanation

ByteArrayPropertyEditor

Editor de matrices de bytes. Convierte cadenas a sus representaciones de bytes correspondientes. De forma predeterminada, BeanWrapperImpl está registrado.

ClassEditor

Analiza cadenas que representan clases en clases reales y viceversa. Si no se encuentra la clase, se lanza una excepción IllegalArgumentException. De forma predeterminada, BeanWrapperImpl está registrado.

CustomBooleanEditor

Editor de propiedades personalizadas para propiedades Boolean. De forma predeterminada, BeanWrapperImpl está registrado, pero se puede anular registrando una instancia personalizada del mismo como editor personalizado.

CustomCollectionEditor

Editor de propiedades para colecciones que convierte cualquier Collection de origen en una Collection de destino determinada tipo.

CustomDateEditor

Editor de propiedades personalizadas para java.util.Date, que admite un DateFormat especial. NO registrado por defecto. El usuario debe registrarlo en el formato apropiado según sea necesario.

CustomNumberEditor

Editor de propiedades personalizable para cualquier subclase Number como Integer, Long, Float o Double. De forma predeterminada, BeanWrapperImpl está registrado, pero se puede anular registrando una instancia personalizada del mismo como editor personalizado.

FileEditor

Resuelve cadenas en objetos java.io.File. De forma predeterminada, BeanWrapperImpl está registrado.

InputStreamEditor

Un editor de propiedades unidireccional que puede aceptar una cadena y crear (a través de un ResourceEditor y un Resource intermedios) un InputStream para que que las propiedades InputStream se pueden establecer directamente como cadenas. Tenga en cuenta que cuando se utiliza de forma predeterminada, InputStream no se cierra. De forma predeterminada, BeanWrapperImpl está registrado.

LocaleEditor

Puede convertir cadenas a objetos Locale y viceversa (el formato de cadena es [language]_[country]_[variant], igual que toString() en Configuración regional Locale). También acepta espacios como delimitadores, como alternativa a los guiones bajos. De forma predeterminada, BeanWrapperImpl está registrado.

PatternEditor

Puede resolver cadenas en objetos java.util.regex.Pattern y viceversa.

PropertiesEditor

Puede convertir cadenas (formateadas en el formato definido en el javadoc de la clase java.util.Properties ) en Properties de objetos . De forma predeterminada, BeanWrapperImpl está registrado.

StringTrimmerEditor

Editor de propiedades que recorta líneas. Opcionalmente, le permite transformar una cadena vacía en un valor null. El valor predeterminado NO está registrado; debe ser registrado por el usuario.

URLEditor

Puede convertir una representación de cadena de alguna URL en un objeto URL real. De forma predeterminada, BeanWrapperImpl está registrado.

Spring usa java.beans.PropertyEditorManager para configurar la ruta de búsqueda de los editores de propiedades que puedan ser necesarios. La ruta de búsqueda también incluye sun.bean.editors, que incluye implementaciones de PropertyEditor para tipos como Font, Color y los tipos más primitivos. Tenga en cuenta que el marco de clases estándar JavaBeans descubre automáticamente clases PropertyEditor (sin tener que registrarlas explícitamente) si están en el mismo paquete que la clase que procesan y tienen el mismo nombre que esa clase, con la adición de Editor . Por ejemplo, podría tener la siguiente estructura de clase y paquete, que sería suficiente para garantizar que la clase SomethingEditor sea reconocida y utilizada como PropertyEditor para propiedades de tipo Something.

com
  chank
    pop
      Something
      SomethingEditor //  PropertyEditor for class Something

Tenga en cuenta que en este caso también puede utilizar el mecanismo estándar BeanInfo de la clase JavaBeans (descrito hasta cierto punto por aquí). El siguiente ejemplo utiliza el mecanismo BeanInfo para registrar explícitamente una o más instancias de PropertyEditor con las propiedades de la clase asociada:

com
  chank
    pop
      Something
      SomethingBeanInfo // BeanInfo for class Something

El siguiente código fuente Java para la clase de referencia SomethingBeanInfo vincula el CustomNumberEditor a la propiedad age de la clase Something:

Java

public class SomethingBeanInfo extends SimpleBeanInfo {
    public PropertyDescriptor[] getPropertyDescriptors() {
        try {
            final PropertyEditor numberPE = new CustomNumberEditor(Integer.class, true);
            PropertyDescriptor ageDescriptor = new PropertyDescriptor("age", Something.class) {
                @Override
                public PropertyEditor createPropertyEditor(Object bean) {
                    return numberPE;
                }
            };
            return new PropertyDescriptor[] { ageDescriptor };
        }
        catch (IntrospectionException ex) {
            throw new Error(ex.toString());
        }
    }
}
Kotlin

class SomethingBeanInfo : SimpleBeanInfo() {
    override fun getPropertyDescriptors(): Array {
        try {
            val numberPE = CustomNumberEditor(Int::class.java, true)
            val ageDescriptor = object : PropertyDescriptor("age", Something::class.java) {
                override fun createPropertyEditor(bean: Any): PropertyEditor {
                    return numberPE
                }
            }
            return arrayOf(ageDescriptor)
        } catch (ex: IntrospectionException) {
            throw Error(ex.toString())
        }
    }
}

Registrar implementaciones adicionales de especial PropertyEditor

Al especificar propiedades de bean como valores de cadena, el contenedor Spring IoC termina usando implementaciones estándar de JavaBeans PropertyEditor para convertir esas cadenas en una propiedad compleja. tipo. Spring registra previamente una serie de implementaciones personalizadas de PropertyEditor (por ejemplo, para convertir un nombre de clase expresado como una cadena en un objeto Class). Además, el mecanismo de búsqueda estándar PropertyEditor en JavaBeans le permite nombrar apropiadamente el PropertyEditor para una clase y colocarlo en el mismo paquete que la clase para la que proporciona soporte, de modo que que se pueda encontrar automáticamente.

Si es necesario registrar otros PropertyEditors personalizados, hay varios mecanismos disponibles. El enfoque más manual, y generalmente no se considera conveniente ni recomendado, es utilizar el método registerCustomEditor() de la interfaz ConfigurableBeanFactory, siempre que haya una referencia a BeanFactory. Otro mecanismo (un poco más conveniente) es utilizar un postprocesador de fábrica de frijoles especializado llamado CustomEditorConfigurer. Aunque no puede usar postprocesadores de Bean Factory usando la implementación BeanFactory, CustomEditorConfigurer tiene una configuración de propiedad anidada, por lo que recomendamos encarecidamente usarlo con ApplicationContext en casos en los que se puede implementar de la misma manera que cualquier otro bean y en los que se puede descubrir y aplicar automáticamente.

Tenga en cuenta que todas las fábricas de beans y contextos de aplicación utilizan automáticamente una serie de funciones integradas. editores de propiedades mediante el uso de BeanWrapper para manejar las conversiones de propiedades. Además, ApplicationContexttambién anula o agrega editores adicionales para manejar las búsquedas de recursos según el tipo de contexto de aplicación específico.

Instancias estándar de la clase PropertyEditor de Los JavaBeans se utilizan para convertir valores de propiedad expresados como cadenas al tipo de propiedad complejo real. Puede utilizar CustomEditorConfigurer, un posprocesador de fábrica de beans, para agregar convenientemente soporte para instancias adicionales de PropertyEditor en ApplicationContext.

Considere el siguiente ejemplo, que define una clase personalizada ExoticType y otra clase DependsOnExoticType que necesita establecer ExoticType como una propiedad:

Java

package example;
public class ExoticType {
    private String name;
    public ExoticType(String name) {
        this.name = name;
    }
}
public class DependsOnExoticType {
    private ExoticType type;
    public void setType(ExoticType type) {
        this.type = type;
    }
}
Kotlin

package example
class ExoticType(val name: String)
class DependsOnExoticType {
    var type: ExoticType? = null
}

Si todo está configurado correctamente, necesitamos poder asignar una propiedad de tipo a una cadena, que el PropertyEditor se convertirá en una instancia real de ExoticType. La siguiente definición de bean muestra cómo establecer esta relación:


<bean id="sample" class="example.DependsOnExoticType">
    <property name="type" value="aNameForExoticType"/>
</bean>

La implementación de PropertyEditor podría verse así:

Java

// converts the string representation to an ExoticType object
package example;
public class ExoticTypeEditor extends PropertyEditorSupport {
    public void setAsText(String text) {
        setValue(new ExoticType(text.toUpperCase()));
    }
}
Kotlin

// Converts a string representation to an ExoticType object
package example
import java.beans.PropertyEditorSupport
class ExoticTypeEditor : PropertyEditorSupport() {
    override fun setAsText(text: String) {
        value = ExoticType(text.toUpperCase())
    }
}

Finalmente, A continuación, el ejemplo muestra cómo usar CustomEditorConfigurer para registrar un nuevo PropertyEditor con ApplicationContext, que luego puede usarlo según sea necesario:


<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
    <property name="customEditors">
        <map>
            <entry key="example.ExoticType" value="example.ExoticTypeEditor"/>
        </map>
    </property>
</bean>
Uso de PropertyEditorRegistrar

Otro mecanismo para registrar editores de propiedades en un contenedor Spring es crear y usar PropiedadEditorRegistrar. Esta interfaz es especialmente útil si desea utilizar el mismo conjunto de editores de propiedades en varias situaciones diferentes. Puedes escribir un registrador correspondiente y reutilizarlo en cada caso. Las instancias PropertyEditorRegistrar operan junto con la interfaz PropertyEditorRegistry, que se implementa mediante BeanWrapper de Spring (y DataBinder). Las instancias PropertyEditorRegistrar son especialmente útiles cuando se usan junto con CustomEditorConfigurer, que expone la propiedad setPropertyEditorRegistrars(..). Las instancias PropertyEditorRegistrar agregadas a CustomEditorConfigurer de esta manera se pueden usar fácilmente con los controladores DataBinder y Spring MVC. Además, esto evita la necesidad de sincronización en editores personalizados: se espera que PropertyEditorRegistrar cree nuevas instancias de PropertyEditor cada vez que se crea un bean.

A continuación, el ejemplo muestra cómo crear su propia implementación de PropertyEditorRegistrar:

Java

package com.foo.editors.spring;
public final class CustomPropertyEditorRegistrar implements PropertyEditorRegistrar {
    public void registerCustomEditors(PropertyEditorRegistry registry) {
        // expect new PropertyEditor instances to be created
        registry.registerCustomEditor(ExoticType.class, new ExoticTypeEditor());
        // in this case, you can register as many custom property editors as required...
    }
}
Kotlin
  
package com.foo.editors.spring
import org.springframework.beans.PropertyEditorRegistrar
import org.springframework.beans.PropertyEditorRegistrar
class CustomPropertyEditorRegistrar : PropertyEditorRegistrar {
    override fun registerCustomEditors(registry: PropertyEditorRegistry) {
        // expect new instances of PropertyEditor registry to be created
        register.CustomEditor(ExoticType::class.java, ExoticTypeEditor())
        // in this case, you can register as many custom property editors as you need...
    }
}

Vea también un ejemplo de implementación de PropertyEditorRegistrar en org.springframework.beans.support.ResourceEditorRegistrar. Observe cómo en su implementación del método registerCustomEditors(...) crea nuevas instancias de cada editor de propiedades.

El siguiente ejemplo muestra cómo configurar CustomEditorConfigurer e implementar en él una instancia de nuestro CustomPropertyEditorRegistrar:


<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
    <property name="propertyEditorRegistrars">
        <list>
            <ref bean="customPropertyEditorRegistrar"/>
        </list>
    </property>
</bean>
<bean id="customPropertyEditorRegistrar"
    class="com.foo.editors.spring.CustomPropertyEditorRegistrar"/>

Por último (y un poco fuera de tema, esto capítulo) para aquellos de ustedes que usan el marco web MVC de Spring, usar PropertyEditorRegistrar en combinación con controladores de enlace de datos en una aplicación web puede ser muy conveniente. En el siguiente ejemplo se usa PropertyEditorRegistrar en la implementación del método @InitBinder:

Java

@Controller
public class RegisterUserController {
    private final PropertyEditorRegistrar customPropertyEditorRegistrar;
    RegisterUserController(PropertyEditorRegistrar propertyEditorRegistrar) {
        this.customPropertyEditorRegistrar = propertyEditorRegistrar;
    }
    @InitBinder
    void initBinder(WebDataBinder binder) {
        this.customPropertyEditorRegistrar.registerCustomEditors(binder);
    }
    // other methods related to user registration
}
Kotlin

@Controller
class RegisterUserController(
    private val customPropertyEditorRegistrar: PropertyEditorRegistrar) {
    @InitBinder
    fun initBinder(binder: WebDataBinder) {
        this.customPropertyEditorRegistrar.registerCustomEditors(binder)
    }
    // other methods related to user registration
            }

Este estilo de registro de PropertyEditor puede mantener el código conciso (la implementación del método @InitBinder ocupa solo una línea) y le permite encapsular el PropertyEditor general código de registro en una clase y luego permitir que muchos controladores lo compartan.