zoukankan      html  css  js  c++  java
  • SPI在JDBC中的运用

    前言

    之前学习了JDK SPI的机制,本文专门讨论2个内容:
    1.为什么在使用SPI后,不需要Class.forName()了?
    2.SPI在JDBC中的运用。

    JDBC模板代码

        private static final String URL = "jdbc:mysql://localhost:3306/demo?useSSL=true&useUnicode=true&characterEncoding=UTF-8";
        private static final String DRIVER = "com.mysql.jdbc.Driver";
    
        //加载驱动信息,使用SPI之后,不需要下面这行代码了。
        static {
            try {
                Class.forName(DRIVER);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
        public static void main(String[] args) throws Exception {
            Connection conn = DriverManager.getConnection(url, user, password); 
            String sql = "insert into user (name, pwd) values(?,?)";
            Statement st = conn.createStatement();
            st.executeQuery(sql);
        }
    

    为什么在使用SPI后,不需要Class.forName()了?

    首先我们需要了解:Class.forName("com.mysql.jdbc.Driver")的作用是:

    根据JVM类加载的原理,该代码会将这个字节码文件加载到虚拟机内部,
    而由于类加载共有7个阶段,并且Class.forName()方法中使用了JDK反射包中Class<?> caller = Reflection.getCallerClass();
    所以,Class.forName()方法不仅仅会加载Driver类,还会执行它的类初始化的过程(即静态代码块,静态变量赋值等操作)

    其次我们看看为什么不需要Class.forName()了?

    我们来分析一下main方法中的第一句源码:Connection conn = DriverManager.getConnection(url, user, password);
    首先调用该方法前,JVM会加载DriverManager类,然后执行连接,初始化。

    DriverManager.Class源码
        /**
         * Load the initial JDBC drivers by checking the System property
         * jdbc.properties and then use the {@code ServiceLoader} mechanism
         * 这里清楚的说明了,该代码块会使用SPI机制进行服务发现。
         */
        static {
            loadInitialDrivers();
            println("JDBC DriverManager initialized");
        }
    

    然后跟踪代码loadInitialDrivers();会发现:

    ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
    Iterator<Driver> driversIterator = loadedDrivers.iterator();
    
    /* 
     * 省略注释
     */
    try{
        while(driversIterator.hasNext()) {
            driversIterator.next();
        }
    } catch(Throwable t) {
    // Do nothing
    }
    

    我的上一篇博客JDK中的SPI机制中的例1和这里是一样的,因为我本来也是参考它来学习的。
    根据上一篇博客里面的类加载分析,我们知道了这里DriverManager.Class的静态初始化,和显示的执行Class.forName()是一致的。
    因为他们使用的类加载器分别是:
    1.DriverManager.Class静态初始化内的SPI机制所使用的是:线程上下文类加载器,默认为系统类加载器AppClassLoader。
    2.Class.forName()为main方法所在的类的类加载器:系统类加载器AppClassLoader。
    所以这里默认是同一个类加载器来加载我们classpath下面的com.mysql.jdbc.Driver

    综上:
    1.在使用SPI后,不需要显示调用Class.forName()了。
    2.SPI在JDBC中的运用就是如此,它使用了SPI来对实现方做了约束,并把实现独立于实现方中,JDK这样来提供了一种服务提供发现机制.

  • 相关阅读:
    Spring MVC 学习总结(五)——校验与文件上传
    Spring MVC 学习总结(四)——视图与综合示例
    Spring学习总结(二)——静态代理、JDK与CGLIB动态代理、AOP+IoC
    Spring MVC 学习总结(二)——控制器定义与@RequestMapping详解
    Spring学习总结(六)——Spring整合MyBatis完整示例
    Spring MVC 学习总结(一)——MVC概要与环境配置(IDea与Eclipse示例)
    Spring集成MyBatis完整示例
    Spring学习总结(五)——Spring整合MyBatis(Maven+MySQL)二
    Spring学习总结(五)——Spring整合MyBatis(Maven+MySQL)一
    Android自动连接指定的wifi,免密码或指定密码
  • 原文地址:https://www.cnblogs.com/1626ace/p/13780511.html
Copyright © 2011-2022 走看看