Tüm sınıfları yansıma kullanarak bir pakette bulabilir misiniz?


528

Belirli bir paketteki tüm sınıfları veya arabirimleri bulmak mümkün müdür? (Hızlı bir şekilde örneğin Package, hayır gibi görünüyor.)


2
Sınıf yolunda bir boşluk karakteri varsa (ve muhtemelen diğer alfasayısal olmayan karakterler için de) bir hataya sahip olmasına rağmen, Amit çalışmalara bağlanan çözümdür. herhangi bir üretim kodunda kullanıyorsanız, geçici bir çözüm için verdiği cevaba bakın.
Kip

2
Ayrıca bu gönderiyi not edin .
barfuin

1
İlgili cevaba bakın: stackoverflow.com/a/30149061/4102160
Cfx

1
Ayrıca bu gönderiyi not edin .
sp00m

1
Aşağıda ClassGraph ile ilgili cevabımı görebilirsiniz, şu anda sınıfyolunu ve modül yolunu taramak için en sağlam yöntemdir.
Luke Hutchison

Yanıtlar:


374

Sınıf yükleyicilerin dinamik doğası nedeniyle bu mümkün değildir. Sınıf yükleyicilerinin VM'ye hangi sınıfları sağlayabileceğini söylemesine gerek yoktur, bunun yerine sadece sınıflar için istekleri teslim eder ve bir sınıf döndürmek veya bir istisna atmak zorundadır.

Ancak, kendi sınıf yükleyicilerinizi yazar veya sınıf yollarını ve kavanozları incelerseniz, bu bilgiyi bulmak mümkündür. Bu, yansıma değil dosya sistemi işlemleri yoluyla olacaktır. Bunu yapmanıza yardımcı olabilecek kütüphaneler bile olabilir.

Uzaktan oluşturulan veya uzaktan teslim edilen sınıflar varsa, bu sınıfları keşfedemezsiniz.

Normal yöntem, bir yere erişmeniz gereken sınıfları bir yere kaydetmek veya farklı bir sınıfta başvuruda bulunmaktır. Veya sadece isimlendirme söz konusu olduğunda kongre kullanın.

Ek: Yansımalar Kitaplığı , geçerli sınıf yolundaki sınıfları aramanıza olanak tanır. Tüm sınıfları bir pakette almak için kullanılabilir:

 Reflections reflections = new Reflections("my.project.prefix");

 Set<Class<? extends Object>> allClasses = 
     reflections.getSubTypesOf(Object.class);

12
Sınıf isimlerini sorgulayamama uzun süredir beni rahatsız etti. Elbette, zor ve performans büyük ölçüde değişebilir ve belirli sınıf yükleyiciler için liste tanımsız veya sınırsızdır, ancak bunun üzerinde çalışılmış olabilir.
Bay Shiny ve Yeni 宇 宇

16
GetSubTypesOf varsayılan olarak Object alt türlerini döndürmez gibi bu çözüm çalışmaz unutmayın. SubTypeScanner'ı yapılandırma hakkında bilgi için Aleksander Blomskøld'un çözümüne bakın.
Alex Spurling

17
Yansımalar Guava gerektirir. Guava büyük. Sürüm 14.0.1 2.1MB'dir.
mike jones

3
Benim için çalışmadı. Mac OSX - Yansımaya bağımlılık sürümü 0.9.9-RC1 (maven) - JDK 1.7. Kabul edilen cevabı tekrar düşünün. @ AleksanderBlomskøld cevap gitmek için biridir. !!!!!
Konstantinos Margaritis

68
Bu boş bir liste döndürürse, Reflections nesnesini şu şekilde başlatın: Reflections reflections = new Reflections ("your.package.here", yeni SubTypesScanner (false));
João Rocha da Silva

186

Muhtemelen açık kaynaklı Yansımalar kütüphanesine bir göz atmalısınız . Bununla beraber kolayca istediğinizi elde edebilirsiniz.

İlk olarak, yansımalar dizinini ayarlayın (tüm sınıfları aramak varsayılan olarak devre dışı bırakıldığından biraz karışıktır):

List<ClassLoader> classLoadersList = new LinkedList<ClassLoader>();
classLoadersList.add(ClasspathHelper.contextClassLoader());
classLoadersList.add(ClasspathHelper.staticClassLoader());

Reflections reflections = new Reflections(new ConfigurationBuilder()
    .setScanners(new SubTypesScanner(false /* don't exclude Object.class */), new ResourcesScanner())
    .setUrls(ClasspathHelper.forClassLoader(classLoadersList.toArray(new ClassLoader[0])))
    .filterInputsBy(new FilterBuilder().include(FilterBuilder.prefix("org.your.package"))));

Ardından belirli bir paketteki tüm nesneleri sorgulayabilirsiniz:

Set<Class<?>> classes = reflections.getSubTypesOf(Object.class);

6
Ah, işte başlıyoruz: code.google.com/p/reflections/issues/detail?id=122 . Nesne varsayılan olarak hariç tutulur, ancak onu yeniden düzenleyebilirsiniz. Beni bu kütüphaneye yönlendirdiğiniz için teşekkürler, harika!
mtrc

1
Bu kodla Mac'imdeki sorunlara koştum (yerel kütüphanelerle ilgili), ancak .addUrls(ClasspathHelper.forJavaClassPath())yukarıdaki yerine kullanmak onları benim için çözdü. Daha az kod da!
David Pärsson

3
Herkes merak ediyor bir varsayılan paket almak için en basit yolu boş bir dize -> "" olması.
JBA

2
"Yansımalar" kitaplığının zor bir lisansı vardır: github.com/ronmamo/reflections/blob/master/COPYING.txt . İşin püf noktası, lisansın sadece lisansın kendisinin ücretsiz kullanımına izin vermesidir. Bu yüzden kütüphaneyi gerçekten kullanmak için (lisansı değil) herkes yazarla iletişime geçmeli ve kullanım şartlarını müzakere etmelidir.
Serge Rogatch

3
Serge, WTFPL'i yanlış anladığınızı düşünüyorum: wtfpl.net WTFPL, sadece lisansla değil, kodla birlikte istediğiniz her şeyi yapmakta özgür olduğunuz anlamına gelir
Richo

122

Google Guava 14, ClassPathüst düzey sınıfları taramak için üç yöntem içeren yeni bir sınıf içerir :

  • getTopLevelClasses()
  • getTopLevelClasses(String packageName)
  • getTopLevelClassesRecursive(String packageName)

Daha fazla bilgi için ClassPathjavadoc'lara bakınız .


Bu, Reflection'ın yapamayacağı yerde çalıştı (ortak doğrudan ata, ortak açıklama yok)
Riccardo Cossu

