摘要:之前的作用范圍其中驅(qū)動(dòng)通常使用來引入所需要的驅(qū)動(dòng)。在編譯期間不用引入具體的具體實(shí)現(xiàn)類不管還是等。所以包的應(yīng)該設(shè)置為。用于從中找到接口的實(shí)現(xiàn)類。文件中有兩個(gè)驅(qū)動(dòng)名。而不是通過來找實(shí)現(xiàn)類的。是通過找一個(gè)固定包下的固定類類而是找固定文件下的內(nèi)容。
之前
maven 的runtime作用范圍
其中JDBC驅(qū)動(dòng)通常使用Class.forName("com.mysql.jdbc.Driver");來引入所需要的驅(qū)動(dòng)。在編譯期間不用引入具體jdbc的具體實(shí)現(xiàn)類(不管mysql還是oracle等)。所以JDBC包的scope應(yīng)該設(shè)置為runtime。
mysql mysql-connector-java ${mysql.driver.version} runtime
JDBC4之后使用SPI技術(shù) 也不用寫Class.forName了
Service Provider InterfaceJava API提供的使用ServiceLoader來實(shí)現(xiàn)的控制反轉(zhuǎn)工具類。用于從classpath中找到接口的實(shí)現(xiàn)類。
先定義一個(gè)接口
package ch.frankel.blog.serviceloader; public interface Foo { }
再定義兩個(gè)實(shí)現(xiàn)類(實(shí)現(xiàn)類可能是其他工程中定義的)
package ch.frankel.blog.serviceloader; public class FooImpl1 implements Foo { } public class FooImpl2 implements Foo { }
然后在Resources/META-INF下的service中新建一個(gè)與接口名對(duì)應(yīng)的文件ch.frankel.blog.serviceloader.Foo
ch.frankel.blog.serviceloader.FooImpl1 ch.frankel.blog.serviceloader.FooImpl2
serviceLoader會(huì)從該文件中找到要加載的類。
public class JavaServiceLoaderTest { @Test public void java_service_loader_should_load_correct_implementations() { //Service.load會(huì)從ch.frankel.blog.serviceloader.Foo中找到要加載的類,然后加載。 ServiceLoaderSpring 集成 ServiceLoaderloader = ServiceLoader.load(Foo.class); List foos = new ArrayList<>(); loader.iterator().forEachRemaining(foos::add); assertThat(foos) .isNotNull() .isNotEmpty() .hasSize(2); } }
首先需要配置對(duì)應(yīng)的factoryBean
@Configuration public class ServiceConfiguration { @Bean public ServiceListFactoryBean serviceListFactoryBean() { ServiceListFactoryBean serviceListFactoryBean = new ServiceListFactoryBean(); serviceListFactoryBean.setServiceType(Foo.class); return serviceListFactoryBean; } }
然后通過ServiceListFactoryBean就可以找到接口對(duì)應(yīng)的實(shí)現(xiàn)類。
@ContextConfiguration(classes = ServiceConfiguration.class) public class ServiceLoaderWithSpringTest extends AbstractTestNGSpringContextTests { @Autowired private ServiceListFactoryBean serviceListFactoryBean; @Test public void spring_service_loader_integration_should_load_correct_implementations() throws Exception { Object object = serviceListFactoryBean.getObject(); Assertions.assertThat(object) .isNotNull() .asList() .isNotEmpty() .hasSize(2); } }Spring Facotories Loader
spring 自己也提供了類似的工具類。使用起來更方便。
首先在META-INF下建立一個(gè)spring.factories文件。
ch.frankel.blog.serviceloader.Foo=ch.frankel.blog.serviceloader.FooImpl1,ch.frankel.blog.serviceloader.FooImpl2
直接指定接口對(duì)應(yīng)的實(shí)現(xiàn)類。
然后通過Spring提供的靜態(tài)方法SpringFactoriesLoader就可以直接使用了。
public class SpringFactoriesTest { @Test public void spring_factories_should_load_correct_implementations() { ListService Provider Interface的應(yīng)用 JDBC SPIfoos = SpringFactoriesLoader.loadFactories(Foo.class, null); assertThat(foos) .isNotNull() .isNotEmpty() .hasSize(2); } }
查看jdbc的代碼
//java.sql.DriverManager public class DriverManager { /** * 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"); } private static void loadInitialDrivers() { String drivers; try { drivers = AccessController.doPrivileged(new PrivilegedAction() { public String run() { //從系統(tǒng)變量中獲取jdbc.drivers //System.getProperty 可以在vm arguments中設(shè)置。java -Djdbc.drivers=xxxx return System.getProperty("jdbc.drivers"); } }); } catch (Exception ex) { drivers = null; } // If the driver is packaged as a Service Provider, load it. // Get all the drivers through the classloader // exposed as a java.sql.Driver.class service. // ServiceLoader.load() replaces the sun.misc.Providers() AccessController.doPrivileged(new PrivilegedAction () { public Void run() { ServiceLoader loadedDrivers = ServiceLoader.load(Driver.class); Iterator 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; } }); println("DriverManager.initialize: jdbc.drivers = " + drivers); if (drivers == null || drivers.equals("")) { return; } //多個(gè)drivers? String[] driversList = drivers.split(":"); println("number of Drivers:" + driversList.length); for (String aDriver : driversList) { try { println("DriverManager.Initialize: loading " + aDriver); //還是使用Class.forName來加載驅(qū)動(dòng) Class.forName(aDriver, true, ClassLoader.getSystemClassLoader()); } catch (Exception ex) { println("DriverManager.Initialize: load failed: " + ex); } } } }
在jdbc的實(shí)現(xiàn)包中META-INF下有java.sql.Driver文件。
com.mysql.jdbc.Driver com.mysql.fabric.jdbc.FabricMySQLDriver
文件中有兩個(gè)驅(qū)動(dòng)名。
其實(shí)jdbc可以同時(shí)管理多個(gè)驅(qū)動(dòng),(jdbc詳解:2、DriverManager管理多個(gè)數(shù)據(jù)庫(kù)驅(qū)動(dòng))
Commons-Logging//org.apache.commons.logging.LogFactory#getFactory public static LogFactory getFactory() throws LogConfigurationException { ... // Second, try to find a service by using the JDK1.3 class // discovery mechanism, which involves putting a file with the name // of an interface class in the META-INF/services directory, where the // contents of the file is a single line specifying a concrete class // that implements the desired interface. if (factory == null) { if (isDiagnosticsEnabled()) { logDiagnostic( "[LOOKUP] Looking for a resource file of name [" + SERVICE_ID + "] to define the LogFactory subclass to use..."); } try { 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 xerces BufferedReader 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 == null if (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" + ": [" + ex.getMessage().trim() + "]. Trying alternative implementations..."); } ; // ignore } } ... }
其中也使用到SPI。從 META-INF/services/org.apache.commons.logging.LogFactory中找要加載的實(shí)現(xiàn)類(Apache Commons Logging 是如何決定使用哪個(gè)日志實(shí)現(xiàn)類的)。
slf4j而slf4j不是通過SPI來找實(shí)現(xiàn)類的。slf4j 1.7是通過找一個(gè)固定包下的固定類StaticLoggerBinder類(而SPI是找固定文件下的內(nèi)容)。這個(gè)類定義在各個(gè)實(shí)現(xiàn)包中。
貌似slf4j 1.8 開始使用SPI了,如下圖。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/71203.html
摘要:通過注解引入對(duì)象。添加一個(gè)使用接收請(qǐng)求添加賬號(hào)訪問層添加微服務(wù)啟動(dòng)類鏈接數(shù)據(jù)庫(kù)通過編寫數(shù)據(jù)庫(kù)訪問。 SpringCloud(第 041 篇)鏈接Mysql數(shù)據(jù)庫(kù),通過JdbcTemplate編寫數(shù)據(jù)庫(kù)訪問 - 一、大致介紹 1、前面章節(jié)提到了JPA操作訪問數(shù)據(jù)庫(kù),本章節(jié)我們講解一下如何用JdbcTemplate操作訪問數(shù)據(jù)庫(kù); 2、使用JdbcTemplate是一個(gè)相對(duì)更底層的操作,可...
摘要:一考慮用靜態(tài)工廠方法代替構(gòu)造器構(gòu)造器是創(chuàng)建一個(gè)對(duì)象實(shí)例的最基本最常用的方法。開發(fā)者在使用某個(gè)類的時(shí)候,通常會(huì)使用一個(gè)構(gòu)造器來實(shí)現(xiàn),其實(shí)也有其他方式可以實(shí)現(xiàn)的,如利用發(fā)射機(jī)制。 一、考慮用靜態(tài)工廠方法代替構(gòu)造器 構(gòu)造器是創(chuàng)建一個(gè)對(duì)象實(shí)例的最基本最常用的方法。開發(fā)者在使用某個(gè)類的時(shí)候,通常會(huì)使用new一個(gè)構(gòu)造器來實(shí)現(xiàn),其實(shí)也有其他方式可以實(shí)現(xiàn)的,如利用發(fā)射機(jī)制。這里主要說的是通過靜態(tài)類工...
摘要:添加用戶接口類實(shí)現(xiàn)類簡(jiǎn)單用戶鏈接數(shù)據(jù)庫(kù)微服務(wù)通過注解標(biāo)注該類為持久化操作對(duì)象。添加一個(gè)使用接收請(qǐng)求添加微服務(wù)啟動(dòng)類鏈接數(shù)據(jù)庫(kù)簡(jiǎn)單的集成框架采用訪問數(shù)據(jù)庫(kù)。 SpringCloud(第 044 篇)鏈接Mysql數(shù)據(jù)庫(kù)簡(jiǎn)單的集成Mybatis框架采用MapperXml訪問數(shù)據(jù)庫(kù) - 一、大致介紹 1、前面章節(jié)講解的是在方法上面添加sql語句操作,雖然說僅僅只是一種簡(jiǎn)單的操作,在測(cè)試期間可...
閱讀 916·2021-11-18 10:02
閱讀 2683·2021-11-11 16:54
閱讀 2852·2021-09-02 09:45
閱讀 715·2019-08-30 12:52
閱讀 2902·2019-08-29 14:04
閱讀 2798·2019-08-29 12:39
閱讀 523·2019-08-29 12:27
閱讀 1956·2019-08-26 13:23