প্রতিবিম্ব ব্যবহার করে আপনি কি কোনও প্যাকেজে সমস্ত ক্লাস খুঁজে পেতে পারেন?


527

কোনও প্রদত্ত প্যাকেজে সমস্ত শ্রেণি বা ইন্টারফেস পাওয়া সম্ভব? (তাড়াতাড়ি উদাহরণস্বরূপ তাকানো Package, এটি মনে হবে না।)


2
FYI সমাধানটি অমিতের কাজের সাথে লিঙ্ক করে, যদিও এটিতে একটি বাগ রয়েছে যদি শ্রেণীর পাথের মধ্যে একটি স্পেস অক্ষর থাকে (এবং সম্ভবত অন্যান্য অ-অক্ষরীয় অক্ষরের জন্যও)। আপনি যদি এটিকে কোনও প্রকারের কোড কোডে ব্যবহার করেন তবে কাজের উত্তরটির জন্য তার উত্তর সম্পর্কে আমার মন্তব্য দেখুন।
কিপ

2

1
সম্পর্কিত উত্তর দেখুন: stackoverflow.com/a/30149061/4102160
সিএফএক্স

1
এই পোস্টটি নোট করুন ।
sp00m

1
ক্লাসগ্রাফ সম্পর্কে আমার উত্তর নীচে দেখুন, বর্তমানে এটি ক্লাসপথ এবং মডিউল পাথ স্ক্যান করার জন্য সবচেয়ে শক্তিশালী পদ্ধতি।
লুক হাচিসন

উত্তর:


373

শ্রেণি লোডারগুলির গতিশীল প্রকৃতির কারণে এটি সম্ভব নয়। ক্লাস লোডাররা ভিএম কে কোন ক্লাস সরবরাহ করতে পারে তা জানাতে হবে না, পরিবর্তে তারা ক্লাসগুলির জন্য কেবল অনুরোধ হ'ল, এবং একটি ক্লাস ফিরে আসতে হবে বা একটি ব্যতিক্রম নিক্ষেপ করতে হবে।

তবে, আপনি যদি নিজের শ্রেণীর লোডারগুলি লিখেন, বা ক্লাসপাথগুলি এবং এর জারগুলি পরীক্ষা করেন তবে এই তথ্যটি পাওয়া সম্ভব। এটি ফাইল সিস্টেম অপারেশনগুলির মাধ্যমে হবে, এবং প্রতিবিম্ব নয়। এমনকি এমন লাইব্রেরিও থাকতে পারে যা আপনাকে এটি করতে সহায়তা করতে পারে।

যদি এমন কোনও ক্লাস থাকে যা উত্পন্ন হয়, বা দূরবর্তীভাবে সরবরাহ করা হয় তবে আপনি এই ক্লাসগুলি আবিষ্কার করতে পারবেন না।

সাধারণ পদ্ধতিটি কোথাও কোথাও আপনার কোনও ফাইলের অ্যাক্সেসের জন্য প্রয়োজনীয় ক্লাসগুলি রেজিস্ট্রেশন করা বা তাদের আলাদা ক্লাসে রেফারেন্স করা। বা নামকরণের ক্ষেত্রে কেবল কনভেনশনটি ব্যবহার করুন।

সংযোজন: রিফ্লেকশন লাইব্রেরি আপনাকে বর্তমান ক্লাসপথে ক্লাস সন্ধান করার অনুমতি দেবে। এটি একটি প্যাকেজে সমস্ত ক্লাস পেতে ব্যবহার করা যেতে পারে:

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

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

12
শ্রেণীর নামগুলির জন্য জিজ্ঞাসা করতে অক্ষমতা আমাকে দীর্ঘদিন ধরে বাধা দিয়েছে। অবশ্যই, এটি কঠোর এবং পারফরম্যান্স ব্যাপকভাবে পরিবর্তিত হতে পারে এবং নির্দিষ্ট শ্রেণিবদ্ধদের জন্য তালিকাটি অপরিজ্ঞাত বা আনবাউন্ডেড নয়, তবে এই উপায়গুলি প্রায়শই কাজ করা যেতে পারে।
মিঃ শাইন এবং নতুন 安 宇

16
নোট করুন যে এই সমাধানটি ডিফল্ট হিসাবে getSubTypesOf হিসাবে অবজেক্টের সাব টাইপগুলি ফেরত দেয় না হিসাবে কাজ করবে না। সাবটাইপস্ক্যানার কীভাবে কনফিগার করতে হয় তার জন্য আলেকসান্দার ব্লুমস্কেল্ডের সমাধান দেখুন।
অ্যালেক্স স্পার্লিং

17
প্রতিচ্ছবি পেয়ারা প্রয়োজন। পেয়ারা বড়। 14.0.1 সংস্করণটি 2.1MB।
মাইক জোনে

