ThymeleafとJSPを共存させた話

転職して、JSPを使っている会社に入りました。今どきJSPはないだろ!っていうことでThymeleafと共存させることにしました。
プロジェクト構成は、こんな感じです。

JavaCofigでBean定義を行うプロジェクト構成

f:id:b1a9id:20170627220712p:plain

xmlでBean定義を行うプロジェクト構成

f:id:b1a9id:20170627234120p:plain

では、さっそくコードを見ていただこうと思います。

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>spring-framework-sandbox</artifactId>
	<version>1.0-SNAPSHOT</version>
	<packaging>war</packaging>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<spring.mvc.version>4.3.8.RELEASE</spring.mvc.version>
		<jvl.over.slf4j.version>1.7.25</jvl.over.slf4j.version>
		<logback.classic.version>1.2.3</logback.classic.version>
		<lombok.version>1.16.16</lombok.version>
		<thymeleaf.version>3.0.5.RELEASE</thymeleaf.version>
	</properties>

	<dependencies>
		<!-- Spring MVC -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${spring.mvc.version}</version>
		</dependency>

		<!-- Jvl over slf4j -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>jcl-over-slf4j</artifactId>
			<version>${jvl.over.slf4j.version}</version>
		</dependency>

		<!-- logback classic -->
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>${logback.classic.version}</version>
		</dependency>

		<!-- Lombok -->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<version>${lombok.version}</version>
		</dependency>

		<!-- Thymeleaf -->
		<dependency>
			<groupId>org.thymeleaf</groupId>
			<artifactId>thymeleaf-spring4</artifactId>
			<version>${thymeleaf.version}</version>
		</dependency>

		<!-- JSP -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jsp-api</artifactId>
			<version>2.0</version>
		</dependency>

		<!-- JSTL -->
		<dependency>
			<groupId>jstl</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>
	</dependencies>
</project>

JavaConfig

ThymeleafConfig.java
@Configuration
public class ThymeleafConfig {

	@Bean
	public SpringResourceTemplateResolver templateResolver(){
		SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
                // テンプレートファイルがどのディレクトリの配下にあるかを指定。ここでは、/WEB-INF/views/配下
		templateResolver.setPrefix("/WEB-INF/views/");
                // 接尾字が何かを指定。ここでは.html
		templateResolver.setSuffix(".html");
                // テンプレートの種類は何かを指定。ここでhtml
		templateResolver.setTemplateMode(TemplateMode.HTML);
		return templateResolver;
	}

	@Bean
	public SpringTemplateEngine templateEngine(){
		SpringTemplateEngine templateEngine = new SpringTemplateEngine();
		templateEngine.setTemplateResolver(templateResolver());
		templateEngine.setEnableSpringELCompiler(true);
		return templateEngine;
	}

	@Bean
	public ThymeleafViewResolver viewResolver(){
		ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
		viewResolver.setTemplateEngine(templateEngine());
		viewResolver.setCharacterEncoding("UTF-8");
                // ViewResolverを使う順序を指定。JSPより先にする。
		viewResolver.setOrder(1);
                // Controllerから返すView名がこの文字列であれば、ThymeleafViewResolverを使う
		viewResolver.setViewNames(new String[] {"thymeleaf/*"});
		return viewResolver;
	}
}
WevMvcConfig.java

JSP用のViewResolverを登録する。Thymeleafとほぼ同じなので説明は割愛します。

@Configuration
@EnableWebMvc
@ComponentScan("com.example")
// ThymeleafConfigをインポートします。
@Import(ThymeleafConfig.class)
public class WebMvcConfig extends WebMvcConfigurerAdapter {

	@Override
	public void configureViewResolvers(ViewResolverRegistry registry) {
		registry.jsp()
				.prefix("/WEB-INF/views/")
				.suffix(".jsp")
				.viewNames("jsp/*");
		registry.order(2);
	}
}

xml

beans-biz.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

	<context:component-scan base-package="com.example"/>

	<bean id="messageSource"
	      class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
		<property name="basename" value="META-INF.messages" />
	</bean>

	<bean id="globalValidator"
	      class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
		<property name="validationMessageSource" ref="messageSource" />
	</bean>
</beans>
beans-webmvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">

	<context:component-scan base-package="com.example.web.controller" />

	<!-- Spring MVCアノテーション利用設定 -->
	<mvc:annotation-driven validator="globalValidator" />

	<!-- Static Resourceの設定 -->
	<mvc:resources mapping="/resources/**" location="/WEB-INF/views/" />

	<!-- ViewResolverの設定 -->
	<!-- Thymeleaf -->
	<beans>
		<bean id="templateResolver" class="org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver" >
			<property name="prefix" value="/WEB-INF/views/" />
			<property name="suffix" value=".html" />
			<property name="characterEncoding" value="UTF-8" />
		</bean>
		<bean id="templateEngine" class="org.thymeleaf.spring4.SpringTemplateEngine">
			<property name="templateResolver" ref="templateResolver" />
		</bean>
		<bean class="org.thymeleaf.spring4.view.ThymeleafViewResolver">
			<property name="templateEngine" ref="templateEngine" />
			<property name="characterEncoding" value="UTF-8" />
			<property name="viewNames" value="thymeleaf/*" />
			<property name="order" value="1" />
		</bean>
	</beans>
	<!-- JSP -->
	<beans>
		<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
			<property name="prefix" value="/WEB-INF/views/" />
			<property name="suffix" value=".jsp" />
			<property name="viewNames" value="jsp/*" />
			<property name="order" value="2" />
		</bean>
	</beans>

</beans>


JavaConfigとxmlの共通のControllerクラスです。
View返すだけのControllerなので、特に説明はいらないかと思います。

WelcomeController.java
@Controller
@RequestMapping("/hello")
public class WelcomeController {

	@GetMapping("/jsp")
	public String helloJsp() {
		return "jsp/hello/index";
	}

	@GetMapping("/thymeleaf")
	public String helloThymeleaf() {
		return "thymeleaf/hello/index";
	}
}

あとは、アプリケーションを起動して次にアクセスするだけです。
http://localhost:8080/hello/jsp
http://localhost:8080/hello/thymeleaf

※ViewResolverの順序は、JSPよりThymeleafを先に指定してください。JSPを先に指定してしまうと、JSP以降に登録されてあるViewResolverを使わなくなってしまいます。