最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java深入講解SPI的使用

 更新時間:2022年06月16日 08:58:50   作者:hi wei  
SPI英文全稱為Service Provider Interface,顧名思義,服務提供者接口,它是jdk提供給“服務提供廠商”或者“插件開發(fā)者”使用的接口

什么是Java SPI

    SPI的全名為:Service Provider Interface。在java.util.ServiceLoader的文檔里有比較詳細的介紹。簡單的總結下 Java SPI 機制的思想。我們系統(tǒng)里抽象的各個模塊,往往有很多不同的實現(xiàn)方案,比如日志模塊的方案,xml解析模塊、jdbc模塊的方案等。面向的對象的設計里,我們一般推薦模塊之間基于接口編程,模塊之間不對實現(xiàn)類進行硬編碼。一旦代碼里涉及具體的實現(xiàn)類,就違反了可拔插的原則,如果需要替換一種實現(xiàn),就需要修改代碼。為了實現(xiàn)在模塊裝配的時候能不在程序里動態(tài)指明,這就需要一種服務發(fā)現(xiàn)機制。

    Java SPI 就是提供這樣的一個機制:為某個接口尋找服務實現(xiàn)的機制。有點類似IOC的思想,就是將裝配的控制權移到程序之外,在模塊化設計中這個機制尤其重要Java SPI 的具體約定為:當服務的提供者,提供了服務接口的一種實現(xiàn)之后,在jar包的META-INF/services/目錄里同時創(chuàng)建一個以服務接口命名的文件。該文件里就是實現(xiàn)該服務接口的具體實現(xiàn)類。而當外部程序裝配這個模塊的時候,就能通過該jar包META-INF/services/里的配置文件找到具體的實現(xiàn)類名,并裝載實例化,完成模塊的注入?;谶@樣一個約定就能很好的找到服務接口的實現(xiàn)類,而不需要再代碼里制定。jdk提供服務實現(xiàn)查找的一個工具類:java.util.ServiceLoader。

Java SPI使用demo

定義一個接口:

package com.hiwei.spi.demo;
public interface Animal {
    void speak();
}

創(chuàng)建兩個實現(xiàn)類:

package com.hiwei.spi.demo;
public class Cat implements Animal {
    @Override
    public void speak() {
        System.out.println("喵喵喵!");
    }
}
package com.hiwei.spi.demo;
public class Dog implements Animal {
    @Override
    public void speak() {
        System.out.println("汪汪汪!");
    }
}

在resources目錄下創(chuàng)建META-INF/services目錄:

創(chuàng)建以接口類路徑命名的文件,文件中添加實現(xiàn)類路徑:

com.hiwei.spi.demo.Cat
com.hiwei.spi.demo.Dog

使用

package com.hiwei.spi;
import com.hiwei.spi.demo.Animal;
import java.sql.SQLException;
import java.util.ServiceLoader;
public class SpiDemoApplication {
    public static void main(String[] args){
    	//會根據文件找到對應的實現(xiàn)類
        ServiceLoader<Animal> load = ServiceLoader.load(Animal.class);
        //執(zhí)行實現(xiàn)類方法
        for (Animal animal : load) {
            animal.speak();
        }
    }
}

執(zhí)行結果:

上面我們可以看到java spi會幫助我們找到接口實現(xiàn)類。那么實際生產中怎么使用呢? 將上面的代碼打成jar,然后在其它項目中引入,同樣的目錄下創(chuàng)建文件,并寫上自己實現(xiàn)類的路徑:

本項目實現(xiàn)類:

package com.example.demo;
import com.hiwei.spi.demo.Animal;
public class Pig implements Animal {
    @Override
    public void speak() {
        System.out.println("哼哼哼!");
    }
}

代碼中,我們調用jar中的main方法:

package com.example.demo;
import com.hiwei.spi.SpiDemoApplication;
public class DemoApplication {
    public static void main(String[] args) {
        SpiDemoApplication.main(args);
    }
}

執(zhí)行結果:

可以看見自定義的實現(xiàn)類也被執(zhí)行了。在實際生產中,我們就可以使用java spi面向接口編程,實現(xiàn)可插拔。

SPI在JDBC中的應用

以最新的mysql-connector-java-8.0.27.jar為例

<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.27</version>
</dependency>

在使用JDBC連接數(shù)據庫時,只需要使用:

DriverManager.getConnection("url", "username", "password");