3
আমার পক্ষে কাজ করেনি। ম্যাক ওএসএক্স - প্রতিচ্ছবি নির্ভরতা সংস্করণ 0.9.9-আরসি 1 (মার্ভেন) - জেডিকে 1.7। গৃহীত উত্তর পুনর্বিবেচনা করুন। @ আলেকসান্দার ব্লমস্কেল্ড উত্তরটিই যাবেন !!!! !!!!!
কনস্ট্যান্টিনোস মার্জারাইটিস

68
যদি এটি একটি খালি তালিকা ফেরত দেয় তবে প্রতিচ্ছবিগুলির এই বিষয়টিকে আরম্ভ করুন: প্রতিচ্ছবি প্রতিফলন = নতুন প্রতিচ্ছবি ("আপনার.প্যাকেজ.এইয়ার", নতুন সাবটাইপসস্ক্যানার (মিথ্যা));
জোও রোচা দা সিলভা

186

আপনার সম্ভবত উন্মুক্ত উত্স প্রতিচ্ছবি গ্রন্থাগারটি একবার দেখে নেওয়া উচিত । এটি দিয়ে আপনি যা চান সহজেই তা অর্জন করতে পারেন।

প্রথমত, প্রতিচ্ছবি সূচকটি সেটআপ করুন (ডিফল্টরূপে সমস্ত শ্রেণীর সন্ধান করা অক্ষম হওয়ার কারণে এটি কিছুটা অগোছালো):

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"))));

তারপরে আপনি প্রদত্ত প্যাকেজে সমস্ত বস্তুর অনুসন্ধান করতে পারেন:

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

6
আহা, আমরা এখানে যাচ্ছি: code.google.com/p/reflections/issues/detail?id=122 । অবজেক্টটি ডিফল্টরূপে বাদ দেওয়া হয় তবে আপনি এটি আবার নতুন করে তৈরি করতে পারেন। আমাকে এই লাইব্রেরিতে দেখানোর জন্য ধন্যবাদ, এটি দুর্দান্ত!
এমআরটিসি

1
আমি এই কোডটি দিয়ে আমার ম্যাকের সমস্যায় পড়েছি (নেটিভ লাইব্রেরিগুলির সাথে সম্পর্কিত), তবে .addUrls(ClasspathHelper.forJavaClassPath())উপরের পরিবর্তে ব্যবহার করে আমার জন্য সেগুলি সমাধান করে। পাশাপাশি কম কোড!
ডেভিড পারসন

3
যদি কেউ ডিফল্ট প্যাকেজটি পাওয়ার সহজতম উপায়টির উপসর্গটি খালি স্ট্রিং -> "" থাকে তবে তা অবাক হয়।
জেবিএ

2
"ভাবনা" গ্রন্থাগার একটি চতুর লাইসেন্স রয়েছে: github.com/ronmamo/reflections/blob/master/COPYING.txt । কৌশলটি হ'ল লাইসেন্সটি কেবল নিজেরাই লাইসেন্সের বিনামূল্যে ব্যবহারের অনুমতি দেয়। সুতরাং লাইব্রেরিটি সত্যই ব্যবহার করতে (লাইসেন্স নয়) প্রত্যেককে অবশ্যই লেখকের সাথে যোগাযোগ করতে হবে এবং ব্যবহারের শর্তাদি আলোচনা করতে হবে।
সার্জ রোগাচ

3
সার্জ, আমি মনে করি আপনি ডাব্লুটিএফপিএলকে ভুল বুঝেছেন: wtfpl.net আমার মনে হয় ডাব্লুটিএফপিএল এর অর্থ আপনি কেবল লাইসেন্স দিয়ে নয়
কোডটিও

122

গুগল পেয়ারা ১৪ ClassPathশীর্ষ স্তরের শ্রেণির জন্য স্ক্যান করার জন্য তিনটি পদ্ধতি সহ একটি নতুন শ্রেণি অন্তর্ভুক্ত করেছে:

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

আরও তথ্যের জন্য ClassPathজাভাডোকগুলি দেখুন ।


এটি আমার জন্য কাজ করেছিল যেখানে প্রতিচ্ছবি করতে পারে না (কোনও সাধারণ প্রত্যক্ষ পূর্বপুরুষ, কোনও সাধারণ টিকা নেই)
রিকার্ডো কোসু

1
আমি নীচের মন্তব্যে যেমন উল্লেখ করেছি , এর ClassPathসাথে ট্যাগ করা হয়েছে @Beta, তাই কারও কারও পক্ষে ভাল ধারণা নাও হতে পারে ...
খ্রিস্টান

1
প্রতিচ্ছবি না করে যেখানে এটি কাজ করে এটি কিছুটা বিস্ময়কর বলে, সমাধানটি নিঃসন্দেহে প্রতিবিম্ব (এবং শ্রেণি লোডার) কার্যকারিতা ব্যবহার করে প্রয়োগ করা হয়।
মার্টেন বোদেউয়েস

5
আমি মনে করি তিনি অন্য উত্তরে উল্লিখিত প্রতিচ্ছবি গ্রন্থাগারটি বোঝাতে চেয়েছিলেন।
ক্রিস্টোফ লেইটার

