Пакет org.springframework.beans
дотримується стандарту
класу JavaBeans. JavaBean — це клас із конструктором за умовчанням без аргументів, який дотримується угоди про
іменування, де (наприклад) властивість з ім'ям bingoMadness
матиме сетер
setBingoMadness(…)
та гетер getBingoMadness()
. Для отримання додаткової інформації про
JavaBeans та специфікації див. javabeans .
Одним із важливих класів у пакеті бінів є
інтерфейс BeanWrapper
та його відповідна реалізація(BeanWrapperImpl
). Як зазначено в
javadoc, BeanWrapper
пропонує функціональність для встановлення та отримання значень властивостей
(окремо чи масово), отримання дескрипторів властивостей та запиту властивостей для того, щоб визначати, чи доступні
вони для читання чи запису. Крім того, BeanWrapper
пропонує підтримку вкладених властивостей, що
дозволяє встановлювати властивості підвластивостей на необмежену глибину. BeanWrapper
також підтримує
можливість додавання стандартних JavaBeans PropertyChangeListeners
та
VetoableChangeListeners
без потреби допоміжного коду в цільовому класі. І останнє, але не менш важливе:
BeanWrapper
забезпечує підтримку встановлення індексованих властивостей. BeanWrapper
зазвичай не використовується кодом програми безпосередньо, але використовується DataBinder
і BeanFactory
.
Те, як працює BeanWrapper
, частково зрозуміло з його назви: він обертає бін для виконання певних
дій над ним, таких як встановлення та отримання властивостей.
Вказання та отримання базових та вкладених властивостей
Установка і отримання властивостей здійснюється за допомогою перевантажених методів setPropertyValue
та getPropertyValue
у BeanWrapper
. Докладніше див. в їх Javadoc. У наведеній нижче таблиці
наведено деякі приклади цих угод:
Вираз | Пояснення |
---|---|
|
Вказує |
|
Вказує |
|
Вказує третій елемент індексованої властивості |
|
Вказує значення запису асоціативного масиву, проіндексованого за ключем |
(Цей розділ не є надзвичайно важливим, якщо не плануєш працювати з BeanWrapper
безпосередньо. Якщо використовуєш лише DataBinder
та BeanFactory
та їх реалізації за
замовчуванням, слід одразу перейти до PropertyEditors
.)
Наступні два приклади класів
використовують BeanWrapper
для отримання та встановлення властивостей:
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
}
Наступні фрагменти коду демонструють приклади отримання та маніпулювання деякими властивостями створених
екземплярів Company
і Employees
:
BeanWrapper company = new BeanWrapperImpl(new Company());
// встановлення назви компанії...
company.setPropertyValue("name", "Some Company Inc.");
// ... cтакож можна виконати так:
PropertyValue value = new PropertyValue("name", "Some Company Inc.");
company.setPropertyValue(value);
// Гаразд, давай створимо директора та прив'яжемо його до компанії:
BeanWrapper jim = new BeanWrapperImpl(new Employee());
jim.setPropertyValue("name", "Jim Stravinsky");
company.setPropertyValue("managingDirector", jim.getWrappedInstance());
// отримання salary для managingDirector через company
Float salary = (Float) company.getPropertyValue("managingDirector.salary");
val company = BeanWrapperImpl(Company())
// встановлення назви компанії...
company.setPropertyValue("name", "Some Company Inc.")
// ... також можна виконати таким чином:
val value = PropertyValue("name", "Some Company Inc.")
company.setPropertyValue(value)
// Гаразд, давай створимо директора і прив'яжемо його до компанії:
val jim = BeanWrapperImpl(Employee())
jim.setPropertyValue("name", "Jim Stravinsky")
company.setPropertyValue("managingDirector", jim.wrappedInstance)
// отримання salary для managingDirector через company
val salary = company.getPropertyValue("managingDirector.salary") as Float?
Вбудовані реалізації PropertyEditor
Spring використовує концепцію
PropertyEditor
для виконання перетворення між Object
та String
. Може бути
зручно представляти властивості інакше, ніж об'єкт. Наприклад, Date
можна представити у читабельному
для людини вигляді (як String
: '2007-14-09'
), у той час як ми всі ще можемо перетворити
форму читання для людини назад до вихідноъ дати (або, що ще краще, перетворити будь-яку дату, введену в
форму, зручну для читання людиною, назад в об'єкти Date
). Такої логіки роботи можна досягти, якщо
зареєструвати спеціальні редактори
типу java.beans.PropertyEditor
. Реєстрація спеціальних редакторів для BeanWrapper
або, як
варіант, у певному IoC-контейнері (як згадувалося в попередньому розділі), дає йому відомості про те, як перетворити
властивості на потрібний тип. Для отримання додаткової інформації про PropertyEditor
, див. javadoc за
пакетом java.beans
від Oracle.
Кілька прикладів використання функції редагування властивостей у Spring:
Встановлення властивостей бінів здійснюється за допомогою реалізацій
PropertyEditor
. Якщо ти використовуєшString
як значення властивості будь-якого біна, який ти оголошуєш в XML-файлі, Spring (якщо сетер відповідної властивості має параметрClass
) використовуєClassEditor
, щоб спробувати дозволити параметр до об'єктаClass
.Синтаксичний аналіз параметрів HTTP-запиту в MVC-фреймворку Spring здійснюється за допомогою різноманітних реалізацій
PropertyEditor
, які можна вручну прив'язати у всіх підкласахCommandController
.
Spring має ряд вбудованих реалізацій PropertyEditor
, які полегшують роботу. Всі вони знаходяться
у пакеті org.springframework.beans.propertyeditors
. Більшість із них (але не всі, як зазначено в
наступній таблиці) за замовчуванням реєструються BeanWrapperImpl
. Якщо редактор властивостей
налаштовується певним чином, ти можеш зареєструвати власний варіант, щоб перевизначити варіант за
замовчуванням. У наступній таблиці описано різні реалізації PropertyEditor
, які надає Spring:
Клас | Пояснення |
---|---|
|
Редактор для байтових масивів. Перетворює рядки у відповідні їм байтові уявлення. За замовчуванням
реєструється BeanWrapperImpl . |
|
Здійснює синтаксичний розбір рядків, що представляють класи, на реальні класи і навпаки. Якщо клас не
знайдено, генерується виняток |
|
Налаштований редактор властивостей для |
|
Редактор властивостей для колекцій, що перетворює будь-яку вихідну |
|
Налаштований редактор властивостей для |
|
Налаштований редактор властивостей для будь-якого підкласу |
|
Дозволяє рядки в об'єкти |
|
Односпрямований редактор властивостей, який може приймати рядок і створювати (через проміжний |
|
Може перетворювати рядки на об'єкти |
|
Можна перетворювати рядки в об'єкти |
|
Може перетворювати рядки (відформатовані у форматі, визначеному в javadoc класі |
|
Редактор властивостей, який обрізає рядки. Опціонально дозволяє трансформувати порожній рядок на
значення |
|
Може перетворити рядкове подання будь-якої URL-адреси на реальний об'єкт |
Spring використовує java.beans.PropertyEditorManager
для встановлення шляхів пошуку для
редакторів властивостей, які можуть знадобитися. Шлях пошуку також містить sun.bean.editors
, який
має реалізації PropertyEditor
для таких типів, як Font
, Color
та
більшості примітивних типів. Зверни увагу, що стандартна інфраструктура класу JavaBeans автоматично виявляє класи
PropertyEditor
(без необхідності їхньої явної реєстрації), якщо вони знаходяться в тому ж пакеті, що і
клас, що їх обробляє, і мають те ж ім'я, що і цей клас із додаванням Editor
. Наприклад, можна мати таку
структуру класів і пакетів, якої буде достатньо, щоб клас SomethingEditor
розпізнавався і
використовувався як PropertyEditor
для властивостей типу Something
.
com chank pop Something SomethingEditor // PropertyEditor for class Something
Зверни увагу, що в цьому випадку також можна використовувати стандартний механізм BeanInfo
класу
JavaBeans (деякою мірою описаний тут).
У наступному прикладі використовується механізм BeanInfo
для явної реєстрації одного або декількох
екземплярів PropertyEditor
з властивостями асоційованого класу:
com chank pop Something SomethingBeanInfo // BeanInfo for class Something
Наступний вихідний код Java для класу, що посилається, SomethingBeanInfo
пов'язує CustomNumberEditor
із властивістю age
класу 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())
}
}
}
Реєстрація додаткових реалізацій спеціального PropertyEditor
Під час встановлення
властивостей
біна як рядкових значень, IoC-контейнер Spring в кінцевому підсумку використовує стандартні реалізації JavaBeans
PropertyEditor
для перетворення цих рядків у складний тип властивості. Spring попередньо реєструє ряд
кастомних реалізацій PropertyEditor
(наприклад, перетворення імені класу, вираженого у вигляді рядка,
на
об'єкт Class
). До того ж, стандартний механізм пошуку PropertyEditor
в JavaBeans дозволяє
назвати PropertyEditor
для класу відповідним чином і помістити його в той же пакет, що і клас, для
якого він забезпечує підтримку, щоб його можна було знайти автоматично.
У разі необхідності зареєструвати
інші кастомні PropertyEditors
є кілька механізмів. Найбільш ручний підхід, який зазвичай не вважається
зручним або рекомендованим, полягає у використанні методу registerCustomEditor()
інтерфейсу ConfigurableBeanFactory
,
за умови, що є посилання на BeanFactory
. Інший (трохи зручніший) механізм — це використання
спеціалізованого постпроцесора фабрики бінів під назвою CustomEditorConfigurer
. Хоч і не можна
використовувати постпроцесори фабрики бінів за допомогою реалізації BeanFactory
, CustomEditorConfigurer
має вкладене налаштування властивостей, тому рекомендуємо використовувати його з ApplicationContext
у випадках, в яких його можна розгорнути аналогічно будь-якому іншому біну і в яких він може бути автоматично
виявлений і застосований для обробки перетворень властивостей. ApplicationContext
також перевизначає або додає додаткові редактори для обробки пошуку ресурсів відповідно до конкретного типу
контексту програми.
Стандартні екземпляри PropertyEditor
класу JavaBeans використовуються для
перетворення значень властивостей, виражених у вигляді рядків, на фактичний складний тип властивості. Можна
використовувати CustomEditorConfigurer
, постпроцесор фабрики бінів, для зручного додавання підтримки
додаткових екземплярів PropertyEditor
у ApplicationContext
.
Розглянемо наступний
приклад, в якому визначено користувальницький клас ExoticType
та інший клас
DependsOnExoticType
, якому як властивість потрібно встановити ExoticType
:
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
}
Якщо все налаштовано правильно, нам потрібно мати можливість привласнити властивості типу рядок, який PropertyEditor
перетворює на реальний екземпляр ExoticType
. Наступне визначення біна показує, як встановити це
відношення:
<bean id="sample" class="example.DependsOnExoticType">
<property name="type" value="aNameForExoticType"/>
</bean>
Реалізація PropertyEditor
може виглядати так:
// перетворює рядкове представлення на об'єкт ExoticType
package example;
public class ExoticTypeEditor extends PropertyEditorSupport {
public void setAsText(String text) {
setValue(new ExoticType(text.toUpperCase()));
}
}v
// перетворює рядкове представлення на об'єкт ExoticType
package example
import java.beans.PropertyEditorSupport
class ExoticTypeEditor : PropertyEditorSupport() {
override fun setAsText(text: String) {
value = ExoticType(text.toUpperCase())
}
}
Нарешті, в наступному прикладі показано, як використовувати CustomEditorConfigurer
для
реєстрації нового PropertyEditor
у ApplicationContext
, який потім зможе використовувати
його за необхідності:
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="customEditors">
<map>
<entry key="example.ExoticType" value="example.ExoticTypeEditor"/>
</map>
</property>
</bean>
Використання
PropertyEditorRegistrar
Іншим механізмом реєстрації редакторів властивостей у контейнері Spring
є створення та використання PropertyEditorRegistrar
. Цей інтерфейс особливо корисний, якщо потрібно
використовувати один і той самий набір редакторів властивостей у різних ситуаціях. Можна написати відповідний
реєстратор і використовувати його повторно в кожному випадку. Примірники PropertyEditorRegistrar
працюють спільно з інтерфейсом PropertyEditorRegistry
, який реалізується BeanWrapper
із
Spring (і DataBinder
). Примірники PropertyEditorRegistrar
особливо зручні при використанні
в поєднанні з CustomEditorConfigurer
, який надає властивість
setPropertyEditorRegistrars(..)
. Екземпляри PropertyEditorRegistrar
, додані до CustomEditorConfigurer
таким чином, можна легко використовувати разом з контролерами DataBinder
та Spring MVC. Це також
дозволяє уникнути необхідності синхронізації в кастомних редакторах: Очікується, що
PropertyEditorRegistrar
створюватиме нові екземпляри PropertyEditor
при кожній спробі
створення біна.
На прикладі показано, як створити власну реалізацію PropertyEditorRegistrar
:
package com.foo.editors.spring;
public final class CustomPropertyEditorRegistrar implements PropertyEditorRegistrar {
public void registerCustomEditors(PropertyEditorRegistry registry) {
// очікується, що будуть створені нові екземпляри PropertyEditor
registry.registerCustomEditor(ExoticType.class, new ExoticTypeEditor());
// у цьому випадку можна зареєструвати стільки кастомних редакторів властивостей, скільки потрібно...
}
}
package com.foo.editors.spring
import org.springframework.beans.PropertyEditorRegistrar
import org.springframework.beans.PropertyEditorRegistrar
class CustomPropertyEditorRegistrar : PropertyEditorRegistrar {
override fun registerCustomEditors(registry: PropertyEditorRegistry) {
// очікується, що будуть створені нові екземпляри PropertyEditor
register.CustomEditor(ExoticType::class.java, ExoticTypeEditor())
// у цьому випадку можна зареєструвати стільки спеціальних редакторів властивостей, скільки потрібно...
}
}
Дивись також приклад реалізації PropertyEditorRegistrar
у org.springframework.beans.support.ResourceEditorRegistrar
.
Зверни увагу, як у своїй реалізації методу registerCustomEditors(…)
він створює нові екземпляри
кожного редактора властивостей.
Наступний приклад показує, як налаштувати CustomEditorConfigurer
та впровадити у нього екземпляр нашого 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"/>
Нарешті (і трохи відхиляючись від теми цього розділу) для тих із вас, хто використовує веб-фреймворк MVC з
Spring,
використовувати PropertyEditorRegistrar
у поєднанні з контролерами збору, обробки та інтерпретації
даних у додатку інтернету, що прив'язують дані, може бути дуже зручно. У наступному прикладі використовується PropertyEditorRegistrar
для реалізації методу @InitBinder
:
@Controller
public class RegisterUserController {
private final PropertyEditorRegistrar customPropertyEditorRegistrar;
RegisterUserController(PropertyEditorRegistrar propertyEditorRegistrar) {
this.customPropertyEditorRegistrar = propertyEditorRegistrar;
}
@InitBinder
void initBinder(WebDataBinder binder) {
this.customPropertyEditorRegistrar.registerCustomEditors(binder);
}
// інші методи, пов'язані з реєстрацією користувача
}}
@Controller
class RegisterUserController(
private val customPropertyEditorRegistrar: PropertyEditorRegistrar) {
@InitBinder
fun initBinder(binder: WebDataBinder) {
this.customPropertyEditorRegistrar.registerCustomEditors(binder)
}
// інші методи, пов'язані з реєстрацією користувача
}
Такий стиль реєстрації PropertyEditor
може зробити код лаконічним (реалізація методу @InitBinder
займає лише один рядок) і дозволяє інкапсулювати загальний код реєстрації
PropertyEditor
до класу, а потім дозволити спільно використовувати його багатьом контролерам.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