- 投稿日:2020-02-24T23:03:02+09:00
SpringBootプロジェクトをXAMPPのTomcatにデプロイする
はじめに
SpringBoot
はtomcat
を内蔵しておりjar
実行するだけでアプリを起動できる。
別で構築したtomcat
にデプロイする方法を知らなかったので調べてみた。検証環境
- Windows10
- Java : 1.8.0_221
- XAMPP : 7.3.11
- Tomcat(XAMPPに同梱) : 7.0.96
- Spring Boot : 2.2.4
SpringBootでアプリを作成する
「Spring Initializr」で検証用の雛形プロジェクトをgradleで作成する。
https://start.spring.io/テキトーにWebAPIっぽいものを作る。
- 「/」で「Hello World!」を返却
- 「/hoge」で「hogehoge」を返却
なんの変哲もないコントローラを作成。
AppController.javapackage warapp.app.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class AppController { @GetMapping("/") public String index() { return "Hello World!"; } @GetMapping("/hoge") public String hoge() { return "hogehoge"; } }Applicationクラスを実行して、SpringBootを起動。
「localhost:8080」、「localhost:8080/hoge」に接続して期待通りの結果が取得できた。Tomcatにデプロイする準備
Tomcatに配置するためには2点の条件をクリアしなければならない。
- warファイルの作成
- 内臓Tomcatを使わないように設定
Initializrで作成した状態だと、jarを作成するタスクはあるがwar作成のタスクがない。
やり方がちゃんと公式に書いてあった。Spring Boot Reference Documentation
この手順に沿って行くだけでよい。
SpringBootServletInitializer を継承する
@SpringBootApplication
の付与されているメインクラスをSpringBootServletInitializer
のサブクラスにする。configure
メソッドをオーバライドする。【変更前】
AppApplication.javapackage warapp.app; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class AppApplication { public static void main(String[] args) { SpringApplication.run(AppApplication.class, args); } }【変更後】
AppApplication.javapackage warapp.app; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; @SpringBootApplication public class AppApplication extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { return builder.sources(AppApplication.class); } public static void main(String[] args) { SpringApplication.run(AppApplication.class, args); } }warプラグインを追加
build.gradle
にwarプラグインを追加する。build.gradleplugins { id 'org.springframework.boot' version '2.2.4.RELEASE' id 'io.spring.dependency-management' version '1.0.9.RELEASE' id 'java' } group = 'war-app' version = '0.0.1-SNAPSHOT' sourceCompatibility = '1.8' configurations { developmentOnly runtimeClasspath { extendsFrom developmentOnly } compileOnly { extendsFrom annotationProcessor } } repositories { mavenCentral() } //以下1行を追加 apply plugin: 'war' dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' compileOnly 'org.projectlombok:lombok' developmentOnly 'org.springframework.boot:spring-boot-devtools' annotationProcessor 'org.projectlombok:lombok' testImplementation('org.springframework.boot:spring-boot-starter-test') { exclude group: 'org.junit.vintage', module: 'junit-vintage-engine' } } test { useJUnitPlatform() }組み込みTomcatが干渉しないようにする
dependencies
に以下の設定を追加。build.gradleplugins { id 'org.springframework.boot' version '2.2.4.RELEASE' id 'io.spring.dependency-management' version '1.0.9.RELEASE' id 'java' } group = 'war-app' version = '0.0.1-SNAPSHOT' sourceCompatibility = '1.8' configurations { developmentOnly runtimeClasspath { extendsFrom developmentOnly } compileOnly { extendsFrom annotationProcessor } } repositories { mavenCentral() } apply plugin: 'war' dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' compileOnly 'org.projectlombok:lombok' developmentOnly 'org.springframework.boot:spring-boot-devtools' annotationProcessor 'org.projectlombok:lombok' testImplementation('org.springframework.boot:spring-boot-starter-test') { exclude group: 'org.junit.vintage', module: 'junit-vintage-engine' } //以下を追加 providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat' } test { useJUnitPlatform() }これでwarファイルがbuildできるようになる。
warファイルのビルド
※IntelliJを使用したが、eclipseなどでも同じだと思われる
- gradleのタスク一覧を開く。
- 「Tasks」 → 「build」 → 「bootWar」を実行
【実行結果】
22:31:35: Executing task 'bootWar'... > Task :compileJava UP-TO-DATE > Task :processResources UP-TO-DATE > Task :classes UP-TO-DATE > Task :bootWar BUILD SUCCESSFUL in 1s 3 actionable tasks: 1 executed, 2 up-to-date 22:31:37: Task execution finished 'bootWar'.
<プロジェクトルート>/build/libs
の配下に 「~-0.0.1-SNAPSHOT.war」というファイルができている。Tomcatに反映
あとで面倒くさいのでwarファイル名を変える。
「~-0.0.1-SNAPSHOT.war」→「~.war」
ex. 「app-0.0.1-SNAPSHOT.war」→「app.war」ファイルの配置
「~.war」をTomcatのwebappsディレクトリに配置する。
今回はxamppを使っているので、「xampp\tomcat\webapps」に配置。Tomcatの起動
xamppのコントロールパネルからTomcatを起動する。
起動するとwarファイルが展開される。ログを確認
「xampp\tomcat\logs\catalina.yyyy-mm-dd.log」に以下のようなエラーが出ているはず。
catalina.2020-02-24.log情報: Webアプリケーションアーカイブ C:\xampp\tomcat\webapps\app.war を配備します 2 24, 2020 10:43:45 午後 org.apache.catalina.startup.TldConfig execute 情報: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time. 2 24, 2020 10:43:49 午後 org.apache.catalina.core.ContainerBase addChildInternal 重大: ContainerBase.addChild: start: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/app]] at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:162) at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:1018) at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:994) at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:662) at org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:1127) at org.apache.catalina.startup.HostConfig$DeployWar.run(HostConfig.java:2020) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748) Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'defaultValidator' defined in class path resource [org/springframework/boot/autoconfigure/validation/ValidationAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.validation.beanvalidation.LocalValidatorFactoryBean]: Factory method 'defaultValidator' threw exception; nested exception is java.lang.NoClassDefFoundError: javax/el/ELManager at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:656) at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:484) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1338) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1177) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:557) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517) at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:879) at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:878) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550) at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:141) at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:747) at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397) at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) at org.springframework.boot.web.servlet.support.SpringBootServletInitializer.run(SpringBootServletInitializer.java:152) at org.springframework.boot.web.servlet.support.SpringBootServletInitializer.createRootApplicationContext(SpringBootServletInitializer.java:132) at org.springframework.boot.web.servlet.support.SpringBootServletInitializer.onStartup(SpringBootServletInitializer.java:92) at org.springframework.web.SpringServletContainerInitializer.onStartup(SpringServletContainerInitializer.java:172) at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5709) at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:145) ... 10 more Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.validation.beanvalidation.LocalValidatorFactoryBean]: Factory method 'defaultValidator' threw exception; nested exception is java.lang.NoClassDefFoundError: javax/el/ELManager at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185) at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) ... 32 more Caused by: java.lang.NoClassDefFoundError: javax/el/ELManager at org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator.buildExpressionFactory(ResourceBundleMessageInterpolator.java:88) at org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator.<init>(ResourceBundleMessageInterpolator.java:47) at org.hibernate.validator.internal.engine.ConfigurationImpl.getDefaultMessageInterpolator(ConfigurationImpl.java:474) at org.springframework.boot.validation.MessageInterpolatorFactory.getObject(MessageInterpolatorFactory.java:53) at org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration.defaultValidator(ValidationAutoConfiguration.java:57) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ... 33 more 2 24, 2020 10:43:49 午後 org.apache.catalina.startup.HostConfig deployWAR 重大: Webアプリケーションアーカイブ C:\xampp\tomcat\webapps\app.war を配備中のエラーです java.lang.IllegalStateException: ContainerBase.addChild: start: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/app]] at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:1022) at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:994) at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:662) at org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:1127) at org.apache.catalina.startup.HostConfig$DeployWar.run(HostConfig.java:2020) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748)tomcat側の
javax/el/
ライブラリが古いのが原因のエラーのようだ。
バージョンは2.2
だが、3.0
以上に上げる必要がある。下記リンクから3.0のjarをダウンロードして、「xampp\tomcat\libs」配下に配置する。
このとき、古いバージョンのjarは削除してよい。jarを配置したらTomcatを再起動(STOP→START)する。
立ち上がった後、ログを確認し、スタックトレースが出ていなければ問題なし。
デプロイ確認
ブラウザでデプロイされているのを確認する。
※以下からは「app.war」を配置した体で確認を行っている。ブラウザで「localhost:8080/app」に接続 → 「HelloWorld!」を確認
ブラウザで「localhost:8080/app/hoge」に接続 → 「hogehoge」を確認正常にデプロイされていることが確認できた。
おわりに
今までTomcatにはお世話になってきたが、自分でデプロイしたことがなかったので良い勉強になった。
eclipseの内蔵tomcat、SpringBootの内蔵tomcatを使わなくても自分で対応することができるようになったので安心。
Linux環境へのインストールとデプロイも今後やってみようと思う。
- 投稿日:2020-02-24T19:02:01+09:00
Javaでよく使う構文例
はじめに
Javaで開発することが多くなり、使っている構文などを載せていこうかと思います。
基本的な構文なので、当たり前だと思うこともあると思いますがメモぐらいの気持ちで見ていただければと思います。StringUtils.isEmpty()
String str = "Hello"; if (StringUtils.isEmpty(str)) { }使い方としては空かnullなどをチェックする時に使います。nullチェックがこれでできるのでよく使ったりします。
最近のJavaであればOptionalを使う方がいいですが、まだまだよく使われる記法
isEmptyというのもありますが、nullチェックはしてないのでStringUtils.isEmptyを使ったほうがいいです。ObjectUtils.isEmpty()
StringUtils.isEmpty()と同様の働きをします、Objectに対して行うので、APIなどで取得したデータをEntityに入れたあとにチェックしたりします。APIの取得がうまく行かなくてObjectが正しく作られていない場合にfalseを返すようになります。
簡易的な等式チェック strA.equals(strB)
private static final String RESULT_CODE = "OK"; // 何かのAPIの結果 String apiResult = nanikanoApi(); if (RESULT_CODE.equals(apiResult)) {}このような使い方をします。RESULT_CODE側はnullにならない定数や決まった値にする必要があります。
nullの値をしてしまうとNullPointerExceptionで落ちますので注意。
比較したい値はapiResult側で処理してください。こういうのもあります A == null
基本的にはString型のものだけです。他の型でやるとうまく動きません。
※ 「==」の特性上、メモリの参照先が同じならtrue、メモリの参照先が違えばfalseとなります。今回はnullの場合ですので頭の片隅に置いていただければと思います。
昔から「null == A」と「A == null」の2つの書き方がありますが、現在のJavaではどちらも同じ動きをします。なので「A == null」でいいと思います。(昔はNullPointerExceptionを防げるとかあったらしい)
ちなみに「null == A」はヨーダ記法と呼ばれています。ヨーダがその昔「強いぞ、ベーダ卿は」といったことにつながるとかなんとか。
- 投稿日:2020-02-24T18:22:37+09:00
SocialHub を支える技術 - iOS Android 共通ライブラリ編 -
SocialHub とは?
まずはアプリの紹介をさせてください。SocialHub とは、先日筆者がリリースした iOS 向けマルチ SNS クライアントアプリです。大きな特徴として挙げられるのが、複数の SNS を同時に見て投稿できる ことで、現在対応している SNS の種類は Twitter, Mastodon, Slack, Tumblr になります。
SocialHub - マルチ SNS クライアント - | App Storeマルチプラットフォーム
SocialHub は現在 iOS のアプリしかリリースしていませんが、Android のアプリも見据えたプロジェクトで、複数の SNS を統合して扱う都合上複雑なロジックから逃げられないため、できればワンコードでロジックを記述したい気持ちがありました。昨今流行りのマルチプラットフォームなフレームワークを用いての実装を、当初 (2018年初旬) 考えており、当時のマルチプラットフォームというと、Xamarin や React Native が選択肢としてあったのですが、自分がその分野に明るくなかった事や、一番の問題として、そのライブラリで本当に自分の目指した UI/UX が実現できるか自信がなかったことです。
また、技術選定中に Flutter も注目を浴びましたが、こちらについてもサンプルプロジェクトを作って動作を検証したのですが、パフォーマンスが気になる事や、情報の少なさから、自分の求める UI が作成できるか自信がなかったので採用を見送りました。
共有ライブラリという選択
そこで注目したのが、iOS/Android で共有ライブラリを作ることのできるフレームワークです。共有ライブラリを用いることで、共通のロジックは全て単一の言語で記述し、クライアントアプリ側はネイティブで UI を記述することによって、リッチな体験を担保します。以下に選択肢として考えたものについて記述します。(理解が浅い部分があったらコメントくださると助かります)
Embeddinator-4000
共有ライブラリという方針を決めてから、一番初めに見つけたのが Embeddinator-4000 です。これは Xamarin のサブプロジェクトの一つで C# で書かれたコードを iOS/Android 向けの共有ライブラリとして扱うことができるようにするコンパイラ? (適切な表現かどうか不明) です。細かい内容については、以下の自分のブログ記事を御覧ください。.NET のコードを他言語に変換? Embeddinator-4000 を使ってみた。| urushi blog
動作確認までしたのですが、正直 iOS 向けにバイナリをビルドする際に、C# のジェネリクスが使えない事がかなりの痛手で、ほぼまともに動作するライブラリを作成することができませんでした。理由としては iOS 向けに mono のランタイムをアプリに同梱することができないためで、一朝一夕でなんとかなる問題ではありませんでした。また、開発もあまり活発的ではなく、上記の問題の解決が期待できないので諦めざるを得ない状況でした。
J2ObjC
次に発見したのが J2ObjC です。Google が主に開発を進めているプロジェクトで、Java のコードを Objective-C に変換してくれるコンパイラ になります。既に公開から八年以上経っているプロジェクトですが、まだ開発が行われており、Google 社内でも恐らく使われているようなので、安心感があります (?) J2ObjC についても、ブログ記事を書いたので、詳しくは以下を参照してください。Java のコードを Objective-C に? J2ObjC を試してみた。 | urushi blog
SocialHub はこの J2ObjC を採用しました。 理由としては、
- 自分に Java に対しての言語理解があること
- プロジェクトがメンテされていること
- パフォーマンスの劣化がほぼ無いこと
が挙げられます。J2ObjC はコンパイル前後の言語 (要するに Java と Objective-C) の知識をある程度必要とするので、問題に対応する際に、Java が分からないでは対処が難しいケースが存在します。
ということで SocialHub で作成したコアライブラリがこちら。
(ライブラリ名も SocialHub) J2ObjC でここまでコンパイルしている例って Google 外ではあまりないんじゃないかと思う。(そもそも J2ObjC なんて誰も使ってねえよ、っていう声は聞かないことにします) コンパイルは GitHub Actions に代行させており、大体所要時間は 15 分程。Java を Objective-C に変換した後、マルチアーキテクチャ向けにバイナリを作成し、Cocoapod のレポジトリを作成するところまで実行します。
補足として、J2ObjC のサブプロジェクトでは、既にメンテナンスされていないものも存在し、ややエコシステムとして不便な部分もありました。J2ObjC はコンパイルする際に make (or bazel) を使うケースが多いのですが、自分はめんどくさがりなので、J2ObjC の Gradle Plugin を用いてコンパイルを行っていました。Plugin を使うと Gradle で記述されている依存関係についても自動的にコンパイルターゲット加え、かつ import での参照関係も確認してくれるため、余計なコンパイルをしないで済みます。しかし Gradle Plugin は既にメンテされなくなっており、Java11 では動作しない問題を抱えていました。そのため Fork して修正しました。(結局手間がかかっている)
正直、J2ObjC での初期の実装は茨の道でしたが、その道を通り抜けると、それなりに快適な開発環境を整えることができました。ということで、SocialHub では Java のライブラリを使用して iOS アプリを作成しています。
SocialHub の権利表記画面頑張りすぎた。。。こんながんばらんでもええだろ、誰も見ねえよ。。。 pic.twitter.com/Q93wySWfRt
— うるし (@U_Akihir0) November 10, 2019アプリ内の権利表記にはコアライブラリ内で使用した Java のライブラリも記載しています。Twitter4J をはじめとした各種 SNS クライアントライブラリを使っています。
Kotlin/Native
一応、共有ライブラリということなので、忘れてはいけないもう一つの選択肢である
Kotlin/Native について記載します。こちらは Kotlin で書かれたコードを LLVM エコシステムに混ぜてしまおうという、実にシンプルな目的のプロジェクトです。ここで厄介なのが、Kotlin で書かれたコードといっても、その内容に制約がかかることです。Kotlin といえば一般的に Java の資産がそのまま流用できるイメージを持たれていると思いますが、Kotlin/Native においては JVM 外での Kotlin の使用になるので java, javax 以下のパッケージのライブラリには使用の制限がかかりますし、既存の Kotlin ライブラリもマルチプラットフォーム用に記述されていないと使用できません。
利点としては、プラットフォーム API 等を用いたより高度な抽象化ロジックを記述することができる事にあります。Kotlin/Native では iOS の UIKit を扱うためのライブラリ等が提供されているため、共有ライブラリのレベルで実現できる範囲が広いのが特徴です。SocialHub ではあまりその恩恵を受けられないので、J2ObjC を選択しました。将来的に Java のライブラリが困難なく使用できるレベルになってきたら使用するか検討したいと考えています。(そんな日が来るのかあんま詳しくない)
まとめ
- マルチプラットフォームを選択する場合、本当に実現したい事が何かに立ち戻る
SocialHub の場合ユーザー体験について妥協することがどうしても嫌だったので、UI やパフォーマンスに対してフレームワークを理由に妥協することがどうしてもできなかった → 共通ライブラリという選択
無論、何を意識するかで全てが変わるので、実装スピードやスキルセットに合わせて Flutter や ReactNative を使用することは全然アリです。というか、かなり自分は異端だと感じています。
- J2ObjC 使ってる人いないけどいいぞ
共通ライブラリをまとめると以下のような印象。
フレームワーク 言語 導入難度 知名度 実用性 Embeddinator-4000 C# 難しい ほぼ無名 ✗ J2ObjC Java 難しい ほぼ無名 ○ Kotlin/Native Kotlin 普通 有名 △ J2ObjC のすごいところは、Java の標準ライブラリをほぼ Objective-C で再実装されている点で、思った以上に普通に Java がコンパイルできることです。でも Java なんだよなぁ。。。
- 投稿日:2020-02-24T15:58:11+09:00
Spring Boot パフォーマンス関連の設定
Spring Bootでパフォーマンス関連の設定をする機会があったので、メモしておきます。
私の仮説も含んでいますので、その点はご了承ください。
随時ブラッシュアップしていこうと思います。各種バージョン
openjdk version "11.0.6"
org.springframework.boot:spring-boot-starter-web:jar:2.1.7.RELEASE
org.springframework.boot:spring-boot-starter-jetty:jar:2.1.7.RELEASE
org.springframework.boot:spring-boot-starter-jdbc:jar:2.1.7.RELEASE
com.h2database:h2:jar:1.4.199Jetty(Java Servletコンテナ/Webサーバ)
Jetty はJava Servletコンテナ/Webサーバで、Spring Bootの組み込みコンテナです。
Jettyでは主にスレッド数を設定します。設定項目
設定項目 意味 デフォルト値 application.propertiesの項目名 スレッドプールの最大スレッド数 意味は左記の通りですが、application.propertiesで値を設定してもデフォルト値から変更されませんでした。QueuedThreadPoolには外部からの設定を受け入れるような箇所がないのが原因かと思いますが、調査次第、記載しようと思います。ただ、最大で200もあれば十分なのかなと思ってしまうので、設定できる必要性は薄いのかもしれません。 200 server.jetty.max-threads スレッドプールの最小スレッド数 最大スレッド数と同様、設定しても有効になりませんでした。調査していこうと思います。 8 server.jetty.min-threads アクセプタースレッドの数 アクセプタースレッドは、クライアントからの要求を受け付け、セレクタースレッドに処理を任せます。アクセプタースレッド自体は大したことをしないので、このスレッド数は少なくても大丈夫です。デフォルトから変更する必要はほぼ無いかと思います。なお、アクセプタースレッドもスレッドプールのスレッドです。 -1(オペレーティング環境から導出される。導出のロジックについては参考ページをご覧ください。) server.jetty.acceptors セレクタースレッドの数 セレクターとは、ノンブロッキングIOでクライアントとの通信チャンネルを管理するコンポーネントです。従来のブロッキングIOでは、クライアントとの通信が開始してから完了するまで、スレッドがブロックされていました。Jettyで採用されているノンブロッキングIOでは、ちょっとした待ち時間ではスレッドはブロックされず、その間に別の処理を実行することができ、効率性が向上しています。このためには、通信するチャンネルを覚えておく必要があり、その役割を担うのがセレクターです。セレクターは、後続のJava処理を呼び出したりするので、セレクタースレッドの数は性能に大きな影響を与えます。なお、セレクタースレッドもスレッドプールのスレッドです。 -1(オペレーティング環境から導出される。導出のロジックについては参考ページをご覧ください。) jetty.server.selectors application.propertiesに設定すると、以下のようになります。
application.propertiesserver.jetty.acceptors=2 server.jetty.selectors=3設定が効いているかどうかを確認する方法
Macで確認する方法です。(他の環境でも確認できるはずですが、未確認です。)
①Spring Bootアプリケーションを起動
②jconsoleを起動
ターミナルで
jconsole
と入力し、実行します。jconsoleはJDKに同梱されているGUIツールで、JMXを利用してMBeanの状態を可視化してくれます。起動すると、以下のような画面が表示されます。Spring Bootアプリケーションを選択して、接続してみましょう。
③スレッドの状態を確認
スレッドの状態を確認すると、以下のようになっており、設定が効いていることが確認できます。
なお、ここでは先述のとおり
server.jetty.acceptors=2
、server.jetty.selectors=3
と設定しています。ちなみに、
qtp
で始まるスレッドが8本あるのは、スレッドプールの最小サイズ(スレッド数)のデフォルト値が8だからです。参考
https://www.eclipse.org/jetty/documentation/current/architecture.html
https://support.sonatype.com/hc/en-us/articles/360000744687-Understanding-Eclipse-Jetty-9-4-8-Thread-Allocation
https://www.techscore.com/tech/Java/JavaSE/NIO/5/
https://www.techscore.com/tech/Java/JavaSE/NIO/5-2/
https://spring.pleiades.io/spring-boot/docs/current/reference/html/appendix-application-properties.html#server-propertiesHikariCP(コネクションプール)
HikariCPは高速なコネクションプールとして、Spring Bootでもよく使われます。spring-boot-starter-jdbc -> HikariCP という依存となっていて、spring-boot-starter-jdbcを使用するとHikariCPがデフォルトで利用されます。
HikariCPではコネクションプールで維持するコネクションの数などについて、設定します。
設定項目
設定項目 意味 デフォルト値 application.propertiesの項目名 connectionTimeout クライアントがコネクションプールからコネクションを取得するときの最大待ち時間です。 30000(30秒) spring.datasource.hikari.connection-timeout idleTimeout コネクションプールの中のコネクションが、この時間だけ使われずに放って置かれた場合、そのコネクションはコネクションプールから解放されます。この設定は、minimumIdleがmaximumPoolSize未満に定義されている場合にのみ適用されます。アイドルなコネクションがたくさんあっても、minimumIdleの数のコネクションは解放されずに維持されます。コネクションが解放されるかどうかの判定は、この設定値そのものが閾値になるわけではなく、この設定値を起点としてバラつきがあります(設定値+平均15秒、+最大30秒のバラつき)。値0は、アイドル接続がプールから解放されないことを意味します。許可される最小値は10000ms(10秒)です。 600000(10分) spring.datasource.hikari.idle-timeout maxLifetime プールされてからこの時間が経過したコネクションは、コネクションプールから解放されます。使用中のコネクションは、そのコネクションがcloseされるのを待って削除されます。とはいえ、一気に大量のコネクションが解放されるのはマズイので、そうならないように制御されます。 1800000(30分) spring.datasource.hikari.max-lifetime minimumIdle プールに最低限 維持しておこうとHikariCPが努める、アイドルなコネクションの数。つまりこの数を下回らないことは保証されません。下回った場合はHikarriCPがベストエフォルトでこの数だけのアイドルなコネクションを生成してくれます。しかし、最高のパフォーマンスと、突発的な処理要求に対応できるようにするために、そもそもHicariCPではこの設定を使わず、デフォルト値のまま(maximumPoolSize と同じ)が推奨されます。 maximumPoolSize と同じ。 spring.datasource.hikari.minimum-idle maximumPoolSize プールで維持するコネクション(使用中、アイドルの両方)の上限数です。プール中のコネクションが全て使用中だった場合、getConnection()すると、connectionTimeoutだけブロックします。 10 spring.datasource.hikari.maximum-pool-size application.propertiesに設定すると、以下のようになります。
application.propertiesspring.datasource.hikari.connection-timeout=30000 spring.datasource.hikari.idle-timeout=600000 spring.datasource.hikari.max-lifetime=1800000 spring.datasource.hikari.minimum-idle=10 spring.datasource.hikari.maximum-pool-size=10設定が効いているかどうかを確認する方法
Macで確認する方法です。(他の環境でも確認できるはずですが、未確認です。)
①HikariCPのオブジェクトがMBeanとして登録されるように設定
以下の設定を追加します。
application.propertiesspring.datasource.hikari.register-mbeans=trueこれで、HikariCPのオブジェクトがMBeanとして登録され、JMXを利用して外部からモニタリングできるようになります。
②Spring Bootアプリケーションを起動
③jconsoleを起動
ターミナルで
jconsole
と入力し、実行します。jconsoleはJDKに同梱されているGUIツールで、JMXを利用してMBeanの状態を可視化してくれます。起動すると、以下のような画面が表示されます。Spring Bootアプリケーションを選択して、接続してみましょう。
④MBeanの状態を確認
アクティブなコネクション、アイドルなコネクションの状況については、以下のように確認できます。
参考
https://github.com/brettwooldridge/HikariCP
https://spring.pleiades.io/spring-boot/docs/current/reference/html/appendix-application-properties.html#data-properties
https://matsumana.info/blog/2016/02/06/spring-boot-hikaricp-metrics/
- 投稿日:2020-02-24T12:42:42+09:00
Java8メソッド参照
今回みなさんと一緒にJava8のメソッド参照を勉強しましょう。
まずは、Java8以前の方法で年齢ソードのメソットを作りましょうか。
lombokを使う
import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.util.*; public class App { public static void main( String[] args ) { List<Person> personList = new ArrayList<Person>(); personList.add(new Person("Jack",25)); personList.add(new Person("Mary",23)); personList.add(new Person("Jill",24)); personList.add(new Person("Dock",34)); personList.add(new Person("Lily",27)); // 匿名クラス Collections.sort(personList, new Comparator<Person>() { @Override public int compare(Person o1, Person o2) { return o1.getAge() - o2.getAge(); } }); System.out.println(personList); } } @Data @AllArgsConstructor @NoArgsConstructor class Person { private String name; private int age; }実装結果:
[Person(name=Mary, age=23), Person(name=Jill, age=24), Person(name=Jack, age=25), Person(name=Lily, age=27), Person(name=Dock, age=34)]
Lambda式
import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.util.*; public class App { public static void main( String[] args ) { List<Person> personList = new ArrayList<Person>(); personList.add(new Person("Jack",25)); personList.add(new Person("Mary",23)); personList.add(new Person("Jill",24)); personList.add(new Person("Dock",34)); personList.add(new Person("Lily",27)); // Lambda式 Collections.sort(personList,(p1,p2) -> p1.getAge() - p2.getAge()); System.out.println(personList); } } @Data @AllArgsConstructor @NoArgsConstructor class Person { private String name; private int age; }実装結果:
[Person(name=Mary, age=23), Person(name=Jill, age=24), Person(name=Jack, age=25), Person(name=Lily, age=27), Person(name=Dock, age=34)]
static メソッド参照
import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.util.*; public class App { public static void main( String[] args ) { List<Person> personList = new ArrayList<Person>(); personList.add(new Person("Jack",25)); personList.add(new Person("Mary",23)); personList.add(new Person("Jill",24)); personList.add(new Person("Dock",34)); personList.add(new Person("Lily",27)); // static メソッド参照 Collections.sort(personList, Person::CompareAge); System.out.println(personList); } } @Data @AllArgsConstructor @NoArgsConstructor class Person { private String name; private int age; public static int CompareAge(Person o1, Person o2) { return o1.getAge() - o2.getAge(); } }
- 投稿日:2020-02-24T12:42:12+09:00
ラムダ式入門
何でラムダ式を使うのか?
大規模な開発に使われるJavaでは、なるべくコードの量を減らし読みやすいことを求められています。そのような時役にたつのがJava8で使われている「ラムダ式」です。
ラムダ式の特徴としては、メソッドを変数のように扱えることです。書き方:
[インタフェース宣言]= (引数)->{実行ブロック}さっそくソースコードから確認してみましょう。
まずは伝統的な表現式:public class LambdaTest { public static void main(String[] args) { // 伝統的な表現 new Thread(new Runnable() { @Override public void run() { System.out.println("threading" + Thread.currentThread().getId()); } }).start(); } }次は、Lambda式の表現です。
public class LambdaTest { public static void main(String[] args) { // Lambda表現 new Thread(() -> { System.out.println("Lambda threading" + Thread.currentThread().getId()); }).start(); } }Java関数型インタフェース
Javaの関数型インタフェースは伝統的なインタフェースとほとんど同じですが、唯一の特徴は関数インタフェースが「定義されている抽象メソッドが1つだけあるインターフェース」であることです。staticメソッドやデフォルトメソッドは含まれていても構わないです。(java.lang.FunctionalInterfaceアノテーションを付けるのが良いでしょう)。
Java関数インタフェースについての詳細紹介を参考ください。
https://qiita.com/Jenny1025/items/470e304ff77c44874fc8例:
@FunctionalInterface public interface UserIdentify { String verifyUser(String userName); }Lambda式のメリット
JavaのLambda式のメリットは、匿名クラスを用いる関数型インタフェースの記述を簡潔になることが実現できます。
*匿名クラス:一度だけ使わないようなクラス匿名クラス:
public class App { public static void main( String[] args ) { // 関数型インタフェース UserIdentify ic = new UserIdentify() { @Override public String verifyUser(String userName) { return "admin".equals(userName)? "admin":"member"; } }; System.out.println(ic.verifyUser("admin")); System.out.println(ic.verifyUser("manager")); } }では、Lambda式でどう表現するか?
public class App { public static void main( String[] args ) { UserIdentify ic1 = (String userName) -> { return "admin".equals(userName)? "admin":"member"; }; System.out.println(ic1.verifyUser("admin")); System.out.println(ic1.verifyUser("manager")); } }具体的にラムダ式の書き方の説明
1)宣言:ラムダ式のインタフェースの型
2)引数:()中に記入する。インタフェースの引数の数及び順番と同じ
3)表現式:->
4)実行ブロック:{}中に実装するでは、具体的な例から説明に行きましょう。
引数なし、戻り値なし
public class App { public static void main( String[] args ) { // 引数なし、戻り値なし Lambda1 lambda1 = () -> System.out.println("Hello world"); lambda1.test(); } interface Lambda1 { void test(); } } 実装結果:Hello world引数あり、戻り値なし
public class App { public static void main( String[] args ) { // 引数あり、戻り値なし Lambda2 lambda2 = (n, a) -> System.out.println(n + " is " + a + " years old"); lambda2.test("Sally",18); } interface Lambda2 { void test(String name, Integer age); } } 実装結果:Sally is 18 years old引数あり、戻り値あり
public class App { public static void main( String[] args ) { // 引数あり、戻り値あり Lambda3 lambda3 = (a, b) -> a + b; System.out.println(lambda3.test(100,3000)); } interface Lambda3 { int test(int a, int b); } } 実装結果:3100
- 投稿日:2020-02-24T12:39:56+09:00
関数型インタフェース紹介
みなさん、こんにちは。
今回関数型のインタフェースを紹介させていただきます。Predicate (戻り値→ boolean)
public interface Predicate <T>メソッド例:
import java.util.function.Predicate; public class App { public static void main( String[] args ) { Predicate <String> pre = (String username) -> { return "admin".equals(username); }; System.out.println(pre.test("manager")); System.out.println(pre.test("admin")); } }戻り値:
false trueConsumer (戻り値なし)
public interface Consumer<T>import java.util.function.Consumer; public class App { public static void main( String[] args ) { Consumer<String> con = (String message) -> { System.out.println("Return message: " + message); System.out.println("The message send succeed"); }; con.accept("Hello world"); con.accept("Lambda"); } }Function
public interface Function<T,R>例:
import java.util.function.Function; public class App { public static void main( String[] args ) { Function<String, Integer> fun = (String gender) -> { return ("male".equals(gender))?0:1; }; System.out.println(fun.apply("male")); System.out.println(fun.apply("female")); } }結果:
0 1
- 投稿日:2020-02-24T11:05:10+09:00
[Java] 今(2020年)から始めるJava開発で選択できるプラットフォーム
Java界隈のウワサ話は聞いていたけれど、いざ使おうとしたらどれを使ったらいいかわからない。
サポート期間が長くて、あまりお金がかからないのがいいな。
そんなひと(自分![]()
)がのための情報まとめです。
JDK一覧
受託開発業者が、客先へ納品するプロダクトの実行環境として使用できそうなJavaをまとめてみました。
名前 Javaバージョン サポートOS サポート期限 必要なもの ライセンス Oracle JRE (Oracle Java SE) 8 Windows, Linux, macOS, Solaris 2025年3月 サブスクリプションを購入 OTN Agreement for Oracle Java SE Oracle Java SE 11 Windows, Linux, macOS 2026年9月 サブスクリプションを購入 OTN Agreement for Oracle Java SE Red Hat OpenJDK (Windows用) 11 Windows 2024年10月 サブスクリプションを購入 GPLv2 with CPE AdoptOpenJDK 8 Windows, Linux, macOS, AIX, Solaris 2023年9月 (なし) GPLv2 with CPE AdoptOpenJDK 11 Windows, Linux, macOS, AIX 2022年9月 (なし) GPLv2 with CPE Amazon Corretto 8 Windows, Linux, macOS 2023年6月 (なし) GPLv2 with CPE Amazon Corretto 11 Windows, Linux, macOS 2024年8月 (なし) GPLv2 with CPE Azul Zulu Enterprise 8 Windows, Linux, macOS 2026年3月 サブスクリプションを購入 GPLv2 with CPE Azul Zulu Enterprise 11 Windows, Linux, macOS 2027年9月 サブスクリプションを購入 GPLv2 with CPE Azul Zulu Community 11 Windows, Linux, macOS, Solaris 2027年9月 (なし) GPLv2 with CPE SapMachine 11 Windows, Linux, macOS 2022年9月 (なし) GPLv2 with CPE Oracle OpenJDK 13(サポート期限2020年3月)とOracle OpenJDK 14(サポート期限2020年9月)は省きました。
Linuxディストリビューション付属のパッケージ
Linuxディストリビューションに含まれているOpenJDKパッケージです。
たいていはこちらを利用することになると思います。
ディストリビューションとバージョン Javaバージョン サポート期限 RHEL 6, 7, 8 8 2023年6月 RHEL 7, 8 11 2024年10月 CentOS 6, 7, 8 8 RHELに準ずる? CentOS 7, 8 11 RHELに準ずる? クラウド環境で使用できるJDK
クラウドベンダ サービス OS Javaバージョン サポート期限 Amazon Web Services EC2 Amazon Linux 1 8 2020年6月(?) Amazon Web Services EC2 Amazon Linux 2 8 2023年4月(?) Amazon Web Services EC2 Amazon Linux 2 11 2023年4月(?) Microsoft Azure VM Windows, Linux 7 2023年7月 Microsoft Azure VM Windows, Linux 8 2025年3月 Microsoft Azure VM Windows, Linux 11 2026年9月 結局どれ使えばいい?
バージョンは?
今すぐ動かすなら11一択です。
2021年9月以降に動かすなら、次のLTSである17へ向けて備えると良いと思われます。
JDKは?
条件に応じての選択になるかと思われます。
Oracleにする?Oracle以外にする?
まずOracleにするか、Oracle以外にするかを選びます。
フェラーリを買えるようなお金持ちはOracleを選ぶんだろうなぁ(ひがみ)。Oracle以外の場合
クラウドの場合は、環境についてくるJavaを使うのがよいと思われます。
オンプレのLinuxの場合は、OSとサポートを合わせられますし、付属のパッケージを使うのが簡単かと思われます。
オンプレのWindowsで有償サポートが欲しい場合は、Red HatかZuluがよいと思われます。
リンク集
- Oracle
- RedHat
- AdoptOpenJDK
- Amazon
- Microsoft
- Azul Systems
- SAP
- 最適なOpenJDKディストリビューションの選び方
ところで
いまどきのJavaアプリケーションのデプロイは、どうするのが正解なんでしょうか?
- 昔ながらのjarを配布
- dockerイメージを作成して配布
- GraalVMでネイティブ化して配布
使う側からしたら、3.が一番手間なくてよさそうですが、実際にやっているひといるのかな。
- 投稿日:2020-02-24T10:04:34+09:00
Javaコメントカウント(VBA資材)
備忘録
2020/02.24
単体コメントカウントOption Explicit Const Start_Row As Integer = 3 Const Start_Col As Integer = 2 Const Start_msg As String = "ここにスタートコメント" Const End_msg As String = "ここにエンドコメント" Sub Delコメントカウント() Dim i As Integer Dim MaxRow As Long Dim DelCount As Long Dim checkStr As String Dim checkPoint As Boolean checkPoint = False ' B列の最後行を取得 MaxRow = Range("B65536").End(xlUp).Row For i = 3 To MaxRow checkStr = Cells(i, Start_Col).Value checkStr = Trim(checkStr) ' スタートメッセージもしくは2周目の場合 If checkStr = Start_msg Or checkPoint Then checkPoint = True checkStr = Left(checkStr, 1) ' /コメント判定 If checkStr = "/" Then ' *コメント反映 ElseIf checkStr = "*" Then ' 空白行判定 ElseIf checkStr = "" Then Else ' 2周目フラグに真を設定 ' カウント数インクリメントする DelCount = DelCount + 1 End If End If ' エンドメッセージの場合 If checkStr = End_msg Then ' 2周目フラグに偽を設定 checkPoint = False ' スタート、エンドメッセージ分カウントを引く DelCount = DelCount - 2 End If Next i MsgBox DelCount End Sub