采用JDBC解释java SPI机制和线程上下文类加载器 —————— 开开开山怪

常见消息队列:ActiveMQ、RabbitMQ、RocketMQ、Kafka的区别总结 常见消息队列:ActiveMQ、RabbitMQ、RocketMQ、Kafka的区别。 阅读详情

采用JDBC解释JAVA SPI机制和线程上下文类加载器

SPI(Service Provider Interface)网上有关于SPI的解释,在这里我简单总结一下。

SPI机制可以做到将服务接口和真正的服务接口的实现类分开,可以增加程序的可扩展性,通过扫描规定的路径来进行实现类的获取,可以说是一种服务发现机制。

优点: 在面向对象的设计中,我们一般建议基于接口的编程,如果代码中涉及到具体的实现类,如果我们想要换一种实现方案就不得不更改代码,但是采用接口的方式,只要我们采用一种机制,可以使得我们能够获取接口的不同的实现类,那么我们的代码的灵活性就比较的高,这种机制就是SPI。

SPI机制的工作过程

当服务提供者提供了服务接口的实现类,当实现类打成jar包之后,在jar包的META-INF/services/ 建立一个以服务接口名称为文件名称的文件,并且文件的内容为该服务接口的实现类的名称,那么当应用程序需要这部分功能模块的时候,就能通过META-INF/services/下的这个配置文件找到对应的实现类的名称,可以进行加载并且实例化。这也是同时也是服务提供者需要遵守的规则。

下面我们举例子所说的服务提供者就是mysql-connector-java-5.1.46-bin.jar
服务接口就是java.sql.Driver.

栗子:
在我们程序中,需要链接数据库的时候,我们都会在工程中导入一个jar包,数据库驱动jar包在我们的用户类路径中。当然关于实现java.sql.Driver接口的就是在jar包中的。
我的jar包就是这个mysql-connector-java-5.1.46-bin.jar

在这里插入图片描述

然后我们在我们自己的程序的代码中会写像下面这样的的代码进行数据库的链接,在第一句代码执行的时候就开始加载com.mysql.jdbc.Driver这个类了,并且采用的是加载当前类的系统类加载进行加载,因为com.mysql.jdbc.Driver这个类是在我们用户类路径中mysql-connector-java-5.1.46-bin.jar中所实现java.sql.Driver接口的实现类,系统类加载器可以直接加载这个类。

图一:
数据库链接述
之前在使用的过程中,不明白为什么这么写,只是内心默念一句,好了,这是神仙咒语,记住就好,管它什么意思。最近在看线程上下文类加载器的时候牵扯到了这部分,中间也牵扯到了SPI机制,所以就拿这个举例子进行线程上下文加载器和SPI机制的说明。

其实上面代码中Class.forName(“com.mysql.jdbc.Driver”)这句代码不用写。

图二:在这里插入图片描述像图二这样的代码也会正常运行滴,主要是因为SPI和线程上下文加载器应用的原因。

下面详细解释为什么图二代码可以正常运行,并且一同解释SPI机制的工作原理,和线程上下文类加载器的作用。

这部分代码是在我们用户自己的程序中写的,那么当我们用户代码需要运行的时候,首先会加载我们的主类,当然加载我们主类的类加载器是系统类加载器,当主类的代码运行到DriverManager.getConnection(url, “KSG”, “99999”);的时候,我们知道调用了一个类的静态方法,那么会对这个类进行初始化,同时也要加载类DriverManager,那么在初始化的时候当然是执行该类的类构造器()方法,那么就会执行该类中的静态块中的方法。

下面是DriverManager类的静态块的方法:

DriverManager在rt.jar中的java.sql下:加载DriverManager类的类加载器为启动类加载器
DriverManager类是驱动程序管理类

public class DriverManager {  
  private DriverManager(){}

   static {
          loadInitialDrivers();//加载数据库驱动程序,初始化时执行这个方法
          println("JDBC DriverManager initialized");
    }
 }

图三:

在这里插入图片描述
下面看loadInitialDrivers方法,

