Ajax для Java-разработчиков: Часть 3. Ajax и Direct Web Remoting (DWR)

Сериализация данных не может быть проще, чем эта!

В некоторых случаях Ajax-функциональность в приложениях может потребовать много новой и сложной работы. В третьей статье серии для Java-разработчиков в Ajax, Ф. Маккарти рассказывает, как использовать DWR, применяя JavaBeans прямо в JavaScript-коде и автоматизируя Ajax.

Филипп Маккарти (Philip McCarthy), консультант и разработчик программного обеспечения, независимый специалист

Филипп Маккарти (Philip McCarthy) - консультант и разработчик программного обеспечения, специализирующийся в области языка Java и Web-технологий. В данный момент он работает в проекте Hewlett Packard над Digital Media Platform в HP Labs, Бристол. В течение последних лет Фил разработал несколько толстых Web-клиентов, применяя асинхронную связь между сервером и машиной сценариев DOM. Он рад, что сейчас у нас есть название для этого. Вы можете связаться с Филом по e-mail: philmccarthy@gmail.com.



27.02.2007

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

В предыдущей статье, я показал, как использовать JavaScript Object Notation (JSON), чтобы сериализовать данные в формате, легко конвертируемом в JavaScript-объекты клиента. С этой схемой, вы можете вызвать запросы удаленных сервисов, используя код JavaScript, и получить в ответ графы объектов JavaScript, не похожие на получаемые при вызове удаленных процедур. Вы узнаете, как продумывать работу на шаг вперед, используя схему, которая формализует возможность вызова удаленных процедур серверных Java-объектов из клиентского кода JavaScript.

DWR - продукт с открытым исходным кодом, лицензированное Apache решение, состоящее из Java-библиотек на сервере, сервлета DWR и библиотеки JavaScript. DWR - это не только Ajax-RPC средство, пригодное для платформы Java, но и одно из самых продуманных приложений, и оно дает много полезных функций. См. Ресурсы чтобы скачать DWR прежде чем приступить к работе с примерами.

Что такое DWR?

Объясняя простым языком, DWR - это механизм, который преобразует методы объектов Java на сервере в код JavaScript. Действительно, с DWR вы можете исключить часть кода, отвечающую за работу машины цикла запросов-ответов Ajax, из вашего приложения. Это значит, что клиентской части вашего кода не придется работать напрямую с объектом XMLHttpRequest или с запросами сервера. Вам не нужно будет писать код для сериализации объектов или использовать дополнительные средства для размещения объектов в XML. Вы даже не будете писать код сервлета для трансляции Ajax-запросов в вызовы ваших доменных Java-объектов.

DWR разворачивается в вашем Web-приложении как сервлет. Рассматриваемый как "черный ящик", этот сервлет выполняет две основные роли: во-первых, для каждого выполняемого класса, DWR динамически генерирует JavaScript, чтобы включить его в вашу Web-страницу. Сгенерированный, JavaScript содержит в себе функции-заглушки, которые представляют собой соответствующие методы Java-класса и также самостоятельно работают с XMLHttpRequest. Эти запросы посылаются на сервлет DWR, который, выполняя свою вторую роль, транслирует запрос в вызов метода серверного Java-объекта и отправляет возвращаемое значение метода обратно на сторону клиента в качестве ответа сервлета в виде JavaScript-кода. DWR также имеет набор полезных функций JavaScript, который помогает выполнять стандартные UI-задачи.


О примере

Перед более детальным объяснением DWR, я приведу типичный пример сценария. Как и в предыдущих статьях, я буду использовать минимальную модель, представляющую он-лайн магазин, на этот раз состоящий из основного представления продуктов, списка покупок пользователя, который может содержать продукты, а также объекта "Доступ к данным" (DAO) для поиска детальной информации о продукте в хранилище данных. Класс Item - такой же, как и в предыдущей статье, но он больше не использует ни один из "ручных" методов сериализации. Рисунок 1 изображает эту простую схему:

