Статьи

Откройте для себя Spring Authoring

В этой статье я опишу, как полезная, но сильно недоиспользуемая функция Spring, определение пользовательских тегов в файлах определения бинов Spring.

Весенние пространства имен

Я начну с простого примера, взятого из документации Spring. До версии 2.0 была доступна только одна XML-схема. Итак, чтобы сделать константу доступной в виде bean-компонента и таким образом внедрить ее в другие bean-компоненты, вам нужно было определить следующее:

<bean id="java.sql.Connection.TRANSACTION_SERIALIZABLE"
class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean" />

Spring сделал это возможным, но понял, что это всего лишь уловка, чтобы представить константу как боб. Однако, начиная с Spring 2.0, инфраструктура позволяет вам использовать пространство имен util, так что предыдущий пример становится:

<util:constant static-field="java.sql.Connection.TRANSACTION_SERIALIZABLE"/>

На самом деле, теперь доступно много пространств имен:

Приставка Пространство имен Описание
боб http://www.springframework.org/schema/bean Исходная схема бобов
Util http://www.springframework.org/schema/util Утилиты: константы, пути свойств и коллекции
Util http://www.springframework.org/schema/jee JNDI поиск
языки http://www.springframework.org/schema/lang Использование других языков
Техас http://www.springframework.org/schema/tx операции
АОП http://www.springframework.org/schema/aop АОП
контекст http://www.springframework.org/schema/context ApplicationContext манипулирование

Каждый из них предназначен для уменьшения многословия и повышения читабельности, как показано в первом примере.

авторинг

Многим до сих пор неизвестно, является ли эта функция расширяемой, то есть Spring API предоставляет вам возможность написать свою собственную. Фактически, многие провайдеры фреймворков должны воспользоваться этим и предоставить свои собственные пространства имен, чтобы упростить интеграцию своего продукта с Spring. Некоторые уже делают: CXF с его многочисленными пространствами имен приходит на ум, но должны быть другие, о которых я не знаю.

Создание собственного пространства имен — это 4 этапа: два этапа проверки XML, два других — для создания самого компонента. Чтобы проиллюстрировать этот процесс, я буду использовать простой пример: я создам схему для EhCache , механизма кэширования по умолчанию в Hibernate.

Основной фабрикой бинов будет существующий EhCacheFactoryBean . Как таковая, она не будет настолько полезной, как реальная функция, но она позволит нам сосредоточиться на истинном авторском подходе, а не на деталях реализации EhCache.

Создание схемы

Создание схемы — это описание синтаксиса XML и, что более важно, ограничений. Я хочу, чтобы мой XML выглядел примерно так:

<ehcache:cache id="myCache" eternal="true" cacheName="foo"
maxElementsInMemory="5" maxElementsOnDisk="2" overflowToDisk="false"
diskExpiryThreadIntervalSeconds="18" diskPersistent="true" timeToIdle="25" timeToLive="50"
memoryStoreEvictionPolicy="FIFO">
<ehcache:manager ref="someManagerRef" />
</ehcache:echcache>

Поскольку я не собираюсь никого учить XML, вот схема. Просто обратите внимание на объявление пространства имен:

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://blog.frankel.ch/spring/ehcache" xmlns="http://blog.frankel.ch/spring/ehcache"
elementFormDefault="qualified">
<xsd:complexType name="cacheType">
<xsd:sequence maxOccurs="1" minOccurs="0">
<xsd:element name="manager" type="managerType" />
</xsd:sequence>
<xsd:attribute name="id" type="xsd:string" />
<xsd:attribute name="cacheName" type="xsd:string" />
<xsd:attribute name="diskExpiryThreadIntervalSeconds" type="xsd:int" />
<xsd:attribute name="diskPersistent" type="xsd:boolean" />
<xsd:attribute name="eternal" type="xsd:boolean" />
<xsd:attribute name="maxElementsInMemory" type="xsd:int" />
<xsd:attribute name="maxElementsOnDisk" type="xsd:int" />
<xsd:attribute name="overflowToDisk" type="xsd:boolean" />
<xsd:attribute name="timeToLive" type="xsd:int" />
<xsd:attribute name="timeToIdle" type="xsd:int" />
<xsd:attribute name="memoryStoreEvictionPolicy" type="memoryStoreEvictionPolicyType" />
</xsd:complexType>
<xsd:simpleType name="memoryStoreEvictionPolicyType">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="LRU" />
<xsd:enumeration value="LFU" />
<xsd:enumeration value="FIFO" />
</xsd:restriction>
</xsd:simpleType>
<xsd:complexType name="managerType">
<xsd:attribute name="ref" type="xsd:string" />
</xsd:complexType>
<xsd:element name="cache" type="cacheType" />
</xsd:schema>

