Dev Study
← 解説「既存プロジェクトの読み方」に戻る

サンプルコードで身につける: 既存プロジェクトの読み方

解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。

12つのプロジェクトの tree を並べる

左は で を作って に置く構成、右は の雛形です。左は に を手で置き、src の直下からが始まります。右は がプロジェクトの中に無く、 の下にパッケージがあります。

tree の並置(右は手で2列に組んだ要約)コマンド出力
shop/ (Ant + WAR + 外部 Tomcat) boot-pristine/ (Maven + Spring Boot)
├── build.xml ├── pom.xml
├── lib/ ← JAR 11本を手で置く ├── mvnw, mvnw.cmd, .mvn/wrapper/
├── lib-compile/ ← servlet-api ├── .gitignore, .gitattributes, HELP.md
├── WebContent/ └── src/
│ ├── index.html ├── main/
│ └── WEB-INF/ │ ├── java/com/example/demo/
│ ├── web.xml │ │ └── DemoApplication.java
│ ├── applicationContext.xml │ └── resources/
│ └── dispatcher-servlet.xml │ ├── application.properties
└── src/ │ ├── static/
└── jp/co/example/shop/ │ └── templates/
├── service/GreetingService.java └── test/java/com/example/demo/
└── web/HelloController.java └── DemoApplicationTests.java
(11 directories, 19 files) (16 directories, 10 files)

2見分けの手がかりの一覧

ルートのビルド定義から順に、2つの構成で何が違うかを並べた表です。最初に見るのは1行目のビルド定義と、の有無です。この表は両端の2例で、 で WAR を作る形などの中間もあります。

Ant + WAR と Spring Boot の見分け表
見るもの Ant + WAR (shop/) Spring Boot (雛形)
ビルド定義 build.xml pom.xml、または build.gradle + settings.gradle
ラッパー 無し (Ant を別途入れる) mvnw / .mvn/wrapper/、または gradlew / gradle/wrapper/
依存 JAR リポジトリ内の lib/*.jar (11本を手で置く) プロジェクト内に無い (pom.xml に座標だけ)
ソースの位置 src/jp/co/... (build.xml の srcdir で決める) src/main/java/com/... (Maven の規約)
設定ファイル WebContent/WEB-INF/web.xml ほか XML 2つ src/main/resources/application.properties
静的ファイル WebContent/ 直下 src/main/resources/static/
main メソッド 無い DemoApplication.java (@SpringBootApplication)
ビルドの出力 build/classes/、dist/shop.war target/ (Maven)、build/ (Gradle)
動かし方 WAR を Tomcat の webapps/ に置く java -jar

3WAR の入口は web.xml

WAR の構成では、Tomcat がこの を読んで を起こします。listener の行と servlet の行が、それぞれ Spring の部品の入れ物を1つずつ作ります。 の /app/* が URL の途中に入るので、このアプリの URL は /shop/app/hello になりました。

WebContent/WEB-INF/web.xml(全文)設定ファイル
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_1.xsd"
version="6.1">
 
<!-- root context: service layer -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
 
<!-- servlet context: web layer -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/dispatcher-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/app/*</url-pattern>
</servlet-mapping>
</web-app>

4起動の向きをログで比べる

上は WAR を Tomcat に置いたときのログで、Tomcat が WAR を見つけてから Spring のが作られています。下は(雛形に HelloController を1つ足したもの)のログで、自分の DemoApplication が先に始まり、その中で Tomcat が起動しています。誰が誰を起こすかが逆です。時間の値は極小のアプリでの1回の値で、両者の比較には使えません。

Tomcat 11 に WAR を置いたときと、java -jar のとき(抜粋)追跡ログ
# WAR を Tomcat の webapps/ に置いたとき(Tomcat のログ。時刻の日付部分を省略)
22:28:03.388 INFO [main] org.apache.catalina.startup.HostConfig.deployWAR Deploying web application archive [/usr/local/tomcat/webapps/shop.war]
22:28:03.729 INFO [main] org.springframework.web.context.ContextLoader.initWebApplicationContext Root WebApplicationContext: initialization started
22:28:03.867 INFO [main] org.springframework.web.context.ContextLoader.initWebApplicationContext Root WebApplicationContext initialized in 138 ms
22:28:03.881 INFO [main] org.springframework.web.servlet.DispatcherServlet.initServletBean Initializing Servlet 'dispatcher'
22:28:04.004 INFO [main] org.springframework.web.servlet.DispatcherServlet.initServletBean Completed initialization in 123 ms
22:28:04.007 INFO [main] org.apache.catalina.startup.HostConfig.deployWAR Deployment of web application archive [/usr/local/tomcat/webapps/shop.war] has finished in [619] ms
22:28:04.012 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in [644] milliseconds
 
# Spring Boot の実行可能 JAR(右端を省略)
$ java -jar target/demo-0.0.1-SNAPSHOT.jar
... INFO 1 --- [demo] [main] com.example.demo.DemoApplication : Starting DemoApplication v0.0.1-SNAPSHOT using Java 25.0.4.1 with PID 1
... INFO 1 --- [demo] [main] o.s.boot.tomcat.TomcatWebServer : Tomcat initialized with port 8080 (http)
... INFO 1 --- [demo] [main] o.s.boot.tomcat.TomcatWebServer : Tomcat started on port 8080 (http) with context path '/'
... INFO 1 --- [demo] [main] com.example.demo.DemoApplication : Started DemoApplication in 0.657 seconds (process running for 0.891)

5成果物の中身を比べる

WAR は自分のを に、依存の JAR を に入れます。 の実行可能 JAR は と に入れ、も持ちます。どちらも、パッケージ名どおりの階層がそのまま中に入っています。

shop.war と demo-0.0.1-SNAPSHOT.jar の中身(抜粋。JAR は雛形に HelloController を1つ足して作ったもの)コマンド出力
$ ls -l dist/shop.war && jar tf dist/shop.war
-rw-r--r-- 1 root root 7825305 Oct 1 22:28 dist/shop.war
META-INF/
META-INF/MANIFEST.MF
WEB-INF/
WEB-INF/web.xml
WEB-INF/applicationContext.xml
WEB-INF/dispatcher-servlet.xml
index.html
WEB-INF/classes/
WEB-INF/classes/jp/
WEB-INF/classes/jp/co/
WEB-INF/classes/jp/co/example/
WEB-INF/classes/jp/co/example/shop/
WEB-INF/classes/jp/co/example/shop/service/
WEB-INF/classes/jp/co/example/shop/web/
WEB-INF/classes/jp/co/example/shop/service/GreetingService.class
WEB-INF/classes/jp/co/example/shop/web/HelloController.class
WEB-INF/lib/
WEB-INF/lib/commons-logging-1.3.5.jar
...(WEB-INF/lib/ に JAR が計11本)
 
$ jar tf target/demo-0.0.1-SNAPSHOT.jar (BOOT-INF/lib/ の中身とローダのクラスを除く。抜粋)
META-INF/MANIFEST.MF
org/springframework/boot/loader/
BOOT-INF/classes/com/example/demo/DemoApplication.class
BOOT-INF/classes/com/example/demo/HelloController.class
BOOT-INF/classes/application.properties
BOOT-INF/lib/
...(BOOT-INF/lib/ に JAR が計34本)

図やコードに出てくる用語(タップで説明)