Java获取指定包名下的所有类的全类名的解决方案详解编程语言

    最近有个需求需要获取一个指定包下的所有类的全类名,因此特意写了个获取指定包下所有类的全类名的工具类。在此记录一下,方便后续查阅

一、思路

       
通过ClassLoader来查找指定包,如果是在classes文件夹下的class文件,则用遍历文件的方式来获取该包下的所有类名。如果这个包名是jar包里面的,那么需要通过遍历jar包内文件的方式来获取该包下的所有类的类名

二、代码

       代码如下
package com.zxy.demo.common.utils; 
import java.io.File; 
import java.io.IOException; 
import java.net.JarURLConnection; 
import java.net.URL; 
import java.util.ArrayList; 
import java.util.Enumeration; 
import java.util.List; 
import java.util.jar.JarEntry; 
import java.util.jar.JarFile; 
 
/** 
 * ClazzUtils 
 * @author ZENG.XIAO.YAN 
 * @version 1.0 
 */ 
public class ClazzUtils { 
	private static final String CLASS_SUFFIX = ".class"; 
	private static final String CLASS_FILE_PREFIX = File.separator + "classes"  + File.separator; 
	private static final String PACKAGE_SEPARATOR = "."; 
	 
	 
	 
	 
	/** 
	 * 查找包下的所有类的名字 
	 * @param packageName 
	 * @param showChildPackageFlag 是否需要显示子包内容 
	 * @return List集合,内容为类的全名 
	 */ 
	public static List<String> getClazzName(String packageName, boolean showChildPackageFlag ) { 
		List<String> result = new ArrayList<>(); 
		String suffixPath = packageName.replaceAll("//.", "/"); 
		ClassLoader loader = Thread.currentThread().getContextClassLoader(); 
		try { 
			Enumeration<URL> urls = loader.getResources(suffixPath); 
			while(urls.hasMoreElements()) { 
				URL url = urls.nextElement(); 
				if(url != null) { 
					String protocol = url.getProtocol(); 
					if("file".equals(protocol)) { 
						String path = url.getPath(); 
						System.out.println(path); 
						result.addAll(getAllClassNameByFile(new File(path), showChildPackageFlag)); 
					} else if("jar".equals(protocol)) { 
						JarFile jarFile = null; 
						try{ 
			                jarFile = ((JarURLConnection) url.openConnection()).getJarFile(); 
						} catch(Exception e){ 
							e.printStackTrace(); 
						} 
						if(jarFile != null) { 
							result.addAll(getAllClassNameByJar(jarFile, packageName, showChildPackageFlag)); 
						} 
					} 
				} 
			} 
		} catch (IOException e) { 
			e.printStackTrace(); 
		} 
		return result; 
	} 
	 
	 
	/** 
	 * 递归获取所有class文件的名字 
	 * @param file  
	 * @param flag	是否需要迭代遍历 
	 * @return List 
	 */ 
	private static List<String> getAllClassNameByFile(File file, boolean flag) { 
		List<String> result =  new ArrayList<>(); 
		if(!file.exists()) { 
			return result; 
		} 
		if(file.isFile()) { 
			String path = file.getPath(); 
			// 注意:这里替换文件分割符要用replace。因为replaceAll里面的参数是正则表达式,而windows环境中File.separator="//"的,因此会有问题 
			if(path.endsWith(CLASS_SUFFIX)) { 
				path = path.replace(CLASS_SUFFIX, ""); 
				// 从"/classes/"后面开始截取 
				String clazzName = path.substring(path.indexOf(CLASS_FILE_PREFIX) + CLASS_FILE_PREFIX.length()) 
						.replace(File.separator, PACKAGE_SEPARATOR); 
				if(-1 == clazzName.indexOf("$")) { 
					result.add(clazzName); 
				} 
			} 
			return result; 
			 
		} else { 
			File[] listFiles = file.listFiles(); 
			if(listFiles != null && listFiles.length > 0) { 
				for (File f : listFiles) { 
					if(flag) { 
						result.addAll(getAllClassNameByFile(f, flag)); 
					} else { 
						if(f.isFile()){ 
							String path = f.getPath(); 
							if(path.endsWith(CLASS_SUFFIX)) { 
								path = path.replace(CLASS_SUFFIX, ""); 
								// 从"/classes/"后面开始截取 
								String clazzName = path.substring(path.indexOf(CLASS_FILE_PREFIX) + CLASS_FILE_PREFIX.length()) 
										.replace(File.separator, PACKAGE_SEPARATOR); 
								if(-1 == clazzName.indexOf("$")) { 
									result.add(clazzName); 
								} 
							} 
						} 
					} 
				} 
			}  
			return result; 
		} 
	} 
	 
	 
	/** 
	 * 递归获取jar所有class文件的名字 
	 * @param jarFile  
	 * @param packageName 包名 
	 * @param flag	是否需要迭代遍历 
	 * @return List 
	 */ 
	private static List<String> getAllClassNameByJar(JarFile jarFile, String packageName, boolean flag) { 
		List<String> result =  new ArrayList<>(); 
		Enumeration<JarEntry> entries = jarFile.entries(); 
		while(entries.hasMoreElements()) { 
			JarEntry jarEntry = entries.nextElement(); 
			String name = jarEntry.getName(); 
			// 判断是不是class文件 
			if(name.endsWith(CLASS_SUFFIX)) { 
				name = name.replace(CLASS_SUFFIX, "").replace("/", "."); 
				if(flag) { 
					// 如果要子包的文件,那么就只要开头相同且不是内部类就ok 
					if(name.startsWith(packageName) && -1 == name.indexOf("$")) { 
						result.add(name); 
					} 
				} else { 
					// 如果不要子包的文件,那么就必须保证最后一个"."之前的字符串和包名一样且不是内部类 
					if(packageName.equals(name.substring(0, name.lastIndexOf("."))) && -1 == name.indexOf("$")) { 
						result.add(name); 
					} 
				} 
			} 
		} 
		return result; 
	} 
	 