জাভা 11 এর অধীনে কাজ করে, যদি পেয়ারা সংস্করণ 28.1-jre ব্যবহার করা হয়।
গর্জনজ

111

আপনি এই পদ্ধতিটি 1 ব্যবহার করতে পারেন যা ব্যবহার করে 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 এই পদ্ধতিটি মূলত http://snippets.dzone.com/posts/show/4831 থেকে নেওয়া হয়েছিল , যা ছিল এখনকার সাথে সংযুক্ত হিসাবে ইন্টারনেট সংরক্ষণাগার দ্বারা সংরক্ষণাগারভুক্ত হয়েছিল। স্নিপেটটি https://dzone.com/articles/get-all-classes-within-package এ উপলব্ধ ।


6
আমার পথে যদি জায়গাটি অন্তর্ভুক্ত করে তবে আমার এ নিয়ে সমস্যা ছিল। ইউআরএল বর্গটি ফাঁকা স্থান থেকে পালাচ্ছিল %20, তবে নির্মাতা new File()আচরণ করেছিলেন যে আক্ষরিক শতাংশ হিসাবে দুটি শূন্যের চিহ্ন sign আমি পরিবর্তন করে এটি সংশোধন করা হয়েছে dirs.add(...)এই লাইন: dirs.add(new File(resource.toURI())); এই বোঝানো আমি যোগ করতে ছিল URISyntaxExceptionকরা দফা ছোঁড়ারgetClasses
কিপ

19
আপনি স্রেফ dzone.com/articles/get-all-class-within-package থেকে অনুলিপি করেছেন ! দয়া করে পরের বার উত্সটি দেখুন
আরএস

19
+1 কারণ এই সমাধানটির জন্য বাহ্যিক লাইব্রেরিগুলির প্রয়োজন হয় না ... সত্যিকারের মতো একটি ছোট জিনিস অর্জনের জন্য লাইব্রেরিগুলির সাথে আপনার কোড এলোমেলোভাবে কখনও জোড়া লাগবে না। আপনি কী জানেন যে আপনি আক্রমণকারীদের জন্য সম্ভাব্য আক্রমণ পৃষ্ঠ যুক্ত করছেন? নভেম্বর 2015 এপাচি কমন্স সমস্যাটি আবিষ্কার করেছে যা কেবলমাত্র জবাবাস / ওয়েবলোগিক [ foxglovesecurity.com/2015/11/06/…-
sc0p

@ কুইক্স সঠিকভাবে উল্লেখ করেছে যে এই কোডটি জার সমর্থন করে না। জার এবং ডিরেক্টরি সমর্থন করার জন্য । কোডটি নীচে উল্লিখিত হিসাবে পরিবর্তন করা হয়েছে:
ব্যবহারকারীর 1523177

1
ভাল সমাধান, তবে 'Class.forName (স্ট্রিং ক্লাসনাম)' এর পরিবর্তে 'Class.forName (স্ট্রিং ক্লাসনেম, বুলিয়ান ইনিশিয়ালাইজ, ক্লাসলোডার লোডার)' যেখানে 'ইনিশিয়ালাইজ = মিথ্যা;' যাতে শ্রেণীর দৃষ্টান্ত তৈরি না হয়।
Andrei_N

99

বসন্ত

এই উদাহরণটি স্প্রিং 4 এর জন্য, তবে আপনি পূর্ববর্তী সংস্করণগুলিতে ক্লাসপাথ স্ক্যানারটিও খুঁজে পেতে পারেন।

// 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 ...
}

গুগল পেয়ারা

দ্রষ্টব্য: 14 সংস্করণে, API টি এখনও @ বিটা হিসাবে চিহ্নিত হয়েছে , সুতরাং উত্পাদন কোডে সাবধান থাকুন।

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
দুর্দান্ত উত্তর। এখানে প্রচুর সমাধান রয়েছে যা ভার্ভোজ, অ-পরীক্ষা করা, অ-কর্মহীন! এটি চমত্কার: এটি সংক্ষিপ্ত এবং পরীক্ষিত (এটি পেয়ারা থেকে এসেছে)। খুব ভালো! এটি দরকারী, এটি আরও উন্নয়নের দাবিদার।
জিনওয়ালজিয়ান

দুর্ভাগ্যক্রমে, ClassPathপেয়ারাতে ক্লাসটি @Betaএটির সাথেও চিহ্নিত রয়েছে : "ক্লাস বা পদ্ধতি স্তরে @ বিটা টীকা দিয়ে চিহ্নিত এপিআইগুলি পরিবর্তন সাপেক্ষে They এগুলি যে কোনও বড় আকারে ছাড়ানো যেতে পারে, এমনকি মুছে ফেলা যায় can যদি আপনার কোড থাকে এটি একটি লাইব্রেরি নিজেই (যেমন এটি আপনার নিজের নিয়ন্ত্রণের বাইরের ব্যবহারকারীদের ক্লাসস্প্যাটে ব্যবহার করা হয়), আপনি বিটা এপিআই ব্যবহার করবেন না, যদি না আপনি সেগুলি পুনরায় ত্যাগ
খ্রিস্টান