Рисунок 1. Диаграмма классов Cart, CatalogDAO и Item
Рисунок 1. Диаграмма классов, изображающая классы Cart, CatalogDAO и Item

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


Работа с каталогом

Стартовая точка DWR-приложения - написание модели серверного объекта. В этом случае, я начинаю работу с написания объекта "Доступа к данным" (DAO), чтобы обеспечить возможность поиска в базе данных каталога продуктов. CatalogDAO.java - простой класс, не имеющий состояния, с конструктором без аргументов. Листинг 1 показывает характерные черты Java-методов, которые будут работать с Ajax-клиентами:

Листинг 1. CatalogDAO-методы для работы в DWR
 /** 
 * Вовращает список продуктов в каталоге, 
 * у которых есть имена и описания, индексируемые в поиске 
 * [@param expression] описание параметров для поиска продукта 
 * по имени и описанию 
 * [@return list] список возвращаемого методом значения всех подходящих продуктов 
 */ 
public List<Item> findItems(String expression);

/** 
* Возвращает Item в соответствии с его заданным Item ID 
* [@param id] описание параметра - ID код для продукта
* [@return] возвращает подходящий продукт 
*/ 
public Item getItem(String id);

Далее мне необходимо сконфигурировать DWR, давая ему понять, что Ajax-клиенты должны иметь возможность создавать CatalogDAO и вызвать его методы. Я делаю это в конфигурационном файле 'dwr.xml', показанном в листинге 2:

Листинг 2. Конфиг для работы с CatalogDAO методами
 <!DOCTYPE dwr PUBLIC
  "-//GetAhead Limited//DTD Direct Web Remoting 1.0//EN"
  "http://www.getahead.ltd.uk/dwr/dwr10.dtd">
<dwr>
  <allow>
    <create creator="new" javascript="catalog">
      <param name="class" 
        value="developerworks.ajax.store.CatalogDAO"/>
      <include method="getItem"/> 
      <include method="findItems"/> 
    </create> 
    <convert converter="bean" 
      match="developerworks.ajax.store.Item">
      <param name="include" 
        value="id,name,description,formattedPrice"/>
    </convert>
  </allow>
</dwr>

Корневой элемент документа 'dwr.xml' - dwr. Внутри этого элемента находится элемент allow, который определяет классы, которые DWR использует удаленно. Два дочерних элемента allow - элементы create и convert.

Элемент create

Элемент create сообщает DWR, что класс на стороне сервера должен быть доступен Ajax-запросам и определяет, каким образом DWR получает экземпляр класса для вызова. Атрибут creator здесь имеет значение new, это значит, что DWR для получения экземпляра класса должен вызывать конструктор по умолчанию. С другой стороны есть возможность создать экземпляр через фрагмент скрипта, используя Bean Scripting Framework (BSF), или получить экземпляр за счет интеграции с IOC-контейнером, Spring. По умолчанию, когда запрос от Ajax к DWR вызывает creator, созданный объект помещается в область видимости страницы и становится недоступным после завершения запроса. В случае, когда CatalogDAO - класс без состояния, это нас полностью устраивает.

Атрибут javascript элемента create определяет имя, по которому объект будет доступен из JavaScript-кода. Вложенный в create элемент param определяет Java-класс, который создаст атрибут creator. Наконец, include-элементы определяют имена методов, которые должны быть доступны. Подробное описание доступных методов - хорошая практика для предотвращения случайного разрешения доступа к потенциально опасным функциям -- если это описание пропущено, все методы класса будут доступны для удаленных вызовов. С другой стороны, вы можете использовать элементы exclude , чтобы определить только те методы, доступ к которым вы хотите запретить.

Конвертирование

В то время как creator соответствует доступным классам и их методам удаленного доступа, convertor соотносится с параметрами и типами возвращаемых значений классов. Роль элемента convert - сообщить DWR, как преобразовывать типы данных между серверным представлением Java-объекта и сериализованным JavaScript-представлением, и наоборот.

