覚えたら書く

IT関係のデベロッパとして日々覚えたことを書き残したいです。twitter: @yyoshikaw

アプリ実行中に条件に合ったクラスを抽出する

前回のエントリでFastClasspathScannerを利用して特定のアノテーションが付与されたクラスを抽出するサンプルを実行しました。


FastClasspathScannerでは、他にも色々な条件でクラス等を抽出できます。いくつかサンプルを試してみます


インターフェースクラスを全部取得する

あるパッケージ以下に存在する全てのインターフェースクラスを抽出する場合、以下のAPIを利用します

  • FastClasspathScanner#matchAllInterfaceClasses


■サンプルコード

sample.app.domainというパッケージ以下に存在する全インターフェースクラスを取得する例です

import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner;

new FastClasspathScanner("sample.app.domain")
        .matchAllInterfaceClasses(
                clazz -> System.out.println("Found class: " + clazz.getName()))
        .scan();

■実行結果

Found class: sample.app.domain.Interface1
Found class: sample.app.domain.Interface1$InnerInterface
Found class: sample.app.domain.Interface2
Found class: sample.app.domain.SampleInterface


特定のインターフェースを実装したクラスを取得する

特定のインターフェースを実装したクラスを取得する場合、以下のAPIを利用します

  • FastClasspathScanner#matchClassesImplementing


■サンプルコード

sample.appというパッケージ以下に存在するクラスの中でSampleInterfaceというインターフェースを実装したものを取得する例です

import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner;

new FastClasspathScanner("sample.app")
        .matchClassesImplementing(SampleInterface.class,
                clazz -> System.out.println("Found class: " + clazz.getName()))
        .scan();

■実行結果

Found class: sample.app.domain.Impl1
Found class: sample.app.domain.Impl3


特定のインターフェースを継承したサブインターフェースを取得する

特定のインターフェースを継承したインターフェースクラスを取得する場合、以下のAPIを利用します

  • FastClasspathScanner#matchSubinterfacesOf


■サンプルコード

sample.appというパッケージ以下に存在するインターフェースの中でSampleInterfaceというインターフェースを継承したものを取得する例です

import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner;

new FastClasspathScanner("sample.app")
        .matchSubinterfacesOf(SampleInterface.class,
                clazz -> System.out.println("Found class: " + clazz.getName()))
        .scan();

■実行結果

Found class: sample.app.domain.Interface1


特定のクラスを継承したサブクラスを取得する

特定のクラスを継承したサブクラスを取得する場合、以下のAPIを利用します

  • FastClasspathScanner#matchSubclassesOf


■サンプルコード

sample.appというパッケージ以下に存在するクラスの中でAbstractSampleというクラスを継承したものを取得する例です

import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner;

new FastClasspathScanner("sample.app")
        .matchSubclassesOf(AbstractSample.class,
                clazz -> System.out.println("Found class: " + clazz.getName()))
        .scan();

■実行結果

Found class: sample.app.domain.SubClass
Found class: sample.app.domain.SubClass$InnerSubClass


メソッドに特定のアノテーションが付与されたものを取得する

メソッドに特定のアノテーションが付与されているクラスとそのメソッドを取得する場合以下のAPIを利用します

  • FastClasspathScanner#matchClassesWithMethodAnnotation


■サンプルコード

sample.app.domainというパッケージ以下に存在するクラスの中で@MethodAnnotationというアノテーションが付与されているメソッドを持つクラスとそのメソッドを取得する例です

import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner;

new FastClasspathScanner("sample.app.domain")
        .matchClassesWithMethodAnnotation(MethodAnnotation.class,
                (clazz, method) -> System.out.println("Found : " + clazz.getName() + "#" + method.getName()))
        .scan();

■実行結果

Found : sample.app.domain.SimpleClass#printHello
Found : sample.app.domain.SubClass#execute1
Found : sample.app.domain.SubClass#execute2



関連エントリ

特定のアノテーションが付与されたクラスを実行時に抽出したい

Javaアプリケーションの処理内で、特定のパッケージ配下にあるクラスの中で、指定の条件を満たすものを抽出したい場合があります。

