Статьи

JSF 2.0 New Feature Preview Series (часть 4) Перемещение ресурсов

Это шестая запись в серии предварительного просмотра новых функций JSF 2.0. Последняя запись покрыла новую систему событий. Для этой записи мы рассмотрим перераспределение ресурсов. Движущей силой этой функции является упрощение разработки страниц. Автору страницы не нужно знать, какие ресурсы нужны конкретному компоненту. Набор компонентов Tomahawk делает это с помощью установки пользователем фильтра, который обрабатывает ответ, выданный JSF. Это решение не так хорошо масштабируется, так как весь ответ необходимо буферизовать, анализировать, манипулировать и затем выводить. Создавая новые системы ресурсов и событий, мы можем избежать этого и позволить размещать ресурсы там, где они должны быть (например, таблицы стилей и ссылки на скрипты в элементе head).

А пока давайте начнем с механики на уровне страницы, а затем углубимся в то, как это работает. Спецификация 2.0 EDR1 требует четырех новых тегов.

  • h: head — представляет элемент head HTML-страницы
  • h: body — представляет элемент body HTML-страницы
  • h: outputScript — представляет ссылку на внешний файл javascript
  • h: outputStylesheet — это представляет ссылку на внешнюю таблицу стилей

Итак, вооружившись этими новыми тегами, давайте создадим простую лицевую сторону:

<!DOCTYPE html      
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html">
<h:head id="head">
<title>resourcereslocation</title>
</h:head>
<h:body id="body">
<h:form id="form">
<h:outputScript name="simple.js" target="#{param.location}"/>
<h:outputText value="Hello"/>
<h:outputStylesheet name="simple.css" target="#{param.location}"/>
</h:form>
</h:body>
</html>

В голове и тело теги довольно прямо вперед , как их атрибуты. Их основное назначение — знать, куда выводить перемещенные ресурсы. Теперь обратите внимание, что теги outputScript и outputStylesheet находятся внутри элемента формы. Имя атрибут для имени ресурса можно найти в ResourceHandler (Примечание: есть библиотека доступные атрибуты, а). В целевой атрибут указывает , где должно появиться отображаемое содержимое. В приведенном выше примере мы используем выражение, чтобы мы могли передать параметр запроса, чтобы легко изменить поведение.

Таким образом, учитывая все это, если параметр запроса местоположения отсутствует или цель не определена, ссылка на таблицу стилей будет отображаться в заголовке, а ссылка на сценарий будет отображаться встроенной:

<html xmlns="http://www.w3.org/1999/xhtml">    
<head>
<title>resourcereslocation</title>
<link type="text/css" rel="stylesheet" href="/ctx/faces/javax.faces.resource/simple.css" />
</head>
<body>
<form id="form" name="form" method="post" action="..." enctype="...">
<script type="text/javascript" src="/ctx/faces/javax.faces.resource/simple.js"></script>
hello
</form>
</body>
</html>

Выполните тот же запрос, на этот раз с параметром запроса location = head :

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>resourcereslocation</title>
<link type="text/css" rel="stylesheet" href="/ctx/faces/javax.faces.resource/simple.css" />
<script type="text/javascript" src="/ctx/faces/javax.faces.resource/simple.js"></script>
</head>
<body>
<form id="form" name="form" method="post" action="..." enctype="...">
hello
</form>
</body>
</html>

Затем снова выполните запрос на этот раз со значением параметра запроса, равным body :

<html xmlns="http://www.w3.org/1999/xhtml">    
<head>
<title>resourcereslocation</title>
<link type="text/css" rel="stylesheet" href="/ctx/faces/javax.faces.resource/simple.css" />
</head>
<body>
<form id="form" name="form" method="post" action="..." enctype="...">
hello
</form>
<script type="text/javascript" src="/ctx/faces/javax.faces.resource/simple.js"></script>
</body>
</html>

Обратите внимание, что рендеринг таблицы стилей игнорирует целевой атрибут и всегда рендерит ссылку внутри заголовка — как и должно быть!