ক্রিশ্চিয়ান ভালো পয়েন্ট, আমি লক্ষ্য করি না! ধন্যবাদ. আমি স্প্রিং ক্লাসপাথ স্ক্যানার ব্যবহার করে আরও একটি উত্তর যুক্ত করব, এটি নিশ্চিতভাবে বিটা নয়।
ভোহো

পেয়ারা দ্রবণ ব্যবহার করে নেস্টেড স্ট্যাটিক ক্লাসগুলি অনুসন্ধান getAllClasses()করতে , পদ্ধতিটি ব্যবহার করা যেতে পারে।
v.ladynev

1
এক্সিকিউটেবল জার থেকে চালিত হলে কেবল বসন্ত সমাধানটি কাজ করে।
লুক

38

হ্যালো. উপরের সমাধানগুলি (এবং অন্যান্য সাইটে) নিয়ে আমার সর্বদা কিছু সমস্যা ছিল।
আমি, একজন বিকাশকারী হিসাবে, একটি এপিআইয়ের জন্য একটি অ্যাডন প্রোগ্রামিং করছি। এপিআই কোনও বাহ্যিক গ্রন্থাগার বা তৃতীয় পক্ষের সরঞ্জাম ব্যবহার আটকাচ্ছে। সেটআপটিতে জার বা জিপ ফাইল এবং কিছু ডিরেক্টরিতে সরাসরি ক্লাস ফাইলগুলিতে কোডের মিশ্রণ থাকে। সুতরাং আমার কোডটি প্রতিটি সেটআপের চারপাশে কাজ করতে সক্ষম হতে হয়েছিল। অনেক গবেষণার পরে আমি একটি পদ্ধতি নিয়ে এসেছি যা কমপক্ষে 95% সমস্ত সম্ভাব্য সেটআপে কাজ করবে।

নিম্নলিখিত কোডটি মূলত ওভারকিল পদ্ধতি যা সর্বদা কার্যকর হবে।

কোড:

এই কোডটি এতে অন্তর্ভুক্ত সমস্ত শ্রেণীর জন্য একটি প্রদত্ত প্যাকেজ স্ক্যান করে। এটি কেবলমাত্র বর্তমানের সমস্ত শ্রেণীর জন্য কাজ করবে 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;
}

এই তিনটি পদ্ধতি আপনাকে প্রদত্ত প্যাকেজে সমস্ত শ্রেণি সন্ধান করার ক্ষমতা সরবরাহ করে।
আপনি এটি এর মতো ব্যবহার করুন:

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

ব্যাখ্যা:

পদ্ধতিটি প্রথমে কারেন্টটি পায় ClassLoader। এরপরে এটি সমস্ত সংস্থান যুক্ত করে যা এতে প্যাকেজ এবং এইগুলির পুনরাবৃত্তিগুলি ধারণ করে URL। এরপরে এটি একটি তৈরি করে URLConnectionএবং নির্ধারণ করে যে আমাদের কোন প্রকারের ইউআরএল রয়েছে। এটি হয় ডিরেক্টরি ( FileURLConnection) বা জার বা জিপ ফাইলের ভিতরে ডিরেক্টরি ( ) হতে পারে JarURLConnection। আমাদের দুটি ধরণের পৃথক পদ্ধতি সংযোগের ধরণের উপর নির্ভর করে কল করা হবে।

প্রথমে দেখা যাক এটি হলে কী হয় FileURLConnection
এটি প্রথমে পরীক্ষা করে দেয় যে পাস করা ফাইলটি বিদ্যমান এবং এটি কোনও ডিরেক্টরি। যদি কেসটি হয় তবে এটি পরীক্ষা করে এটি কোনও শ্রেণিবদ্ধ ফাইল কিনা। যদি তাই হয় তবে একটি Classবস্তু তৈরি করা হবে এবং এতে স্থাপন করা হবে ArrayList। যদি এটি কোনও শ্রেণি ফাইল না হয় তবে ডিরেক্টরি হয় তবে আমরা কেবল এটিতে পুনরাবৃত্তি করি এবং একই জিনিসটি করি। অন্যান্য সমস্ত ক্ষেত্রে / ফাইলগুলি উপেক্ষা করা হবে।

যদি এটি URLConnectionহয় JarURLConnectionতবে অন্য বেসরকারী সহায়ক পদ্ধতি কল করা হবে। এই পদ্ধতিটি জিপ / জার আর্কাইভের সমস্ত এন্ট্রিগুলিতে পুনরাবৃত্তি করে। যদি একটি এন্ট্রি একটি বর্গ ফাইল এবং প্যাকেজের মাধ্যমে একটি ভেতরে Classবস্তুর সৃষ্টি সংরক্ষণ করা হবে ArrayList

