ITエンジニアの成長ブログ

ITエンジニアとして行う勉強の発信&日々の生活で体験した楽しいことをゆるく発信

Tomcatで動作するWebアプリケーションでは、Class.forName()が必要なのか

最近JDBCのドライバを読み込むための、Class.forName()はJDK1.6で不要になったという記事を書きました。
Javaでデータベース接続時のおまじない「Class.forName()」はもう不要だった - ITエンジニアの成長ブログ

上記の話は、普通のmainメソッドで実行するようなJavaアプリケーションであれば問題なく実行できるのですが、実はTomcatで動作するWebアプリケーションで実行してみると以下のようなエラーが発生しました。

java.lang.IllegalStateException: java.sql.SQLException: No suitable driver found for jdbc:sqlserver://localhost;databaseName=db1;encrypt=false


今まで通り「Class.forName()」を記述すると、上記エラーは発生せずにデータベースコネクションを問題なく取得することができました。

ちなみに、JDBCドライバはWebアプリケーションのクラスパスである「WEB-INF/lib」ディレクトリに配置していました。

JDBCドライバの位置


上記のようなクラスパスが通っている場所であれば、いい感じに動作するのかなと思っていたのでちょっと残念です。

なぜエラーになるかの理由が知りたかったので、ぐぐりました。すると、以下のstack overflowの記事を見つけました。
java - Why do I need to call Class.forName in Tomcat Web Application even if I use a JDBC Driver 4.0? - Stack Overflow

上記記事のリンク先にTomcat公式サイトのリンクがありましたので紹介します。
Apache Tomcat 8 (8.0.53) - JNDI Datasource HOW-TO

私の理解が合っているかどうか自身がないのですが、Webアプリケーションの「WEB-INF/lib」に格納したJDBCドライバは、Class.forName()を明示的に呼び出す必要があるようです。これは、DriverManagerがサービスプロバイダ機構と呼ばれる仕組みでJDBCドライバを自動的に読み込む仕組みが各Webアプリケーションの「WEB-INF/lib」を探索しないから、と解釈しました。

おそらく、Webアプリケーションの仕組みではクロスローダーの階層とかが異なるのでこういうことになるのかなと想定しているのですが、そこは正直よくわかっておりません。。

また、補足として「${CATALINA_HOME}/lib」配下にJDBCドライバを格納するとClass.forName()メソッド呼び出しは不要でした。どうやら、「${CATALINA_HOME}/lib」の配下はJDBCドライバの探索対象となっているようです。これは、Tomcat8.5.58で動作確認しました。

因みに、私の環境ではTomcat8.0.47とTomcat8.5.58の2環境があるのですが、Tomcat8.0.47では「${CATALINA_HOME}/lib」の配下にJDBCドライバを配置してもエラーになりました。使いやすいように、より新しいバージョンで修正されたようですね。

とりあえず、この記事のタイトルの結論としては「Tomcatで動作するWebアプリケーションでは、基本的にはClass.forName()を明示的に呼び出す必要がある」ということが分かりました。

今回はこの辺で。最後までお読みいただきありがとうございました。