Find all classes in package (& call static methods) at runtime

情到浓时终转凉″ 提交于 2020-02-05 02:34:47

问题


I have a package in which there are many classes, and these classes all have a static method called onLoad. I want to call the onLoad method when the program starts, however I do not want to hard-wire each and every one, i.e. classA.onLoad(); classB.onLoad(); etc.

How can I list all classes in package com.foo.bar.asd and run onLoad() on all of them?

Thanks in advance


回答1:


I found this question quite interesting so I came up with a solution. The tricky part here is to actually find all classes that are in a given package.

In this example I assume that all classes are in the same package where the class C is and that all classes except C have an onLoad method which may be inaccessible (i.e. private). Then you can use the following example code.

public final class C {

    public static void main(String[] args) throws Exception {
        for (Class<?> cls : getClasses(C.class)) {
            if (cls != C.class) {
                Method onLoad = cls.getDeclaredMethod("onLoad");
                onLoad.setAccessible(true);
                onLoad.invoke(null);
            }
        }
    }

    private static List<Class<?>> getClasses(Class<?> caller)
            throws IOException, URISyntaxException {
        return Files.walk(getPackagePath(caller))
                .filter(Files::isRegularFile)
                .filter(file -> file.toString().endsWith(".class"))
                .map(path -> mapPathToClass(path, caller.getPackage().getName()))
                .collect(Collectors.toList());
    }

    private static Class<?> mapPathToClass(Path clsPath, String packageName) {
        String className = clsPath.toFile().getName();
        className = className.substring(0, className.length() - 6);
        return loadClass(packageName + "." + className);
    }

    private static Path getPackagePath(Class<?> caller)
            throws IOException, URISyntaxException {
        String packageName = createPackageName(caller);
        Enumeration<URL> resources = caller.getClassLoader()
                .getResources(packageName);
        return Paths.get(resources.nextElement().toURI());
    }

    private static String createPackageName(Class<?> caller) {
        return caller.getPackage().getName().replace(".", "/");
    }

    private static Class<?> loadClass(String name) {
        try {
            return Class.forName(name);
        } catch (ClassNotFoundException e) {
            return null;
        }
    }
}

I've omitted all exception checking and statements like resources.nextElement() may even throw an exception. So if you want to cover those cases, you need to add a few checks here and there.




回答2:


You can use the reflections library.

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

public static void main(String[] args)
   throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException 
{
  Reflections reflections = new Reflections(new ConfigurationBuilder()
    .setScanners(new SubTypesScanner(false /* don't exclude Object.class */), new ResourcesScanner())
    .setUrls(ClasspathHelper.forClassLoader(ClasspathHelper.contextClassLoader()))
    .filterInputsBy(new FilterBuilder().include(FilterBuilder.prefix("com.foo.bar.asd"))));

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


  for (Class<?> clazz : classes)
  {
     Method method = clazz.getMethod("onLoad");
     method.invoke(null);
  }
}

Please note that this library also has dependencies (guava.jar and javassist.jar).



来源:https://stackoverflow.com/questions/58572622/find-all-classes-in-package-call-static-methods-at-runtime

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!