hayashier Tech Blogs
  • hayashier Tech Blogs
  • Author's Books
    • 実践Redis入門 (日本語版)
    • 実践Redis入門 (한국어판)
  • Top Contents
    • Dive Deep Redis
    • Dive Deep Memcached
    • Kubernetes 入門
    • TCP 入門
    • TLS 入門
    • GPG 入門
    • サービス障害が発生した場合の対応方法
    • よく使うトラブルシューティング コマンド実行例 まとめ
    • コピペで使えるELBのアクセスログ解析による事象分析 (ShellScript, Athena)
  • Containers
    • Docker 入門
    • Nomad 導入
    • Dockerを利用してさっと検証環境構築
  • Kubernetes
    • Kubernetes 入門
    • Kubernetes 導入 with Amazon Linux 2
    • EKSを利用してKubernetesでSpring MVCをデプロイ (NLB + Auto Scaling)
  • Load Balancer
    • ALB 認証 導入
    • TLS extensions support with ALB
    • ELB(CLB,ALB,NLB)の種類ごとのHTTPレスポンスの違い
    • ELB(CLB) で WebSocket 通信
  • RDBMS
    • PostgreSQL DBA 入門
    • RDBMS Benchmark Get Started
    • RDBMS サンプルデータ生成 Get Started
    • RDS PostgreSQL Extensions Get Started
    • RDBMS Engine Inspection for Troubleshooting
  • Redis
    • Dive Deep Redis ~ 入門から実装の確認まで
    • Dive Deep Redis Internals ~ GETコマンド実行時の動作 ~
    • RedisのString型は今でも本当に512MBが上限か?
    • Redis 公式ドキュメント まとめ
    • Redis / Memcached Source Code Reading - Overview -
  • Memcached
    • Dive Deep Memcached ~ 入門から実装の確認まで ~
    • Dive Deep Memcached ~ SETコマンド実行時の動作 ~
    • Memcached 公式ドキュメント まとめ
    • memtier_benchmark + memcached-tool の導入
    • Redis / Memcached Source Code Reading - Overview -
  • Hadoop
    • Hadoop Get Started
  • Networking
    • TCP 入門
    • TLS 入門
    • ksnctf: HTTPS is secure, Writeup (TLS 通信解読)
    • オンプレ側ルーター(Cisco 1812J, Juniper SRX210, YAMAHA RTX 1210)から Direct Connect へ BGP 設定
  • Software
    • アルゴリズムとデータ構造 入門
    • デザインパターン 入門
    • ソフトウェアテスト 入門
  • System Admin
    • Shell Script 入門
    • サービス障害が発生した場合の対応方法
    • よく使うトラブルシューティング コマンド実行例 まとめ
    • コピペで使えるELBのアクセスログ解析による事象分析 (ShellScript, Athena)
    • GPG 入門
    • Operation Misc
  • Development
    • ローカル環境のプログラミング言語のバージョンを切り替え macOS
    • /usr/local/Cellar/pyenv/1.2.21/libexec/pyenv: No such file or directoryのエラーの対処方法
  • AWS
    • AWS Misc
    • AWS CLI, AWS SDKのリトライ処理の実装について
    • AWS CLI バージョンアップでエラー発生を解消
    • Elastic Beanstalkで稼働しているアプリケーション(Ruby, Sinatra)をAmazon Linux AMIからAmazon Linux2へ移行
    • Elastic Beanstalkでインスタンス入れ替え後にnginxのデフォルトの画面が表示されてしまう問題の対応
    • Amazon Lightsail に SSL 証明書設置 with Let's Encrypt (自動更新)
    • Amazon Lightsailで10分で作るお手軽Markdownで書く独自ドメインのブログサイト構築
    • Lambdaをローカルでテスト(with Docker)
    • ECS + ALB でダウンタイムなしでデプロイ
    • `Repository packages-microsoft-com-prod is listed more than once in the configuration`のメッセージの解消方法
  • Others
    • Pandoc 導入
    • textlint + prh による文章校正
    • 紙書籍をPDFに変換
    • Sphinx 導入
    • さくっとPocketのブックマークをはてなブックマークに移行
    • Macが突然起動しなくなった話
    • Macでターミナルが開かない (zsh編)
    • ホスト型 IDS Tripwire とネットワーク型 IDS Snort の導入 with CentOS 6
    • JMeter 導入
    • Squid 導入 with Amazon Linux AMI
    • Spring MVCを導入 (+ MySQL, Redis)
    • 外資系企業で働いている場合の確定申告方法 (RSU考慮)