DWR автоматически переносит простые типы данных между Java и JavaScript-представлениями. Эти типы включают Java-примитивы, наряду с представлениями их классов, а также Strings и Dates, массивы и сложные типы. DWR также может преобразовывать JavaBeans в JavaScript-представления, но по некоторым соображениям безопасности, этот процесс требует подробного и точного конфигурирования.

Элемент convert в листинге 2 сообщает DWR, как использовать его bean-конвертор, основанный на отражении, для элементов Item, возвращаемых доступными методами CatalogDAO и определяет, какой из членов Item следует включить в сериализацию. Параметры определяются с использованием JavaBean- соглашения об именах, по этой причине DWR будет вызывать соответствующие get методы. В данном случае, я опускаю числовое поле price и вместо него включаю поле formattedPrice, которое готово для вывода валюты в отформатированном виде.

Теперь, я готов развернуть dwr.xml в WEB-INF директории нашего Web-приложения, где его "подцепит" DWR. Перед тем, как продолжить, будет неплохо удостовериться в том, что все будет работать так, как мы ожидаем.


Тестирование DWR на примере dwr.xml

Если файле описания web.xml сервлета DWRServlet начальное значение init-param параметра debug установлено в true, станет доступным чрезвычайно полезный модуль тестирования. В ходе навигации по директории /{your-web-app}/dwr/ можно получить список классов, которые сконфигурированы DWR для удаленного вызова. Щелкая мышью по списку классов можно получить статус выбранного класса. Тестовая страница DWR для CatalogDAO показана на рисунке 2. Наряду с тегом скрипта для размещения на Web-страницах, указывающим на сгенерированный DWR JavaScript для класса, этот экран также список методов класса. Этот список включает в себя методы, унаследованные от родительского класса, но как доступные помечены только те методы, для которых удаленный доступ прописан в dwr.xml.

Рисунок 2. Тестовая страница DWR для CatalogDAO
Диагностическая и тестовая страница, сгенерированная с помощью DWR для CatalogDAO

Можно ввести значения параметров в поля ввода, расположенные сразу за доступными методами, и нажать кнопку Execute для их вызова. Ответ сервера будет отображен с использованием нотации JSON в новом окне, но только в том случае, если это не простое значение: в данном случае ответ сервера будет отображен в строке рядом с методом. Такие тестовые страницы очень полезны. Они не только позволяют проверить, какие классы и методы доступны для удаленного вызова, но также делают возможным тестирование логики метода.

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


Вызов объекта дистанционного выполнения

Отображение методов удаленных Java-объектов на соответствующие им JavaScript-заглушки довольно простое. В общем случае JavaScriptName.methodName(methodParams ..., callBack), где JavaScriptName это любое имя, указанное в JavaScript-атрибуте creator; methodParams представляет n параметров Java-метода; callback JavaScript-функция, которая будет вызвана с возвращаемым значение Java-метода. Если вы знакомы с Ajax, вы узнаете механизм обратного вызова, который обычно используется в асинхронной работе с XMLHttpRequest.

В сценарии примера я использую JavaScript-функции из листинга 3 для выполнения поиска и обновления пользовательского интерфейса в результате поиска. В этом листинге также использованы удобные функции из util.js DWR. В данном случае для JavaScript-функций использована запись $(), что можно рассматривать как укороченную запись document.getElementById(). Так гораздо легче печатать. Если вы пользовались JavaScript-библиотекой прототипов, вам должна быть знакома эта функция.

Листинг 3. Вызов удаленного метода findItems().
 /* 
 * Обрабатываем форму поиска 
 */ 
 function searchFormSubmitHandler() {

  // Получаем результат поиска из поля поиска 
  var searchexp = $("searchbox").value;

  // Вызываем удаленный DAO метод и определяем функцию обратного вызова 
  catalog.findItems(searchexp, displayItems);

  // Вовращаем false, чтобы не заполнять форму 
  return false; }
       
