- MySQL:在 MySQL的 Java 驱动包 mysql-connector-java-XXX.jar 中,可以找到 META-INF/services 目录,该目录下会有一个名字为java.sql.Driver 的文件,文件内容是com.mysql.cj.jdbc.Driver 。
文章插图
- PostgreSQL 实现:在 PostgreSQL 的 Java 驱动包 postgresql-42.0.0.jar 中,也可以找到同样的配置文件,文件内容是 org.postgresql.Driver , org.postgresql.Driver 正是 PostgreSQL 版的 java.sql.Driver 实现 。
以 MySQL 为例,创建数据库连接代码如下:
final String DB_URL = String.format("jdbc:mysql://%s:%s/%s", DB_HOST, DB_PORT, DB_SCHEMA);connection = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);4.1.2 DriverManager从前文 , 我们已经知道 DriverManager 是创建数据库连接的关键 。它究竟是如何工作的呢?
可以看到是加载实例化驱动的,接着看 loadInitialDrivers 方法:
private static void loadInitialDrivers() {String drivers;try {drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {public String run() {return System.getProperty("jdbc.drivers");}});} catch (Exception ex) {drivers = null;}// 通过 classloader 获取所有实现 java.sql.Driver 的驱动类AccessController.doPrivileged(new PrivilegedAction<Void>() {public Void run() {// 利用 SPI,记载所有 Driver 服务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);}}}上面的代码主要步骤是:
- 从系统变量中获取驱动的实现类 。
- 利用 SPI 来获取所有驱动的实现类 。
- 遍历所有驱动 , 尝试实例化各个实现类 。
- 根据第 1 步获取到的驱动列表来实例化具体的实现类 。
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);这里实际获取的是java.util.ServiceLoader.LazyIterator 迭代器 。调用其 hasNext 方法时,会搜索 classpath 下以及 jar 包中的 META-INF/services 目录,查找 java.sql.Driver 文件 , 并找到文件中的驱动实现类的全限定名 。调用其 next 方法时 , 会根据驱动类的全限定名去尝试实例化一个驱动类的对象 。
4.2 SPI 应用案例之 Common-Loggincommon-logging(也称 Jakarta Commons Logging,缩写 JCL)是常用的日志门面工具包 。common-logging 的核心类是入口是 LogFactory , LogFatory 是一个抽象类,它负责加载具体的日志实现 。
其入口方法是 LogFactory.getLog 方法,源码如下:
public static Log getLog(Class clazz) throws LogConfigurationException {return getFactory().getInstance(clazz);}public static Log getLog(String name) throws LogConfigurationException {return getFactory().getInstance(name);}从以上源码可知 , getLog 采用了工厂设计模式 , 是先调用 getFactory 方法获取具体日志库的工厂类,然后根据类名称或类型创建日志实例 。
LogFatory.getFactory 方法负责选出匹配的日志工厂,其源码如下:
public static LogFactory getFactory() throws LogConfigurationException {// 省略...// 加载 commons-logging.properties 配置文件Properties props = getConfigurationFile(contextClassLoader, FACTORY_PROPERTIES);// 省略...// 决定创建哪个 LogFactory 实例// (1)尝试读取全局属性 org.apache.commons.logging.LogFactoryif (isDiagnosticsEnabled()) {logDiagnostic("[LOOKUP] Looking for system property [" + FACTORY_PROPERTY +"] to define the LogFactory subclass to use...");}try {// 如果指定了 org.apache.commons.logging.LogFactory 属性,尝试实例化具体实现类String factoryClass = getSystemProperty(FACTORY_PROPERTY, null);if (factoryClass != null) {if (isDiagnosticsEnabled()) {logDiagnostic("[LOOKUP] Creating an instance of LogFactory class '" + factoryClass +"' as specified by system property " + FACTORY_PROPERTY);}factory = newFactory(factoryClass, baseClassLoader, contextClassLoader);} else {if (isDiagnosticsEnabled()) {logDiagnostic("[LOOKUP] No system property [" + FACTORY_PROPERTY + "] defined.");}}} catch (SecurityException e) {// 异常处理} catch (RuntimeException e) {// 异常处理}// (2)利用 Java SPI 机制 , 尝试在 classpatch 的 META-INF/services 目录下寻找 org.apache.commons.logging.LogFactory 实现类if (factory == null) {if (isDiagnosticsEnabled()) {logDiagnostic("[LOOKUP] Looking for a resource file of name [" + SERVICE_ID +"] to define the LogFactory subclass to use...");}try {final InputStream is = getResourceAsStream(contextClassLoader, SERVICE_ID);if( is != null ) {// This code is needed by EBCDIC and other strange systems.// It's a fix for bugs reported in xercesBufferedReader rd;try {rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));} catch (java.io.UnsupportedEncodingException e) {rd = new BufferedReader(new InputStreamReader(is));}String factoryClassName = rd.readLine();rd.close();if (factoryClassName != null && ! "".equals(factoryClassName)) {if (isDiagnosticsEnabled()) {logDiagnostic("[LOOKUP]Creating an instance of LogFactory class " +factoryClassName +" as specified by file '" + SERVICE_ID +"' which was present in the path of the context classloader.");}factory = newFactory(factoryClassName, baseClassLoader, contextClassLoader );}} else {// is == nullif (isDiagnosticsEnabled()) {logDiagnostic("[LOOKUP] No resource file with name '" + SERVICE_ID + "' found.");}}} catch (Exception ex) {// note: if the specified LogFactory class wasn't compatible with LogFactory// for some reason, a ClassCastException will be caught here, and attempts will// continue to find a compatible class.if (isDiagnosticsEnabled()) {logDiagnostic("[LOOKUP] A security exception occurred while trying to create an" +" instance of the custom factory class" +": [" + trim(ex.getMessage()) +"]. Trying alternative implementations...");}// ignore}}// (3)尝试从 classpath 目录下的 commons-logging.properties 文件中查找 org.apache.commons.logging.LogFactory 属性if (factory == null) {if (props != null) {if (isDiagnosticsEnabled()) {logDiagnostic("[LOOKUP] Looking in properties file for entry with key '" + FACTORY_PROPERTY +"' to define the LogFactory subclass to use...");}String factoryClass = props.getProperty(FACTORY_PROPERTY);if (factoryClass != null) {if (isDiagnosticsEnabled()) {logDiagnostic("[LOOKUP] Properties file specifies LogFactory subclass '" + factoryClass + "'");}factory = newFactory(factoryClass, baseClassLoader, contextClassLoader);// TODO: think about whether we need to handle exceptions from newFactory} else {if (isDiagnosticsEnabled()) {logDiagnostic("[LOOKUP] Properties file has no entry specifying LogFactory subclass.");}}} else {if (isDiagnosticsEnabled()) {logDiagnostic("[LOOKUP] No properties file available to determine" + " LogFactory subclass from..");}}}// (4)以上情况都不满足 , 实例化默认实现类 org.apache.commons.logging.impl.LogFactoryImplif (factory == null) {if (isDiagnosticsEnabled()) {logDiagnostic("[LOOKUP] Loading the default LogFactory implementation '" + FACTORY_DEFAULT +"' via the same classloader that loaded this LogFactory" +" class (ie not looking in the context classloader).");}factory = newFactory(FACTORY_DEFAULT, thisClassLoader, contextClassLoader);}if (factory != null) {/*** Always cache using context class loader.*/cacheFactory(contextClassLoader, factory);if (props != null) {Enumeration names = props.propertyNames();while (names.hasMoreElements()) {String name = (String) names.nextElement();String value = https://www.huyubaike.com/biancheng/props.getProperty(name);factory.setAttribute(name, value);}}}return factory;}
推荐阅读
- Redisson源码解读-分布式锁
- 红米note9pro评测最新_红米note9pro深度测评
- EasyPoi大数据导入导出百万级实例
- Dubbo-聊聊通信模块设计
- 1分钟完成在线测试部署便捷收集班级同学文件的web管理系统
- 联想拯救者R7000 2021款官方配置_联想拯救者R7000 2021款升级情况
- 简易版 纯css爱心代码-最近超级火的打火机与公主裙中的爱心代码
- 三年级数学下册练习题300道 三年级数学计算题300道
- 【lwip】10-ICMP协议&源码分析
- 华为开发者大会2022:HMS Core 3D建模服务再升级,万物皆可驱动