DriverManager有靜態(tài)方法:

    static {
        loadInitialDrivers();
        println("JDBC DriverManager initialized");
    }

看下loadInitialDrivers()方法,其中有:

AccessController.doPrivileged(new PrivilegedAction<Void>() {
            public Void run() {
				//獲取Driver.class的實現(xiàn)類
                ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
                Iterator<Driver> driversIterator = loadedDrivers.iterator();

                /* Load these drivers, so that they can be instantiated.
                 * It may be the case that the driver class may not be there
                 * i.e. there may be a packaged driver with the service class
                 * as implementation of java.sql.Driver but the actual class
                 * may be missing. In that case a java.util.ServiceConfigurationError
                 * will be thrown at runtime by the VM trying to locate
                 * and load the service.
                 *
                 * Adding a try catch block to catch those runtime errors
                 * if driver not available in classpath but it's
                 * packaged as service and that service is there in classpath.
                 */
                try{
                    while(driversIterator.hasNext()) {
                        driversIterator.next();
                    }
                } catch(Throwable t) {
                // Do nothing
                }
                return null;
            }
        });

可以看見,會根據java spi獲取Driver.class的實現(xiàn)類,可以在mysql-connector-java-8.0.27.jar下面看到,定義的文件:

程序會根據文件找到對應的實現(xiàn)類,并連接數(shù)據庫。

SPI在sharding-jdbc中的應用

    sharding-jdbc是一款用于分庫分表的中間件,在數(shù)據庫分布式場景中,對于主鍵生成要保證唯一性,主鍵生成策略有很多種實現(xiàn)。sharding-jsbc在主鍵生成上就使用了SPI進行擴展。

下面看下sharding-jdbc源碼在主鍵生成上是怎么應用的: 源碼中的 ShardingRule.class主要封裝分庫分表的策略規(guī)則,包括主鍵生成??聪耤reateDefaultKeyGenerator方法:

//生成默認主鍵生成策略
private ShardingKeyGenerator createDefaultKeyGenerator(final KeyGeneratorConfiguration keyGeneratorConfiguration) {
     //SPI服務發(fā)現(xiàn)
     ShardingKeyGeneratorServiceLoader serviceLoader = new ShardingKeyGeneratorServiceLoader();
     return containsKeyGeneratorConfiguration(keyGeneratorConfiguration)
             ? serviceLoader.newService(keyGeneratorConfiguration.getType(), keyGeneratorConfiguration.getProperties()) : 					serviceLoader.newService();
    }

繼續(xù)看ShardingKeyGeneratorServiceLoader(),有靜態(tài)代碼塊注冊:

    static {
        //SPI: 加載主鍵生成策略
        NewInstanceServiceLoader.register(ShardingKeyGenerator.class);
    }

看下register方法:

    public static <T> void register(final Class<T> service) {
    	//服務發(fā)現(xiàn)
        for (T each : ServiceLoader.load(service)) {
            registerServiceClass(service, each);
        }
    }

看到這,真相大白,就是應用java spi機制。

我們再看下resources目錄下:

可以看到有對應接口命名的文件,文件內容:

有兩個實現(xiàn),分別是雪花算法和UUID,這也對應了sharding-jdbc的提供的兩種生成策略。我們在使用sharding-jdbc時,也可以自定義策略,便于擴展。 sharding-jdbc對于SPI的使用點還有很多,這里就不一一列舉了。對于SPI機制,我們在工作中也可以實際應用,提升程序的可擴展性。

擴展

以上是Java SPI的解析。其實SPI機制在很多地方都有用到,只是以不同的形式應用,具體的實現(xiàn)略有不同。例如dubbo中也有類似的spi機制;springboot的自動裝配,也使用了spi機制:

springboot自動裝配:

定義文件:

文件中聲明需要發(fā)現(xiàn)的類:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.hiwei.valve.ValveAutoConfiguration

springboot的掃描文件,裝配對應的類:

	private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
		Map<String, List<String>> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}
		result = new HashMap<>();
		try {
			//加載文件中的類
			Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryTypeName = ((String) entry.getKey()).trim();
					String[] factoryImplementationNames =
							StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
					for (String factoryImplementationName : factoryImplementationNames) {
						result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
								.add(factoryImplementationName.trim());
					}
				}
			}
			// Replace all lists with unmodifiable lists containing unique elements
			result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
					.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
			cache.put(classLoader, result);
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
		return result;
	}