/* 
* Отображаем список продуктов из каталога 
*/ 
function displayItems(items) {

  // Удаляем имеющиеся результаты поиска, показанные на экране 
  DWRUtil.removeAllRows("items");

 if (items.length == 0) {
    alert("No matching products found");
    $("catalog").style.visibility = "hidden";
  } else {

    DWRUtil.addRows("items",items,cellFunctions);
    $("catalog").style.visibility = "visible";
  }
}

В использованной выше функции searchFormSubmitHandler() интересным представляется, конечно, catalog.findItems(searchexp, displayItems);. Эта строка - это все, что нужно для отправки по сети XMLHttpRequest в сервелет DWR и вызова функции displayItems() с ответом от удаленного объекта.

Обратный вызов displayItems() выполняется с массивом представлений Item. Этот массив передается в функцию Item вместе с ID заполняемой таблицы и массивом функций. В этом массиве столько же функций сколько ячеек в каждой строке таблицы. Каждая фунция вызывается вместе с элементом массива Item и должна возвращать содержимое для заполнения соответствующей ячейки.

В данном случае, мне нужно в каждой строке таблицы отобразить название продукта, описание и стоимость, а также кнопку Add to Cart в последней колонке для каждого продукта. Листинг 4 показывает массив функций ячеек, который выполняет заданную логику:

Листинг 4. Массив функций вызова, чтобы заполнить таблицу продуктов
 /* 
 * Массив функций для заполнения строки таблицы продуктов, 
 * используя функцию DWRUtil's 
 */
 var cellFunctions = [
  function(item) { return item.name; },
  function(item) { return item.description; },
  function(item) { return item.formattedPrice; },
  function(item) {
    var btn = document.createElement("button");
    btn.innerHTML = "Add to cart";
    btn.itemId = item.id;
    btn.onclick = addToCartButtonHandler;
    return btn;
  }
];

Первые три функции просто возвращают содержимое полей, включенных в convertor из Item в dwr.xml. Последняя функция создает кнопку, присваивает ей ID продукта и определяет, что функция с именем addToCartButtonHandler должна быть вызвана при нажатии на кнопку. Функция является является точкой входа для второго прецедента: добавления продуктов в список покупателя.


Заполнение списка покупателя

Безопасность в DWR

DWR был разработан с учетом требований безопасности. Использование dwr.xml для перечисления только тех классов и методов, для которых разрешен удаленный доступ, позволяет избежать появление непредвиденного взаимного влияние элементов программы. В дополнение к этому, за счет использования режима тестирования достаточно просто контролировать классы и методы доступные в Web.

DWR также поддерживает безопасность на основе ролевого разделения доступа. Вы можете задать роль J2EE, которая должна быть присвоена пользователю для доступа к определенному bean-компоненту за счет конфигурации его элемента creator. Разворачивая множество экземпляров DWRServlet с защитой на уровне URL и конфигурационными dwr.xml файлами для каждого, вы можете создать несколько групп пользователей с разными правами удаленного доступа.

Java-представление списка продуктов пользователя основано на Map. Когда какой-либо продукт добавляется в список, Item помещается в Map как ключ. Соответствующее значение в Map - это Integer (целое число), представляющее количество единиц заданного продукта в списке. То есть в Cart.java есть элемент contents, объявленный как Map<Item,Integer>.

Использование сложных типов таких, как смешанные ключи, представляет проблему для DWR, так как в JavaScript ключи массивов должны быть литеральными. Таким образом contents в Map не может быть преобразован DWR в том виде, в каком он есть. Однако для целей пользовательского интерфейса списка продуктов покупателя, пользователю необходимо видеть только название и количество каждого продукта в списке. Поэтому я могу добавить метод getSimpleContents() в Cart, который получает содержимое Map и стоит упрощенный вид Map<String,Integer> из него, представляя только имя и количество каждого продукта Item. Отображение представления со строковыми ключами может быть просто конвертировано в JavaScript встроенными конверторами DWR.

Другое поле в Cart, в котором заинтересован клиент, - это totalPrice, представляющая собой общую стоимость содержимого списка продуктов покупателя. Как и в Item, я ввел искусственный элемент formattedTotalPrice, который обеспечивает числовое форматированное представление суммы.

