My開発メモ

Class.forName(“org.h2.Driver”)は何をやっているのか

Class.forName(“org.h2.Driver”)が H2データベースドライバを
JVMに読み込んでいることはネットや本で解説されているが、
具体的に何をしているかまではわからない。

そこでいろいろ調べてみた。

クラスをファイルシステムからロードする

(1) ブートストラップ・クラスローダ
(2) プラットフォーム・クラスローダ
(3) システム・クラスローダ(アプリケーション・クラスローダ)

(1) JavaSEの主要なクラスをロードする。たとえば、java.lang.String
(2) JavaSEのクラスをロードする。例、java.net.http.HttpClient
(3) 開発者が作成したクラスをロードする。

クラスローダを調べる

以下のようにすれば、クラスローダを調べることができる。

public class ClassLoaderTest {
  public static void main(String[] args) {
    Hero h = new Hero();
    Class<?> clazz = Hero.class;
    System.out.println(clazz.getClassLoader());
  }
}
// jdk.internal.loader.ClassLoaders$AppClassLoader@42110406

クラスがロードされるタイミング

(1) スタティックなメンバーへのアクセス
(2) Classクラスの forName()メソッド呼び出し
(3) new演算子によるインスタンス生成
(4) Constructorクラスの newInstance()メソッド呼び出し

Class.forName()で何がおこなわれているか — static初期化ブロック

Class.forName()によって、クラスがメモリにロードされることがわかったが、
ロードされたタイミングで処理を仕込むことができる。

ロードされるクラスに static{}ブロックを作成し、そこに処理を
書いておくと、ロード時にそれが実行される。
以下のようにして試してみる。

srcに demoパッケージを作成し、Pqrクラスを作成する。

package demo;

public class Pqr {
  static {
    System.out.println("in static!");
  }
  {
    System.out.println("in instance!");
  }
}

static{} — static初期化ブロックは、クラスがクラスローダによってロードされた時点で動作する。
{} — インスタンス初期化ブロック は、インスタンスが生成される直前で動作する。

試してみる。

package demo;

public class DemoClass {
	public static void main(String[] args) {
		Pqr obj = new Pqr();
	}
}
// 実行例
// in static!
// in instance!

static初期化ブロック、インスタンス初期化ブロックともに実行されている。

package demo;

public class DemoClass {
	public static void main(String[] args) throws Exception {
		// Pqr obj = new Pqr();
		Class.forName("demo.Pqr");
	}
}
// 実行例
// in static!

static初期化ブロックのみが実行されている。

org.h2.Driverクラスはどうなっているか

Driver.java
package org.h2;

public class Driver implements java.sql.Driver, JdbcDriverBackwardsCompat {

  private static final Driver INSTANCE = new Driver();
  private static final String DEFAULT_URL = "jdbc:default:connection";
  private static final ThreadLocal<Connection> DEFAULT_CONNECTION =
        new ThreadLocal<>();

  private static boolean registered;

  static {
    load();
  }
...
}

static初期化ブロックでは、load()メソッドがあるだけである。
その load()メソッドは以下である。

Driver.java (一部)
...
    public static synchronized Driver load() {
        try {
            if (!registered) {
                registered = true;
                DriverManager.registerDriver(INSTANCE);
            }
        } catch (SQLException e) {
            DbException.traceThrowable(e);
        }
        return INSTANCE;
    }
...

DriverManagetクラスの registerDriver()メソッドを呼び出し、
引数に自身のインスタンスを渡して、自分自身のインスタンスを
登録している。

であるから、以下のコードでも DriverManagerクラスに H2ドライバーを
登録することができる。

DAOクラス
try {
  DriverManager.registerDriver(new org.h2.Driver());
  System.out.println("DBドライバーを登録しました。");
} catch (SQLException e) {
  throw new IllegalStateException("ドライバーを登録できません!");
}

この場合は、コンパイル時に org.h2.Driverクラスが必要である。

しかし、以下のようにすると、実行時に org.h2.Driverクラスがあればよい。

try {
    Class.forName("org.h2.Driver");
} catch (ClassNotFoundException e) {
    throw new IllegalStateException("ドライバファイルが見つかりません");
}

参考

8.1 クラスローダーの仕組みとClassクラス(クラスのライフサイクル、ClassLoaderなど)~Java Advanced編

カテゴリー: Java, memo

タグ: Class.forName, DriverManager, H2ドライバー, org.h2.Driver, sql, sql.Driver

カウント: 189