Eugene Burtsev's blog

Пример совместной работы JSF2 и Spring Framework

Совсем недавно открыл для себя Spring Framework и понял, что до этого момента упускал множество возможностей. По это причине решил переписать парочку приложений с использованием Spring. Ниже я расскажу как интегрировать Spring и JSF2

Подключение зависимостей

В первую очередь необходимо добавить в зависимости проекта артефакт org.springframework:spring-web:

pom.xml
1
2
3
4
5
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-web</artifactId>
  <version>3.1.1.RELEASE</version>
</dependency>

Остальные необходимые зависимости maven подтянет сам.

Настройка

Чтобы все заработало нужно настроить слушателей в web.xml, как показано в примере ниже:

web.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
      id="jsf2-spring-example" version="2.5">

  <display-name>jsf2-spring-example Application</display-name>

  <listener>
      <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

  <listener>
      <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
  </listener>

  <context-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:spring-context.xml</param-value>
  </context-param>

  <servlet>
      <servlet-name>Faces Servlet</servlet-name>
      <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
      <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
      <servlet-name>Faces Servlet</servlet-name>
      <url-pattern>*.jsf</url-pattern>
  </servlet-mapping>

  <servlet-mapping>
      <servlet-name>Faces Servlet</servlet-name>
      <url-pattern>/faces/*</url-pattern>
  </servlet-mapping>

</web-app>

Так же необходимо установить el-resolver из Spring взамен стандартного, чтобы дать возможность спрингу рулить бинами. Для этого меняем faces-config.xml:

faces-config.xml
1
2
3
4
5
6
7
8
9
<?xml version="1.0"?>
<faces-config version="2.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xi="http://www.w3.org/2001/XInclude"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd">

  <application>
      <el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
  </application>

</faces-config>

Теперь настроим spring-context.xml. Собственно тут все сводится к изменению значения base-package для context:component-scan:

spring-context.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:context="http://www.springframework.org/schema/context"
  xsi:schemaLocation="
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">

  <!-- enable component scanning -->
  <context:component-scan base-package="net.burtsev.example" />

  <!-- enable autowire -->
  <context:annotation-config />

</beans>

Код

Ну а теперь после того как все необходимые настройки сделаны - попробуем написать код:

index.xhtml

index.xhtml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
  xmlns:h="http://java.sun.com/jsf/html"
  xmlns:f="http://java.sun.com/jsf/core"
  xmlns:ui="http://java.sun.com/jsf/facelets" template="/templates/main.xhtml">

  <ui:define name="title">Home</ui:define>

  <ui:define name="body">
      <h:form prependId="false">
          <h:inputText value="#{testBean.name}" />
          <h:commandButton action="#{testBean.sayHelloAction}" value="Say Hello"/>
          <br/>
          #{testBean.hello}
      </h:form>
  </ui:define>

</ui:composition>

TestBean.java

Здесь нужно отметить использование аннотации @Controller вместо @ManagedBean и @Scope для указания времени жизни бина. Список всех возможных scope можно посмотреть тут: Spring Framework Reference. Так же стоит отметить отсутствие view scope по дефолту. Но эту проблему можно обойти, например, как описано в этой статье или в этой

TestBean.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package net.burtsev.example.web;

import java.io.Serializable;

import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;

@Controller
@Scope("session")
public class TestBean implements Serializable {

  private static final long serialVersionUID = -1;

  private String name;
  private String hello;

  public void sayHelloAction() {
      hello = StringUtils.isNotBlank(name) ? "Hello " + name : "";
  }

  public String getName() {
      return name;
  }

  public void setName(String name) {
      this.name = name;
  }

  public String getHello() {
      return hello;
  }

  public void setHello(String hello) {
      this.hello = hello;
  }
}

Пример работы

Запускаем, переходим по адресу: http://localhost:8080/jsf2-spring-example/index.jsf и видим что все работает :)

P.S. Полный проект можно взять здесь: github repository

Comments