И для тех, кто, как я, предпочитает графический дисплей:

Отображение схемы

Создание схемы — это только первая часть. Теперь мы должны сообщить об этом Spring. Создайте файл META-INF / spring.schemas и напишите следующую строку:

http\://blog.frankel.ch/spring/schema/custom.xsd=ch/frankel/blog/spring/authoring/custom.xsd

Просто позаботьтесь о том, чтобы вставить обратную косую черту, иначе она не будет работать. Он сопоставляет объявление схемы в XML с реальным файлом, который будет использоваться из jar!

Прежде чем идти дальше, и для более любопытного, просто обратите внимание, что в spring-beans.jar (v3.0) есть такой файл. Вот это содержание:

http\://www.springframework.org/schema/beans/spring-beans-2.0.xsd=org/springframework/beans/factory/xml/spring-beans-2.0.xsd
http\://www.springframework.org/schema/beans/spring-beans-2.5.xsd=org/springframework/beans/factory/xml/spring-beans-2.5.xsd
http\://www.springframework.org/schema/beans/spring-beans-3.0.xsd=org/springframework/beans/factory/xml/spring-beans-3.0.xsd
http\://www.springframework.org/schema/beans/spring-beans.xsd=org/springframework/beans/factory/xml/spring-beans-3.0.xsd
http\://www.springframework.org/schema/tool/spring-tool-2.0.xsd=org/springframework/beans/factory/xml/spring-tool-2.0.xsd
http\://www.springframework.org/schema/tool/spring-tool-2.5.xsd=org/springframework/beans/factory/xml/spring-tool-2.5.xsd
http\://www.springframework.org/schema/tool/spring-tool-3.0.xsd=org/springframework/beans/factory/xml/spring-tool-3.0.xsd
http\://www.springframework.org/schema/tool/spring-tool.xsd=org/springframework/beans/factory/xml/spring-tool-3.0.xsd
http\://www.springframework.org/schema/util/spring-util-2.0.xsd=org/springframework/beans/factory/xml/spring-util-2.0.xsd
http\://www.springframework.org/schema/util/spring-util-2.5.xsd=org/springframework/beans/factory/xml/spring-util-2.5.xsd
http\://www.springframework.org/schema/util/spring-util-3.0.xsd=org/springframework/beans/factory/xml/spring-util-3.0.xsd
http\://www.springframework.org/schema/util/spring-util.xsd=org/springframework/beans/factory/xml/spring-util-3.0.xsd

Это приносит некоторые замечания:

  • Весной едят свою собачью еду (это приятно знать)
  • Я не изучал код, но думаю, что именно поэтому XML-проверка файлов bean-компонентов Spring никогда не жалуется на то, что не находит схему через Интернет (реальная проблема в производственной среде из-за проблем безопасности межсетевого экрана). Это потому, что XSD смотрят внутрь банки
  • Если вы не укажете версию используемой вами схемы Spring (2.0, 2.5, 3.0 и т. Д.), Spring автоматически обновит ее для вас с каждой основной / вспомогательной версией jar. Если вы хотите это поведение, хорошо, если нет, вам придется указать версию

Создание парсера

Предыдущие шаги предназначены только для проверки XML, чтобы, например, вечный атрибут принимал логическое значение. Мы все еще не подключили наше пространство имен к фабрике Spring. Это цель этого шага.

Первое, что нужно сделать, это создать класс, который реализует org.springframework.beans.factory.xml.BeanDefinitionParser . Глядя на его иерархию, кажется, что org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser является хорошей точкой входа, поскольку:

  • XML не слишком сложен
  • будет одно определение бина

Вот код:

