Java类加载器--自定义类加载器(ClassLoader)

文章引用:

https://blog.csdn.net/xyang81/article/details/7292380


既然JVM已经提供了默认的类加载器,为什么还要定义自已的类加载器呢?

因为Java中提供的默认ClassLoader,只加载指定目录下的jar和class,如果我们想加载其它位置的类或jar时,比如:我要加载网络上的一个class文件,通过动态加载到内存之后,要调用这个类中的方法实现我的业务逻辑。在这样的情况下,默认的ClassLoader就不能满足我们的需求了,所以需要定义自己的ClassLoader。

定义自已的类加载器分为两步:

1、继承java.lang.ClassLoader

2、重写父类的findClass方法

读者可能在这里有疑问,父类有那么多方法,为什么偏偏只重写findClass方法?

因为JDK已经在loadClass方法中帮我们实现了ClassLoader搜索类的算法,当在loadClass方法中搜索不到类时,loadClass方法就会调用findClass方法来搜索类,所以我们只需重写该方法即可。如没有特殊的要求,一般不建议重写loadClass搜索类的算法。下图是API中ClassLoader的loadClass方法:

这里写图片描述


示例:自定义一个NetworkClassLoader,用于加载网络上的class文件

public class NetWorkClassLoader extends ClassLoader {

    private String rootUrl;

    public NetWorkClassLoader(String rootUrl) {
        this.rootUrl = rootUrl;
    }

    @SuppressWarnings("rawtypes")
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        Class clazz = null;
        byte[] classData = getClassData(name);
        if (classData == null) {
            throw new ClassNotFoundException();
        }

        // 将class的字节码数组转换成Class类的实例
        clazz = defineClass(name, classData, 0, classData.length);
        return clazz;
    }

    /**
     * 根据类的二进制名称,获得该class文件的字节码数组
     * 
     * @param name
     * @return
     */
    private byte[] getClassData(String name) {
        InputStream inputStream = null;
        try {
            String path = classNameToPath(name);
            URL url = new URL(path);

            byte[] buff = new byte[1024 * 4];
            int len = -1;
            inputStream = url.openStream();
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            while ((len = inputStream.read(buff)) != -1) {
                byteArrayOutputStream.write(buff, 0, len);
            }
            return byteArrayOutputStream.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            IOUtils.closeQuietly(inputStream);
        }
        return null;
    }

    private String classNameToPath(String name) {
        return rootUrl + "/" + name.replace(".", "/") + ".class";
    }

    /*
        通过URL读取网页内容
            1通过URL对象的openStream()方法能够得到指定资源的输入流。
            2通过输入流能够读取、訪问网络上的数据。
     */

}

测试类:

public static void main(String[] args) {
    try {
        String rootUrl = "http://localhost:8080/httpweb/classes";
        NetWorkClassLoader networkClassLoader = new NetWorkClassLoader(rootUrl);
        String classname = "org.classloader.simple.NetClassLoaderTest";
        Class clazz = networkClassLoader.loadClass(classname);
        System.out.println(clazz.getClassLoader());

    } catch (Exception e) {
        e.printStackTrace();
    }
}

打印结果:

这里写图片描述


下图是我机器上web服务器的目录结构:

这里写图片描述

目前常用web服务器中都定义了自己的类加载器,用于加载web应用指定目录下的类库(jar或class),如:Weblogic、Jboss、tomcat等,下面我以Tomcat为例,展示该web容器都定义了哪些个类加载器:

1、新建一个web工程httpweb

2、新建一个ClassLoaderServletTest,用于打印web容器中的ClassLoader层次结构


import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ClassLoaderServletTest extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        ClassLoader loader = this.getClass().getClassLoader();
        while(loader != null) {
            out.write(loader.getClass().getName()+"<br/>");
            loader = loader.getParent();
        }
        out.write(String.valueOf(loader));
        out.flush();
        out.close();
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        this.doGet(request, response);
    }

}

3、配置Servlet,并启动服务

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" 
    xmlns="http://java.sun.com/xml/ns/j2ee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee 
    http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
  <servlet>
    <servlet-name>ClassLoaderServletTest</servlet-name>
    <servlet-class>ClassLoaderServletTest</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>ClassLoaderServletTest</servlet-name>
    <url-pattern>/servlet/ClassLoaderServletTest</url-pattern>
  </servlet-mapping>
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
</web-app>

4、访问Servlet,获得显示结果

这里写图片描述

猜你喜欢

转载自blog.csdn.net/u013412772/article/details/80848909