1
Aşağıdaki bir yorumda belirttiğim gibi , ClassPathile etiketlendi @Beta, bu yüzden bazıları için iyi bir fikir olmayabilir ...
Christian

1
Bunun yansımanın yapmadığı yerlerde işe yaradığını söylemek biraz garip, çözüm şüphesiz yansıma (ve sınıf yükleyici) işlevselliği kullanılarak uygulanır.
Maarten Bodewes

5
Sanırım diğer cevapta bahsedilen Yansımalar kütüphanesini kastediyordu.
Christoph Leiter

Guava 28.1-jre sürümü kullanılıyorsa Java 11 altında çalışır.
gorjanz

112

Bu yöntemi kullanan 1 yöntemini kullanabilirsiniz ClassLoader.

/**
 * Scans all classes accessible from the context class loader which belong to the given package and subpackages.
 *
 * @param packageName The base package
 * @return The classes
 * @throws ClassNotFoundException
 * @throws IOException
 */
private static Class[] getClasses(String packageName)
        throws ClassNotFoundException, IOException {
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    assert classLoader != null;
    String path = packageName.replace('.', '/');
    Enumeration<URL> resources = classLoader.getResources(path);
    List<File> dirs = new ArrayList<File>();
    while (resources.hasMoreElements()) {
        URL resource = resources.nextElement();
        dirs.add(new File(resource.getFile()));
    }
    ArrayList<Class> classes = new ArrayList<Class>();
    for (File directory : dirs) {
        classes.addAll(findClasses(directory, packageName));
    }
    return classes.toArray(new Class[classes.size()]);
}

/**
 * Recursive method used to find all classes in a given directory and subdirs.
 *
 * @param directory   The base directory
 * @param packageName The package name for classes found inside the base directory
 * @return The classes
 * @throws ClassNotFoundException
 */
private static List<Class> findClasses(File directory, String packageName) throws ClassNotFoundException {
    List<Class> classes = new ArrayList<Class>();
    if (!directory.exists()) {
        return classes;
    }
    File[] files = directory.listFiles();
    for (File file : files) {
        if (file.isDirectory()) {
            assert !file.getName().contains(".");
            classes.addAll(findClasses(file, packageName + "." + file.getName()));
        } else if (file.getName().endsWith(".class")) {
            classes.add(Class.forName(packageName + '.' + file.getName().substring(0, file.getName().length() - 6)));
        }
    }
    return classes;
}

__________
1 Bu yöntem aslen alındı http://snippets.dzone.com/posts/show/4831 hangi, arşivlenen şimdi bağlantılı olarak, Internet Archive tarafından. Snippet'e https://dzone.com/articles/get-all-classes-within-package adresinden de ulaşılabilir .


6
Yolumda boşluklar varsa bu sorunla karşılaştım. URL sınıfı, alanlardan kaçıyordu %20, ancak new File()yapıcı bunu gerçek bir yüzde işareti olarak iki sıfır olarak kabul etti. Bunu şu şekilde değiştirerek çözdüm dirs.add(...): dirs.add(new File(resource.toURI())); Bu aynı zamanda URISyntaxExceptionatar maddesine eklemek zorunda olduğum anlamına geliyordugetClasses
Kip

19
Az önce dzone.com/articles/get-all-classes-within-package adresinden kopyaladınız ! bir dahaki sefere kaynağa bakın
RS

19
+1 çünkü bu çözüm harici kütüphaneler GEREKTİRMEZ ... ASLA, gerçekten böyle küçük bir şeyi elde etmek için kodunuzu Rastgele ASLA kodlarla rastgele birleştirmeyin. saldırganlar için potansiyel saldırı yüzeyi eklediğinizi biliyor musunuz? Kasım 2015 Apache Commons sorun sadece JBoss / Weblogic'te [üzerinde dağıtılan bir uygulamanın sınıf yolunda Apaçi Commons alarak Uzaktan Kumanda Yürütme o yol keşfetti foxglovesecurity.com/2015/11/06/...
sc0p

@Qix, bu kodun kavanozu desteklemediğini doğru şekilde kaydetti. Kavanozları ve dizinleri desteklemek için . Kod aşağıda belirtildiği gibi değiştirildi:
user1523177

1
İyi bir çözüm, ancak 'Class.forName (String className)' yerine 'Class.forName (String className, boolean initialize, ClassLoader loader)' ile değiştirilirse daha iyi görünüyor. sınıf örnekleri oluşturmamak için.
Andrei_N

99

bahar

Bu örnek Bahar 4 içindir, ancak sınıf yolu tarayıcısını önceki sürümlerde de bulabilirsiniz.

// create scanner and disable default filters (that is the 'false' argument)
final ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
// add include filters which matches all the classes (or use your own)
provider.addIncludeFilter(new RegexPatternTypeFilter(Pattern.compile(".*")));

// get matching classes defined in the package
final Set<BeanDefinition> classes = provider.findCandidateComponents("my.package.name");

// this is how you can load the class type from BeanDefinition instance
for (BeanDefinition bean: classes) {
    Class<?> clazz = Class.forName(bean.getBeanClassName());
    // ... do your magic with the class ...
}

Google Guava

Not: Sürüm 14'te API hala @Beta olarak işaretlenmiştir , bu nedenle üretim koduna dikkat edin.

final ClassLoader loader = Thread.currentThread().getContextClassLoader();

for (final ClassPath.ClassInfo info : ClassPath.from(loader).getTopLevelClasses()) {
  if (info.getName().startsWith("my.package.")) {
    final Class<?> clazz = info.load();
    // do something with your clazz
  }
}