public class EhCacheBeanDefinitionParser extends AbstractSimpleBeanDefinitionParser {

private static final List&amp;amp;amp;lt;String&amp;amp;amp;gt; PROP_TAG_NAMES;

static {

PROP_TAG_NAMES = new ArrayList();

PROP_TAG_NAMES.add("eternal");
PROP_TAG_NAMES.add("cacheName");
PROP_TAG_NAMES.add("maxElementsInMemory");
PROP_TAG_NAMES.add("maxElementsOnDisk");
PROP_TAG_NAMES.add("overflowToDisk");
PROP_TAG_NAMES.add("diskExpiryThreadIntervalSeconds");
PROP_TAG_NAMES.add("diskPersistent");
PROP_TAG_NAMES.add("timeToLive");
PROP_TAG_NAMES.add("timeToIdle");
}

@Override
protected Class getBeanClass(Element element) {

return EhCacheFactoryBean.class;
}

@Override
protected boolean shouldGenerateIdAsFallback() {

return true;
}

@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {

for (String name : PROP_TAG_NAMES) {

String value = element.getAttribute(name);

if (StringUtils.hasText(value)) {

builder.addPropertyValue(name, value);
}
}

NodeList nodes = element.getElementsByTagNameNS("http://blog.frankel.ch/spring/ehcache", "manager");

if (nodes.getLength() &amp;amp;amp;gt; 0) {

builder.addPropertyReference("cacheManager",
nodes.item(0).getAttributes().getNamedItem("ref").getNodeValue());
}

String msep = element.getAttribute("memoryStoreEvictionPolicy");

if (StringUtils.hasText(msep)) {

MemoryStoreEvictionPolicy policy = MemoryStoreEvictionPolicy.fromString(msep);

builder.addPropertyValue("memoryStoreEvictionPolicy", policy);
}
}
}

Это заслуживает некоторых объяснений. Статический блок указывает, какие атрибуты являются действительными. Метод getBeanClass () возвращает класс, который будет использоваться, либо непосредственно как компонент, либо как фабрика. Метод shouldGenerateIdAsFallback () используется для того, чтобы сообщить Spring, что когда идентификатор не указан в XML, он должен сгенерировать его. Это позволяет создавать псевдоанонимные bean-компоненты (ни один bean-компонент не является действительно анонимным на фабрике Spring).

Настоящее волшебство происходит в методе doParse (): он просто добавляет каждое простое свойство, которое находит в конструкторе. Есть два интересных свойства: cacheManager и memoryStoreEvictionPolicy.

Первый, если он существует, является ссылкой на другой компонент. Поэтому его следует добавлять в конструктор не как значение, а как ссылку. Конечно, код не проверяет, объявил ли разработчик кеш-менеджера как анонимный компонент внутри ehcache, но проверка схемы уже позаботилась об этом.

Последний просто использует строковое значение, чтобы получить реальный объект и добавить его в качестве свойства для компоновщика. Аналогично, поскольку значение было перечислено в схеме, исключений, вызванных неправильным синтаксисом, не может быть.

Регистрация парсера

Последний шаг — регистрация парсера в Spring. Во-первых, вам просто нужно создать класс, который расширяет org.springframework.beans.factory.xml.NamespaceHandlerSupport и зарегистрировать обработчик под именем тега XML в его методе init ():

public class EhCacheNamespaceHandler extends NamespaceHandlerSupport {

public void init() {

registerBeanDefinitionParser("cache", new EhCacheBeanDefinitionParser());
}
}

Если у вас есть больше анализаторов, просто зарегистрируйте их в том же методе под каждым именем тега.

Во-вторых, просто сопоставьте ранее созданное пространство имен с вновь созданным обработчиком в файле META-INF / spring.handlers:

http\://blog.frankel.ch/spring/ehcache=ch.frankel.blog.spring.authoring.ehcache.EhCacheNamespaceHandler

Обратите внимание, что вы сопоставляете объявленный файл схемы с реальной схемой, а пространство имен — с обработчиком.

Вывод

Теперь, когда вы столкнулись с чрезмерно многословной конфигурацией bean-компонента, у вас есть возможность использовать эту изящную 4-шаговую технику, чтобы упростить ее. Этот метод, конечно, больше ориентирован на поставщиков продуктов, но может использоваться проектами при условии, что время, затрачиваемое на создание пространства имен, в реальном времени выигрывает по сравнению с обычными определениями бинов.

Вы найдете источники этой статьи здесь в формате Maven / Eclipse.

Идти дальше

  • Весенняя авторизация : версии 2.0 достаточно, так как ничего не изменилось (вообще?) Со следующими версиями
  • Весенний Javadoc относительно авторства

С http://blog.frankel.ch