はじめに
SpringBoot 1.3.5で書き直しました。
SpringFrameworkはグローバルで利用され、枯れたフレームワークなので品質的にも安心感がありますし、SpringとSpringのサブプロジェクトの機能は非常に多岐に渡ります。
SpringBootは私のようにSpringに詳しくなくても簡単にWebアプリケーションを作ることができます。
SpringFrameworkの情報はWeb上に沢山存在しますが、Spring初心者にはどれがどのバージョンで利用できることなのかがわかりにくく戸惑うことが多いのです。そこで、今回勉強したことを検索しやすいように1ページにまとめてみました。
できるだけ公式サイトへの参照を貼ってあります。また、探す糸口がわかると良いと思ったので探し方もできるだけ記載しておきました。
自分のメモ的な感じで記載しているので記載が荒いところはご容赦ください。間違いなどあればご指摘いただければ助かります。
サンプルアプリケーション(2016/6/8追記)
- サンプルがあった方がよさそうなので、簡単なアプリケーションを作ってみました。ログイン・ログアウト、ユーザ登録、iTunesSearchAPIを使った簡単なサンプルですが参考になるかと思います。
- こちらも1.3.5に合わせて修正しました。
- https://github.com/uzresk/springboot-samples.git
SpringBootとは?
- 設定楽々Spring=SpringBootという感じ。煩わしい設定を行わずSpringが利用できます。
- 初期設定はMaven or Gradleでpomをかけば完了。依存するライブラリのおすすめの組み合わせは自動で決まります。
- Springの基本的な設定は自動で行われます。web.xmlすら無くなります。
- APサーバはEmbededTomcat利用しており、デプロイはwarではなくjarをデプロイします。(起動はjava -jar xx.jarで行います。)
- Tomcatが嫌な方はJettyを利用することもできます。
- 画面の開発にはJSPは推奨されません。Thymeleaf,Velocityなどを利用します。
http://docs.spring.io/spring-boot/docs/1.3.5.RELEASE/reference/htmlsingle/#boot-features-spring-mvc-template-engines
JSPs should be avoided if possible, there are several known limitations when using them with embedded servlet containers.
前提(環境)
- Java8
- Maven3.0.4
- SpringBoot1.3.5
リファレンス
はじめに設定したいもの
コンポーネントをscanするパッケージを変更する。
- scanするパッケージについては、デフォルトではSpringBootを起動するクラス配下が自動でスキャンされますが、アノテーションを設定することでスキャンするパッケージを変更することができます。
- 私の場合はComponentはWeb,Batchでそれぞれ作成し、Entity、RepositoryはWeb,Batchで共用したかったのでアノテーションを設定しました。
-
Component
@ComponentScan(basePackages = "org.xxx")
-
JpaRepository
@EnableJpaRepositories(basePackages = "org.xxx.repository")
-
Entityクラス
@EntityScan(basePackages = "org.xxx.entity")
まとめて書くとこんな感じになります。
@EnableAutoConfiguration
@ComponentScan(basePackages = "org.xxx")
@EnableJpaRepositories(basePackages = "org.xxx.repository")
@EntityScan(basePackages = "org.xxx.entity")
public class App {
・・・
}
ログの設定
- slf4j+logbackを使うのが便利でしょう。log4jと大差ないのですんなり入れるはずです。sfl4j+logbackを抑えるにはこちらのURLがわかりやすいです。
-
pomの設定を行います。必要に応じてjclとかをつっこみます。slf4j+logbackはデフォルトで利用可能ですので、pomの変更は必要ありません。
- あとはlogback.xmlをresource配下に配置します。logback.xmlは適当に。一番シンプルな例を上げておきます。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE logback>
<configuration>
<include resource="org/springframework/boot/logging/logback/base.xml"/>
<logger name="com.amazonaws" level="INFO"/>
<logger name="org.hibernate" level="ERROR"/>
<logger name="org.springframework" level="INFO"/>
<logger name="org.thymeleaf" level="INFO"/>
</configuration>
- importしているbase.xmlを見るとわかりますが、javaのシステムプロパティを使うことでファイルの出力場所を指定できます。
-Dlogging.file=/var/log/my.log
- そのほか、log.pathなどの設定もありますので参考ドキュメントを参照してください。
- 参考:66.1 Configure Logback for logging
ログの設定 - logback.xmlの差し替え
- logbackは通常-Dlogback.configurationFile=/foo/bar/logback.xmlとすれば設定ファイルの差し替えができますが、SpringBootではこのシステムプロパティは無効化されています。
- そんなときはこんな風にします。
-Dlogging.config=/foo/bar/logback.xml
リクエストのエンコーディングをUTF-8にする。
- Fiterの設定を当初記載していましたが、Spring Boot 1.2からは自動でCharacterEncodingFilterが登録されるようになっていますので特に設定は不要です。
セッションのクラスタリング on AWS
デフォルトのエラーページを塞ぐ
- デフォルトのエラーページにはセキュリティ上望ましくない情報が記載されていますので表示されないように設定を変更します。
- 設定する一つの方法はtemplatesディレクトリの直下にerror.htmlを置くことです。これでどんなエラーでもerror.htmlが表示されます。
- より細かい制御はErrorMvcAutoConfigurationをオーバライドして実現します。
- 参考:70.2 Customize the ‘whitelabel’ error page
開発時に設定するもの
ThymeleafのキャッシュをOFFにする。
- Thymeleafのキャッシュが効いていると変更したあと即反映されません。開発時にはキャッシュをOFFにしておきます。
- 参考:http://docs.spring.io/spring-boot/docs/current/reference/html/howto-hotswapping.html#howto-reload-thymeleaf-content
spring.thymeleaf.cache: false
ホットデプロイ
- SpringBootでもSeasarで言うところのホットデプロイできます。
- 参考:72.6 Reload Java classes without restarting the container
- spring-loadedをダウンロードしておきます。
https://github.com/spring-projects/spring-loaded
- Eclipseで開発している時には、SpringBoot起動時の実行構成のVM引数を設定します。
-javaagent:/springloaded-1.2.1.RELEASE.jar -noverify
- spring loadedはまだまだ発展途上という感じです。バージョンは良く上がるのでマメにサイトをチェックして最新版を適用しましょう。
- mavenで実行する場合には以下のようにpomを記載します。
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>springloaded</artifactId>
<version>1.2.1.RELEASE</version>
</dependency>
</dependencies>
設定ファイルのプロファイルを切り替える
- 私の場合は、会社ではローカルのPostgreSQL、家ではSSLVPN経由でAWSのPostgreSQL(RDS)。という感じで使っています。
- 通常の開発時でも開発・ステージング・本番とかで設定を切り替えたいですよね。そういときはProfileを使います。
- application.ymlでは、---で区切り。profiles:にプロファイル名を記載します。
spring:
profiles: dev-aws
thymeleaf:
cache: false
datasource:
url: jdbc:postgresql://foobar:5432/cmp
username: foo
password: bar
jpa:
database-platform: org.hibernate.dialect.PostgreSQLDialect
---
spring:
profiles: production
thymeleaf:
cache: true
datasource:
url: jdbc:postgresql://hogehoge:5432/cmp
username: foo
password: bar
jpa:
database-platform: org.hibernate.dialect.PostgreSQLDialect
- あとはVM引数(-Dspring.profiles.active)にプロファイル名を書いてSpringBootを起動します。
java -Dspring.profiles.active=production -jar /home/ec2-user/XXX-0.0.1-SNAPSHOT.jar &
画面系の実装
設定の方法
- POMを編集し、spring mvcとthymeleafを使えるようにしてあげます。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
値を表示する
model.addAttribute("hoge", hoge);
<span th:text="${hoge}"></span>
ネストされた値を表示する
下記の例ではfooオブジェクトにgetBar()というGetterが必要となります。
<span th:text="${foo.bar}"></span>
日付をフォーマットする。
${#dates.format([java.util.Dateオブジェクト], 'yyyy/MM/dd')}
List内のオブジェクトを表示したい。
List<Image> images = new ArrayList<Image>();
model.addAttribute("images", images);
<tr th:each="image : ${images}">
<td th:text="${image.name}">test1</td>
<td th:text="${image.description}">test1</td>
<td th:text="${image.imageId}">test1</td>
<td th:text="${image.state}">test1</td>
<td th:text="${image.architecture}">test1</td>
<td th:text="${image.imageType}">test1</td>
</tr>
Map内のオブジェクトを表示したい。
- 年のプルダウンを作る例を使って説明します。Mapの中に2014:2014と入っている場合は以下のように実装します。getやkeySetなどはそのまま利用できます。
<select th:field="*{year}" class="selectpicker" data-width="75px">
<option th:each="key : ${years.keySet()}"
th:value="${key}"
th:text="${years.get(key)}">pulldown</option>
</select>
Sessionにあるキーの存在有無でボタンの表示非表示を制御する。
- thymeleafでもJSPのように暗黙オブジェクトがあります。session上にfooというキーが存在する場合にのみ「取消」リンクを表示するには下記のように実装します。
<a th:if="${session.containsKey('foo')}" th:href="@{/hoge}">
<button type="button" class="btn btn-danger">取消</button>
</a>
パラメータを送りたい・受け取りたい
- GETパラメータを送る。
<a th:href="@{/foo/bar(hoge=${hoge},fuga=true)}">
<button type="button" class="btn btn-success">ボタン名</button>
</a>
- ControllerでGETパラメータを受け取る。
@RequestMapping(value = "/foo/bar", method = RequestMethod.GET)
String images(@RequestParam String hoge,
@RequestParam String fuga) {
Formを使って値の取得を行う(POST)
- AControllerでは対応するFormを宣言しておき、初期値を設定します。
- @ModelAttributeで宣言されたメソッドが実行時に自動で呼ばれます。(メソッド名は任意で、@ModelAttributeが付いているかどうかが重要です。))
@ModelAttribute
HogeForm setUpForm() {
return new HogeForm();
}
@RequestMapping(value = "/hoge", method = RequestMethod.GET)
String hoge(HogeForm form) {
// 初期値を設定
form.setName('name');
}
<form class="form" th:action="@{/hoge/confirm}" th:object="${hogeForm}" method="POST">
<button type="submit" id="submit" class="btn btn-primary">ボタン名</button>
</form>
POSTされた値はBControllerの引数に自動で入ります。
@RequestMapping(value = "/hoge/confirm", method = RequestMethod.POST)
String hogeConfirm(HogeForm form) {
System.out.println(form.getName());
}
リダイレクトする。リダイレクト先に値を引き渡す。
- リダイレクトするには、Controllerの返却値のprefixにredirect:と付けるだけです。
return "redirect:/foo/bar"
- リダイレクト先に値を渡すにはRedirectAttributesを使います。これを使うとリダイレクト先でmodelに値を再度セットする必要もないので便利ですね。
@RequestMapping(value = "/to", method = RequestMethod.GET)
String to(Model model, @RequestParam String foo,
@RequestParam String bar,
RedirectAttributes attributes) {
attributes.addFlashAttribute("CompleteMessage", completeMessage);
return "redirect:/hoge";
}
単項目チェックを実装したい。
- 単項目チェックはFormのフィールドにアノテーションを付けること実現できます。
- アノテーションはBean Validationの実装であるhibernate validatorを利用します。
- 付与できるアノテーションについては下記を参照
- https://access.redhat.com/documentation/ja-JP/JBoss_Enterprise_Application_Platform/5/html-single/Hibernate_Validator_Reference_Guide/index.html#validator-defineconstraints-builtin
@Data
@NoArgsConstructor
public class ReserveForm implements Serializable {
public static final long serialVersionUID = 1L;
@NotEmpty
private String hour;
@NotEmpty
private String minutes;
}
- メソッドの引数のFormの前に@Validatedを付けることでチェックが動きます。
- 入力チェックの結果はBindingResultに入ります。この例では入力チェックでエラーに成ったら入力画面に戻しています。
- 【要注意】BindingResultはFormのすぐ後ろに記載しないと動きません。
@RequestMapping(value = "reserveAMI/complete", method = RequestMethod.POST)
String reserveAMIComplete(@Validated AwsAMIReserveForm form,
BindingResult result, Model model) {
// 入力チェック
if (result.hasErrors()) {
createDropDownList(model);
return "input";
}
}
@NotNull/@NotEmpty/@NotBlankの違い
相関チェックを実装したい。
- controllerでInjectionするので、コンポーネント(@Component)として実装します。
- validateメソッドではFormオブジェクトにキャストして実装します。
- rejectValueの第一引数はエラーとなったフィールド名。第二引数はメッセージコード。第三引数はメッセージコードからメッセージが取得できなかった場合に表示するメッセージになります。
- 第一引数となるフィールド名に対応するプロパティはFormに存在する必要があります。
@Component
public class AccountValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
return AccountCreateForm.class.isAssignableFrom(clazz);
}
@Override
public void validate(Object target, Errors errors) {
AccountCreateForm form = (AccountCreateForm) target;
//入力チェックを実装
・・・
// メッセージを埋め込み
errors.rejectValue("accountId",
"AccountValidator.duplicateAccountId", "メッセージ");
}
}
- ValidatorをControllerにInjectionして、initBinderを実装します。
- validatorをWebDataBinderにaddしておくことで、単項目チェックが動くタイミングで相関チェックも動きます。
- ハンドリングの仕方については単項目チェックと同様なので割愛します。
@Autowired
AccountValidator accountValidator;
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.addValidators(accountValidator);
}
メッセージを表示したい
- マニュアルから引用します。
- thymeleafでメッセージを表示するには#を使います。
<p th:utext="#{home.welcome}">Welcome to our grocery store!</p>
- 引数付きのメッセージ
<p th:utext="#{home.welcome(${session.user.name})}">
Welcome to our grocery store, Sebastian Pepper!
</p>
入力チェックのメッセージを変更したい。
- src/main/resuorce/ValidationMessages_ja.propertiesを作成します。
- メッセージIDをオーバライドします。
サーバ側でメッセージを作りたい。
- サーバ側でメッセージを作ってログや画面に表示したいケースで使います。Controllerでの実装を例にとります。
- MessageSourceをinjectionします。
@Autowired
MessageSource messageSource;
- メッセージキー、引数。第三引数にはデフォルトメッセージ、第四引数にはロケールを設定します。
messageSource.getMessage("messageId",
new String[] { argument },
"defaultMessage", Locale.JAPAN);
エラーの存在有無を判定したい
- エラーが存在した場合画面上部に共通のメッセージだしたい。というときには下記を埋め込みます。
- サンプルではエラーが発生したら、メッセージキーcommon.errorに紐付くメッセージを表示しています。
<div th:if="${#fields.hasErrors('all')}" class="alert alert-danger">
<p th:text="#{common.error}">ErrorMessage</p>
</div>
フィールドの横にエラーメッセージを表示したい
- テキストの横に下記を埋め込みます。(別にspanである必要はありません)
- サンプルではreserveTypeというフィールドにエラーがあったらそれに紐付くメッセージを表示する場合の記載方法です。
<span th:if="${#fields.hasErrors('reserveType')}" class="text-danger" th:errors="*{reserveType}">Error</span>
エラー時のフィールドのスタイルを変えたい
- エラーとなったテキストボックスの枠は赤くしたい。などの場合は下記を埋め込みます。
- サンプルではtwitter bootstrapを利用しています。hogeという項目がエラーとなった場合にform-groupのクラス名を変更しています。
<div class="form-group" th:classappend="${#fields.hasErrors('hoge')}? 'has-error has-feedback'">
<label for="cron">項目名</label>
<input type="text" id="hoge" name="hoge" th:field="*{hoge}" class="form-control" style="width:75px;"/>
</div>
セキュリティ
設定
- 認証・認可処理を実装するにはSpringSecurityを利用します。
- pomを編集するだけで利用可能になります。springsecurityと合わせて、thymeleafでspringsecurityの機能を利用できるようにしておきます。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity3</artifactId>
</dependency>
認証
- ログイン周りの設定はWebSecurityConfigurerAdapterを継承したConfigurationを定義することが全てです。
- 下記のコードではこのようなことをやってます。
- 認証が必要なページで認証されてなかったら/loginFormに飛ばす。
- ログインページは/loginForm
- ログインボタンが押されるとaccountIdとpasswordが/loginにPOSTされる。
- ログインが成功したら/topに飛ばす。
- loginエラーの場合は/loginForm?errorに飛ばす。
- ログアウト処理は/logout。ログアウト成功後は/loginFormに飛ばす。
- AuthenticationConfigurationは認証処理時のpasswordEncoderを作って渡してあげてます。
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/loginForm").permitAll()
.anyRequest().authenticated();
http.formLogin().loginProcessingUrl("/login").loginPage("/loginForm")
.failureUrl("/loginForm?error").defaultSuccessUrl("/top", true)
.usernameParameter("accountid").passwordParameter("password")
.and();
http.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout**"))
.logoutSuccessUrl("/loginForm");
}
@Configuration
static class AuthenticationConfiguration extends
GlobalAuthenticationConfigurerAdapter {
@Autowired
UserDetailsService userDetailsService;
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(
passwordEncoder());
}
}
}
静的コンテンツは認可の対象から除外したい
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/css/**", "/js/**","/fonts/**", "/img/**");
}
認証情報に任意の情報を埋めたい
- 認証処理後にユーザに紐付く付属情報を取得してどこからでも利用できるようにしたいですよね。
- Userを継承した入れ物を作成する。
- UserDetailServiceを継承したコンポーネントを作成する。
- ここではRepositoryを使ってアカウントIDに紐付く情報を取得後UserDetailsとして返却しています
public class LoginAccountDetails extends User {
private static final long serialVersionUID = 1L;
@Getter
private final Account account;
@Getter
private final AccountInfo accountInfo;
public LoginAccountDetails(Account account, AccountInfo accountInfo) {
super(account.getAccountId(), account.getPassword(), AuthorityUtils
.createAuthorityList("USER"));
this.account = account;
this.accountInfo = accountInfo != null ? accountInfo : new AccountInfo();
}
}
@Service
public class LoginAccountDetailService implements UserDetailsService {
@Autowired
AccountRepository accountRepository;
@Autowired
AccountInfoRepository accountInfoRepository;
@Override
public UserDetails loadUserByUsername(String accountId)
throws UsernameNotFoundException {
Account account = accountRepository.findOne(accountId);
if (account == null) {
throw new UsernameNotFoundException("Account is not found");
}
AccountInfo accountInfo = accountInfoRepository
.findOne(new AccountInfoPk(accountId));
return new LoginAccountDetails(account, accountInfo);
}
}
認証情報を取得する
- Controllerで認証情報を取得するには@AuthenticationPrincipalを付与した引数を与えてあげるだけです。
- org.springframework.security.web.bind.annotation.AuthenticationPrincipalはdeprecatedになっていますので、org.springframework.security.core.annotation.AuthenticationPrincipalを使いましょう。
@RequestMapping(value = "images", method = RequestMethod.GET)
String images(@RequestParam String instanceId,
@AuthenticationPrincipal LoginAccountDetails loginAccountDetails,
Model model) {
ロールを使った表示制御
- sec:authorize="hasRole('ロール名')"を使うとロールを持っているユーザにしかそのセクションが表示されなくなります。
<div id="navbar" class="navbar navbar-inverse" sec:authorize="hasRole('USER')">
<div class="container">
・・・
</div>
</div>
Session Fixation対策
- デフォルトでログイン後にセッションIDは自動で変更されています。正しくはセッションの情報を引き継いでセッションIDを変更しています。
- セッションの情報を引き継がずセッションIDを変更するには以下のようにします。
// セッションIDを変更し、セッションを新しく作り直す
http.sessionManagement().sessionFixation().newSession();
SpringBootでTOTPを利用したMFA、Facebook/Twitterログインを実装したい
- Java(SpringBoot)でTOTP(Time-based One-time Password),twitter/facebookログインを実現する
- 認証プロバイダの追加方法についても記載してあります。
データベース・トランザクション
コネクションの設定
- application.ymlのHOST,PORT,DATABASE,USERNAME,PASSWORDを設定します。Databaseの方言を吸収するためのDiarectも合わせて設定します。下記はPostgreSQLの例です。
spring:
datasource:
url: jdbc:postgresql://HOST:PORT/DATABASE
username: USERNAME
password: PASSWORD
jpa:
database-platform: org.hibernate.dialect.PostgreSQLDialect
トランザクション境界
- アノテーションを付けることでトランザクション境界を設けることができます。
- デフォルトでは非チェック例外(RuntimeException)が発生しないとロールバックしません。例えばExceptionを継承したクラス全てがthrowされた時にロールバックするにはアノテーションに引数を設定してあげます。
@Component
@Transactional(rollbackOn = Exception.class)
public class AwsEbsService extends ReservationService {
・・・
}
JPA
公式ドキュメントURL
JPAの初期設定
- POMに追記すれば完了です。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
データベースへのCRUD
- PrimaryKeyでのCRUDはEntityを作る→Repositoryを作る。で完了です。制約などは全てEntityのアノテーションで実現しますし、Repositoryもインタフェースを切るだけです。
- JPAの仕様に則っているのでここで作ったEntityは実装が変わっても利用可能です。(JavaEE7のJPA実装であるEclipseLinkでも動きます)
- Entityクラスの例(lombokを使用)はこちら。付与するアノテーションは[こちら]が参考になると思います。(http://otndnld.oracle.co.jp/products/ias/toplink/jpa/resources/toplink-jpa-annotations.html)
@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name="account")
public class Account implements Serializable {
private static final long serialVersionUID = 1L;
/** アカウントID */
@Id
@Column(name = "account_id")
private String accountId;
/** パスワード */
@Column(name = "password")
private String password;
/** メールアドレス */
@Column(name = "mail")
private String mail;
}
- RepositoryはJpaRepositoryを継承したインタフェースです。継承するだけでCRUDの操作を行うことができます。
- JPAをよく知らない人は自分でSQL書いてゴリゴリやるのと少し違うのでRepositoryでの操作については以下のドキュメントを読んで勉強しておくこと。
- http://terasolunaorg.github.io/guideline/public_review/ArchitectureInDetail/DataAccessJpa.html#repository
@Repository
public interface AccountRepository extends JpaRepository<Account, String> {
}
サーバの設定
起動するポートを変更する。
- embedded tomcatの起動するポートを変更することができます。
server: port: 18080
コンテキストパスを設定する。
- spring bootを起動させるとデフォルトではコンテキストパスが設定されません。
- 下記のようにするとコンテキストパスに/appが付与され、RequestMappingアノテーションのPrefixに自動で付与され動きます。
server: contextPath: /app
セッションタイムアウトを設定する。
- デフォルトでセッションタイムアウトは設定されません。セキュリティ上よろしくないので設定します。単位は秒です。
server: session-timeout: 900
ヘルスチェックを実装したい
- ロードバランサからのヘルスチェックはいつも簡単なServletを作ってデータベースや検索エンジンが生きていることを確認するアプリを作っていました。Spring-Actuatorを使えば実装が不要になります。
- 参考:http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#production-ready-health
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
management:
contextPath: /management
port: 8081
security.enabled: false
- このように設定するとport8081の/managementというコンテキストパスにactuatorが稼働します。この状態でヘルスチェックのURLを叩いてみます。
http://localhost:8081/management/health
{"status":"UP","database":"PostgreSQL","hello":1}
- ということでLBからはUPという文字が入っているか否かで判断すれば良さそうですね。
起動・停止スクリプト
- これまでは自前でshを作っていたかもしれませんが、init.d serviceとして動くようになりました。素晴らしい!
- /etc/init.d/myappにシンボリックリンクを貼ります。
sudo ln -s /var/myapp/myapp.jar /etc/init.d/myapp
- ランレベルを設定します。
chkconfig myapp on
- 起動します。
service myapp start
- 環境変数やVMの引数についてはjarファイルと同じディレクトリにconfファイルを配置するだけでOKです。
APP_NAME=cmp-web
PID_FOLDER=/var/run/cmp-web
umask 002
JAVA_OPTS="-Dspring.profiles.active=production -Dlog.path=/var/log/cmp-web"
番外編
SpringBootで起動するtomcatのバージョンを変えたい
- SpringBootのバージョンは変えたくないんだけどtomcatのマイナーバージョンだけは脆弱性に対する対応がされたものに変更したい。という場合はこちらの記事をどうぞ
warでデプロイしたい。
- 74.1 Create a deployable war fileに記載がありますが、私の環境ではそれだけでは上手くいきませんでした。
- pomのpackagingをwarにします。
<packaging>jar</packaging>
- SpringBootを起動するクラスをSpringBootServletInitializerを継承させ、configureを実装します。
public class App extends SpringBootServletInitializer {
private static Log logger = new Log(App.class);
public static void main(String[] args) throws Exception {
ApplicationContext ctx = SpringApplication.run(App.class, args);
String[] beanNames = ctx.getBeanDefinitionNames();
Arrays.sort(beanNames);
for (String beanName : beanNames) {
logger.info(beanName);
}
}
@Override
protected SpringApplicationBuilder configure(
SpringApplicationBuilder application) {
return application.sources(App.class);
}
}
- starter-tomcatのscopeをprovidedにします。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
- これだけで上手くいくはずですが私の場合は2つ手順を足しました。 1. warpluginの設定を追加し、web.xmlが無くてもエラーにしないようにしておく。
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>2.4</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
2.spring-boot-starter-actuatorを利用している場合はコメントアウトする。
SpringBootで設定できるものを探したい。
- 設定はクラスパス直下のapplication.propertiesかapplication.ymlで行います。
- どちらを使うかについてはお好みです。(個人的にはYAMLのほうが見やすくて良い気がしてます)
- このチートシートに記載されておらず、設定できるものについてはSpringBoot Common application propertiesを参照してからググるといいでしょう。
h2 databaseを簡単に使いたい
- サンプルアプリケーションなどを作るときにDBを外に用意するの面倒ですよね。
- そういうときはh2を使うと便利です。SpringBoot1.3.0M4からはより簡単に利用できるようになっています。
- pomに依存関係を追記します。
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
- application.ymlに設定を追記します。基本的にこれだけでDBアクセスが可能になります。
h2:
console:
enabled: true
path: /h2-console
- h2-consoleにアクセスできるようにするには、まずServletRegistrationBeanの設定を行います。
@Configuration
public class WebConfiguration {
ServletRegistrationBean h2servletRegistration() {
ServletRegistrationBean registrationBean = new ServletRegistrationBean(new WebServlet());
registrationBean.addUrlMappings("/h2-console");
return registrationBean;
}
}
- SpringSecurityを利用している場合はHttpSecurityに設定します。
@Configuration
@EnableWebSecurity
public class SecurityConfig {
~~~~ 略 ~~~~
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/loginForm").permitAll().antMatchers("/h2-console/**").permitAll()
.anyRequest().fullyAuthenticated();
~~~~ 略 ~~~~
}
}
- こうすることでh2-consoleにアクセスできるようになります。
http://DOMAIN:PORT/app_name/h2-console
- この他h2-consoleに認証を付けることも可能です。これらの設定については29.4 Using H2’s web consoleをご覧ください。