JSFをSpring Bootに統合してアプリケーションを作成
はじめに
JSFとSpring Bootを組み合わせてアプリケーションを作成しました。 前回はThymeleafをテンプレートに使用していましたが、今回はJSFのFaceletを使用します。 すべてのコードは以下に載せてます。 github.com
どんなものを作ったか
ToDoリストを作りました。 最初の画面のボタンをクリックするとToDoリスト画面に遷移します。
「Save」ボタンでリストを追加できます。 削除したいNoを入力して「Delete」ボタンを押下すると、リストの削除ができます。
環境
Windows 10 Java 1.8 Spring Framework 5.3.2 STS 4.8.1 JSF 2.3.7
構成
テンプレート
http:localhost:8080/index.jsf を入力して最初の画面が開くように設定します。
<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>ToDoリストアプリケーションへようこそ</p> <p style="height:50px"> <h:form> <h:commandButton value="Todoリストを利用する" action="#{jsfController.loadTodoPage}" /> </h:form> </p> </div> </h:body> </f:view>
web.xmlにJSFで使用する専用のサーブレットを登録しておきます。「Facesサーブレット」を登録しています。
<?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" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> <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>facesServlet</servlet-name> <url-pattern>*.jsf</url-pattern> </servlet-mapping> </web-app>
コントローラークラス
最初の画面から、ToDoリスト画面への遷移はコントローラーが担当します。「Todoリストを利用する」ボタンを押すと処理が走ります。
package com.example.controller; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; @Scope(value = "session") @Component(value = "jsfController") public class JsfController { public String loadTodoPage() { return "/todo.xhtml"; } }
遷移先のToDoリスト画面です。
<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> ToDoを追加しましょう </div> <h:dataTable value="#{todoService.allTodo}" var="item"> <h:column> <f:facet name="header"> <h:outputText value="No"/> </f:facet> <h:outputText value="#{item.id}"/> </h:column> <h:column> <div style="display:block; float:left; width:60px"></div> </h:column> <h:column> <f:facet name="header"> <h:outputText value="やること"/> </f:facet> <h:outputText value="#{item.message}"/> </h:column> <h:column> <div style="display:block; float:left; width:60px"></div> </h:column> <h:column> <f:facet name="header"> <h:outputText value="期限" style="display:block; float:left"/> </f:facet> <h:outputText value="#{item.deadline}"><f:convertDateTime pattern="yyyy年MM月dd日"/></h:outputText> </h:column> </h:dataTable> </div> <div><br></br><br></br><br></br><br></br><br></br> <div> New: </div> <h:form> <h:panelGrid columns="8"> <h:outputLabel for="message" value="やること"/> <h:inputText id="message" value="#{todoService.todo.message}"/> <div style="display:block; float:left; width:60px"></div> <h:outputLabel for="deadline" value="期限"/> <h:inputText id="deadline" value="#{todoService.todo.deadline}" style="width:80px"><f:convertDateTime pattern="yyyyMMdd"/> </h:inputText>(yyyyMMdd) <h:commandButton value="Save" action="#{todoService.save}"/> </h:panelGrid> </h:form> <h:form> <h:panelGrid columns="3"> <h:outputLabel for="id" value="削除"/> <h:inputText id="id" value="#{todoService.todo.id}" style="width:60px"/> <h:commandButton value="Delete" action="#{todoService.delete}"/> </h:panelGrid> </h:form> </div> </h:body> </f:view>
サービスクラス
todo.xhtmlにToDoの要素を表示します。
package com.example.service; import java.util.Collection; import java.util.Comparator; import java.util.stream.Collectors; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; import com.example.model.Dao; import com.example.model.Todo; @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 void delete() { todoDao.delete(todo); todo = new Todo(); } public Collection<Todo> getAllTodo() { return todoDao.getAll(); } public Collection<Todo> getAllTodoSortedByPriority() { return todoDao.getAll().stream().sorted(Comparator.comparingInt(Todo::getId)).collect(Collectors.toList()); } public int saveTodo(Todo todo) { return todoDao.save(todo); } public Todo getTodo() { return todo; } }
package com.example.model; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; import org.springframework.stereotype.Component; @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(); todo.setId(index); return index; } @Override public void delete(Todo todo) { todoList.set(todo.getId() - 1, null); } }
TodoServiceクラスの getAllTodo() メソッドがはじめに呼ばれ、ToDoの要素をすべて表示します。@Scope(value = "session")で データをセッションスコープで保持しています。
つまずいたところ
- cannot find the declaration of element 'faces-config' エラー発生 アプリケーション起動時に『cannot find the declaration of element 'faces-config'.』というエラーが発生しました。 エラーの原因は「faces-config.xml」にありました。
<?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>
4行目の【xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd】のURLの記述が間違っていました。
<?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 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
xsdファイルのURLを変更することで、エラーを解決できました。 参考:jtdz-solenoids.com
- java.lang.ClassNotFoundException: javax.enterprise.context.spi.Contextual エラー発生 アプリケーション起動時に『java.lang.ClassNotFoundException: javax.enterprise.context.spi.Contextual』というエラーが発生しました。 「pom.xml」に以下のライブラリーを追加することで治りました。
<dependency> <groupId>javax.enterprise</groupId> <artifactId>cdi-api</artifactId> <version>1.2</version> </dependency>