例えば、アプリ実行中に特定のアノテーションが付与されたクラスを探したい 等がこれに該当します。

このような場合、FastClasspathScannerを利用すると簡単に実現できます。

FastClasspathScannerを用いると、classpathをスキャンして指定の条件に合うクラスを抽出することができます。


(追記: FastClasspathScannerClassGraph に名称が変更された? ようです)


準備

pom.xmlに以下を追記します。

<dependency>
    <groupId>io.github.lukehutch</groupId>
    <artifactId>fast-classpath-scanner</artifactId>
    <version>{lib-version}</version>
</dependency>

今回のサンプルではバージョンとして 2.0.13 を指定しました


サンプル

@SampleAnnotation というアノテーションを用意して、sample.appパッケージ以下にあるクラスの中でそのアノテーションが付与されているクラスを取得します。


■サンプルコード

クラスに付与するアノテーション(@SampleAnnotation)は以下です。(何の変哲もないアノテーションです)

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface SampleAnnotation {
}


FastClasspathScannerを用いて@SampleAnnotationが付与されたクラスを探す処理は以下のように記述できます。

import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner;


public class SampleMain {

    public static void main(String[] args) {
        new FastClasspathScanner("sample.app")
                .matchClassesWithAnnotation(SampleAnnotation.class,
                        clazz -> System.out.println("Found class : " + clazz.getName()))
                .scan();
    }
}

FastClasspathScanner#matchClassesWithAnnotation で 対象のアノテーションを指定しています


■実行結果

Found class : sample.app.domain.Logic1
Found class : sample.app.util.Util1

(条件を明確に記載していないので分かりにくいのですが)@SampleAnnotation が 付与されたクラスだけが抽出されました


補足

今回は、特定のアノテーションが付与されたクラスを探すサンプルを実行しましたが、FastClasspathScannerでは他にも色々な条件でクラス等を探すことができます。



関連エントリ

Guice - Listに名前を付けて@NamedでInjectionしたい

Guiceを使っている場合に@NamedでListに名前を付けて依存関係を解決するサンプルです。

今回のサンプルではList<String>の値に sample.list という名前を付けてInjection出来るようにします。


依存関係を記述

■ListがInjectionされるクラス

コンストラクタで@Namedsample.list という値を指定して List<String> の値を受け取るようにしてます

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import java.util.ArrayList;
import java.util.List;

@Singleton
public class SampleList {

    private final List<String> list = new ArrayList<>();

    @Inject
    public SampleList(@Named("sample.list") List<String> srcList) {
        this.list.addAll(srcList);
    }

    public List<String> list() {
        return list;
    }
}


■GuiceのModuleのクラスを定義

GuiceのModuleのクラスを定義して、sample.list という名前で、文字列Listをbindします
TypeLiteralを利用するのがポイントです

import com.google.inject.AbstractModule;
import com.google.inject.TypeLiteral;
import com.google.inject.name.Names;

import java.util.Arrays;
import java.util.List;

public class SampleModule extends AbstractModule {

    @Override
    protected void configure() {
        // 紐づけしたいデータ
        List<String> targetList = Arrays.asList("data1", "data2", "data3");

        bind(new TypeLiteral<List<String>>() {})
                .annotatedWith(Names.named("sample.list")).toInstance(targetList);

        // これではダメ
        // bind(List.class)
        //         .annotatedWith(Names.named("sample.list")).toInstance(targetList);
    }
}


実行

■実行用のサンプルクラス

上で定義したModuleを使ってGuiceのInjectorを生成します。これで準備完了です。

import com.google.inject.Guice;
import com.google.inject.Injector;

public class Launcher {

    public static void main(String[] args) {
        Injector injector = Guice.createInjector(new SampleModule());

        SampleList sampleList = injector.getInstance(SampleList.class);

        System.out.println("SampleList#list -> " + sampleList.list());
    }
}


■実行結果

SampleList#list -> [data1, data2, data3]

sample.list という名前を付けた文字列ListがうまくInjectionされていることが分かります

