简体   繁体   中英

Find classes implementing an interface in Jar

I want to find whether the classes inside the jar has implemented a particular interface or not. I have implemented below code, but it iterates over all the classes inside jar file and finds on each class whether it has implemented this particular interface or not.

    public static synchronized boolean findClassesInJar(final Class<?> baseInterface, final String jarName){
        final List<String> classesTobeReturned = new ArrayList<String>();
        if (!StringUtils.isBlank(jarName)) {
            //jarName is relative location of jar wrt. 
            final String jarFullPath = File.separator + jarName;
            final ClassLoader classLoader = this.getClassLoader();
            JarInputStream jarFile = null;
            URLClassLoader ucl = null;
            final URL url = new URL("jar:file:" + jarFullPath + "!/");
            ucl = new URLClassLoader(new URL[] { url }, classLoader);
            jarFile = new JarInputStream(new FileInputStream(jarFullPath));
            JarEntry jarEntry;
            while (true) {
                jarEntry = jarFile.getNextJarEntry();
                if (jarEntry == null)
                    break;
                if (jarEntry.getName().endsWith(".class")) {
                    String classname = jarEntry.getName().replaceAll("/", "\\.");
                    classname = classname.substring(0, classname.length() - 6);
                    if (!classname.contains("$")) {
                        try {
                            final Class<?> myLoadedClass = Class.forName(classname, true, ucl);
                            if (baseInterface.isAssignableFrom(myLoadedClass)) {
                                return true;
                            }
                        } catch (final ClassNotFoundException e) {

                        } 
                    }
                }
            }
        return false;
    }

Is there any simple way to do this ? Because If have a jar with 100 class files and 100th class has implemented this interface, through the above code I need to iterate all the 100 class files and find whether it has implemented the interface or not. Is there any efficient way of doing it ?

The Reflections library can do that:

Reflections reflections = new Reflections(
    ClasspathHelper.forPackage("your.root.package"), new SubTypesScanner());
Set<Class<? extends YourInterface>> implementingTypes =
     reflections.getSubTypesOf(YourInterface.class);

Spring has some helper code for this in ClassPathScanningCandidateComponentProvider but it basically does what you did.

You might also want to look at Freud which is a comfortable toolkit to write static analysis tests like making sure that all classes in a certain package or which implement a certain interface also implement equals() and hashCode()

The nice thing about Freud is that it can analyze Java source and class files (ie source code and compiled byte code), it can look into properties or text files, it can read CSS and Spring configurations (so it can make sure that all important methods of a DAO bean have @Transactional ).

Any modern IDE (eclipse, IntelliJ, ...) should be able to find the usages of a particular interface (or class or method).

If you have the source of the jar file, you can add it as a dependency to your project. Alternatively you can simply unzip the contents of the jar and open that as a project in your IDE and find the usages of the interface you're interested in.

In general, no there is no easy way. There isn't even a generic way of obtaining that information.

The problem here lies with Java's classloading mechanism. You can't tell if a class implements an interface before you actually loaded it. And you can't tell if a class exists without attempting to load it (note that you can't even tell which classes are available on the class path).

The reason here is simply that class loaders do not provide any enumrate classes functionality, and the reason for that is that the classpath can be potentially infinitely large/deep and the cost (in time) to retrieve that enumeration is potentially infinite (think of an URL class loader that connects you to a large remote code repository).

So your method is already as good as it can be.

You can use IntelliJ for this. Create a dummy project, and add your jars to classpath. Then open your interface, and click the (I) icon left of the interface name. Not sure about Eclilse, but pretty sure you can do the same.

Same goes for classes.

在此输入图像描述

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM