Spring bootはjsp、velocity、thymeleafなどのテンプレートエンジンが使えますが今回はvaadinを使ってみます。
vaadinにspringのtutorialがあるのでこれをやってみます。

tutorialのソースコードはgitにおいてあったのでコードが見たい人は以下のレポジトリを見るとよい。
https://github.com/vaadin/spring-tutorial

Vaadinとは?

出処: https://vaadin.com/framework

VaadinはUI FrameworkでリッチなViewを簡単に作ることが出来ます。2007年あたりからOSSとして公開されているUI Frameworkです。Vaadinが用意しているUI Componentを使うことでhtml, css, jsと言ったフロント周りのコードを一切書くこと無くJavaだけでリッチなViewを作ることができるのが最大の特徴です。自分たちで使うツールのViewに使ったり、FrontのjsがかけるWebデザイナがいないけど無難なUIが作りたいといった場合に重宝します。

vaadin spring boot tutorial

以下の4章を順にやっていく。

  • I - Getting Started with Vaadin Spring and Spring Boot
  • II - Injection and Scopes with Vaadin Spring
  • III - Views and Navigation with Vaadin Spring
  • IV - Configuring and Using Vaadin Spring without Spring Boot

プロジェクトを作る

springはmaven, gradleプロジェクトであれば以下のアプリを使うことで簡単に雛形を作ることができるのでこれを利用します。
http://start.spring.io/

mavenプロジェクトを選択し、Dependenciesにvaadinと入力してプロジェクトをダウンロードすればOKです。mvnコマンドでアーキテクトタイプを調べてといった操作をしなくてもプロジェクトが作れてしまいます。

IDEはSpring Tool SuiteでもEclipseでもintellijでもなんでも良いので、ここではintellijを使います。

CentOS7でOpenjdk8を使う場合はopenjdk-develをインストールする必要があるので以下のコマンドを実行する。

root$ yum install java-1.8.0-openjdk-devel

https://www.jetbrains.com/idea/ でintellijをダウンロードして先の手順で作成したプロジェクトを解凍してmavenプロジェクトとしてインポートします。

UIを作る

早速UIを作ります。

以下の2つのコードをclasspath以下に追加する。

Greeter.java
@SpringComponent
@UIScope
public class Greeter {
public String sayHello() {
return "Hello from bean " + toString();
}
}

@SpringComponentはDIできるようにComponentに登録するアノテーション。 @UIScopeは後ほど説明がある。

MyVaadinUI.java
@Theme("valo")
@SpringUI
public class MyVaadinUI extends UI {
@Autowired
private Greeter greeter;

@Override
protected void init(VaadinRequest request) {
setContent(new Label(greeter.sayHello()));
}
}

このクラスがUIの雛形になる。このUIに対してView(UIの部品、例えばフォームとかリンクとか)を追加していく。
@ThemeでVaadinが用意しているテーマや自分で作ったテーマを指定する。テーマvaloがカッコいい。

@SpringUIでUIとして登録する。

UIのクラスを作る場合はUIを継承しておく。先に作ったGreeterクラスをDIする。

IDEのMaven Projects -> Plugins -> spring-boot -> spring-boot:runから実行し、http://localhost:8080 に接続すると以下のような文字列が画面が表示される。

Hello from bean com.example.component.Greeter@77215bc

Viewを追加する

MyVaadinUI.javaを以下のように変更する。

MyVaadinUI.java
@Theme("valo")
@SpringUI
public class MyVaadinUI extends UI {

@Autowired
private SpringViewProvider viewProvider;

@Override
protected void init(VaadinRequest request) {
final VerticalLayout root = new VerticalLayout();
root.setSizeFull();
root.setMargin(true);
root.setSpacing(true);
setContent(root);

final Panel viewContainer = new Panel();
viewContainer.setSizeFull();
root.addComponent(viewContainer);
root.setExpandRatio(viewContainer, 1.0f);

Navigator navigator = new Navigator(this, viewContainer);
navigator.addProvider(viewProvider);
}
}

SpringViewProviderがSpringViewを良しなにautowiringしてくれるらしい。

MyVaadinUIにViewを追加する。MyVaadinUIはVerticalLayoutでPanelを追加している。

DefaultViewとして以下のクラスを追加する。

DefaultView.java
@SpringView(name = DefaultView.VIEW_NAME)
public class DefaultView extends VerticalLayout implements View {
public static final String VIEW_NAME = "";

@PostConstruct
void init() {
addComponent(new Label("This is the default view"));
}

@Override
public void enter(ViewChangeEvent event) {
// the view is constructed in the init() method()
}
}