Конвертирование Карты

Вместо использования клиентского кода, делающего два вызова Cart: один для получения содержимого списка, а другой для получения общей стоимости; я попробую отправлять эти данные клиенту сразу. Чтобы добиться этого я добавил выглядящий странно метод, показанный в листинге 5:

Листинг 5. Метод Cart.getCart()
/** 
* Возвращает сам список - для DWR
* @return the cart
*/
public Cart getCart() {
  return this;
}

Хотя этот метод будет выглядеть избыточным в обычном Java-коде (потому что у вас уже есть ссылка на Cart при вызове метода), он позволит DWR-клиенту получить сериализацию Cart в JavaScript.

Кроме getCart(), нужно обеспечить удаленный доступ к другому методу: addItemToCart(). Этот метод получает String-представление ID продукта из каталога, и добавляет продукт в список покупателя Cart, обновляя общую стоимость. Кроме этого метод возвращает весь список Cart целиком, поэтому клиентский код может обновлять содержимое Cart и получать его новое состояние в рамках одной операции.

В листинге 6 показан расширенный dwr.xml конфигурационный файл, который включает в себя дополнительную информацию по конфигурации удаленного вызова класса Cart:

Листинг 6. Модицифированный dwr.xml, включающий в себя настройки для класса Cart
<!DOCTYPE dwr PUBLIC
    "-//GetAhead Limited//DTD Direct Web Remoting 1.0//EN"
    "http://www.getahead.ltd.uk/dwr/dwr10.dtd">
<dwr>
  <allow>
    <create creator="new" javascript="catalog">
      <param name="class" 
        value="developerworks.ajax.store.CatalogDAO"/>
      <include method="getItem"/>
      <include method="findItems"/>
    </create>
    <convert converter="bean" 
      match="developerworks.ajax.store.Item">
      <param name="include" 
        value="id,name,description,formattedPrice"/>
    </convert>
<create creator="new" scope="session" javascript="Cart">
      <param name="class" 
        value="developerworks.ajax.store.Cart"/>
      <include method="addItemToCart"/>
      <include method="getCart"/>
    </create>
    <convert converter="bean" 
      match="developerworks.ajax.store.Cart">
      <param name="include" 
        value="simpleContents,formattedTotalPrice"/>
    </convert>
 </allow>
</dwr>

В данной версии dwr.xml, я добавил creator и convertor для Cart. Элемент create определяет, что методы addItemToCart() и getCart() должны быть удаленными и, что более важно, создаваемый экземпляр Cart должен быть размещен в пользовательской сессии. В результате, содержимое списка будет сохраняться между запросами пользователя.

Элемент convert для Cart необходим, потому что вызываемые удаленно методы Cart возвращают сам список Cart. Здесь я определил, что члены Cart, которые должны быть представлены в его сериализованной JavaScript-форме, - это simpleContents и formattedTotalPrice.

Если вы находите это несколько запутанным, вспомните, что элемент create определяет серверные методы Cart, которые могут быть вызваны DWR-клиентами, а элемент convert определяет члены, которые нужно включить в JavaScript-сериализацию Cart.

Теперь можно использовать клиентский код, который вызывает мои удаленные методы для Cart.


Вызов удаленных методов Cart

Во-первых, при первой загрузке Web-страницы магазина я собираюсь проверить состояние Cart, сохраненной в сессии, если она есть. Это необходимо, потому что пользователь мог добавить продукты в список, а затем обновить страницу или перейти на другую и вернуться обратно. В данных обстоятельствах, перезагруженная страница должна синхронизироваться с данными списка Cart в сессии. Я могу добиться этого за счет вызова, выполняемого в функции загрузки страницы, например так: Cart.getCart(displayCart). Заметьте, что displayCart() - это функция обратного вызова, вызванная с данными из ответа Cart с сервера.

Если список Cart уже есть в сессии, creator получит его и вызовет его метод getCart(). Если нет, то creator создаст новый экземпляр, разместит его в сессии и вызовет метод getCart().

