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で利用可能]です。