SPRING-SOURCE.RU

Spring и JavaServer Faces (JSF)

В контексте Java разработок, Spring Framework и JavaServrFaces - два имени, которые часто встречаются при разговоре. Spring - это мощный фреймворк, являющийся соперником Enterprise JavaBeans в полноте своих особенностей. JavaServer Faces, известно как JSF, это компонентно-ориентированный, событийно-управляемый фреймворк для построения Web приложений. Учитывая популярность Spring и JSF, вполне естественно, что они будут представлять некоторый интерес в интеграции друг с другом. Как выяснилось, это вполне возможно. В действительности, они дополняют друг друга очень хорошо. Эта статься демонстрирует как использовать JSF и Spring для построения приложения.

Детальное обсуждение обоих Spring и JSF выходят за рамки данной статьи. Вы можете найти множество других ресурсов на которых вы сможете получить исчерпывающую информацию по этим продуктам. Мы остановимся лишь на изложении сути их работы.

Spring - это фреймворк, который состоит из нескольких модулей. Его основной модуль обеспечивает инверсию контроля. Инверсия контроля часто обозначается как IoC; В этой статье мы будем использовать именно это обозначение. Более подробно о Spring Framework вы можете узнать в разделе Документация - Лекции Основы Spring.

JavaServer Faces представляют новый подход в Web разработке. Ранее, разработчики использовали Servlet технологии такие как JavaServer Pages. Эта модель включает такие объекты как Request и Response. Разработчикам приходилось явно писать код для сохранения значений формы между запросами (хотя фреймворки помогали в этом отношении). Хотя эти объекты все еще важны для JSF разработчиков, JSF компоненты пользовательских интерфейсов разработаны для автоматического сохранения их состояния между запросами. Код для обработки управления состояниями значительно сократился. В дополнение к компонентам пользовательского интерфейса, JSF предоставляет 'управляемые-бины', которые могут быть использованы для захвата данных и реагирования на действия пользователей. JSF также предоставляет многочисленные невизуальные компоненты, которые занимаются проверкой данных и выполняют задачи преобразования. Используя средство разработки JSF, построение интерфейса делается также просто как перетаскивание компонентов и бросая их на страницу, где вы можете редактировать их поведение и внешний вид при помощи палитры свойств. Кратко, JSF разработан чтобы делать Web разработку похожую на разработку приложений с такой технологией как Visual Basic.

В оставшейся части этой статьи, мы шагнем через простое приложение, которое использует Spring и JSF. Наше приложение - это система бронирования отеля.

Для начала мы создадим доменную модель представлющую таблицу комнат. В классе Room атрибуты совпадают с колонками в таблице. Этот класс должен лежать в пакете 'domain'. В этом приложении вы будете использовать Hibernate (объектно - реляционное связывание) фреймворк. Hibernate может значительно снизить объем кода доступа данных необходимого в Java приложениях. Связывание между таблицами и Java объектами определяется в XML файле. Файл Room.hbm.xml связывает таблицу 'room' с доменом 'Room':

                        
<hibernate-mapping>
   <class name="hotelReservations.domain.Room" table="room">
   <id name="roomId" column="room_id" unsaved-value="0">
      <generator class="increment" />
   </id>
   <property name="floor" column="floor" not-null="true"/>
   <property name="roomNumber" column="room_number" not-null="true"/>
   <property name="reserveStatus" column="reserve_status"
             not-null="true"/>
   <property name="roomType" column="room_type" not-null="true"/>
   <property name="canSmoke" column="can_smoke" not-null="true"/>
   lt;/class>
</hibernate-mapping>
		

Этот файл должен быть в той же 'domain' директории что и Room класс. Следующее, необходим класс, который управляет доступом к данным, требующихся для работы с данными room. Этот класс назыается как Data Access Object, или DAO, шаблон. Для добавления большей гибкости в переключении реализации доступа к данным в дальнейшем, наш DAO должен реализовывать интерфейс. Поступая таким образом, мы отделям часть приложения и другая часть будет оставаться в неведении того, как осуществляется доступ к данным. RoomDao определяет public методы любого DAO класса приложения. RoomDaoHibernate реализует RoomDao и будет использовать Hibernate программный интерфейс. Вот методы в этом конкретном Hibernate классе:

                        
public List getRooms() {
   return getHibernateTemplate().find("from Room");
}

public Room getRoom(Long id) {
   return (Room) getHibernateTemplate().get(Room.class, id);
}

public void saveRoom(Room room) {
   getHibernateTemplate().saveOrUpdate(room);
}

public void removeRoom(Long id) {
   Object room = getHibernateTemplate().load(Room.class, id);
   getHibernateTemplate().delete(room); 
}
		

Теперь, когда мы создали доменный слой и реализовали слой доступа к данным, было бы приемлимо, приступить к разработке view слоя приложения с помощью JSF. Тем не менее, распространенной практикой является добавление одного финального слоя в приложение. Этот финальный слой находится между view и data access объектами и определяет интерфейс для доступа клиентов приложения. Этот слой содержит ядро бизнес логики для приложения и вызывает методы DAO. Управление бинами JSF будет связываться только с этим слоем, не с DAO. HotelManager класс ничего не делает, кроме как, выполняет методы DAO. В более сложных приложениях, дополнительная бизнес логика находится именно здесь.

HotelManager содержит экземпляр DAO объекта приложения:

                        
private RoomDao dao;

   public void setRoomDAO(RoomDao dao) {
      this.dao = dao;
   }
		

Вы не пишете этот код. Лучше всего, Spring сделает инъекцию этой зависимости в ходе запуска. Эта зависимость определяется в Spring applicationContext.xml файле.

                        
<!--Dao (Data Access Object) for application. -->
<bean id="roomDAO" class="hotelReservations.dao.RoomDaoHibernate">
   <property name="sessionFactory">
      <ref local="sessionFactory"/>
   </property>
</bean>

<!--Manager for Hotel rooms application.-->
<bean id="hotelManager" class="hotelReservations.HotelManager">
   <property name="roomDAO">
      <ref local="roomDAO"/>
   </property>
</bean>
		

Здесь может быть много различных настроек, таких как свойства соединения к базе данных. Spring Framework также позволяет разработчикам определять транзакции в бизнес слое приложения. Это достигается через определение transaction proxy бина, который обволакивает бизнес слой объекта, в нашем случае HotelManager.

                        
<!--Wraps manager in order to support transactions.-->
<bean id="manager"
   class="org.springframework.transaction.interceptor.
       TransactionProxyFactoryBean">
  <property name="transactionManager">
   <ref local="transactionManager"/>
  </property>
  <property name="target">
   <ref local="hotelManager"/>
  </property>
  <property name="transactionAttributes">
   <props>
     <prop key="save*">PROPAGATION_REQUIRED</prop>
     <prop key="remove*">PROPAGATION_REQUIRED</prop>
     <prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
   </props>
  </property>
</bean>
		

Это было краткое введение в то, как Spring используется в этом приложении. Хотелось бы предложить посетить Spring Web сайт для более подробного разъяснения, как работает Spring. Как упоминалось ранее, JSF предоставляет управляемые бины на Web страницы, когда они определены в faces-config.xml файле. FacesServlet загружает бины перечисленные в этом файле при запуске приложения. Это возможно, хотя не рекомендуется, класть бизнес логику внутрь JSF управляемых бинов. Обычно, вам не нужно будет трогать код управления бинами, а сосредоточить его на задачах связанных с view. Мы создали управление бином ShowRooms, который может содержать методы и свойства, на которые JSF Web страница, ShowRooms.jsp сможет ссылаться.

Примечание
Java Server Faces имеет огромную силу потому что за каждой страницей может стоять так называемый JSF managed bean, который представляет из себя обычный java class с set/get методами и всеми возможностями java.

Эти классы бывают нескольких типов:

  1. Request scoped bean
    Этот бин рождается и умирает с каждым запросом.
  2. Session Scoped bean
    Этот бин существует во время сеанса, рождается и умирает после сеанса, для этого его нужно использовать в меньших количествах.
  3. Application Scoped beans
    Этот бин жив пока весь сайт работает, с сайта можно выйти, но этот бин будет работать и из него можно будет брать информацию.

JSF 2,0 имеет еще и view scoped bean, который живет дольше чем request bin, но меньше чем session bean. То есть она живет в пределах одной страницы. Это хорошо потому что, после того как вы сделали отправку формы, и получили ошибки вы не теряем свойства из бина, как это может получится с request scope beans.

Продолжим
Управляемый бин, доступный JSF Web странице

                        
<managed-bean>
   <managed-bean-name>showRooms</managed-bean-name>
   <managed-bean-class>hotelReservations.ShowRooms</managed-bean-class>
   <managed-bean-scope>request</managed-bean-scope>
