JAVA笔记2.类加载子系统与SPI机制

类加载子系统

在这里插入图片描述

核心代码:
• private static Launcher launcher = new Launcher();
• extcl = ExtClassLoader.getExtClassLoader();
• loader = AppClassLoader.getAppClassLoader(extcl);
• Thread.currentThread().setContextClassLoader(loader);

1.启动类加载器

启动类加载器是由C++编写,不像其它加载类一样有实体类,jvm是将c++处理类加载的一段逻辑定义为启动类加载器
查看启动类的加载路径

    /**
     * 启动类加载器
     * bootstrap classloader path
     * -Xbootclasspath 指定路径
     */
    public void BootstrapClassLoader() {
        URLClassPath bootstrapClassPath = Launcher.getBootstrapClassPath();
        for (URL url : bootstrapClassPath.getURLs()) {
            System.out.println(url);
        }
    }

openjdk1.8 源码
在这里插入图片描述

2.拓展类加载器

    /**
     * 拓展类加载器
     * ext classloader path
     * java.ext.dirs 指定路径
     */
    public void ExtClassLoader() {
        ClassLoader parent = ClassLoader.getSystemClassLoader().getParent();
        URLClassLoader urlClassLoader = (URLClassLoader) parent;
        Arrays.stream(urlClassLoader.getURLs()).forEach((u)-> System.out.println(u));
    }

3.应用程序加载器

    /**
     * 应用类加载器
     * app classloader path
     * java.class.path 指定路径
     */
    public void AppClassLoader() {
        URLClassLoader systemClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
        Arrays.stream(systemClassLoader.getURLs()).forEach(s-> System.out.println(s));
    }

4.自定义类加载器

    /**
     * 实现自定义加载器的步骤:
     * 1.实现ClassLoader接口
     * 2.重写超类的findClass方法
     * @param name
     * @return
     * @throws ClassNotFoundException
     */

    @Override
    public Class<?> findClass(String name) throws ClassNotFoundException {

        String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";
        InputStream is = getClass().getResourceAsStream(fileName);
        if (is !=null) {
            byte[] b = new byte[0];
            try {
                b = new byte[is.available()];
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                is.read(b);
            } catch (IOException e) {
                e.printStackTrace();
            }
            return defineClass(name, b, 0, b.length);

        }
        return null ;
    }

5.线程上下文类加载器在这里插入图片描述

1.线程上下文类加载器:是一种特殊的类加载器,为了解决双亲委派的缺陷而产生的,SPI机制运用到

 public static <S> ServiceLoader<S> load(Class<S> service) {
              ClassLoader cl = Thread.currentThread().getContextClassLoader();
              return ServiceLoader.load(service, cl);
          }

2.获取:Thread.currentThread().getContextClassLoader();
设置:Thread.currentThread().setContextClassLoader(new KasonClassLoader());

6.类加载器存储空间

在这里插入图片描述
ps:不同类加载器在方法区会分配不同的内存区域

7.双亲委派

如果一个类加载器收到了加载某个类的请求,则该类加载器并不会去加载该类,而是把这个请求委派给父类加载器,每一个层次的类加载器都是如此,因此所有的类加载请求最终都会传送到顶端的启动类加载器;只有当父类加载器在其搜索范围内无法找到所需的类,并将该结果反馈给子类加载器,子类加载器会尝试去自己加载。
ps:类加载器存在逻辑上的父子关系,自定义->app->拓展类->启动类
类加载器加载类,首先自己不会去加载,会委托父类去加载,直到找到,启动类加载器找不到,在由自定义加载器去加载,如果没有直接抛异常
在这里插入图片描述

8.双亲委派的局限性(打破双亲委派)

优点:安全性高
缺陷:无法做到向下委派
在某些情况,父类的加载器需要委托子类去加载类信息,这个时候双亲委派无法满足
经典的例子:各种数据库的驱动类,如:mysql,oracle
DriverManager(也由jdk提供)要加载各个实现了Driver接口的实现类,然后进行管理,但是DriverManager由启动类加载器加载,只能记载JAVA_HOME的lib下文件,而其实现是由服务商提供的,由系统类加载器加载,这个时候就需要启动类加载器来委托子类来加载Driver实现

这个时候需要打破双亲委派,
SPI机制:运用到线程上下文类加载器,ServiceLoader
实现SPI机制的步骤
1.定义一个接口
2.实现接口类
3.在META-INF/services/下,添加接口类的全限定类名文件,内容是实现类的全限定类名,多个用换行
4.ServiceLoader.load 加载
简单代码实现:https://gitee.com/kasonlj/kason-spring-v1.0/tree/master/src/com/kason/spi
自定义类加载器也可以打破双亲委派
https://gitee.com/kasonlj/kason-spring-v1.0/tree/master/src/com/kason/classloader

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值