воскресенье

Про AppFuse

AppFuse, если кто не знает, это опенсорсный проект на Java, разрабатываемый Matt Raible сотоварищи, неплохо ускоряющий разработку несложных веб-приложений. А уж для обучения основам работы с Java-фреймворками (Hibernate, iBatis, Spring, Struts2 и т.д.) AppFuse-у вообще, на мой взгляд, нет равных.

Собственно объяснять как конкретно работать с AppFuse смысла особого нет, т.к. это и так подробно описано на сайте проекта. Здесь же будет рассказано о том, чем AppFuse может быть полезен.

Речь пойдут о второй версии AppFuse, а точнее о версии 2.2. Первая версия работала через Ant, вторая интегрирована уже с Maven. Это позволило создателям AppFuse написать свой собственный плагин к Maven, что делает работу с AppFuse более комфортной.

Итак, работа с AppFuse начинается с создания структуры директорий на основе одного из предлагаемых архетипов Maven. 

В итоге, какой бы вы архетип не выбрали, вы получаете готовое работающее веб-приложение, к которому уже прикручен ряд фреймворков (взависимости от выбора архетипа).  В pom.xml также определены несколько альтернативных профилей, позволяющих запускать AppFuse на разных СУБД.

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

По умолчанию прикручены hbm2dll (примочка для Hibernate - создает схему по классам сущностей) и dbunit (система, облегчающая тестирования баз данных). Вообще тестирование организовано на должном уровне, как модульное, так и интеграционное (при помощи Canoo WebTest). 
Также реализована система авторизации и админка для работы с пользователями.

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

Помимо всего прочего, AppFuse-овский плагин предлагает возможности, сравнимые со scaffolding-ом Ruby on Rails. Запустив соответствующую команду и указав имя сущности, вы получите готовую страницу просмотра списка этих сущностей и страницу с формой редактирования сущности. 

Сделано это, правда, будет не как в RoR - динамически, а статически, т.е. просто будут одномоментно сгенерированы файлы с соответствующим кодом, и помещены в соответствующие директории. Проблема в том, что при этом будут внесены изменения в почти во все конфигурационные файлы, и избавиться от сгенерированного будет непросто.

Также плагин не умеет по-нормальному генерировать формы для редактирования объектов, у которых есть какие-либо ассоциации (т.е. дочерние или родительские сущности). Подобные связи будут проигнорированы, и сохранение будет работать неправильно, пока вы не обработаете код напильником.

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

Это beta-версия статьи.

Использование MultiActionController для CRUD. Часть 1

Какую функциональность чаще всего приходиться реализовывать в веб-приложениях? Не знаю, как вам, а лично мне очень часто приходится программировать стандартные CRUD-операции над разными сущностями. На всякий случай напомню, что CRUD расшифровывается как Create, Read, Update, Delete (т.е. по-русски это будет примерно так: Создание, Чтение, Обновление, Удаление).

Данная статья предполагает, что вы используете программный каркас Spring версии 2.5, в частности, одну из его компонент – Spring MVC. Рассмотрим, что Spring MVC может нам предложить для CRUD по части контроллеров.

Spring MVC предлагает нам большое количество разнообразных контроллеров. Это и SimpleFormController для упрощения работы с формами, и AbstractWizardFormController для построения т.н. «мастеров». Есть среди контроллеров и MultiActionController, который, собственно, и представляет сейчас для нас наибольший интерес.

Данный контроллер поддерживает (вольный пересказ Spring Reference) «аггрегацию множественных методов обработки запросов в один контроллер для группировки схожей функциональности». Думаю, бесспорно, что CRUD-операции над сущностью попадают под определение «схожей функциональности».

Итак, для начала нам следует создать наследника класса MultiActionController.

public class BeanController extends MultiActionController { ... }

Затем добавим к контроллеру методы list, view, edit, save.

public class BeanController extends MultiActionController {

    //просмотр списка сущностей

    public ModelAndView list(HttpServletRequest request, 

        HttpServletResponse response) { ... }


//просмотр сущности

public ModelAndView view(HttpServletRequest request, 

        HttpServletResponse response) { ... }

 

//подготовка к редактированю сущности

public ModelAndView edit(HttpServletRequest request, 

        HttpServletResponse response) { ... }

 

//сохранение/обновление и удаление сущности

public ModelAndView save(HttpServletRequest request, 

        HttpServletResponse response, Bean beanToSave) { ... }

}


Надо отметить, что эти методы не соответствуют CRUD-операциям на 100%.  Однако с их помощью будет легко соорудить UI схожий с тем, который генерируется scaffolding-ом Ruby On Rails - т.е. страницу со списком объектов, страницу с подробной информацией об объекте и страницу с формой для редактирования объекта.

Прежде чем писать "начинку" для этих методов, пропишем наш контроллер в конфигурационном файле для Spring MVC. По умолчанию он называется dispatcher-servlet.xml и должен храниться на classpath-е нашего приложения.

Добавляем bean нашего контроллера:

<bean id="beanController " class="com.mycompany.BeanController ">

        <property name="methodNameResolver" ref="actionResolver"/>

</bean>

Контроллеру надо сделать как минимум одну инъекцию в свойство methodNameResolverЭто объект, реализующий интерфейс MethodNameResolver из пакета org.springframework.web.servlet.mvc.multiaction. У него есть единственный метод со следующей сигнатурой: 

String  getHandlerMethodName(HttpServletRequest request)  throws NoSuchRequestHandlingMethodException

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

<bean id="actionResolver" class="org.springframework.web.servlet.mvc.multiaction.InternalPathMethodNameResolver"/>

Работает он следующим образом.

Мы прописываем отображение запросов по определенным путям на наш контроллер в urlMapping:


<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">

        <property name="mappings">

            <value> /bean/*=beanController</value>

        </property>

        <property name="order" value="0"/>

    </bean>


Теперь строка, которая в URL идет после "/bean/" будет ассоциироваться с одноименным методом нашего контроллера. Таким образом, наши методы привязаны к URL-ам  /bean/view.html,  /bean/list.html,  /bean/edit.html и  /bean/save.html при условии, что DispatcherServlet обрабатывает запросы вида *.html.


Продолжение следует...

Это beta-версия статьи.