Листинг 7 показывает применение функции addToCartButtonHandler(), которая вызывается при нажатии кнопки Add to Cart:

Листинг 7. Выполнение метода addToCartButtonHandler()
	/* Кнопка "Add to Cart" для нажатия */ 
	function addToCartButtonHandler() {

  // 'this' - это нажатая кнопка. 
  // Получить ID (номер) продукта,который ему соответствует 
  // и добавить его в карту
   Cart.addItemToCart(this.itemId,displayCart); 
 }

Так как все взаимодействие сосредоточено в DWR, вся логика добавления товара в список представлена в виде функции, занимающей одну строку. Листинг 8 показывает последний кусочек мозаики: применение обратного вызова displayCart(), который обновляет пользовательский интерфейс, используя Cart:

Листинг 8. Выполнение displayCart()
 /* 
 * Выводит на экран содержимое списка продуктов покупателя 
 */
  function displayCart(cart) {

  // Стирает существующее содержимое списка в  UI 
  var contentsUL = $("contents"); contentsUL.innerHTML="";

  // Для каждого продукта 
  for (var item in cart.simpleContents) {

    // Добавляет элемент списка -- продукт -- с описанием его наименования и количества 
    var li = document.createElement("li");
    li.appendChild(document.createTextNode(
                    cart.simpleContents[item] + " x " + item
                  ));
    contentsUL.appendChild(li);
  }

  // Обновляет итоговые данные списка 
  var totalSpan = $("totalprice");
  totalSpan.innerHTML = cart.formattedTotalPrice;
}

Важно помнить, что simpleContents - это JavaScript отображающий String в численное представление. Каждая строка - это название продукта, а соответствующее число в ассоциативном массиве - это количество единиц продукта в списке. То есть выражение cart.simpleContents[item] + " x " + item значит "2 x Oolong 128MB CF Card".

Приложение базы DWR

Рисунок 3 показывает мое Ajax-приложение, основанное на DWR, в действии: оно отображает продукты, полученные в результате поиска и список продуктов пользователя справа:

Рисунок 3. Ajax-программа, действующая при помощи DWR, в действии:
Рисунок 3. Ajax-программа, действующая при помощи DWR, в действии:

Преимущества и недостатки DWR

Объединение вызовов

В DWR несколько удаленных вызовов могут быть отправлены на сервер в рамках одного HTTP-запроса. Вызов DWREngine.beginBatch() сигнализирует DWR о том, что удаленные серверы не следует отправлять немедленно и поодиночке, вместо этого их следует соединить в один запрос. Вызов DWREngine.endBatch() отправляет запрос на сервер. Удаленные вызовы по очереди обрабатываются на стороне сервера, а затем выполняются обратные вызовы для каждого JavaScript.

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

Вы увидели, как легко создать основанную на Java Ajax-программу с использованием DWR. Хотя сценарий примера достаточно прост, и я руководствовался минималистским подходом при реализации прецедентов, не стоит недооценивать количество работы, от которого вам помог избавиться DWR при создании Ajax-приложения. В прошлых статьях я рассмотрел все шаги по созданию вручную запроса и ответа Ajax и конвертации графа Java-объекта в представление JSON, здесь же DWR всю работу сделал за меня. Я написал меньше, чем 50 строчек JavaScript-кода для реализации клиента, а на серверной стороне все, что мне нужно было сделать, - это добавить к моим обычным JavaBeans пару дополнительных методов.

Конечно, у каждой технологии есть свои недостатки. Как и в любом другом RPC-механизме, в DWR можно легко забыть, что каждый вызов удаленного объекта сопряжен с большими затратами ресурсов, чем вызов локальной функции. DWR выполняет большую работу, скрывая механизм действия Ajax, но важно помнить, что сеть непрозрачна -- при осуществлении DWR-вызовов существует некоторая задержка, и ваше приложение должно быть построено таким образом, чтобы все удаленные методы были четко определены и понятны. Именно по этой причине метод addItemToCart() возвращает сам Cart. Хотя было бы более естественным сделать метод addItemToCart() пустым, а каждый запрос к нему от DWR следовало бы сопровождать вызовом getCart(), получающим измененное состояние Cart.

