はじめに
この記事は、Javaの動作を実感することを目的として作成しています。そのため、
イマドキがどうとか、Mavenがどうとか、IDEがどうとか。そういった議論は一切省きます。これを知って初めて効率のよいやり方に移らないと、根本的な事が抜け落ちてしまうからです。
資料の目的
誰かがかいたライブラリを自分のプログラムに組込むまでの手順を整理し、クラスパスやパッケージ
importなどの概念を獲得することが目的です。そのために、「プログラム実行時に動作を目で見て確認できる」Log出力機能を選定します。選定ライブラリは今を時めくLog4j2です。僕のお友達が、仕事でよくLog4j2の名前を聞くということで。せっかくだからこいつを使おうと決めました。だからLog4j2の使い方とか、設定方法とかも全くの範囲外です
前提条件
- jdkが既に導入されていること(javac/javaコマンドが利用可能であること)
- インターネットに接続できること。
- Windows でやってね。(コマンドプロンプト) Linuxは適宜読み替えてください。
>javac --version
javac 17
JDKが入っていれば、バージョン情報が出力される。
準備作業
まずは、作業の準備から。いつものようにコマンドプロンプトで
> mkdir c:\dev\workspace\java\libconfirm
> cd c:\dev\workspace\java\libconfirm
> mkdir src\main\java\
> mkdir src\main\java\cococlub
> mkdir lib
> curl -o log4j.zip https://dlcdn.apache.org/logging/log4j/2.3.2/apache-log4j-2.3.2-bin.zip
> jar xvf log4j.zip
> copy apache-log4j-2.3.2-bin lib\
> del /s apache-log4j-2.3.2-bin
> rmdir apache-log4j-2.3.2-bin
> del log4j.zip
> set classpath=
コードを作成しよう
まずは。自分の作りたいコードを作るところからです。
>notepad src\main\java\cococlub\MyApp.java
(ダイアログで新しく作るか聴かれるので、作成ボタンを押下してください。)
入力するコードは以下の通りです。
public class MyApp{
}
さっそくコンパイルしてみましょう
> javac -d bin src\main\java\cococlub\MyApp.java
コンパイルが終了するので
> dir bin
c:\dev\workspace\java\libconfirm\bin のディレクトリ
2022/01/30 17:48 <DIR> .
2022/01/30 17:48 <DIR> ..
2022/01/30 17:48 184 MyApp.class
ここで -d の意味ですが、コンパイルした結果を -d で指定したディレクトリに保存してくださいという意味です。え?何がいいのかわからない。では指定していない場合の結果と見比べてみましょう
まず指定した場合
libconfirm>dir /S /A-D /B src bin
c:\dev\workspace\java\libconfirm\src\main\java\cococlub\MyApp.java
c:\dev\workspace\java\libconfirm\bin\MyApp.class
指定していない場合
libconfirm>dir /S /A-D /B src bin
c:\dev\workspace\java\libconfirm\src\main\java\cococlub\MyApp.class
c:\dev\workspace\java\libconfirm\src\main\java\cococlub\MyApp.java
★ソースコードと同じ場所に.classファイルができちゃいます。
これは、「前の結果を忘れて綺麗な状態でコンパイルしたい」って場合にとても都合が悪いのです。
指定していれば、binディレクトリそのものを消してしまえばいいのですが、指定しない場合。*.classファイルを探し出して消さなければならない。消し漏らすと、問題なケースなどもあるので、都合が悪いのです。以後
dオプションは必ず指定して実行するようにしましょう。
さて、こんな何にもないコードでも、いろいろ分かることがあります。こうやって、少しずついろいろ変えながら理解することは初学者にとってとても大切なステップです。ここをおろそかにするとあとから全くわからないことがでてきます。遠きに行くには必ず邇きよりす 武田先生。貴方すごくいい言葉を教えてくれていますよ!!!
パッケージを作ってみよう
実はJava は、パッケージに属さない(デフォルトパッケージ)クラスを推奨していません。まずは最初にパッケージをつけてみます。パッケージでは
- .class を格納するフォルダはパッケージ名と一致さえる必要がある
という鉄のおきてがあります。つまりパッケージを付けると、生成される.classは<package名のフォルダ> に格納されます。そのため、ソースコードも、パッケージ名と格納先をそろえておくのがいいでしょう。(エラーの内容から、どのソースが問題だかすぐに分かるようにしておきたいのです。)
cococlub.net ⇒ ...../cococlub/net にそのソースが置いておく。
という事です。今ソースは「cococlub」にあるので、cococlubというパッケージにします。出来上がるソースは
package cococlub;
public class MyApp{
}
です。では早速コンパイルしてみましょう
>javac -d bin src\main\java\cococlub\MyApp.java
>dir /S /B /A-D bin
c:\dev\workspace\java\libconfirm\bin\cococlub\MyApp.class
cococlub配下にMyApp.classが生成されています。
Log4j2を使ってみよう
プログラムを書こう!!
まずは、本格的に使う前に、Log4jにおいて、ログを記録する役割をになうorg.apache.logging.log4j.Logger型の変数を宣言しましょう。
package cococlub;
public class MyApp{
public static void main(String[] args){
org.apache.logging.log4j.Logger logger;
//★↑ちょっと長くて面倒くさいので、別の方法を後述します。
}
}
さてと、コンパイルコンパイル
set classpath=
javac -encoding UTF-8 -d bin src\main\java\cococlub\MyApp.java
★以下実行結果
src\main\java\cococlub\MyApp.java:5: エラー: パッケージorg.apache.logging.log4jは存在しません
org.apache.logging.log4j.Logger logger;
^
エラー1個
-encoding UTF-8
サンプルプログラムに日本語のコメントをいれたので、ファイルの文字コードがUTF-8であることをjavacコマンドにおしえている。これを教えないと、javacはシステムのデフォルト文字コードwindows-31j であることを仮定して動作するため、実際の文字コードと異なりエラーとなる。
えらー・・・・ですと・・・だってさっきlibに解凍したのに・・・・・と思いますよね?でも。そのlibフォルダにlog4jが置いてあるなんて、コンパイラはいつ知るのでしょうか?知りえないんですよね。。
知らぬなら、教えてあげようjarの位置
javac -encoding UTF-8 -d bin --class-path=lib\log4j-api-2.3.2.jar src\main\java\cococlub\MyApp.java
今度はうまくいきました。class-pathはjavac/javaに「ソースにないClass=外部ライブラリの位置はここだよ~」と教えげ上げるための仕掛けです。今回は「org.apache.logging.log4j.Logger」が入っている「lib\log4j-api-2.3.2.jar」を追加しています。さてもう少しコードを追加しましょう。
package cococlub;
public class MyApp{
public static void main(String[] args){
org.apache.logging.log4j.Logger logger = org.apache.logging.log4j.LogManager.getLogger(MyApp.class);
logger.error("An Error is Occured!");
}
}
さぁコンパイルコンパイル♪さっき、class-pathも追加したし、今回は・・・・
> javac -encoding UTF-8 -d bin --class-path=lib\log4j-api-2.3.2.jar src\main\java\cococlub\MyApp.java
お!!!とおりましたよ!!! やった!!やったよ!!
動かしてみよう
では動かしてみましょう。
>java cococlub.MyApp
エラー: メイン・クラスcococlub.MyAppが見つからなかったかロードできませんでした
ええええそりゃないよおとっつぁん。さっきコンパイルしたじゃん!!だってほら
> dir /A-D /B /S bin
c:\dev\workspace\java\libconfirm\bin\cococlub\MyApp.class
いるじゃん・・・・はっ!!!このクラスがbin に置いてあるなんて、愛しのJavaチャンにだれが伝えたのか。。。つたえてねー------。そりゃ呼べないよ。。java(javacであれjavaであれ)に「クラスの在処」を教えるためには、classpathを使えばよかったのです。では積んでみましょう
set classpath=bin
java -cp %classpath% cococlub.MyApp
★以下実行結果
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/logging/log4j/LogManager
at cococlub.MyApp.main(MyApp.java:5)
Caused by: java.lang.ClassNotFoundException: org.apache.logging.log4j.LogManager
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
-cp がjava(javacではなく) にクラスパスを与える方法です。覚えておこうね。どうやらMyAppは呼べたようですが。。なんと、log4jが・・・log4j忘れてたごめんよ。log4j。。。だって教えてないんだもん。「og4j-api-2.3.2.jar」を使えばいいんだよね。もう慣れてるから簡単さ。
set classpath=
set classpath=bin
set classpath=%classpath%;lib\log4j-api-2.3.2.jar
java -cp %classpath% cococlub.MyApp
★以下実行結果
ERROR StatusLogger Log4j2 could not find a logging implementation. Please add log4j-core to the classpath. Using SimpleLogger to log to the console...
ERROR MyApp An Error is Occured!←ただメッセージが出ただけで、ログのフォーマットとしてだめだめ。
あ、怒られた。log4j-coreをclasspathにいれろだって・・・なんでだ・・・コンパイル時にはいらなかったじゃないか!!
これ、javaあるあるなので気をつけましょう。
- コンパイル時に必要なライブラリ(jar)=プログラムが直接呼ぶライブラリ(jar)
- 実行時に必要なライブラリ(jar)=プログラムが直接呼ぶライブラリ(jar)が呼ぶライブラリ(jar)を全部
なので、呼んだ先で必要なライブラリを要求されているのです。場合によっては先の先の先でとかすごい数のjarが必要いなったりもします。今回はlog4jの自己申告通りlog4-coreを追加してあげましょう
set classpath=
set classpath=bin
set classpath=%classpath%;lib\log4j-api-2.3.2.jar;lib\log4j-core-2.3.2.jar
java -cp %classpath% cococlub.MyApp
ERROR StatusLogger No log4j2 configuration file found. Using default configuration: logging only errors to the console.
★log4j2の設定ファイルがないから、デフォルトの動作として Errorレベルだけ標準出力に出力するよ。設定ファイル面倒なので今回は割愛。見逃し。
22:33:18.852 [main] ERROR cococlub.MyApp - An Error is Occured!←お、どのコードからいつ出されたのか一目瞭然。
見事エラーレベルのログが出力されました。参考まで、infoも追加してみましょう。
package cococlub;
public class MyApp{
public static void main(String[] args){
org.apache.logging.log4j.Logger logger = org.apache.logging.log4j.LogManager.getLogger(MyApp.class);
logger.error("An Error is Occured!");
logger.info("It is an information");
}
}
早速、コンパイル。
javac -encoding UTF-8 -d bin --class-path=lib\log4j-api-2.3.2.jar src\main\java\cococlub\MyApp.java
さて実行
set classpath=
set classpath=bin
set classpath=%classpath%;lib\log4j-api-2.3.2.jar;lib\log4j-core-2.3.2.jar
java -cp %classpath% cococlub.MyApp
ERROR StatusLogger No log4j2 configuration file found. Using default configuration: logging only errors to the console.
22:41:14.331 [main] ERROR cococlub.MyApp - An Error is Occured!
きちんとErrorレベルのログだけが出されていますね。すばらしい。
import を使おうよ
さて、今まで作ってたこのコード。ちょっときついですよね。
package cococlub;
public class MyApp{
public static void main(String[] args){
org.apache.logging.log4j.Logger logger = org.apache.logging.log4j.LogManager.getLogger(MyApp.class);
logger.error("An Error is Occured!");
logger.info("It is an information");
}
}
なにがって、org.apache.logging.log4j.Loggerとorg.apache.logging.log4j.LoggerをFQDN で指定するのはつらいです。1か所ならまだしも、複数個所でてきたら目もあてられません。こういう「なんども書くだろう長い表記」というのは普通に省略する方法があります。javaではimport文です。importしたクラスは、クラス名だけで呼び出しできるというありがたー----い仕様がjavaにはあるのです。使わない手はありません。
package cococlub;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
public class MyApp{
public static void main(String[] args){
Logger logger = LogManager.getLogger(MyApp.class);
logger.error("An Error is Occured!");
logger.info("It is an information");
}
}
あ、かなりすっきり。これのコンパイルと実行は今までと代わり映えしないので割愛します。
まとめ
- jarをもってきて利用する方法が分かったよ
- packageの使い方もわかったよ
- 実行時に依存するすべてのライブラリを指定するのは結構面倒だよ。
- ライブラリ(Jar) を各サイトから集まるのも結構手間だと思うよ