1前書き

JavaServer Facesは、サーバーサイドのコンポーネントベースのユーザーインタフェースフレームワークです。もともとはJava EEの一部として開発されました。このチュートリアルでは、JSFをSpring Bootアプリケーションに統合する方法について説明します。

例として、TO-DOリストを作成するための簡単なアプリケーションを実装します。


2 Mavenの依存関係

JSFテクノロジを使用するには、

pom.xml

を拡張する必要があります。

<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<!--JSF-->
<dependency>
    <groupId>org.glassfish</groupId>
    <artifactId>javax.faces</artifactId>
    <version>2.3.7</version>
</dependency>



javax.faces


成果物には、JSF APIとその実装も含まれています。詳細な情報はhttps://javaee.github.io/javaserverfaces-spec/[こちら]にあります。

3. JSFサーブレットの設定

JSFフレームワークはXHTMLファイルを使用して、ユーザーインターフェイスの内容と構造を記述します。サーバーサイドはXHTMLの記述からJSFファイルを生成します。


src/main/webapp

ディレクトリの

index.xhtml

ファイルに静的構造を作成することから始めましょう。

<f:view xmlns="http://www.w3c.org/1999/xhtml"
  xmlns:f="http://java.sun.com/jsf/core"
  xmlns:h="http://java.sun.com/jsf/html">
    <h:head>
        <meta charset="utf-8"/>
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
        <title>TO-DO application</title>
    </h:head>
    <h:body>
        <div>
            <p>Welcome in the TO-DO application!</p>
            <p style="height:50px">
                This is a static message rendered from xhtml.
            </p>
        </div>
    </h:body>
</f:view>

コンテンツは

<your-url>/index.jsf

にあります。ただし、この段階でコンテンツにアクセスしようとすると、クライアント側にエラーメッセージが表示されます。

There was an unexpected error (type=Not Found, status=404).
No message available

バックエンドエラーメッセージは表示されません。それでも、リクエストを処理するためのJSFサーブレットと、リクエストとハンドラを一致させるためのサーブレットマッピングが必要です。

Spring Bootに入っているので、必要な設定を処理するためにアプリケーションクラスを簡単に拡張できます。

@SpringBootApplication
public class JsfApplication extends SpringBootServletInitializer {

    public static void main(String[]args) {
        SpringApplication.run(JsfApplication.class, args);
    }

    @Bean
    public ServletRegistrationBean servletRegistrationBean() {
        FacesServlet servlet = new FacesServlet();
        ServletRegistrationBean servletRegistrationBean =
          new ServletRegistrationBean(servlet, "** .jsf");
        return servletRegistrationBean;
    }
}

これは素晴らしく、かなり合理的に見えますが、残念ながらまだ十分ではありません。

<your-url>/index.jsf

を開こうとすると、もう1つエラーが発生します。

java.lang.IllegalStateException: Could not find backup for factory javax.faces.context.FacesContextFactory.

  • 残念ながら、Javaの設定の横に

    web.xml

    が必要です。

<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>

これで構成は完了です。

<your-url>/index.jsf

を開きます。

Welcome in the TO-DO application!

This is a static message rendered from xhtml.

ユーザーインターフェイスを作成する前に、アプリケーションのバックエンドを作成しましょう。


4 DAOパターンの実装

DAOはデータアクセスオブジェクトを表します。通常、DAOクラスは2つの概念を担当します。永続層の詳細をカプセル化し、単一のエンティティに対するCRUDインタフェースを提供します。あなたはhttps://www.baeldung.com/java-dao-pattern[この]チュートリアルで詳細な説明を見つけることができます。

DAOパターンを実装するには、まず汎用インターフェースを定義します。

public interface Dao<T> {

    Optional<T> get(int id);
    Collection<T> getAll();
    int save(T t);
    void update(T t);
    void delete(T t);
}

それでは、このやるべきアプリケーションに、私たちの最初で唯一のドメインクラスを作成しましょう。

public class Todo {

    private int id;
    private String message;
    private int priority;

   //standard getters and setters

}

次のクラスは

Dao <Todo>

の実装です。このパターンの長所は、このインターフェースの新しい実装をいつでも提供できることです。

その結果、コードの他の部分に触れることなく永続層を変更できます。

この例では、

インメモリストレージクラスを使用します

@Component
public class TodoDao implements Dao<Todo> {

    private List<Todo> todoList = new ArrayList<>();

    @Override
    public Optional<Todo> get(int id) {
        return Optional.ofNullable(todoList.get(id));
    }

    @Override
    public Collection<Todo> getAll() {
        return todoList.stream()
          .filter(Objects::nonNull)
          .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
    }

    @Override
    public int save(Todo todo) {
        todoList.add(todo);
        int index = todoList.size() - 1;
        todo.setId(index);
        return index;
    }

    @Override
    public void update(Todo todo) {
        todoList.set(todo.getId(), todo);
    }

    @Override
    public void delete(Todo todo) {
        todoList.set(todo.getId(), null);
    }
}


5サービス層

DAO層の主な目的は、永続化メカニズムの詳細を処理することです。サービス層はビジネス要件を処理するためにその上にあります。

DAOインターフェースはサービスから参照されることに注意してください。

@Scope(value = "session")
@Component(value = "todoService")
public class TodoService {

    @Autowired
    private Dao<Todo> todoDao;
    private Todo todo = new Todo();

    public void save() {
        todoDao.save(todo);
        todo = new Todo();
    }

    public Collection<Todo> getAllTodo() {
        return todoDao.getAll();
    }

    public int saveTodo(Todo todo) {
        validate(todo);
        return todoDao.save(todo);
    }