http://localhost:8080 に接続するとpanelが追加された以下の画面が表示されることを確認できる。

UIScopeとViewScopeの違い

UIScopeはUIに紐づくスコープでViewが変わっても保持されるがViewScopeはViewが変わると保持されず変更されるスコープ。
UIScopeは画面を再読込すると変更される。

ViewScopedView.java
@SpringView(name = ViewScopedView.VIEW_NAME)
public class ViewScopedView extends VerticalLayout implements View {
public static final String VIEW_NAME = "view";

@Autowired
private ViewGreeter viewGreeter;

@Autowired
private Greeter uiGreeter;

@PostConstruct
void init() {
setMargin(true);
setSpacing(true);
addComponent(new Label("This is a view scoped view"));
addComponent(new Label(uiGreeter.sayHello()));
addComponent(new Label(viewGreeter.sayHello()));
}

@Override
public void enter(ViewChangeEvent event) {
// the view is constructed in the init() method()
}
}

ViewScopeのViewGreeterを持つView。

ViewGreeter.java
@SpringComponent
@ViewScope
public class ViewGreeter {
public String sayHello() {
return "Hello from a view scoped bean " + toString();
}

ViewScopeのComponentを作る。ViewScopeなのでViewが更新されるごとにインスタンスが変わる。

UIScopedView.java
@UIScope
@SpringView(name = UIScopedView.VIEW_NAME)
public class UIScopedView extends VerticalLayout implements View {
public static final String VIEW_NAME = "ui";

@Autowired
private Greeter greeter;

@PostConstruct
void init() {
setMargin(true);
setSpacing(true);
addComponent(new Label("This is a UI scoped view. Greeter says: " + greeter.sayHello()));
}

@Override
public void enter(ViewChangeEvent event) {
// the view is constructed in the init() method()
}
}

UIScopeのGreeterを表示するView。

MyVaadinUI.java
public class MyVaadinUI extends UI {
...
@Override
protected void init(VaadinRequest request) {
final VerticalLayout root = new VerticalLayout();
root.setSizeFull();
root.setMargin(true);
root.setSpacing(true);
setContent(root);

final CssLayout navigationBar = new CssLayout();
navigationBar.addStyleName(ValoTheme.LAYOUT_COMPONENT_GROUP);
navigationBar.addComponent(createNavigationButton("UI Scoped View",
UIScopedView.VIEW_NAME));
navigationBar.addComponent(createNavigationButton("View Scoped View",
ViewScopedView.VIEW_NAME));
root.addComponent(navigationBar);

final Panel viewContainer = new Panel();
viewContainer.setSizeFull();
root.addComponent(viewContainer);
root.setExpandRatio(viewContainer, 1.0f);

Navigator navigator = new Navigator(this, viewContainer);
navigator.addProvider(viewProvider);
}

private Button createNavigationButton(String caption, final String viewName) {
Button button = new Button(caption);
button.addStyleName(ValoTheme.BUTTON_SMALL);
// If you didn't choose Java 8 when creating the project, convert this to an anonymous listener class
button.addClickListener(event -> getUI().getNavigator().navigateTo(viewName));
return button;
}
}

UIScopedViewとViewScopedViewを切り替えるButtonを設置。
ラムダ式を使って書くこともできる。

実行し2つのボタンを交互に押すとViewScopeの値が変更されるがUIScopeの値は変わらないことが確認できる。UIを再読込するとUIScopeの値も変更される。

その他

ほかはViewNameを指定しなかった場合に設定されるViewの命名規則が書いてあったりする。

明示的に名前をつけることを強く推奨しているが、VIEW名を指定しない場合はクラス名に基き以下の様に自動的に名前が割り当てられる。
HelloWorldView → hello-world
ExampleView → example
VisualEditor → visual-editor

まとめ

あとはひたすらVaadinのドキュメントを読み込んで行く必要がある。

vaadinが用意しているテーマを使えば全くhtml, css, jsを書かなくても良い事がわかった。

例えばリンクが追加したければ以下のようにjavaを書くことで追加できるのが結構いい感じだなと思った。

Link link = new Link("Take me a away to a faraway land",
new ExternalResource("http://vaadin.com/"));
root.addComponent(link);

出処: https://vaadin.com/docs/-/part/framework/components/components-overview.html

htmlに対応するコンポーネントが用意されているので比較的簡単に画面が作れそうである。

db周りはspringですればいいのかな?ココらへんはまたの機会に。

終わり。