Spring with Maven Tutorial
In this post, we feature a comprehensive Tutorial on Spring with Maven. Spring Framework is a Java application framework that provides many useful services for building applications. Apache Maven is a software project management and comprehension tool. In this article, I will demonstrate how to build a web application utilizing Spring MVC framework as a Maven project.
Table of Contents
1. Introduction
Apache Maven is a software project management and comprehension tool. Maven can manage a project’s build, reporting and documentation from a central piece of information based on the concept of a project object model (POM). Spring is a Java application framework that provides many useful services for building applications. It contains many modules such as core, beans, context, expression language, AOP, JDBC, JMS, Web, etc.
In this example, I will build a web application as a Maven project to demonstrate these Spring modules:
- Spring stereotype annotations
- Spring Model-View-Controller (MVC) framework
- Spring Data JPA
- Spring boot
- Spring property sources
- Spring test
2. Technologies Used
The example code in this article was built and run using:
- Java 1.8.101 (1.8.x will do fine)
- Maven 3.3.9 (3.3.x will do fine)
- Spring boot 1.5.14 (Higher version will do fine)
- Spring 4.x (Higher version will do fine)
- Eclipse Oxygen (Any Java IDE would work)
- H2 Database
3. Spring Boot Web Application
There are many ways to create a Spring boot web application. The easiest way for me is via the Spring starter tool with the steps below:
- Go to
https://start.spring.io/
. - Select
Maven Project
withJava
and Spring Boot version 1.5.14 and type inweb
,JPA
, andH2
in the “search for dependencies” bar. - Enter the group name as
jcg.zheng.demo
and artifact asspring-boot-web-jsp-demo
. - Click the
Generate Project
button.
A maven project will be generated and downloaded to your workstation. Import it into your Eclipse workspace.
3.1 Web Application Overview
In this example, I will build a web application with these features:
- Valid account users can login to view their account’s reward point and the transaction history.
- Invalid account users will be rejected by the application.
- A URL to load account transaction history.
3.2 Dependency
Maven pom.xml
manages the project libraries. Here is the generated pom.xml
from step 3.
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> <artifactId>spring-boot-web-jsp-demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>spring-boot-web-jsp-demo</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.14.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- JSTL --> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> </dependency> <!-- Need this to compile JSP for Embedded Tomcat --> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> </dependency> <!-- Spring MVC --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
4. Web Views
I found it’s useful to define the web views first when working on a web application. There are four views based on the requirements:
- Home view – the web application’s landing page.
- Valid user’s view – a valid user can view the account reward point and the transaction history.
- Invalid user’s view – display an error message to an invalid user.
- Error view – the exception handling view.
All of these views are placed at webapp/WEB-INF/jsp
directory. We will configure them in the application.properties
at step 5.1.
4.1 Home View
In this step, I will create a home.jsp
page, which displays the server’s local time, the welcome message from the Spring managed property, and a login form.
home.jsp
<!DOCTYPE html> <html lang="en"> <head> </head> <body> <h1>Spring Boot Web JSP Example</h1> <h2>Message From Property: ${message}</h2> <h3>The time on the server is ${serverTime}.</h3> <form action="user" method="post"> <input type="text" name="userName"><br> <input type="submit" value="Login"> </form> </body> </html>
4.2 User View
In this step, I will create a user.jsp
page to display a valid user’s reward point and the transaction history.
user.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <spring:url value="/css/main.css" var="demoCSS" /> <link href="${demoCSS}" rel="stylesheet"> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>User Home Page</title> </head> <body> <h3>Hi ${account.firstname} ${account.lastname}!</h3> <p>Account Id: ${account.id} Reward Point:${account.rewardPoint}</p> <h3>Transaction Detail</h3> <table> <tr> <th>Date</th> <th>Payee</th> <th>Description</th> <th>Amount</th> </tr> <c:forEach var="listValue" items="${account.transactions}"> <tr> <td>${listValue.createdDate}</td> <td>${listValue.payeeName}</td> <td>${listValue.description}</td> <td>${listValue.amount}</td> </tr> </c:forEach> </table> </body> </html>
4.3 Invalid User View
In this step, I will create an invalidUser.jsp
page to display an invalid user message.
invalidUser.jsp
<%@page contentType="text/html" pageEncoding="UTF-8"%> <%@ page session="false"%> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Error</title> </head> <body> <h2>Invalid User!</h2> <p>${userName} is not found in the system!</p> </body> </html>
4.4 Error View
In this step, I will create an error.jsp
page to handle any unexpected exceptions.
error.jsp
<!DOCTYPE html> <html lang="en"> <head> </head> <body> <h1>Caught Unexpected Exception</h1> <h2>from: ${content}.</h2> <p>due to: ${error}.</p> </body> </html>
5. Spring MVC Framework
5.1 Configuration
Spring MVC framework configures the views based on the naming convention.
application.properties
# Spring MVC view prefix. spring.mvc.view.prefix: /WEB-INF/jsp/ # Spring MVC view suffix. spring.mvc.view.suffix: .jsp # Locations of static resources. spring.resources.staticLocations:/resources/static/ #disable the white label when using an embedded servlet container. server.error.whitelabel.enabled = false spring.mvc.throw-exception-if-no-handler-found=true welcome.message: Hello from Spring boot Web JSP Demo!
5.2 Controller
@Controller is a stereotype annotation. It is a specialization of the @Component
class and allows implementation classes to be auto-detected. It is a part of the Spring MVC framework and typically used on request handling methods along with a @RequestMapping
annotation. It returns a ModelAndView
object.
In this step, I will create a WelcomeController
class which provides four methods:
welcome
– maps to the web application root context ("/"
) and returns thehome
view.user
– maps to the web application user context ("/user"
) and returns theuser
view for a valid user andinvalidUser
view for an invalid user.setup
– maps to the web application context ("/loadTestData"
).exceptionHandler
– annotates with@ExceptionHandler
for any unexpected exceptions.
WelcomeController.java
package jcg.zheng.demo.spring; import java.math.BigDecimal; import java.text.DateFormat; import java.util.Date; import java.util.Locale; import java.util.Map; import javax.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import jcg.zheng.demo.spring.entity.Account; import jcg.zheng.demo.spring.model.User; import jcg.zheng.demo.spring.repository.AccountRepository; import jcg.zheng.demo.spring.service.AccountService; import jcg.zheng.demo.spring.util.TestData; @Controller public class WelcomeController { @Autowired private AccountService accountService; @Autowired private AccountRepository acctRep; // inject via application.properties @Value("${welcome.message:test}") private String message = "Hello World"; @RequestMapping("/") public String welcome(Locale locale, Map model) { model.put("message", this.message); System.out.println("Home Page Requested, locale = " + locale); Date date = new Date(); DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale); String formattedDate = dateFormat.format(date); model.put("serverTime", formattedDate); return "home"; } @RequestMapping("/loadTestData") public String setup() { Account maryZheng = TestData.createDummy("maryZheng", "Mary", "Zheng"); maryZheng.addTransactions(TestData.createTransaction("KOHL", "Birthday gifts", new BigDecimal(300))); maryZheng.addTransactions(TestData.createTransaction("Macy", "Allen clothes", new BigDecimal(100))); maryZheng.addTransactions(TestData.createTransaction("Home Depot", "house items", new BigDecimal(1000))); maryZheng.addTransactions(TestData.createTransaction("Wal-mart", "small items", new BigDecimal(60))); acctRep.save(maryZheng); Account demo = TestData.createDummy("demo", "Demo", "JCG"); demo.addTransactions(TestData.createTransaction("Shop&Save", "food items", new BigDecimal(60))); demo.addTransactions(TestData.createTransaction("Webster", "school supplies", new BigDecimal(260))); acctRep.save(demo); return "home"; } @RequestMapping(value = "/user", method = RequestMethod.POST) public String user(@Validated User user, Model model) { System.out.println("User Page Requested"); model.addAttribute("userName", user.getUserName()); Account foundUser = accountService.findByUsername(user.getUserName()); if (foundUser != null) { model.addAttribute("account", foundUser); return "user"; } else { return "invalidUser"; } } @ExceptionHandler(Exception.class) public String exceptionHandler(HttpServletRequest request, Exception ex, Model model) { model.addAttribute("content", request.getRequestURL()); model.addAttribute("error", ex.getMessage()); return "error"; } }
Note: All methods return String
instead of ModelAndView
for easier test. Spring will wrap it as a ModelAndView
object with the matching view name.
5.3 Model
Spring MVC framework provides the Model
object to allow the controller class and UI view to pass the data between them. In this example, the User
class contains the Account
object which includes a list of RewardTransaction
objects.
5.3.1 User
User
class contains a user name and an account object.
User.java
package jcg.zheng.demo.spring.model; import jcg.zheng.demo.spring.entity.Account; public class User { private String userName; private Account account; public Account getAccount() { return account; } public void setAccount(Account account) { this.account = account; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } }
5.3.2 Account
Account
class includes the first name, last name, and list of RewardTransaction
.
Account.java
package jcg.zheng.demo.spring.entity; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.OneToMany; import javax.persistence.Table; @Entity @Table(name = "ACCOUNT") public class Account { @Id @GeneratedValue @Column(name = "ID") private Integer id; @Column(name = "First_Name") private String firstname; @Column(name = "Last_Name") private String lastname; private String username; @OneToMany(mappedBy="account", cascade = CascadeType.ALL, fetch = FetchType.EAGER, targetEntity = RewardTransaction.class) private List transactions = new ArrayList(); public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getFirstname() { return firstname; } public void setFirstname(String firstname) { this.firstname = firstname; } public String getLastname() { return lastname; } public void setLastname(String lastname) { this.lastname = lastname; } public BigDecimal getRewardPoint() { return transactions.stream().map(RewardTransaction::getAmount) .reduce(BigDecimal.ZERO, BigDecimal::add); } public List getTransactions() { return transactions; } public void addTransactions(RewardTransaction transaction) { transaction.setAccount(this); this.transactions.add(transaction); } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } }
5.3.3 RewardTransaction
RewardTransaction
includes the transaction date, amount, description, and payee name.
RewardTransaction.java
package jcg.zheng.demo.spring.entity; import java.math.BigDecimal; import java.util.Date; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; @Entity public class RewardTransaction { @Id @GeneratedValue private Long id; private Date createdDate; private BigDecimal amount; private String description; private String payeeName; @ManyToOne @JoinColumn(name = "ACCOUNT_ID") private Account account; public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getPayeeName() { return payeeName; } public void setPayeeName(String payeeName) { this.payeeName = payeeName; } public Date getCreatedDate() { return createdDate; } public void setCreatedDate(Date createdDate) { this.createdDate = createdDate; } public BigDecimal getAmount() { return amount; } public void setAmount(BigDecimal amount) { this.amount = amount; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Account getAccount() { return account; } public void setAccount(Account account) { this.account = account; } }
6. Spring Beans
In this step, I will create several Spring beans to manage the user’s reward account.
6.1 AccountRepository
In this step, I will create an AccountRepository
interface with the findByUserName
method and annotate it with Spring @Repository
stereotype annotation.
AccountRepository.java
package jcg.zheng.demo.spring.repository; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import jcg.zheng.demo.spring.entity.Account; @Repository public interface AccountRepository extends JpaRepository<Account, Integer> { @Query("SELECT acct from Account acct WHERE acct.username = :username") Account findByUserName(@Param("username") String userName); }
6.2 AccountService
AccountService
interface defines the findByUserName
method.
AccountService.java
package jcg.zheng.demo.spring.service; import jcg.zheng.demo.spring.entity.Account; public interface AccountService { Account findByUsername(String username); }
6.3 AccountServiceImpl
AccountServiceImpl
implements AccountService
interface and annotates it with @Service
annotation. It depends the Spring AccountRepository
bean and invokes its findByUserName
method.
AccountServiceImpl.java
package jcg.zheng.demo.spring.service.impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import jcg.zheng.demo.spring.entity.Account; import jcg.zheng.demo.spring.repository.AccountRepository; import jcg.zheng.demo.spring.service.AccountService; @Service public class AccountServiceImpl implements AccountService { @Autowired private AccountRepository acctDao; @Override public Account findByUsername(String username) { return acctDao.findByUserName(username); } }
6.4 Java Configuration
The generated SpringBootWebApplication
is annotated with @SpringBootApplication
. It is equivalent to use @Configuration
, @EnableAutoConfiguration
, and @ComponentScan
with their default attributes.
We will annotate it with @EnableJpaRepositories
. It enables Spring to scan the base package and all its sub-packages for any interfaces extending Repository
or one of its sub-interfaces. For each interface found, Spring creates the appropriate bean to handle invocation of the query methods.
SpringBootWebApplication.java
package jcg.zheng.demo.spring; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.web.support.SpringBootServletInitializer; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; @SpringBootApplication @EnableJpaRepositories(basePackages = "jcg.zheng.demo.spring") public class SpringBootWebApplication extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { return application.sources(SpringBootWebApplication.class); } public static void main(String[] args) throws Exception { SpringApplication.run(SpringBootWebApplication.class, args); } }
7. Junit Tests
7.1 AccountTest
we will test the Account
class’ getRewardPoint
method which is used in the user
view.
AccountTest.java
package jcg.zheng.demo.spring.entity; import static org.junit.Assert.assertEquals; import java.math.BigDecimal; import org.junit.Test; import jcg.zheng.demo.spring.util.TestData; public class AccountTest { @Test public void test_Account_getRewardPoint() { Account acct = TestData.createDummy("test", "firstNm", "lastNm"); acct.addTransactions(TestData.createTransaction("payee", "desp", new BigDecimal(10))); acct.addTransactions(TestData.createTransaction("payee2", "desp2", new BigDecimal(15))); assertEquals(25, acct.getRewardPoint().intValue()); } }
7.2 AccountServiceImplTest
We will test AccountServiceImp
to make sure findByUserName
works as expected.
AccountServiceImplTest.java
package jcg.zheng.demo.spring.service.impl; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import jcg.zheng.demo.spring.TestConfig; import jcg.zheng.demo.spring.entity.Account; import jcg.zheng.demo.spring.repository.AccountRepository; import jcg.zheng.demo.spring.service.AccountService; import jcg.zheng.demo.spring.util.TestData; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = { TestConfig.class }) public class AccountServiceImplTest { @Autowired private AccountService classToTest; @Autowired private AccountRepository acctRepo; @Test public void findByUsername_not_found() { Account foundAcct = classToTest.findByUsername("testUser"); assertNull(foundAcct); } @Test public void findByUsername_found() { acctRepo.save(TestData.createDummy("MaryZheng", "Mary", "Zheng")); Account foundAcct = classToTest.findByUsername("MaryZheng"); assertNotNull(foundAcct); assertEquals(1, foundAcct.getId().intValue()); } }
7.3 WelcomeControllerTest
We will test the WelcomeController
.
WelcomeControllerTest.java
package jcg.zheng.demo.spring; import static org.junit.Assert.assertEquals; import java.util.HashMap; import java.util.Locale; import java.util.Map; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.ui.Model; import org.springframework.validation.support.BindingAwareModelMap; import jcg.zheng.demo.spring.model.User; import jcg.zheng.demo.spring.service.AccountService; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = { TestConfig.class }) public class WelcomeControllerTest { @Autowired private WelcomeController welController; @Autowired private AccountService accountService; @Test public void welcome_view() { Map model= new HashMap(); Locale locale = new Locale.Builder().setLanguage("en").setRegion("MO").build(); String viewName = welController.welcome(locale, model); assertEquals("home", viewName); } @Test public void invalidUser_view() { User user = new User(); user.setUserName("not exist"); Model model = new BindingAwareModelMap(); String viewName = welController.user(user , model ); assertEquals("invalidUser", viewName); } }
8. Demo
Now, we completed the web application. Here is the project structure.
We will demonstrate the web application to show it meets the business requirements.
8.1 Start the Web Application
First, start the web application with command java -jar target\spring-boot-web-jsp-demo-0.0.1-SNAPSHOT.jar
.
Confirmed the web application is up and running by viewing the server output.
Server output
C:\gitworkspace\spring-boot-web-jsp-demo>java -jar target\spring-boot-web-jsp-demo-0.0.1-SNAPSHOT.jar . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v1.5.14.RELEASE) 2018-06-29 15:22:43.448 INFO 19076 --- [ main] j.z.d.spring.SpringBootWebApplication : Starting SpringBootWebApplication v0.0.1-SNAPSHOT on SL2LS431841 with PID 19076 (C:\gitworkspace\spring-boot-web-jsp-demo\target\spring-boot-web-jsp-demo-0.0.1-SNAPSHOT.jar started by shu.shan in C:\gitworkspace\spring-boot-web-jsp-demo) 2018-06-29 15:22:43.448 INFO 19076 --- [ main] j.z.d.spring.SpringBootWebApplication : No active profile set, falling back to default profiles: default 2018-06-29 15:22:43.698 INFO 19076 --- [ main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@1698c449: startup date [Fri Jun 29 15:22:43 CDT 2018]; root of context hierarchy 2018-06-29 15:22:46.142 INFO 19076 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration' of type [org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration$EnhancerBySpringCGLIB$5edb6f43] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying) 2018-06-29 15:22:47.282 INFO 19076 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 8080 (http) 2018-06-29 15:22:47.345 INFO 19076 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2018-06-29 15:22:47.345 INFO 19076 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/8.5.31 2018-06-29 15:22:48.585 INFO 19076 --- [ost-startStop-1] org.apache.jasper.servlet.TldScanner : At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time. 2018-06-29 15:22:48.960 INFO 19076 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2018-06-29 15:22:48.960 INFO 19076 --- [ost-startStop-1] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 5262 ms 2018-06-29 15:22:49.256 INFO 19076 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean : Mapping servlet: 'dispatcherServlet' to [/] 2018-06-29 15:22:49.272 INFO 19076 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'characterEncodingFilter' to: [/*] 2018-06-29 15:22:49.272 INFO 19076 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'hiddenHttpMethodFilter' to: [/*] 2018-06-29 15:22:49.272 INFO 19076 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'httpPutFormContentFilter' to: [/*] 2018-06-29 15:22:49.272 INFO 19076 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'requestContextFilter' to: [/*] 2018-06-29 15:22:49.397 INFO 19076 --- [ main] o.s.j.d.e.EmbeddedDatabaseFactory : Starting embedded database: url='jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false', username='sa' 2018-06-29 15:22:50.054 INFO 19076 --- [ main] j.LocalContainerEntityManagerFactoryBean : Building JPA container EntityManagerFactory for persistence unit 'default' 2018-06-29 15:22:50.085 INFO 19076 --- [ main] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [ name: default ...] 2018-06-29 15:22:50.319 INFO 19076 --- [ main] org.hibernate.Version : HHH000412: Hibernate Core {5.0.12.Final} 2018-06-29 15:22:50.319 INFO 19076 --- [ main] org.hibernate.cfg.Environment : HHH000206: hibernate.properties not found 2018-06-29 15:22:50.335 INFO 19076 --- [ main] org.hibernate.cfg.Environment : HHH000021: Bytecode provider name : javassist 2018-06-29 15:22:50.475 INFO 19076 --- [ main] o.hibernate.annotations.common.Version : HCANN000001: Hibernate Commons Annotations {5.0.1.Final} 2018-06-29 15:22:51.159 INFO 19076 --- [ main] org.hibernate.dialect.Dialect : HHH000400: Using dialect: org.hibernate.dialect.H2Dialect 2018-06-29 15:22:52.874 INFO 19076 --- [ main] org.hibernate.tool.hbm2ddl.SchemaUpdate : HHH000228: Running hbm2ddl schema update 2018-06-29 15:22:52.921 INFO 19076 --- [ main] rmationExtractorJdbcDatabaseMetaDataImpl : HHH000262: Table not found: ACCOUNT 2018-06-29 15:22:52.937 INFO 19076 --- [ main] rmationExtractorJdbcDatabaseMetaDataImpl : HHH000262: Table not found: ACCOUNT 2018-06-29 15:22:52.952 INFO 19076 --- [ main] rmationExtractorJdbcDatabaseMetaDataImpl : HHH000262: Table not found: RewardTransaction 2018-06-29 15:22:52.952 INFO 19076 --- [ main] rmationExtractorJdbcDatabaseMetaDataImpl : HHH000262: Table not found: RewardTransaction 2018-06-29 15:22:53.140 INFO 19076 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default' 2018-06-29 15:22:53.693 INFO 19076 --- [ main] o.h.h.i.QueryTranslatorFactoryInitiator : HHH000397: Using ASTQueryTranslatorFactory 2018-06-29 15:22:54.609 INFO 19076 --- [ main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@1698c449: startup date [Fri Jun 29 15:22:43 CDT 2018]; root of context hierarchy 2018-06-29 15:22:54.875 INFO 19076 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/loadTestData]}" onto public java.lang.String jcg.zheng.demo.spring.WelcomeController.setup() 2018-06-29 15:22:54.875 INFO 19076 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/user],methods=[POST]}" onto public java.lang.String jcg.zheng.demo.spring.WelcomeController.user(jcg.zheng.demo.spring.model.User,org.springframework.ui.Model) 2018-06-29 15:22:54.875 INFO 19076 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/]}" onto public java.lang.String jcg.zheng.demo.spring.WelcomeController.welcome(java.util.Locale,java.util.Map) 2018-06-29 15:22:54.875 INFO 19076 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest) 2018-06-29 15:22:54.875 INFO 19076 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse) 2018-06-29 15:22:55.015 INFO 19076 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler] 2018-06-29 15:22:55.389 INFO 19076 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup 2018-06-29 15:22:55.514 INFO 19076 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http) 2018-06-29 15:22:55.514 INFO 19076 --- [ main] j.z.d.spring.SpringBootWebApplication : Started SpringBootWebApplication in 12.926 seconds (JVM running for 13.906)
8.2 Home View
Go to http://localhost:8080/
, which displays the landing page.
8.3 Invalid User View
Enter maryZheng
and click the login
button.
It displays the invalid user page.
8.4 User View
Go to http://localhost:8080/loadTestData
.
Go to http://localhost:8080/
and enter maryZheng
and click the login
button.
It displays the Mary Zheng’s account.
8.5 Error View
Go to http://localhost:8080/loadTestData
, it will load the same data again.
Go to http://localhost:8080/
and enter maryZheng
and click the login
button.
It displays the exception page.
9. Summary
In this example, we created a Spring MVC web application utilizing the Spring boot, Spring JPA, and Spring MVC framework in a few short steps.
- Generate the Spring boot MVC project via the starter tool
- Add
Account
andUser
model classes - Add the JSP view pages
- Create a
Controller
for the views - Create Spring
AccountService
andAccountRepository
beans to manage the account
10. References
- https://stackoverflow.com/questions/7175509/which-is-better-return-modelandview-or-string-on-spring3-controller
- http://www.mkyong.com/spring-boot/spring-boot-hello-world-example-jsp/
- https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
- https://www.logicbig.com/tutorials/spring-framework/spring-boot/boot-serve-static.html
- http://www.springboottutorial.com/spring-boot-and-h2-in-memory-database
- https://www.journaldev.com/14476/spring-mvc-example
11. Download the Source Code
This example consists of a Spring MVC Web application as a Maven project.
You can download the full source code of the Spring with Maven Tutorial: Spring with Maven Tutorial