【Java反射】自动扫描某个包内的所有类的简单实现

  • Post author:
  • Post category:java


学习java反射的时候,了解到可能存在某种场景需要扫描一个包内的所有类的需求,在各个框架中都存在这种需求,配合注解的使用能够实现强大功能,所以尝试实现了一下。

  1. 获取包路径
  2. 获取包内所有文件名
  3. 根据文件名得到反射Class对象



1. 获取包路径(扫描包)

这一部分的功能由于本人原因可能描述的不是很好

​ 这个部分我主要使用的是通过类加载器ClassLoader来进行扫描,之所以使用类加载器的原因是因为源代码java文件一个文件中可能会包含多个类,不能完全的扫描到,而扫描class文件则可以更加全面的获取到包内的所有类

使用扫描class文件的方式还可以获取内部类,注意内部类的class文件名中多一个美元符号$,不过本篇介绍的方法实现中不包含对内部类的获取,这只是一个简单的实现。

​ 首先我们的包名和文件路径是存在差异的,包中表示结构关系的分割符为

“ . ”

,而系统中表示文件结构关系的分隔符又因操作系统的不同而不同,windows的分隔符为**” \ “** ,所以需要先将完整的包名中的分隔符替换为当前系统的分隔符。

String path = basePackageName.replace(".","\\");

​ 替换后就得到包在输出目录中的文件夹相对路径,再通过ClassLoader的getResources方法获取到绝对路径

//获取编译后的class文件夹路径dirPath
Enumeration<URL> enums = loader.getResources(path);
String dirPath = "";
while (enums.hasMoreElements()){
    URL url = enums.nextElement();
    if (url != null){
        String protool = url.getProtocol();
        if ("file".equals(protool)){
            dirPath = url.getPath();
        }
    }
}

需要注意的是:这里获取到的绝对路径dirPath中可能会存在**“%e65%e64%e35%e23”**乱码,此时通过这个路径是无法找到正确的文件夹的,需要使用

URLDecoder.decode(dirPath,"utf-8");

将其转换为UTF-8编码方式消除乱码后能获取到正确的包路径。



2. 获取包内所有文件名

通过上一步骤,我们已经获取到了包的绝对路径,这一步,我们就需要获取包文件夹内的所有文件名。

​ 首先根据包绝对路径实例化一个File对象,通过

isDirectory()

方法判断是否为文件夹,若不为文件夹则不作任何操作。

​ 当确保是文件夹时,就可以调用File对象的

listFiles()

方法获取其子文件列表,返回值为一个File类型数组。

​ 遍历得到的子文件数组,通过getName()方法获取文件名,由于通过类名创建反射不需要后缀名,所以我们这里需要将文件夹的后缀名去掉,再添加到结果集列表中。

​ 实现代码如下:

private List<String> scanForDir(String dirPath){
    List<String> classNames = new ArrayList<>();
    //根据传入文件夹路径创建File对象
    File dir = new File(dirPath);
    //检查是否为文件夹
    if (dir.isDirectory()){
        //遍历文件夹内的文件
        for (File f : dir.listFiles()){
            //获取文件名,并删除后缀
            String fileName = f.getName();
            fileName = fileName.substring(0,fileName.lastIndexOf("."));
            //添加到结果中
            classNames.add(fileName);
        }
    }
    return classNames;
}



3.根据文件名得到反射Class对象

​ 这一步就很简单了,直接遍历所有文件名,通过

Class.forName

方法得到反射对象

使用Class.forName方法获取反射对象时,需要注意,传入的参数需要的是类的全名,即所在包+类名,所以传参的时候需要将包名给拼接上去

//将获取到的包内文件名转换为反射对象
List<Class<?>> classes =  new ArrayList<>();
List<String> classNames = scanForPackageName(packageName);
for (String str : classNames){
    try {
        classes.add(Class.forName(packageName+"."+str));
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
}

以上就是关于自动扫描某个包内的所有类的简单实现,本人水平有限,感觉代码中还存在着不少瑕疵,后续可能会继续完善,如果有存在的问题,还请在评论区留言。



版权声明:本文为INER1原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。