5
Mükemmel cevap. Burada ayrıntılı, test edilmemiş, çalışmayan çok fazla çözüm var! Bu harika: kısa ve test edilmiş (Guava'dan). Çok iyi! Yararlı, daha fazla oyu hak ediyor.
JeanValjean

Maalesef, Guava'daki sınıf da ClassPathşu şekilde işaretlenmiştir @Beta: "Sınıf veya yöntem düzeyinde @Beta ek açıklamasıyla işaretlenen API'ler değişebilir. Herhangi bir şekilde değiştirilebilir veya hatta herhangi bir büyük sürümde kaldırılabilir. bir kütüphanenin kendisidir (yani kendi kontrolünüz dışındaki kullanıcıların CLASSPATH'inde kullanılır), onları yeniden paketlemediğiniz sürece beta API'lerini kullanmamalısınız ... " code.google.com/p/guava-libraries/#Important_Warnings
Christian

@Christian İyi bir nokta, fark etmedim! Teşekkür ederim. Kesinlikle beta olmayan Spring sınıf yolu tarayıcısını kullanarak başka bir cevap ekleyeceğim.
voho

Guava çözümünü kullanarak iç içe geçmiş statik sınıfları bulmak için getAllClasses()yöntem kullanılabilir.
v.ladynev

1
Spring çözümü, yürütülebilir bir kavanozdan çalıştırıldığında çalışan tek çözümdür.
Luke

38

Merhaba. Her zaman yukarıdaki çözümlerle (ve diğer sitelerde) bazı sorunlar yaşadım.
Bir geliştirici olarak bir API için bir addon programlıyorum. API, harici kitaplıkların veya üçüncü taraf araçlarının kullanılmasını önler. Kurulum ayrıca jar veya zip dosyalarındaki kod karışımından ve doğrudan bazı dizinlerde bulunan sınıf dosyalarından oluşur. Yani benim kodum her kurulum etrafında çalışması gerekiyordu. Bir çok araştırmadan sonra tüm olası kurulumların en az% 95'inde çalışacak bir yöntem buldum.

Aşağıdaki kod temel olarak her zaman çalışacak olan aşırı doldurma yöntemidir.

Kod:

Bu kod, verilen bir paketi içinde bulunan tüm sınıflar için tarar. Yalnızca geçerli olan tüm sınıflar için çalışır ClassLoader.

/**
 * Private helper method
 * 
 * @param directory
 *            The directory to start with
 * @param pckgname
 *            The package name to search for. Will be needed for getting the
 *            Class object.
 * @param classes
 *            if a file isn't loaded but still is in the directory
 * @throws ClassNotFoundException
 */
private static void checkDirectory(File directory, String pckgname,
        ArrayList<Class<?>> classes) throws ClassNotFoundException {
    File tmpDirectory;

    if (directory.exists() && directory.isDirectory()) {
        final String[] files = directory.list();

        for (final String file : files) {
            if (file.endsWith(".class")) {
                try {
                    classes.add(Class.forName(pckgname + '.'
                            + file.substring(0, file.length() - 6)));
                } catch (final NoClassDefFoundError e) {
                    // do nothing. this class hasn't been found by the
                    // loader, and we don't care.
                }
            } else if ((tmpDirectory = new File(directory, file))
                    .isDirectory()) {
                checkDirectory(tmpDirectory, pckgname + "." + file, classes);
            }
        }
    }
}

/**
 * Private helper method.
 * 
 * @param connection
 *            the connection to the jar
 * @param pckgname
 *            the package name to search for
 * @param classes
 *            the current ArrayList of all classes. This method will simply
 *            add new classes.
 * @throws ClassNotFoundException
 *             if a file isn't loaded but still is in the jar file
 * @throws IOException
 *             if it can't correctly read from the jar file.
 */
private static void checkJarFile(JarURLConnection connection,
        String pckgname, ArrayList<Class<?>> classes)
        throws ClassNotFoundException, IOException {
    final JarFile jarFile = connection.getJarFile();
    final Enumeration<JarEntry> entries = jarFile.entries();
    String name;

    for (JarEntry jarEntry = null; entries.hasMoreElements()
            && ((jarEntry = entries.nextElement()) != null);) {
        name = jarEntry.getName();

        if (name.contains(".class")) {
            name = name.substring(0, name.length() - 6).replace('/', '.');

            if (name.contains(pckgname)) {
                classes.add(Class.forName(name));
            }
        }
    }
}

/**
 * Attempts to list all the classes in the specified package as determined
 * by the context class loader
 * 
 * @param pckgname
 *            the package name to search
 * @return a list of classes that exist within that package
 * @throws ClassNotFoundException
 *             if something went wrong
 */
public static ArrayList<Class<?>> getClassesForPackage(String pckgname)
        throws ClassNotFoundException {
    final ArrayList<Class<?>> classes = new ArrayList<Class<?>>();

    try {
        final ClassLoader cld = Thread.currentThread()
                .getContextClassLoader();

        if (cld == null)
            throw new ClassNotFoundException("Can't get class loader.");

        final Enumeration<URL> resources = cld.getResources(pckgname
                .replace('.', '/'));
        URLConnection connection;

        for (URL url = null; resources.hasMoreElements()
                && ((url = resources.nextElement()) != null);) {
            try {
                connection = url.openConnection();

                if (connection instanceof JarURLConnection) {
                    checkJarFile((JarURLConnection) connection, pckgname,
                            classes);
                } else if (connection instanceof FileURLConnection) {
                    try {
                        checkDirectory(
                                new File(URLDecoder.decode(url.getPath(),
                                        "UTF-8")), pckgname, classes);
                    } catch (final UnsupportedEncodingException ex) {
                        throw new ClassNotFoundException(
                                pckgname
                                        + " does not appear to be a valid package (Unsupported encoding)",
                                ex);
                    }
                } else
                    throw new ClassNotFoundException(pckgname + " ("
                            + url.getPath()
                            + ") does not appear to be a valid package");
            } catch (final IOException ioex) {
                throw new ClassNotFoundException(
                        "IOException was thrown when trying to get all resources for "
                                + pckgname, ioex);
            }
        }
    } catch (final NullPointerException ex) {
        throw new ClassNotFoundException(
                pckgname
                        + " does not appear to be a valid package (Null pointer exception)",
                ex);
    } catch (final IOException ioex) {
        throw new ClassNotFoundException(
                "IOException was thrown when trying to get all resources for "
                        + pckgname, ioex);
    }

    return classes;
}

Bu üç yöntem, belirli bir paketteki tüm sınıfları bulma olanağı sağlar.
Bu şekilde kullanırsınız:

getClassesForPackage("package.your.classes.are.in");

Açıklama:

Yöntem önce akımı alır ClassLoader. Daha sonra, söz konusu paketi içeren tüm kaynakları alır ve bu dosyaları tekrarlar URL. Daha sonra a oluşturur URLConnectionve ne tür bir URl'ye sahip olduğumuzu belirler. Bir dizin ( FileURLConnection) veya bir kavanoz veya zip dosyasının ( JarURLConnection) içindeki bir dizin olabilir . Ne tür bir bağlantımız olduğuna bağlı olarak iki farklı yöntem denir.

İlk önce eğer varsa ne olacağını görelim FileURLConnection.
Önce iletilen Dosya'nın var olup olmadığını ve bir dizin olup olmadığını kontrol eder. Bu durumda, bir sınıf dosyası olup olmadığını kontrol eder. Eğer öyleyse bir Classnesne oluşturulacak veArrayList . Bir sınıf dosyası değilse, ancak bir dizinse, sadece onu tekrarlar ve aynı şeyi yaparız. Diğer tüm durumlar / dosyalar yok sayılır.

Eğer URLConnectionbir olan JarURLConnectiondiğer özel yardımcı yöntem çağrılır. Bu yöntem zip / jar arşivindeki tüm Girişleri tekrarlar. Bir girdi bir sınıf dosyasıysa ve paketin içindeyse, bir Classnesne oluşturulur ve içinde saklanır ArrayList.

Tüm kaynaklar ayrıştırıldıktan sonra (ana yöntem) ArrayListverilen paketteki akımın ClassLoaderbildiği tüm sınıfları içerir .

İşlem herhangi bir noktada başarısız olursa ClassNotFoundException, kesin nedeni hakkında ayrıntılı bilgi içerecek şekilde a atılacaktır.


4
Bu örnek sun.net.www.protocol.file.FileURLConnection, derleme zamanında bir uyarı oluşturan içe aktarmayı gerektiriyor gibi görünüyor ("uyarı: sun.net.www.protocol.file.FileURLConnection Sun'a özel API'dir ve gelecekteki bir sürümde kaldırılabilir"). Bu sınıfı kullanmanın bir alternatifi var mı, yoksa ek açıklamalar kullanılarak uyarı bastırılabilir mi?
Hıristiyan

Bu yöntem, java.lang, java.util, ... gibi bootstrap sınıfları için çalışmaz. Bunlar, System.getProperty ("sun.boot.class.path") ile bölünerek bulunabilir: veya; (işletim sistemine bağlı olarak) ve ardından yukarıdaki checkDirectory ve checkJarFile öğelerinin biraz değiştirilmiş sürümlerini çalıştırır.
coderforlife

1
Connection.getClass (). GetCanonicalName (). Eşittir ("sun.net.www.protocol.file.FileURLConnection") öğesini kullanarak uyarı / hatayı alabilirsiniz. Gerçekten istiyorsanız, sun.net.www.protocol.file.FileURLConnection kullanmanız gerektiğini düşündüğünüz bir URLConnection oluşturabilir ve ayrıca bağlantı sınıfının adını oluşturduğunuz sınıfın adını karşılaştırabilirsiniz. Her ikisi de aynı ise, sınıf adının değişmesi durumunda başarısız olmak yerine sun.net.www.protocol.file.FileURLConnection örneğini ele alabilirsiniz.
William Deans

1
@Christian FileURLConnection uygulamasının böyle bir şey yapmasını önleyebilirsiniz: if ( ... instanceof JarURLConnecton) { ... } else { // Asume that the Connection is valid and points to a File }JPA açıklamalı sınıfları aramak için kodumda yaptığım şey budur
Zardoz89

15

Fazladan kitaplık kullanmadan:

package test;

import java.io.DataInputStream;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

public class Test {
    public static void main(String[] args) throws Exception{
        List<Class> classes = getClasses(Test.class.getClassLoader(),"test");
        for(Class c:classes){
            System.out.println("Class: "+c);
        }
    }

    public static List<Class> getClasses(ClassLoader cl,String pack) throws Exception{

        String dottedPackage = pack.replaceAll("[/]", ".");
        List<Class> classes = new ArrayList<Class>();
        URL upackage = cl.getResource(pack);

        DataInputStream dis = new DataInputStream((InputStream) upackage.getContent());
        String line = null;
        while ((line = dis.readLine()) != null) {
            if(line.endsWith(".class")) {
               classes.add(Class.forName(dottedPackage+"."+line.substring(0,line.lastIndexOf('.'))));
            }
        }
        return classes;
    }
}

Bunu bir JAR'da çalıştırdığımda upackage, null... :(
Christian

"Com.mycompany.beans" paketi için, "test" yerine "com / mycompany / beans" yazın
James Jithin

4
Bu kodu kullanırken null alıyorum. Sadece kavanozunuz çalıştırılabilir olduğunda işe yarıyor gibi görünüyor
Alao

paket adını String pack = getPackage().getName();aldıysanız, şunu eklemeniz gerekirpack = pack.replaceAll("[.]", "/");
user2682877

13

Genel olarak sınıf yükleyicileri, sınıfyolundaki tüm sınıflar arasında tarama yapılmasına izin vermez. Ancak genellikle tek kullanılan sınıf yükleyici, dizinlerin ve jar dosyalarının ( getURL'lere bakın ) listesini alabileceğimiz ve kullanılabilir sınıfları listelemek için bunları tek tek açabileceğimiz UrlClassLoader'dir . Sınıf yolu tarama olarak adlandırılan bu yaklaşım, uygulanan Scannotation ve Yansımaları .

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

Başka bir yaklaşım, derleme zamanında tüm açıklamalı sınıfları toplayacak ve çalışma zamanı kullanımı için dizin dosyasını oluşturacak ek açıklama işlemcisi yazmak için Java Takılabilir Ek Açıklama İşleme API'sini kullanmaktır. Bu mekanizma ClassIndex kütüphanesinde uygulanır:

// package-info.java
@IndexSubclasses
package my.package;

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

Java derleyicisinin sınıf yolunda bulunan tüm işlemcileri otomatik olarak keşfetmesi sayesinde tarama tamamen otomatik olduğu için ek bir kurulum gerekmediğine dikkat edin.


bu bir Kavanozda paketlenmiş sınıfları keşfediyor mu? Benim için işe yaramıyor gibi görünüyor.
Haziran'da

hangi aracı kullanmaya çalışıyorsunuz?
Sławek

Reflections lib kullanıyorum. Ama @Alexander Blomskøld tarafından bu lib'in son sürümleri için belirtilen geçici çözümü takip ettikten sonra çalıştım.
32'de asgs

Merhaba, tutulma kullanıyorum ve çalışamıyorum, ClassIndex.getPackageClasses ("my.package") boş bir harita döndür
Juan

11

Belirli bir paketteki tüm sınıfları listelemek için en sağlam mekanizma şu anda ClassGraph'dır , çünkü yeni JPMS modül sistemi de dahil olmak üzere mümkün olan en geniş sınıf yolu belirtim mekanizması dizisini işler . (Ben yazarım.)

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

5

İşte böyle yapıyorum. Tüm alt klasörleri (alt paketleri) tararım ve anonim sınıfları yüklemeye çalışmaz:

   /**
   * Attempts to list all the classes in the specified package as determined
   * by the context class loader, recursively, avoiding anonymous classes
   * 
   * @param pckgname
   *            the package name to search
   * @return a list of classes that exist within that package
   * @throws ClassNotFoundException
   *             if something went wrong
   */
  private static List<Class> getClassesForPackage(String pckgname) throws ClassNotFoundException {
      // This will hold a list of directories matching the pckgname. There may be more than one if a package is split over multiple jars/paths
      ArrayList<File> directories = new ArrayList<File>();
      String packageToPath = pckgname.replace('.', '/');
      try {
          ClassLoader cld = Thread.currentThread().getContextClassLoader();
          if (cld == null) {
              throw new ClassNotFoundException("Can't get class loader.");
          }

          // Ask for all resources for the packageToPath
          Enumeration<URL> resources = cld.getResources(packageToPath);
          while (resources.hasMoreElements()) {
              directories.add(new File(URLDecoder.decode(resources.nextElement().getPath(), "UTF-8")));
          }
      } catch (NullPointerException x) {
          throw new ClassNotFoundException(pckgname + " does not appear to be a valid package (Null pointer exception)");
      } catch (UnsupportedEncodingException encex) {
          throw new ClassNotFoundException(pckgname + " does not appear to be a valid package (Unsupported encoding)");
      } catch (IOException ioex) {
          throw new ClassNotFoundException("IOException was thrown when trying to get all resources for " + pckgname);
      }

      ArrayList<Class> classes = new ArrayList<Class>();
      // For every directoryFile identified capture all the .class files
      while (!directories.isEmpty()){
          File directoryFile  = directories.remove(0);             
          if (directoryFile.exists()) {
              // Get the list of the files contained in the package
              File[] files = directoryFile.listFiles();

              for (File file : files) {
                  // we are only interested in .class files
                  if ((file.getName().endsWith(".class")) && (!file.getName().contains("$"))) {
                      // removes the .class extension
                      int index = directoryFile.getPath().indexOf(packageToPath);
                      String packagePrefix = directoryFile.getPath().substring(index).replace('/', '.');;                          
                    try {                  
                      String className = packagePrefix + '.' + file.getName().substring(0, file.getName().length() - 6);                            
                      classes.add(Class.forName(className));                                
                    } catch (NoClassDefFoundError e)
                    {
                      // do nothing. this class hasn't been found by the loader, and we don't care.
                    }
                  } else if (file.isDirectory()){ // If we got to a subdirectory
                      directories.add(new File(file.getPath()));                          
                  }
              }
          } else {
              throw new ClassNotFoundException(pckgname + " (" + directoryFile.getPath() + ") does not appear to be a valid package");
          }
      }
      return classes;
  }  

4

Bu sorunu çözen basit bir github projesini bir araya getirdim:

https://github.com/ddopson/java-class-enumerator

Her iki dosya tabanlı sınıf yolu VE jar dosyaları için çalışmalıdır.

Projeyi kontrol ettikten sonra 'make' komutunu çalıştırırsanız, bunu yazdırır:

 Cleaning...
rm -rf build/
 Building...
javac -d build/classes src/pro/ddopson/ClassEnumerator.java src/test/ClassIShouldFindOne.java src/test/ClassIShouldFindTwo.java src/test/subpkg/ClassIShouldFindThree.java src/test/TestClassEnumeration.java
 Making JAR Files...
jar cf build/ClassEnumerator_test.jar -C build/classes/ . 
jar cf build/ClassEnumerator.jar -C build/classes/ pro
 Running Filesystem Classpath Test...
java -classpath build/classes test.TestClassEnumeration
ClassDiscovery: Package: 'test' becomes Resource: 'file:/Users/Dopson/work/other/java-class-enumeration/build/classes/test'
ClassDiscovery: Reading Directory '/Users/Dopson/work/other/java-class-enumeration/build/classes/test'
ClassDiscovery: FileName 'ClassIShouldFindOne.class'  =>  class 'test.ClassIShouldFindOne'
ClassDiscovery: FileName 'ClassIShouldFindTwo.class'  =>  class 'test.ClassIShouldFindTwo'
ClassDiscovery: FileName 'subpkg'  =>  class 'null'
ClassDiscovery: Reading Directory '/Users/Dopson/work/other/java-class-enumeration/build/classes/test/subpkg'
ClassDiscovery: FileName 'ClassIShouldFindThree.class'  =>  class 'test.subpkg.ClassIShouldFindThree'
ClassDiscovery: FileName 'TestClassEnumeration.class'  =>  class 'test.TestClassEnumeration'
 Running JAR Classpath Test...
java -classpath build/ClassEnumerator_test.jar  test.TestClassEnumeration
ClassDiscovery: Package: 'test' becomes Resource: 'jar:file:/Users/Dopson/work/other/java-class-enumeration/build/ClassEnumerator_test.jar!/test'
ClassDiscovery: Reading JAR file: '/Users/Dopson/work/other/java-class-enumeration/build/ClassEnumerator_test.jar'
ClassDiscovery: JarEntry 'META-INF/'  =>  class 'null'
ClassDiscovery: JarEntry 'META-INF/MANIFEST.MF'  =>  class 'null'
ClassDiscovery: JarEntry 'pro/'  =>  class 'null'
ClassDiscovery: JarEntry 'pro/ddopson/'  =>  class 'null'
ClassDiscovery: JarEntry 'pro/ddopson/ClassEnumerator.class'  =>  class 'null'
ClassDiscovery: JarEntry 'test/'  =>  class 'null'
ClassDiscovery: JarEntry 'test/ClassIShouldFindOne.class'  =>  class 'test.ClassIShouldFindOne'
ClassDiscovery: JarEntry 'test/ClassIShouldFindTwo.class'  =>  class 'test.ClassIShouldFindTwo'
ClassDiscovery: JarEntry 'test/subpkg/'  =>  class 'null'
ClassDiscovery: JarEntry 'test/subpkg/ClassIShouldFindThree.class'  =>  class 'test.subpkg.ClassIShouldFindThree'
ClassDiscovery: JarEntry 'test/TestClassEnumeration.class'  =>  class 'test.TestClassEnumeration'
 Tests Passed. 

Ayrıca diğer cevabımı da görün


4

Evet birkaç API kullanarak, işte böyle yapmayı seviyorum, hazırda bekletme çekirdeğini kullandığım ve belirli bir ek açıklama ile açıklamalı sınıfları bulmak zorunda kaldım bu sorunla karşı karşıya kaldım.

Hangi sınıfları almak istediğinizi işaretleyeceğiniz özel bir ek açıklama yapın.

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface EntityToBeScanned {

}

Sonra sınıfınızı bunun gibi işaretleyin

@EntityToBeScanned 
public MyClass{

}

Aşağıdaki yöntemi içeren bu yardımcı program sınıfını yapın

public class ClassScanner {

    public static Set<Class<?>> allFoundClassesAnnotatedWithEntityToBeScanned(){
        Reflections reflections = new Reflections(".*");
        Set<Class<?>> annotated = reflections.getTypesAnnotatedWith(EntityToBeScanned.class);
        return annotated;
    }

}

Bulunan Sınıflar Seti almak için allFoundClassesAnnotatedWithEntityToBeScanned () yöntemini çağırın .

Aşağıda verilen kütüphanelere ihtiyacınız olacak

<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>21.0</version>
    </dependency>
<!-- https://mvnrepository.com/artifact/org.javassist/javassist -->
<dependency>
    <groupId>org.javassist</groupId>
    <artifactId>javassist</artifactId>
    <version>3.22.0-CR1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.reflections/reflections -->
<dependency>
    <groupId>org.reflections</groupId>
    <artifactId>reflections</artifactId>
    <version>0.9.10</version>
</dependency>

3

Sınıf yolundaki her sınıf yükleyici girdisine bakmanız gerekir:

    String pkg = "org/apache/commons/lang";
    ClassLoader cl = ClassLoader.getSystemClassLoader();
    URL[] urls = ((URLClassLoader) cl).getURLs();
    for (URL url : urls) {
        System.out.println(url.getFile());
        File jar = new File(url.getFile());
        // ....
    }   

Giriş dizinse, sağ alt dizine bakın:

if (jar.isDirectory()) {
    File subdir = new File(jar, pkg);
    if (!subdir.exists())
        continue;
    File[] files = subdir.listFiles();
    for (File file : files) {
        if (!file.isFile())
            continue;
        if (file.getName().endsWith(".class"))
            System.out.println("Found class: "
                    + file.getName().substring(0,
                            file.getName().length() - 6));
    }
}   

Giriş dosyaysa ve jar ise, ZIP girişlerini inceleyin:

else {
    // try to open as ZIP
    try {
        ZipFile zip = new ZipFile(jar);
        for (Enumeration<? extends ZipEntry> entries = zip
                .entries(); entries.hasMoreElements();) {
            ZipEntry entry = entries.nextElement();
            String name = entry.getName();
            if (!name.startsWith(pkg))
                continue;
            name = name.substring(pkg.length() + 1);
            if (name.indexOf('/') < 0 && name.endsWith(".class"))
                System.out.println("Found class: "
                        + name.substring(0, name.length() - 6));
        }
    } catch (ZipException e) {
        System.out.println("Not a ZIP: " + e.getMessage());
    } catch (IOException e) {
        System.err.println(e.getMessage());
    }
}

Şimdi tüm sınıf isimlerini paketle birlikte aldıktan sonra, bunları yansıma ile yüklemeyi deneyebilir ve sınıflar veya arayüzler olup olmadığını analiz edebilirsiniz.


Jar dosyasındaki bir pakete ne girersiniz?
Kyle Bridenstine

Bu örnek alt paketlerden geçmeyecektir. Belki bu biraz ilgilendi ... @ mr-tea Sadece aradığınız paketi belirtin. Bunu bir projeye koydum, o proje içinde bir test paketi belirledim, derledim ve paketledim ve JAR'ın ana yöntemini örnek olarak adlandırdım. Bir cazibe gibi çalıştı. :)
Hıristiyan

3

Reflections kütüphanesini kullanmaya çalışıyorum, ancak bunu kullanırken bazı problemler yaşadım ve sadece bir pakette dersleri almak için dahil etmem gereken çok fazla kavanoz vardı.

Bu yinelenen soruda bulduğum bir çözümü göndereceğim : Tüm sınıf isimlerini bir pakete nasıl alabilirim?

Cevap sp00m tarafından yazılmıştır ; Çalışması için bazı düzeltmeler ekledim:

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;

public final class ClassFinder {

    private final static char DOT = '.';
    private final static char SLASH = '/';
    private final static String CLASS_SUFFIX = ".class";
    private final static String BAD_PACKAGE_ERROR = "Unable to get resources from path '%s'. Are you sure the given '%s' package exists?";

    public final static List<Class<?>> find(final String scannedPackage) {
        final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        final String scannedPath = scannedPackage.replace(DOT, SLASH);
        final Enumeration<URL> resources;
        try {
            resources = classLoader.getResources(scannedPath);
        } catch (IOException e) {
            throw new IllegalArgumentException(String.format(BAD_PACKAGE_ERROR, scannedPath, scannedPackage), e);
        }
        final List<Class<?>> classes = new LinkedList<Class<?>>();
        while (resources.hasMoreElements()) {
            final File file = new File(resources.nextElement().getFile());
            classes.addAll(find(file, scannedPackage));
        }
        return classes;
    }

    private final static List<Class<?>> find(final File file, final String scannedPackage) {
        final List<Class<?>> classes = new LinkedList<Class<?>>();
        if (file.isDirectory()) {
            for (File nestedFile : file.listFiles()) {
                classes.addAll(find(nestedFile, scannedPackage));
            }
        //File names with the $1, $2 holds the anonymous inner classes, we are not interested on them. 
        } else if (file.getName().endsWith(CLASS_SUFFIX) && !file.getName().contains("$")) {

            final int beginIndex = 0;
            final int endIndex = file.getName().length() - CLASS_SUFFIX.length();
            final String className = file.getName().substring(beginIndex, endIndex);
            try {
                final String resource = scannedPackage + DOT + className;
                classes.add(Class.forName(resource));
            } catch (ClassNotFoundException ignore) {
            }
        }
        return classes;
    }

}

Kullanmak için sadece bu örnekte belirtilen sp00n olarak find yöntemini çağırın: Gerekirse sınıfların örneklerinin oluşturulmasını ekledim.

List<Class<?>> classes = ClassFinder.find("com.package");

ExcelReporting excelReporting;
for (Class<?> aClass : classes) {
    Constructor constructor = aClass.getConstructor();
    //Create an object of the class type
    constructor.newInstance();
    //...
}

3

Ben sadece bir util sınıfı yazdım, test yöntemleri içerir, bir çek olabilir ~

IteratePackageUtil.java:

package eric.j2se.reflect;

import java.util.Set;

import org.reflections.Reflections;
import org.reflections.scanners.ResourcesScanner;
import org.reflections.scanners.SubTypesScanner;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;
import org.reflections.util.FilterBuilder;

/**
 * an util to iterate class in a package,
 * 
 * @author eric
 * @date Dec 10, 2013 12:36:46 AM
 */
public class IteratePackageUtil {
    /**
     * <p>
     * Get set of all class in a specified package recursively. this only support lib
     * </p>
     * <p>
     * class of sub package will be included, inner class will be included,
     * </p>
     * <p>
     * could load class that use the same classloader of current class, can't load system packages,
     * </p>
     * 
     * @param pkg
     *            path of a package
     * @return
     */
    public static Set<Class<? extends Object>> getClazzSet(String pkg) {
        // prepare reflection, include direct subclass of Object.class
        Reflections reflections = new Reflections(new ConfigurationBuilder().setScanners(new SubTypesScanner(false), new ResourcesScanner())
                .setUrls(ClasspathHelper.forClassLoader(ClasspathHelper.classLoaders(new ClassLoader[0])))
                .filterInputsBy(new FilterBuilder().includePackage(pkg)));

        return reflections.getSubTypesOf(Object.class);
    }

    public static void test() {
        String pkg = "org.apache.tomcat.util";

        Set<Class<? extends Object>> clazzSet = getClazzSet(pkg);
        for (Class<? extends Object> clazz : clazzSet) {
            System.out.println(clazz.getName());
        }
    }

    public static void main(String[] args) {
        test();
    }
}

3

Aleksander Blomskøld'un çözümü@RunWith(Parameterized.class) Maven kullanırken parametreli testler için benim için çalışmadı . Testler doğru bir şekilde adlandırılmış ve aynı zamanda bulunmuş ancak yürütülmemiştir:

-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running some.properly.named.test.run.with.maven.SomeTest
Tests run: 0, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.123 sec

Benzer bir sorun da burada bildirildi .

Benim durumumda @Parametersbir pakette her sınıfın örnekleri oluşturmak. IDE'de yerel olarak yürütüldüğünde testler iyi sonuç verdi. Ancak, Maven'i çalıştırırken Aleksander Blomskøld'un çözümü ile hiçbir sınıf bulunamadı.

David Pärsson'un Aleksander Blomskøld'un cevabı hakkındaki yorumundan esinlenen aşağıdaki snip ile çalıştım:

Reflections reflections = new Reflections(new ConfigurationBuilder()
            .setScanners(new SubTypesScanner(false /* don't exclude Object.class */), new ResourcesScanner())
            .addUrls(ClasspathHelper.forJavaClassPath()) 
            .filterInputsBy(new FilterBuilder()
            .include(FilterBuilder.prefix(basePackage))));

Set<Class<?>> subTypesOf = reflections.getSubTypesOf(Object.class);

3

Peki buna ne dersin:

public static List<Class<?>> getClassesForPackage(final String pkgName) throws IOException, URISyntaxException {
    final String pkgPath = pkgName.replace('.', '/');
    final URI pkg = Objects.requireNonNull(ClassLoader.getSystemClassLoader().getResource(pkgPath)).toURI();
    final ArrayList<Class<?>> allClasses = new ArrayList<Class<?>>();

    Path root;
    if (pkg.toString().startsWith("jar:")) {
        try {
            root = FileSystems.getFileSystem(pkg).getPath(pkgPath);
        } catch (final FileSystemNotFoundException e) {
            root = FileSystems.newFileSystem(pkg, Collections.emptyMap()).getPath(pkgPath);
        }
    } else {
        root = Paths.get(pkg);
    }

    final String extension = ".class";
    try (final Stream<Path> allPaths = Files.walk(root)) {
        allPaths.filter(Files::isRegularFile).forEach(file -> {
            try {
                final String path = file.toString().replace('/', '.');
                final String name = path.substring(path.indexOf(pkgName), path.length() - extension.length());
                allClasses.add(Class.forName(name));
            } catch (final ClassNotFoundException | StringIndexOutOfBoundsException ignored) {
            }
        });
    }
    return allClasses;
}

Daha sonra işlevi aşırı yükleyebilirsiniz:

public static List<Class<?>> getClassesForPackage(final Package pkg) throws IOException, URISyntaxException {
    return getClassesForPackage(pkg.getName());
}

Test etmeniz gerekiyorsa:

public static void main(final String[] argv) throws IOException, URISyntaxException {
    for (final Class<?> cls : getClassesForPackage("my.package")) {
        System.out.println(cls);
    }
    for (final Class<?> cls : getClassesForPackage(MyClass.class.getPackage())) {
        System.out.println(cls);
    }
}

IDE'nizde içe aktarma yardımcısı yoksa:

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.FileSystemNotFoundException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;

İşe yarıyor:

  • IDE'nizden

  • JAR dosyası için

  • harici bağımlılıklar olmadan


2

Hemen hemen tüm cevaplar Reflectionsdosya sistemindeki sınıf dosyalarını kullanır veya okur. Sınıfları dosya sisteminden okumaya çalışırsanız, uygulamanızı JAR veya başka bir paket olarak paketlediğinizde hata alabilirsiniz. Ayrıca bu amaçla ayrı bir kütüphane kullanmak istemeyebilirsiniz.

İşte saf java olan ve dosya sistemine bağlı olmayan başka bir yaklaşım.

import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

public class PackageUtil {

    public static Collection<Class> getClasses(final String pack) throws Exception {
        final StandardJavaFileManager fileManager = ToolProvider.getSystemJavaCompiler().getStandardFileManager(null, null, null);
        return StreamSupport.stream(fileManager.list(StandardLocation.CLASS_PATH, pack, Collections.singleton(JavaFileObject.Kind.CLASS), false).spliterator(), false)
                .map(javaFileObject -> {
                    try {
                        final String[] split = javaFileObject.getName()
                                .replace(".class", "")
                                .replace(")", "")
                                .split(Pattern.quote(File.separator));

                        final String fullClassName = pack + "." + split[split.length - 1];
                        return Class.forName(fullClassName);
                    } catch (ClassNotFoundException e) {
                        throw new RuntimeException(e);
                    }

                })
                .collect(Collectors.toCollection(ArrayList::new));
    }
}

Java 8 bir zorunluluk değildir . Akışlar yerine döngüler için kullanabilirsiniz. Ve bunu böyle test edebilirsiniz

public static void main(String[] args) throws Exception {
    final String pack = "java.nio.file"; // Or any other package
    PackageUtil.getClasses(pack).stream().forEach(System.out::println);
}

1
Aşağıdakiler nedeniyle çok yararlı değildir: JDK kullanmak gerekir ToolProvider.getSystemJavaCompiler(), bu kod iç içe paketleri taramaz.
v.ladynev

Harici bir kavanoz paketiyle çalıştıramıyorum
Enrico Giurin

1

Herhangi bir dinamik sınıf yükleyici kullanmamanız koşuluyla, sınıf yolunu arayabilir ve her giriş için dizinde veya JAR dosyasında arama yapabilirsiniz.


1

Bahsetmeye değer

Bir paketin altındaki tüm sınıfların bir listesini almak istiyorsanız Reflection, aşağıdaki yolu kullanabilirsiniz :

List<Class> myTypes = new ArrayList<>();

Reflections reflections = new Reflections("com.package");
for (String s : reflections.getStore().get(SubTypesScanner.class).values()) {
    myTypes.add(Class.forName(s));
}

Bu, daha sonra dilediğiniz gibi kullanabileceğiniz sınıfların bir listesini oluşturur.


1

Bu çok mümkündür, ama Reflectionsbunun gibi ek kütüphaneler olmadan zordur ...
Zordur, çünkü sınıf adını almak için tam bir enstrümana sahip değilsiniz.
Ve ClassFindersınıfımın kodunu alıyorum :

package play.util;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * Created by LINKOR on 26.05.2017 in 15:12.
 * Date: 2017.05.26
 */
public class FileClassFinder {
private JarFile file;
private boolean trouble;
public FileClassFinder(String filePath) {
    try {
        file = new JarFile(filePath);
    } catch (IOException e) {
        trouble = true;
    }
}

public List<String> findClasses(String pkg) {
    ArrayList<String> classes = new ArrayList<>();
    Enumeration<JarEntry> entries = file.entries();
    while (entries.hasMoreElements()) {
        JarEntry cls = entries.nextElement();
        if (!cls.isDirectory()) {
            String fileName = cls.getName();
            String className = fileName.replaceAll("/",         ".").replaceAll(File.pathSeparator, ".").substring(0, fileName.lastIndexOf('.'));
            if (className.startsWith(pkg)) classes.add(className.substring(pkg.length() + 1));
        }
    }
    return classes;
}
}

0

@ Staale'in cevabına dayanarak ve üçüncü taraf kitaplıklarına güvenmemeye çalışırken, ilk paket fiziksel konumunu aşağıdakilerle inceleyerek Dosya Sistemi yaklaşımını uygularım:

import java.io.File;
import java.io.FileFilter;
import java.util.ArrayList;
...
Class<?>[] foundClasses = new Class<?>[0];
final ArrayList<Class<?>> foundClassesDyn = new ArrayList<Class<?>>();

new java.io.File(
    klass.getResource(
        "/" + curPackage.replace( "." , "/")
    ).getFile()
).listFiles(
    new java.io.FileFilter() {
        public boolean accept(java.io.File file) {
            final String classExtension = ".class";

            if ( file.isFile()
                && file.getName().endsWith(classExtension)
                // avoid inner classes
                && ! file.getName().contains("$") )
            {
                try {
                    String className = file.getName();
                    className = className.substring(0, className.length() - classExtension.length());
                    foundClassesDyn.add( Class.forName( curPackage + "." + className ) );
                } catch (ClassNotFoundException e) {
                    e.printStackTrace(System.out);
                }
            }

            return false;
        }
    }
);

foundClasses = foundClassesDyn.toArray(foundClasses);

0

Sadece bir grup ilgili sınıfı yüklemek istiyorsanız, Bahar size yardımcı olabilir.

Spring, belirli bir kod satırında belirli bir arabirimi uygulayan tüm sınıfların bir listesini veya haritasını başlatabilir. Liste veya harita, bu arabirimi uygulayan tüm sınıfların örneklerini içerecektir.

Bununla birlikte, sınıf listesini dosya sisteminden yüklemeye alternatif olarak, paketten bağımsız olarak yüklemek istediğiniz tüm sınıflara aynı arayüzü uygulayın ve hepsinin örneklerini sunmak için Spring'i kullanın. Bu şekilde, hangi pakette olduklarına bakılmaksızın, istediğiniz tüm sınıfları yükleyebilir (ve başlatabilirsiniz).

Öte yandan, hepsini bir pakette bulundurmak istediğiniz şeyse, o paketteki tüm sınıfların belirli bir arabirimi uygulamasını sağlayın.


0

plain java: FindAllClassesUsingPlainJavaReflectionTest.java

@Slf4j
class FindAllClassesUsingPlainJavaReflectionTest {

  private static final Function<Throwable, RuntimeException> asRuntimeException = throwable -> {
    log.error(throwable.getLocalizedMessage());
    return new RuntimeException(throwable);
  };

  private static final Function<String, Collection<Class<?>>> findAllPackageClasses = basePackageName -> {

    Locale locale = Locale.getDefault();
    Charset charset = StandardCharsets.UTF_8;
    val fileManager = ToolProvider.getSystemJavaCompiler()
                                  .getStandardFileManager(/* diagnosticListener */ null, locale, charset);

    StandardLocation location = StandardLocation.CLASS_PATH;
    JavaFileObject.Kind kind = JavaFileObject.Kind.CLASS;
    Set<JavaFileObject.Kind> kinds = Collections.singleton(kind);
    val javaFileObjects = Try.of(() -> fileManager.list(location, basePackageName, kinds, /* recurse */ true))
                             .getOrElseThrow(asRuntimeException);

    String pathToPackageAndClass = basePackageName.replace(".", File.separator);
    Function<String, String> mapToClassName = s -> {
      String prefix = Arrays.stream(s.split(pathToPackageAndClass))
                            .findFirst()
                            .orElse("");
      return s.replaceFirst(prefix, "")
              .replaceAll(File.separator, ".");
    };

    return StreamSupport.stream(javaFileObjects.spliterator(), /* parallel */ true)
                        .filter(javaFileObject -> javaFileObject.getKind().equals(kind))
                        .map(FileObject::getName)
                        .map(fileObjectName -> fileObjectName.replace(".class", ""))
                        .map(mapToClassName)
                        .map(className -> Try.of(() -> Class.forName(className))
                                             .getOrElseThrow(asRuntimeException))
                        .collect(Collectors.toList());
  };

  @Test
  @DisplayName("should get classes recursively in given package")
  void test() {
    Collection<Class<?>> classes = findAllPackageClasses.apply(getClass().getPackage().getName());
    assertThat(classes).hasSizeGreaterThan(4);
    classes.stream().map(String::valueOf).forEach(log::info);
  }
}

PS: hataları, vb işleme için kazan plakaları basitleştirmek için, ben burada vavrve lombokkütüphaneler kullanıyorum

diğer uygulamalar GitHub daggerok / java-yansıma-bul-ek açıklama-sınıflar-veya-yöntemler repo'mda bulunabilir


0

Bu kadar basit bir şey için kısa bir çalışma bulamadık. İşte burada, bir süre vidaladıktan sonra kendim yaptım:

    Reflections reflections =
        new Reflections(new ConfigurationBuilder()
                .filterInputsBy(new FilterBuilder().includePackage(packagePath))
                .setUrls(ClasspathHelper.forPackage(packagePath))
                .setScanners(new SubTypesScanner(false)));

    Set<String> typeList = reflections.getAllTypes(); 

0

Bahar arazisindeyseniz kullanabilirsiniz PathMatchingResourcePatternResolver;

  PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
  Resource[] resources = resolver.getResources("classpath*:some/package/name/*.class");

    Arrays.asList(resources).forEach(r->{
        ...
    });

-4

Her zaman bir sınıfın paketini bilirken, paketteki tüm sınıflar yüklenmeyebilir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.