获取 包 的类名信息以及 使用类名信息反向实例化类

反实例化

所有类实现 DeviceInterface 接口本处不做简绍

  • Class.forName(classPath)
    Class.forName(classPath) 是 Java 反射 API 中的一个方法,用于根据给定的类名(通常是完全限定的类名,即包含包名的类名)加载类。这个方法会在类路径(classpath)中查找指定的类,并返回一个 Class 对象,该对象代表了指定类的运行时类信息。
  • DeviceInterface.class.isAssignableFrom
    判断是否对应类信息
  • clazz.getDeclaredConstructor()
    clazz.getDeclaredConstructor() 是 Java 反射 API 中的一个方法,用于获取一个类中声明的构造函数(即没有继承的构造函数)。此方法仅返回类中声明的构造函数,而不包括从父类继承的构造函数。如果类中没有任何构造函数,那么这个方法将返回 null
  • constructor.newInstance();
    返回实例
String classPath = devicePackAge + "." + className;
Class<?> aclazz = Class.forName(classPath);
if(DeviceInterface.class.isAssignableFrom(aclazz)){
	return createInstance((Class<DeviceInterface>) aclazz);
}

private static <T> T createInstance(Class<T> clazz) throws Exception {
	Constructor<T> constructor = clazz.getDeclaredConstructor();
	return constructor.newInstance();
}
获取对应的包路径

在处理 Java 类加载器和资源定位时,URL 对象的 getProtocol() 方法返回的协议(protocol)指定了资源的来源类型。常见的协议有两种:file 和 jar。

  • file 协议
    当 URL 的协议是 file 时,它表示资源位于本地文件系统中。这种情况下,URL 的路径部分通常指向一个文件或目录的本地路径。
  • jar 协议
    当 URL 的协议是 jar 时,它表示资源位于一个 JAR 文件中。这种情况下,URL 的路径部分通常指向一个 JAR 文件的位置,后面跟着一个 ! 符号,再跟上一个内部路径,指示 JAR 文件内的某个资源位置。
public static List<String> getClassName(String packageName, boolean recursive) {
        List<String> classes = new ArrayList<>();
        String packageDirName = packageName.replace('.', '/');
        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");
                    findAndAddClassNamesByFile(packageName, filePath, recursive, classes);
                } else if ("jar".equals(protocol)) {
                    handleJarFile(url, packageName, recursive, classes);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return classes;
    }
获取 file 的 class 文件类名

获取项目运行时 的class 文件信息获取类名

private static void findAndAddClassNamesByFile(String packageName, String packagePath, final boolean recursive, List<String> classes) {
        File dir = new File(packagePath);
        if (!dir.exists() || !dir.isDirectory()) {
            return;
        }
        File[] dirfiles = dir.listFiles(file -> (recursive && file.isDirectory()) || (file.getName().endsWith(".class")));
        if (dirfiles != null) {
            for (File file : dirfiles) {
                if (file.isDirectory()) {
                    findAndAddClassNamesByFile(
                            packageName + "." + file.getName(),
                            file.getAbsolutePath(),
                            recursive,
                            classes
                    );
                } else {
                    String className = file.getName().substring(0, file.getName().length() - 6); // Remove .class extension
                    if (className.length() > 0) {
                        classes.add(className);
                    }
                }
            }
        }
    }
获取 jar 包class 文件类名
  • 处理文件系统路径 (file 协议):
    使用 URLDecoder.decode 来解码文件路径。
    调用 findAndAddClassesInPackageByFile 方法来递归地查找文件系统中的类文件。
  • 处理 JAR 文件 (jar 协议):
    使用 URL.openStream() 打开 JAR 文件流。
    创建 JarInputStream 来读取 JAR 文件中的条目。
    遍历 JAR 文件中的所有条目,并检查每个条目是否是一个类文件。
    如果是类文件,则将其名字添加到类列表中。
    如果是目录并且递归标志为 true,则递归处理子目录。
  • 递归处理子目录:
    当遇到 JAR 文件中的子目录时,可以递归调用 handleJarFile 方法来处理子目录中的类文件。
  • getNextJarEntry:
    getNextJarEntry 是 java.util.jar.JarInputStream 类中的一个方法,用于从 JAR 文件流中读取下一个条目(即 JarEntry)。这个方法在处理 JAR 文件时非常有用,因为它允许你逐个读取 JAR 文件中的每个条目,并对其进行处理。
private static void handleJarFile(URL jarUrl, String packageName, boolean recursive, List<String> classes) throws IOException {
	InputStream is = jarUrl.openStream();
	JarInputStream jis = new JarInputStream(is);
	try {
		JarEntry entry;
		while ((entry = jis.getNextJarEntry()) != null) {
			String name = entry.getName();
			if (name.startsWith(packageName) && name.endsWith(".class")) {
				String className = name.substring(0, name.length() - 6).replace('/', '.');
				classes.add(className);
			} else if (recursive && entry.isDirectory()) {
				String subPackageName = name.replace('/', '.').substring(0, name.length() - 1);
				if (!subPackageName.equals(packageName)) {
					handleJarFile(jarUrl, subPackageName, recursive, classes);
				}
			}
		}
	} finally {
		jis.close();
	}
}
完整Utils方法

这个是完整的 整个 utils 方法,通过filie获取跟jar 包获取, 用于获取对应的类名去反项实例化实体类去调用方法

package org.gateway.utils.scanner;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;

public class PackageScanner {

    public static List<String> getClassName(String packageName, boolean recursive) {
        List<String> classes = new ArrayList<>();
        String packageDirName = packageName.replace('.', '/');
        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");
                    findAndAddClassNamesByFile(packageName, filePath, recursive, classes);
                } else if ("jar".equals(protocol)) {
                    handleJarFile(url, packageName, recursive, classes);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return classes;
    }

    private static void findAndAddClassNamesByFile(String packageName, String packagePath, final boolean recursive, List<String> classes) {
        File dir = new File(packagePath);
        if (!dir.exists() || !dir.isDirectory()) {
            return;
        }
        File[] dirfiles = dir.listFiles(file -> (recursive && file.isDirectory()) || (file.getName().endsWith(".class")));
        if (dirfiles != null) {
            for (File file : dirfiles) {
                if (file.isDirectory()) {
                    findAndAddClassNamesByFile(
                            packageName + "." + file.getName(),
                            file.getAbsolutePath(),
                            recursive,
                            classes
                    );
                } else {
                    String className = file.getName().substring(0, file.getName().length() - 6); // Remove .class extension
                    if (className.length() > 0) {
                        classes.add(className);
                    }
                }
            }
        }
    }

    private static void handleJarFile(URL jarUrl, String packageName, boolean recursive, List<String> classes) throws IOException {
        InputStream is = jarUrl.openStream();
        JarInputStream jis = new JarInputStream(is);
        try {
            JarEntry entry;
            while ((entry = jis.getNextJarEntry()) != null) {
                String name = entry.getName();
                if (name.startsWith(packageName) && name.endsWith(".class")) {
                    String className = name.substring(0, name.length() - 6).replace('/', '.');
                    classes.add(className);
                } else if (recursive && entry.isDirectory()) {
                    String subPackageName = name.replace('/', '.').substring(0, name.length() - 1);
                    if (!subPackageName.equals(packageName)) {
                        handleJarFile(jarUrl, subPackageName, recursive, classes);
                    }
                }
            }
        } finally {
            jis.close();
        }
    }
}
  • 7
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值