সমস্ত সংস্থান পার্স করার পরে (মূল পদ্ধতি) ArrayListপ্রদত্ত প্যাকেজের সমস্ত শ্রেণীর কনটেইনগটি দেয় , যা বর্তমান ClassLoaderসম্পর্কে জানে।

যদি প্রক্রিয়াটি কোনও পর্যায়ে ব্যর্থ হয় তবে একটি ClassNotFoundExceptionসঠিক কারণ সম্পর্কে বিশদ তথ্যের সাথে যোগাযোগ করে নিক্ষেপ করা হবে।


4
এই উদাহরণটির জন্য আমদানি করা দরকার বলে মনে হচ্ছে sun.net.www.protocol.file.FileURLConnection, যা সংকলন সময়ে একটি সতর্কতা জেনারেট করে ("সতর্কতা: sun.net.www.protocol.file.File URL সংযোগটি সান মালিকানাধীন এপিআই এবং ভবিষ্যতে প্রকাশে অপসারণ করা যেতে পারে")। সেই শ্রেণিটি ব্যবহার করার বিকল্প আছে, বা মন্তব্যগুলি ব্যবহার করে সতর্কতাটি দমন করা যেতে পারে?
খ্রিস্টান

এই পদ্ধতিটি java.lang, java.util, ... এর মতো বুটস্ট্র্যাপ শ্রেণীর জন্য কাজ করে না ... ... সিস্টেম.জেটপ্রটি ("sun.boot.class.path") পেয়ে বিভক্ত: বা এর মাধ্যমে এটি পাওয়া যায় (ওএসের উপর নির্ভর করে), এবং তারপরে উপরের চেক ডিরেক্টরী এবং চেকজারফাইলে সামান্য পরিবর্তিত সংস্করণগুলি চলছে।
কোডারফরফ্লাই

1
সংযোগ.সেটক্লাস ()। GetCanonicalName ()। সমান ("sun.net.www.protocol.file.File URL সংযোগ") ব্যবহার করে আপনি সতর্কতা / ত্রুটিটি পেতে পারেন। আপনি যদি সত্যিই চান তবে আপনি এমন একটি ইউআরএল সংযোগ তৈরি করতে পারেন যা আপনি মনে করেন যে sun.net.www.protocol.file.File URL সংযোগ ব্যবহার করা উচিত এবং সংযোগ শ্রেণীর নামটিও আপনি তৈরি ক্লাসের নামের সাথে তুলনা করতে পারেন। যদি উভয়ই একই হয় তবে আপনি শ্রেণীর নাম পরিবর্তনের ক্ষেত্রে ব্যর্থ হওয়ার চেয়ে এটি sun.net.www.protocol.file.File URL সংযোগ হিসাবে উদাহরণ হিসাবে বিবেচনা করতে পারেন।
উইলিয়াম ডিনস

1
@ খ্রিস্টিয়ান আপনি ফাইল URL সংযোগটি এরকম কিছু করা এড়াতে পারবেন: if ( ... instanceof JarURLConnecton) { ... } else { // Asume that the Connection is valid and points to a File }জেপিএ
টিকাশী

15

কোনও অতিরিক্ত পাঠাগার ব্যবহার না করে:

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;
    }
}

আমি যখন এটি কোনও জেআরে চালিত করি, upackageতা হ'ল null... :(
খ্রিস্টান

"Com.mycompany.beans" প্যাকেজের জন্য, "com" কে "com / mycompany / মটরশুটি" দিয়ে "পরীক্ষা" প্রতিস্থাপন করুন
জেমস জীথিন

4
এই কোডটি ব্যবহার করার সময় আমি একটি নাল পেতে পারি। কেবলমাত্র আপনার জার যদি একটি কার্যকরযোগ্য হয়
আলাও

আপনি যদি প্যাকেজের নামটি পেয়ে থাকেন String pack = getPackage().getName();তবে আপনাকে যুক্ত করতে হবেpack = pack.replaceAll("[.]", "/");
user2682877

13

সাধারণ শ্রেণিতে লোডাররা ক্লাসপাথের সমস্ত ক্লাসের মাধ্যমে স্ক্যান করার অনুমতি দেয় না। তবে সাধারণত কেবলমাত্র ব্যবহৃত ক্লাস লোডার হ'ল UrlClassLoader যা থেকে আমরা ডিরেক্টরি এবং জার ফাইলগুলির তালিকা ( get URLs দেখুন ) পুনরুদ্ধার করতে এবং উপলব্ধ ক্লাসগুলির তালিকাতে তাদের একে একে খুলতে পারি। এই পদ্ধতির, বর্গ পথ স্ক্যানিং বলা হয়, বাস্তবায়িত হয় Scannotation এবং ভাবনা

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

আর একটি পদ্ধতি হ'ল এনভাটেশন প্রসেসর লিখতে জাভা প্লাগেবল অ্যানোটেশন প্রসেসিং এপিআই ব্যবহার করা যা সংকলন সময়ে সমস্ত টীকাগত ক্লাস সংগ্রহ করবে এবং রানটাইম ব্যবহারের জন্য সূচি ফাইলটি তৈরি করবে। এই প্রক্রিয়াটি ClassIndex গ্রন্থাগারে প্রয়োগ করা হয়েছে :

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

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

