zoukankan      html  css  js  c++  java
  • 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)写代码时,注意String类的replace方法和replaceAll方法的区别
            (2)内部类生成的class文件有有美元符号"$"
  • 相关阅读:
    jdk.exe转zip免安装
    jdk全版本下载链接
    Cesium primitive绘制折线和多边形
    sql调优的几种方式
    maven操作
    如何设计高并发系统?
    用友华表cell的程序发布
    OpenCV异常问题(一)
    js jquery window 高 宽
    sql中游标的使用一
  • 原文地址:https://www.cnblogs.com/zeng1994/p/59313120e95e2074981870c787cd69d9.html
Copyright © 2011-2022 走看看