private static void loadInitialDrivers() {String drivers;try {drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {public String run() {return System.getProperty("jdbc.drivers");}});} catch (Exception ex) {drivers = null;}// 通过 classloader 获取所有实现 java.sql.Driver 的驱动类AccessController.doPrivileged(new PrivilegedAction<Void>() {public Void run() {// 利用 SPI,记载所有 Driver 服务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;}});// 打印数据库驱动信息println("DriverManager.initialize: jdbc.drivers = " + drivers);if (drivers == null || drivers.equals("")) {return;}String[] driversList = drivers.split(":");println("number of Drivers:" + driversList.length);for (String aDriver : driversList) {try {println("DriverManager.Initialize: loading " + aDriver);// 尝试实例化驱动Class.forName(aDriver, true,ClassLoader.getSystemClassLoader());} catch (Exception ex) {println("DriverManager.Initialize: load failed: " + ex);}}}上面的代码主要步骤是:
- 从系统变量中获取驱动的实现类 。
- 利用 SPI 来获取所有驱动的实现类 。
- 遍历所有驱动,尝试实例化各个实现类 。
- 根据第 1 步获取到的驱动列表来实例化具体的实现类 。
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);这里实际获取的是java.util.ServiceLoader.LazyIterator 迭代器 。调用其 hasNext 方法时,会搜索 classpath 下以及 jar 包中的 META-INF/services 目录,查找 java.sql.Driver 文件,并找到文件中的驱动实现类的全限定名 。调用其 next 方法时,会根据驱动类的全限定名去尝试实例化一个驱动类的对象 。
4.2 SPI 应用案例之 Common-Loggincommon-logging(也称 Jakarta Commons Logging,缩写 JCL)是常用的日志门面工具包 。common-logging 的核心类是入口是 LogFactory,LogFatory 是一个抽象类,它负责加载具体的日志实现 。
其入口方法是 LogFactory.getLog 方法,源码如下:
public static Log getLog(Class clazz) throws LogConfigurationException {return getFactory().getInstance(clazz);}public static Log getLog(String name) throws LogConfigurationException {return getFactory().getInstance(name);}从以上源码可知,getLog 采用了工厂设计模式,是先调用 getFactory 方法获取具体日志库的工厂类,然后根据类名称或类型创建日志实例 。
LogFatory.getFactory 方法负责选出匹配的日志工厂,其源码如下:
public static LogFactory getFactory() throws LogConfigurationException {// 省略...// 加载 commons-logging.properties 配置文件Properties props = getConfigurationFile(contextClassLoader, FACTORY_PROPERTIES);// 省略...// 决定创建哪个 LogFactory 实例// (1)尝试读取全局属性 org.apache.commons.logging.LogFactoryif (isDiagnosticsEnabled()) {logDiagnostic("[LOOKUP] Looking for system property [" + FACTORY_PROPERTY +"] to define the LogFactory subclass to use...");}try {// 如果指定了 org.apache.commons.logging.LogFactory 属性,尝试实例化具体实现类String factoryClass = getSystemProperty(FACTORY_PROPERTY, null);if (factoryClass != null) {if (isDiagnosticsEnabled()) {logDiagnostic("[LOOKUP] Creating an instance of LogFactory class '" + factoryClass +"' as specified by system property " + FACTORY_PROPERTY);}factory = newFactory(factoryClass, baseClassLoader, contextClassLoader);} else {if (isDiagnosticsEnabled()) {logDiagnostic("[LOOKUP] No system property [" + FACTORY_PROPERTY + "] defined.");}}} catch (SecurityException e) {// 异常处理} catch (RuntimeException e) {// 异常处理}// (2)利用 Java SPI 机制,尝试在 classpatch 的 META-INF/services 目录下寻找 org.apache.commons.logging.LogFactory 实现类if (factory == null) {if (isDiagnosticsEnabled()) {logDiagnostic("[LOOKUP] Looking for a resource file of name [" + SERVICE_ID +"] to define the LogFactory subclass to use...");}try {final 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 xercesBufferedReader 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 == nullif (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" +": [" + trim(ex.getMessage()) +"]. Trying alternative implementations...");}// ignore}}// (3)尝试从 classpath 目录下的 commons-logging.properties 文件中查找 org.apache.commons.logging.LogFactory 属性if (factory == null) {if (props != null) {if (isDiagnosticsEnabled()) {logDiagnostic("[LOOKUP] Looking in properties file for entry with key '" + FACTORY_PROPERTY +"' to define the LogFactory subclass to use...");}String factoryClass = props.getProperty(FACTORY_PROPERTY);if (factoryClass != null) {if (isDiagnosticsEnabled()) {logDiagnostic("[LOOKUP] Properties file specifies LogFactory subclass '" + factoryClass + "'");}factory = newFactory(factoryClass, baseClassLoader, contextClassLoader);// TODO: think about whether we need to handle exceptions from newFactory} else {if (isDiagnosticsEnabled()) {logDiagnostic("[LOOKUP] Properties file has no entry specifying LogFactory subclass.");}}} else {if (isDiagnosticsEnabled()) {logDiagnostic("[LOOKUP] No properties file available to determine" + " LogFactory subclass from..");}}}// (4)以上情况都不满足,实例化默认实现类 org.apache.commons.logging.impl.LogFactoryImplif (factory == null) {if (isDiagnosticsEnabled()) {logDiagnostic("[LOOKUP] Loading the default LogFactory implementation '" + FACTORY_DEFAULT +"' via the same classloader that loaded this LogFactory" +" class (ie not looking in the context classloader).");}factory = newFactory(FACTORY_DEFAULT, thisClassLoader, contextClassLoader);}if (factory != null) {/*** Always cache using context class loader.*/cacheFactory(contextClassLoader, factory);if (props != null) {Enumeration names = props.propertyNames();while (names.hasMoreElements()) {String name = (String) names.nextElement();String value = https://www.huyubaike.com/biancheng/props.getProperty(name);factory.setAttribute(name, value);}}}return factory;}
经验总结扩展阅读
- 10月28日3时42分新疆阿克苏地区沙雅县发生3.0级地震
- 10月28日4时34分新疆阿克苏地区温宿县发生3.0级地震
- 今年第22号台风“尼格”最强可达台风级 30日傍晚前后进入南海海面
- 红米note9pro评测最新_红米note9pro深度测评
- 空调一级能效与三级能效的区别是什么
- iOS 16.3.1正式版续航怎么样 iOS 16.3.1升级建议
- ipx7和ip68哪个更防水 哪个防水等级更高
- 10月28日今起三天陕西持续阴雨模式局地阵风6级 风寒效应明显需保暖
- EasyPoi大数据导入导出百万级实例
- Dubbo-聊聊通信模块设计