かんがるーさんの日記

最近自分が興味をもったものを調べた時の手順等を書いています。今は Spring Boot をいじっています。

Spring Boot 2.2.x の Web アプリを 2.3.x へバージョンアップする ( その16 )( ValuesHelper クラスで guava の ClassPath を使用しないよう変更する )

概要

記事一覧はこちらです。

Spring Boot 2.2.x の Web アプリを 2.3.x へバージョンアップする ( その15 )( Doma 2 を 2.26.0 → 2.44.3 へバージョンアップする+domaGen タスクを doma-codegen-plugin を利用したものに作り直す ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • ksbysample.webapp.lending.values.ValuesHelper クラスのコンストラクタで values パッケージの下のクラス一覧を取得してフィールドの Map オブジェクトに保存しており、現在 guava の ClassPath クラスを利用しています。
    • Spring のクラスでも実現できるのではないかと思い以前から変更方法を調べていたのですが、その方法を見つけたので変更します。Web アプリの中で com.google.common.collect.Multiset を使用している箇所があるので guava を依存関係から削除することはできませんが、values パッケージを利用するだけなら guava を依存関係に追加する必要がなくります。

参照したサイト・書籍

目次

  1. ksbysample.webapp.lending.values.ValuesHelper クラスで guava の ClassPath を使用しないよう変更する

手順

ksbysample.webapp.lending.values.ValuesHelper クラスで guava の ClassPath を使用しないよう変更する

現在 guava の ClassPath を使用して以下のように実装していますが、

import com.google.common.reflect.ClassPath;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * ???
 */
@Component("vh")
public final class ValuesHelper {

    private final Map<String, String> valuesObjList;

    private ValuesHelper(@Value("${valueshelper.classpath.prefix:}") String classpathPrefix) throws IOException {
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        valuesObjList = ClassPath.from(loader)
                .getTopLevelClassesRecursive(classpathPrefix + this.getClass().getPackage().getName())
                .stream()
                .filter(classInfo -> {
                    try {
                        Class<?> clazz = Class.forName(classInfo.getName().replace(classpathPrefix, ""));
                        return !clazz.equals(Values.class) && Values.class.isAssignableFrom(clazz);
                    } catch (ClassNotFoundException e) {
                        throw new RuntimeException(e);
                    }
                })
                .collect(Collectors.toMap(classInfo -> classInfo.getSimpleName()
                        , classInfo -> classInfo.getName().replace(classpathPrefix, "")));
    }

    ..........

Can you find all classes in a package using reflection? の 104 の Spring のコードを参考に以下のように変更します。@ComponentScan アノテーションがあるので Spring のクラスで実現できるはずと思っていましたが、ClassPathScanningCandidateComponentProvider クラスを使えばできることが分かりました。

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.type.filter.RegexPatternTypeFilter;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * ???
 */
@Component("vh")
public final class ValuesHelper {

    private final Map<String, String> valuesObjList;

    private ValuesHelper() {
        ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
        provider.addIncludeFilter(new RegexPatternTypeFilter(Pattern.compile(".*")));
        Set<BeanDefinition> beans = provider.findCandidateComponents(this.getClass().getPackage().getName());
        valuesObjList = beans.stream()
                .map(bean -> {
                    try {
                        return Class.forName(bean.getBeanClassName());
                    } catch (ClassNotFoundException e) {
                        throw new RuntimeException(e);
                    }
                })
                .filter(clazz -> !clazz.equals(Values.class) && Values.class.isAssignableFrom(clazz))
                .collect(Collectors.toMap(clazz -> clazz.getSimpleName(), clazz -> clazz.getName()));
    }

    ..........

コンストラクタの引数に渡していた @Value("${valueshelper.classpath.prefix:}") String classpathPrefix を削除したので、以下の設定ファイルの項目を削除します。

  • src/main/resources/application.properties の valueshelper.classpath.prefix=
  • src/main/resources/application-product.properties の valueshelper.classpath.prefix=BOOT-INF.classes.

clean タスク実行 → Rebuild Project 実行 → build タスクを実行すると無事 "BUILD SUCCESSFUL" のメッセージが出力されました。

f:id:ksby:20201226195331p:plain

IntelliJ IDEA から Web アプリを起動しても Docker から Web アプリを起動しても動作には問題ありませんでした。

履歴

2020/12/26
初版発行。