private static void loadInitialDrivers() {
   String drivers;
    try {
    //这种方式是通过系统的属性得到驱动程序的实现类的名称,上面的图可以说明问题
    //将我们的驱动程序类通过属性的设置,在这个方法中便可以通过属性系统属性获取驱动类的名称;
    //如果我们在图三中不进行系统属性的设置,也是可以运行成功的,这里只是为了解释System.getProperty("jdbc.drivers")这句代码,所以给出的图三。
        drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
            public String run() {
            //通过系统属性获取驱动程序的类的名称,
            //如果获取不到,没有关系,原因是图三的写法其实和图一的写法效果一样
            //但是没有图三和图一的第一句,程序依然可以成功执行,原因就在下面的 AccessController.doPrivileged这段代码中。
                return System.getProperty("jdbc.drivers");
                
            }
        });
    } catch (Exception ex) {
        drivers = null;
    }
    

   AccessController.doPrivileged(new PrivilegedAction<Void>() {
        public Void run() {
    
       
            ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
            Iterator<Driver> driversIterator = loadedDrivers.iterator();
            try{
                while(driversIterator.hasNext()) {
                    driversIterator.next();
                }
            } catch(Throwable t) {
            // Do nothing
            }
            return null;
          }
    });
    
    println("DriverManager.initialize: jdbc.drivers = " + drivers);
  
   //下面就是对通过系统属性获得的驱动程序的类的名称的处理
   //通过类名称采用系统类加载器进行类的加载
    if (drivers == null || drivers.equals("")) {
        return;//表示没有设置系统属性
    }
    String[] driversList = drivers.split(":");
    println("number of Drivers:" + driversList.length);
    for (String aDriver : driversList) {
        try {
            println("DriverManager.Initialize: loading " + aDriver);
            //进行驱动程序类的加载,采用系统加载器进行加载
            Class.forName(aDriver, true,
                    ClassLoader.getSystemClassLoader());
        } catch (Exception ex) {
            println("DriverManager.Initialize: load failed: " + ex);
        }
    }
  }

上述代码中中间一部分没有写注释,其实中间部分是我所要说的重点。

loadInitialDrivers方法是在初始化DriverManager 类的时候执行的,DriverManager类由启动类加载器加载,当然在loadInitialDrivers方法中如果用到某个类还没有加载,当然是采用启动类加载器进行加载。

ServiceLoader.load(Driver.class)这句代码调用了ServiceLoader类中的静态方法,所以需要加载ServiceLoader类,由启动类加载器进行加载该类,然后执行load方法。

第一句:
ServiceLoader loadedDrivers = ServiceLoader.load(Driver.class);

Driver.class 位于rt.jar包下 java.sql.Driver.class是一个接口

ServiceLoader这个类其实是在rt.jar java.util下的类,这个类的作用就是用来查找服务,可以说是jdk提供的一个实现服务查找的一个工具类。同样也是SPI机制中的一部分。

在DriverManager驱动程序管理类中用到这个ServiceLoader类当然是需要查找相关驱动程序接口的服务。

那很明显这里想要查找的服务是Driver服务,也就是数据库连接的驱动程序的实现类,因为Driver是java sql下的一个接口,在博文最开头已经说过了,SPI机制就是将接口与实现类进行分离,但同时也是一种服务发现机制,这里已经开始有些SPI的苗头了。

public static <S> ServiceLoader<S> load(Class<S> service) {
//这个ServiceLoader类中的load方法,采用java.lang.Thread类中getContextClassLoader方法获取类加载器,获取的类加载器默认为系统类加载器
        ClassLoader cl = Thread.currentThread().getContextClassLoader();     
        return ServiceLoader.load(service, cl);
        
    }
}

第二句和第三句:

Iterator driversIterator = loadedDrivers.iterator();
while ( driversIterator.hasNext () ) {
driversIterator.next();
}
上面说到需要查找的服务为Driver服务,那么这里就说到真正的SPI机制,
因为ServiceLoader这个类是用于查找实现类的,所有此类中有一个类的静态变量为private static final String PREFIX = “META-INF/services/”;

public final class ServiceLoader<S> implements Iterable<S>{
    private static final String PREFIX = "META-INF/services/";
    private final ClassLoader loader;
    private final ClassLoader loader;
    private final AccessControlContext acc;
    private LinkedHashMap<String,S> providers = new LinkedHashMap<>();
    private LazyIterator lookupIterator;
}

