Get package names using java reflections

2019-02-24 15:01发布

问题:

I want to get all the package names from an separate Java project, how would I do this in Java preferably using reflections?

I have imported the project into my build path and I've tried using the code below to get the package names.

Package[] pack = Package.getPackages();

EDIT: I'm not using jar files, I have just imported the project as it will be in the same dir. I only need packages that I created, specifically packages which begin with a certain directory.

回答1:

You really can't know every package name in the libraries by just using getPackages() as it only will list the ones known to the classloader. This means that if a class has not yet been loaded from a specific package, then it will be absent from the list.

Use the zip file handling routines to open the jar files, and read out the packages by directory name. With this technique, you will discover all package names, even those that are not yet "in use". Basically, any path that contains a .class file is a "package name".

---- Edited as further details indicates JAR files are not being used ---

Since you aren't using JAR files (and why not? they're really good!), you will have to scan directories. To "find" the directories, you will need to chop up the class path. The way to get the class path is:

 String classpath = System.getProperty("java.class.path", null);

Then you do a search of all the directories under each "starting point" that has a .class file in it. Once you have all of those, you have all of the packages.

Again, it is not possible to know all the packages by just asking the classloader, unless you could somehow guarantee the class loader has loaded all of the classes (which it typically doesn't).



回答2:

As long as you don't need empty packages, something like this should work:

/**
 * Finds all package names
 * @return Set of package names
 */
public Set<String> findAllPackages() {
    List<ClassLoader> classLoadersList = new LinkedList<ClassLoader>();
    classLoadersList.add(ClasspathHelper.contextClassLoader());
    classLoadersList.add(ClasspathHelper.staticClassLoader());
    Reflections reflections = new Reflections(new ConfigurationBuilder()
            .setScanners(new SubTypesScanner(false), new ResourcesScanner())
            .setUrls(ClasspathHelper.forClassLoader(classLoadersList.toArray(new ClassLoader[0])))
            .filterInputsBy(new FilterBuilder().include(FilterBuilder.prefix("my.base.package"))));
    Set<Class<? extends Object>> classes = reflections.getSubTypesOf(Object.class);
    Set<String> packageNameSet = new TreeSet<String>();
    for (Class classInstance : classes) {
        packageNameSet.add(classInstance.getPackage().getName());
    }
    return packageNameSet;
}

It finds all classes, loops through them and gets the package name stored in a Set to avoid duplicates.

Unfortunately, we can't just use the code below with a normal Reflections object without specifying a custom configuration to allow Object scanning.

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