前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >类加载器之URLClassLoader

类加载器之URLClassLoader

作者头像
每天学Java
发布2020-08-05 16:39:36
5.7K0
发布2020-08-05 16:39:36
举报
文章被收录于专栏:每天学Java

在前面关于类加载子系统的文章中,我们提到启动类加载器是其他类加载器的parent,其本身parent为null、扩展类加载器的parent是启动类加载器、应用类加载器的parent是扩展类加载器,但是在那篇文章中并没有强调parent代表的含义是一个“委派”体系,而并不是继承关系

在类加载器的体系中,真实的继承关系如下:

通过源码我们可以发现AppClassLoader和ExtClassLoader都是Launcher的静态内部类,继承自URLClassLoader。

那么URLClassLoader和SecureClassLoader具体是做什么的呢?

  • SecureClassLoader:扩展了ClassLoader,并为定义具有相关代码源和权限的类提供了额外支持,这些代码源和权限默认情况下由系统策略检索。
  • URLClassLoader:继承自SecureClassLoader,支持从jar文件和文件夹中获取class,继承于classload,加载时首先去classload里判断是否由启动类加载器加载过。

今天这篇文章我们重点要说的就是URLClassLoader,在上面类加载器的真实继承关系图中,我们知道URLClassLoader扩展了ClassLoader,所以它在ClassLoader的基础上扩展了一些功能,这些扩展的功能中,最主要的一点就是URLClassLoader却可以加载任意路径下的类(ClassLoader只能加载classpath下面的类)。

如果你未接触URLClassLoader,那么要实现动态加载类都是使用用Class.forName()这个方法,但是这个方法只能创建程序中已经引用的类,如果我们需要动态加载程序外的类,Class.forName()是不够的,这个时候就是需要使用URLClassLoader的时候。

在我的个人项目中,对于URLClassLoader是有实际使用过的,这里以我的项目作为案例,来看一下URLClassLoader的使用。

背景: 在我的个人网站和小程序接口开发的最开始阶段,是使用Spring Boot来开发的,这种方式搭建接口十分便捷,但是在后面不断学习的过程中,我萌发了自己实现一个类似Tomcat的小应用,然后模拟Spring实现自己的一套开发规范。这套个人项目首先是一个Maven项目,以Netty为基础实现Http协议,然后参考Spring实现DI和AOP,以及一系列的注解实现接口的开发,下面大概展示一些目录结构:

那么这个应用和URLClassLoader有什么关系呢?

首先一个普通Maven项目在打包成jar的时候,jar包内部本身是不包含依赖的,我们可以通过Maven-assembly-plugin插件将程序和它本身所依赖的jar包一起打到一个包里,但是这种方式每次打包后,jar都会比较大,所以最终采用依赖和项目代码进行分离的方案,将依赖jar放入lib文件下。

代码语言:javascript
复制
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <executions>
                    <execution>
                        <id>copy-dependencies</id>
                        <phase>package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <!--target/lib是依赖jar包的输出目录,根据自己喜好配置-->
                            <outputDirectory>target/lib</outputDirectory>
                            <excludeTransitive>false</excludeTransitive>
                            <stripVersion>false</stripVersion>
                            <includeScope>runtime</includeScope>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

当程序代码和依赖代码分离之后,如何部署到服务器成为了一个问题,最开始我想的是将其打包为一个可执行的jar,以类似SpringBoot的方式去部署,但是思考之后我更倾向于能有一个统一的部署方案,对于后续新的应用也可以部署,于是就想到了URLClassLoader:将项目jar和lib下的依赖jar放入URLClassLoader类的urls(存储加载的classes和resources)中,然后通过JarFile对项目jar进行遍历,找到程序的入口Main,然后使用URLClassLoader对类进行加载,最终启动它。这里的jar和lib路径是使用参数进行传递(这里为了直观所以写死),然后将程序编译为可执行的命令(参考模拟javac命令文章),后续就可以进行统一部署发布了

代码语言:javascript
复制
   public static void main(String[] args) throws Exception {
        String jar = "/jerry/target/jerry-1.0-SNAPSHOT.jar";
        String lib = "/jerry/target/lib";
        JarFile jarFile = new JarFile(new File(jar));
        Enumeration<JarEntry> entry = jarFile.entries();
        //Lib文件+项目Jar
        URL url = new URL("file:" + jar);
        File file_directory = new File(lib);
        URL[] urls = new URL[file_directory.listFiles().length + 1];
        int i = 0;
        urls[i++] = url;
        for (File file : file_directory.listFiles()) {
            urls[i++] = new URL("file:" + file.getAbsolutePath());
        }
        ClassLoader loader = new URLClassLoader(urls);
        Class<?> c = null;
        Object o = null;
        while (entry.hasMoreElements()) {
            JarEntry jarEntry = entry.nextElement();
            String name = jarEntry.getName();
            System.out.println(name);
            if (name != null && name.endsWith(".class")) {
                if (name.equals("Main.class")) {
                    c = loader.loadClass(name.substring(0, name.length() - 6));
                    o = c.newInstance();
                }
            }
        }
        if (o != null) {
            System.out.println(o);
            System.out.println(c);
            Method method = c.getDeclaredMethod("main", String[].class);
            method.invoke(o, (Object) new String[]{});
        }
    }
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-08-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 每天学Java 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云服务器
云服务器(Cloud Virtual Machine,CVM)提供安全可靠的弹性计算服务。 您可以实时扩展或缩减计算资源,适应变化的业务需求,并只需按实际使用的资源计费。使用 CVM 可以极大降低您的软硬件采购成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档