diff --git a/pom.xml b/pom.xml
index d1b27bc9c9..e6e11326d7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -122,6 +122,7 @@
solr
spark-java
spring-5
+ spring-5-mvc
spring-akka
spring-amqp
spring-all
diff --git a/spring-5-mvc/.gitignore b/spring-5-mvc/.gitignore
new file mode 100644
index 0000000000..b7b5c734f2
--- /dev/null
+++ b/spring-5-mvc/.gitignore
@@ -0,0 +1,20 @@
+*.class
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+.settings/
+.classpath
+.project
+target/
+
+*.iml
+.idea
+
+# Package Files #
+*.jar
+*.war
+*.ear
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
diff --git a/spring-5-mvc/pom.xml b/spring-5-mvc/pom.xml
new file mode 100644
index 0000000000..b8c341afde
--- /dev/null
+++ b/spring-5-mvc/pom.xml
@@ -0,0 +1,154 @@
+
+
+ 4.0.0
+
+ com.baeldung
+ spring-5-mvc
+ 0.0.1-SNAPSHOT
+ jar
+
+ spring-5-mvc
+ spring 5 MVC sample project about new features
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.0.0.M1
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+
+
+ org.apache.commons
+ commons-lang3
+
+
+ org.slf4j
+ slf4j-api
+
+
+ org.slf4j
+ jcl-over-slf4j
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-devtools
+ runtime
+
+
+ com.h2database
+ h2
+ runtime
+
+
+
+ org.springframework
+ spring-test
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+ junit
+ junit
+ test
+
+
+
+ com.jayway.restassured
+ rest-assured
+ ${rest-assured.version}
+ test
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+ true
+ false
+
+ **/*IntegrationTest.java
+ **/*LiveTest.java
+
+
+
+
+
+
+
+
+
+ spring-snapshots
+ Spring Snapshots
+ https://repo.spring.io/snapshot
+
+ true
+
+
+
+ spring-milestones
+ Spring Milestones
+ https://repo.spring.io/milestone
+
+ false
+
+
+
+
+
+ spring-snapshots
+ Spring Snapshots
+ https://repo.spring.io/snapshot
+
+ true
+
+
+
+ spring-milestones
+ Spring Milestones
+ https://repo.spring.io/milestone
+
+ false
+
+
+
+
+
+ 2.9.0
+ UTF-8
+ UTF-8
+ 1.8
+
+
+
diff --git a/spring-5-mvc/src/main/java/com/baeldung/Spring5Application.java b/spring-5-mvc/src/main/java/com/baeldung/Spring5Application.java
new file mode 100644
index 0000000000..41b5c1eed1
--- /dev/null
+++ b/spring-5-mvc/src/main/java/com/baeldung/Spring5Application.java
@@ -0,0 +1,13 @@
+package com.baeldung;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Spring5Application {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Spring5Application.class, args);
+ }
+
+}
diff --git a/spring-5-mvc/src/main/java/com/baeldung/model/Foo.java b/spring-5-mvc/src/main/java/com/baeldung/model/Foo.java
new file mode 100644
index 0000000000..a9ffee14da
--- /dev/null
+++ b/spring-5-mvc/src/main/java/com/baeldung/model/Foo.java
@@ -0,0 +1,84 @@
+package com.baeldung.model;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+
+@Entity
+public class Foo {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.AUTO)
+ private Long id;
+
+ private String name;
+
+ public Foo() {
+ super();
+ }
+
+ public Foo(final String name) {
+ super();
+
+ this.name = name;
+ }
+
+ public Foo(final long id, final String name) {
+ super();
+
+ this.id = id;
+ this.name = name;
+ }
+
+ // API
+
+ public String getName() {
+ return name;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public void setName(final String name) {
+ this.name = name;
+ }
+
+ //
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Foo other = (Foo) obj;
+ if (name == null) {
+ if (other.name != null)
+ return false;
+ } else if (!name.equals(other.name))
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "Foo [name=" + name + "]";
+ }
+
+}
\ No newline at end of file
diff --git a/spring-5-mvc/src/main/java/com/baeldung/persistence/DataSetupBean.java b/spring-5-mvc/src/main/java/com/baeldung/persistence/DataSetupBean.java
new file mode 100644
index 0000000000..cf78977961
--- /dev/null
+++ b/spring-5-mvc/src/main/java/com/baeldung/persistence/DataSetupBean.java
@@ -0,0 +1,26 @@
+package com.baeldung.persistence;
+
+import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
+
+import java.util.stream.IntStream;
+
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import com.baeldung.model.Foo;
+
+@Component
+public class DataSetupBean implements InitializingBean {
+
+ @Autowired
+ private FooRepository repo;
+
+ //
+
+ @Override
+ public void afterPropertiesSet() throws Exception {
+ IntStream.range(1, 5).forEach(i -> repo.save(new Foo(randomAlphabetic(8))));
+ }
+
+}
diff --git a/spring-5-mvc/src/main/java/com/baeldung/persistence/FooRepository.java b/spring-5-mvc/src/main/java/com/baeldung/persistence/FooRepository.java
new file mode 100644
index 0000000000..3c70f38fce
--- /dev/null
+++ b/spring-5-mvc/src/main/java/com/baeldung/persistence/FooRepository.java
@@ -0,0 +1,10 @@
+package com.baeldung.persistence;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+
+import com.baeldung.model.Foo;
+
+public interface FooRepository extends JpaRepository, JpaSpecificationExecutor {
+
+}
diff --git a/spring-5-mvc/src/main/java/com/baeldung/web/FooController.java b/spring-5-mvc/src/main/java/com/baeldung/web/FooController.java
new file mode 100644
index 0000000000..d03cebb4fd
--- /dev/null
+++ b/spring-5-mvc/src/main/java/com/baeldung/web/FooController.java
@@ -0,0 +1,66 @@
+package com.baeldung.web;
+
+import java.util.List;
+
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.http.HttpStatus;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.server.ResponseStatusException;
+
+import com.baeldung.model.Foo;
+import com.baeldung.persistence.FooRepository;
+
+@RestController("/foos")
+public class FooController {
+
+ @Autowired
+ private FooRepository repo;
+
+ // API - read
+
+ @GetMapping("/foos/{id}")
+ @Validated
+ public Foo findById(@PathVariable @Min(0) final long id) {
+ return repo.findById(id).orElse(null);
+ }
+
+ @GetMapping
+ public List findAll() {
+ return repo.findAll();
+ }
+
+ @GetMapping(params = { "page", "size" })
+ @Validated
+ public List findPaginated(@RequestParam("page") @Min(0) final int page, @Max(100) @RequestParam("size") final int size) {
+ return repo.findAll(PageRequest.of(page, size)).getContent();
+ }
+
+ // API - write
+
+ @PutMapping("/foos/{id}")
+ @ResponseStatus(HttpStatus.OK)
+ public Foo update(@PathVariable("id") final String id, @RequestBody final Foo foo) {
+ return foo;
+ }
+
+ @PostMapping
+ @ResponseStatus(HttpStatus.CREATED)
+ public void create( @RequestBody final Foo foo) {
+ if (null == foo || null == foo.getName()) {
+ throw new ResponseStatusException(HttpStatus.BAD_REQUEST," 'name' is required");
+ }
+ repo.save(foo);
+ }
+}
diff --git a/spring-5-mvc/src/main/resources/application.properties b/spring-5-mvc/src/main/resources/application.properties
new file mode 100644
index 0000000000..886ea1978b
--- /dev/null
+++ b/spring-5-mvc/src/main/resources/application.properties
@@ -0,0 +1,6 @@
+server.port=8081
+
+security.user.name=user
+security.user.password=pass
+
+logging.level.root=INFO
\ No newline at end of file
diff --git a/spring-5-mvc/src/main/webapp/WEB-INF/web.xml b/spring-5-mvc/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000000..bfcf43dad2
--- /dev/null
+++ b/spring-5-mvc/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,21 @@
+
+
+
+ Spring Functional Application
+
+
+ functional
+ com.baeldung.functional.RootServlet
+ 1
+ true
+
+
+ functional
+ /
+
+
+
+
\ No newline at end of file
diff --git a/spring-5-mvc/src/test/java/com/baeldung/test/LiveTest.java b/spring-5-mvc/src/test/java/com/baeldung/test/LiveTest.java
new file mode 100644
index 0000000000..637913541c
--- /dev/null
+++ b/spring-5-mvc/src/test/java/com/baeldung/test/LiveTest.java
@@ -0,0 +1,54 @@
+package com.baeldung.test;
+
+import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.springframework.http.MediaType;
+
+import com.jayway.restassured.RestAssured;
+import com.jayway.restassured.response.Response;
+import com.jayway.restassured.specification.RequestSpecification;
+
+public class LiveTest {
+
+ private static String APP_ROOT = "http://localhost:8081";
+
+
+ @Test
+ public void givenUser_whenPostWithNullName_then400BadRequest() {
+ final Response response = givenAuth("user", "pass").contentType(MediaType.APPLICATION_JSON.toString()).body(resourceWithNullName()).post(APP_ROOT + "/user");
+ assertEquals(400, response.getStatusCode());
+ }
+
+ @Test
+ public void givenUser_whenResourceCreated_then201Created() {
+ final Response response = givenAuth("user", "pass").contentType(MediaType.APPLICATION_JSON.toString()).body(resourceString()).post(APP_ROOT + "/user");
+ assertEquals(201, response.getStatusCode());
+ }
+
+ /*@Test
+ public void givenUser_whenGetAllFoos_thenOK() {
+ final Response response = givenAuth("user", "pass").get(APP_ROOT + "/foos");
+ assertEquals(200, response.getStatusCode());
+ }*/
+
+
+
+ //
+
+ private final String resourceWithNullName() {
+ final String roleData = "{\"name\":null}";
+ return roleData;
+ }
+
+ private final String resourceString() {
+ final String roleData = "{\"name\":\"" + randomAlphabetic(8) + "\"}";
+ return roleData;
+ }
+
+ private final RequestSpecification givenAuth(String username, String password) {
+ return RestAssured.given().auth().preemptive().basic(username, password);
+ }
+
+}
\ No newline at end of file
diff --git a/spring-5-mvc/src/test/java/com/baeldung/test/Spring5ApplicationIntegrationTest.java b/spring-5-mvc/src/test/java/com/baeldung/test/Spring5ApplicationIntegrationTest.java
new file mode 100644
index 0000000000..c3790333ff
--- /dev/null
+++ b/spring-5-mvc/src/test/java/com/baeldung/test/Spring5ApplicationIntegrationTest.java
@@ -0,0 +1,18 @@
+package com.baeldung.test;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import com.baeldung.Spring5Application;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = Spring5Application.class)
+public class Spring5ApplicationIntegrationTest {
+
+ @Test
+ public void contextLoads() {
+ }
+
+}
diff --git a/spring-5-mvc/src/test/resources/baeldung-weekly.png b/spring-5-mvc/src/test/resources/baeldung-weekly.png
new file mode 100644
index 0000000000..5a27d61dae
Binary files /dev/null and b/spring-5-mvc/src/test/resources/baeldung-weekly.png differ