JDK SPI recently learned JDK SPI

Recently learned JDK SPI

 

What is JDK SPI

Several recent workday SPI colleagues said several times this term, though, and I'm okay, but my heart still silently wish to learn about, or else the next SPI and I said, do not even know what it would be embarrassing.

SPI So what is it? SPI stands for Service Provider Interface, is a more important concept in Java, Java is intended to be provided by a third-party implementation or expansion of API, or in other words, SPI is a service discovery mechanism .

 

JDK SPI instructions and examples

SPI is relatively simple to use, only need to follow the following steps:

  • Jar package created under the META-INF / services directory with a "fully qualified name of the interface" as the name of the file content fully qualified name of the implementation class
  • Interface class is a jar on the classpath
  • The main program module implemented by java.util.ServiceLoader dynamic state, it is found fully qualified name of the implementation class by the configuration file in the scan META-INF / services directory, the class is loaded into the JVM
  • SPI implementation class must bring a constructor with no arguments

Then we look at a specific example, first define a SpiService, it is an interface:

Copy the code
package org.xrq.test.spi;

public interface SpiService {

    public void hello();
    
}
Copy the code

Achieve two categories, namely SpiServiceA and SpiServiceB:

Copy the code
package org.xrq.test.spi;

public class SpiServiceA implements SpiService {

    public void hello() {
        System.out.println("SpiServiceA.Hello");
    }
    
}
Copy the code
Copy the code
package org.xrq.test.spi;

public class SpiServiceB implements SpiService {

    @Override
    public void hello() {
        System.out.println("SpiServiceB.hello");
    }
    
}
Copy the code

Then we built a META-INF / services folder, which built a file, file name is the fully qualified name org.xrq.test.spi.SpiService interface:

Contents of the file is SpiService implementation class SpiServiceA, SpiServiceB fully qualified name:

org.xrq.test.spi.SpiServiceA 
org.xrq.test.spi.SpiServiceB

This is done! Then we write a test class automatically loads at these two categories:

Copy the code
public class SpiTest { 

    @Test 
    public void testSpi () { 
        Service Loading <Eating Vice> = ServiceLoader.load service loader (SpiService.class); 
        
        Iterator <Eating Vice> iterator = serviceLoader.iterator (); 
        while (iterator.hasNext ()) { 
            Eating Vice eat vice iterator.next = (); 
            
            spiService.hello (); 
        } 
    } 
    
}
Copy the code

Results at a glance, call the hello () method:

SpiServiceA.Hello 
SpiServiceB.hello

This is an example of the use of SPI, and then we look at the practical application of SPI in the scene.

 

Application of SPI in JDBC

Looking back at nearly forty years ago, the article https://www.cnblogs.com/xrq730/p/4851944.html , Entitled "JDBC study 2: Why write Class.forName (" XXX ")?"The article which was really immature technology, why not write Class.forName (" XXX ") interpretation now seems really weak burst, last reply to the first floor of users' reasons do not write that the new version of the JDBC use the SPI ", so learning a bit SPI immediately think of the examples, so you talk about the practical application of the JDBC SPI's.

In older versions of JDBC, assuming we are using MySql, JDBC initialization time is required to explicitly call Class.forName ( "com.mysql.jdbc.Driver") this one, but you do not need after a version do this step, and as noted above this is achieved by SPI, how to understand it. Class.forName fact no real meaning, in fact, neither new nor reflective objects generated objects, it's just to call the static method block com.mysql.jdbc.Driver of it:

Copy the code
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    //
    // Register ourselves with the DriverManager
    //
    static {
        try {
            java.sql.DriverManager.registerDriver(new Driver());
        } catch (SQLException E) {
            throw new RuntimeException("Can't register driver!");
        }
    }

    /**
     * Construct a new driver and register it with DriverManager
     * 
     * @throws SQLException
     *             if a database error occurs.
     */
    public Driver() throws SQLException {
        // Required for Class.forName().newInstance()
    }
}
Copy the code

Method of action is only a block by jdk own DriverManager registered Driver, registerDrivers nothing routine method, the Driver into CopyOnArrayList inside it:

Copy the code
public static synchronized void registerDriver(java.sql.Driver driver, DriverAction da)
      throws SQLException {

    /* Register the driver if it has not already been added to our list */
    if(driver != null) {
        registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
    } else {
        // This is for compatibility with the original DriverManager
        throw new NullPointerException();
    }

    println("registerDriver: " + driver);

}
Copy the code

From a JDK version, do not know which specific version, abandoned this operation, the new look of DriverManager, I was JDK1.8 of:

Copy the code
/**
 * Load the initial JDBC drivers by checking the System property
 * jdbc.properties and then use the {@code ServiceLoader} mechanism
 */
static {
    loadInitialDrivers();
    println("JDBC DriverManager initialized");
}
Copy the code

LoadInitialDrivers direct look at the core of this approach:

Copy the code
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;
    }
});
Copy the code

SPI used to find a way to see this java.sql.Driver file from the META-INF / services, and found inside the Driver implementation class by one injection. Finally, we look at the next Iterator () method to do something completely understand, by next () method call:

