最近刚入职,领导分配了一个小任务:将自研的mq系统的配置全部打印出来。听起来好像挺简单,但是一看代码发现好多配置项不在一个module里,而且有的配置项置只暴露出来api,如果在不修改代码的情况下,是无法获取到所有配置项的。想了一下午,想到使用
spi或者asm来解决,spi一样有无法获取private,package-protected或者其他模块的配置项的问题。asm想着应该可以解决,但我是个asm半吊子水货,用的不好。所以想来想去只想到扫描mvn项目来解决这个问题。好在已经有人遇到过这种问题并已经解决了(地址),先说声谢谢啦。
定义Scanner接口
public interface Scanner {
    String CLASS_SUFFIX = ".class";
    Set<Class<?>> search(String pkgName, Predicate<Class<?>> predicate);
    default Set<Class<?>> search(String pkgName){
        return search(pkgName,null);
    }
}定义FileScanner
public class FileScanner implements Scanner{
    private String defaultClassPath = FileScanner.class.getResource("/").getPath();
    public String getDefaultClassPath(){
        return defaultClassPath;
    }
    public void setDefaultClassPath(String defaultClassPath) {
        this.defaultClassPath = defaultClassPath;
    }
    public FileScanner(){}
    public FileScanner(String defaultClassPath){
        this.defaultClassPath=defaultClassPath;
    }
    @Override
    public Set<Class<?>> search(String pkgName, Predicate<Class<?>> predicate) {
        String classPath = defaultClassPath;
        String basePkgPath = pkgName.replace(".",File.separator);
        String searchPath = classPath+basePkgPath;
        return new ClassSearcher().doPath(new File(searchPath),pkgName,predicate,true);
    }
    private static class ClassSearcher {
        private Set<Class<?>> classPaths = new HashSet<>(0);
        private Set<Class<?>> doPath(File file,String pkgName,Predicate<Class<?>> predicate,boolean flag){
            if(file.isDirectory()){
                File[] files=file.listFiles();
                if(!flag){
                    pkgName = pkgName+"."+file.getName();
                }
                if(files!=null) {
                    for (File f : files) {
                        doPath(f, pkgName, predicate, false);
                    }
                }
            }else{
                if(file.getName().endsWith(CLASS_SUFFIX)){
                    try {
                        Class<?> clazz = Class.forName(pkgName+"."+file.getName().substring(0,file.getName().lastIndexOf(".")));
                        if(predicate==null||predicate.test(clazz)){
                            classPaths.add(clazz);
                        }
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }
                }
            }
            return classPaths;
        }
    }
}定义JarScanner
public class JarScanner implements Scanner {
    @Override
    public Set<Class<?>> search(String pkgName, Predicate<Class<?>> predicate) {
        Set<Class<?>> clazzSet = new HashSet<>(0);
        try {
            Enumeration<URL> urlEnumeration = Thread.currentThread().getContextClassLoader().getResources(pkgName.replace(".","/"));
            while (urlEnumeration.hasMoreElements()){
                URL url = urlEnumeration.nextElement();
                String protocol = url.getProtocol();
                if("jar".equalsIgnoreCase(protocol)){
                    JarURLConnection connection = (JarURLConnection) url.openConnection();
                    if(connection!=null){
                        JarFile jarFile = connection.getJarFile();
                        if(jarFile!=null){
                            Enumeration<JarEntry> jarEntryEnumeration = jarFile.entries();
                            while (jarEntryEnumeration.hasMoreElements()){
                                JarEntry entry = jarEntryEnumeration.nextElement();
                                String entryName = entry.getName();
                                if(entryName.contains(CLASS_SUFFIX)&&entryName.replaceAll("/",".").startsWith(pkgName)){
                                    String clazzName = entryName.substring(0,entryName.lastIndexOf(".")).replace("/",".");
                                    Class<?> clazz = Class.forName(clazzName);
                                    if(predicate==null||predicate.test(clazz)){
                                        clazzSet.add(clazz);
                                    }
                                }
                            }
                        }
                    }
                }
                else if("file".equalsIgnoreCase(protocol)){
                    FileScanner fileScanner = new FileScanner();
                    fileScanner.setDefaultClassPath(url.getPath().replace(pkgName.replace(".","/"),""));
                    clazzSet.addAll(fileScanner.search(pkgName,predicate));
                }
            }
        } catch (ClassNotFoundException | IOException e) {
            e.printStackTrace();
        }
        return clazzSet;
    }
}定义两种Scanner的委托
public class ScannerExecutor implements Scanner{
    private volatile static ScannerExecutor INSTANCE;
    @Override
    public Set<Class<?>> search(String pkgName, Predicate<Class<?>> predicate) {
        Scanner fileScanner = new FileScanner();
        Set<Class<?>> fileSearch = fileScanner.search(pkgName,predicate);
        Scanner jarScanner = new JarScanner();
        Set<Class<?>> jarSearch = jarScanner.search(pkgName,predicate);
        fileSearch.addAll(jarSearch);
        return fileSearch;
    }
    private ScannerExecutor(){}
    public static ScannerExecutor getInstance(){
        if(INSTANCE==null){
            synchronized (ScannerExecutor.class){
                if(INSTANCE==null){
                    INSTANCE=new ScannerExecutor();
                }
            }
        }
        return INSTANCE;
    }
}定义封装的工具类
public class ClassScanner {
    public static Set<Class<?>> search(String pkgName){
        return search(pkgName);
    }
    public static Set<Class<?>> search(String pkgName, Predicate<Class<?>> predicate){
        return ScannerExecutor.getInstance().search(pkgName,predicate);
    }
} 
                     
                     
                        
                        