Visual Studio CodeによるSpring5 MVC Webアプリ開発　HelloWorld作成編

#はじめに
[こちら](https://qiita.com/t_skri/items/aafb1b1c4942ae644f58)で作成したひな型にHelloWorldを作成していきます。

#環境
OS:Windows 10 Pro 64bit
Editor:Visual Studio Code 1.42.1
JDK:AdoptOpenJDK 11.0.6+10 x64
Apache Maven:v3.6.3
Apache Tomcat:v9.0.31

#pom.xml
pom.xmlにSpring MVCに必要なRepositoryを追加します。

Repositoryの検索はMVN REPOSITORYを使用しています。
[https://mvnrepository.com/](https://mvnrepository.com/)

propertiesに追加

```XML
<spring.version>5.2.4.RELEASE</spring.version>
<!-- web.xmlが無い場合でもビルドを実行する -->
<failOnMissingWebXml>false</failOnMissingWebXml>
```

dependenciesに追加

```XML
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-webmvc</artifactId>
	<version>${spring.version}</version>
	<scope>compile</scope>
</dependency>

<dependency>
    	<groupId>org.springframework</groupId>
    	<artifactId>spring-context-support</artifactId>
    	<version>${spring.version}</version>
    	<scope>compile</scope>
</dependency>

<dependency>
	<groupId>org.thymeleaf</groupId>
	<artifactId>thymeleaf-spring5</artifactId>
	<version>3.0.11.RELEASE</version>
	<scope>compile</scope>
</dependency>

<dependency>
  	<groupId>org.thymeleaf.extras</groupId>
    	<artifactId>thymeleaf-extras-springsecurity5</artifactId>
    	<version>3.0.4.RELEASE</version>
    	<scope>compile</scope>
</dependency>

<dependency>
	<groupId>javax.servlet</groupId>
	<artifactId>javax.servlet-api</artifactId>
	<version>4.0.1</version>
	<scope>provided</scope>
</dependency>

<dependency>
	<groupId>org.projectlombok</groupId>
	<artifactId>lombok</artifactId>
	<version>1.18.10</version>
	<scope>provided</scope>
</dependency>
```
pom.xml全体です。

```xml:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.example</groupId>
	<artifactId>hello</artifactId>
	<version>1.0-SNAPSHOT</version>
	<packaging>war</packaging>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<java.version>11</java.version>
		<spring.version>5.2.4.RELEASE</spring.version>
        <!-- web.xmlが無い場合でもビルドを実行する -->
        <failOnMissingWebXml>false</failOnMissingWebXml>
	</properties>

	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.1</version>
				<configuration>
					<source>${java.version}</source>
					<target>${java.version}</target>
				</configuration>
			</plugin>
		</plugins>
	</build>
	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.11</version>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${spring.version}</version>
			<scope>compile</scope>
		</dependency>

		<dependency>
    		<groupId>org.springframework</groupId>
    		<artifactId>spring-context-support</artifactId>
    		<version>${spring.version}</version>
    		<scope>compile</scope>
		</dependency>

		<dependency>
			<groupId>org.thymeleaf</groupId>
			<artifactId>thymeleaf-spring5</artifactId>
			<version>3.0.11.RELEASE</version>
			<scope>compile</scope>
		</dependency>

		<dependency>
			<groupId>org.thymeleaf.extras</groupId>
			<artifactId>thymeleaf-extras-java8time</artifactId>
			<version>3.0.4.RELEASE</version>
			<scope>compile</scope>
		</dependency>

		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>4.0.1</version>
			<scope>provided</scope>
		</dependency>

		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<version>1.18.10</version>
			<scope>provided</scope>
		</dependency>
	</dependencies>
</project>

```


#MvcConfig.java
「D:\JAVA\Project\hello\src\main\java\com\example\web\config」に作成します。
フォルダが無い場合はフォルダを作成して下さい。

```Java:MvcConfig.java
package com.example.web.config;

import java.nio.charset.StandardCharsets;

import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.resource.VersionResourceResolver;
import org.thymeleaf.extras.java8time.dialect.Java8TimeDialect;
import org.thymeleaf.spring5.SpringTemplateEngine;
import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver;
import org.thymeleaf.spring5.view.ThymeleafViewResolver;
import org.thymeleaf.templatemode.TemplateMode;

@EnableWebMvc // Spring MVCを有効化する
@Configuration
@ComponentScan(basePackages = {"com.example.web.controller"})
public class MvcConfig implements WebMvcConfigurer {
	@Bean
    public SpringResourceTemplateResolver templateResolver() {
        SpringResourceTemplateResolver templateResolver =
                new SpringResourceTemplateResolver();
        // ビューを保存するフォルダ名を指定する
        templateResolver.setPrefix("/WEB-INF/templates/");
        // ビューの拡張子を指定する
        templateResolver.setSuffix(".html");
        // テンプレートモードをHTMLに指定する
        templateResolver.setTemplateMode(TemplateMode.HTML);
        // テンプレートを読み込む際の文字コードを指定する
        templateResolver.setCharacterEncoding(StandardCharsets.UTF_8.name());

        templateResolver.setCacheable(false);
        return templateResolver;
    }

    @Bean
    public SpringTemplateEngine templateEngine(SpringResourceTemplateResolver templateResolver) {
        SpringTemplateEngine templateEngine = new SpringTemplateEngine();
        // SpringResourceTemplateResolverをセットする
        templateEngine.setTemplateResolver(templateResolver());
        // SpELのコンパイラを有効化してパフォーマンスを向上させる
        templateEngine.setEnableSpringELCompiler(true);
        // Date and Time APiを利用するためのDialectを追加
        templateEngine.addDialect(new Java8TimeDialect());
        return templateEngine;
    }

    @Bean
    public ThymeleafViewResolver viewResolver(SpringTemplateEngine templateEngine) {
        ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
        viewResolver.setTemplateEngine(templateEngine);
        // ビューを書き出す際の文字コードを指定する
        viewResolver.setCharacterEncoding(StandardCharsets.UTF_8.name());
        return viewResolver;
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resources/**")
                .addResourceLocations("/resources/");
        registry.addResourceHandler("/webjars/**")
        		.addResourceLocations("classpath:/META-INF/resources/webjars/")
        		.resourceChain(false)
        		.addResolver(new VersionResourceResolver()
        				.addContentVersionStrategy("/**"));
    }

    //メッセージソースの設定
    //WEBページでプロパティファイルを使用できる
    //日本語メッセージ：messages_ja.properties
    @Bean
    public MessageSource messageSource() {
        ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
        messageSource.setBasename("classpath:i18n/messages");

        // trueをセットすれば、メッセージのキーがない場合にキーを表示
        // false の場合、NoSuchMessageExceptionを投げる
        messageSource.setUseCodeAsDefaultMessage(true);
        messageSource.setDefaultEncoding("UTF-8");
        // # -1 : リロードしない、0 : 常にリロードする
        messageSource.setCacheSeconds(0);
        return messageSource;
    }
}
```

#WebAppInitializer.java
「D:\JAVA\Project\hello\src\main\java\com\example\web\config」に作成します。
フォルダが無い場合はフォルダを作成して下さい。


```Java:WebAppInitializer.java
package com.example.web.config;

import java.nio.charset.StandardCharsets;

import javax.servlet.Filter;

import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
	/**
     * ビジネスロジックなど、Spring MVC以外に関するJava Configクラスを指定します。
     */
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[] {};
    }

    /**
     * Spring MVCに関するJava Configクラスを指定します。
     */
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[] {MvcConfig.class};
    }

    /**
     * DispatcherServletに対するURLパターンを指定します。
     * "/"を指定することで、全リクエストをDispatcherServletが受け取ります。
     */
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }

    /**
     * サーブレットフィルターを指定します。
     * 複数のフィルターがあった場合、配列に指定した順番に実行されます。
     */
    @Override
    protected Filter[] getServletFilters() {
        return new Filter[]{
                new CharacterEncodingFilter(StandardCharsets.UTF_8.name(), true)};
    }
}
```

#Controllerの作成
「D:\JAVA\Project\hello\src\main\java\com\example\web\controller」に作成します。
フォルダが無い場合はフォルダを作成して下さい。

```Java:RootController.java
package com.example.web.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class RootController {

    @GetMapping("/")
    public String root() {
        // "redirect:"を先頭につけるとリダイレクトになる
        return "redirect:hello/index";
    }

}
```

```Java:HelloController.java
package com.example.web.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/hello")
public class HelloController {

    @GetMapping("/index")
    public String indexGet() {
        return "hello/index";
    }
}
```

#Viewの作成
「D:\JAVA\Project\hello\src\main\webapp\WEB-INF\templates\hello」に作成します。
フォルダが無い場合はフォルダを作成して下さい。

```HTML:index.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Hello World</title>
    </head>
    
    <body>
        <h1>Hello World</h1>
    </body>
</html>
```

#コンパイル
次のコマンドでコンパイル及びwarファイルの作成が出来ます。

```
mvn package
```

```
D:\JAVA\Project\hello>mvn package
[INFO] Scanning for projects...
[INFO]
[INFO] -------------------------< com.example:hello >--------------------------
[INFO] Building hello 1.0-SNAPSHOT
[INFO] --------------------------------[ war ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ hello ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory D:\JAVA\Project\hello\src\main\resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ hello ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 4 source files to D:\JAVA\Project\hello\target\classes
[INFO] 
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ hello ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory D:\JAVA\Project\hello\src\test\resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ hello ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ hello ---
[INFO] 
[INFO] --- maven-war-plugin:2.2:war (default-war) @ hello ---
[INFO] Packaging webapp
[INFO] Assembling webapp [hello] in [D:\JAVA\Project\hello\target\hello-1.0-SNAPSHOT]
[INFO] Processing war project
[INFO] Copying webapp resources [D:\JAVA\Project\hello\src\main\webapp]
[INFO] Webapp assembled in [164 msecs]
[INFO] Building war: D:\JAVA\Project\hello\target\hello-1.0-SNAPSHOT.war
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  3.883 s
[INFO] Finished at: 2020-03-08T20:35:04+09:00
[INFO] ------------------------------------------------------------------------
```

#動作確認
コマンドパレット

```
Tomcat: Add Tomcat Server
```
Tomcatフォルダ選択ダイアログが表示されますので、
先程Tomcatを解凍したフォルダ(D:\JAVA\Tomcat\apache-tomcat-9.0.31)を選択します。

コマンドパレット

```
Tomcat: Run on Tomcat Server
```
warファイル選択ダイアログが表示されますので、「mvn package」で作成されたwarファイル(D:\JAVA\Project\SpringSample01\target\hello-1.0-SNAPSHOT.war)を選択します。

「[http://localhost:8080/](http://localhost:8080/)」にアクセスして下さい。
設定によっては、自動でブラウザが立ち上がります。
![tomcat2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/591423/347b961c-90ac-a578-2a82-1e5d0c8952e8.jpeg)

hello-1.0-SNAPSHOTをクリックして下さい。

「Hello World」が表示されます。
![tomcat3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/591423/8d870923-624e-248a-cb17-e693bf7bcfdd.jpeg)


#今回のサンプルソース
GitHubにアップしました。
[https://github.com/t-skri1/SpringSample01](https://github.com/t-skri1/SpringSample01)

#参考/出典
VScodeでMVN, Tomcat, JSP/ServletでWebアプリ開発メモ
[https://qiita.com/harhogefoo/items/2fa52ecee90b7a25e4c9](https://qiita.com/harhogefoo/items/2fa52ecee90b7a25e4c9)

#まとめ
今回のサンプルはMVCのうち、ViewとControllerだけになっています。
Modelを追加するには別途設定が必要です。
設定方法はSQL Serverへの接続サンプルで投稿します。