GitHubでJavaの流行ってそうなライブラリを探す

私が、Javaの有用そうだったり流行ってそうなライブラリをGitHubで探す時の手順です。
(完全に個人的なやり方です)


■Trendingを確認する

Trending Java repositories を確認します。

ページにアクセスすると、以下のような内容が表示されます。ここに新しく表示されたりしたものが無いかをチェックしています。

f:id:nini_y:20170122151627p:plain


■Awesome Javaを確認する

Awesome Javaを確認して、新しいライブラリの情報が追加されていないかをチェックしています

ページにアクセスすると、以下のような内容が表示されます

f:id:nini_y:20170122151637p:plain


■Starが多いポジトリを検索する

GitHub内のSearch用テキストボックスに以下のような条件を入れて検索します

例1

Starが1000より多く付いていて、対象言語はJava

stars:>1000 language:java

例2

Starが500より多く付いていて、対象言語はJava、最終更新日が2016年より後

stars:>500 language:java pushed:>2016


検索の実行例は以下の通りです

f:id:nini_y:20170122151943p:plain

size=1のListのメモリサイズってどの程度

先日、Arrays#asListで生成されるArrayListについてのエントリを書きました。

そのエントリ書いた後に、ふとメモリサイズの違いもあるかなと思ってJOLで測定してみました。


対象

生成するList

今回は条件を単純化して、生成するListを以下内容にしています。

  • 生成するのは文字列List
  • 要素数は1
  • 要素の内容は "hello" という文字列

Listの生成方法

以下操作によって生成された各Listを今回の測定対象としています

  • java.util.Arrays#asList
  • java.util.ArrayList のデフォルトコンストラクタ + addで要素追加
  • java.util.ArrayList の キャパシティを指定するコンストラクタ(引数に1を指定) + addで要素追加
  • java.util.LinkedList のデフォルトコンストラクタ + addで要素追加
  • java.util.concurrent.CopyOnWriteArrayList のデフォルトコンストラクタ + addで要素追加
  • java.util.Collections#singletonList


メモリサイズ取得

以下のサンプルコードを実行してメモリサイズを取得してみました

■サンプルコード

import org.openjdk.jol.info.GraphLayout;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;


public class ListSample {

    public static void main(String[] args) {
        List<String> list1 = Arrays.asList("hello");
        String className1 = list1.getClass().getName();
        System.out.println("## Arrays#asList");

        System.out.println(className1 + " totalSize(byte) -> " + GraphLayout.parseInstance(list1).totalSize());
        System.out.println(className1 + " footprint -> \n" + GraphLayout.parseInstance(list1).toFootprint());

        System.out.println("--------------------");


        List<String> list2 = new ArrayList<>();
        list2.add("hello");

        String className2 = list2.getClass().getName();
        System.out.println("## new ArrayList()");

        System.out.println(className2 + " totalSize(byte) -> " + GraphLayout.parseInstance(list2).totalSize());
        System.out.println(className2 + " footprint -> \n" + GraphLayout.parseInstance(list2).toFootprint());

        System.out.println("--------------------");


        List<String> list3 = new ArrayList<>(1);
        list3.add("hello");

        String className3 = list3.getClass().getName();
        System.out.println("## new ArrayList(initialCapacity=1)");

        System.out.println(className3 + " totalSize(byte) -> " + GraphLayout.parseInstance(list3).totalSize());
        System.out.println(className3 + " footprint -> \n" + GraphLayout.parseInstance(list3).toFootprint());

        System.out.println("--------------------");

        List<String> list4 = new LinkedList<>();
        list4.add("hello");

        String className4 = list4.getClass().getName();
        System.out.println("## new LinkedList");

        System.out.println(className4 + " totalSize(byte) -> " + GraphLayout.parseInstance(list4).totalSize());
        System.out.println(className4 + " footprint -> \n" + GraphLayout.parseInstance(list4).toFootprint());

        System.out.println("--------------------");

        List<String> list5 = new CopyOnWriteArrayList<>();
        list5.add("hello");

        String className5 = list5.getClass().getName();
        System.out.println("## new CopyOnWriteArrayList");

        System.out.println(className5 + " totalSize(byte) -> " + GraphLayout.parseInstance(list5).totalSize());
        System.out.println(className5 + " footprint -> \n" + GraphLayout.parseInstance(list5).toFootprint());

        System.out.println("--------------------");

        List<String> list6 = Collections.singletonList("hello");

        String className6 = list6.getClass().getName();
        System.out.println("## Collections.singletonList");

        System.out.println(className6 + " totalSize(byte) -> " + GraphLayout.parseInstance(list6).totalSize());
        System.out.println(className6 + " footprint -> \n" + GraphLayout.parseInstance(list6).toFootprint());
    }
}


