SpringBootでapplication.yaml以外の外部リソースファイルを読み込む

エキサイト株式会社エンジニアの佐々木です。アプリケーションを開発していると設定が増えてくると思いますが、増えてくるとファイルが増えて大変です。また、モジュラーモノリスのような構成にすると設定を別々に管理した方が都合がいいことがでてきます。SpringBootで試してみます。

前提

SpringBoot2.4以上で使用ができます。

設定ファイルで指定する方法

下記のような database.yaml のファイルがあるとします。

database:
  host: localhost
  port: 33067
  schema: hogehogea

application.yamlファイルではないので、SpringBoot標準では読み込みません。application.yamlに次の設定を追記します。

spring:
  config:
    import: classpath:/database.yaml

上記の設定で設定を分けられます。

クラスに書く方法

設定ファイル

デフォルトのままapplication.yamlをそのまま読み込めなかったと思うので、property(.ini)ファイルにします。(factoryクラスを書けば読み込み可能です)

database.host = localhost
database.port = 3306
database.schema = hogehoge

クラスファイル

クラスファイルは下記のようになります。

@Data
@Configuration
@ConfigurationProperties(prefix = "database")
@PropertySource("classpath:database.properties")
public static class DatabaseConfig {
    private String host;
    private Integer port;
    private String schema;

    @Bean
    public DatabaseConfig databaseConfig(){
        return new DatabaseConfig();
    }
}

@PropertySourceにファイル名を書くと読み込んでくれます。アノテーションがたくさんついていますが、下記にまとめます。

@Data : lombokというライブラリを使って、Getter/Setterのメソッド等を自動的に生成してくれます
@Configuration: 設定ファイルクラスであることを明示します
@ConfigurationProperties: prefixで指定した文字列の配下とクラスのフィールド名が一致しているものにマッピングします
@PropertySource: 設定ファイルの置き場所を指定します
@Bean: DIコンテナに登録します

記述量は多くなりますね。ちなみに、@PropertySourceがデフォルトでYAMLに対応するというのは、GithubのIssueで拒否されています。(spring.config.importがでてきたから?)

まとめ

SpringBoot2.4以降は、spring.config.importを使うのが良さそうです。ニッチなネタですが、意外とソースが古かったりニーズにはずれていたりします。参考になれば幸いです。

最後に

エキサイトではフロントエンジニア、バックエンドエンジニア、アプリエンジニアを随時募集しております。長期インターンも歓迎していますので、興味があれば連絡いただければと思います。

カジュアル面談はこちらになります! meety.net

募集職種一覧はこちらになります!(カジュアルからもOK) www.wantedly.com

「第一回テクデザ総会」を開催しました!

こんにちは。 エキサイト株式会社の三浦です。

2021年10月8日、第一回目となる「テクデザ総会」を開催しました!

f:id:excite-takayuki-miura:20211011110649p:plain
第一回テクデザ総会

テクデザ総会

テクデザ総会は、コミュニケーションの活性化を主目的としたエキサイト・iXITの技術者の総会となります。 総会とは書きましたが格式張ったものではなく、今回のZoomでの開催中もたくさんのコメントを頂きながらの進行になりました。

総会は、

  • 各事業部のトピックスの発表
  • Zoomのブレイクアウトルーム機能を使った交流
  • 投票を募って決定した多数の大賞の授与

をメインのコンテンツとしています。

各事業部のトピックスの発表

エキサイトやiXITの各事業部の技術部長などの協力を得て、各事業部の上期のトピックスを発表してもらいました。 なかなか自分が所属している事業部以外でやっていることは把握しにくいところがあるので、この発表で他事業部のことを知ってもらい、交流のきっかけにしてもらいたいという意図があります。

Zoomのブレイクアウトルーム機能を使った交流

Zoomにはブレイクアウトルームという、参加者を任意の人数に分けて別々のルームに入れるという機能があります。 その機能を使い、約3~4名ほどの人数に参加者を分けて、それぞれのルームで話し合ってもらいました。

この交流は2回行いました。 1回目は自己紹介や雑談を5分ほど行ってもらって交流を深めてもらい、2回目はエキサイトやiXITに欲しい文化・ルール・制度等について10分ほど話し合ってもらいました。

今まで交流がなかった人と交流してもらうことで、技術組織全体のコミュニケーションを活性化することを目的としていましたが、実際に今まで話す機会がなかった人と話すことができたと非常に好評で、次回も是非取り入れたいところです。

投票を募って決定した多数の大賞の授与

