Spring Boot 2.2.x の Web アプリを 2.3.x へバージョンアップする ( その16 )( ValuesHelper クラスで guava の ClassPath を使用しないよう変更する )
概要
記事一覧はこちらです。
- 今回の手順で確認できるのは以下の内容です。
- ksbysample.webapp.lending.values.ValuesHelper クラスのコンストラクタで values パッケージの下のクラス一覧を取得してフィールドの Map オブジェクトに保存しており、現在 guava の ClassPath クラスを利用しています。
- Spring のクラスでも実現できるのではないかと思い以前から変更方法を調べていたのですが、その方法を見つけたので変更します。Web アプリの中で com.google.common.collect.Multiset を使用している箇所があるので guava を依存関係から削除することはできませんが、values パッケージを利用するだけなら guava を依存関係に追加する必要がなくります。
参照したサイト・書籍
- Can you find all classes in a package using reflection?
https://stackoverflow.com/questions/520328/can-you-find-all-classes-in-a-package-using-reflection
目次
手順
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" のメッセージが出力されました。
IntelliJ IDEA から Web アプリを起動しても Docker から Web アプリを起動しても動作には問題ありませんでした。
履歴
2020/12/26
初版発行。