利用URLClassLoader读取Jar包并反射类(利用Tomcat源码)

本文介绍如何通过自定义ClassLoader读取本地Jar包,并利用反射获取实体类的类名及属性等信息。主要涉及自定义ClassLoader的创建、配置及使用过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第一次写博客,写的不好请见谅。

背景:在公司内部项目中碰到了一个需求,我需要读取本地jar包然后反射其中的实体类来获取其类名属性等字段并保存。

分析:显然,我们首先需要用到反射,但是反射需要有类加载器来加载类。比如classLoader.load("com.test.Test");对加载器不了解的朋友可以自行百度。因为是读的本地的jar包,我们当前web用的appClassLoader显然不够用,也无法load本地的类,因此我们可以自己使用URLClassLoader创建一个加载器。(在这里我是用了tomcat生成自定义classloader的方法,很简单)。

方法:将jar存在项目根路径下,创建ClassLoader之前获取jar包的所有路径即可。

实施


/**
 * 创建classloader(调用了tomcatapi * @return
 * @throws Exception
 */
private ClassLoader createClassLoader() throws Exception {
    //在这里拿到存在本地的jar包的路径
    //模仿tomcat的处理方式
    String value = JarProperties.getProperty("jar.path");
    if (value == null) {
        return null;
    }
    //处理字符串路径
    value = replace(value);
    List<ClassLoaderFactory.Repository> repositories = new ArrayList<>();
    //分割成路径数组
    String[] repositoryPaths = getPaths(value);

    for (String repository : repositoryPaths) {
        // Local repository
        if (repository.endsWith("*.jar")) {
            repository = repository.substring
                    (0, repository.length() - "*.jar".length());
            repositories.add(
                    new ClassLoaderFactory.Repository(repository, ClassLoaderFactory.RepositoryType.GLOB));
        } else if (repository.endsWith(".jar")) {
            repositories.add(
                    new ClassLoaderFactory.Repository(repository, ClassLoaderFactory.RepositoryType.JAR));
        } else {
            repositories.add(
                    new ClassLoaderFactory.Repository(repository, ClassLoaderFactory.RepositoryType.DIR));
        }
    }
    return ClassLoaderFactory.createClassLoader(repositories, null);
}
	其中JarProperties如下:

public class JarProperties { private static final Logger logger = LoggerFactory.getLogger(JarProperties.class); private static Properties properties = null; static { loadProperties(); } /**  * @param name The property name  * @return specified property value  */  public static String getProperty(String name) { return properties.getProperty(name); } private static void loadProperties() { InputStream is = null; try { ClassLoader cl = JarProperties.class.getClassLoader(); URL url = cl.getResource("jar.properties"); if (url != null) { is = url.openStream(); } } catch (Exception e) { e.printStackTrace(); logger.error("url打开stream错误"); // logger.error(e.getMessage() + "没有找到.properties文件");  } if (is != null) { try { properties = new Properties(); properties.load(is); } catch (Exception e) { e.printStackTrace(); logger.error("加载jar.properties文件出错"); } finally { try { is.close(); } catch (IOException e) { e.printStackTrace(); logger.error("无法关闭jar.properties" + e); } } } if (is == null) { // Do something  logger.error("没有找到jar.properties文件"); // That's fine - we have reasonable defaults.  properties = new Properties(); } } }
	jar.properties:

jar.path = "${authority.home}/jar","${authority.home}/jar/*.jar"
然后在ClassLoaderFactory.createClassLoader(repositories, null)中:

/**  * Create and return a new class loader, based on the configuration  * defaults and the specified directory paths:  *  * @param repositories List of class directories, jar files, jar directories  * or URLS that should be added to the repositories of  * the class loader.  * @param parent Parent class loader for the new class loader, or  * <code>null</code> for the system class loader.  * @return the new class loader  *  * @exception Exception if an error occurs constructing the class loader  */ public static ClassLoader createClassLoader(List<Repository> repositories, final ClassLoader parent) throws Exception { if (log.isDebugEnabled()) log.debug("Creating new class loader"); // Construct the "class path" for this class loader  Set<URL> set = new LinkedHashSet<>(); if (repositories != null) { for (Repository repository : repositories) { if (repository.getType() == RepositoryType.URL) { URL url = buildClassLoaderUrl(repository.getLocation()); if (log.isDebugEnabled()) log.debug(" Including URL " + url); set.add(url); } else if (repository.getType() == RepositoryType.DIR) { File directory = new File(repository.getLocation()); directory = directory.getCanonicalFile(); if (!validateFile(directory, RepositoryType.DIR)) { continue; } URL url = buildClassLoaderUrl(directory); if (log.isDebugEnabled()) log.debug(" Including directory " + url); set.add(url); } else if (repository.getType() == RepositoryType.JAR) { File file=new File(repository.getLocation()); file = file.getCanonicalFile(); if (!validateFile(file, RepositoryType.JAR)) { continue; } URL url = buildClassLoaderUrl(file); if (log.isDebugEnabled()) log.debug(" Including jar file " + url); set.add(url); } else if (repository.getType() == RepositoryType.GLOB) { File directory=new File(repository.getLocation()); directory = directory.getCanonicalFile(); if (!validateFile(directory, RepositoryType.GLOB)) { continue; } if (log.isDebugEnabled()) log.debug(" Including directory glob "  + directory.getAbsolutePath()); String filenames[] = directory.list(); if (filenames == null) { continue; } for (int j = 0; j < filenames.length; j++) { String filename = filenames[j].toLowerCase(Locale.ENGLISH); if (!filename.endsWith(".jar")) continue; File file = new File(directory, filenames[j]); file = file.getCanonicalFile(); if (!validateFile(file, RepositoryType.JAR)) { continue; } if (log.isDebugEnabled()) log.debug(" Including glob jar file "  + file.getAbsolutePath()); URL url = buildClassLoaderUrl(file); set.add(url); } } } } // Construct the class loader itself  final URL[] array = set.toArray(new URL[set.size()]); if (log.isDebugEnabled()) for (int i = 0; i < array.length; i++) { log.debug(" location " + i + " is " + array[i]); } return AccessController.doPrivileged( new PrivilegedAction<URLClassLoader>() { @Override  public URLClassLoader run() { if (parent == null) return new URLClassLoader(array); else  return new URLClassLoader(array, parent); } }); }

具体注释我就不写了,就是把路径处理一下然后放在数组中然后new URLClassLoader(array)就可以。

	大部分代码都是tomcat源码中的,我是直接用的tomcat的类完成的这个接口。所以说,多看源码还是很有帮助滴。

你可以使用URLClassLoader来加载Jar里面的Jar。首先,你需要创建一个URLClassLoader对象,将需要加载的Jar路径传递给它。然后,你可以使用该ClassLoader对象来加载Jar中的或资源。 以下是一个示例代码,演示了如何使用URLClassLoader加载Jar里面的Jar: ```java import java.net.URL; import java.net.URLClassLoader; public class Main { public static void main(String[] args) throws Exception { // 定义需要加载的Jar路径 String jarPath = "path/to/your/jar/file.jar"; // 创建URL数组,含需要加载的Jar路径 URL[] urls = new URL[]{new URL("file:" + jarPath)}; // 创建URLClassLoader对象,将Jar路径传递给它 URLClassLoader classLoader = new URLClassLoader(urls); // 加载Jar中的 Class<?> loadedClass = classLoader.loadClass("com.example.SomeClass"); // 创建该的实例,调用方法 Object instance = loadedClass.getDeclaredConstructor().newInstance(); loadedClass.getMethod("someMethod").invoke(instance); } } ``` 在上述代码中,你需要将"path/to/your/jar/file.jar"替换为实际的Jar路径。然后,你可以使用URLClassLoader对象加载Jar中的调用其中的方法。 请注意,加载嵌套的Jar可能会导致一些加载问题,因此你可能需要注意处理这些问题。此外,记得在使用完URLClassLoader后,关闭它以释放资源。 希望对你有所帮助!如有更多问题,请随时提问。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值