hasNext()方法中会将"META-INF/services/" + 服务接口的全称作为最终的查找文件的路径 ,因为服务接口是Driver,那么此时的文件查找路径就是"META-INF/services/java.sql.Driver.

hasNext()方法调用了ServiceLoader中的hasNextService()方法:
截取了一部分代码进行说明

private boolean hasNextService() {
            if (nextName != null) {
                return true;
            }
            if (configs == null) {
                try {
                    String fullName = PREFIX + service.getName();
                    if (loader == null)
                        configs = ClassLoader.getSystemResources(fullName);
                    else
                        configs = loader.getResources(fullName);
                } catch (IOException x) {
                    fail(service, "Error locating configuration files", x);
                }
            }
  }

因为传入的服务是Driver.class 那么此时的service.getName()就是java.sql.Driver

fullName = PREFIX + service.getName()
= META-INF/services/ java.sql.Driver

configs = loader.getResources(fullName);
这里的loader就是通过ServiceLoader.load(Driver.class)中的load方法所获得的的线程上下文类加载器(即默认的系统类加载器)

loader.getResources(fullName) 就是获取这个fullName文件中的实现类的名称,也就是下面的两个名称
(com.mysql.jdbc.Driver
com.mysql.fabric.jdbc.FabricMySQLDriver)
名称为最后一张图的右边内容。

这就对照了我们开头所说的,一个服务提供者需要在自己的jar包中
"META-INF/services/"路径下创建一个以服务接口为名称的文件,并将自己的实现类的名称写入。并且我们的服务提供者就是mysql-connector-java-5.1.46-bin.jar

因为mysql-connector-java-5.1.46-bin.jar实现java.sql.Driver的接口,所以同样也遵守相应的规则,从最后一张图就可以看出来。

driversIterator.next()调用了ServiceLoader中的nextService()方法

 private S nextService() {
            if (!hasNextService())
                throw new NoSuchElementException();
            String cn = nextName;
            nextName = null;
            Class<?> c = null;
            try {
                c = Class.forName(cn, false, loader);
            } catch (ClassNotFoundException x) {
                fail(service,
                     "Provider " + cn + " not found");
            }
   }

我们很明显看到,在方法中有一句代码是
c = Class.forName(cn, false, loader);这句话事实上就代替了图一的第一句话
Class.forName(name),所以开头说过图一的第一行代码完全不用写也可以运行。

像图一那样写的话,程序进行到Class.forname(name)时,我们就要对名为name的实现类进行初始化,同时也要加载名为name的类,那么图一的情况当然是采用加载当前主类的系统类加载器进行加载,合情合理。

但是在 nextService() 方法中为什么采用了三参,原因是如果我们在nextService() 方法中采用单参的Class.forname(name)会出现什么情况,当运行到Class.forname(name)的时候需要加载名为name的类,但是会采用加载当前的类的类加载器来加载名为name的类,当前类是ServiceLoader,而ServiceLoader是由启动类加载器进行加载的,我们又知道双亲委派的加载模式是自下而上的,启动类加载器没有父加载器,所以只能由自己进行加载,可是当前名为name的类是我们查找的在用户类路径下类库中的类。
启动类加载器无法加载,所以采用了三参模式。

c = Class.forName(cn, false, loader);
此时的cn就是上面通过SPI机制找到的位于用户类路径下的"META-INF/services/java.sql.Driver文件配置的类的名称
(com.mysql.jdbc.Driver
com.mysql.fabric.jdbc.FabricMySQLDriver))也就是下图中的右边的内容。

这个loader就是上面ServiceLoader.load(Driver.class)的load代码中通过ClassLoader cl = Thread.currentThread().getContextClassLoader()获得的线程上下文类加载器(默认为系统类加载器)所以此时loader就是系统类加载器,那么此时可以加载名为com.mysql.jdbc.Driver和com.mysql.fabric.jdbc.FabricMySQLDriver的实现类了。

