1. 程式人生 > >為什麼說JDBC驅動類載入破壞了雙親委派機制

為什麼說JDBC驅動類載入破壞了雙親委派機制

大家都知道jdk的類載入機制是雙親委派機制,當我們需要載入一個類的時候,比如如下幾種情況

  • new一個類的物件
  • 呼叫類的靜態成員(除了final常量)和靜態方法
  • 使用java.lang.reflect包的方法對類進行反射呼叫
  • 當虛擬機器啟動,java Demo01,則一定會初始化Demo01類,載入main方法所在的類
  • 當初始化一個類,如果其父類沒有被初始化,則先初始化它父類

那麼看一段程式碼:

    public static void main(String[] args) throws SQLException {
        String url = "jdbc:mysql://...";
        String username = "root";
        String password = "root";
        Connection conn = null;
        try {
            conn = DriverManager.getConnection(url, username, password);
            PreparedStatement statement = conn.prepareStatement("select * from table where id = ?");
            statement.setLong(1,615444000000000L);
            ResultSet set = statement.executeQuery();
            while(set.next()){
                System.out.println(set.getString("user_id"));
            }
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            if(conn!=null){
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }

上面是一段標準的jdbc查詢程式碼,在載入java.sql.DriverManager類的時候,會執行靜態塊

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

主要是這兩行程式碼

ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Iterator<Driver> driversIterator = loadedDrivers.iterator();
//我們看到這裡使用了執行緒上下文載入器,為什麼這裡要這麼做??如果不這麼做會怎樣?
    public static <S> ServiceLoader<S> load(Class<S> service) {
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        return ServiceLoader.load(service, cl);
    }

下面是java.util.ServiceLoader$LazyIterator.nextService()方法

    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
        }

我們看到了Class.forName(cn, false, loader)的呼叫,如果前面沒有用執行緒上下文載入器(也就是loader),那麼這裡預設使用的是呼叫方的類載入器,如下所示:

/**
     * Returns the {@code Class} object associated with the class or
     * interface with the given string name.  Invoking this method is
     * equivalent to:
     *
     * <blockquote>
     *  {@code Class.forName(className, true, currentLoader)}
     * </blockquote>
     *
     * where {@code currentLoader} denotes the defining class loader of
     * the current class.
     *
     * <p> For example, the following code fragment returns the
     * runtime {@code Class} descriptor for the class named
     * {@code java.lang.Thread}:
     *
     * <blockquote>
     *   {@code Class t = Class.forName("java.lang.Thread")}
     * </blockquote>
     * <p>
     * A call to {@code forName("X")} causes the class named
     * {@code X} to be initialized.
     *
     * @param      className   the fully qualified name of the desired class.
     * @return     the {@code Class} object for the class with the
     *             specified name.
     * @exception LinkageError if the linkage fails
     * @exception ExceptionInInitializerError if the initialization provoked
     *            by this method fails
     * @exception ClassNotFoundException if the class cannot be located
     */
    @CallerSensitive
    public static Class<?> forName(String className)
                throws ClassNotFoundException {
        Class<?> caller = Reflection.getCallerClass();
        return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
    }

那麼如果沒有使用執行緒上下文載入器,其實用的就是呼叫方 ServiceLoader 的載入器,而ServiceLoader在java.util下面,是無法載入com.mysql.jdbc包下的類。 我不認為這是對雙