Вспомните, как я сказал, что авторам страниц не нужно знать, какие ресурсы нужны компоненту? Ну, приведенные выше примеры только дошли до вас. Было бы утомительно, если бы использование стороннего набора компонентов потребовало бы использования разных ссылок h: outputScript или h: outputStylesheet в зависимости от того, какой компонент использовался. Таким образом, спецификация 2.0 добавила аннотацию @ResourceDependency, чтобы позволить авторам компонентов объявлять ресурсы, в которых будет нуждаться компонент. Простой пример может выглядеть так:

@ResourceDependency(name="style.css",library="corp")
public class MastHead extends UIComponentBase {
.
.
.
}

более сложный компонент может выглядеть так:

@ResourceDependencies({
@ResourceDependency(name="style.css",library="corp"),
@ResourceDependency(name="menu.js",library="corp",target="head")
})
public class Menu extends UIComponentBase {
.
.
.
}

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

Так как же это работает под одеялом? Рад, что ты спросил. Давайте основывать последующие примеры компонентов с одной аннотацией @ResourceDependency .

  1. Среда выполнения Facelets создаст компонент
  2. Компонент будет добавлен как дочерний элемент к другому компоненту. Перед возвратом из метода add () компонент будет проверен на наличие аннотаций @ResourceDependency (версия с одинарной или множественной версией).
  3. @ResourceDependency найден. Создается новый экземпляр компонента UIOutput.
  4. ResourceHandler запрашиваются для соответствующего видеообработки на основе типа содержимого ресурса, который в данном случае является текст / CSS, так что таблица стилей визуализация будет установлена в качестве Rendere для этого нового UIOutput.
  5. Значения атрибутов name, library и target (библиотеки и target являются необязательными) из аннотации хранятся в карте атрибутов компонента.
  6. UIViewRoot.addComponentResource () вызывается, передавая UIOutput и значение целевого атрибута из аннотации (если есть)

Все это произошло при построении представления. Теперь, когда мы визуализируем представление, средство визуализации головы (в данном случае это будет средство визуализации головы, поскольку в качестве примера мы используем таблицу стилей), мы просто кодируем каждый из ресурсов, предназначенных для головы, следующим образом:

UIViewRoot viewRoot = context.getViewRoot();
for (UIComponent resource : viewRoot.getComponentResources(context, "head")) {
resource.encodeAll(context);
}

Теперь я упомянул, что система событий вступает в игру с реализацией этой функции. Поэтому я был бы упущен, если бы не коснулся этого, но это требует возврата к примеру, где h: outputScript или h: outputStylesheet ссылаются в представлении. В этом случае нет компонента с аннотациями, поэтому, когда компонент добавляется в представление, последовательность событий, описанная выше, не произойдет, и, следовательно, UIViewRoot.addComponentResource () не будет вызван. В этом случае средства визуализации таблиц стилей и скриптов имеют немного дополнительной магии, чтобы помочь получить тот же конечный результат.

Средства визуализации используют еще одну новую аннотацию, @ListenerFor, Эта аннотация позволяет компоненту подписываться на определенные события с помощью самого компонента, в данном случае Renderer, в качестве слушателя. Поскольку эти два средства визуализации будут прослушивателями этих событий, они также реализуют интерфейс ComponentSystemEventListener, который был упомянут в предыдущей записи . Таким образом, рендерер с этими новыми наворотами выглядит примерно так:

@ListenerFor(systemEventClass=AfterAddToParentEvent.class, sourceClass=UIOutput.class)
public class ScriptRenderer extends Renderer implements ComponentSystemEventListener {
.
.
.
public void processEvent(ComponentSystemEvent event) throws AbortProcessingException {
UIComponent component = event.getComponent();
FacesContext context = FacesContext.getCurrentInstance();
String target = (String) component.getAttributes().get("target");
if (target != null) {
context.getViewRoot().addComponentResource(context, component, target);
}
}
.
.
.
}

Когда среда выполнения Facelets создает компонент, связанный с h: outputScript, он получает Renderer для этого компонента и запрашивает его для любых аннотаций @ListenerFor (конечно, существует форма множественного числа, @ListenersFor ). Для каждой найденной аннотации рендерер будет добавлен в качестве прослушивателя компонента для, в случае выше, события AfterAddToParent. Поэтому, когда среда выполнения Facelets добавляет компонент в дерево, вызывается событие AfterAddToParent и вызывается метод processEvent () этого рендерера, который добавляет компонент в качестве ресурса в ViewRoot с соответствующей целью.

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