Java SPI机制分析(1),源码解读及如何保证线程安全

        }

        if (configs == null) {

            try {

                String fullName = PREFIX + service.getName();

                if (loader == null)

                    configs = ClassLoader.getSystemResources(fullName);

                else

                    configs = loader.getResources(fullName);

            } catch (IOException x) {

                fail(service, "Error locating configuration files", x);

            }

        }

        while ((pending == null) || !pending.hasNext()) {

            if (!configs.hasMoreElements()) {

                return false;

            }

            pending = parse(service, configs.nextElement());

        }

        nextName = pending.next();

        return true;

    }



    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

    }

    ......

}



类中指定了一个静态常量PREFIX = “META-INF/services/”,然后和java.sql.Driver拼接组成了fullName,然后通过类加载器去获取所有类路径下java.sql.Driver文件,获取之后存放在configs中,里面的每个元素对应一个文件,每个文件中可能会存在多个驱动类,所以使用pending用来存放每个文件中的驱动信息,获取驱动信息之后在nextService中使用Class.forName加载类信息,并且指定不进行初始化;同时在下面使用newInstance对驱动类进行了实例化操作;每个驱动类中都提供了一个静态注册代码块,比如mysql:



static {

try {

    java.sql.DriverManager.registerDriver(new Driver());

} catch (SQLException E) {

    throw new RuntimeException("Can't register driver!");

}

}




这里又实例化了一个驱动类,同时注册到DriverManager;接下来就是调用DriverManager的getConnection方法,代码如下:



private static Connection getConnection(

   String url, java.util.Properties info, Class caller) throws SQLException {

   /*

    * When callerCl is null, we should check the application's

    * (which is invoking this class indirectly)

    * classloader, so that the JDBC driver class outside rt.jar

    * can be loaded from here.

    */

   ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;

   synchronized(DriverManager.class) {

       // synchronize loading of the correct classloader.

       if (callerCL == null) {

           callerCL = Thread.currentThread().getContextClassLoader();

       }

   }



   if(url == null) {

       throw new SQLException("The url cannot be null", "08001");

   }



   println("DriverManager.getConnection(\"" + url + "\")");



   // Walk through the loaded registeredDrivers attempting to make a connection.

   // Remember the first exception that gets raised so we can reraise it.

   SQLException reason = null;



   for(DriverInfo aDriver : registeredDrivers) {

       // If the caller does not have permission to load the driver then

       // skip it.

       if(isDriverAllowed(aDriver.driver, callerCL)) {

           try {

               println("    trying " + aDriver.driver.getClass().getName());

               Connection con = aDriver.driver.connect(url, info);

               if (con != null) {

                   // Success!

                   println("getConnection returning " + aDriver.driver.getClass().getName());

                   return (con);

               }

           } catch (SQLException ex) {

               if (reason == null) {

                   reason = ex;

               }

           }



       } else {

           println("    skipping: " + aDriver.getClass().getName());

       }



   }



   // if we got here nobody could connect.

   if (reason != null)    {

       println("getConnection failed: " + reason);

       throw reason;

   }



   println("getConnection: no suitable driver found for "+ url);

   throw new SQLException("No suitable driver found for "+ url, "08001");

}




此方法主要是遍历之前注册的DriverInfo,拿着url信息去每个驱动类中建立连接,当然每个驱动类中都会进行url匹配校验,成功之后返回Connection,如果中途有失败的连接并不影响尝试新的驱动连接,遍历完之后还是无法获取连接,则抛出异常;



4.扩展  

如果想扩展新的驱动类也很简单,只需要在类路径下创建META-INF/services/文件夹,同时在里面创建java.sql.Driver文件,在文件中写入具体的驱动类名称,当然此类需要继承java.sql.Driver接口类;例如实例中提供的TestDriver。



**序列化实战**  

1.准备接口类



public interface Serialization {

/**

 * 序列化

 * 

 * @param obj

 * @return

 */

public byte[] serialize(Object obj) throws Exception;



/**

 * 反序列化

 * 

 * @param param

 * @param clazz

 * @return

 * @throws Exception

 */

public  T deserialize(byte[] param, Class clazz) throws Exception;



/**

 * 序列化名称

 * 

 * @return

 */

public String getName();

}




2.准备实现类  

分别准备JsonSerialization和ProtobufSerialization



3.接口文件  

在META-INF/services/目录下创建文件com.spi.serializer.Serialization,内容如下:



com.spi.serializer.JsonSerialization

com.spi.serializer.ProtobufSerialization

最后

由于篇幅限制,小编在此截出几张知识讲解的图解,有需要的程序猿(媛)可以点赞后戳这里免费领取全部资料获取哦

P8级大佬整理在Github上45K+star手册,吃透消化,面试跳槽不心慌

P8级大佬整理在Github上45K+star手册,吃透消化,面试跳槽不心慌

P8级大佬整理在Github上45K+star手册,吃透消化,面试跳槽不心慌

P8级大佬整理在Github上45K+star手册,吃透消化,面试跳槽不心慌

P8级大佬整理在Github上45K+star手册,吃透消化,面试跳槽不心慌

m.spi.serializer.ProtobufSerialization

最后

由于篇幅限制,小编在此截出几张知识讲解的图解,有需要的程序猿(媛)可以点赞后戳这里免费领取全部资料获取哦

[外链图片转存中…(img-4Lq2OMjc-1628339334284)]

[外链图片转存中…(img-XGFfsyp4-1628339334287)]

[外链图片转存中…(img-P4j6022Q-1628339334289)]

[外链图片转存中…(img-WSHEvj1C-1628339334292)]

[外链图片转存中…(img-4o8YgcxL-1628339334295)]

你可能感兴趣的:(程序员,后端,java,面试)