事前にGoogleアンケートで投票を募り、様々な項目に関する大賞の受賞者を決定して、総会にて授与しました。 大賞は合計でなんと21個、投票の結果同数だった人を含め計23名への授与となりました。

大賞としては正直かなり数が多いですが、お祭り感を出してコミュニケーションを活性化させるという目的は達成できたと考えています。

全体として、非常にうまく行った総会だったと思います。 あとはこれが、今後の技術組織全体のコミュニケーションのさらなる活性化へと繋がっていけば言うことなしです。

最後に

組織のコミュニケーションの活性化は、一朝一夕でうまくいくものではありません。 今回のような総会を継続的に行いつつ、他にも様々な施策を行い、活性度を上げていきたいと思います。

axios通信時にNuxt.jsのローディングアニメーションを実装する

はじめに

こんにちは!SaaS事業部エンジニアの小川です。

普段はSaaS事業部でKUROTEN.という経営管理SaaSの開発をしています。

今回は、Nuxt.jsでaxios通信時にローディングアニメーションを表示する方法を紹介します。

NuxtのLoading設定(Nuxt標準)

基本的にNuxt.jsでローディングアニメーションを実装する場合、nuxt.config.jsのloadingオプションをtrueにする方法がよく使われていると思います。 ここでいうローディングは、ページ遷移の間の読み込み時に動作します。

# nuxt.config.js
export default {
    loading: true
}

また、こちらのローディングオプションにはオブジェクトを渡してあげることで、ある程度細かく見た目を変えることもできます。(以下では一例を紹介)

# nuxt.config.js
export default {
    loading: {
        progress-bar-height: 2px, // プログレスバーの高さ
        rtl: false // プログレスバーの向きを指定
    }
}

さらに見た目をカスタマイズしたい場合は以下のようにコンポーネントを用意し、 NuxtのLoadingオプションにコンポーネントを設定することも可能です。

// nuxt.config.js
export default {
    Loading: './commponents/Loading'
}

axios通信時にLoadingを動かす

ここからが本題です。 Nuxt.jsでaxios通信時にローディング処理を行いたい場合、以下の2つの手法があります。

this.loading.startとthis.loading.finishで通信中を取得

公式で紹介されている方法で、 axios通信している箇所にstartfinishの処理を追加するだけで実装できるのでお手軽です。 あとは先ほど紹介したNuxtのLoadingオプションをtrueにすれば従来どおりの方法でローディングが動作します。

// ローディング処理を入れたい箇所
this.loading.start ()
hoge () // axiosの処理
this.loading.finish ()

storeとpluginのaxiosでローディングの状態を管理する

上で述べた手法でもaxios通信時にローディング処理を走らせることができますが、 呼び出し処理を追加するたびにstartとfinishの記述が必要になってしまうため 通信処理が複数あると開発者の負担が大きいように思います。

そこで、storeでローディングの状態管理をすることで、axios通信時の処理を一括して記述する方法をご紹介します。

まずはloadingの状態を管理するためのstoreを追加します。

// ./store/loading.js

export const state = () => ({
  loadingCount: 0
})

export const getters = {
  loadingCount: (state) => state.loadingCount,
  isLoading: (state) => state.loadingCount > 0
}

export const mutations = {
  incrementLoadingCount (state) {
    ++state.loadingCount
  },
  decrementLoadingCount (state) {
    if (state.loadingCount > 0) {
      --state.loadingCount
    }
  }
}

export const actions = {
  incrementLoadingCount ({ commit }) {
    return commit('incrementLoadingCount')
  },
  decrementLoadingCount ({ commit }) {
    return commit('decrementLoadingCount')
  }
}

次にaxiosを用いた通信中にLoadingCountが1以上になるようpluginsのaxios.jsに先ほど作成したアクションを追加します。 このpluginsはaxiosのリクエストやレスポンスをフックにして呼び出されるので、共通化したい場合にとても便利です。

// ./plugins/axios.js

export default function({ $axios, store }) {
    $axios.onRequest(config => {
        store.dispatch('loading/incrementLoadingCount')
    })

    $axios.onResponse(() => {
        store.dispatch('loading/decrementLoadingCount')
    })
    
    $axios.onError(e => {
        store.dispatch('loading/decrementLoadingCount')
    })
}

あとは上述で作成したLoadingCountをトリガーにして読み込みのオーバーレイやアニメーションを表示すれば完成です。 以下は通信中にVuetifyのプログレスバーを動かすサンプルです。LoadingCountをトリガーに使うだけなので、自由度は非常に高いです。