Copy the code
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");
   }
    if (!service.isAssignableFrom(c)) {
       fail(service,
             "Provider " + cn  + " not a subtype");
    }
    try {
        S p = service.cast(c.newInstance());
        providers.put(cn, p);
        return p;
    } catch (Throwable x) {
        fail(service,
             "Provider " + cn + " could not be instantiated",
            x);
    }
    throw new Error();          // This cannot happen
}
            
Copy the code

Class.forName see it, though they are Class.forName, but by the way the user manual SPI do the action becomes a framework to do.

 

Understanding of the SPI

After the last talk about my understanding of the SPI, learning how to use SPI, SPI examples of practical applications, a deep understanding of SPI mechanism to work in the future will truly SPI for our use.

First, we can note that the title is JDK SPI, SPI that is not exclusive JDK. Yes, I understand the SPI is actually a general term for pluggable technology, the simplest example is the USB, the USB standard provides manufacturers, manufacturers create their own USB peripherals according to the standard, such as a mouse, keyboard, gamepad etc., but particularly in the USB standard is how to use the computer, the manufacturers do not need the tube.

Back to our code is the same reason. When we develop a framework of time, in addition to ensuring the basic functions, the most important point is what? I think the most important should be loosely coupled, that is, to open for extension, but closed for modification to ensure that the framework for the user to achieve a black box.

Framework can not do all the things that can only be pulled out of the common part of the process, the core is the definition of loose coupling loose enough to achieve a good interface, or can be understood that the extension points, specific extension points allow users to implementation, such different extensions do not need to modify the source code or to customize the framework, which is the benefits of interface-oriented programming.

Back part of our framework for:

  • For JDK is achieved by SPI META-INF / services catalog + ServiceLoader
  • Spring is the way to achieve SPI left N number of interfaces, such as BeanPostProcessor, InitializingBean, DisposableBean, we only need to implement these interfaces can then inject

For existing framework, we can provide the framework for our extension point spread function framework. For write their own framework, SPI remember this thing, stay well enough extension points, which will greatly enhance the scalability you write frame.

 

================================================== ================================ 

every place I can not guarantee write is right, but at least can not guarantee copy, paste does not ensure that every word, every line of code after careful scrutiny and careful consideration. Behind every article, I hope to be able to see their skills, attitudes to life.

I believe that Steve Jobs said, only those who are crazy enough to think they can change the world can truly change the world. The face of pressure, I can burn the midnight oil, around the clock; the face of difficulties, I am willing to grasp the nettle, never back down.

In fact, I want to say is, I'm just a programmer, this is purely my whole life now.
 
https://www.cnblogs.com/xrq730/p/11440174.html

What is JDK SPI

Several recent workday SPI colleagues said several times this term, though, and I'm okay, but my heart still silently wish to learn about, or else the next SPI and I said, do not even know what it would be embarrassing.

SPI So what is it? SPI stands for Service Provider Interface, is a more important concept in Java, Java is intended to be provided by a third-party implementation or expansion of API, or in other words, SPI is a service discovery mechanism .

 

JDK SPI instructions and examples

SPI is relatively simple to use, only need to follow the following steps:

  • Jar package created under the META-INF / services directory with a "fully qualified name of the interface" as the name of the file content fully qualified name of the implementation class
  • Interface class is a jar on the classpath
  • The main program module implemented by java.util.ServiceLoader dynamic state, it is found fully qualified name of the implementation class by the configuration file in the scan META-INF / services directory, the class is loaded into the JVM
  • SPI implementation class must bring a constructor with no arguments

Then we look at a specific example, first define a SpiService, it is an interface:

Copy the code
package org.xrq.test.spi;

public interface SpiService {

    public void hello();
    
}
Copy the code

Achieve two categories, namely SpiServiceA and SpiServiceB:

Copy the code
package org.xrq.test.spi;

public class SpiServiceA implements SpiService {

    public void hello() {
        System.out.println("SpiServiceA.Hello");
    }
    
}
Copy the code
Copy the code
package org.xrq.test.spi;

public class SpiServiceB implements SpiService {

    @Override
    public void hello() {
        System.out.println("SpiServiceB.hello");
    }
    
}
Copy the code

Then we built a META-INF / services folder, which built a file, file name is the fully qualified name org.xrq.test.spi.SpiService interface:

Contents of the file is SpiService implementation class SpiServiceA, SpiServiceB fully qualified name:

org.xrq.test.spi.SpiServiceA 
org.xrq.test.spi.SpiServiceB

This is done! Then we write a test class automatically loads at these two categories:

Copy the code
public class SpiTest { 

    @Test 
    public void testSpi () { 
        Service Loading <Eating Vice> = ServiceLoader.load service loader (SpiService.class); 
        
        Iterator <Eating Vice> iterator = serviceLoader.iterator (); 
        while (iterator.hasNext ()) { 
            Eating Vice eat vice iterator.next = (); 
            
            spiService.hello (); 
        } 
    } 
    
}
Copy the code

Results at a glance, call the hello () method:

SpiServiceA.Hello 
SpiServiceB.hello

This is an example of the use of SPI, and then we look at the practical application of SPI in the scene.

 

Application of SPI in JDBC

Looking back at nearly forty years ago, the article https://www.cnblogs.com/xrq730/p/4851944.html , Entitled "JDBC study 2: Why write Class.forName (" XXX ")?》的文章里面当时技术真的是稚嫩,为什么不写Class.forName("XXX")的解释现在看来真的是弱爆了,最后一楼网友的回复"不用写的原因是,新版本JDBC使用了SPI",所以学了一下SPI马上就想起这个例子来了,因此就由JDBC讲讲SPI的实际应用。

在老版本的JDBC中,假设我们使用的是MySql,初始化JDBC的时候是需要显式调用Class.forName("com.mysql.jdbc.Driver")这一句的,但是在某个版本之后就不需要做这一步操作了,如上所说这是通过SPI实现的,怎么理解呢。Class.forName其实没有实际意义,其实既不会new对象也不会反射生成对象,它只是为了调用com.mysql.jdbc.Driver的static方法块而已:

Copy the code
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    //
    // Register ourselves with the DriverManager
    //
    static {
        try {
            java.sql.DriverManager.registerDriver(new Driver());
        } catch (SQLException E) {
            throw new RuntimeException("Can't register driver!");
        }
    }

    /**
     * Construct a new driver and register it with DriverManager
     * 
     * @throws SQLException
     *             if a database error occurs.
     */
    public Driver() throws SQLException {
        // Required for Class.forName().newInstance()
    }
}
Copy the code

方法块的作用只有一个,通过jdk自带的DriverManager注册Driver,registerDrivers方法没什么套路,把Driver放到CopyOnArrayList里面而已:

Copy the code
public static synchronized void registerDriver(java.sql.Driver driver, DriverAction da)
      throws SQLException {

    /* Register the driver if it has not already been added to our list */
    if(driver != null) {
        registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
    } else {
        // This is for compatibility with the original DriverManager
        throw new NullPointerException();
    }

    println("registerDriver: " + driver);

}
Copy the code

从某个JDK版本,具体也不知道哪个版本,废弃了这个操作,看下新版的DriverManager,我的是JDK1.8的:

Copy the code
/**
 * Load the initial JDBC drivers by checking the System property
 * jdbc.properties and then use the {@code ServiceLoader} mechanism
 */
static {
    loadInitialDrivers();
    println("JDBC DriverManager initialized");
}
Copy the code

直接看一下loadInitialDrivers这个方法的核心部分:

Copy the code
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;
    }
});
Copy the code

看到使用SPI的方式从META-INF/services下去找java.sql.Driver这个文件,并找到里面的Driver实现类逐一注入。最后我们看一下Iterator的next()方法做了什么就完全懂了,通过next()方法调用了:

Copy the code
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");
   }
    if (!service.isAssignableFrom(c)) {
       fail(service,
             "Provider " + cn  + " not a subtype");
    }
    try {
        S p = service.cast(c.newInstance());
        providers.put(cn, p);
        return p;
    } catch (Throwable x) {
        fail(service,
             "Provider " + cn + " could not be instantiated",
            x);
    }
    throw new Error();          // This cannot happen
}
            
Copy the code

看到Class.forName了吧,虽然都是Class.forName,但是通过SPI的方式把用户手动做的动作变成框架做。

 

对SPI的理解

最后谈一谈我对SPI的理解,学习了怎么用SPI、SPI在实际应用中的示例之后,深刻理解SPI机制才能在以后工作中真正将SPI为我所用。

首先大家可以注意到,标题是JDK SPI,也就是说SPI并不是JDK专属的。是的,我理解的SPI其实是一种可插拔技术的总称,最简单的例子就是USB,厂商提供了USB的标准,厂家根据USB的标准制造自己的外设,例如鼠标、键盘、游戏手柄等等,但是USB标准具体在电脑中是怎么用的,厂家就不需要管了。

回到我们的代码中也是一样的道理。当我们开发一个框架的时候,除了保证基本的功能外,最重要的一个点是什么?我认为最重要的应该是松耦合,即扩展开放、对修改关闭,保证框架实现对于使用者来说是黑盒。

框架不可能做好所有的事情,只能把共性的部分抽离出来进行流程化,松耦合实现的核心就是定义好足够松散的接口,或者可以理解是扩展点,具体的扩展点让使用者去实现,这样不同的扩展就不需要修改源代码或者对框架进行定制,这就是面向接口编程的好处。

回到我们框架的部分来说:

  • JDK对于SPI的实现是通过META-INF/services这个目录 + ServiceLoader
  • Spring is the way to achieve SPI left N number of interfaces, such as BeanPostProcessor, InitializingBean, DisposableBean, we only need to implement these interfaces can then inject

For existing framework, we can provide the framework for our extension point spread function framework. For write their own framework, SPI remember this thing, stay well enough extension points, which will greatly enhance the scalability you write frame.

 

Guess you like

Origin www.cnblogs.com/think90/p/11443161.html
SPI
Recommended