    private void validate(Todo todo) {
       //Details omitted
    }

    public Todo getTodo() {
        return todo;
    }
}

ここでは、サービスは名前付きコンポーネントです。この名前を使用して、JSFコンテキストからBeanを参照します。

また、このクラスにはこの単純なアプリケーションに適したセッションスコープがあります。

Springスコープの詳細については、https://www.baeldung.com/spring-bean-scopes[この]チュートリアルをご覧ください。 Springの組み込みスコープはJSFとはモデルが異なるため、カスタムスコープを定義することを検討する価値があります。

これに関するより多くのガイダンスはhttps://www.baeldung.com/spring-custom-scope[この]チュートリアルで利用可能です。


6. コントローラー

JSPアプリケーションと同様に、コントローラは異なるビュー間のナビゲーションを処理します。

次に、最小限のコントローラを実装します。最初のページからやることリストのページに移動します。

@Scope(value = "session")
@Component(value = "jsfController")
public class JsfController {

    public String loadTodoPage() {
        checkPermission();
        return "/todo.xhtml";
    }

    private void checkPermission() {
       //Details omitted
    }
}

  • ナビゲーションは返された名前に基づいています** そのため

    loadTodoPage

    は次に実装する

    todo.xhtml

    ページに移動します。


7. JSFとSpring Beansの接続

JSFコンテキストからコンポーネントを参照する方法を見てみましょう。

まず、

index.xthml

を拡張します。

<f:view
  xmlns="http://www.w3c.org/1999/xhtml"
  xmlns:f="http://java.sun.com/jsf/core"
  xmlns:h="http://java.sun.com/jsf/html">
    <h:head>
      //same code as before
    </h:head>
    <h:body>
        <div>
          //same code as before
           <h:form>
             <h:commandButton value="Load To-do page!" action="#{jsfController.loadTodoPage}"/>
           </h:form>
        </div>
    </h:body>
</f:view>

ここではフォーム要素の中に

commandButton

を導入しました。

すべての

UICommand

要素(たとえば

commandButton)


** は

UIForm

要素(たとえばフォーム)の内側に配置する必要があるので重要です。

この段階で、アプリケーションを起動して

<your-url>/index.jsf

を調べることができます。

リンク:/uploads/2018-09-21-13

55

06-TO-DO-application.png%20390w[]

残念ながら、ボタンをクリックするとエラーが発生します。

There was an unexpected error (type=Internal Server Error, status=500).
javax.el.PropertyNotFoundException:/index.xhtml @11,104 action="#{jsfController.loadTodoPage}":
Target Unreachable, identifier[jsfController]resolved to null

メッセージには、問題が明確に示されています。

jsfController

は__nullに解決されました。対応するコンポーネントは作成されていないか、少なくともJSFコンテキストからは見えません。

この状況では後者が真実です。


  • webapp/WEB-INF/faces-config.xml内でSpringコンテキストと

    JSF__コンテキストを関連付ける必要があります。

<?xml version="1.0" encoding="UTF-8"?>
<faces-config xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig__2__2.xsd"
  version="2.2">
    <application>
        <el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
    </application>
</faces-config>

これでコントローラの動作準備が整ったので、

todo.xhtml

が必要になります。


8 JSF

からのサービスとの対話


todo.xhtml

ページには2つの目的があります。最初に、それはすべてのto-do要素を表示します。

次に、リストに新しい要素を追加する機会を提供します。

そのために、UIコンポーネントは以前に宣言されたサービスと直接対話します。

<f:view xmlns="http://www.w3c.org/1999/xhtml"
  xmlns:f="http://java.sun.com/jsf/core"
  xmlns:h="http://java.sun.com/jsf/html">
    <h:head>
        <meta charset="utf-8"/>
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
        <title>TO-DO application</title>
    </h:head>
    <h:body>
        <div>
            <div>
                List of TO-DO items
            </div>
            <h:dataTable value="#{todoService.allTodo}" var="item">
                <h:column>
                    <f:facet name="header"> Message</f:facet>
                    #{item.message}
                </h:column>
                <h:column>
                    <f:facet name="header"> Priority</f:facet>
                    #{item.priority}
                </h:column>
            </h:dataTable>
        </div>
        <div>
            <div>
                Add new to-do item:
            </div>
            <h:form>
                <h:outputLabel for="message" value="Message: "/>
                <h:inputText id="message" value="#{todoService.todo.message}"/>
                <h:outputLabel for="priority" value="Priority: "/>
                <h:inputText id="priority" value="#{todoService.todo.priority}" converterMessage="Please enter digits only."/>
                <h:commandButton value="Save" action="#{todoService.save}"/>
            </h:form>
        </div>
    </h:body>
</f:view>

上記の2つの目的は、2つの別々の

div

要素に実装されています。

最初に、

todoService.AllTodo

のすべての値を表すために

dataTable

要素を使用しました。

2番目の

div

には、

TodoService内の

Todo__オブジェクトの状態を変更できるフォームが含まれています。


  • inputText

    要素を使用して、2番目の入力が自動的に

    intに変換されます。

    **

    commandButtonを使用すると、

    Todo

    オブジェクトを

    todoService.save__で永続化できます。


9結論

JSFフレームワークはSpringフレームワークに統合することができます。どのフレームワークがBeanを管理するかを選択する必要があります。このチュートリアルでは、Springフレームワークを使用しました。

ただし、スコープモデルはJSFフレームワークとは少し異なります。そのため、Springコンテキストでカスタムスコープを定義することを検討するかもしれません。

いつものように、このコードはhttps://github.com/eugenp/tutorials/tree/master/spring-boot-mvc[GitHubで利用可能]です。