zoukankan      html  css  js  c++  java
  • Java SPI机制

    最近在一个日志标准化的项目中,使用了责任链模式来链接每一个具体的处理Handler.但是在实例化时,需要每一个都去创建实例。
    如:

    /**
     * 初始化具体的处理类
     */
    private void initConcreteHandler() {
    
        handlers.add(new BasicParamHandler());
        handlers.add(new CommonParamHandler());
        handlers.add(new TestParamHandler());
        ……
    }

    这种,涉及了具体的实现类,就违反了可拔插的原则,如果需要替换一种实现,就需要修改代码。老大看到我的代码,直接让用Java spi机制去做。

    1. SPI机制简介

    Service Provider Interface:服务提供者接口.例如,系统里抽象的各个模块,往往有很多不同的实现方案,比如日志模块的方案,xml解析模块、jdbc模块的方案等。

    面向的对象的设计里,我们一般推荐模块之间基于接口编程,模块之间不对实现类进行硬编码。为了实现在模块装配的时候能不在程序里动态指明,这就需要一种服务发现机制。在模块化设计中这个机制尤其重要。

    java spi就是提供这样的一个机制:为某个接口寻找服务实现的机制。

    2. 实现案例

    1.common-logging

    apache最早提供的日志的门面接口。只有接口,没有实现。具体方案由各提供商实现, 发现日志提供商是通过扫描 META-INF/services/org.apache.commons.logging.LogFactory配置文件,通过读取该文件的内容找到日志提工商实现类。只要我们的日志实现里包含了这个文件,并在文件里制定 LogFactory工厂接口的实现类即可。

    2. JDBC

    jdbc4.0以前, 开发人员还需要基于Class.forName("xxx")的方式来装载驱动。
    创建连接:

    DriverManage.getConnection()中,有Connection con = aDriver.driver.connect(url, info);

    driver成员变量,是java.sql.Driver接口,Java对外公开的一个加载驱动接口,Java并未实现,至于实现这个接口由各个Jdbc厂商去实现。

    如MySQL,mysql-connector-java-5.1.38.jar包下面META-INF.services包下有个java.sql.Driver文件打开文件有下面两行
    com.mysql.jdbc.Driver
    com.mysql.fabric.jdbc.FabricMySQLDriver

    3. 具体实现

    (1)如,我们这里的处理模块。面向接口编程。这是我的一种实现方式。

    public interface LogProcessHandler {
    
    /**
     * 解析日志,具体的给Bean赋值的逻辑
     * @param context 上下文
     */
    void process(ProcessorContext context);
    
    /**
     * 日志解析handler拓扑顺序,从小到大排列
    
     */
    int order();
    }

    (2)每一个具体实现类,如BasicParamHandler、CommonParamHandler都实现了这个接口,里面具体的process()方法。

    (3)在Maven工程的src/main/resources/下,创建META-INF/services/com.A.standard.chain.LogProcessHandler文件(UTF-8)
    里面具体内容:

    com.A.tools.BasicParamProcessHandler
    com.A.tools.CommonParamProcessHandler
    com.A.tools.TestParamProcessHandler

    (4)主类中初始化

     /**
     * 初始化具体的处理类
     */
    private void initConcreteHandler() {
        // 初始化
        handlers = new ArrayList<>();
        // 通过java spi机制load所有处理的hander
        ServiceLoader<LogProcessHandler> loader = ServiceLoader.load(LogProcessHandler.class);
        for (LogProcessHandler hander : loader ) {
            handlers.add(hander);
        }
        // handler 排序
        Collections.sort(handlers, new Comparator<LogProcessHandler>() {
            @Override
            public int compare(LogProcessHandler o1, LogProcessHandler o2) {
                return o1.order() - o2.order();
            }
        });
    }

    Ps:

    一般情况下,是按照文件中实现类顺序加载类,但是可能出现特殊情况。因此对集合中的实现类进行排序,这也是接口中定义了order方法的原因。

    4. 原理

    当服务的提供者,提供了服务接口的一种实现之后,在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件。该文件里就是实现该服务接口的具体实现类。

    当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。

    基于这样一个约定就能很好的找到服务接口的实现类,而不需要再代码里制定。

    from: https://www.cnblogs.com/wangrd/p/7090918.html

  • 相关阅读:
    Docs-.NET-C#-指南-语言参考-预处理器指令:#error(C# 参考)
    Docs-.NET-C#-指南-语言参考-预处理器指令:#warning(C# 参考)
    Docs-.NET-C#-指南-语言参考-预处理器指令:#undef(C# 参考)
    Docs-.NET-C#-指南-语言参考-预处理器指令:#define(C# 参考)
    Docs-.NET-C#-指南-语言参考-预处理器指令:#endif(C# 参考)
    Docs-.NET-C#-指南-语言参考-预处理器指令:#elif(C# 参考)
    Docs-.NET-C#-指南-语言参考-预处理器指令:#else(C# 参考)
    Docs-.NET-C#-指南-语言参考-预处理器指令:#if 预处理指令
    Docs-.NET-C#-指南-语言参考-预处理器指令:C# 预处理器指令
    1212 最大公约数
  • 原文地址:https://www.cnblogs.com/GarfieldEr007/p/10201787.html
Copyright © 2011-2022 走看看