├── .gitignore
├── README.md
├── account-jsp
├── README.MD
├── pom.xml
└── src
│ ├── main
│ ├── java
│ │ └── com
│ │ │ └── bobocode
│ │ │ ├── config
│ │ │ ├── AccountWebAppInitializer.java
│ │ │ ├── RootConfig.java
│ │ │ └── WebConfig.java
│ │ │ └── web
│ │ │ └── controller
│ │ │ ├── AccountController.java
│ │ │ └── WelcomeController.java
│ └── webapp
│ │ └── WEB-INF
│ │ └── views
│ │ ├── accounts.jsp
│ │ └── welcome.jsp
│ └── test
│ └── java
│ └── com
│ └── bobocode
│ ├── AccountControllerTest.java
│ ├── WebAppConfigTest.java
│ ├── WelcomeControllerTest.java
│ └── util
│ └── WebAppInitializerWrapper.java
├── account-rest-api
├── README.MD
├── pom.xml
└── src
│ ├── main
│ └── java
│ │ └── com
│ │ └── bobocode
│ │ ├── config
│ │ ├── AccountRestApiInitializer.java
│ │ ├── RootConfig.java
│ │ └── WebConfig.java
│ │ ├── dao
│ │ ├── AccountDao.java
│ │ └── impl
│ │ │ └── InMemoryAccountDao.java
│ │ ├── exception
│ │ └── EntityNotFountException.java
│ │ └── web
│ │ └── controller
│ │ └── AccountRestController.java
│ └── test
│ └── java
│ └── com
│ └── bobocode
│ └── AccountRestControllerTest.java
├── hello-application-context
├── README.MD
├── pom.xml
└── src
│ ├── main
│ └── java
│ │ └── com
│ │ └── bobocode
│ │ ├── config
│ │ └── AppConfig.java
│ │ ├── dao
│ │ ├── AccountDao.java
│ │ └── FakeAccountDao.java
│ │ └── service
│ │ └── AccountService.java
│ └── test
│ └── java
│ └── com
│ └── bobocode
│ └── AppConfigTest.java
├── hello-spring-mvc
├── README.MD
├── pom.xml
└── src
│ ├── main
│ └── java
│ │ └── com
│ │ └── bobocode
│ │ ├── config
│ │ ├── RootConfig.java
│ │ ├── WebAppInitializer.java
│ │ └── WebConfig.java
│ │ └── web
│ │ └── controller
│ │ └── WelcomeController.java
│ └── test
│ └── java
│ └── com
│ └── bobocode
│ ├── WebAppInitializerWrapper.java
│ └── WelcomeWebAppTest.java
├── inter-bean-dependencies
├── README.MD
├── pom.xml
└── src
│ ├── main
│ └── java
│ │ └── com
│ │ └── bobocode
│ │ ├── config
│ │ └── RootConfig.java
│ │ ├── dao
│ │ ├── AccountDao.java
│ │ └── impl
│ │ │ └── FakeAccountDao.java
│ │ └── service
│ │ └── AccountService.java
│ └── test
│ └── java
│ └── com
│ └── bobocode
│ ├── RootConfigContextTest.java
│ └── RootConfigHacksTest.java
├── pom.xml
├── spring-framework-exercises-model
├── pom.xml
└── src
│ └── main
│ └── java
│ └── com
│ └── bobocode
│ └── model
│ ├── Account.java
│ ├── Gender.java
│ └── jpa
│ ├── Role.java
│ ├── RoleType.java
│ └── User.java
├── spring-framework-exercises-util
├── pom.xml
└── src
│ └── main
│ └── java
│ └── com
│ └── bobocode
│ └── TestDataGenerator.java
└── transactional-user-service
├── README.MD
├── pom.xml
└── src
├── main
└── java
│ └── com
│ └── bobocode
│ ├── config
│ ├── JpaConfig.java
│ └── RootConfig.java
│ ├── dao
│ ├── UserDao.java
│ └── impl
│ │ └── JpaUserDao.java
│ └── service
│ └── UserService.java
└── test
└── java
└── com
└── bobocode
└── TransactionalUserServiceTest.java
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 | .idea
3 | **/*.iml
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Spring framework exercises
2 | The list of exercises dedicated to training your Spring framework related skills
3 |
4 | ### No pain, No gain :heavy_exclamation_mark:
5 |
6 | > Skill is only developed by hours and hours and hours of beating on your craft
7 |
8 | Working on real problems, you're focused on finding a solution. Learning new things, you're trying to understand how it works.
9 | It is important to have a different type of activities, which purpose is improving your skill
10 |
11 | ***An exercise** is a predefined task that you continuously implement to improve a certain skill* :muscle:
12 | ##
13 | * [Hello ApplicationContext](https://github.com/boy4uck/spring-framework-exercises/tree/master/hello-application-context#hello-applicationcontext-exercise-muscle)
14 | * [Hello Spring MVC](https://github.com/boy4uck/spring-framework-exercises/tree/master/hello-spring-mvc#hello-spring-mvc-exercise-muscle)
15 | * [Account JSP](https://github.com/bobocode-projects/spring-framework-exercises/tree/master/account-jsp)
16 | * [Account REST API](https://github.com/bobocode-projects/spring-framework-exercises/tree/master/account-rest-api#account-rest-api-exercise-muscle)
17 | * [Transactional User Service](https://github.com/bobocode-projects/spring-framework-exercises/tree/master/transactional-user-service#transactional-userservice-exercise-muscle)
18 |
--------------------------------------------------------------------------------
/account-jsp/README.MD:
--------------------------------------------------------------------------------
1 | #
Accounts JSP exercise :muscle:
2 | Improve your *Spring MVC* configuration and controller mapping skills
3 | ### Task
4 | This is a typical *Spring MVC* application with *JSP pages*. Your job is to **configure dispatcher servlet initializer**
5 | separating root and web contexts, **configure `ViewResolver`** for *JSP* views, and **implement two controllers
6 | `WelcomeController` and `AccountController`**
7 |
8 | To verify your configuration, run all tests from test package of this module
9 |
10 |
11 | ### Pre-conditions :heavy_exclamation_mark:
12 | You're supposed to be familiar with *Spring MVC*
13 |
14 | ### How to start :question:
15 | * Just clone the repository and start implementing the **todo** section, verify your changes by running tests
16 | * If you don't have enough knowledge about this domain, check out the [links below](#related-materials-information_source)
17 | * Don't worry if you got stuck, checkout the **exercise/completed** branch and see the final implementation
18 |
19 | ### Related materials :information_source:
20 | * [Spring MVC basics tutorial](https://github.com/boy4uck/spring-framework-tutorial/tree/master/spring-framework-mvc-basics)
21 |
22 |
--------------------------------------------------------------------------------
/account-jsp/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 | * todo: provide default servlet mapping ("/") 9 | * todo: use {@link WebConfig} as ServletConfig class 10 | * todo: use {@link RootConfig} as root application config class 11 | */ 12 | public class AccountWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { 13 | @Override 14 | protected Class>[] getRootConfigClasses() { 15 | throw new UnsupportedOperationException("It's your job to implement this method!"); 16 | } 17 | 18 | @Override 19 | protected Class>[] getServletConfigClasses() { 20 | throw new UnsupportedOperationException("It's your job to implement this method!"); 21 | } 22 | 23 | @Override 24 | protected String[] getServletMappings() { 25 | throw new UnsupportedOperationException("It's your job to implement this method!"); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /account-jsp/src/main/java/com/bobocode/config/RootConfig.java: -------------------------------------------------------------------------------- 1 | package com.bobocode.config; 2 | 3 | import com.bobocode.TestDataGenerator; 4 | import org.springframework.stereotype.Controller; 5 | import org.springframework.web.servlet.config.annotation.EnableWebMvc; 6 | 7 | /** 8 | * This class provides application root (non-web) configuration. 9 | *
10 | * todo: 1. Mark this class as config 11 | * todo: 2. Enable component scanning for all packages in "com.bobocode" using annotation property "basePackages" 12 | * todo: 3. Exclude web related config and beans (ignore @{@link Controller}, ignore {@link EnableWebMvc}) 13 | * todo: 4. Configure {@link TestDataGenerator} bean with name "dataGenerator" (don't specify name explicitly) 14 | */ 15 | public class RootConfig { 16 | } 17 | -------------------------------------------------------------------------------- /account-jsp/src/main/java/com/bobocode/config/WebConfig.java: -------------------------------------------------------------------------------- 1 | package com.bobocode.config; 2 | 3 | /** 4 | * This class provides web (servlet) related configuration. 5 | *
6 | * todo: 1. Mark this class as Spring config class 7 | * todo: 2. Enable web mvc using annotation 8 | * todo: 3. Enable component scanning for package "web" using annotation value 9 | * todo: 4. Configure JPS internal view resolver with prefix = "/WEB-INF/views/" and suffix ".jsp" 10 | */ 11 | public class WebConfig { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /account-jsp/src/main/java/com/bobocode/web/controller/AccountController.java: -------------------------------------------------------------------------------- 1 | package com.bobocode.web.controller; 2 | 3 | import com.bobocode.TestDataGenerator; 4 | import com.bobocode.model.Account; 5 | 6 | /** 7 | * This controller provides endpoint that generates a list of {@link Account} and passes it to the view. 8 | *
9 | * todo: 1. Configure controller that handles requests with url "/accounts" 10 | * todo: 2. Inject a {@link TestDataGenerator} 11 | * todo: 3. Implement a method that handles GET request with param "size" to url "/accounts" and forwards it to the accounts.jsp view 12 | * todo: 4. In this method generate a list of account using data generator and received int value "size" 13 | * todo: 5. Provide a default value "10" for parameter "size" 14 | * todo: 6. Pass the list of accounts to the view using model attribute with name "accountList" 15 | */ 16 | public class AccountController { 17 | 18 | } 19 | -------------------------------------------------------------------------------- /account-jsp/src/main/java/com/bobocode/web/controller/WelcomeController.java: -------------------------------------------------------------------------------- 1 | package com.bobocode.web.controller; 2 | 3 | /** 4 | * Welcome controller that consists of one method that handles get request to "/" and "/welcome" and respond with a message. 5 | *
6 | * todo: 1. Mark this class as Spring controller 7 | * todo: 2. Configure HTTP GET mapping "/" and "/welcome" for method {@link WelcomeController#welcome()} 8 | * todo: 3. Forward the request to "welcome.jsp" view 9 | */ 10 | public class WelcomeController { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /account-jsp/src/main/webapp/WEB-INF/views/accounts.jsp: -------------------------------------------------------------------------------- 1 | <%@ page contentType="text/html;charset=UTF-8" language="java" %> 2 | 3 |
4 | 5 |Got ${accountList.size()} accounts
9 | 10 | 11 | -------------------------------------------------------------------------------- /account-jsp/src/main/webapp/WEB-INF/views/welcome.jsp: -------------------------------------------------------------------------------- 1 | <%@ page contentType="text/html;charset=UTF-8" language="java" %> 2 | 3 | 4 | 5 |9 | * todo: 1. Mark this class as config 10 | * todo: 2. Enable component scanning for all packages in "com.bobocode" using annotation property "basePackages" 11 | * todo: 3. Exclude web related config and beans (ignore @{@link Controller}, ignore {@link EnableWebMvc}) 12 | */ 13 | public class RootConfig { 14 | } 15 | -------------------------------------------------------------------------------- /account-rest-api/src/main/java/com/bobocode/config/WebConfig.java: -------------------------------------------------------------------------------- 1 | package com.bobocode.config; 2 | 3 | /** 4 | * This class provides web (servlet) related configuration. 5 | *
6 | * todo: 1. Mark this class as Spring config class
7 | * todo: 2. Enable web mvc using annotation
8 | * todo: 3. Enable component scanning for package "web" using annotation value
9 | */
10 | public class WebConfig {
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/account-rest-api/src/main/java/com/bobocode/dao/AccountDao.java:
--------------------------------------------------------------------------------
1 | package com.bobocode.dao;
2 |
3 | import com.bobocode.model.Account;
4 |
5 | import java.util.List;
6 |
7 | public interface AccountDao {
8 | List
15 | * todo: 1. Configure a component with name "accountDao"
16 | */
17 | public class InMemoryAccountDao implements AccountDao {
18 | private Map
7 | * todo: 1. Configure rest controller that handles requests with url "/accounts"
8 | * todo: 2. Inject {@link AccountDao} implementation
9 | * todo: 3. Implement method that handles GET request and returns a list of accounts
10 | * todo: 4. Implement method that handles GET request with id as path variable and returns account by id
11 | * todo: 5. Implement method that handles POST request, receives account as request body, saves account and returns it
12 | * todo: Configure HTTP response status code 201 - CREATED
13 | * todo: 6. Implement method that handles PUT request with id as path variable and receives account as request body.
14 | * todo: It check if account id and path variable are the same and throws {@link IllegalStateException} otherwise.
15 | * todo: Then it saves received account. Configure HTTP response status code 204 - NO CONTENT
16 | * todo: 7. Implement method that handles DELETE request with id as path variable removes an account by id
17 | * todo: Configure HTTP response status code 204 - NO CONTENT
18 | */
19 | public class AccountRestController {
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/account-rest-api/src/test/java/com/bobocode/AccountRestControllerTest.java:
--------------------------------------------------------------------------------
1 | package com.bobocode;
2 |
3 | import com.bobocode.config.RootConfig;
4 | import com.bobocode.config.WebConfig;
5 | import com.bobocode.dao.impl.InMemoryAccountDao;
6 | import com.bobocode.model.Account;
7 | import com.bobocode.web.controller.AccountRestController;
8 | import org.junit.jupiter.api.BeforeEach;
9 | import org.junit.jupiter.api.Test;
10 | import org.springframework.beans.factory.annotation.Autowired;
11 | import org.springframework.http.MediaType;
12 | import org.springframework.test.context.junit.jupiter.web.SpringJUnitWebConfig;
13 | import org.springframework.test.web.servlet.MockMvc;
14 | import org.springframework.test.web.servlet.setup.MockMvcBuilders;
15 | import org.springframework.web.bind.annotation.RequestMapping;
16 | import org.springframework.web.bind.annotation.RestController;
17 | import org.springframework.web.context.WebApplicationContext;
18 |
19 | import static org.hamcrest.MatcherAssert.assertThat;
20 | import static org.hamcrest.Matchers.arrayContaining;
21 | import static org.hamcrest.Matchers.arrayWithSize;
22 | import static org.hamcrest.Matchers.hasItems;
23 | import static org.hamcrest.core.IsNull.notNullValue;
24 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
25 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
26 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
27 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
28 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
29 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
30 |
31 | @SpringJUnitWebConfig(classes = {RootConfig.class, WebConfig.class})
32 | class AccountRestControllerTest {
33 | @Autowired
34 | private WebApplicationContext applicationContext;
35 |
36 | @Autowired
37 | private InMemoryAccountDao accountDao;
38 |
39 | private MockMvc mockMvc;
40 |
41 | @BeforeEach
42 | void setup() {
43 | mockMvc = MockMvcBuilders.webAppContextSetup(applicationContext).build();
44 | accountDao.clear();
45 | }
46 |
47 | @Test
48 | void testAccountRestControllerAnnotation() {
49 | RestController restController = AccountRestController.class.getAnnotation(RestController.class);
50 |
51 | assertThat(restController, notNullValue());
52 | }
53 |
54 | @Test
55 | void testAccountRestControllerRequestMapping() {
56 | RequestMapping requestMapping = AccountRestController.class.getAnnotation(RequestMapping.class);
57 |
58 | assertThat(requestMapping, notNullValue());
59 | assertThat(requestMapping.value(), arrayWithSize(1));
60 | assertThat(requestMapping.value(), arrayContaining("/accounts"));
61 | }
62 |
63 | @Test
64 | void testHttpStatusCodeOnCreate() throws Exception {
65 | mockMvc.perform(
66 | post("/accounts")
67 | .contentType(MediaType.APPLICATION_JSON)
68 | .content("{\"firstName\":\"Johnny\", \"lastName\":\"Boy\", \"email\":\"jboy@gmail.com\"}"))
69 | .andExpect(status().isCreated());
70 | }
71 |
72 | @Test
73 | void testCreateAccountReturnsAssignedId() throws Exception {
74 | mockMvc.perform(
75 | post("/accounts")
76 | .contentType(MediaType.APPLICATION_JSON)
77 | .content("{\"firstName\":\"Johnny\", \"lastName\":\"Boy\", \"email\":\"jboy@gmail.com\"}"))
78 | .andExpect(jsonPath("$.id").value(1L));
79 | }
80 |
81 | @Test
82 | void testGetAccountsResponseStatusCode() throws Exception {
83 | mockMvc.perform(get("/accounts").accept(MediaType.APPLICATION_JSON_UTF8))
84 | .andExpect(status().isOk());
85 | }
86 |
87 | @Test
88 | void testGetAllAccounts() throws Exception {
89 | Account account1 = create("Johnny", "Boy", "jboy@gmail.com");
90 | Account account2 = create("Okko", "Bay", "obay@gmail.com");
91 | accountDao.save(account1);
92 | accountDao.save(account2);
93 |
94 | mockMvc.perform(get("/accounts"))
95 | .andExpect(status().isOk())
96 | .andExpect(jsonPath("$.[*].email").value(hasItems("jboy@gmail.com", "obay@gmail.com")));
97 | }
98 |
99 | private Account create(String firstName, String lastName, String email) {
100 | Account account = new Account();
101 | account.setFirstName(firstName);
102 | account.setLastName(lastName);
103 | account.setEmail(email);
104 | return account;
105 | }
106 |
107 | @Test
108 | void testGetById() throws Exception {
109 | Account account = create("Johnny", "Boy", "jboy@gmail.com");
110 | accountDao.save(account);
111 |
112 | mockMvc.perform(get(String.format("/accounts/%d", account.getId())))
113 | .andExpect(status().isOk())
114 | .andExpect(jsonPath("$.id").value(account.getId()))
115 | .andExpect(jsonPath("$.email").value("jboy@gmail.com"))
116 | .andExpect(jsonPath("$.firstName").value("Johnny"))
117 | .andExpect(jsonPath("$.lastName").value("Boy"));
118 | }
119 |
120 | @Test
121 | void testRemoveAccount() throws Exception {
122 | Account account = create("Johnny", "Boy", "jboy@gmail.com");
123 | accountDao.save(account);
124 |
125 | mockMvc.perform(delete(String.format("/accounts/%d", account.getId())))
126 | .andExpect(status().isNoContent());
127 | }
128 |
129 | @Test
130 | void testUpdateAccount() throws Exception {
131 | Account account = create("Johnny", "Boy", "jboy@gmail.com");
132 | accountDao.save(account);
133 |
134 | mockMvc.perform(put(String.format("/accounts/%d", account.getId())).contentType(MediaType.APPLICATION_JSON)
135 | .content(String.format("{\"id\":\"%d\", \"firstName\":\"Johnny\", \"lastName\":\"Boy\", \"email\":\"johnny.boy@gmail.com\"}", account.getId())))
136 | .andExpect(status().isNoContent());
137 | }
138 |
139 |
140 | }
141 |
--------------------------------------------------------------------------------
/hello-application-context/README.MD:
--------------------------------------------------------------------------------
1 | #
8 | * todo: make this class a Spring configuration class
9 | * todo: enable component scanning for dao and service packages
10 | * todo: provide explicit configuration for a bean of type {@link TestDataGenerator} with name "dataGenerator" in this class.
11 | * todo: Don't specify bean name "dataGenerator" explicitly
12 | */
13 | public class AppConfig {
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/hello-application-context/src/main/java/com/bobocode/dao/AccountDao.java:
--------------------------------------------------------------------------------
1 | package com.bobocode.dao;
2 |
3 | import com.bobocode.model.Account;
4 |
5 | import java.util.List;
6 |
7 | /**
8 | * Defines an API for {@link Account} data access object (DAO).
9 | */
10 | public interface AccountDao {
11 | List
15 | * todo: configure this class as Spring component with bean name "accountDao"
16 | * todo: use explicit (with {@link Autowired} annotation) constructor-based dependency injection
17 | */
18 | public class FakeAccountDao implements AccountDao {
19 | private List
12 | * todo: configure {@link AccountService} bean implicitly using special annotation for service classes
13 | * todo: use implicit constructor-based dependency injection (don't use {@link org.springframework.beans.factory.annotation.Autowired})
14 | */
15 | public class AccountService {
16 | private final AccountDao accountDao;
17 |
18 | public AccountService(AccountDao accountDao) {
19 | this.accountDao = accountDao;
20 | }
21 |
22 | public Account findRichestAccount() {
23 | List
9 | * todo: mark this class as config
10 | * todo: enable component scanning for all packages in "com.bobocode"
11 | * todo: ignore all web related config and beans (ignore @{@link Controller}, ignore {@link EnableWebMvc}) using exclude filter
12 | */
13 | public class RootConfig {
14 | }
15 |
--------------------------------------------------------------------------------
/hello-spring-mvc/src/main/java/com/bobocode/config/WebAppInitializer.java:
--------------------------------------------------------------------------------
1 | package com.bobocode.config;
2 |
3 | import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
4 |
5 | /**
6 | * This class is used to configure DispatcherServlet and links it with application config classes
7 | *
8 | * todo: provide default servlet mapping ("/")
9 | * todo: use {@link WebConfig} as ServletConfig class
10 | * todo: use {@link RootConfig} as root application config class
11 | */
12 | public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
13 | @Override
14 | protected Class>[] getRootConfigClasses() {
15 | throw new UnsupportedOperationException("Method is not implemented yet!");
16 | }
17 |
18 | @Override
19 | protected Class>[] getServletConfigClasses() {
20 | throw new UnsupportedOperationException("Method is not implemented yet!");
21 | }
22 |
23 | @Override
24 | protected String[] getServletMappings() {
25 | throw new UnsupportedOperationException("Method is not implemented yet!");
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/hello-spring-mvc/src/main/java/com/bobocode/config/WebConfig.java:
--------------------------------------------------------------------------------
1 | package com.bobocode.config;
2 |
3 | /**
4 | * This class provides web (servlet) related configuration.
5 | *
6 | * todo: mark this class as Spring config class
7 | * todo: enable web mvc using annotation
8 | * todo: enable component scanning for package "web"
9 | */
10 | public class WebConfig {
11 | }
12 |
--------------------------------------------------------------------------------
/hello-spring-mvc/src/main/java/com/bobocode/web/controller/WelcomeController.java:
--------------------------------------------------------------------------------
1 | package com.bobocode.web.controller;
2 |
3 | /**
4 | * Welcome controller that consists of one method that handles get request to "/welcome" and respond with a message.
5 | *
6 | * todo: mark this class as Spring controller
7 | * todo: configure HTTP GET mapping "/welcome" for method {@link WelcomeController#welcome()}
8 | * todo: tell Spring that {@link WelcomeController#welcome()} method provides response body
9 | */
10 | public class WelcomeController {
11 |
12 | public String welcome() {
13 | return "Welcome to Spring MVC!";
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/hello-spring-mvc/src/test/java/com/bobocode/WebAppInitializerWrapper.java:
--------------------------------------------------------------------------------
1 | package com.bobocode;
2 |
3 | import com.bobocode.config.WebAppInitializer;
4 |
5 | class WebAppInitializerWrapper extends WebAppInitializer {
6 | public String[] getServletMappings() {
7 | return super.getServletMappings();
8 | }
9 |
10 | public Class[] getRootConfigClasses() {
11 | return super.getRootConfigClasses();
12 | }
13 |
14 | public Class>[] getServletConfigClasses() {
15 | return super.getServletConfigClasses();
16 | }
17 | }
--------------------------------------------------------------------------------
/hello-spring-mvc/src/test/java/com/bobocode/WelcomeWebAppTest.java:
--------------------------------------------------------------------------------
1 | package com.bobocode;
2 |
3 | import com.bobocode.config.RootConfig;
4 | import com.bobocode.config.WebConfig;
5 | import com.bobocode.web.controller.WelcomeController;
6 | import org.junit.jupiter.api.Test;
7 | import org.springframework.context.annotation.ComponentScan;
8 | import org.springframework.context.annotation.ComponentScan.Filter;
9 | import org.springframework.context.annotation.Configuration;
10 | import org.springframework.context.annotation.FilterType;
11 | import org.springframework.stereotype.Controller;
12 | import org.springframework.web.bind.annotation.GetMapping;
13 | import org.springframework.web.servlet.config.annotation.EnableWebMvc;
14 |
15 | import java.util.List;
16 | import java.util.stream.Collectors;
17 | import java.util.stream.Stream;
18 |
19 | import static org.hamcrest.MatcherAssert.assertThat;
20 | import static org.hamcrest.Matchers.arrayContaining;
21 | import static org.hamcrest.Matchers.arrayWithSize;
22 | import static org.hamcrest.Matchers.containsInAnyOrder;
23 | import static org.hamcrest.Matchers.equalTo;
24 | import static org.hamcrest.Matchers.notNullValue;
25 |
26 | class WelcomeWebAppTest {
27 |
28 | @Test
29 | void testDispatcherServletMapping() {
30 | WebAppInitializerWrapper webAppInitializerWrapper = new WebAppInitializerWrapper();
31 |
32 | assertThat(webAppInitializerWrapper.getServletMappings(), arrayContaining("/"));
33 | }
34 |
35 | @Test
36 | void testInitializerRootConfigClasses() {
37 | WebAppInitializerWrapper webAppInitializerWrapper = new WebAppInitializerWrapper();
38 |
39 | assertThat(webAppInitializerWrapper.getRootConfigClasses(), arrayContaining(RootConfig.class));
40 | }
41 |
42 | @Test
43 | void testInitializerWebConfigClasses() {
44 | WebAppInitializerWrapper webAppInitializerWrapper = new WebAppInitializerWrapper();
45 |
46 | assertThat(webAppInitializerWrapper.getServletConfigClasses(), arrayContaining(WebConfig.class));
47 | }
48 |
49 | @Test
50 | void testRootConfigClassIsMarkedAsConfiguration() {
51 | Configuration configuration = RootConfig.class.getAnnotation(Configuration.class);
52 |
53 | assertThat(configuration, notNullValue());
54 | }
55 |
56 | @Test
57 | void testRootConfigClassEnablesComponentScan() {
58 | ComponentScan componentScan = RootConfig.class.getAnnotation(ComponentScan.class);
59 |
60 | assertThat(componentScan, notNullValue());
61 | }
62 |
63 | @Test
64 | void testRootConfigComponentScanPackages() {
65 | ComponentScan componentScan = RootConfig.class.getAnnotation(ComponentScan.class);
66 | String[] packages = componentScan.basePackages();
67 | if (packages.length == 0) {
68 | packages = componentScan.value();
69 | }
70 |
71 | assertThat(packages, arrayContaining("com.bobocode"));
72 | }
73 |
74 | @Test
75 | void testRootConfigComponentScanFilters() {
76 | ComponentScan componentScan = RootConfig.class.getAnnotation(ComponentScan.class);
77 | Filter[] filters = componentScan.excludeFilters();
78 | List
17 | * todo: 1. Mark this class as spring config
18 | * todo: 2. Configure a bean of {@link DataSource}
19 | * todo: 3. Configure a bean of {@link JpaVendorAdapter}
20 | * todo: 4. Configure bean {@link javax.persistence.EntityManagerFactory} with name "entityManagerFactory"
21 | *
22 | */
23 | public class JpaConfig {
24 | public DataSource dataSource() {
25 | return new EmbeddedDatabaseBuilder()
26 | .setType(EmbeddedDatabaseType.H2)
27 | .build();
28 | }
29 |
30 | public JpaVendorAdapter jpaVendorAdapter() {
31 | HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
32 | adapter.setDatabase(Database.H2);
33 | adapter.setShowSql(true);
34 | adapter.setGenerateDdl(true); // this sets hibernate.hbm2ddl.auto=update (Hibernate will generate db tables)
35 | return adapter;
36 | }
37 |
38 | public LocalContainerEntityManagerFactoryBean localContainerEMF(DataSource dataSource, JpaVendorAdapter jpaVendorAdapter) {
39 | LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
40 | emf.setDataSource(dataSource);
41 | emf.setJpaVendorAdapter(jpaVendorAdapter);
42 | // todo: 5. Configure package "com.bobocode.model" to scan for JPA entities
43 | return emf;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/transactional-user-service/src/main/java/com/bobocode/config/RootConfig.java:
--------------------------------------------------------------------------------
1 | package com.bobocode.config;
2 |
3 | import org.springframework.transaction.PlatformTransactionManager;
4 |
5 | /**
6 | * This class provides root application configuration. It scans for all available beans and enables declarative
7 | * transaction management.
8 | *
9 | * todo: 1. Mark this class as config
10 | * todo: 2. Enable component scanning for package "com.bobocode"
11 | * todo: 3. Configure JPA {@link PlatformTransactionManager} with bean name "transactionManager"
12 | * todo: 4. Enable declarative transaction management
13 | */
14 | public class RootConfig {
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/transactional-user-service/src/main/java/com/bobocode/dao/UserDao.java:
--------------------------------------------------------------------------------
1 | package com.bobocode.dao;
2 |
3 | import com.bobocode.model.jpa.User;
4 |
5 | import java.util.List;
6 |
7 | /**
8 | * User Data Access Object (DAO) API
9 | */
10 | public interface UserDao {
11 | List
15 | * todo: 1. Configure {@link JpaUserDao} bean as Spring Repository with name "userDao"
16 | * todo: 2. Enable transaction management on class level
17 | * todo: 3. Inject {@link EntityManager} using @{@link PersistenceContext} annotation
18 | */
19 | public class JpaUserDao implements UserDao {
20 | private EntityManager entityManager;
21 |
22 | @Override
23 | public List
15 | * todo: 1. Configure {@link UserService} bean as spring service
16 | * todo: 2. Inject {@link UserDao} using constructor-based injection
17 | * todo: 3. Enable transaction management on class level
18 | * todo: 4. Configure {@link UserService#getAll()} as read-only method
19 | * todo: 4. Configure {@link UserService#getAllAdmins()} as read-only method
20 | */
21 | public class UserService {
22 | private UserDao userDao;
23 |
24 | public void save(User user) {
25 | userDao.save(user);
26 | }
27 |
28 | public ListHello ApplicationContext exercise :muscle:
2 | Improve your *Spring ApplicationContext* Java configuration skills
3 | ### Task
4 | The task is to **configure `ApplicationContext`** that contains `AccountService` bean, `AccountDao` bean
5 | and `TestDataGenerator` bean. Your job is to follow the instructions in the *todo* section and **implement
6 | a proper configuration.**
7 |
8 | To verify your configuration, run `AppConfigTest.java`
9 |
10 |
11 | ### Pre-conditions :heavy_exclamation_mark:
12 | You're supposed to be familiar with *Spring IoC* and *Dependency injection*
13 |
14 | ### How to start :question:
15 | * Just clone the repository and start implementing the **todo** section, verify your changes by running tests
16 | * If you don't have enough knowledge about this domain, check out the [links below](#related-materials-information_source)
17 | * Don't worry if you got stuck, checkout the **exercise/completed** branch and see the final implementation
18 |
19 | ### Related materials :information_source:
20 | * [Spring IoC basics tutorial](https://github.com/boy4uck/spring-framework-tutorial/tree/master/spring-framework-ioc-basics)
21 |
22 |
--------------------------------------------------------------------------------
/hello-application-context/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
Hello Spring MVC exercise :muscle:
2 | Improve your *Spring MVC* configuration skills
3 | ### Task
4 | The task is to **configure a typical *Spring MVC* application** separating web and root configs. Your job is to follow
5 | the instructions in the *todo* section and **implement a proper configuration.**
6 |
7 | To verify your configuration, run `WelcomeWebAppTest.java`
8 |
9 |
10 | ### Pre-conditions :heavy_exclamation_mark:
11 | You're supposed to be familiar with *Spring MVC*
12 |
13 | ### How to start :question:
14 | * Just clone the repository and start implementing the **todo** section, verify your changes by running tests
15 | * If you don't have enough knowledge about this domain, check out the [links below](#related-materials-information_source)
16 | * Don't worry if you got stuck, checkout the **exercise/completed** branch and see the final implementation
17 |
18 | ### Related materials :information_source:
19 | * [Spring MVC basics tutorial](https://github.com/boy4uck/spring-framework-tutorial/tree/master/spring-framework-mvc-basics)
20 |
21 |
--------------------------------------------------------------------------------
/hello-spring-mvc/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
Inter-bean dependencies exercise :muscle:
2 | Improve your *Spring ApplicationContext* Java configuration skills
3 | ### Task
4 | The task is to **refactor Java configuration class `RootConfig`, in order to properly use Inter-bean dependencies.**
5 | Your job is to follow the instructions in the *todo* section and **implement
6 | a proper configuration.**
7 |
8 | To verify your configuration, run `RootConfigContextTest.java` and `RootConfigHacksTest.java`
9 |
10 |
11 | ### Pre-conditions :heavy_exclamation_mark:
12 | You're supposed to be familiar with *Spring IoC* and *Dependency injection* and Proxy pattern
13 |
14 | ### How to start :question:
15 | * Just clone the repository and start implementing the **todo** section, verify your changes by running tests
16 | * If you don't have enough knowledge about this domain, check out the [links below](#related-materials-information_source)
17 | * Don't worry if you got stuck, checkout the **exercise/completed** branch and see the final implementation
18 |
19 | ### Related materials :information_source:
20 | * [Spring @Configuration vs @Component](https://youtu.be/KWvTgqtA_HA)
21 | * [Injecting Inter-bean Dependencies](https://docs.spring.io/spring/docs/5.1.9.RELEASE/spring-framework-reference/core.html#beans-java-injecting-dependencies)
22 |
23 |
--------------------------------------------------------------------------------
/inter-bean-dependencies/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
Transactional UserService exercise :muscle:
2 | Improve your *Spring ORM* and *Transaction manager* configuration skills
3 | ### Task
4 | This is a *Spring* application that consists of `UserService` and `UserDao` that is implemented using *JPA*.
5 | Your job is to **configure all required beans to use *JPA*** then **configure `PlatformTransactionManager`** and
6 | **enable declarative transaction management**. In order to complete the task, please **follow the instructions
7 | in the *todo* section**
8 |
9 | To verify your configuration, run `TransactionalUserServiceTest.java` :white_check_mark:
10 |
11 |
12 | ### Pre-conditions :heavy_exclamation_mark:
13 | You're supposed to be familiar with *Spring MVC*
14 |
15 | ### How to start :question:
16 | * Just clone the repository and start implementing the **todo** section, verify your changes by running tests
17 | * If you don't have enough knowledge about this domain, check out the [links below](#related-materials-information_source)
18 | * Don't worry if you got stuck, checkout the **exercise/completed** branch and see the final implementation
19 |
20 | ### Related materials :information_source:
21 | * [Spring JPA and Tx management](https://github.com/bobocode-projects/spring-framework-tutorial/tree/master/jpa-tx-management)
22 |
23 |
--------------------------------------------------------------------------------
/transactional-user-service/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |