Spring MVC hello world example (Gradle and JSP)
This tutorial shows you how to create a Spring Web MVC application with the Jakarta Server Pages (JSP; formerly JavaServer Pages) template.
Technologies and tools used:
- Java 11
- Spring 5.2.22.RELEASE
- JSP
- JSTL 1.2
- Servlet API 4.0.1
- Bootstrap 5.2.0 (webjars)
- IntelliJ IDEA
- Gradle 7.5.1
- Gradle Gretty plugin 3.0.9 for embedded servlet containers (Tomcat 9 and Jetty 9.4)
- Spring Test 5.2.22.RELEASE
- Hamcrest 2.2
- JUnit 5.9
Table of contents:
- 1. Directory Structure
- 2. Project Dependencies
- 3. Project Dependencies – Tree Format
- 4. Spring Controller
- 5. Spring Configuration
- 6. Spring DispatcherServlet
- 7. Spring MVC and Unit Tests
- 8. JSP and JSTL
- 9. Demo
- 10. Download Source Code
- 10. References
Note
This tutorial is NOT a Spring Boot application, just pure Spring Web MVC!
1. Directory Structure
Below is the standard Java directory structure for this project.
2. Project Dependencies
Below is the core dependency for this project; The spring-webmvc
dependency is a must, but other dependencies depend on your project requirement.
spring-webmvc
– For Spring core related web components.jstl
– For Jakarta Standard Tag Library (JSTL; formerly JavaServer Pages Standard Tag Library).org.webjars.bootstrap
– For WebJars to manage client-side web libraries, for example bootstrap.javax.servlet-api
– We needservlet-api
to compile the web application, set tocompileOnly
scope; usually, the embedded server will provide this.org.gretty
– Run this project with the embedded servlet containers like Tomcat 9 or Jetty 9.4.
Why Gretty 3 plugin?
The latest Gretty 4 plugin supports only Tomcat 10, and we need Tomcat 8.5 or 9 for Spring MVC 5, so we will stick with the older Gretty 3.
Why Spring Web MVC 5 does not run o Tomcat 10?
- Tomcat 10 implements the Servlet 5 specification (part of Jakarta EE 9), under package
jakarta.*
. - Spring Web MVC 5.x implements the Servlet 4 specification (part of Jakarta EE 8) , under package
javax.*
.
Spring 5 still depends on javax.servlet.*
, and the latest Tomcat 10 depends on jakarta.servlet
. In a nutshell, Spring 5 does not works with Tomcat 10 because of the package renaming from javax.*
to jakarta.*
.
plugins {
id 'org.gretty' version '3.0.9'
}
apply plugin: 'java'
apply plugin: 'war'
apply plugin: 'idea'
// JDK 11
sourceCompatibility = 11
targetCompatibility = 11
repositories {
mavenCentral()
}
dependencies {
compileOnly 'javax.servlet:javax.servlet-api:4.0.1'
implementation 'javax.servlet:jstl:1.2'
implementation 'org.springframework:spring-webmvc:5.2.22.RELEASE'
implementation 'org.webjars:bootstrap:5.2.0'
testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.9.0'
testImplementation 'org.springframework:spring-test:5.2.22.RELEASE'
testImplementation 'org.hamcrest:hamcrest-core:2.2'
}
gretty {
httpPort = 8080
contextPath = '/'
//servletContainer = 'jetty9.4'
servletContainer = 'tomcat9'
}
test {
useJUnitPlatform()
}
3. Project Dependencies – Tree Format
Review again the project dependencies in a tree structure.
gradle dependencies --configuration compileClasspath
> Task :dependencies
------------------------------------------------------------
Root project 'spring-mvc-hello-world-jsp'
------------------------------------------------------------
compileClasspath - Compile classpath for source set 'main'.
+--- javax.servlet:javax.servlet-api:4.0.1
+--- javax.servlet:jstl:1.2
+--- org.springframework:spring-webmvc:5.2.22.RELEASE
| +--- org.springframework:spring-aop:5.2.22.RELEASE
| | +--- org.springframework:spring-beans:5.2.22.RELEASE
| | | \--- org.springframework:spring-core:5.2.22.RELEASE
| | | \--- org.springframework:spring-jcl:5.2.22.RELEASE
| | \--- org.springframework:spring-core:5.2.22.RELEASE (*)
| +--- org.springframework:spring-beans:5.2.22.RELEASE (*)
| +--- org.springframework:spring-context:5.2.22.RELEASE
| | +--- org.springframework:spring-aop:5.2.22.RELEASE (*)
| | +--- org.springframework:spring-beans:5.2.22.RELEASE (*)
| | +--- org.springframework:spring-core:5.2.22.RELEASE (*)
| | \--- org.springframework:spring-expression:5.2.22.RELEASE
| | \--- org.springframework:spring-core:5.2.22.RELEASE (*)
| +--- org.springframework:spring-core:5.2.22.RELEASE (*)
| +--- org.springframework:spring-expression:5.2.22.RELEASE (*)
| \--- org.springframework:spring-web:5.2.22.RELEASE
| +--- org.springframework:spring-beans:5.2.22.RELEASE (*)
| \--- org.springframework:spring-core:5.2.22.RELEASE (*)
\--- org.webjars:bootstrap:5.2.0
4. Spring Controller
Below is a Spring Web MVC controller to handle web requests for /
and /hello/{name}
and display messages.
package com.mkyong.web;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class HelloController {
//@RequestMapping(value = "/", method = RequestMethod.GET)
@GetMapping("/")
public String defaultPage(ModelMap model) {
return "hello";
}
//@RequestMapping(value = "/hello/{name:.+}", method = RequestMethod.GET)
@GetMapping("/hello/{name:.+}")
public ModelAndView hello(@PathVariable("name") String name) {
ModelAndView model = new ModelAndView();
model.setViewName("hello");
model.addObject("message", name);
return model;
}
}
5. Spring Configuration
Spring needs a viewResolver
bean for JSP page.
package com.mkyong.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
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.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;
@EnableWebMvc
@Configuration
@ComponentScan({ "com.mkyong.web" })
public class SpringWebConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("/webjars/");
}
@Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/jsp/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
}
6. Spring DispatcherServlet
This MyServletInitializer
will be auto-detected by the Servlet containers (e.g., Jetty or Tomcat).
package com.mkyong;
import com.mkyong.config.SpringWebConfig;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class MyServletInitializer
extends AbstractAnnotationConfigDispatcherServletInitializer {
// services and data sources
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[0];
}
// controller, view resolver, handler mapping
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringWebConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
7. Spring MVC and Unit Tests
A simple unit test for the Spring MVC controller.
package com.mkyong;
import com.mkyong.config.SpringWebConfig;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.ModelAndView;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ExtendWith(SpringExtension.class)
@WebAppConfiguration
@ContextConfiguration(classes = {SpringWebConfig.class})
public class HelloControllerTest {
@Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
@BeforeEach
void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
@Test
public void testDefaultPage() throws Exception {
MvcResult result = this.mockMvc.perform(get("/"))
/*.andDo(print())*/
.andExpect(status().isOk())
.andReturn();
ModelAndView modelAndView = result.getModelAndView();
assertEquals("hello", modelAndView.getViewName());
assertNull(modelAndView.getModel().get("message"));
}
@Test
public void testHelloPage() throws Exception {
MvcResult result = this.mockMvc.perform(get("/hello/mkyong"))
.andExpect(status().isOk())
.andReturn();
ModelAndView modelAndView = result.getModelAndView();
assertEquals("hello", modelAndView.getViewName());
assertEquals("mkyong", modelAndView.getModel().get("message"));
}
}
8. JSP and JSTL
A simple JSP + JSTL template to display a hello world message also shows how to integrate webjars’ Bootstrap and custom CSS and JS files.
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html lang="en">
<head>
<title>Spring Web MVC and JSP</title>
<spring:url value="/resources/core/css/main.css" var="coreCss" />
<spring:url value="/webjars/bootstrap/5.2.0/css/bootstrap.min.css" var="bootstrapCss" />
<link href="${bootstrapCss}" rel="stylesheet" />
<link href="${coreCss}" rel="stylesheet" />
</head>
<nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top">
<a class="navbar-brand" href="#">Mkyong.com</a>
</nav>
<main role="main" class="container">
<div class="starter-template">
<h1>Spring Web MVC JSP Example</h1>
<h2>
<c:if test="${not empty message}">
Hello ${message}
</c:if>
<c:if test="${empty message}">
Welcome!
</c:if>
</h2>
</div>
</main>
<spring:url value="/resources/core/js/main.js" var="coreJs" />
<spring:url value="/webjars/bootstrap/5.2.0/js/bootstrap.min.js" var="bootstrapJs" />
<script src="${coreJs}"></script>
<script src="${bootstrapJs}"></script>
</body>
</html>
9. Demo
Go to the terminal, project folder, and run gradle tomcatRunWar
.
gradle tomcatRunWar
//...
INFO: Completed initialization in 628 ms
17:19:14 INFO Tomcat 9.0.58 started and listening on port 8080
17:19:14 INFO runs at:
17:19:14 INFO http://localhost:8080
> Task :tomcatRunWar
Press any key to stop the server.
P.S Try to run the Spring Web MVC on the embedded Jetty with the command gradle jettyRunWar
.
http://localhost:8080/
http://localhost:8080/hello/mkyong
10. Download Source Code
$ git clone https://github.com/mkyong/spring-mvc/
$ cd spring-mvc-hello-world-jsp
$ gradle tomcatRunWar
or
$ gradle jettyRunWar
visit http://localhost:8080/
visit http://localhost:8080/hello/mkyong
10. References
- Spring MVC hello world example (Maven and Thymeleaf)
- Apache Tomcat Versions
- Gradle Gretty Plugin
- Gretty Github
- Gretty documentation
- Building Java Applications Sample
- Wikipedia – Model–view–controller
- Wikipedia – Jakarta Server Pages
- Spring Boot Hello World Example – Thymeleaf
- Spring 5, Embedded Tomcat 8, and Gradle: a Quick Tutorial
- Spring boot does not work with Tomcat 10
thanks, it help me up!
I don’t understand why view -> jsp -> index.jsp file and directory structure is under WEB-INF? Is this normal practice? Thank you (and thank you for all your tutorials).
Yes, this is normal practice. Usually WEB-INF folder is hidden for accessing on server from file manager, for example, but accessible from application. It’s trick for increase security.