FACTORIES_RESOURCE_LOCATION的值:

SPI在Java開發(fā)中是個很重要的設計,所以我們一定要熟練掌握。

到此這篇關于Java深入講解SPI的使用的文章就介紹到這了,更多相關Java SPI內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Java基礎:徹底搞懂java多線程

    Java基礎:徹底搞懂java多線程

    篇文章主要介紹了Java多線程的相關資料,幫助大家更好的理解和學習Java線程相關知識,感興趣的朋友可以了解下,希望能給你帶來幫助
    2021-08-08
  • Java連接Oracle數(shù)據庫實例解析

    Java連接Oracle數(shù)據庫實例解析

    數(shù)據庫的操作是當前系統(tǒng)開發(fā)必不可少的開發(fā)部分之一。接下來通過本文給大家介紹Java連接Oracle數(shù)據庫實例解析,非常不錯具有參考借鑒價值,感興趣的朋友一起學習吧
    2016-06-06
  • Spring Data Redis對象緩存序列化問題解決

    Spring Data Redis對象緩存序列化問題解決

    相信在項目中,你一定是經常使用Redis,在使用時,有沒有遇到同我一樣,對象緩存序列化問題的呢,本文主要介紹了Spring Data Redis對象緩存序列化問題解決,感興趣的可以了解一下
    2024-01-01
  • Java Lambda表達式詳解

    Java Lambda表達式詳解

    這篇文章主要介紹了Java Lambda表達式詳解,包括了Java Lambda表達式創(chuàng)建線程,Java Lambda表達式的語法,Java lambda遍歷List集合,Java lambda過濾String需要的朋友可以參考下
    2023-02-02
  • Java獲取HttpServletRequest的三種方法詳解

    Java獲取HttpServletRequest的三種方法詳解

    這篇文章主要介紹了Java獲取HttpServletRequest的三種方法詳解,是一個接口,全限定名稱為Jakarta.Serclet.http.HttpServletRequest
    HttpServletRequest接口是Servlet規(guī)范的一員,需要的朋友可以參考下
    2023-11-11
  • Java使用注解實現(xiàn)防止重復提交實例

    Java使用注解實現(xiàn)防止重復提交實例

    這篇文章主要介紹了Java使用注解實現(xiàn)防止重復提交實例,在一些項目中由于用戶誤操作,多次點擊表單提交按鈕,會產生很多次的數(shù)據交互,為了解決這一問題,本文使用注解來實現(xiàn)防止重復提交,需要的朋友可以參考下
    2023-07-07
  • visual studio 2019安裝配置可編寫c/c++語言的IDE環(huán)境

    visual studio 2019安裝配置可編寫c/c++語言的IDE環(huán)境

    這篇文章主要介紹了visual studio 2019安裝配置可編寫c/c++語言的IDE環(huán)境,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-03-03
  • 快速了解JAVA垃圾回收機制

    快速了解JAVA垃圾回收機制

    這篇文章主要介紹了有關Java垃圾回收機制的知識,文中實例簡單易懂,方便大家更好的學習,有興趣的朋友可以了解下
    2020-06-06
  • Spring Boot啟動及退出加載項的方法

    Spring Boot啟動及退出加載項的方法

    這篇文章主要介紹了Spring Boot啟動及退出加載項的方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-04-04
  • Mybatis的TypeHandler加解密數(shù)據實現(xiàn)

    Mybatis的TypeHandler加解密數(shù)據實現(xiàn)

    在我們數(shù)據庫中有些時候會保存一些用戶的敏感信息,所以就需要對這些數(shù)據進行加密,那么本文就介紹了Mybatis的TypeHandler加解密數(shù)據實現(xiàn),感興趣的可以了解一下
    2021-06-06

最新評論

涞水县| 中方县| 瑞安市| 比如县| 闸北区| 桦南县| 普洱| 连山| 郴州市| 抚顺县| 隆安县| 修武县| 彩票| 建宁县| 潞西市| 鄄城县| 郓城县| 改则县| 阿拉尔市| 双江| 盈江县| 原平市| 新泰市| 灵川县| 库车县| 扎鲁特旗| 韶关市| 留坝县| 高邑县| 兴隆县| 无锡市| 眉山市| 台江县| 新巴尔虎左旗| 东至县| 岑溪市| 双流县| 荆州市| 新和县| 申扎县| 兴义市|