	public static void main(String[] args) { 
		List<String> list = ClazzUtils.getClazzName("com.mysql.fabric", false); 
		for (String string : list) { 
			System.out.println(string); 
		} 
	} 
} 
 

1

package com.zxy.demo.common.utils;

2

import java.io.File;

3

import java.io.IOException;

4

import java.net.JarURLConnection;

5

import java.net.URL;

6

import java.util.ArrayList;

7

import java.util.Enumeration;

8

import java.util.List;

9

import java.util.jar.JarEntry;

10

import java.util.jar.JarFile;

11

12

/**

13

 * ClazzUtils

14

 * @author ZENG.XIAO.YAN

15

 * @version 1.0

16

 */

17

public class ClazzUtils {

18

    private static final String CLASS_SUFFIX = ".class";

19

    private static final String CLASS_FILE_PREFIX = File.separator + "classes"  + File.separator;

20

    private static final String PACKAGE_SEPARATOR = ".";

21

    

22

    

23

    

24

    

25

    /**

26

     * 查找包下的所有类的名字

27

     * @param packageName

28

     * @param showChildPackageFlag 是否需要显示子包内容

29

     * @return List集合,内容为类的全名

30

     */

31

    public static List<String> getClazzName(String packageName, boolean showChildPackageFlag ) {

32

        List<String> result = new ArrayList<>();

33

        String suffixPath = packageName.replaceAll("//.", "/");

34

        ClassLoader loader = Thread.currentThread().getContextClassLoader();

35

        try {

36

            Enumeration<URL> urls = loader.getResources(suffixPath);

37

            while(urls.hasMoreElements()) {

38

                URL url = urls.nextElement();

39

                if(url != null) {

40

                    String protocol = url.getProtocol();

41

                    if("file".equals(protocol)) {

42

                        String path = url.getPath();

43

                        System.out.println(path);

44

                        result.addAll(getAllClassNameByFile(new File(path), showChildPackageFlag));

45

                    } else if("jar".equals(protocol)) {

46

                        JarFile jarFile = null;

47

                        try{

48

                            jarFile = ((JarURLConnection) url.openConnection()).getJarFile();

49

                        } catch(Exception e){

50

                            e.printStackTrace();

51

                        }

52

                        if(jarFile != null) {

53

                            result.addAll(getAllClassNameByJar(jarFile, packageName, showChildPackageFlag));

54

                        }

55

                    }

56

                }

57

            }

58

        } catch (IOException e) {

59

            e.printStackTrace();

60

        }

61

        return result;

62

    }

63

    

64

    

65