因为这两个类是由服务提供者mysql-connector-java-5.1.46-bin.jar实现的类,并在mysql-connector-java-5.1.46-bin.jar中,所以这两个类是在用户类路径下的两个类,所有可由系统类加载器直接进行加载。但是这样便打破了了双亲委派加载模式,由于启动类记载器不能够进行加载,所以采用了线程上下文类加载器。也就是父加载器要求子类加载器去完成类加载。在这里插入图片描述mysql-connector-java-5.1.46-bin.jar这个jar包中的com.mysql.jdbc.Driver实现了我们之前说的rt.jar中的java.sql.Driver接口,那么mysql-connector-java-5.1.46-bin.jar相当于一个服务提供者,它在自己的jar包的"META-INF/services/路径下创建一个文件名为java.sql.Driver的文件,并且在文件中配置 了实现Driver接口的实现类的名称。

上面的例子同时也体现出了当基础类调用用户代码的时候,我们实际上采用的是一种打破双亲委派模型的的一种加载方式,因为我们知道双亲委派模型的加载方式是自下而上的,而这里采用的是线程上下文类加载器去加载所需要的SPI代码,也就是父加载器请求子加载器去完成类加载动作。

《YOLO11魔术师专栏》专栏介绍 & 专栏目录 【原创自研模块】【多组合点优化】【注意力机制】【主干篇】【neck优化】【卷积魔改】 【block&多尺度融合结合】【损失&IOU优化】【上下采样优化】【小目标性能提升】【前沿论文分享】【训练实战篇】 阅读详情

相关推荐

Isaacgym使用操作指南

Isaac Gym 是一个强大的仿真工具,特别适合那些需要进行大规模并行仿真和训练的机器人和强化学习任务。通过 GPU 加速、深度学习集成和丰富的物理仿真能力,Isaac Gym 能够显著提高仿真和训练效率,是机器人学和 AI 研究中的一大利器。

Tony Wey的博客 1万+

mysql fabric java_MySQL Fabric和MyBatis的整合过程中遇到的问题

这是我昨天在整合MySQL Fabric和MyBatis时遇到的问题,花了大半天才解决的问题,解决的过程中在网上查找了很久,都没有找到解决的方案。现在记下来,希望能够帮助有同样问题的朋友。如果各位朋友有更好的解决方案,也请告诉我。1. 问题描述这个问题是在整合MySQL和MyBatis的时候遇到的。首先说一下我使用的jar包的版本,MySQL Connector用的是5.1.36,myBatis用...

weixin_30798713的博客 276

Agilent N5230A矢量网络分析仪在射频器件S参数测试中的关键操作指南

本文详细介绍了Agilent N5230A矢量网络分析仪在射频器件S参数测试中的关键操作流程与技巧。内容涵盖从开机预热、校准(SOLT/ECal)、参数设置(频率、点数、功率、IFBW)到连接测量、数据保存分析的全过程,并分享了夹具去嵌入、时域分析等进阶功能与常见问题避坑指南,旨在帮助工程师获得精准可靠的散射参数测试结果。

kotlin6android的博客 401

Mysql Connector/J 源码分析(普通Connection)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 一、pandas是什么? 二、使用步骤 1.引入库 2.读入数据 总结 前言 《Mysql Connector/J 源码分析(综述)》提到普通的Connection是最基本的连接。本文试图揭开它的内幕,看看它是如何与Mysql交互的。 本次分析的版本为5.1.46。若通过maven下载,可添加以下依赖: <dependency> <groupId>m..

yyb_gz的专栏 2296

多传感器标定全栈系统学习教程

无论是激光雷达(Lidar)、毫米波雷达(Radar)、相机(Camera)还是惯导(IMU),传感器之间的精确标定直接决定了系统的可靠性与性能。本文将以系统化的视角,梳理多传感器标定的核心知识体系,并分享一套覆盖全栈技术的学习路径。Calibration)是实现点云与图像融合的前提,而雷达与车辆的标定(Radar2Car)则是精准速度估计的关键。:Lidar2Car、Camera2Car的快速标定流程,标定板布置规范。:从单传感器标定入手(如相机内参),逐步过渡到多传感器联合标定。

weixin_41792788的博客 904

三菱FX3U PLC红绿灯ST编程实战:从基础逻辑到急停功能完整实现

