Java 获得指定包名下接口类的所有实现类

2015

说明

  使用目录扫描和Java反射的方法获得指定包名下接口类的所有实现类,不同于SPI需要指定实现类的完整包路径,也不同于 Spring IOC 需要使用注解或配置去扫描实现类。

实现

package cn.chenyuning.utils;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.util.HashSet;
import java.util.Set;

public class ClassUtils {

    private static final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    
    /**
    * @Description: 获取指定包名下接口的实现类
    * @Param: [clazz, packageName]
    * @return: java.util.Set<java.lang.Class<?>>
    * @Author: Dr.Chan
    * @Date: 2021/12/27 17:42
    */
    public static Set<Class<?>> getSubClass(Class clazz, String packageName) {
        Set<Class<?>> subClass = new HashSet<>();
        if(!clazz.isInterface()) return subClass;
        if(packageName == null) {
            packageName = clazz.getPackage().getName();
        }
        try {
            Set<Class<?>> classSet = findClass(toAbsolutePath(packageName));
            classSet.forEach(item -> {
                if(clazz.isAssignableFrom(item) && !clazz.equals(item)) {
                    subClass.add(item);
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
        return subClass;
    }

    /**
    * @Description: 获取相同包下接口的实现类
    * @Param: [clazz]
    * @return: java.util.Set<java.lang.Class<?>>
    * @Author: Dr.Chan
    * @Date: 2021/12/27 17:43
    */
    public static Set<Class<?>> getSubClass(Class clazz) {
        return getSubClass(clazz, null);
    }

    private static String toAbsolutePath(String packageName) {
        String packagePath = packageName.replace(".", "/");
        return new File(classLoader.getResource("").getPath() + packagePath).getAbsolutePath();
    }

    private static Set<Class<?>> findClass(String path) throws UnsupportedEncodingException, ClassNotFoundException {
        Set<Class<?>> classSet = new HashSet<>();
        File dir = new File(path);
        if (!dir.exists()) return classSet;
        File baseDir = new File(classLoader.getResource("").getPath());
        for (File file : dir.listFiles()) {
            if (file.isDirectory()) {
                classSet.addAll(findClass(file.getAbsolutePath()));
            }
            if (file.getName().endsWith(".class")) {
                classSet.add(Class.forName(
                        path.replace(baseDir.getAbsolutePath(), "").replaceFirst("^(.)", "").replace(File.separator, ".") + "." +
                                file.getName().replace(".class", "")
                ));
            }
        }
        return classSet;
    }
}