লক্ষ্য করুন যে স্ক্যানিং পুরোপুরি স্বয়ংক্রিয়ভাবে জাভা সংকলককে ক্লাসপথে পাওয়া কোনও প্রসেসরের আবিষ্কার করার জন্য সম্পূর্ণ স্বয়ংক্রিয়ভাবে ধন্যবাদ হিসাবে প্রয়োজন নেই needed


এটি কি জারে প্যাকেজ করা ক্লাসগুলি আবিষ্কার করে? এটি আমার পক্ষে কাজ করবে বলে মনে হয় না।
এজেস করুন

আপনি কোন সরঞ্জামটি ব্যবহার করার চেষ্টা করছেন?
সাউভেক

আমি রিফ্লেকশন লিব ব্যবহার করছি। তবে এই লিবারের সাম্প্রতিক সংস্করণগুলির জন্য @ অ্যালেক্সান্দার ব্লুমস্কেল্ডের দ্বারা উল্লিখিত কার্যকারিতা অনুসরণ করার পরে আমি এটি কাজ করেছিলাম।
32

হাই, আমিগ্রহণটি ব্যবহার করছি এবং এটি কাজ করতে পারছি না, ClassIndex.getPackageClass ("my.package") খালি মানচিত্রটি ফিরিয়ে দেয়
জুয়ান

11

প্রদত্ত প্যাকেজে সমস্ত শ্রেণি তালিকাভুক্ত করার জন্য সবচেয়ে শক্তিশালী প্রক্রিয়াটি বর্তমানে ক্লাসগ্রাফ , কারণ এটি নতুন জেপিএমএস মডিউল সিস্টেম সহ শ্রেণিপথ স্পেসিফিকেশন ব্যবস্থার বিস্তৃত সম্ভাব্য অ্যারে পরিচালনা করে । (আমি লেখক।)

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

5

আমি এখানে এটি কিভাবে। আমি সমস্ত সাবফোল্ডার (সাব-প্যাকেজ) স্ক্যান করেছি এবং বেনামে ক্লাস লোড করার চেষ্টা করি না:

   /**
   * 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

আমি একটি সাধারণ গিথুব প্রকল্প একসাথে রেখেছি যা এই সমস্যার সমাধান করে:

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

এটি দুটি ফাইল-ভিত্তিক ক্লাসপাথ এবং জার ফাইলগুলির জন্য কাজ করা উচিত।

আপনি যদি প্রকল্পটি যাচাই করার পরে 'মেক' চালান তবে এটি এটি মুদ্রণ করবে:

 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. 

আরও দেখুন আমার অন্যান্য জবাব


4

হ্যাঁ আপনি করতে পারেন এমন কয়েকটি এপিআই ব্যবহার করে, এখানে আমি কীভাবে এটি করতে পছন্দ করি তা এই সমস্যাটির মুখোমুখি হয়েছিল যা আমি হাইবারনেট কোর ব্যবহার করছিলাম এবং ক্লাসগুলি সন্ধান করতে হবে যেখানে কোন নির্দিষ্ট টীকা দিয়ে মন্তব্য করা হয়েছিল।

এগুলি ব্যবহার করে একটি স্বনির্ধারিত টীকা তৈরি করুন যা আপনি কোন ক্লাসে উঠতে চান তা চিহ্নিত করবেন।

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 {

}

তারপরে আপনার ক্লাসটিকে এটির মতো চিহ্নিত করুন

@EntityToBeScanned 
public MyClass{

}

এই ইউটিলিটি ক্লাসটি তৈরি করুন যার নিম্নলিখিত পদ্ধতি রয়েছে

public class ClassScanner {

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

}

ক্লাসের সেট পাওয়ার জন্য allFoundClassAnnotatedWithEntityToBeScanned () পদ্ধতিতে কল করুন ।

আপনি নীচে দেওয়া libs প্রয়োজন হবে

<!-- 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

আপনার ক্লাসের পথে প্রতিটি শ্রেণি লোডার এন্ট্রি সন্ধান করতে হবে:

    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());
        // ....
    }   

যদি এন্ট্রি ডিরেক্টরি হয় তবে ডান উপ-ডিরেক্টরিতে সন্ধান করুন:

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));
    }
}   

যদি এন্ট্রিটি ফাইল হয় এবং এটি জার হয় তবে এর জিপ এন্ট্রিগুলি পরীক্ষা করুন:

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());
    }
}

এখন একবার আপনার সমস্ত ক্লাসের নাম উইডিং প্যাকেজ হয়ে গেলে আপনি সেগুলি প্রতিবিম্বের সাথে লোড করার চেষ্টা করতে পারেন এবং সেগুলি ক্লাস বা ইন্টারফেস ইত্যাদি হলে বিশ্লেষণ করতে পারেন


আপনি একটি জার ফাইলটিতে একটি প্যাকেজের জন্য কী প্রবেশ করবেন?
কাইল ব্রাইডেনস্টাইন

এই উদাহরণটি সাব-প্যাকেজগুলির মধ্য দিয়ে যাবে না। কারও কারও পক্ষে এটি আগ্রহী ... @ এমআর-চা কেবল আপনি যে প্যাকেজটি সন্ধান করছেন তা নির্দিষ্ট করুন। আমি এটি একটি প্রকল্পে রেখেছি, সেই প্রকল্পের মধ্যে একটি পরীক্ষার প্যাকেজ নির্দিষ্ট করেছি, এটি সংকলিত এবং প্যাকেজ করেছি এবং উদাহরণটিকে JAR এর প্রধান পদ্ধতির রূপ বলে। কবজির মতো কাজ করেছেন। :)
খ্রিস্টান

3

আমি রিফ্লেকশন লাইব্রেরিটি ব্যবহার করার চেষ্টা করেছি, তবে এটি ব্যবহার করতে কিছু সমস্যা হয়েছিল, এবং কেবলমাত্র প্যাকেজের ক্লাসগুলি পাওয়ার জন্য আমার অনেকগুলি জার অন্তর্ভুক্ত করা উচিত।

এই সদৃশ প্রশ্নের মধ্যে আমি একটি সমাধান পোস্ট করব: একটি প্যাকেজে সমস্ত ক্লাসের নাম কীভাবে পাবেন?

উত্তর sp00m দ্বারা লিখিত হয় ; আমি এটি কার্যকর করতে কিছু সংশোধন যুক্ত করেছি:

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;
    }

}

এটি ব্যবহারের জন্য এই উদাহরণটিতে উল্লিখিত sp00n হিসাবে সন্ধানের পদ্ধতিটি কল করুন: প্রয়োজন হলে আমি ক্লাসগুলির উদাহরণ তৈরি করতে যুক্ত করেছি।

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

আমি স্রেফ একটি ব্যবহারের ক্লাস লিখেছি, এতে পরীক্ষার পদ্ধতি রয়েছে, আপনার চেক থাকতে পারে ~

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

আলেকসান্দার ব্লুমস্কেল্ডের সমাধানটি@RunWith(Parameterized.class) মাভেন ব্যবহার করার সময় প্যারামিটারাইজড টেস্টগুলির জন্য আমার পক্ষে কাজ করে নি । পরীক্ষাগুলির নামকরণ করা হয়েছে সঠিকভাবে এবং যেখানে পাওয়া গেছে কিন্তু কার্যকর হয়নি:

-------------------------------------------------------
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

অনুরূপ একটি সমস্যা এখানে রিপোর্ট করা হয়েছে

আমার ক্ষেত্রে @Parametersপ্যাকেজে প্রতিটি শ্রেণীর উদাহরণ তৈরি হচ্ছে। স্থানীয়ভাবে আইডিইতে চালানোর সময় পরীক্ষাগুলি ভালভাবে কাজ করেছিল। যাইহোক, মাভেন কোনও ক্লাস চলাকালীন যেখানে আলেকসান্দার ব্লুমস্কেল্ডের সলিউশনটি পাওয়া যায় নি।

আমি নিম্নলিখিত স্নিপড দিয়ে এটি কাজ করেছিলাম যা আলেকসান্দার ব্লমস্কেল্ডের উত্তরের উপর ডেভিড পার্সনের মন্তব্য দ্বারা অনুপ্রাণিত হয়েছিল:

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

এই সম্পর্কে কি:

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;
}

তারপরে আপনি ফাংশনটি ওভারলোড করতে পারেন:

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

আপনার যদি এটি পরীক্ষা করার দরকার হয়:

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);
    }
}

আপনার আইডিইতে আমদানি সহায়ক না থাকলে:

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;

এটি কাজ করে:

  • আপনার আইডিই থেকে

  • একটি JAR ফাইলের জন্য

  • বাহ্যিক নির্ভরতা ছাড়াই


2

প্রায় সমস্ত উত্তরই হয় Reflectionsফাইল সিস্টেম থেকে ক্লাস ফাইলগুলি ব্যবহার করে বা পড়ে। আপনি যদি ফাইল সিস্টেম থেকে ক্লাসগুলি পড়ার চেষ্টা করেন, আপনি যখন JAR বা অন্য হিসাবে আপনার অ্যাপ্লিকেশনটি প্যাকেজ করেন তখন আপনি ত্রুটিগুলি পেতে পারেন। এছাড়াও আপনি সেই উদ্দেশ্যে পৃথক গ্রন্থাগার ব্যবহার করতে নাও চান।

এখানে আরেকটি পদ্ধতি যা শুদ্ধ জাভা এবং ফাইল সিস্টেমের উপর নির্ভর করে না।

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));
    }
}

জাভা 8 একটি আবশ্যক নয় । আপনি স্ট্রিমের পরিবর্তে লুপের জন্য ব্যবহার করতে পারেন। এবং আপনি এটি পরীক্ষা করতে পারেন

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
এটি খুব কার্যকর কারণ এটি নয়: এটি ব্যবহারের জন্য জেডিকে থাকা দরকার ToolProvider.getSystemJavaCompiler(), এই কোডটি নেস্টেড প্যাকেজগুলি স্ক্যান করে না।
v.ladynev

আমি এটি কোনও বাহ্যিক জারের প্যাকেজ দিয়ে কাজ করতে পারি না
এনরিকো গিউরিন

1

আপনি যে কোনও গতিশীল শ্রেণীর লোডার ব্যবহার করছেন না তবে আপনি শ্রেণিপথ এবং প্রতিটি প্রবেশের জন্য ডিরেক্টরি বা জেআর ফাইল অনুসন্ধান করতে পারেন।


1

উল্লেখযোগ্য

আপনি যদি কিছু প্যাকেজের অধীনে সমস্ত শ্রেণীর তালিকা পেতে চান তবে আপনি Reflectionনিম্নলিখিত পদ্ধতিটি ব্যবহার করতে পারেন :

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));
}

এটি ক্লাসগুলির একটি তালিকা তৈরি করবে যা পরে আপনি নিজের ইচ্ছামত ব্যবহার করতে পারেন can


1

এটি খুব সম্ভব, তবে অতিরিক্ত লাইব্রেরি ছাড়া Reflectionsএটি শক্ত যেমন ...
এটি শক্ত কারণ শ্রেণীর নাম পাওয়ার জন্য আপনার কাছে পুরো যন্ত্র নেই।
এবং, আমি আমার ClassFinderক্লাসের কোড নিই :

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

@ স্টাএলের উত্তরের ভিত্তিতে এবং তৃতীয় পক্ষের লাইব্রেরিগুলিতে নির্ভর না করার প্রয়াসের সাথে আমি প্রথম প্যাকেজটির শারীরিক অবস্থান পরীক্ষা করে ফাইল সিস্টেমের পদ্ধতির প্রয়োগ করব:

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

আপনি যদি কেবল সম্পর্কিত শ্রেণীর একটি গ্রুপ লোড করার চেষ্টা করছেন তবে স্প্রিং আপনাকে সহায়তা করতে পারে।

স্প্রিং সমস্ত শ্রেণীর তালিকা বা মানচিত্র ইনস্ট্যান্ট করতে পারে যা কোডের এক লাইনে একটি প্রদত্ত ইন্টারফেস প্রয়োগ করে। তালিকা বা মানচিত্রে সেই ইন্টারফেসটি প্রয়োগকারী সমস্ত শ্রেণীর উদাহরণ রয়েছে।

বলা হচ্ছে, ফাইল সিস্টেমের বাইরে ক্লাসের তালিকা লোড করার বিকল্প হিসাবে, প্যাকেজ নির্বিশেষে আপনি যে সমস্ত শ্রেণিতে লোড করতে চান সেগুলিতে একই ইন্টারফেসটি প্রয়োগ করুন এবং সেগুলির সমস্ত উদাহরণ সরবরাহ করার জন্য স্প্রিং ব্যবহার করুন। এই পদ্ধতিতে, আপনি যে ক্লাসে থাকুন না কেন আপনার পছন্দসই ক্লাসগুলি লোড করতে (এবং তাত্ক্ষণিক) করতে পারেন।

অন্যদিকে, যদি প্যাকেজে সমস্ত থাকে তবে আপনি যা চান তা কেবল সেই প্যাকেজের সমস্ত ক্লাস একটি প্রদত্ত ইন্টারফেস প্রয়োগ করে।


0

প্লেইন জাভা: ফাইন্ডএলক্লাসস ইউজিংপ্লেইনজ্যাভা রেফ্লেকশন টেস্ট.জভা

@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: ত্রুটি ইত্যাদি পরিচালনার জন্য বয়লারপ্লেটগুলি সহজ করার জন্য, আমি এখানে vavrএবং lombokলাইব্রেরি ব্যবহার করছি

অন্যান্য বাস্তবায়নগুলি আমার গিটহাব ড্যাগ্রোক / জাভা-প্রতিবিম্ব-সন্ধান-টীকা-ক্লাস-বা-পদ্ধতি রেপোতে পাওয়া যাবে


0

এত সহজ কিছু করার জন্য আমি একটি স্বল্প পরিশ্রমের স্নিপড খুঁজে পাইনি। সুতরাং এটি এখানে, আমি কিছুক্ষণের জন্য প্রায় স্ক্রু পরে এটি নিজেকে তৈরি:

    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

আপনি যদি বসন্তের জমিতে থাকেন তবে আপনি ব্যবহার করতে পারেন PathMatchingResourcePatternResolver;

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

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

-4

এটি সম্ভব নয়, যেহেতু প্যাকেজের সমস্ত ক্লাস লোড করা নাও হতে পারে, আপনি যখন সর্বদা ক্লাসের প্যাকেজটি জানেন।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.