All Downloads are FREE. Search and download functionalities are using the official Maven repository.

top.jfunc.common.utils.ClassUtil Maven / Gradle / Ivy

The newest version!
package top.jfunc.common.utils;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * 该类来源于#hutool#
 * 
 * 地址:http://git.oschina.net/loolly/hutool
 * 
 * 类工具类 1、扫描指定包下的所有类
* 参考 http://www.oschina.net/code/snippet_234657_22722 * * @author seaside_hi, xiaoleilu * */ public final class ClassUtil{ private ClassUtil(){} /** Class文件扩展名 */ private static final String CLASS_EXT = ".class"; /** Jar文件扩展名 */ private static final String JAR_FILE_EXT = ".jar"; /** 在Jar中的路径jar的扩展名形式 */ private static final String JAR_PATH_EXT = ".jar!"; /** 当Path为文件形式时, path会加入一个表示文件的前缀 */ private static final String PATH_FILE_PRE = "file:"; /** * 扫描指定包路径下所有包含指定注解的类 * * @param packageName * 包路径 * @param inJar * 在jar包中查找 * @param annotationClass * 注解类 * @return 类集合 */ public static Set> scanPackageByAnnotation(String packageName, boolean inJar, final Class annotationClass){ return scanPackage(packageName, inJar, new ClassFilter(){ @Override public boolean accept(Class clazz){ return clazz.isAnnotationPresent(annotationClass); } }); } /** * 扫描指定包路径下所有指定类的子类 * * @param packageName * 包路径 * @param inJar * 在jar包中查找 * @param superClass * 父类 * @return 类集合 */ public static Set> scanPackageBySuper(String packageName, boolean inJar, final Class superClass){ return scanPackage(packageName, inJar, (clazz)-> superClass.isAssignableFrom(clazz) && !superClass.equals(clazz)); } /** * 扫面包路径下满足class过滤器条件的所有class文件 如果包路径为 com.abs + A.class 但是输入 * abs会产生classNotFoundException 因为className 应该为 com.abs.A * 现在却成为abs.A,此工具类对该异常进行忽略处理,有可能是一个不完善的地方,以后需要进行修改 * * @param packageName * 包路径 com | com. | com.abs | com.abs. * @param inJar * 在jar包中查找 * @param classFilter * class过滤器,过滤掉不需要的class * @return 类集合 */ public static Set> scanPackage(String packageName, boolean inJar, ClassFilter classFilter){ if(StrUtil.isBlank(packageName)){ packageName = ""; } packageName = getWellFormedPackageName(packageName); final Set> classes = new HashSet>(); for(String classPath : getClassPaths(packageName)){ // 填充 classes, 并对classpath解码 classPath = decodeUrl(classPath); fillClasses(classPath, packageName, classFilter, classes); } // 如果在项目的ClassPath中未找到,去系统定义的ClassPath里找 if(inJar){ for(String classPath : getJavaClassPaths()){ // 填充 classes, 并对classpath解码 classPath = decodeUrl(classPath); fillClasses(classPath, new File(classPath), packageName, classFilter, classes); } } return classes; } /** * 获得ClassPath * * @param packageName * 包名称 * @return ClassPath路径字符串集合 */ public static Set getClassPaths(String packageName){ String packagePath = packageName.replace(".", "/"); Enumeration resources; try{ resources = getClassLoader().getResources(packagePath); } catch(IOException e){ throw new RuntimeException(String.format("Loading classPath [%s] error!", packageName), e); } Set paths = new HashSet(); while(resources.hasMoreElements()){ paths.add(resources.nextElement().getPath()); } return paths; } /** * @return 获得Java ClassPath路径,不包括 jre */ public static String[] getJavaClassPaths(){ return System.getProperty("java.class.path").split(System.getProperty("path.separator")); } /** * 获取当前线程的ClassLoader * * @return 当前线程的class loader */ public static ClassLoader getContextClassLoader(){ return Thread.currentThread().getContextClassLoader(); } /** * 获得class loader 若当前线程class loader不存在,取当前类的class loader * * @return 类加载器 */ public static ClassLoader getClassLoader(){ ClassLoader classLoader = getContextClassLoader(); if(classLoader == null){ classLoader = ClassUtil.class.getClassLoader(); } return classLoader; } /** * 根据指定的类名称加载类 * * @param className * 完整类名 * @return {Class} * @throws ClassNotFoundException * 找不到异常 */ public static Class loadClass(String className) throws ClassNotFoundException{ try{ return ClassUtil.getContextClassLoader().loadClass(className); } catch(ClassNotFoundException e){ try{ return Class.forName(className, false, ClassUtil.getClassLoader()); } catch(ClassNotFoundException ex){ try{ return ClassLoader.class.getClassLoader().loadClass(className); } catch(ClassNotFoundException exc){ throw exc; } } } } // --------------------------------------------------------------------------------------------------- // Private method start /** * 文件过滤器,过滤掉不需要的文件 只保留Class文件、目录和Jar */ private static FileFilter fileFilter = new FileFilter(){ @Override public boolean accept(File pathname){ return isClass(pathname.getName()) || pathname.isDirectory() || isJarFile(pathname); } }; /** * 改变 com -> com. 避免在比较的时候把比如 completeTestSuite.class类扫描进去,如果没有"." * 那class里面com开头的class类也会被扫描进去,其实名称后面或前面需要一个 ".",来添加包的特征 * * @param packageName * 包名 * @return 格式化后的包名 */ private static String getWellFormedPackageName(String packageName){ return packageName.lastIndexOf('.') != packageName.length() - 1 ? packageName + '.' : packageName; } /** * 去掉指定前缀 * * @param str * 字符串 * @param prefix * 前缀 * @return 切掉后的字符串,若前缀不是 preffix, 返回原字符串 */ private static String removePrefix(String str, String prefix){ if(str != null && str.startsWith(prefix)){ return str.substring(prefix.length()); } return str; } /** * 填充满足条件的class 填充到 classes 同时会判断给定的路径是否为Jar包内的路径,如果是,则扫描此Jar包 * * @param path * Class文件路径或者所在目录Jar包路径 * @param packageName * 需要扫面的包名 * @param classFilter * class过滤器 * @param classes * List 集合 */ private static void fillClasses(String path, String packageName, ClassFilter classFilter, Set> classes){ // 判定给定的路径是否为Jar int index = path.lastIndexOf(JAR_PATH_EXT); if(index != -1){ // Jar文件 path = path.substring(0, index + JAR_FILE_EXT.length()); // 截取jar路径 path = removePrefix(path, PATH_FILE_PRE); // 去掉文件前缀 processJarFile(new File(path), packageName, classFilter, classes); } else{ fillClasses(path, new File(path), packageName, classFilter, classes); } } /** * 填充满足条件的class 填充到 classes * * @param classPath * 类文件所在目录,当包名为空时使用此参数,用于截掉类名前面的文件路径 * @param file * Class文件或者所在目录Jar包文件 * @param packageName * 需要扫面的包名 * @param classFilter * class过滤器 * @param classes * List 集合 */ private static void fillClasses(String classPath, File file, String packageName, ClassFilter classFilter, Set> classes){ if(file.isDirectory()){ processDirectory(classPath, file, packageName, classFilter, classes); } else if(isClassFile(file)){ processClassFile(classPath, file, packageName, classFilter, classes); } else if(isJarFile(file)){ processJarFile(file, packageName, classFilter, classes); } } /** * 处理如果为目录的情况,需要递归调用 fillClasses方法 * * @param directory * 目录 * @param packageName * 包名 * @param classFilter * 类过滤器 * @param classes * 类集合 */ private static void processDirectory(String classPath, File directory, String packageName, ClassFilter classFilter, Set> classes){ File[] files = directory.listFiles(fileFilter); if(null == files || files.length == 0){ return; } for(File file : files){ fillClasses(classPath, file, packageName, classFilter, classes); } } /** * 处理为class文件的情况,填充满足条件的class 到 classes * * @param classPath * 类文件所在目录,当包名为空时使用此参数,用于截掉类名前面的文件路径 * @param file * class文件 * @param packageName * 包名 * @param classFilter * 类过滤器 * @param classes * 类集合 */ private static void processClassFile(String classPath, File file, String packageName, ClassFilter classFilter, Set> classes){ if(false == classPath.endsWith(File.separator)){ classPath += File.separator; } String path = file.getAbsolutePath(); if(StrUtil.isBlank(packageName)){ path = removePrefix(path, classPath); } final String filePathWithDot = path.replace(File.separator, "."); int subIndex = -1; if((subIndex = filePathWithDot.indexOf(packageName)) != -1){ final int endIndex = filePathWithDot.lastIndexOf(CLASS_EXT); final String className = filePathWithDot.substring(subIndex, endIndex); fillClass(className, packageName, classes, classFilter); } } /** * 处理为jar文件的情况,填充满足条件的class 到 classes * * @param file * jar文件 * @param packageName * 包名 * @param classFilter * 类过滤器 * @param classes * 类集合 */ private static void processJarFile(File file, String packageName, ClassFilter classFilter, Set> classes){ try{ for(JarEntry entry : Collections.list(new JarFile(file).entries())){ if(isClass(entry.getName())){ final String className = entry.getName().replace("/", ".").replace(CLASS_EXT, ""); fillClass(className, packageName, classes, classFilter); } } } catch(Throwable ex){ } } /** * 填充class 到 classes * * @param className * 类名 * @param packageName * 包名 * @param classes * 类集合 * @param classFilter * 类过滤器 */ private static void fillClass(String className, String packageName, Set> classes, ClassFilter classFilter){ if(className.startsWith(packageName)){ try{ final Class clazz = ClassUtil.loadClass(className); if(classFilter == null || classFilter.accept(clazz)){ classes.add(clazz); } } catch(Throwable ex){ } } } /** * @param file * 文件 * @return 是否为类文件 */ private static boolean isClassFile(File file){ return isClass(file.getName()); } /** * @param fileName * 文件名 * @return 是否为类文件 */ private static boolean isClass(String fileName){ return fileName.endsWith(CLASS_EXT); } /** * @param file * 文件 * @return是否为Jar文件 */ private static boolean isJarFile(File file){ return file.getName().endsWith(JAR_FILE_EXT); } // --------------------------------------------------------------------------------------------------- // Private method end /** * 类过滤器,用于过滤不需要加载的类 */ public interface ClassFilter{ boolean accept(Class clazz); } /** * 对路径解码 * * @param url * 路径 * @return String 解码后的路径 */ private static String decodeUrl(String url){ try{ return URLDecoder.decode(url, "UTF-8"); } catch(java.io.UnsupportedEncodingException e){ throw new RuntimeException(e); } } public static void makeAccessible(Constructor ctor) { if ((!Modifier.isPublic(ctor.getModifiers()) || !Modifier.isPublic(ctor.getDeclaringClass().getModifiers())) && !ctor.isAccessible()) { ctor.setAccessible(true); } } /** * 确定class是否可以被加载 * @param classNames 完整类名集合,全部满足才返回true * @param classLoader 类加载 * @return {boolean} */ public static boolean isPresent(ClassLoader classLoader , String... classNames) { try { for (String className : classNames) { Class.forName(className, true, classLoader); } return true; } catch (Throwable ex) { // Class or one of its dependencies is not present... return false; } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy