java spi 类加载_类加载机制及SPI

最近重温Java类加载及双亲委派机制,并写了一个SPI的例子

56638f702352416238cb655a30afbdaa.png

从网上找了一张图片,对着图片及课堂笔记来梳理下。

首先java自带的类加载器分为BootStrapClassLoader(引导\启动类加载器),ExtClassLoader(扩展类加载器),AppClassLoader(应用程序类加载器)三种,此外还支持用户自己定义的自定义类加载器,加载的是用户自己指定的目录。

BootStrapClassLoader:jvm中,c++处理类加载的这套逻辑,被称为启动类加载器,是由c++编写的,在java中为null,加载的路径是Jre/lib/rt.jar, 在这个过程中会通过启动类加载器,来加载sun.launcher.LauncherHelper,并执行checkAndLoadMain,以及加载main函数所在的类,并启动扩展类加载器、应用类加载器

ExtClassLoader: 扩展类加载器,加载的是Jre/lib/ext/*.jar,查看方式:

public static voidmain(String[] args) {

ClassLoader classLoader=ClassLoader.getSystemClassLoader().getParent();

URLClassLoader urlClassLoader=(URLClassLoader) classLoader;

URL[] urls=urlClassLoader.getURLs();for(URL url : urls) {

System.out.println(url);

}

}

AppClassLoader: 应用类加载器,加载用户程序的类加载器,加载的是CLASS_PATH中指定的所有jar

public static voidmain(String[] args) {

String[] urls= System.getProperty("java.class.path").split(":");for(String url : urls) {

System.out.println(url);

}

System.out.println("---------------------------------------------------------");

URLClassLoader classLoader=(URLClassLoader) ClassLoader.getSystemClassLoader();

URL[] urls1=classLoader.getURLs();for(URL url : urls1) {

System.out.println(url);

}

}

双亲委派机制:类加载时,AppClassLoader 会先查看自身是否已经加载过当前class文件,如果加载过则直接返回,如果没有加载过,则委托他的父类(ExtClassLoader)尝试进行加载,ExtClassLoader也会先查看自己是否加载过,加载过则直接返回,没有加载过,则继续委派给BootStrapClassLoader,如果直至BootStrapClassLoader都没有加载过,则会AppClassLoader会尝试进行加载。

打破双亲委派的方式:改变这个加载流程,不向上委派

packagecom.learn;importjava.net.URI;importjava.nio.file.Files;importjava.nio.file.Path;importjava.nio.file.Paths;public class CustomClassLoader extendsClassLoader {

@Overridepublic Class> loadClass(String name, boolean resolve) throwsClassNotFoundException {synchronized(getClassLoadingLock(name)) {//First, check if the class has already been loaded

Class> c =findLoadedClass(name);if (c == null) {long t0 =System.nanoTime();try{if (name.startsWith("com.learn")) { //打破双亲委派

c =findClass(name);

}else{

c= this.getParent().loadClass(name);

}

}catch(ClassNotFoundException e) {//ClassNotFoundException thrown if class not found//from the non-null parent class loader

}if (c == null) {//If still not found, then invoke findClass in order//to find the class.

long t1 =System.nanoTime();

c=findClass(name);//this is the defining class loader; record the stats

sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 -t0);

sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);

sun.misc.PerfCounter.getFindClasses().increment();

}

}if(resolve) {

resolveClass(c);

}returnc;

}

}

@Overrideprotected Class> findClass(String name) throwsClassNotFoundException {return null;

}

}

SPI:一种服务发现机制。它通过在ClassPath路径下的META-INF/services文件夹查找文件,自动加载文件里所定义的类。这一机制为很多框架扩展提供了可能,比如在Dubbo、JDBC中都使用到了SPI机制

e144b17abf63ea5769841847a0ef9aa3.png

代码如下:

首先定义一个公共接口

packagecom.learn.spi.common;public interfaceIJdbc {voidconnection();

}

然后是两个实现类

packagecom.learn.spi.mysql;importcom.learn.spi.common.IJdbc;public class MysqlJdbc implementsIJdbc {

@Overridepublic voidconnection() {

System.out.println("this is MysqlJdbc...");

}

}

packagecom.learn.spi.oracle;importcom.learn.spi.common.IJdbc;public class OracleJdbc implementsIJdbc {

@Overridepublic voidconnection() {

System.out.println("this is OracleJdbc...");

}

}

最后main函数使用ServiceLoader调用

packagecom.learn.spi.gateway;importcom.learn.spi.common.IJdbc;importjava.util.ServiceLoader;public classGateWayMain {public static voidmain(String[] args) {

ServiceLoader iPays = ServiceLoader.load(IJdbc.class);for(IJdbc iJdbc : iPays) {

iJdbc.connection();

}

System.out.println("end...");

}

}

META-INF/services文件夹下的文件名定义为接口全路径名,文件内容为实现类全路径名,这里是JDK源码中规定死的

com.learn.spi.oracle.OracleJdbc

com.learn.spi.mysql.MysqlJdbc

8ac99ea53a3f900f765411163658e796.png

执行结果:

d5ef8ddddf6d4af1721efd209927e8e8.png

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值