クラスパスのJavaパッケージからすべてのクラスを読み取るにはどうすればよいですか?


94

Javaパッケージに含まれるクラスを読み取る必要があります。これらのクラスはクラスパスにあります。このタスクをJavaプログラムから直接行う必要があります。簡単な方法を知っていますか?

List<Class> classes = readClassesFrom("my.package")

7
簡単に言えば、いや、それは簡単にはできません。いくつかの状況で機能する非常に長いトリックがいくつかありますが、別のデザインを強くお勧めします。
skaffman 2009


解決策はWeldプロジェクトにあります。
OndraŽižka

その答えは、このリンクを参照してください:stackoverflow.com/questions/176527/...
カルティクE

回答:


48

あなたが持っている場合は春をあなたにクラスパス、その後、次はそれを行います。

XmlRootElementで注釈が付けられているパッケージ内のすべてのクラスを検索します。

private List<Class> findMyTypes(String basePackage) throws IOException, ClassNotFoundException
{
    ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
    MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resourcePatternResolver);

    List<Class> candidates = new ArrayList<Class>();
    String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                               resolveBasePackage(basePackage) + "/" + "**/*.class";
    Resource[] resources = resourcePatternResolver.getResources(packageSearchPath);
    for (Resource resource : resources) {
        if (resource.isReadable()) {
            MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
            if (isCandidate(metadataReader)) {
                candidates.add(Class.forName(metadataReader.getClassMetadata().getClassName()));
            }
        }
    }
    return candidates;
}

private String resolveBasePackage(String basePackage) {
    return ClassUtils.convertClassNameToResourcePath(SystemPropertyUtils.resolvePlaceholders(basePackage));
}

private boolean isCandidate(MetadataReader metadataReader) throws ClassNotFoundException
{
    try {
        Class c = Class.forName(metadataReader.getClassMetadata().getClassName());
        if (c.getAnnotation(XmlRootElement.class) != null) {
            return true;
        }
    }
    catch(Throwable e){
    }
    return false;
}

コードスニペットをありがとう。:)候補リストでクラスの重複を避けたい場合は、コレクションタイプをリストからセットに変更します。そして、classMetaDataのhasEnclosingClass()およびgetEnclosingClassName()を使用します。 基本となるクラスについて使用getEnclosingClass方法
traeper

29

ここで説明したリフレクションプロジェクトを使用できます

それは非常に完全で使いやすいです。

上記のウェブサイトからの簡単な説明:

Reflectionsはクラスパスをスキャンし、メタデータにインデックスを付け、実行時にクエリを実行できるようにし、プロジェクト内の多くのモジュールの情報を保存および収集できます。

例:

Reflections reflections = new Reflections(
    new ConfigurationBuilder()
        .setUrls(ClasspathHelper.forJavaClassPath())
);
Set<Class<?>> types = reflections.getTypesAnnotatedWith(Scannable.class);

Equinoxでの作業はありますか?もしそうなら、プラグインをアクティブ化することなくそれを行うことができますか?
タグ付け

春分のために働く。古いバージョンのReflectionsでは、バンドルjar vfsTypeを追加します。こちらを
zapp

28

私はこれを使用します、それはファイルまたはjarアーカイブで動作します

public static ArrayList<String>getClassNamesFromPackage(String packageName) throws IOException{
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    URL packageURL;
    ArrayList<String> names = new ArrayList<String>();;

    packageName = packageName.replace(".", "/");
    packageURL = classLoader.getResource(packageName);

    if(packageURL.getProtocol().equals("jar")){
        String jarFileName;
        JarFile jf ;
        Enumeration<JarEntry> jarEntries;
        String entryName;

        // build jar file name, then loop through zipped entries
        jarFileName = URLDecoder.decode(packageURL.getFile(), "UTF-8");
        jarFileName = jarFileName.substring(5,jarFileName.indexOf("!"));
        System.out.println(">"+jarFileName);
        jf = new JarFile(jarFileName);
        jarEntries = jf.entries();
        while(jarEntries.hasMoreElements()){
            entryName = jarEntries.nextElement().getName();
            if(entryName.startsWith(packageName) && entryName.length()>packageName.length()+5){
                entryName = entryName.substring(packageName.length(),entryName.lastIndexOf('.'));
                names.add(entryName);
            }
        }

    // loop through files in classpath
    }else{
    URI uri = new URI(packageURL.toString());
    File folder = new File(uri.getPath());
        // won't work with path which contains blank (%20)
        // File folder = new File(packageURL.getFile()); 
        File[] contenuti = folder.listFiles();
        String entryName;
        for(File actual: contenuti){
            entryName = actual.getName();
            entryName = entryName.substring(0, entryName.lastIndexOf('.'));
            names.add(entryName);
        }
    }
    return names;
}

