文件夹下类与类关系,轻松搞定

93 阅读3分钟

前言

  • 有的时候我们需要获取当前项目下所有实现某接口的所有实现类,这个时候如果我们是spring项目的还是有方式来实现的,但是纯Java又该如何实现呢?下面我们来看看如何实现。
  • 首先我们澄清下是获取当前项目下的实现类,其它项目不可以哦。

前置准备

  • 首先我们得实现如何获取当前项目下的所有的类,只有获取到所有的类然后逐一比较是否是接口的实现类就可以了。
  • 这里就涉及到我们包下是文件还是Jar包,如果是Jar我们是否需要读取jar里的文件呢?
public static List<Class<?>> getClasses(String packageName){  
      
    //第一个class类的集合  
    List<Class<?>> classes = new ArrayList<Class<?>>();  
    //是否循环迭代  
    boolean recursive = true;  
    //获取包的名字 并进行替换  
    String packageDirName = packageName.replace('.', '/');  
    //定义一个枚举的集合 并进行循环来处理这个目录下的things  
    Enumeration<URL> dirs;  
    try {  
        dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);  
        //循环迭代下去  
        while (dirs.hasMoreElements()){  
            //获取下一个元素  
            URL url = dirs.nextElement();  
            //得到协议的名称  
            String protocol = url.getProtocol();  
            //如果是以文件的形式保存在服务器上  
            if ("file".equals(protocol)) {  
                //获取包的物理路径  
                String filePath = URLDecoder.decode(url.getFile(), "UTF-8");  
                //以文件的方式扫描整个包下的文件 并添加到集合中  
                findAndAddClassesInPackageByFile(packageName, filePath, recursive, classes);  
            } else if ("jar".equals(protocol)){  
                //如果是jar包文件   
                //定义一个JarFile  
                JarFile jar;  
                try {  
                    //获取jar  
                    jar = ((JarURLConnection) url.openConnection()).getJarFile();  
                    //从此jar包 得到一个枚举类  
                    Enumeration<JarEntry> entries = jar.entries();  
                    //同样的进行循环迭代  
                    while (entries.hasMoreElements()) {  
                        //获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件  
                        JarEntry entry = entries.nextElement();  
                        String name = entry.getName();  
                        //如果是以/开头的  
                        if (name.charAt(0) == '/') {  
                            //获取后面的字符串  
                            name = name.substring(1);  
                        }  
                        //如果前半部分和定义的包名相同  
                        if (name.startsWith(packageDirName)) {  
                            int idx = name.lastIndexOf('/');  
                            //如果以"/"结尾 是一个包  
                            if (idx != -1) {  
                                //获取包名 把"/"替换成"."  
                                packageName = name.substring(0, idx).replace('/', '.');  
                            }  
                            //如果可以迭代下去 并且是一个包  
                            if ((idx != -1) || recursive){  
                                //如果是一个.class文件 而且不是目录  
                                if (name.endsWith(".class") && !entry.isDirectory()) {  
                                    //去掉后面的".class" 获取真正的类名  
                                    String className = name.substring(packageName.length() + 1, name.length() - 6);  
                                    try {  
                                        //添加到classes  
                                        classes.add(Class.forName(packageName + '.' + className));  
                                    } catch (ClassNotFoundException e) {  
                                        e.printStackTrace();  
                                    }  
                                  }  
                            }  
                        }  
                    }  
                } catch (IOException e) {  
                    e.printStackTrace();  
                }   
            }  
        }  
    } catch (IOException e) {  
        e.printStackTrace();  
    }  
     
    return classes;  
} 

开始比对

  • 然后我们再开始根据接口获取当前所在包路径。在根据包路径获取所有类,进行比对确认
public static List<Class> getAllClassByInterface(Class c) {  
        List<Class>  returnClassList = null;  
          
        if(c.isInterface()) {  
            // 获取当前的包名  
            String packageName = c.getPackage().getName();  
            // 获取当前包下以及子包下所以的类  
            List<Class<?>> allClass = getClasses(packageName);  
            if(allClass != null) {  
                returnClassList = new ArrayList<Class>();  
                for(Class classes : allClass) {  
                    // 判断是否是同一个接口  
                    if(c.isAssignableFrom(classes)) {  
                        // 本身不加入进去  
                        if(!c.equals(classes)) {  
                            returnClassList.add(classes);          
                        }  
                    }  
                }  
            }  
        }  
          
        return returnClassList;  
    }
  • 最终就可以获取到所有的实现类了。

总结

  • 其实想法很简单。但是实现起来确实不容易的,实现过程中涉及到jar内部如何实现卡了很久。
  • 这里顺带说下在spring中是如何操作的呢?我们可以在实现类上添加我们自定义注解,然后再上下文中可以根据注解方式获取
Map<String, Object> beansWithAnnotation = ApplicationContextUtil.getApplicationContext().getBeansWithAnnotation(SuperDirectionHandler.class);

\