    /**

66

     * 递归获取所有class文件的名字

67

     * @param file 

68

     * @param flag  是否需要迭代遍历

69

     * @return List

70

     */

71

    private static List<String> getAllClassNameByFile(File file, boolean flag) {

72

        List<String> result =  new ArrayList<>();

73

        if(!file.exists()) {

74

            return result;

75

        }

76

        if(file.isFile()) {

77

            String path = file.getPath();

78

            // 注意:这里替换文件分割符要用replace。因为replaceAll里面的参数是正则表达式,而windows环境中File.separator="//"的,因此会有问题

79

            if(path.endsWith(CLASS_SUFFIX)) {

80

                path = path.replace(CLASS_SUFFIX, "");

81

                // 从"/classes/"后面开始截取

82

                String clazzName = path.substring(path.indexOf(CLASS_FILE_PREFIX) + CLASS_FILE_PREFIX.length())

83

                        .replace(File.separator, PACKAGE_SEPARATOR);

84

                if(-1 == clazzName.indexOf("$")) {

85

                    result.add(clazzName);

86

                }

87

            }

88

            return result;

89

            

90

        } else {

91

            File[] listFiles = file.listFiles();

92

            if(listFiles != null && listFiles.length > 0) {

93

                for (File f : listFiles) {

94

                    if(flag) {

95

                        result.addAll(getAllClassNameByFile(f, flag));

96

                    } else {

97

                        if(f.isFile()){

98

                            String path = f.getPath();

99

                            if(path.endsWith(CLASS_SUFFIX)) {

100

                                path = path.replace(CLASS_SUFFIX, "");

101

                                // 从"/classes/"后面开始截取

102

                                String clazzName = path.substring(path.indexOf(CLASS_FILE_PREFIX) + CLASS_FILE_PREFIX.length())

103

                                        .replace(File.separator, PACKAGE_SEPARATOR);

104

                                if(-1 == clazzName.indexOf("$")) {

105

                                    result.add(clazzName);

106

                                }

107

                            }

108

                        }

109

                    }

110

                }

111

            } 

112

            return result;

113

        }

114

    }

115

    

116

    

117

    /**

118

     * 递归获取jar所有class文件的名字

119

     * @param jarFile 

120

     * @param packageName 包名

121

     * @param flag  是否需要迭代遍历

122

     * @return List

123

     */

124

    private static List<String> getAllClassNameByJar(JarFile jarFile, String packageName, boolean flag) {

125

        List<String> result =  new ArrayList<>();

126

        Enumeration<JarEntry> entries = jarFile.entries();

127

        while(entries.hasMoreElements()) {

128

            JarEntry jarEntry = entries.nextElement();

129

            String name = jarEntry.getName();

130

            // 判断是不是class文件

131

            if(name.endsWith(CLASS_SUFFIX)) {

132

                name = name.replace(CLASS_SUFFIX, "").replace("/", ".");

133

                if(flag) {

134

                    // 如果要子包的文件,那么就只要开头相同且不是内部类就ok

135

                    if(name.startsWith(packageName) && -1 == name.indexOf("$")) {

136

                        result.add(name);

137

                    }

138

                } else {

139

                    // 如果不要子包的文件,那么就必须保证最后一个"."之前的字符串和包名一样且不是内部类

140

                    if(packageName.equals(name.substring(0, name.lastIndexOf("."))) && -1 == name.indexOf("$")) {

141

                        result.add(name);

142

                    }

143

                }

144

            }

145

        }

146

        return result;

147

    }

148

    

149

    public static void main(String[] args) {

150

        List<String> list = ClazzUtils.getClazzName("com.mysql.fabric", false);

151

        for (String string : list) {

152

            System.out.println(string);

153

        }

154

    }

155

}

156

三、小结

        (1)写代码时,注意String类的replace方法和replaceAll方法的区别
        (2)内部类生成的class文件有有美元符号”$”

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/15518.html

(0)
上一篇 2021年7月19日
下一篇 2021年7月19日

相关推荐

发表回复

登录后才能评论