Powered by GitBook
On this page
  • Spring MVCでHello,Worldを表示 (Mavenでパッケージ管理)
  • Spring MVCにMySQL導入
  • 設定ファイルを外部ファイル化
  • Spring MVCにRedisの導入
  • Tomcat上にSpring MVCのプロジェクトをデプロイ
  • Error
  1. Others

Spring MVCを導入 (+ MySQL, Redis)

PreviousSquid 導入 with Amazon Linux AMINext外資系企業で働いている場合の確定申告方法 (RSU考慮)

Last updated 1 month ago

Spring MVCが動作する仕組みについて、設定等が理解できるように、最初にイメージできるように整理しておきます。大きく以下の流れとなります。

  1. Front ControllerとなるDispatcherServletでリクエストを受付

  2. XMLファイルからハンドラーマッピングのエントリーを取得し、Controllerへリクエストを転送

  3. ControllerはModelAndViewのオブジェクトを返す

  4. DispatcherServletはXMLファイル中のViewResolverのエントリーを確認し、指定されたビューのコンポーネントを呼び出す

Spring MVCでHello,Worldを表示 (Mavenでパッケージ管理)

実際にプロジェクトを作成していきます。以下の手順でMaven管理による Spring MVCのプロジェクトを作成します。

  1. File > New > Dynamic Web Project を選択

  2. 遷移先の画面で Project Name: HayashierWebsite のようにプロジェクト名を入力

  3. Target runtimeで[New Runtime...]を選択

  4. Apache Tomcat v9.0を選択し、[Create a new local server]を選択

  5. 以下のように入力。Tomcatのディレクトリは上記ファイルをダウンロードしてきて展開したものの場所。JREは使用しているJavaのバージョンを確認して選択。 

    • Name: Apache Tomcat v9.0

    • Tomcat installation directory: /Users/hayashier/Downloads/apache-tomcat-9.0.34

    • JRE: Java SE 11.0.5 [11.0.5]

    $ java --version
    java 11.0.5 2019-10-15 LTS
    Java(TM) SE Runtime Environment 18.9 (build 11.0.5+10-LTS)
    Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.5+10-LTS, mixed mode)
  6. プロジェクト名を右クリックして、Configure > Convert to Maven Project と選択

  7. 遷移先の画面で以下を入力して、Finishを選択

    • Group Id: hayashier.website

    • Artifact Id: hayashier-website

  8. 生成されたpom.xmlを編集して以下のようなXMLファイルにします。

    <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>hayashier.website</groupId>
    <artifactId>hayashier-website</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>
    <properties>
        <org.springframework.version>5.2.5.RELEASE</org.springframework.version>
        <org.springframework.data.version>2.2.7.RELEASE</org.springframework.data.version>
        <javax.servlet.version>1.2</javax.servlet.version>
        <org.apache.tomcat.embed.version>9.0.34</org.apache.tomcat.embed.version>
    </properties>
    <build>
        <sourceDirectory>src</sourceDirectory>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
                <configuration>
                    <release>11</release>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.2.1</version>
                <configuration>
                    <warSourceDirectory>WebContent</warSourceDirectory>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${org.springframework.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${org.springframework.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${org.springframework.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${org.springframework.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${org.springframework.version}</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>${javax.servlet.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
            <version>${org.apache.tomcat.embed.version}</version>
        </dependency>
    </dependencies>
    </project>
  9. プロジェクト名を右クリックして、Java EE Tools > Generate Deployment Descriptor Stub と選択してweb.xmlを生成

  10. 生成されたweb.xmlのweb-appタグ内に以下を追加

    <servlet>
        <servlet-name>mywebsite</servlet-name>
        <servlet-class>
        org.springframework.web.servlet.DispatcherServlet
        </servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>mywebsite</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
  11. web.xml中のservlet-nameタグに記載されている名前がサーブレット名でWEB-INF/以下に[servlet-name]-servlet.xmlという名前の設定ファイルをispatcherServlet初期化後に参照されるようになります。ここではmywebsite-servlet.xmlというファイル名で以下の内容を記載します。XMLやアノテーションの使用より Spring 3 で導入された JavaConfig を利用することも多いかと思いますが、ここではXMLによる例となります。

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:p="http://www.springframework.org/schema/p"
	xsi:schemaLocation="
        http://www.springframework.org/schema/beans     
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc 
        http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context.xsd">
 
	<mvc:annotation-driven />
	<context:component-scan
		base-package="hayashier.mywebsite" />
	<mvc:default-servlet-handler />

</beans>
  1. その他、CSS/JavaScriptを利用できるように[servlet-name]-servlet.xmlのbeansタグ内に以下を追加しておきます。ここでは、ローカルの開発環境での開発効率向上を優先してCSSはキャッシュを無効化していますが、不要な場合、mvc:resource-chainタグおよびその中身は不要です。

	<mvc:resources mapping="/js/**" location="/WEB-INF/resources/js/" />
	<mvc:resources mapping="/css/**" location="/WEB-INF/resources/css/">
		<mvc:resource-chain resource-cache="false">
			<mvc:resolvers>
				<mvc:version-resolver>
					<mvc:content-version-strategy patterns="/**"/>
				</mvc:version-resolver>
			</mvc:resolvers>
		</mvc:resource-chain>
	</mvc:resources>
  1. 同ファイル内に共通ヘッダーを作成できるように以下も追加しておきます。

	<mvc:resources mapping="/common/**" location="/WEB-INF/views/common/" />
  1. 現状の設定のままだと、http://localhost:8080/hayashier-website/ でないとアクセスできない状態になっている。ルートアクセスするために、ServersタブでTomcat v 9.0 Server at localhostとなっている部分をクリックして展開

  2. Modulesタブを選択すると、Pathが/hayashier-websiteとなっていることが確認

  3. Pathを選択した状態で、[Edit...]を選択し、/に変更して再起動することでルートアクセスできるようになる。

  4. 再度15を実行

  5. Portsの項目でHTTP/1.1が8080になっているので、80に変更。その後、ポート番号を指定しなくてもアクセスできるようになる。

  6. Run As > Run on Server を選択

  7. http://localhost でアクセスできるようになる

  • 参考

Summary

ここまでで作成したファイルは以下のようになります。

  • pom.xml

<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>hayashier.website</groupId>
    <artifactId>hayashier-website</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>
    <properties>
        <org.springframework.version>5.2.5.RELEASE</org.springframework.version>
        <org.springframework.data.version>2.2.7.RELEASE</org.springframework.data.version>
        <javax.servlet.version>1.2</javax.servlet.version>
        <org.apache.tomcat.embed.version>9.0.34</org.apache.tomcat.embed.version>
    </properties>
    <build>
        <sourceDirectory>src</sourceDirectory>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
                <configuration>
                    <release>11</release>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.2.1</version>
                <configuration>
                    <warSourceDirectory>WebContent</warSourceDirectory>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${org.springframework.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${org.springframework.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${org.springframework.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${org.springframework.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${org.springframework.version}</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>${javax.servlet.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
            <version>${org.apache.tomcat.embed.version}</version>
        </dependency>
    </dependencies>
</project>
  • WebContent/web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0">
  <display-name>Hayashier.com - Private page</display-name>
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
  
  <servlet>
    <servlet-name>mywebsite</servlet-name>
    <servlet-class>
      org.springframework.web.servlet.DispatcherServlet
    </servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>mywebsite</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>
  • WebContent/mywebsite-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:p="http://www.springframework.org/schema/p"
	xsi:schemaLocation="
        http://www.springframework.org/schema/beans     
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc 
        http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context.xsd">
 
	<mvc:annotation-driven />
	<context:component-scan
		base-package="hayashier.mywebsite" />
	<mvc:default-servlet-handler />

	<bean id="viewResolver"
		class="org.springframework.web.servlet.view.UrlBasedViewResolver">
		<property name="viewClass"
			value="org.springframework.web.servlet.view.JstlView" />
		<property name="prefix" value="/WEB-INF/jsp/" />
		<property name="suffix" value=".jsp" />
	</bean>

	<mvc:resources mapping="/js/**" location="/WEB-INF/resources/js/" />
	<mvc:resources mapping="/css/**" location="/WEB-INF/resources/css/">
		<mvc:resource-chain resource-cache="false">
			<mvc:resolvers>
				<mvc:version-resolver>
					<mvc:content-version-strategy patterns="/**"/>
				</mvc:version-resolver>
			</mvc:resolvers>
		</mvc:resource-chain>
	</mvc:resources>
	<mvc:resources mapping="/common/**" location="/WEB-INF/views/common/" />

</beans>
  • WebContent/index.jsp

<!DOCTYPE html>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<html>
	<head>
		<title>Test Title</title>
	</head>
	<body>
		Hello, World!
	</body>
</html>

Spring MVCによるルーティング

リクエストパスと実際に行う処理について、対象のControllerには@Controllerのアノテーションをつけ、処理を行いたいメソッドに対してパスを定義した@RequestMappingのアノテーションを付与します。 例えば、以下のようにTopControllerを作成して@Controllerのアノテーションを付与し、helloWorld()メソッドに@RequestMapping("/top")のアノテーションを付与することで、http://localhost/top のようにアクセスすることでhelloworld()メソッドによる処理が行われるようになります。

return new ModelAndView("top", "message", message)のように第1引数で指定した文字列の JSP ファイルのビューを返し、第3引数に渡した値に対し第2引数に渡した文字列をビューで指定することで呼び出すことができます。ここでは、"message"という文字列を第2引数として、第3引数に<h1>Hello, World!</h1>を渡しているため、ビューから${message}のように呼び出すことで、ビュー上で<h1>Hello, World!</h1>を表示させることができます。

  • src/contoller/TopController.java

package hayashier.mywebsite.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class TopController {

	@RequestMapping("/top")
	public ModelAndView helloWorld() {
		String message = "<h1>Hello, World!</h1>";
		return new ModelAndView("top", "message", message);
	}
	
}
  • WebContent/WEB-INF/jsp/top.jsp

<!DOCTYPE html>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<html>
	<head>
		<meta charset="UTF-8">
		<title>Top | hayashier.com</title>
	</head>
	<body>
        ${message}
	</body>
</html>

Spring MVCにMySQL導入

pom.xml中のprojectタグに以下を追加

  <properties>
	<org.springframework-version>5.2.5.RELEASE</org.springframework-version>
	<mysql-connector-java.version>8.0.20</mysql-connector-java.version>
  </properties>

projectタグ内のdependenciesタグに以下を追加

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-jdbc</artifactId>
	<version>${org.springframework-version}</version>
</dependency>
<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<version>${mysql-connector-java.version}</version>
</dependency>
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-tx</artifactId>
	<version>${org.springframework-version}</version>
</dependency>

mywebsite-servlet.xmlのbeansタグにデータベースへアクセスするためのドライバー情報を追加

<bean id="dataSource"
	class="org.springframework.jdbc.datasource.DriverManagerDataSource">
	<property name="driverClassName" value="com.mysql.jdbc.Driver" />
	<property name="url" value="jdbc:mysql://localhost:3306/mywebsite" />
	<property name="username" value="root" />
	<property name="password" value="Test1234" />
</bean>

<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
	<property name="dataSource" ref="dataSource" />
</bean>

最小構成では、Controllerで以下のようにアクセス可能です。

@Controller
public class HomeController {

	@Autowired
	private JdbcTemplate jdbcTemplate;

	@RequestMapping("/article/")
	public String show(Model model) {

	final String selectQuery = "select * from Article";
	
	Map<?, ?> map = jdbcTemplate.queryForMap(selectQuery);
	model.addAttribute("data", map.toString());
	return "article";
	}
}

実際にはControllerに全てを記述せずにモデル等を定義することが多いかと思います。

ここでは、Articleのオブジェクトを表すクラスを以下のように定義します。

package hayashier.mywebsite.model;

public class Article {
	private Integer id;
	private String title;
	private String body;
	private String path;
	
	public Article() {
		
	}
	
	public Article(Integer id, String title, String body, String path) {
		this.id = id;
		this.title = title;
		this.body = body;
		this.path = path;
	}
	
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
	public String getBody() {
		return body;
	}
	public void setBody(String body) {
		this.body = body;
	}
	public String getPath() {
		return path;
	}
	public void setPath(String path) {
		this.path = path;
	}
}

Mapperを定義します。

package hayashier.mywebsite.mapper;

import java.sql.ResultSet;
import java.sql.SQLException;

import org.springframework.jdbc.core.RowMapper;

import hayashier.mywebsite.model.Article;

public class ArticleMapper implements RowMapper<Article> {
	public static final String BASE_SQL =
			"SELECT a.id, a.title, a.body, a.path"
			+ " FROM Article a";

	@Override
	public Article mapRow(ResultSet rs, int nowNum) throws SQLException {
		Integer id = rs.getInt("id");
		String title = rs.getString("title");
		String body = rs.getString("body");
		String path = rs.getString("path");
		
		return new Article(id, title, body, path);
	}

}

DAOを定義します。

package hayashier.mywebsite.dao;

import java.util.List;

import javax.sql.DataSource;

import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import hayashier.mywebsite.mapper.ArticleMapper;
import hayashier.mywebsite.model.Article;

@Repository
@Transactional
public class ArticleDAO extends JdbcDaoSupport {

	public ArticleDAO(DataSource dataSource) {
		this.setDataSource(dataSource);
	}
	
	public List<Article> getArticles() {
		String sql = ArticleMapper.BASE_SQL;
		
		Object[] params = new Object[] {};
		ArticleMapper mapper = new ArticleMapper();
		List<Article> articles = this.getJdbcTemplate().query(sql, params, mapper);
		
		return articles;
	}
	
	public Article getArticleFromPath(String path) {
		String sql = ArticleMapper.BASE_SQL
				+ " WHERE a.path = ?";
		
		Object[] params = new Object[] { path };
		ArticleMapper mapper = new ArticleMapper();
		List<Article> articles = this.getJdbcTemplate().query(sql, params, mapper);
		
		if (articles.size() > 0) {
			return articles.get(0);
		}
		
		return new Article();
	}
}

Controllerから以下のように使用することができる

@Controller
public class ListController {

	@Autowired
	private ArticleDAO articleDAO;

	@RequestMapping("/list/{page_id}")
	public ModelAndView list(@PathVariable int page_id) {
		ArrayList<Article> articles = (ArrayList<Article>) articleDAO.getArticles();

MySQL上で以下のようにDDLを定義して、サンプルデータを挿入しておきます。

CREATE DATABASE IF NOT EXISTS `mywebsite` DEFAULT CHARACTER SET utf8mb4;
USE `mywebsite`;

DROP TABLE IF EXISTS Article;
CREATE TABLE Article (
  id integer NOT NULL AUTO_INCREMENT,
  title VARCHAR(255),
  body LONGTEXT,
  path VARCHAR(255),
  PRIMARY KEY (id),
  UNIQUE (path)
);

INSERT INTO Article (title, body, path) VALUES (
	'test title',
	'Test Body',
	'test-path'
);
  • 参考

設定ファイルを外部ファイル化

MySQLのホスト情報やユーザー名やパスワードを外部ファイル化しておきます。ファイルの場所は相対ディレクトリ指名やclasspath:,file:/// or file: が使用できます。

mywebsite-servlet.xmlにファイルの場所を示す以下のbeanタグを追加します。

	<bean id="appProperties"
	    class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
	    <property name="locations">
	        <list>
	            <value>classpath:db.properties</value>
	        </list>
	    </property>
	</bean>

また、外部ファイルから読み込んだ値を設定するために同ファイル内に以下のbeanタグを追加しておきます。 

	<bean id="dataSource"
		class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
		<property name="url" value="${db.url}" />
		<property name="username" value="${db.username}" />
		<property name="password" value="${db.password}" />
	</bean>

WebContent/WEB-INF以下にclassesディレクトリを作成し、WEB-INF/classes ディレクトリに.propertiesのファイルを配置します。

db.url=localhost
db.username=root
db.password=Test1234
  • 参考

Spring MVCにRedisの導入

pom.xmlに以下を追加

	<dependency>
	    <groupId>org.springframework.data</groupId>
	    <artifactId>spring-data-redis</artifactId>
	    <version>${org.springframework.data.version}</version>
	</dependency>
	<dependency>
	    <groupId>redis.clients</groupId>
	    <artifactId>jedis</artifactId>
	    <version>${jedis.version}</version>
	</dependency>

mywebsite-servlet.xmlの<value>classpath:db.properties</value>の下に<value>classpath:cache.properties</value>を追加してRedisの設定ファイルの場所を指定します。

	<bean id="appProperties"
	    class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
	    <property name="locations">
	        <list>
	            <value>classpath:db.properties</value>
	            <value>classpath:cache.properties</value>
	        </list>
	    </property>
	</bean>

外部ファイルで指定した値を読み込んで設定等行うために以下を追加します。

	<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
		<property name="maxTotal" value="200" />
		<property name="maxIdle" value="50" />
		<property name="maxWaitMillis" value="3000" />
		<property name="testOnBorrow" value="true" />
	</bean>

	<bean id="jedisFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
		<property name="hostName" value="${cache.host}" />
		<property name="port" value="${cache.port}" />
		<property name="poolConfig" ref="jedisPoolConfig" />
		<property name="usePool" value="true" />
	</bean>
  
	<bean id="stringRedisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer" />
	<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" p:connection-factory-ref="jedisFactory" p:valueSerializer-ref="stringRedisSerializer" p:keySerializer-ref="stringRedisSerializer" />

WebContent/WEB-INF/classes以下にcache.propertiesという名前で以下の内容のファイルを作成します。

cache.host=localhost
cache.port=6379
cache.password=
cache.timeout=

DAOファイルを定義。ここれではRedisTemplateのHash型を使用しています。

@Repository
@Transactional
public class ArticleCacheDAO {

	// The purpose of this threshold is to prevent from not being able to get data
	// 	caused by data loss event, for example, cache node failure or reboot
	public final static int CACHE_FOR_THREAHOLD = 0;

    @Autowired
    RedisTemplate<String,Object> cacheTemplate;

    @Resource(name="redisTemplate")
    HashOperations<String,String,Article> hashOps;

    private static final String KEY = "mywebsite:articles";
    TimeUnit ARTICLE_EXPIRED_TIME_UNIT = TimeUnit.DAYS;
    int ARTICLE_TIME_OUT = 24;

    public Article getArticle(String path) {
    	return hashOps.get(KEY, path);
    }

    public List<Article> getAllArticles() {	
    	return new ArrayList<Article>(hashOps.entries(KEY).values());
    }

    public void addArticle(Article article) {
    	hashOps.putIfAbsent(KEY, article.getPath(), article);
    	cacheTemplate.expire(KEY, ARTICLE_TIME_OUT, ARTICLE_EXPIRED_TIME_UNIT);
    }
    public void addArticles(List<Article> articles) {
    	for (Article article : articles) {
    		addArticle(article);
    	}
    }

    public long getNumberOfArticles() {
    	return hashOps.size(KEY);
    }

}

モデルをSerializableのインターフェイスとして実装するように変更しておきます。

public class Article implements Serializable {

あとは使う場所のクラスでメンバー変数として宣言して使用します。

@Autowired
private ArticleCacheDAO articleCacheDAO;

要件に応じて、SQLで取得する前にキャッシュから取得して、データがなければ、SQLで取得し、その後キャッシュも更新するといった実装をします。

public Article getArticleFromPath(String path) {
	Article article = articleCacheDAO.getArticle(path);
	if (article != null) return article;

	String sql = ArticleMapper.BASE_SQL
			+ " WHERE a.path = ?";
	
	Object[] params = new Object[] { path };
	ArticleMapper mapper = new ArticleMapper();
	List<Article> articles = this.getJdbcTemplate().query(sql, params, mapper);
	
	if (articles.size() == 0) {
		return new Article();
	}
	
	article = articles.get(0);
	articleCacheDAO.addArticle(article);
	
	return article;
}
  • 参考

Tomcat上にSpring MVCのプロジェクトをデプロイ

先程はEclipseで組み込みの機能のTomcatを利用して動作確認をしていましたが、MacOS上にTomcatをインストールしてこの上で、Spring MVCを動作させます。上記でSpring MVCをwarファイルとして作成していたため、これをTomcatでWebサーバとして稼働させるものの上に配置して動作させます。

  1. 以下のようにHomebrewでTomcatをインストールして起動します。

    $ brew install tomcat
    $ brew services start tomcat
  2. http://localhost でTomcatのデフォルトページにアクセスできるようになります。 

  3. Tomcatで稼働するWebサーバのルートディレクトリの場所の確認をおこないます。brew installからインストールした場合、/usr/local/Cellar/tomcat/9.0.34/libexec/webapps/のディレクトリ(9.0.34の部分は、tomcatで使用しているバージョン)となり、こちらに移動しておきます。上記ディレクトリはbrew ls tomcatコマンドで確認でき、webappsディレクトリ以下が実際にWebサーバーとして稼働させるデフォルトのルートディレクトリとなります。

    $ brew ls tomcat
    /usr/local/Cellar/tomcat/9.0.34/bin/catalina
    /usr/local/Cellar/tomcat/9.0.34/homebrew.mxcl.tomcat.plist
    /usr/local/Cellar/tomcat/9.0.34/libexec/bin/ (17 files)
    /usr/local/Cellar/tomcat/9.0.34/libexec/conf/ (10 files)
    /usr/local/Cellar/tomcat/9.0.34/libexec/lib/ (32 files)
    /usr/local/Cellar/tomcat/9.0.34/libexec/logs/ (5 files)
    /usr/local/Cellar/tomcat/9.0.34/libexec/temp/safeToDelete.tmp
    /usr/local/Cellar/tomcat/9.0.34/libexec/webapps/ (623 files)
    /usr/local/Cellar/tomcat/9.0.34/libexec/work/ (2 files)
    /usr/local/Cellar/tomcat/9.0.34/libexec/ (2 files)
    /usr/local/Cellar/tomcat/9.0.34/RELEASE-NOTES
    /usr/local/Cellar/tomcat/9.0.34/RUNNING.txt
  4. warファイルをwebapps以下に配置

    • cp ~/eclipse-workspace/HayashierWebsite/target/hayashier-website-0.0.1-SNAPSHOT.war .

  5. 以下のURLでアクセスできるようになる

    • http://localhost:8080/hayashier-website-0.0.1-SNAPSHOT/

  6. server.xmlについて、コメントアウト等は除き、主要箇所は全体として以下のようになります。ここでは、Connectorタグでポート番号を8080番から80番に変更。hayashier-website-0.0.1-SNAPSHOT.warはwebapps以下でディレクトリとして展開されますが、このディレクトリをappBase="webapps"の属性を持つHostタグ内のContextタグに対して、docBase="hayashier-website-0.0.1-SNAPSHOT"のように指定します。 

    <?xml version="1.0" encoding="UTF-8"?>
    <Server port="8005" shutdown="SHUTDOWN">
                            :
                            :
    <Service name="Catalina">
        <Connector port="80" protocol="HTTP/1.1"
                connectionTimeout="20000"
                redirectPort="8443" />
        <Engine name="Catalina" defaultHost="localhost">
                            :
        <Host name="localhost"  appBase="webapps"
                unpackWARs="true" autoDeploy="true">
            <Context path="/" docBase="hayashier-website-0.0.1-SNAPSHOT" reloadable="true" />
            <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
                prefix="localhost_access_log" suffix=".txt"
                pattern="%h %l %u %t &quot;%r&quot; %s %b" />
        </Host>
        </Engine>
    </Service>
    </Server>
  7. Tomcatを再起動

    $ brew services restart tomcat
  8. http://localhost で作成していたアプリケーションが見られるようになります。

  • 参考

Error

cannot resolve to be a type のようにでる

importしてるやつも一度削除して、Eclipseによるimportの提案に従って追加

突然Springが機能しなくなる

Consoleには以下のエラー文

情報: No Spring WebApplicationInitializer types detected on classpath
4月 29, 2020 4:29:36 午後 org.apache.catalina.core.ApplicationContext log
情報: サーブレット [mywebsite] を利用不可能にマークします
4月 29, 2020 4:29:36 午後 org.apache.catalina.core.StandardContext loadOnStartup
重大: Web アプリケーション [] のサーブレット [mywebsite] の load() メソッドは例外を投げました。
java.lang.ClassNotFoundException: org.springframework.context.ApplicationContextAware
  • Tomcatを停止

  • Projectメニューから[Clean...]

  • ServersタブからTomcatを右クリックして[Clean...]

  • Tomcatをクリックし、Modulesタブでパスが初期状態になっているので変更(例. /hayashier-website から / に変更)

  • Tomcatを起動し、プロジェクトをサーバー上で稼働

Request processing failed; nested exception is org.springframework.data.redis.serializer.SerializationException: Cannot serialize; nested exception is org.springframework.core.serializer.support.SerializationFailedException: Failed to serialize object using DefaultSerializer; nested exception is java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type [hayashier.mywebsite.model.Article]

対象のモデルにimplements Serializableを追加して、Serializableのインターフェイスとして実装

ルートパスでアクセスできない

Tomcatのデフォルトページへのアクセスが http://localhost:8080/hayashier-website/ のURLとなるので、/hayashier-websiteを省略した形でアクセスできるようにするための設定手順となります。

  1. ルートアクセスするために、ServersタブでTomcat v 9.0 Server at localhostとなっている部分をクリックして展開

  2. Modulesタブを選択すると、Pathが/hayashier-websiteとなっていることが確認

  3. Pathを選択した状態で、[Edit...]を選択し、/に変更することでルートアクセスできるようになる。

Port番号を8080から80番へ変更

以下、Eclipse組み込みのTomcatの設定変更手順となります。

  • ServersタブでTomcat v 9.0 Server at localhostとなっている部分をクリックして展開

  • Portsの項目で、HTTP/1.1を8080から80に変更

  • Eclipse組み込みでばうTomcatのデフォルト設定を変更するためには、conf/server.xmlの以下のようなConnectorタグを8080番ポートを80番ポートへ変更

    <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />

パスにプロジェクト名を含むスナップショットのディレクトリ名ではなくルートディレクトリでアクセスできるように変更

server.xmlのHostタグ内の以下のように変更

<Context path="/" docBase="hayashier-website-0.0.1-SNAPSHOT" reloadable="true" />

jspファイルのままでレンダリングされない

以下のタグは<!DOCTYPE html>の後に配置

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>

the type is not generic; it cannot be parameterized with arguments <Map<String,String>>

List<Map<String, String>> articles = new ArrayList<Map<String, String>>();

以下のようにインターフェイスではなく具象クラスで宣言

ArrayList<Map<String, String>> articles = new ArrayList<Map<String, String>>();

重大: パス[]を持つコンテキスト内のサーブレット[mywebsite]のServlet.service() が例外[Handler processing failed; nested exception is java.lang.NoClassDefFoundError: javax/servlet/jsp/jstl/core/Config]が根本的要因と共に投げられました。 java.lang.ClassNotFoundException: javax.servlet.jsp.jstl.core.Config

以下のエラーが発生

4月 27, 2020 7:03:55 午後 org.apache.catalina.core.StandardWrapperValve invoke
重大: パス[]を持つコンテキスト内のサーブレット[mywebsite]のServlet.service() が例外[Handler processing failed; nested exception is java.lang.NoClassDefFoundError: javax/servlet/jsp/jstl/core/Config]が根本的要因と共に投げられました。
java.lang.ClassNotFoundException: javax.servlet.jsp.jstl.core.Config
	at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1365)
	at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1188)
	at org.springframework.web.servlet.support.JstlUtils.exposeLocalizationContext(JstlUtils.java:103)
	at org.springframework.web.servlet.view.JstlView.exposeHelpers(JstlView.java:137)
	at org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java:145)
	at org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:316)
	at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1373)
	at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1118)
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1057)
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943)
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
	at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:634)
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
	at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:690)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:373)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1590)
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.base/java.lang.Thread.run(Thread.java:834)

pom.xmlに以下を追加してMavenプロジェクトをビルドし直す

  	<dependency>
  		<groupId>javax.servlet</groupId>
  		<artifactId>jstl</artifactId>
  		<version>1.2</version>
  	</dependency>

~.htmlの識別子をつけるとjspがrenderingされるが、識別子をつけない場合、renderingされない

pom.xmlに以下を追加

	<dependency>
	    <groupId>org.apache.tomcat.embed</groupId>
	    <artifactId>tomcat-embed-jasper</artifactId>
	    <version>9.0.34</version>
	</dependency>

web.xmlのurl-patternを変更

  <servlet-mapping>
    <servlet-name>mywebsite</servlet-name>
    <url-pattern>/*</url-pattern>
  </servlet-mapping>

↓

  <servlet-mapping>
    <servlet-name>mywebsite</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>

(引用)

(参考)

のサイトから9.0.34のzipをダウンロードして展開

(参照)

(参照)

(参考)

(参考)

(参考)

22. Web MVC framework
Spring MVC Tutorial
Tomcat
Simplest Spring MVC Framework Tutorial – Hello World Example with UI (JSP) Page
Eclipse: Missing web.xml file? How can I create web.xml in Eclipse?
Spring MVC and Spring JDBC Transaction Tutorial
How to connect to MySQL database using Spring, and use of Spring's JdbcTemplate
SpringMVCでMySQLに接続する
Spring MVCでMySQLにJDBC Templateを使って接続してみた
Reading properties files in Spring with PropertyPlaceholderConfigurer bean
How to read property file in spring using xml based configuration file
Spring MVC 導入Redis 初探
Spring Data Redis Example
How to deploy a .war on Tomcat on OSX
Install Apache Tomcat on Mac using Brew
How To Install Tomcat 8 On Windows
No Spring WebApplicationInitializer types detected on classpath
DefaultSerializer requires a Serializable payload but received an object of type [model.Admin]
SpringSource IDE does not use project name as root URL for Spring MVC application
How to change port numbers for Tomcat in Eclipse
Change Tomcat port numbers in server.xml file