// ./components/Loading.vue
<template>
  <v-progress-linear
    :active="isLoading"
    :indeterminate="isLoading"
    :height="loadingHeight"
    absolute
    bottom
    :color="loadingColor" />
</template>

<script>
  import { mapGetters } from 'vuex'

  export default {
    name: 'Loading',
    data () {
      return {
        loadingHeight: '5',
        loadingColor: '#4DB6AC'
      }
    },
    computed: {
      ...mapGetters({
        isLoading: 'loading/isLoading'
      })
    }
  }
</script>

先程実装したLoadingコンポーネントをHeaderコンポーネントで呼び出し、layoutsのdefaultへ渡すことで、全ページにローディング処理の実装ができます。

// ./components/Header.vue

<template>
  <v-app-bar
    color="primary"
    class="app-bar"
    :height="height"
    fixed
    app>
    <loading />
  </v-app-bar>
</template>

<script>
  import Loading from '@/components/Loading'

  export default {
    name: 'Header',
    components: {
      Loading
    },
    data () {
      return {
        height: '50'
      }
    }
  }
</script>
// ./layouts/default.vue

<template>
    <div>
        <Header />
    </div>
</template>

<script>
import Header from '@/components/Header'

export default {
    components: {
        Header
    }
}
</script>

これでaxios通信が行われている最中にLoadingアニメーションが実行されます。

f:id:honyafan:20211008104441g:plain
axios通信時のLoadingの様子

最後に

axios通信時の手法の肝はLoadingCountという状態を使うことなので、簡単に応用が効くと思います。 axios通信時にローディング処理を入れたいという方は一度試してみてはいかがでしょうか!

SQLServerのWHERE句は大文字小文字でもヒットしてしまう問題

エキサイトしばたにえんです。 早速ですが、

WITH hogefuga_table AS (
    SELECT 'hogefuga' AS word
)
SELECT * FROM hogefuga_table 
WHERE word = 'HOGEFUGA';

こちらhogefugaがヒットされてしまいます。 WHERE句の大文字小文字を判別してくれません。

SQLServerのWHERE句で大文字小文字を判別するためにcollateが使えます。

WITH hogefuga_table AS (
    SELECT 'hogefuga' AS word
)
SELECT * FROM hogefuga_table 
WHERE word = 'HOGEFUGA' COLLATE Japanese_CS_AS_KS_WS;

とすればWHERE句の大文字小文字を判別してくれるようになり、ヒットされることはありません。

COLLATEには他にも種類があります。 例えば、「ぽ」、「ぼ」、「ボ」、「ポ」の判別を「したくない」場合

WITH hogefuga_table AS (
    SELECT 'ボボぼーボボーボぽ' AS word
)
SELECT * FROM hogefuga_table 
WHERE word = 'ぽぼポーぽぽーぽボ' COLLATE Japanese_CI_AI;

とするとボボぼーボボーボぽヒットしてくれるようになります。 他にもCOLLATEには種類があるので必要に応じて使い分けるといいかもしれません。

git branchの結果を時間順に表示

エキサイトのしばたにえんです。
時間順にソートするgit branchのoptionの紹介です
「さっき作ったブランチが見つからない」、
「ターミナルのタブも消してしまってhistoryでも見つからない」
って時に便利です。

新しい順の表示
git branch --sort=-authordate
古い順の表示
git branch --sort=authordate

新しい順の表示なんかは何かと便利だと思いますので使ってみてください。
git branch --helpで他のオプションも確認できるので調べてみてもいいかもしれません。

LocalDateTime型をrequestから受け取るカスタムアノテーションを作る

エキサイトのしばたにえんです。 早速ですが カスタムアノテーションの作成をしていきます。 リクエストからLocalDateTimeを受け取る時に@JsonFormatを使って受け取ると思いますが、この時にpatternを書く必要があります。

@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
private LocalDateTime date;

こんな感じです

pattern = "yyyy-MM-dd'T'HH:mm:ss" 毎回これを書いていくのはめんどうなのと patternが間違ったりでミスをする可能性が出てきます。

そんな時にはカスタムアノテーションを作ると便利かもしれません。 簡単です。

@JacksonAnnotationsInside
@Retention(RetentionPolicy.RUNTIME)
@DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
public @interface LocalDateTimeRequest {
}

としてアノテーションを登録して、

@LocalDateTimeRequest
private LocaldateTime date;

とすれば、毎回patternを書く必要がなくなります。 便利なので是非使ってみてください

