سؤال

ودعونا نقول لدي حزمة commands جافا الذي يحتوي على الفئات التي ترث كل من ICommand يمكنني الحصول على كل هذه الفئات بطريقة أو بأخرى؟ أنا تأمين عن شيء بين السطور من:

Package p = Package.getPackage("commands");
Class<ICommand>[] c = p.getAllPackagedClasses(); //not real 

هل شيء من هذا القبيل ممكن؟

هل كانت مفيدة؟

المحلول

وإليك مثال الأساسي، على افتراض أن الطبقات ليست المعبأة جرة:

// Prepare.
String packageName = "com.example.commands";
List<Class<ICommand>> commands = new ArrayList<Class<ICommand>>();
URL root = Thread.currentThread().getContextClassLoader().getResource(packageName.replace(".", "/"));

// Filter .class files.
File[] files = new File(root.getFile()).listFiles(new FilenameFilter() {
    public boolean accept(File dir, String name) {
        return name.endsWith(".class");
    }
});

// Find classes implementing ICommand.
for (File file : files) {
    String className = file.getName().replaceAll(".class$", "");
    Class<?> cls = Class.forName(packageName + "." + className);
    if (ICommand.class.isAssignableFrom(cls)) {
        commands.add((Class<ICommand>) cls);
    }
}

نصائح أخرى

وفيما يلي، تنفيذا باستخدام API JSR-199 أي فئات من javax.tools.*:

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

JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(
        null, null, null);

Location location = StandardLocation.CLASS_PATH;
String packageName = "commands";
Set<JavaFileObject.Kind> kinds = new HashSet<JavaFileObject.Kind>();
kinds.add(JavaFileObject.Kind.CLASS);
boolean recurse = false;

Iterable<JavaFileObject> list = fileManager.list(location, packageName,
        kinds, recurse);

for (JavaFileObject javaFileObject : list) {
    commands.add(javaFileObject.getClass());
}

وهنا هو وسيلة فائدة، وذلك باستخدام الربيع.

ويمكن الاطلاع على تفاصيل حول نمط <لأ href = "http://static.springsource.org/spring/docs/2.5.6/api/org/springframework/core/io/support/PathMatchingResourcePatternResolver.html" يختلط = "نوفولو noreferrer"> هنا

    public static List<Class> listMatchingClasses(String matchPattern) throws IOException {
    List<Class> classes = new LinkedList<Class>();
    PathMatchingResourcePatternResolver scanner = new PathMatchingResourcePatternResolver();
    Resource[] resources = scanner.getResources(matchPattern);

    for (Resource resource : resources) {
        Class<?> clazz = getClassFromResource(resource);
        classes.add(clazz);
    }

    return classes;
}



public static Class getClassFromResource(Resource resource) {
    try {
        String resourceUri = resource.getURI().toString();
        resourceUri = resourceUri.replace(esourceUri.indexOf(".class"), "").replace("/", ".");
        // try printing the resourceUri before calling forName, to see if it is OK.
        return Class.forName(resourceUri);
    } catch (Exception ex) {
        ex.printStackTrace();
    }
    return null;
}

وتبدأ Classloader.getResources الجمهور (اسم سلسلة). طرح سؤال على classloader لفئة المقابلة لكل اسم في حزمة كنت مهتما. أكرر للجميع classloaders ذات الصلة.

ونعم ولكن ليس أسهل شيء لتفعله. هناك الكثير من القضايا مع هذا. ليس كل الطبقات من السهل العثور عليها. يمكن أن تكون بعض الطبقات في: جرة، وملف فئة، عبر الشبكة الخ

ونلقي نظرة في هذا الموضوع.

لتأكد من أنها كانت نوع ICommand ثم سيكون لديك لاستخدام انعكاس للتحقق من الطبقة راثة.

وهذا من شأنه أن يكون أداة مفيدة جدا نحتاج إليه، ويجب أن JDK تقديم بعض الدعم.

ولكن ربما يتم ذلك بشكل أفضل خلال بناء. تعرف أين هي كل ملفات فئة الخاص بك ويمكنك تفتيشها بشكل ثابت وبناء الرسم البياني. في وقت التشغيل يمكنك الاستعلام هذا الرسم البياني للحصول على كل أنواع فرعية. وهذا يتطلب المزيد من العمل، ولكن أعتقد أنه ينتمي حقا إلى عملية الإنشاء.

يوهانس لينك ClasspathSuite ، وكنت قادرا على القيام بذلك مثل هذا:

import org.junit.extensions.cpsuite.ClassTester;
import org.junit.extensions.cpsuite.ClasspathClassesFinder;

public static List<Class<?>> getClasses(final Package pkg, final boolean includeChildPackages) {
    return new ClasspathClassesFinder(new ClassTester() {
        @Override public boolean searchInJars() { return true; }
        @Override public boolean acceptInnerClass() { return false; }
        @Override public boolean acceptClassName(String name) {
            return name.startsWith(pkg.getName()) && (includeChildPackages || name.indexOf(".", pkg.getName().length()) != -1);
        }
        @Override public boolean acceptClass(Class<?> c) { return true; }
    }, System.getProperty("java.class.path")).find();
}

ووClasspathClassesFinder بالبحث عن ملفات فئة والجرار في classpath النظام.

في حالة معينة، هل يمكن تعديل acceptClass مثل هذا:

@Override public boolean acceptClass(Class<?> c) {
    return ICommand.class.isAssignableFrom(c);
}

وشيء واحد ملاحظة: كن حذرا ما يعود في acceptClassName، والشيء التالي ClasspathClassesFinder يفعله هو لتحميل الطبقة، وندعو acceptClass. إذا acceptClassName العودة دائما صحيحا، فسوف ينتهي تحميل كل فئة في classpath والتي قد تسبب OutOfMemoryError.

هل يمكن استخدام OpenPojo و قيام بذلك:

final List<PojoClass> pojoClasses = PojoClassFactory.getPojoClassesRecursively("my.package.path", null);

وبعد ذلك يمكن أن تذهب أكثر من قائمة وأداء أي وظيفة تريدها.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top