В DWR есть собственное решение проблемы задержек при объединении вызовов (Смотрите примечание Объединени вызовов). Если вы не в состоянии обеспечить подходящую четкую структуру Ajax-интерфейса для вашего приложения, используйте объединение вызовов в тех местах, где возможно комбинировать множественные удаленные вызовы в одном HTTP-запросе.

Разделение

По своей природе, DWR создает жесткую связь между клиентской и серверной частями кода, с некоторым числом соединений. Во-первых, изменения в API удаленных методов должны быть отражены в JavaScript, который обращается к заглушкам DWR. Во-вторых, более важно то, что эта связь вызывает изменения в клиентской части кода, которые должны быть отражены в серверной части. Например, так как не все Java-типы могут быть конвертированы в JavaScript, иногда необходимо добавлять дополнительные методы в Java-объекты для облегчения их удаленного вызова. В сценарии примера, я решил эту проблему, добавив getSimpleContents() в объект Cart. Также я добавил метод getCart(), который полезен в сценарии DWR, но является полностью избыточным. Отдав должное четко-структурированному API удаленных объектов и проблеме конвертирования некоторых Java-типов в JavaScript, вы можете увидеть, как удаленные JavaBeans могут быть перегружены методами, которые полезны только для Ajax-клиентов.

Чтобы обойти это обстоятельство, вы можете использовать промежуточные классы для добавления специфичных для DWR методов в ваши JavaBeans. Это значит, что Java-клиенты JavaBean-классов не увидят ничего лишнего, свзяанного с удаленным доступом, также это позволит вам давать удаленным методам более простые имена: getPrice() вместо getFormattedPrice(), например. Рисунок 4 показывает класс RemoteCart, который добавляет в Cart функциональность DWR:

Рисунок 4. RemoteCart добавляет в Cart еще одну удаленную функцию
Диаграмма класса для метода RemoteCart

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


В заключение

Как вы увидели, у DWR есть много того, чем он может заинтересовать: он позволяет вам просто и быстро создавать интерфейс для ваших серверных доменных объектов без необходимости написания кода сервлета, кода сериализации объекта или клиентского кода XMLHttpRequest. Интерфейс очень просто развернуть на вашем Web-приложении с использованием DWR, а структура безопасности DWR может быть интегрирована в J2EE систему безопасности с ролевым доступом. DWR нельзя применять для всех архитектур приложений, однако, он дает вам ряд полезных идей по созданию API ваших доменных объектов.

Если вы хотите узнать больше о преимуществах и недостатках Ajax на DWR, лучшим способом будет скачать его и начать экспериментировать. В DWR есть множество других особенностей, которые я не рассмотрел. Исходный код примера - это хорошая стартовая точка для начала работы с DWR. Ознакомьтесь с разделом Ресурсы, чтобы узнать больше об Ajax, DWR и родственных технологиях.

Самое важное, что вы должны вынести из данной серии статей, - это то, что для Ajax-приложений нет универсального решения. Ajax - быстро развивающаяся область с постоянно-появляющимися новыми технологиями и способами использования. В трех статьях этой серии я сконцентрировал внимание на том, чтобы дать вам отправную точку в изучении использования Java в приложении к Ajax Web-приложениям: выберите ли вы основанный на XMLHttpRequest подход с оболочкой для сериализации объектов или более высокий уровень абстракции DWR. Следите за новыми статьями в серии "Ajax для Java-разработчиков".


Загрузка

ОписаниеИмяРазмер
Исходный код DWRj-ajax3dwr.zip301 KB

Ресурсы

Научиться

Получить продукты и технологии

Обсудить

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Технология Java, XML
ArticleID=198207
ArticleTitle=Ajax для Java-разработчиков: Часть 3. Ajax и Direct Web Remoting (DWR)
publish-date=02272007