本文以十字路口红绿灯控制为例,详细讲解了三菱FX3U PLC使用ST编程的完整实战过程。内容涵盖从基础时序逻辑、状态机设计,到急停功能、黄灯闪烁、昼夜模式切换等高级功能的实现,并提供了代码优化与调试技巧,帮助工程师掌握结构化文本在工业控制中的清晰、结构化应用。

weixin_29070861的博客 533

使用 MySQL Fabric 实现高可用

绝情少爷 2017-02-20 10:14 官方文档:http://dev.mysql.com/doc/mysql-utilities/1.5/en/fabric.html MySQL Fabric 高可用方案需要最少三个 MySQL 实例,其中一个实例负责存储集群配置和状态信息,称为 backing store ,剩下的全部服务器用于组成高可用集群,集群中的 MySQL 应尽量使用相

u011277123的博客 4260

银河麒麟V10ARM架构服务器离线安装docker\nacos【附安装包】

本文分享了在银河麒麟ARM64操作系统上安装Docker和Nacos的完整过程。首先提供了Docker的自动化安装脚本,包含服务配置、存储路径设置等关键步骤,并给出了常用维护命令。接着详细介绍了通过Docker-compose部署Nacos的方法,包括镜像导入、配置文件修改、鉴权设置等注意事项,特别强调了密钥长度必须超过32个字符的要求。文章还提供了Nacos初次登录的注意事项,并预告了后续可能推出的Redis、Nginx等安装教程。所有配置文件和脚本均可直接下载使用,适合内网环境下国产操作系统的部署

weixin_39054697的博客 2344

JVM--7. 通过JDBC源码来分析线程上下文类加载器以及SPI的使用

文章目录1. 什么是全盘负责委托机制2. 为什么需要有线程上下文类加载器2.1 使用JDBC的例子,分析为什么双亲委托机制不能实现要求2.2 线程上下文类加载器的作用3. 线程上下文类加载器的使用3.1 线程上下文类加载器使用API3.2 线程上下文类加载器的特征3.3 线程上下文类加载器使用的通用写法4. 借助JDBC源码分析上下文类加载器的使用4.1 示例代码4.2 源码分析4.2.1 首先解析第一句 Class.forName("com.mysql.jdbc.Driver");4.2.2 Driver

独孤小标的博客 683

抄表程序员的DLMS/COSEM协议实战:从抓包到解析,手把手教你读懂IEC62056报文

本文深入解析DLMS/COSEM协议在智能电表通信中的应用,从报文捕获到数据解析的全流程实战指南。涵盖HDLC帧解析、应用层连接建立、数据请求/响应解码等核心内容,并提供Wireshark配置、OBIS代码速查表等实用工具,帮助开发者快速掌握IEC62056标准下的电能表通信技术。

weixin_29178069的博客 299

gurobi最新下载安装教程 2023.11

