main/~/domain/RestaurantRepository.java
package kr.co.fastcampus.eatgo.domain;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
@Component // 스프링이 관리할수 있게 @Component를 붙인다. 그럼 Autowired가 사용되는곳에서 이 @Component가 붙어있는곳을찾아 자동으로 의존성주입을 하게된다.
public class RestaurantRepository {
private List<Restaurant> restaurants = new ArrayList<>(); // 가게목록 담을 리스트
public RestaurantRepository(){ // 생성자를 이용해 가게 객체 추가
restaurants.add(new Restaurant(1004L,"Bob zip", "Seoul"));
restaurants.add(new Restaurant(2020L, "Cyber Food", "Seoul"));
}
public List<Restaurant> findAll() { // 가게목록 반환
return restaurants;
}
public Restaurant findById(Long id) { // 특정가게 상세 반환
return restaurants.stream()
.filter(r -> r.getId().equals(id))
.findFirst()
.orElse(null);
}
}
클래스명 상단에 스프링이 관리할수 있게 @Component를 붙인다. 그럼 Autowired가 사용되는곳에서 이 @Component가 붙어있는곳을찾아 자동으로 의존성주입을 하게된다.
/main/~/interfaces/RestaurantController.java
package kr.co.fastcampus.eatgo.interfaces;
import kr.co.fastcampus.eatgo.domain.Restaurant;
import kr.co.fastcampus.eatgo.domain.RestaurantRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
@RestController
public class RestaurantController {
@Autowired // 객체를 따로 생성안해도 @Component가 붙어있는 클래스를 찾아 자동으로 의존주입을하게된다.
private RestaurantRepository repository;
@GetMapping("/restaurants") // 가게목록 반환 api
public List<Restaurant> list(){
List<Restaurant> restaurants = repository.findAll(); // repository.findAll() 메서드로 가게목록 반환
return restaurants;
}
@GetMapping("/restaurants/{id}") // 특정 가게상세 반환 api
public Restaurant detail(@PathVariable("id") Long id){
Restaurant restaurant = repository.findById(id); // repository.findById(id) 메서드로 특정 가게상세 반환
return restaurant;
}
}
기존
private RestaurantRepository repository = new RestaurantRepository();
변경
@Autowired
private RestaurantRepository repository;
/test/~/interfaces/RestaurantControllerTest.java
package kr.co.fastcampus.eatgo.interfaces;
import kr.co.fastcampus.eatgo.domain.RestaurantRepository;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import static org.hamcrest.core.StringContains.containsString;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringRunner.class) // api 요청처리 테스트를 해주기위한 스프링자체의 어노테이션
@WebMvcTest(RestaurantController.class) // 특정 컨트롤러를 테스트해준다는것을 명시하는 어노테이션
public class RestaurantControllerTest {
@Autowired
private MockMvc mvc; // MockMvc생성
@SpyBean // 이것으로 컨트롤러에 우리가 원하는 객체를 주입할수있다. (@Autowired 테스트를 하기위한것)
private RestaurantRepository restaurantRepository;
@Test
public void list() throws Exception {
mvc.perform(get("/restaurants"))
.andExpect(status().isOk())
.andExpect(content().string(containsString("\"id\":1004")))
.andExpect(content().string(containsString("\"name\":\"Bob zip\"")));
}
@Test
public void detail() throws Exception {
mvc.perform(get("/restaurants/1004"))
.andExpect(status().isOk())
.andExpect(content().string(containsString("\"id\":1004")))
.andExpect(content().string(containsString("\"name\":\"Bob zip\"")));
mvc.perform(get("/restaurants/2020"))
.andExpect(status().isOk())
.andExpect(content().string(containsString("\"id\":2020")))
.andExpect(content().string(containsString("\"name\":\"Cyber Food\"")));
}
}
@SpyBean
private RestaurantRepository restaurantRepository;
테스트에 이렇게 넣어줘야 @Autowired 의존성주입 테스트가 정상작동하는것을 확인할 수 있다.
우리가 사용해야되는 객체를 다양하게 변경할 수 있다.
이것을 이용해서 같은 방식으로 작동하는 여러객체들을 효율적으로 관리할 수 있다.
이제 의존성주입 방식을 전체적으로 도입하기 위해 우리가 이전에 만들었던 main/~/domain/RestaurantRepository.java 를
main/~/domain/RestaurantRepository.java 인터페이스와
main/~/domain/RestaurantRepositoryImpl.java 이라는 구체적인 구현체로 나눌것이다.
main/~/domain/RestaurantRepository.java
package kr.co.fastcampus.eatgo.domain;
import java.util.List;
public interface RestaurantRepository {
List<Restaurant> findAll();
Restaurant findById(Long id);
}
main/~/domain/RestaurantRepositoryImpl.java
package kr.co.fastcampus.eatgo.domain;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
@Component // 스프링이 관리할수 있게 @Component를 붙인다. 그럼 Autowired가 사용되는곳에서 이 @Component가 붙어있는곳을찾아 자동으로 의존성주입을 하게된다.
public class RestaurantRepositoryImpl implements RestaurantRepository {
private List<Restaurant> restaurants = new ArrayList<>(); // 가게목록 담을 리스트
public RestaurantRepositoryImpl(){ // 생성자를 이용해 가게 객체 추가
restaurants.add(new Restaurant(1004L,"Bob zip", "Seoul"));
restaurants.add(new Restaurant(2020L, "Cyber Food", "Seoul"));
}
@Override
public List<Restaurant> findAll() { // 가게목록 반환
return restaurants;
}
@Override
public Restaurant findById(Long id) { // 특정가게 상세 반환
return restaurants.stream()
.filter(r -> r.getId().equals(id))
.findFirst()
.orElse(null);
}
}
/main/~/interfaces/RestaurantController.java
package kr.co.fastcampus.eatgo.interfaces;
import kr.co.fastcampus.eatgo.domain.Restaurant;
import kr.co.fastcampus.eatgo.domain.RestaurantRepository;
import kr.co.fastcampus.eatgo.domain.RestaurantRepositoryImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class RestaurantController {
@Autowired // 객체를 따로 생성안해도 @Component가 붙어있는 클래스를 찾아 자동으로 의존주입을하게된다.
private RestaurantRepository repository;
//private RestaurantRepository repository = new RestaurantRepository(); // 중복되는 코드를 RestaurantRepository 클래스로 분리
@GetMapping("/restaurants") // 가게목록 반환 api
public List<Restaurant> list(){
List<Restaurant> restaurants = repository.findAll(); // repository.findAll() 메서드로 가게목록 반환
return restaurants;
}
@GetMapping("/restaurants/{id}") // 특정 가게상세 반환 api
public Restaurant detail(@PathVariable("id") Long id){
Restaurant restaurant = repository.findById(id); // repository.findById(id) 메서드로 특정 가게상세 반환
return restaurant;
}
}
@Autowired
private RestaurantRepository repository;컨트롤러에서 이부분을 보면 인터페이스를 이용하여 @Autowired로 객체생성을 자동으로하는 의존성주입 형태가 된것을 확인할 수 있다.
/test/~/interfaces/RestaurantControllerTest.java
package kr.co.fastcampus.eatgo.interfaces;
import kr.co.fastcampus.eatgo.domain.RestaurantRepository;
import kr.co.fastcampus.eatgo.domain.RestaurantRepositoryImpl;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import static org.hamcrest.core.StringContains.containsString;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringRunner.class) // api 요청처리 테스트를 해주기위한 스프링자체의 어노테이션
@WebMvcTest(RestaurantController.class) // 특정 컨트롤러를 테스트해준다는것을 명시하는 어노테이션
public class RestaurantControllerTest {
@Autowired
private MockMvc mvc; // MockMvc생성
@SpyBean(RestaurantRepositoryImpl.class) // 아래의 인터페이스가 실질적으로 어떤 클래스를 구현하는지 명시해줘야한다.
private RestaurantRepository restaurantRepository;
@Test
public void list() throws Exception {
mvc.perform(get("/restaurants"))
.andExpect(status().isOk())
.andExpect(content().string(containsString("\"id\":1004")))
.andExpect(content().string(containsString("\"name\":\"Bob zip\"")));
}
@Test
public void detail() throws Exception {
mvc.perform(get("/restaurants/1004"))
.andExpect(status().isOk())
.andExpect(content().string(containsString("\"id\":1004")))
.andExpect(content().string(containsString("\"name\":\"Bob zip\"")));
mvc.perform(get("/restaurants/2020"))
.andExpect(status().isOk())
.andExpect(content().string(containsString("\"id\":2020")))
.andExpect(content().string(containsString("\"name\":\"Cyber Food\"")));
}
}
테스트코드에서는 구현체 명시하여 테스트 진행
@SpyBean(RestaurantRepositoryImpl.class) // 아래의 인터페이스가 실질적으로 어떤 클래스를 구현하는지 명시해줘야한다.
private RestaurantRepository restaurantRepository;
테스트가 정상 작동하는것을 확인할 수 있다.