SpringBootで設定ファイル(application.yaml)を一括で読み込む

エキサイト株式会社エンジニアの佐々木です。SpringBootでの設定ファイル(application.yaml)を一括で読む方法のメモになります。

前提

SpringBoot2.4以上で検証しています。

起動クラスの設定

ソースコードは下記になります。

起動クラスに@ConfigurationPropertiesScanを付与します。これで、@ConfigurationPropertiesアノテーションを読みにいきます。

@SpringBootApplication
@ConfigurationPropertiesScan  // ConfigurationPropertiesを読みにいくおまじない
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

単一項目な設定ファイル

設定ファイルの項目名と変数名が一致していればバインドされます。

設定ファイル(appliation.yaml)

設定ファイルは下記になります。

env:
  host: local
  port: 8080

読み取りクラス

読み取りクラスは下記のように定義します。

@Getter
@RequiredArgsConstructor
@ConstructorBinding          // この設定がないと Setterが必要になるのでつけておく
@ConfigurationProperties(prefix = "env")   // prefixで指定された部分のyamlを読みいく
public class Config {
    private final String host;
    private final Integer port;
}

@ConfigurationPropertiesアノテーションapplication.yamlのどこを読むかを設定します。@ConstructorBindingアノテーションで、setterがなくても値がセットされるようにします(つけておかないとSetterメソッドが必要になります)。@Getter@RequiredArgsConstructorLombokアノテーションです。Getterの生成とコンストラクタの定義を省略できます。yamlの中のプロパティ名とクラスのプロパティ名を合わせると、バインドしてくれます。型は気にしなくても、StringやIntegerくらいなら対応してくれます。設定されたものは、DIをすれば、使えます。

出力

{
"host": "local",
"port": 8080
}

Key-Value型の読み取り

Key-Value形式の設定は、Map型を使いKey-Valueをオブジェクトにバインドします。

設定ファイル

regionの中にjausのようなkey-valueのファイルがあるとします。

env:
  host: local
  port: 8080
  region:
    ja: 日本
    us: アメリカ

読み取りクラス

読み取りでは、Map型を使いJavaオブジェクトに変換していきます。

@Getter
@RequiredArgsConstructor
@ConstructorBinding
@ConfigurationProperties(prefix = "env")
public class Config {
    private final String host;
    private final Integer port;
    private final Map<String,String> region; // Map型で定義(ジェネリクスは相応の型を指定)
}

上記で、region変数にKey-Value型のデータがバインドされます。

出力

{
"host": "local",
"port": 8080,
"region": {
    "ja": "日本",
    "us": "アメリカ"
    }
}

プロパティがネストされたapplication.yaml

ネストされた設定ファイルもバインド可能です。項目名と変数名が一致していれば自動的にバインドしてくれます。

設定ファイル

ネストされた設定ファイルです。

env:
  host: local
  port: 8080
  people:
    - name: hogehoge
      age: 18
    - name: fugafuga
      age: 30

読み取りクラス

階層がある場合は、staticな内部クラスを書くか、外部クラスに定義して型を指定するかになります。変数名が一致すればクラスに合わせて読み込みまれます。

@Getter
@RequiredArgsConstructor
@ConstructorBinding
@ConfigurationProperties(prefix = "env")
public static class Config {
    private final String host;
    private final Integer port;
    private final List<People> people;

    @Getter
    @RequiredArgsConstructor
    static class People {
        private String name;
        private Integer age;
    }
}

staticな内部クラスでも@Getter、@RequiredArgsConstructorアノテーションは必要になります。

出力

{
  "host": "local",
  "port": 8080,
  "region": {
    "ja": "日本",
    "us": "アメリカ"
  },
  "people": [
    {
      "name": "hogehoge",
      "age": 18
    },
    {
      "name": "fugafuga",
      "age": 30
    }
  ]
}

まとめ

@Valueで1つずつ読み込む方法もありますが、記述が多くなるので、クラス定義だけでできる方法を記載します。Yamlとの脳内変換が多くなってくるので、ネストの深さがでてくると、冗長でも@Valueの方が楽な場合もあるかもしれません。適材適所でお使いください。

最後に

エキサイト株式会社では、新卒・中途(大卒・高専卒・高卒問わず)バックエンドエンジニア・アプリエンジニア・フロントエンジニア・クリエイティブを募集しております。興味がありましたらご連絡をお待ちしております。

カジュアル面談はこちらになります! meety.net

募集職種一覧はこちらになります!(カジュアルからもOK) www.wantedly.com