我下载的最新版gurobi支持python3.7-python3.11,这个序号与计算机硬件捆绑,一旦激活就会失效。运行激活码之后,产生的。如果既放置在了上面的默认搜索目录,又设置了。系统环境变量(不是用户环境变量),指向保存的目录和文件名(例如。高版本产生的许可文件兼容低版本。验证不会成功,学术许可不会激活。之后cmd进入命令行,cd进入gurobi所在的。,实测不到一小时即可审核完毕,获得对应的许可。如果学校的网关没有将本校的。系统环境变量指向不同的目录和文件,原厂无关,是学校网络的设置问题。

Cachel Wood的博客 2万+

MySQL Fabric原理(主要是HA和分片这2个功能)

转自:https://www.2cto.com/database/201408/327941.htmlOracle在今年5月推出了一套为各方寄予厚望的MySQL产品 -- MySQL Fabric,从字面上不太能看出它是啥,但是从名称上还是有迹可循的。fabric是“织物”的意思,这意味着它是用来“织”起一片MySQL数据库。MySQL Fabric是一套数据库服务器场(Da...

weixin_34178244的博客 665

4.26华为OD机试真题 新系统 - 端口流量统计 (Java/Py/C/C++/Js/Go)

本文整理华为 OD 机试新系统机考真题库,涵盖高频考点精析与完整题目内容,提供 C 语言、Java、C++、Python、Go、JavaScript 多语言源码实现,搭配详细解题思路讲解,并支持在线 OJ 刷题练习,助力高效备考华为 OD 机试新系统。

算法大师! 2333

Java的SPI机制分析

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_33800463的博客 144

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

MySQL Fabric安装使用杂记(未整理)

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_34337265的博客 469

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

jvm原理(24)通过JDBC驱动加载深刻理解线程上下文类加载器机制

public class MyTest27 { public static void main(String[] args) throws Exception { Class.forName("com.mysql.jdbc.Driver"); Connection connection = DriverManager.getConnection("jdbc:...

wzq6578702的专栏 2181

【基础篇】七、线程上下文类加载器打破双亲委派机制

SPI,Service Provider Interface,是JDK内置的一种服务提供发现机制。大致流程:写个小Demo直观感受下SPI,定义对外接口: 其他类写实现类(这里接口和实现类放同一个jar中了,一般来说写接口和写实现的是两种角色): 写文件: 测试模块中引入上面的这个jar: 尝试加载: 成功获取到接口的两个实现类:JDBC使用DriverManager类来完成不同数据库厂商的驱动注册: DriverManager位于JDK的rt.jar包,由启动类加载器去加载,而引入的第三方jar包归应用

llg___的博客 1497

$1.6、线程上下文类加载器揭秘及作用

在前面5篇文章介绍了类加载器和对应的双亲委托模式后,我们还需要了解“破坏”双亲模式的特例,而这种特例在很多SPI(服务提供接口)架构中有着广泛的应用,甚至说 ,如果不破坏双亲模式,Java的很多功能都是无法实现的。 以jdbc的模式进行举例:java对jdbc的规范中,只是定义了Driver(驱动)、Connection(连接)等接口,具体的实现是由各自的数据库厂商决定的,比如mysql厂商的驱...

weixin_39800596的博客 646

线程上下文类加载器作用&使用场景

线程上下文类加载器是 Java 提供的一种灵活机制,用于解决双亲委派模型的局限性。它在 SPI 机制、Web 应用服务器、框架和模块化系统中广泛应用。通过合理使用线程上下文类加载器,可以实现动态加载类和解耦类加载逻辑的目标。

m0_59539752的博客 1228

JVM 线程上下文类加载器

线程上下问类加载器出现的原因 Q: 越基础的类由越上层的加载器进行加载,如果基础类又要调用回用户的代码,那该怎么办? A: 解决方案:使用“线程上下文类加载器” 为了解决这个问题,Java设计团队只好引入了一个不太优雅的设计:线程上下文类加载器(Thread Context ClassLoader)。这个类加载器可以通过java.lang.Thread类的setContextClassLoaser()方法进行设置,如果创建线程时还未设置,它将会从父线程中继承一个,如果在应用程序的全局范围内都没有设置过的

Java的一只小实习生的博客 1655

mysql 做链接_MySQL 几种连接方式配置

MySQL 默认提供4中方式连接。并附带有负载均衡、读写分离方式!以下将分别连接的四种方式列出来。下图为4中连接方式的前缀(来自源码):private static final String REPLICATION_URL_PREFIX = "jdbc:mysql:replication://";private static final String URL_PREFIX = "jdbc:mysq...

weixin_26765363的博客 598

mysql驱动fabric_[数据库]MySQL:Fabric 安装

[DEFAULT]prefix = /usr/localsysconfdir = /usr/local/etclogdir = /var/log[storage]address = localhost:3306user = fabric_storepassword = secretdatabase = mysql_fabricauth_plugin = mysql_native_passwordc...

weixin_34026053的博客 262

JDBC解惑(一) 之 Connection创建

问题: 最近接触到jdbc,我有个疑问,建立连接的时候调用的都是接口,那么接口的方法是怎么实现的呢? 比如声明: Connection con; Statement sql; ResultSet rs; //Connection 、Statement 、ResultSet 都是接口 con=DriverManage.getConnection("jdbc:odbc:hel

键圣的博客 8900

配置c3p0数据源的时候提示的错误

配置c3p0数据源时,提示的几个空指针异常错误,以及配置多个数据源

mtcurry的博客 6226
上一篇: HashMap源码解析JDK1.8 —————— 开开开山怪
下一篇: Reentrantlock和背后金主AQS —————— 开开开山怪
开开开山怪
博客等级 码龄8年 41粉丝 30原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值