1
これは、File.Separatorへの参照を取り出して「/」を使用するだけで機能します
Will Glass

1
パスにスペースが含まれている場合、jarファイルを操作するには、もう1つ変更が必要です。jarファイルのパスをデコードする必要があります。変更:jarFileName = packageURL.getFile(); to:jarFileName = URLDecoder.decode(packageURL.getFile());
Will Glass、

私はjarでパッケージ名のみを取得します。クラス名を取得していません
AutoMEta

新しいFile(uri)はスペースの問題を修正します。
トレイカズ2013年

entryName.lastIndexOf( '。')は-1になります
marstone

11

Springは、優れたクラスパス検索機能をに実装していPathMatchingResourcePatternResolverます。classpath*:接頭辞を使用すると、特定の階層のクラスを含むすべてのリソースを検索でき、必要に応じてそれらをフィルタリングすることもできます。そして、あなたはの子を使用することができAbstractTypeHierarchyTraversingFilterAnnotationTypeFilterおよびAssignableTypeFilterクラスレベルのアノテーションのいずれかで、それらのリソースをフィルタリングするか、インターフェイス上で、彼らは実装しています。


6

Java 1.6.0_24:

public static File[] getPackageContent(String packageName) throws IOException{
    ArrayList<File> list = new ArrayList<File>();
    Enumeration<URL> urls = Thread.currentThread().getContextClassLoader()
                            .getResources(packageName);
    while (urls.hasMoreElements()) {
        URL url = urls.nextElement();
        File dir = new File(url.getFile());
        for (File f : dir.listFiles()) {
            list.add(f);
        }
    }
    return list.toArray(new File[]{});
}

このソリューションは、EJB環境内でテストされました。


6

ScannotationReflectionsはクラスパススキャンアプローチを使用します。

Reflections reflections = new Reflections("my.package");
Set<Class<? extends Object>> classes = reflections.getSubTypesOf(Object.class);

別のアプローチは、Java Pluggable Annotation Processing API使用して、コンパイル時にすべての注釈付きクラスを収集し、実行時に使用するインデックスファイルを構築する注釈プロセッサを作成することです。このメカニズムはClassIndexライブラリに実装されてます。

Iterable<Class> classes = ClassIndex.getPackageClasses("my.package");

3

私の知る限り、その機能はまだJavaリフレクションAPIから疑わしく欠けています。これを行うだけでパッケージオブジェクトを取得できます。

Package packageObj = Package.getPackage("my.package");

しかし、おそらくお気づきのように、そのパッケージ内のクラスを一覧表示することはできません。今のところ、あなたはファイルシステム指向のアプローチのようなものを取る必要があります。

この投稿 でいくつかのサンプル実装を見つけました

クラスがJARファイルに埋め込まれている場合にこれらのメソッドが機能するかどうかは100%わかりませんが、そのうちの1つが機能することを願っています。

@skaffmanに同意します...これについて別の方法がある場合は、代わりに行うことをお勧めします。


4
疑わしいわけではなく、そのように機能しないだけです。クラスはパッケージに属しておらず、それらへの参照を持っています。関連付けは別の方向を指していません。
skaffman 2009

1
@skaffman非常に興味深い点。そのように考えたことはありません。それで、私たちがその考えの道を歩き回っている限り、関連付けが双方向ではないのはなぜですか(これは今、私自身の好奇心のためのものです)?
ブレントがコードを作成

3

特定のパッケージ内のすべてのクラスを一覧表示するための最も堅牢なメカニズムは、新しいJPMSモジュールシステムを含むクラスパス指定メカニズムの可能な限り幅広い配列を処理するため、現在はClassGraphです。(私は著者です。)

List<String> classNames;
try (ScanResult scanResult = new ClassGraph().whitelistPackages("my.package")
        .enableClassInfo().scan()) {
    classNames = scanResult.getAllClasses().getNames();
}

私は数年後にこれに出くわしました。これは素晴らしいツールです!それはリフレクションよりもはるかに速く動作し、静的初期化子を呼び出さないのが好きです。私たちが抱えていた問題を解決するために必要なものだけです。
akagixxer 2017

2

eXtcosは有望に見えます。次のすべてのクラスを検索するとします。

  1. クラス「コンポーネント」から拡張し、それらを格納します
  2. 「MyComponent」という注釈が付けられている
  3. 「共通」パッケージに含まれています。

eXtcosでは、これは次のように簡単です。

