Spring или (в более поздних разговорах)
Guice ?
Это сложный вопрос; Проще говоря, у Tapestry есть требования в качестве основы, для которой другие контейнеры не предлагают решений.
Эта публикация представляет собой простое введение в основы Tapestry 5 IoC. В последующих публикациях мы более подробно расскажем о расширенных функциях контейнера IoC Tapestry, которые действительно удаляют его от Spring и Guice.
Гобелен использует термин «сервис» для основных объектов, которыми он управляет для вас. Весна использует термин «боб». Служба обычно является интерфейсом и классом, который реализует интерфейс. В наиболее типичном случае только один сервис реализует интерфейс, но T5 IoC полностью способен обрабатывать случай, когда один сервисный интерфейс имеет несколько различных сервисов; даже случай, когда один класс создается с другой конфигурацией.
Каждый сервис имеет уникальную строку идентификатора. В большинстве случаев это просто простое имя интерфейса службы. Когда один и тот же интерфейс используется несколькими службами, вам придется явно идентифицировать идентификатор службы.
Чтобы сохранить реальность, я буду использовать реальные, хотя и сокращенные, примеры из базы кода Tapestry.
T5 IoC использует классы модулей, чтобы определить, какие услуги доступны. Класс модуля — это класс POJO со специальным методом, названным методом bind()
. Приложение Tapestry будет состоять из нескольких модулей: некоторые модули предоставляются самим Tapestry, некоторые — сторонними библиотеками или расширениями, а некоторые — самим приложением. Гобелен смешивает и сопоставляет всю эту информацию, все сервисы, определенные каждым из модулей, в единый реестр сервисов.
Это может звучать сложнее, чем на самом деле. Реальность такова, что в bind()
методе мы просто сопоставляем интерфейсы служб с соответствующими реализациями:
public final class TapestryModule
{
public static void bind(ServiceBinder binder)
{
binder.bind(ClasspathAssetAliasManager.class, ClasspathAssetAliasManagerImpl.class);
binder.bind(PersistentLocale.class, PersistentLocaleImpl.class);
binder.bind(ApplicationStateManager.class, ApplicationStateManagerImpl.class);
// ... and so on
}
}
ServiceBinder использует универсальные средства, чтобы гарантировать, что указанный класс реализует интерфейс службы. API — это свободный интерфейс: вы можете связать несколько дополнительных вызовов методов с bind () для переопределения значений по умолчанию, например:
binder.bind(ObjectProvider.class, AssetObjectProvider.class).withId("AssetObjectProvider");
TapestryModule на самом деле определяет довольно много дополнительных сервисов.
Давайте посмотрим на пример:
public interface PersistentLocale
{
void set(Locale locale);
Locale get();
boolean isSet();
}
Я убрал комментарии, чтобы сэкономить место … но этот сервис управляет локалью пользователя; это ключевая часть поддержки локализации в Tapestry 5. Реализация, которую мы вскоре увидим, работает с использованием куки-файлов HTTP, но это не важно для кода, использующего PersistentLocale.
public class PersistentLocaleImpl implements PersistentLocale
{
private static final String LOCALE_COOKIE_NAME = "org.apache.tapestry5.locale";
private final Cookies cookieSource;
public PersistentLocaleImpl(Cookies cookieSource)
{
this.cookieSource = cookieSource;
}
public void set(Locale locale)
{
cookieSource.writeCookieValue(LOCALE_COOKIE_NAME, locale.toString());
}
public Locale get()
{
String localeCookieValue = getCookieValue();
return localeCookieValue != null ? LocaleUtils.toLocale(localeCookieValue) : null;
}
private String getCookieValue()
{
return cookieSource.readCookieValue(LOCALE_COOKIE_NAME);
}
public boolean isSet()
{
return getCookieValue() != null;
}
}
T5 IoC делает все инъекции через конструктор. Это сделано для того, чтобы побудить вас записать свои зависимости в конечные поля, что является потокобезопасным. Как правило, ваши услуги будут неизменными объектами: все поля являются окончательными.
PersistentLocaleImpl зависит от другого сервиса, Cookies. А что такое Cookies? Это еще один сервисный интерфейс. Обратите внимание, что нам не нужно делать никаких дополнительных настроек здесь … так как есть один и только один сервис, который реализует интерфейс Cookies, это вся информация, необходимая Tapestry для объединения вещей.
Другие реализации сервисов внутри Tapestry имеют всего лишь нулевые зависимости и целых восемь. Нет теоретического предела, просто наличие нескольких зависимостей — это запах дизайна … что вы можете разбить вещи на более мелкие части.
Одним из отличительных признаков кодирования с использованием контейнера IoC является этот уровень краткости , также известный как преодоление блага . Учитывая, что PersistentLocaleImpl касается файлов cookie HTTP, можно подумать, что он каким-то образом завладеет объектом HttpServletRequest и начнет вызывать его getCookies()
и использовать addCookie()
для него … но вместо этого все детали взаимодействия с Servlet API и довольно неудобным API для HTTP-куки скрыт в реализации сервиса Cookies.
Это здорово … это делает реализацию PersistentLocaleImpl (как и любой другой код, который заботится о HTTP-куки) намного проще и проще для тестирования.
Сервисный цикл
Услуги гобеленов имеют определенный жизненный цикл:
- определенный
Идентифицирован через ServiceBinder, но еще не указан
- виртуальный
Прокси-сервер существует, который был введен как зависимость какой-либо другой службы, но методы прокси не были вызваны
- понял
Служба была создана с зависимостями
Прелесть этого в том, что ваш код совершенно не знает об этом; вся работа внутри Tapestry … создание прокси, реализация сервисов происходит лениво, но потокобезопасно. Как будто все службы создаются при запуске, не тратя времени на выполнение этой работы.
Опять же, привлекательность контейнера IoC заключается в том, что вы можете разбить ваше приложение на крошечные, легко проверяемые фрагменты, а контейнер IoC отвечает за соединение всего вместе во время выполнения. Это действительно приводит к новому способу кодирования и размышлениям о кодировании.
Методы построения сервисов
Иногда просто создание экземпляра класса недостаточно; может потребоваться дополнительная конфигурация как часть создания экземпляра класса. Tapestry 5 Предшественник IoC, HiveMind , достиг таких целей с помощью комплексных сервисных сервисов. В итоге получилось много XML.
T5 IoC выполняет то же, и более, используя методы построения сервисов ; методы модуля, которые создают сервис. Типичный случай, когда реализация службы должна прослушивать события от какой-либо другой службы:
public static TranslatorSource buildTranslatorSource(ComponentInstantiatorSource componentInstantiatorSource,
ServiceResources resources)
{
TranslatorSourceImpl service = resources.autobuild(TranslatorSourceImpl.class);
componentInstantiatorSource.addInvalidationListener(service);
return service;
}
Методы модуля с префиксом «build» являются методами построения сервисов. Интерфейс службы определяется из возвращаемого значения (TranslatorSource). Идентификатор сервиса — это явно «TranslatorSource» (то есть все после «build» в имени метода).
Здесь Tapestry внедряется в метод построения сервисов . ComponentInstantiatorSource — это служба, которая запускает события. ServiceResources — это нечто иное: это набор ресурсов, связанных с создаваемой службой … включая возможность создания объекта, включая зависимости . Здесь замечательно то, buildTranslatorSource()
что не нужно знать, каковы зависимости TranslatorSourceImpl, он может создать экземпляр класса с зависимостями, используя autobuild()
метод. Затем построитель службы добавляет новую службу в качестве прослушивателя ComponentInstantiatorSource, а затем возвращает его.
Это большое разделение задач : у нас есть конструктивная проблема (являющаяся слушателем событий), которая отличается от эксплуатационных проблем TranslatorSource. И они хранятся отдельно.
Вывод
Tapestry IoC имеет простой и лаконичный API для определения сервисов и, в большинстве случаев, автоматически обрабатывает зависимости. Конечным результатом является то, что становится детской игрой «разделяй и властвуй»: конвертируйте старый, монолитный, трудно поддерживаемый код в небольшие, легко тестируемые, легко понимаемые сервисы.
В будущих публикациях я более подробно расскажу о более продвинутых функциях Tapestry: сферах обслуживания, настройках и оформлении сервисов.