</managed-bean>
		

Доступность экземпляра ShowRooms и его свойство Rooms на JSF Web странице

                        
<html>
   <head>
      <meta http-equiv="Content-Type" content="text/html;
            charset=UTF-8">
      <link href="styles.css" type="text/css" rel="stylesheet">
      <title>Hotel Reservation System</title>
   </head>
   <body>
      <f:view>
         <h1><h:outputText value="Hotel Rooms" /></h1>
         <h:dataTable value="#{showRooms.rooms}" var="rowRoom" 
                      border="1" headerClass="Heading"
                      rowClasses="RowOdd,RowEven">
         <h:column>
            <f:facet name="header">
               <f:verbatim>Room #</f:verbatim>
            </f:facet>
            <h:outputText value="#{rowRoom.roomNumber}"/>
         </h:column>

         ...
         ...

         </h:dataTable>
      </f:view>
   </body>
</html>
		

ShowRooms.jsp показывает коллекцию Room объектов. Эти объекты могут быть получены через вызов Spring бизнес метода getRooms(). Теперь посмотрим, что нужно сделать, чтобы это стало возможным.

Хочется сказать, что JSF, как и Spring, поддерживает dependency injection - механизм, с помощью которого ссылки на одни объекты JavaBean можно сохранять в свойствах других подобных объектов.

Ключевым компонентом, делающим Spring объекты доступными для JSF управление бинами – это Spring DelegatingVariableResolver. JSF VariableResolver может быть использован для привязки значений из объектов, доступных в одной зоне приложения, таких как, зона запроса или зона сессий. JSF VariableResolver ищет во всех возможных зонах и если он не находит соответствий, тогда ищет для значения, используя любые переменные определенные в faces-config файле. Указывая DelegatingVariableResolver, вы можете ввести через инъекцию экземпляр HotelManager в ваш JSF управление бином.

                        
<!-- Allows you to inject Spring beans into JSF managed beans... -->
<application>
   <variable-resolver>
      org.springframework.web.jsf.DelegatingVariableResolver
   </variable-resolver>
</application>
		

Далее, теперь вы должны изменить ваш управлающий бина и определить в нем управляющее свойство. Это свойство называется 'manager', его мы определяем в applicationContext.xml:

                        
<!--Managed-bean accessible to JSF Web page.-->
<managed-bean>
    <managed-bean-name>showRooms</managed-bean-name>
    <managed-bean-class>hotelReservations.ShowRooms</managed-bean-class>
    <managed-bean-scope>request</managed-bean-scope>
    <managed-property>
       <property-name>manager</property-name>
       <value>#{manager}</value>
    </managed-property>
</managed-bean>
		

Мы видим, что property-name у нас manager - это свойство находится в классе ShowRooms (там на него установлен setter), а запись в поле value #{manager} берется из applicationContext.xml.

Хочется обратить внимание еще на managed-bean-scope запись. Ее значение равно request. Что это означает? Все объекты Все объекты, помещаемые в request, доступны только в течение обработки одного запроса. Как правило, туда имеет смысл помещать объекты, чье состояние не представляет интереса после окончательного отображения страницы в конце обработки запроса.

Наконец, после добавления свойства в управляющий бин JSF, вы можете делегировать управление Spring в ShowRooms управление бином:

                        
public class ShowRooms {

   /** Creates a new instance of ShowRooms */
   public ShowRooms() {
   }

   /**
    * Holds value of property hotelReservation.
    */
   private HotelManager manager;

   /**
    * Setter for property hotelManager.
    * @param hotelManager New value of property hotelManager.
    */
   public void setManager(HotelManager manager) {
      this.manager = manager;
   }

   public HotelManager getManager() {
      return manager;
   }

   /**
    * Returns List of all hotel rooms objects.
    */
   public List getRooms() {
      return this.getManager().getRooms();

   }
		

ShowRooms.jsp использует JSF компонент - таблицыдля отображения списка объектов Room через выражение "#{showRooms.rooms}". Во время выполнения, он вызывает getRooms метод для получения данных. Каждая колонка в таблице содержит текст в свойстве, в данном случае это Room экземпляр. Опять же, это будет сделано при помощи следующего выражения "#{rowRoom.roomNumber}". После применения стилей CSS, таблица будет выглядеть следующим образом:

Spring Hibernate table

Более сложные компоненты JSF используются для отображения табличных данных, поддерживающих сортировку и разбиение по страницам. Эта простая таблица является демонстрационной.