ClasspathScanner scanner = new ClasspathScanner();
final Set<Class> classStore = new ArraySet<Class>();

Set<Class> classes = scanner.getClasses(new ClassQuery() {
    protected void query() {
        select().
        from(“common”).
        andStore(thoseExtending(Component.class).into(classStore)).
        returning(allAnnotatedWith(MyComponent.class));
    }
});

2
  1. Bill Burkeが(クラススキャンに関する素晴らしい記事 ]を書き、その後Scannotationを書きました

  2. Hibernateはこれをすでに書いています:

    • org.hibernate.ejb.packaging.Scanner
    • org.hibernate.ejb.packaging.NativeScanner
  3. CDIはこれを解決する可能性がありますが、わかりません-まだ完全には調査されていません

@Inject Instance< MyClass> x;
...
x.iterator() 

注釈についても:

abstract class MyAnnotationQualifier
extends AnnotationLiteral<Entity> implements Entity {}

記事へのリンクは死んでいます。
user2418306 2016

1

私はたまたまそれを実装しました、そしてそれはほとんどの場合うまくいきます。長いのでこちらファイルに入れました。

アイデアは、ほとんどの場合に使用できるクラスソースファイルの場所を見つけることです(既知の例外は、JVMクラスファイルです-私がテストした限り)。コードがディレクトリ内にある場合は、すべてのファイルをスキャンし、スポットクラスファイルのみをスキャンします。コードがJARファイルにある場合は、すべてのエントリをスキャンします。

このメソッドは、次の場合にのみ使用できます。

  1. 発見したい同じパッケージにあるクラスがあります。このクラスはSeedClassと呼ばれます。たとえば、「java.io」内のすべてのクラスを一覧表示する場合、シードクラスはになる可能性がありますjava.io.File

  2. クラスがディレクトリまたはJARファイル内にあり、ソースファイル情報が含まれている(ソースコードファイルではなく、ソースファイルのみ)。私が試した限りでは、JVMクラスを除いてほぼ100%機能します(これらのクラスにはJVMが付属しています)。

  3. プログラムには、これらのクラスのProtectionDomainにアクセスする権限が必要です。プログラムがローカルに読み込まれている場合、問題はありません。

私は定期的に使用するためだけにプログラムをテストしたので、まだ問題があるかもしれません。

これがお役に立てば幸いです。


1
それはとても興味深いもののようです!使ってみます。参考になった場合、オープンソースプロジェクトでコードを使用できますか?

1
私もそれをオープンソースにする準備をしています。だから先に行く:D
NawaMan 2009年

@NawaMan:ついにオープンソースになりましたか?もしそうなら、最新バージョンはどこにありますか?ありがとう!
Joanis、2012年

1

ここに別のオプションがあります、上/下の別の答えを少し変更します:

Reflections reflections = new Reflections("com.example.project.package", 
    new SubTypesScanner(false));
Set<Class<? extends Object>> allClasses = 
    reflections.getSubTypesOf(Object.class);

0

アプレットが一般的な場所だった頃は、クラスパスにURLがあったかもしれません。クラスローダーがクラスを必要とする場合、クラスローダーはhttpリソースを含むクラスパス上のすべての場所を検索します。クラスパスにはURLやディレクトリなどを含めることができるため、クラスの完全なリストを取得する簡単な方法はありません。

ただし、かなり近づくことができます。Springライブラリのいくつかは、現在これを行っています。クラスパス上のすべてのjarを取得して、ファイルのように開くことができます。次に、このファイルのリストを取得して、クラスを含むデータ構造を作成できます。


0

依存関係mavenを使用します。

groupId: net.sf.extcos
artifactId: extcos
version: 0.4b

次に、このコードを使用します:

ComponentScanner scanner = new ComponentScanner();
        Set classes = scanner.getClasses(new ComponentQuery() {
            @Override
            protected void query() {
                select().from("com.leyton").returning(allExtending(DynamicForm.class));
            }
        });

-2

ブレント-関連付けが1つの方法である理由は、CLASSPATHの任意のコンポーネントの任意のクラスが任意のパッケージ(java / javaxを除く)で宣言できるという事実に関係しています。したがって、誰も知らず、知ることもできないため、特定の「パッケージ」内のすべてのクラスのマッピングはありません。明日、jarファイルを更新し、クラスを削除または追加できます。これは、世界のすべての国でJohn / Jon / Johanという名前のすべての人のリストを取得しようとするようなものです。誰も万能ではないため、正しい答えを得る人はいません。


哲学的な答えはいいですが、CDI Beanスキャンはどのように機能しますか?または、Hibernateはどのように@Entitiesをスキャンしますか?
OndraŽižka
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.