SpringBootをKubernetes上で動かしてみる
これは2018年02月26日にQiitaに投稿した記事を移行したものです
about
SoftwareDesign 2018/3号を読んでいてKubernetesに少し興味を持ったので試してみました。 色々ハマりどころがあるかと思いましたが、環境構築〜デプロイ、スケールまで割りとすんなりと進めることができました。
環境
- macOS High Sierra 10.13.3
- Kubernetes v1.8.0
- minikube v0.24.1
- VirtualBox v5.2.6 r120293
インストール
VirtualBox
$ brew cask install virtualbox
※Hish Sierraだとインストール中に止まってしまうことがあります。
その場合はシステム環境設定 > セキュリティとプライバシー
でOracleがブロックされたみたいなものが表示されているので「許可」するとインストールできます。
(スクリーンショット撮り忘れ)
minikube
$ brew cask install minikube
動作確認
minikubeの起動
$ minikube start Starting local Kubernetes v1.8.0 cluster... Starting VM... Getting VM IP address... Moving files into cluster... Setting up certs... Connecting to cluster... Setting up kubeconfig... Starting cluster components... Kubectl is now configured to use the cluster. Loading cached images from config file.
VirtualBox上でminikubeが作成されて起動していることが確認できます
デプロイメントの作成
# deployment $ kubectl run hello-minikube --image=k8s.gcr.io/echoserver:1.4 --port=8080 deployment "hello-minikube" created $ kubectl expose deployment hello-minikube --type=NodePort service "hello-minikube" exposed $ kubectl get pod NAME READY STATUS RESTARTS AGE hello-minikube-7844bdb9c6-88hl4 1/1 Running 0 2m
この状態でダッシュボードを確認してみると
# ダッシュボードの起動 $ minikube dashboard
デプロイメントとポッドが作成される。
# URLの確認 $ minikube service hello-minikube --url $ curl $(minikube service hello-minikube --url) CLIENT VALUES: client_address=172.17.0.1 command=GET real path=/ ・ ・ ・
# サービスの削除 $ kubectl delete service hello-minikube service "hello-minikube" deleted # デプロイメントの削除 $ kubectl delete deployment hello-minikube deployment "hello-minikube" deleted # minikube終了 $ minikube stop Stopping local Kubernetes cluster... Machine stopped.
Spring BootのDocker環境を用意する
今回は公式チュートリアルの完成版を利用します。 https://spring.io/guides/gs/spring-boot-docker/
スターターをclone
$ git clone https://github.com/spring-guides/gs-spring-boot-docker.git # 完成版を使う $ cd gs-spring-boot-docker/complete $ ./gradlew build docker ・ ・ ・ :processTestResources NO-SOURCE :testClasses :test :check :build :dockerClean UP-TO-DATE :dockerPrepare :docker BUILD SUCCESSFUL Total time: 1 mins 37.278 secs
動かしてみる
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE springio/gs-spring-boot-docker latest d486fdb38288 4 minutes ago 116MB $ docker run -p 8080:8080 -t springio/gs-spring-boot-docker
docker上でSpringBootが起動しました。
kubernetes上で動かす
環境変数の設定
$ minikube start $ eval $(minikube docker-env) # この状態では一時的にローカルPCからdockerへの接続などができなくなりますが、ターミナルを再起動すれば設定が消えて元にもどります。
サービスの設定ファイルを以下の内容で作成する。
apiVersion: v1 kind: Service metadata: name: hellojavakubernetes labels: app: hellojavakubernetes tier: backend spec: type: NodePort ports: # the port that this service should serve on - port: 8080 selector: app: hellojavakubernetes tier: backend
サービスの登録
$ kubectl create -f kubernetes-service.yaml
hellojavakubernetes
サービスが登録されました。
デプロイメント設定ファイルを以下の内容で作成する。
apiVersion: extensions/v1beta1 kind: Deployment metadata: name: hellojavakubernetes spec: replicas: 1 template: metadata: labels: app: hellojavakubernetes tier: backend spec: containers: - name: hellojavakubernetes image: springio/gs-spring-boot-docker resources: requests: cpu: 100m memory: 100Mi ports: - containerPort: 8080
デプロイしてみる。
$ kubectl create -f kubernetes-deployment.yaml deployment "hellojavakubernetes" created $ kubectl get deployment NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE hellojavakubernetes 1 1 1 1 3m
hellojavakubernetes
というデプロイメントが作成され、ポッドも作成されています。
URLを調べる。
$ minikube service hellojavakubernetes --url http://192.168.99.100:31830
表示されたURLにアクセスする
正しく表示されました。 ここまで詰まることなく進めることができました。
アプリケーションのスケーリング
せっかくなのでスケーリングしてみます。
$ kubectl get deployments NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE hellojavakubernetes 1 1 1 1 17h # replicasを指定する $ kubectl scale --replicas=2 -f kubernetes-deployment.yaml deployment "hellojavakubernetes" scaled # deploymentsの数が2に増えました。 $ kubectl get deployments NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE hellojavakubernetes 2 2 2 2 17h
ポッドの数が2つに増えていることがわかります。
続いて設定ファイルから変更してみます。
上の方で作成したkubernetes-deployment.yaml
のreplicas
を3
に変更します。
# 設定の適用 $ kubectl apply -f kubernetes-deployment.yaml Warning: kubectl apply should be used on resource created by either kubectl create --save-config or kubectl apply deployment "hellojavakubernetes" configured $ kubectl get deployments NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE hellojavakubernetes 3 3 3 3 17h
こちらも無事スケールできました。
後始末
終了する
$ kubectl delete service,deployment hellojavakubernetes $ minikube stop $ minikube delete
本番で運用するにはまだまだ調べることが多そうですがKubernetes自体はかなり完成されているなという印象でした。
参考ページ
[minikube] https://github.com/kubernetes/minikube
[Spring Boot with Docker] https://spring.io/guides/gs/spring-boot-docker/
[getting started] https://www.bluefyre.io/getting-started-springboot-kubernetes/
Spring Boot (Kotlin) はじめの一歩
これは2018年01月30日にQiitaに投稿した記事を移行したものです
目的
Spring Bootを学習するきっかけが欲しいと友人にたのまれたので、 実際にペアプロしながら進めていくための資料として作成しました。
Spring Boot (Kotlin)入門 - MVC - Rest - DB Access
使用するもの
※IntelliJ Ideaの代わりにSTS + Kotlin PluginでもOKですが、手順は記載していません。 ※Mavenの場合、dependencyなどをpom用に読み替えてください。 ※Doma2のEntity / Dao実装はKotlinではなくJavaになっています。
環境構築
IntelliJ Ideaのインストール
こちらから https://www.jetbrains.com/idea/
Spring Bootプロジェクトの雛形を作成する
Spring Initializrで雛形を作成する https://start.spring.io/
以下の内容で生成します。
- Gradle Project
- Kotlin
- Spring Boot 1.5.9
- Dependencies => Web
Group / Artifactはお好みで
雛形のzipファイルがダウンロードされるので、適当な場所に展開しておきます。
プロジェクトを開く
IntelliJを起動してImport Project
展開したプロジェクトの雛形の中にあるbuild.gradle
を指定する。
Use auto-importにチェックを入れてOK
ビルドが始まるので、synced successfullyになればOK
View -> Tool Window -> Gradle
を選択してGradleのツールウィンドウを開く。
Gradleツールウィンドウ内のdemo -> Tasks -> application -> bootRun
を右クリックして Run 'demo[boot Run]'で起動する
14:44:18: Executing task 'bootRun'... :compileKotlin :compileJava NO-SOURCE :copyMainKotlinClasses :processResources :classes :findMainClass :bootRun . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v1.5.9.RELEASE) ・ ・ ・ s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http) 2018-01-20 14:44:28.125 INFO 13432 --- [ main] com.example.demo.DemoApplicationKt : Started DemoApplicationKt in 2.45 seconds (JVM running for 2.75)
※この時点ではControllerなどが無いので、何もできない。
Spring Boot起動中にIntelliJでコード修正が反映されるようにbuild.gradle
の設定
plugin定義に以下を追加する
apply plugin: 'idea' idea { module { outputDir file('build/classes/main') testOutputDir file('build/classes/test') } } if(project.convention.findPlugin(JavaPluginConvention)) { // Change the output directory for the main and test source sets back to the old path sourceSets.main.output.classesDir = new File(buildDir, "classes/main") sourceSets.test.output.classesDir = new File(buildDir, "classes/test") }
一番下のdependenciesに以下を追加する
compile("org.springframework.boot:spring-boot-starter-thymeleaf") compile("org.springframework.boot:spring-boot-devtools")
- thymeleaf => テンプレートエンジンのthymeleafを使う
- spring-boot-devtools => オートリロードを有効にする
オートリロードの設定
Setting -> Build -> Compiler
を開き、Build project automatically
にチェックを入れてApply
Windowsの場合:Sthif + Ctrl -> A
Macの場合:Command + Ctrl + A
でウィンドウを開き、Registry..
で検索
開いたウィンドウでcompiler.automake.allow.when.app.running
にチェックを入れる
ThymeleafでHTMLを返す
Controllerの作成
com.example.demo
の下にcontroller
パッケージを作成する。
controller
パッケージの下に、GreetingController.kt
を作成する。
package com.example.demo.controller import org.springframework.stereotype.Controller import org.springframework.ui.Model import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestParam @Controller @RequestMapping("greeting") class GreetingController { @GetMapping("/hello") fun hello( @RequestParam(value = "name", required = false, defaultValue = "world") name: String, model: Model): String { model.addAttribute("name", name) return "greeting" } }
html(Thymeleaf)の作成
src/main/resources/templates
の下にgreeting.html
を作成する
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8" http-equiv="Content-Type" content="text/html" /> <title>Getting Started: Saving Web Content</title> </head> <body> <p th:text="'Hello, ' + ${name} + '!'" /> </body> </html>
動作確認
再度Spring Bootを実行する
Webブラウザでhttp://localhost:8080/greeting/hello
にアクセス
http://localhost:8080/greeting/hello?name=hoge
にしてみると、
表示がHello, Hoge!に変わる。
DB環境準備
Virtual Box / Vagrantのインストール
※Windows10 Homeで作業したためDocker for Windowsが使えないので仕方なくVirtual Box / Vagrantを使っています。 Windows10 ProやMacの方はDockerで構築する方が楽なので、VirtualBox / Vagrantの章は飛ばしてdocker-composeからはじめてOKです
Virtual Box https://www.virtualbox.org/
Vagrant https://www.vagrantup.com/downloads.html
プラグインのインストール
$ vagrant plugin install vagrant-vbguest
CentOS7のBoxをインストール・起動
cd [任意のディレクトリ] vagrant init centos/7
Vagrantfile
が生成されるので、ポートフォワード・IPアドレスのバインディングを設定する
# 以下の設定を追加する config.vm.network "forwarded_port", guest: 3306, host: 3306 config.vm.network "private_network", ip: "192.168.33.10"
vagrant up # 終わったら↓でrunningになっていることを確認 vagrant status # vagrantに接続する vagrant ssh
Vagrant上のCentOS7にDockerをインストール
https://docs.docker.com/engine/installation/linux/docker-ce/centos/
# インストール確認 docker -v # docker起動 sudo systemctl start docker # 動作確認 sudo docker run hello-world # vagrant起動時にdockerが起動するように sudo systemctl enable docker
一般ユーザーでもdockerコマンドを使えるようにする
sudo gpasswd -a $USER docker sudo systemctl restart docker # 一度再ログインする exit
続いてdocker-composeをインストールする ※Docker for Mac (Windows)の場合、同時にインストールされるのでスキップしてOK
https://docs.docker.com/compose/install/#install-compose
$ sudo curl -L https://github.com/docker/compose/releases/download/1.18.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose $ sudo chmod +x /usr/local/bin/docker-compose $ docker-compose --version
docker-compose.yml
の作成
db: image: mysql:5.7 ports: - "3306:3306" environment: MYSQL_ROOT_PASSWORD: root volumes_from: - data data: image: busybox:1 volumes: - /var/lib/mysql:/var/lib/mysql # macの場合、適当なディレクトリを指定する
イメージの作成
$ docker-compose up -d $ docker-compose ps [vagrant@localhost ~]$ docker-compose ps Name Command State Ports ------------------------------------------------------------------------------ vagrant_data_1 sh Exit 0 vagrant_db_1 docker-entrypoint.sh mysqld Up 0.0.0.0:3306->3306/tcp
mysqlへの接続確認
$ docker exec -it vagrant_db_1 bash # mysql -uroot -proot # 適当にデータベースを作成しておく mysql> create database test;
ホストマシン(Windows / Mac)からMySQLに接続してみる。
設定方法はクライアント次第だけどこんな感じでつながるはず
※Vagrantを使わない場合はHostは172.0.0.1
項目 | 設定値 |
---|---|
Host | 192.168.33.10 |
Database | test |
User | root |
Password | root |
適当にテーブルを作っておく
CREATE TABLE anything( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255), updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
プロジェクトにDoma2を設定する
# repositoriesに↓を追加 maven { url 'https://oss.sonatype.org/content/repositories/snapshots' } # compileKotlinの前に↓を追加 processResources.destinationDir = compileJava.destinationDir compileJava.dependsOn processResources # dependenciesに↓を追加 compile group: 'mysql', name: 'mysql-connector-java', version: '6.0.6' compile('org.seasar.doma.boot:doma-spring-boot-starter:1.0.2')
application.properties
の設定
# JDBC spring.datasource.url=jdbc:mysql://192.168.33.10:3306/test # vagrantを使わない場合はこちら # spring.datasource.url=jdbc:mysql://172.0.0.1:3306/test spring.datasource.username=root spring.datasource.password=root spring.datasource.driver-class-name=com.mysql.jdbc.Driver # DOMA doma.dialect=mysql doma.naming=snake_lower_case
DBアクセスの実装
src/main
配下にjava
ディレクトリを作成
src/main/java
配下に com.example.demo.entity
, com.example.demo.dao
パッケージを作成する。
以下のクラスを作成していく
package com.example.demo.entity; import org.seasar.doma.Entity; import org.seasar.doma.GeneratedValue; import org.seasar.doma.GenerationType; import org.seasar.doma.Id; import org.seasar.doma.Table; import java.sql.Timestamp; @Entity @Table(name = "anything") public class AnythingEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) public Integer id; public String name; public Timestamp createdAt; public Timestamp updatedAt; }
package com.example.demo.dao; import com.example.demo.entity.AnythingEntity; import org.seasar.doma.Dao; import org.seasar.doma.Insert; import org.seasar.doma.Select; import org.seasar.doma.boot.ConfigAutowireable; import java.util.List; @ConfigAutowireable @Dao public interface AnythingDao { @Select List<AnythingEntity> selectAll(); @Insert int insert(AnythingEntity anything); }
===ここからkotlin配下===
com.example.demo.service
パッケージを作成する
以下のクラスを作成する
package com.example.demo.service import com.example.demo.dao.AnythingDao import com.example.demo.entity.AnythingEntity import org.springframework.stereotype.Service @Service class AnythingService( val anythingDao: AnythingDao ) { fun findAll(): List<AnythingEntity> { return this.anythingDao.selectAll() } fun insert(anything: AnythingEntity): Int { return this.anythingDao.insert(anything) } }
package com.example.demo.controller import com.example.demo.entity.AnythingEntity import com.example.demo.service.AnythingService import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController @RestController @RequestMapping("anything") class AnythingController( val anythingService: AnythingService ) { @GetMapping("/findAll") fun findAll(): List<AnythingEntity> { return this.anythingService.findAll() } // 本当はGetにするべきではないですが、面倒なので・・・ @GetMapping("/insert") fun insert(@RequestParam(value = "name", required = false, defaultValue = "doma") name: String): String { val entity = AnythingEntity() entity.name = name this.anythingService.insert(entity) return "success" } }
sqlファイルを作成する
SELECT * FROM anything
動作確認
curlかブラウザで以下を実行 http://localhost:8080/anything/insert http://localhost:8080/anything/insert?name=fuga http://localhost:8080/anything/findAll
以下のような結果が帰ってくればOK ※上の2行でDBへのインサート、最後の行でインサートした結果を取得しています。
[{"id":1,"name":"doma","createdAt":1516459862000,"updatedAt":1516459862000},{"id":2,"name":"fuga","createdAt":1516459895000,"updatedAt":1516459895000}]
まとめ
今回はSpring Bootで開発を始める第一歩を書きました。 Doma2を使ったのは自分が触ってみたかったからです。 余裕があったら続編を書くかもしれません。
最終的なコードはこちら
Javaで書かれたSpring BootプロジェクトをKotlin対応した話
これは2017年12月18日にQiitaに投稿した記事を移行したものです。
背景
某社のエンジニアチームで使用技術のモダナイズを進めており、「Java / SpringBootで書かれたシステムをどうせならKotlinで書きたいよね」という話が出てきたので、Kotlinで開発できるように対応を進めています。
最初からKotlinで始める(Gradle)といった記事はよく見かけますが、Javaで書かれている途中からKotlinに対応する(Maven)という記事があまり見つからなかったので、手探りで対応していきました。
既存の環境
- macOS High Sierra
- JDK version => 1.8
- Spring Boot => 1.5.8
- Lombokを使用している
- ビルドシステム => Maven
- IntelliJ派とSTS派がいる
- 私は前者です。
基本的にはSpringBootではREST APIのみを実装しています。
Kotlin対応させる方針
- 並行でJavaでの開発が進んでいるため、全コードをJavaからKotlinに変換するのは今回は見送り
- Controller / ServiceはKotlinで書けるようにする
- JPAなどのデータアクセス層はJavaのまま残しておく
- 最終的には全部Kotlinにしたい
また、今回の作業はIntelliJ上で行いました。
移行作業
pom.xmlの設定
- pom.xmlの設定は、以下のKotlinの公式サイトを参考にしました。
- using-maven
- compiler-plugins
※今回の投稿に関係ある部分を中心に記載しています。
- Kotlinバージョンの定義
<properties> ... <java.version>1.8</java.version> + <kotlin.version>1.2.10</kotlin.version> + <kotlin.compiler.incremental>true</kotlin.compiler.incremental> </properties>
- Kotlinの依存ライブラリを追加
<dependencies> ・・・ + <dependency> + <groupId>org.jetbrains.kotlin</groupId> + <artifactId>kotlin-stdlib</artifactId> + <version>${kotlin.version}</version> + </dependency> + <dependency> + <groupId>org.jetbrains.kotlin</groupId> + <artifactId>kotlin-reflect</artifactId> + <version>${kotlin.version}</version> + </dependency> + <dependency> + <groupId>org.jetbrains.kotlin</groupId> + <artifactId>kotlin-test</artifactId> + <version>${kotlin.version}</version> + <scope>test</scope> + </dependency> </dependencies>
- ビルド設定
元々maven-compiler-plugin
は使用していませんでしたが、JavaとKotlinのどちらでも開発ができるように、パッケージをそれぞれ
で分けため、maven-compiler-plugin
を使用しています。
また、Springで使用する各種アノテーションを使用できるようにするため、compilerPluginsとしてspringを定義しています。 ドキュメントにも記載されていますが、springプラグインを定義するとall-openの定義は不要になるようです。
<build> <finalName>${project.name}</finalName> - <sourceDirectory>src/main/java</sourceDirectory> - <testSourceDirectory>src/test/java</testSourceDirectory> <resources> <resource> <directory>${resources.directory}</directory> </resource> <resource> <directory>src/main/resources</directory> </resource> </resources> <testResources> <testResource> <directory>src/test/resources</directory> </testResource> </testResources> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <executable>true</executable> </configuration> </plugin> + <plugin> + <artifactId>kotlin-maven-plugin</artifactId> + <groupId>org.jetbrains.kotlin</groupId> + <version>${kotlin.version}</version> + <configuration> + <compilerPlugins> + <plugin>spring</plugin> + </compilerPlugins> + </configuration> + <executions> + <execution> + <id>compile</id> + <goals> <goal>compile</goal> </goals> + <configuration> + <sourceDirs> + <sourceDir>${project.basedir}/src/main/kotlin</sourceDir> + <sourceDir>${project.basedir}/src/main/java</sourceDir> + </sourceDirs> + </configuration> + </execution> + <execution> + <id>test-compile</id> + <goals> <goal>test-compile</goal> </goals> + <configuration> + <sourceDirs> + <sourceDir>${project.basedir}/src/test/kotlin</sourceDir> + <sourceDir>${project.basedir}/src/test/java</sourceDir> + </sourceDirs> + </configuration> + </execution> + </executions> + <dependencies> + <dependency> + <groupId>org.jetbrains.kotlin</groupId> + <artifactId>kotlin-maven-allopen</artifactId> + <version>${kotlin.version}</version> + </dependency> + </dependencies> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.5.1</version> + <executions> + <!-- Replacing default-compile as it is treated specially by maven --> + <execution> + <id>default-compile</id> + <phase>none</phase> + </execution> + <!-- Replacing default-testCompile as it is treated specially by maven --> + <execution> + <id>default-testCompile</id> + <phase>none</phase> + </execution> + <execution> + <id>java-compile</id> + <phase>compile</phase> + <goals> <goal>compile</goal> </goals> + </execution> + <execution> + <id>java-test-compile</id> + <phase>test-compile</phase> + <goals> <goal>testCompile</goal> </goals> + </execution> + </executions> + </plugin> + </plugins> </build>
これでIntelliJ上でKotlinを書く準備が整いました。
STS(Eclipse)の対応
ここまでの対応で普段STSを使用しているメンバーに確認してもらったところ、うまく動作せずに少しハマったのでSTSでの対応方法も記載します。
src/main/kotlin
がビルドパスとして認識されていないので、Projects > Properties > Java Build Path
のSourceからAdd Fileでkotlinのフォルダをチェック。src/test/kotlin
も同様
- プロジェクトを右クリックし、
Configure Kotlin > Add Kotlin Nature
を実行
下図では、既にAdd Kotlin Nature済みのため選択できなくなっていますが、実行前であれば選択できます。
これでSTS上でもコンパイルが通り、Kotlinでの開発が行えるようになりました。
Kotlinを書く
JavaコードからKotlinコードへコンバート
IntelliJのコンバート機能が優秀なので、基本的にはIntelliJでコンバートしました。 一気にまとめては怖いので、1コードもしくは数コードづつ変換しました。
変換したいコードを選択して、Code > Convert Java File to Kotlin File
でKotlinのコードにコンバートします。
※たまにおかしな変換をするので都度手で修正します。
また、パッケージがsrc/main/java
配下のままであるため、src/main/kotlin
配下の同パッケージ以下に移動します。
これもIntelliJでRefactor > Move...
でいい感じにリファクタリングしてくれます。
DI(Autowiredなど)
たとえば以下のコードは
@Service public class HogeService { @Autowired private HogeRepository hogeRepository; }
Kotlinではlateinit
でも宣言できますが、今回はconstructor injection
で定義するようにしました。
@Service class HogeService( private val hogeRepository: HogeRepository ) { }
Lombokまわり
Lombokを使っていると、Kotlinにコンバートした際にコンパイルエラーが出ることがありました。
基本的には、Kotlinにコンバートするクラス、Kotlinから参照するクラスは、Delombokすることで解消させました。
- Delombok
IntelliJのlombokプラグインを使用します。
Refactor > Delombok
からLombok化を解除してくれます。
- Slf4jアノテーション
loggerの宣言を省略して、いきなりログ出力するコードを書くことができます。 Kotlinにした場合、以下のコードでlog変数が解決できずコンパイルエラーとなってしまいます。
@Service @Slf4j public class HogeService { public void hoge() { log.info("hoge"); } }
kotlinではcompanion object
でロガーを初期化することで利用できるようになります。
@Service class HogeService { companion object { private val log = LoggerFactory.getLogger(HogeService::class.java) } }
アノテーションでのDIの方法などを試されている方もいたので、楽にできるよう検討したいと思います。 http://saiya-moebius.hatenablog.com/entry/2017/11/08/033932
- Dataアノテーション
以下のようなLombok化されたJavaのコードをKotlinから読み、hogeを取得しようとしてもprivateでアクセスでいないと言われてしまいます。 この場合、上に書いたようにDelombok化して対応しました。 (全部KotlinになればそもそもLombok使わなくても・・)
@Entity @Data @EqualsAndHashCode(callSuper = false) @ToString(callSuper = true) public class Hoge extends AbstractEntity { @Column(name = "hoge") private String hoge; }
今後について
現時点で一部のコードはKotlin化して動作するようになりました。 しかし、実際にコードを書いている途中で問題が起こることが何度かあったので、今後も問題が発生する可能性があります。 そこでの対処方などはどんどん追記していければなと思います。
※Kotlin楽しい!!
IntelliJ Ideaでインポートのワイルドカードを無効化する
この記事はQiitaに2016年11月18日に投稿した記事を移行したものです。
はじめに
IntelliJのバージョンを最新の2016に上げたところ、でJavaのimportをIntelliJに行わせようとすると(option
+ return
)、ワイルドカードでインポートされるようになってしまった。
コードレビューで指摘を受けてしまった。
import java.util.*; import java.util.stream.Collectors;
ワイルドカードを使わないようにする方法
以下を参考にしました。 http://stackoverflow.com/questions/3348816/intellij-never-use-wildcard-imports
Preferences
-> Editor
-> Code & Style
-> Java
でJavaコードスタイルの設定ページを開く。
Imports
タブを開くと、以下のような設定画面が表示される。
ワイルドカードを使わないようにするには、以下の3点を設定する。
- Use single class importにチェックを入れる
- Class count to use import with '*': を999などの大きな値にする
- Names count to use static import with '*': を999などの大きな値にする
これでワイルドカードでのインポートがされないようになりました。
SPGアメックスカードを申し込んだ理由と手元に届くまで
先日SPGアメックスのカードを申し込み無事審査に通り手もとに届いたので、これからSPGアメックスのカードを発行しようとしている方の参考になればと思い、申込みしてから手元に届くまでの流れと日数を時系列で紹介していきます。
SPGアメックスとは
アメリカン・エキスプレスが発行する、Marriott Bonvoyとの提携カードです。
主な特徴は
- Marriott Bonvoyゴールドエリートの資格が得られる
- チェックアウト時間を14:00まで延長可能
- 部屋が空いていればアップグレードされる可能性
- ウェルカムギフトポイント
- 対象レストランで15%OFF などなど
- 毎年更新のタイミングでMarriott Bonvoyの無料宿泊券が貰える
- カードを利用している限りはMarriott Bonvoyポイントの有効期限は実質なし
- ポイントを40社もの航空会社のマイルに交換可能
その代わり年会費は高く、税込み 34,100円となっています。
申し込んだ理由
- 以前の記事でも触れましたが、最近は自分でもストレスが溜まっているなということで旅行なんかにいってリフレッシュしたい。
- 今まではあまり旅行に行けていなかったので、毎年もらえる無料宿泊券を使って強制的に旅行に行ってリフレッシュする状態をつくる。
- 高級ホテルに泊まってみたい ←興味。
- 実質ポイント無期限なので、じっくりとマイルを貯められる。
申し込みから手元に届くまで
想像していたより時間がかかりました。
申し込み 3/3(火)
私の場合は普通にWebから申し込みをしました。
紹介してもらった場合などはさらにポイントボーナスもあるそうですが、知り合いにホルダーがいなかったので・・。
申し込みする際には、Marriott Bonvoy会員番号が必要となるため予めMarriott Bonvoyの登録をしておく必要がありました。
早い人だと申し込み完了時点で審査結果が出るそうですが、私の場合は持ち越しでした。
入会特典についてのメール・アメックスから電話 3/4(水)
まずは入会特典についての紹介メールがアメックスより届きました。
また、同日にアメックスから電話が職場にかかってきました。
本当に職場にかかってくることもあるのか・・と思いつつ簡単な質問に答えて終了。
Marriott Bonvoyのステータスがゴールドに 3/7(土)
Marriott Bonvoyのステータスがゴールドに変化していました。
この地点で審査には通過したと思って良さそうです。
Welcomeメール 3/8(日)
アメックスより、Welcomeメールが届き審査に通ったことが通知されました。
本人限定受け取りの連絡 3/11(水)
Welcomeメールの翌日からソワソワしながら毎日ポストを確認していましたが、本人限定受け取りの通知が入っていたのは3/11(水)でした。
楽天カード等とは違い、カードとは別で通知が来るので郵便局に電話をし配達日を翌日にしてもらいました。
※通常の再配達はWebでの申込みが可能ですが、今回は電話もしくはFAXとなっていました。
この際、受け取りなどで身分証明書の提示が必要であることが知らされます。
また、受け取りが円滑に行われるように予め免許証の番号を電話口で伝えました。
受け取り 3/12(木)
受け取る際には、前日電話した際に伝えた免許証番号を参照され本人確認となります。
このあたりは普通のカードと違ってしっかりしているところですね。
申し込んだら忘れずにやること
アメックスは他のカードとは違い、手元にカードが届いてから暗証番号の登録などが必要でした。
- アメックスオンラインの登録
- 暗証番号の登録
- 引き落とし口座の登録
- 利用可能限度額の確認
今後
メインのカードをSPGアメックスに切り替えて色々なところに旅行に行けるよう、コツコツとポイントを貯めていきます。
次回更新でもらえる無料宿泊券への飛行機をマイルでまかなうようにできるよう頑張っていきます。
エンジニアの心を整える技術2を読んだ
先日Webで見かけた「エンジニアの心を整える技術2」という本が気になって読んでみたので、その感想です。
エンジニアの心を整える技術2とは
BOOTHというpixiv社が運用している、クリエイターが作品を自由に販売できるサイトで購入できる書籍です。
本来は技術書典8で販売される予定だったらしいですが、残念ながらコロナウィルスの影響で中止になってしまいました。
そのため、PDF版のみですがBOOTHにて購入できました。
なぜ購入しようと思ったのか
紹介文を読んでいて、以下の部分がまさに当てはまるなぁと思ったからです。
あなたは、いま、このような想いの中に、いるのかもしれません。 * プロジェクトの終わりの見えない忙しさ * 残業続きで抜けない疲れにぐったり * 最新技術やプログラミング言語習得の流行り廃りの虚しさ * 職場でのやり場のない怒りや悲しみ * 未来に対する漠然とした不安
自身に当てはめると以下のような感じでした。
少しでも状況や心の持ちようを改善できないかと思い、思わず手が出ました。
- 業務負荷が上がってきており、週末では疲れが取れない
- そこそこユーザーの増えてきたSaaSのサービスを主担当のエンジニアとしては自分1人だけなので心理的につらい
- ロールが少し変わったため、自分の生産性を発揮できてはいない
- 設計やコードに携わることが減ってきており、最新技術についていけなくなるおのではという不安
読んでみた感想
SHIROBAKOの名言が所々散りばめられていて良い
SHIROBAKOとは、アニメ制作現場をアニメ化したもので、2020年2月29日に劇場版も公開されています。
色々な部分で、「あるある」と共感できる内容になっており心に染みる名言も多い作品になっています。
もちろん劇場版も観ました!
まえがきの段階から、確信を突かれるようなSHIROBAKOのセリフが載っており一気に引き込まれました。
Slackとの付き合い方を見直すきっかけになった
3.6.4 Slack をミュート・退出せよ。心に「ゆとり」を取り戻せ
上記の章には、「はっ」とさせられました。
Slackは便利な反面、様々な情報が飛び交いメンションがついたものは「すぐに見なければ」という意識になってしまっていたなと思います。
この章を読んで、Slackとの付き合い方を少し見直したいなと考えさせられました。
自己肯定感について考えるきっかけになった
第4章 自己肯定感を整える技術
この章については丸々、グサりとくる内容でした。
とくに自己否定に関する部分は自分に当てはまる部分が多く、心の持ちようを変えたいと考えさせられます。
全体を通して
ページ数はそれほど多くないので、小一時間で読める内容になっていました。
そのため、一気に最後まで読み進められ、ビジネス書などにあるような寄り道もほぼないため、スッと内容が頭に入ってきました。
一部は実践的な内容も書かれていたので、試してみたいなと思います。
BOOTHにて購入できるので、気になった人は読んでみてください。
ETORENでGalaxy Note10+(SM-N9750)を購入したので注文から届くまで
今回、ETORENという海外のサイトで、Galaxy Note10+を購入しました。
届くまで意外に時間がかかったので他の方の参考になるかと思い、購入した背景と注文してから届くまでを書いていきます。
なぜGalaxy Note10+ を購入したのか
今まではPixel3を購入していました。
写真は綺麗に撮れるし、Android OSを開発しているGoogleのフラグシップスマホということで、アップデートも早いので基本的には満足していました。
では何が買い替えの決め手になったかというと、以下の点になります。
バッテリーが致命的に持たない
それほどハードに使っているわけでもなく、Pixel3でゲームをするわけでもないのですが、充電は1日に朝出社してからと夜帰宅してからの2回必要でした。
寝る前に充電して、少し使ってから朝起きたら80%くらいになっていることもしばしば・・
たまにメモリが不足する
2018年のフラグシップとしては少ないメモリの4GBがPixel3に載っています。
しかし、利用するアプリによってはもっさりすることもありメモリが足りてないなーと感じることがしばしばありました。
SoCはSnapdragon 845が載っているので、メモリが足りている場合はサクサク動いてくれました。、
画面が小さい
軽くて持ち運びがしやすい反面、画面の小ささが気になるようになりました。
ここは好みの問題ですが、次は画面サイズの大きいものを買おうと前々から考えていました。
Galaxy Note10+について
上記の問題を全て解決してくれる上で、候補はある程度に絞れました。
- Pixel4 XL
- Galaxy Note 10+
- Galaxy S20+ (Note10+ 購入時点では未発表)
- iPhone 11 Pro Max
などです。
ただ、花粉症シーズンなどマスクを使う場合には顔認証は使い物にならないので、基本的には指紋認証に対応している機種でということになりました。
Galaxy S20シリーズの発表まで待つべきかとも考えましたが、リーク情報を見ている限りではNote 10+でもよさそうという結論に至り、Note 10+に決定しました。
2020年3月に5Gがスタートするということで、気がかりではありましたが5G対応のiPhoneが発売するまではあまり普及しないだろうと考え、今回の購入に至りました。
ETORENについて
海外のスマートホンなどを扱うオンラインショップになります。
Expansysと並んで有名ではないでしょうか。
ETORENの特徴は、輸入時にかかる関税がインクルードされていることです。
そのため、サイト表記の価格 + 送料が実際に支払う総額になります。
また、日々価格が変動するのも海外サイトっぽいところです。
私が購入したときは、93,500円 + 送料でした。
購入から届くまで
ETORENは基本的にはページが日本語化されているため、通常のネットショッピングと同じ感覚で購入できると思います。
支払い方法はクレジットカードやPayPalなどが選べました。
また、住所は英語で入力するのですが、以下のサイトなどを使えば困ることはないです。
実際のオーダーになります。
日本のキャリアモデルより3万円くらい安いです。
※技適なし / Felicaも搭載されていないためご購入を検討される場合は自己責任でお願いします。
実際に注文をしたのが2/2(日)で、発送されたのが2/6(木)でした。
発送予定日は 2/3(月) ~ 2/5(水)となっていたので、2/5(水)の夜になっても発送されず不安になったので問い合わせしたところ、2/6(木)に返信があり、当日中に発送されるということで実際にちゃんと発送されました。
このあたりは日本のネットショッピングに慣れているとソワソワする部分です。
実際に発送されてからの流れですが、DHLに引き渡されて、シンガポールから東京まで1晩のうちに届いていました。
DHLから佐川急便に引き渡されて、2/8(土)に無事到着しました。
日本の物流もすごいと思いますが、国際物流も洗練されているなと感じました。
こちらがDHLでの履歴になります。
到着したので開封
早速開封していきます。
ちゃんとしたダンボール箱で届きました。
中もプチプチでしっかりと梱包されています。
箱にはペンの絵が書かれており、非常にシンプルです。
箱を開けたところ。
蓋側には、SIMピンと付属の透明ケース。
本体の下には説明書
さらにイヤホン、ACアダプター、USB-Type Cケーブルなどが入っています。
本体は透明なシールで包まれていました。
オーラグローの背面は、見る角度で表情を変える、非常に美しい仕上がり。
最後に
スマホの買い替え後は移行が大変でした。。
まだ少ししか触れていませんが、上の方で上げたPixel3の不満は全て解消してくれる素晴らしい端末だと思います。
これから、初のSペンなど色々試していきたいと思います。