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:
Expression | Explanation |
---|---|
|
Especifica la propiedad |
|
Especifica el |
|
Especifica el tercer elemento de la propiedad indexada
|
|
Indica el valor de la entrada de matriz asociativa indexada por la clave |
(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:
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;
}
}
class Company {
var name: String? = null
var managingDirector: Employee? = null
}
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;
}
}
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 Employees
instancias creados:
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");
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 usaString
como valor de propiedad de cualquier bean que declare en un archivo XML, Spring (si el definidor de la propiedad correspondiente tiene un parámetroClass
) usa unClassEditor
para intentar resolver el parámetro en un objetoClass
.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 deCommandController
.
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:
Class | Explanation |
---|---|
|
Editor de matrices de bytes. Convierte cadenas a sus representaciones de bytes correspondientes. De forma
predeterminada, |
|
Analiza cadenas que representan clases en clases reales y viceversa. Si no se encuentra la clase, se
lanza una excepción |
|
Editor de propiedades personalizadas para propiedades |
|
Editor de propiedades para colecciones que convierte cualquier |
|
Editor de propiedades personalizadas para |
|
Editor de propiedades personalizable para cualquier subclase |
|
Resuelve cadenas en objetos |
|
Un editor de propiedades unidireccional que puede aceptar una cadena y crear (a través de un |
|
Puede convertir cadenas a objetos |
|
Puede resolver cadenas en objetos |
|
Puede convertir cadenas (formateadas en el formato definido en el javadoc de la clase |
|
Editor de propiedades que recorta líneas. Opcionalmente, le permite transformar una cadena vacía en un
valor |
|
Puede convertir una representación de cadena de alguna URL en un objeto |
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
:
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());
}
}
}
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, ApplicationContext
tambié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:
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;
}
}
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í:
// 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()));
}
}
// 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
:
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...
}
}
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
:
@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
}
@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.