Spring (Boot) で singleton service を使ってDBからデータを取得する
・enum にDI
・メモリの節約
・キャッシュ機能で高負荷に対応
・読み取りのみ
・ランキングなどで利用してます
・Spring Framework, Spring Boot 両対応
・マッパーの方は適宜読み替えて設定してください
・コンテキスト更新時に初期化
※参考:https://www.baeldung.com/spring-context-events
環境
・Spring Framework 4.2.9
・Spring Boot 2.2.1
・Java 1.8
・Windows 10 64bit
SampleRankingSingletonService
・データを取得し保持するシングルトンサービス (enum)
・mapper を @Autowired で DI したいけどできないので初期化用のサービスを別に作る
・データの加工はプログラムを追加して対応すると良いと思います。(SQLの発行は高負荷)
public enum SampleRankingSingletonService {
INSTANCE;
// 読み込みタイムアウト値調整
public static final int TIMEOUT_SEC = 1800;
private static final Object lock = new Object();
private LocalDateTime queryDate;
// 最新の ランキング
private List<Sample> latestRanking = new CopyOnWriteArrayList<>();
private SampleMapper sampleMapper;
private SampleRankingSingletonService () {
}
public static SampleRankingSingletonService getInstance() {
return INSTANCE;
}
private boolean isTimeout() {
LocalDateTime execDate = LocalDateTime.now();
if (queryDate == null) { return true; }
LocalDateTime expireDate = queryDate.plusSeconds(TIMEOUT_SEC);
if (expireDate.isBefore(execDate)) {
return true;
}
return false;
}
public synchronized void reload() {
queryDate = LocalDateTime.now();
latestRanking = sampleMapper.selectLatestRanking(); // <- ランキング取得SQL発行
}
// データ処理を実行する前に呼び出す
public void initQuery() {
synchronized(lock) {
if (isTimeout() || latestRanking.size() <= 0) {
reload();
}
}
}
public Integer countLatestRanking() {
return latestRanking.size();
}
}
SampleRankingSingletonInitializerService
・DIにより自動生成
@Component
public class SampleRankingSingletonInitializerService {
@Autowired
private SampleMapper sampleMapper;
// Bean生成時にシングルトンサービスにDIしたマッパーをセットする
@PostConstruct
public void init() {
SampleRankingSingletonService.INSTANCE.setSampleMapper(sampleMapper);
}
// コンテキスト更新時に初回クエリ実行 (PostConstruct で実行すると Spring Boot ではエラーになる)
@EventListener
public void onContextRefreshedEvent(ContextRefreshedEvent evt) {
SampleRankingSingletonService.INSTANCE.initQuery();
}
public SampleMapper getSampleMapper() {
return sampleMapper;
}
public void setSampleMapper(SampleMapper sampleMapper) {
this.sampleMapper = sampleMapper;
}
}
DI 用の xml ファイル
<bean id="sample.sampleRankingSingletonInitializerService" class="org.sample.SampleRankingSingletonInitializerService">
<property name="sampleMapper" ref="sample.SampleMapper" />
</bean>
ブレイクポイント設定してサーバー起動後ちゃんと止まり、データが取れていればOKです。
以上、お疲れさまでした!