■実行結果

## Arrays#asList
java.util.Arrays$ArrayList totalSize(byte) -> 104
java.util.Arrays$ArrayList footprint -> 
java.util.Arrays$ArrayList@36baf30cd footprint:
     COUNT       AVG       SUM   DESCRIPTION
         1        32        32   [C
         1        24        24   [Ljava.lang.String;
         1        24        24   java.lang.String
         1        24        24   java.util.Arrays$ArrayList
         4                 104   (total)


--------------------
## new ArrayList()
java.util.ArrayList totalSize(byte) -> 136
java.util.ArrayList footprint -> 
java.util.ArrayList@1edf1c96d footprint:
     COUNT       AVG       SUM   DESCRIPTION
         1        32        32   [C
         1        56        56   [Ljava.lang.Object;
         1        24        24   java.lang.String
         1        24        24   java.util.ArrayList
         4                 136   (total)


--------------------
## new ArrayList(initialCapacity=1)
java.util.ArrayList totalSize(byte) -> 104
java.util.ArrayList footprint -> 
java.util.ArrayList@1963006ad footprint:
     COUNT       AVG       SUM   DESCRIPTION
         1        32        32   [C
         1        24        24   [Ljava.lang.Object;
         1        24        24   java.lang.String
         1        24        24   java.util.ArrayList
         4                 104   (total)


--------------------
## new LinkedList
java.util.LinkedList totalSize(byte) -> 112
java.util.LinkedList footprint -> 
java.util.LinkedList@41975e01d footprint:
     COUNT       AVG       SUM   DESCRIPTION
         1        32        32   [C
         1        24        24   java.lang.String
         1        32        32   java.util.LinkedList
         1        24        24   java.util.LinkedList$Node
         4                 112   (total)


--------------------
## new CopyOnWriteArrayList
java.util.concurrent.CopyOnWriteArrayList totalSize(byte) -> 152
java.util.concurrent.CopyOnWriteArrayList footprint -> 
java.util.concurrent.CopyOnWriteArrayList@6f2b958ed footprint:
     COUNT       AVG       SUM   DESCRIPTION
         1        32        32   [C
         1        24        24   [Ljava.lang.Object;
         1        24        24   java.lang.String
         1        24        24   java.util.concurrent.CopyOnWriteArrayList
         1        16        16   java.util.concurrent.locks.ReentrantLock
         1        32        32   java.util.concurrent.locks.ReentrantLock$NonfairSync
         6                 152   (total)


--------------------
## Collections.singletonList
java.util.Collections$SingletonList totalSize(byte) -> 80
java.util.Collections$SingletonList footprint -> 
java.util.Collections$SingletonList@cac736fd footprint:
     COUNT       AVG       SUM   DESCRIPTION
         1        32        32   [C
         1        24        24   java.lang.String
         1        24        24   java.util.Collections$SingletonList
         3                  80   (total)


結果

表にまとめると以下の通りです

Listの生成方法 メモリサイズ(byte)
Arrays#asList 104
ArrayList のデフォルトコンストラクタ + addで要素追加 136
ArrayList の キャパシティを指定するコンストラクタ + addで要素追加 104
LinkedList のデフォルトコンストラクタ + addで要素追加 112
CopyOnWriteArrayList のデフォルトコンストラクタ + addで要素追加 152
Collections#singletonList 80


今回は要素数=1にしていますが、要素数が増えれば当然結果は色々と変わってくると思います



関連エントリ