SPI机制

什么是SPI机制?

SPI(Service Provider Interface),是JDK内置的一种服务提供发现机制,可以用于启用框架扩展和替换组件

如:java.sql.Driver接口,其他不同厂商可以针对同一接口做出不同的实现

Java中SPI机制的主要思想就是将装配的控制权移到程序之外,在模块化设计中,这个机制尤其重要,其核心思想就是解耦

在这里插入图片描述

SPI简单案例

目录结构:

在这里插入图片描述

package com.comtom.spi;

import java.util.List;

public interface Search {
   
    public List<String> searchDoc(String keyword);
}
package com.comtom.spi;

import java.util.List;

public class FileSearch implements Search{
   
    @Override
    public List<String> searchDoc(String keyword) {
   
        System.out.println("文件搜索"+keyword);
        return null;
    }
}
package com.comtom.spi;

import java.util.List;

public class DataBaseSearch implements Search{
   
    @Override
    public List<String> searchDoc(String keyword) {
   
        System.out.println("数据库搜索"+keyword);
        return null;
    }
}

第四步:在resource目录下创建目录/META-INF/services,在该目录下创建Search类的全限定名路径文件,如下图所示:

在这里插入图片描述

注:IDEA需要选择文件类型,右键选择Override File Type,弹框Choose File Type,选择textmate

在这里插入图片描述

com.comtom.spi.Search文件中添加FileSearch类的全限定名或DataBaseSearch类的全限定名

com.comtom.spi.FileSearch
com.comtom.spi.DataBaseSearch

开始测试

public class Main {
   
    public static void main(String[] args) {
   
        ServiceLoader<Search> s = ServiceLoader.load(Search.class);
        Iterator<Search> iterator = s.iterator();
        while (iterator.hasNext()) {
   
            Search next = iterator.next();
            next.searchDoc("hello world");
        }
    }
}

结果:在com.comtom.spi.Search文件添加几个类全限定名,就会有几条结果数据

文件搜索hello world
数据库搜索hello world

SPI机制的应用

JDBC DriverManager

在JDBC4.0之前,连接数据库需要使用Class.forName("com.mysql.jdbc.Driver")先加载数据库相关的驱动,然后再获取连接等操作,在JDBC4.0以后,不需要使用Class.forName("com.mysql.jdbc.Driver")加载驱动,就可以直接获取连接,这种方式就是使用了SPI机制

  1. Java在rt.jar包中定义了java.sql.Driver接口,没有具体的实现,类似于上述案例中的Search接口

  2. 在mysql的jar包mysql-connector-java-5.1.49.jar中,有META-INF/services目录,里面的文件定义了驱动实现类,类似于上述案例的FileSearch和DataBaseSearch

  3. 在建立连接时,就会在加载类的时候加载驱动
    在这里插入图片描述

  4. loadInitialDrivers()方法就会调用Driver.class对应的权限定名文件,读取对应实现类,类似测试时的操作
    在这里插入图片描述
    loadInitialDrivers()方法实现步骤如下:

    1. 从系统变量中获取有关驱动的定义

    2. 使用SPI来获取驱动的实现

    3. 遍历使用SPI获取到的具体实现,实例化各个实现类

    4. 根据第一步获取到的驱动列表来实例化具体实现类

SPI机制的实现步骤:

  1. ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);封装接口类型和类加载器,并初始化了一个迭代器

  2. Iterator<Driver> driversIterator = loadedDrivers.iterator();获取迭代器

  3. 遍历迭代器,调用driversIterator.hasNext()方法时会去resource目录下所有的META-INF/services目录下的java.sql.Driver文件,并找到文件中的实现类的名字

  4. 调用driversIterator.next();方法,此时就会根据驱动名字具体实例化各个实现类

Common Logging

common-logging(也称为Jakarta Commons Logging,JCL)是常用的日志库门面

JCL依赖

<!--引入common-logging-->
<dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    <version>1.2</version>
</dependency>

日志的实例是通过LogFactory的getLog()方法创建的:

public static getLog(Class clazz) throws LogConfigurationException {
   
    return getFactory().getInstance(clazz);
}

分析getFactory方法做了什么

public static LogFactory getFactory() throws LogConfigurationException {
   
         //获取一个类加载器
        ClassLoader contextClassLoader = getContextClassLoaderInternal();
        //如果类加载器为空,且经过诊断确认,则输出类加载器为空
        if (contextClassLoader == null && isDiagnosticsEnabled()) {
   
            logDiagnostic("Context classloader is null.");
        }
        //返回这个类加载器注册过的日志工厂
        LogFactory factory = getCachedFactory(contextClassLoader);
        //如果注册过,直接返回,没有注册过,
        if (factory != null) {
   
            return factory;
        } else {
   
            if (isDiagnosticsEnabled()) {
   
                logDiagnostic("[LOOKUP] LogFactory implementation requested for the first time for context classloader " + objectId(contextClassLoader));
                logHierarchy("[LOOKUP] ", contextClassLoader);
            }
            //加载properties文件
            Properties props = getConfigurationFile(contextClassLoader, "commons-logging.properties");
            ClassLoader baseClassLoader = contextClassLoader;
            String factoryClass;
            if (props != null) {
   
                //如果commons-logging.properties配置文件存在,且文件中配置了use_tccl参数,参数值为false,则将当前类加载器赋值给获取到的日志类加载器
                factoryClass = props.getProperty("use_tccl");
                if (factoryClass != null && !Boolean.valueOf(factoryClass)) {
   
                    baseClassLoader = thisClassLoader;
                }
            }
            //决定使用哪个factory
            //首先尝试查找vm系统中org.apache.commons.logging.LogFactory,并判断其是否可以指定为factory
            if (isDiagnosticsEnabled()) {
   
                logDiagnostic("[LOOKUP] Looking for system property [org.apache.commons.logging.LogFactory] to define the LogFactory subclass to use...");
            }

            try {
   
                factoryClass = getSystemProperty("org.apache.commons.logging.LogFactory", (String)null);
                if (factoryClass != null) {
   
                    if (isDiagnosticsEnabled()) {
   
                        logDiagnostic("[LOOKUP] Creating an instance of LogFactory class '" + factoryClass + "' as specified by system property " + "org.apache.commons.logging.LogF
  • 21
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Carl·杰尼龟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值