@Controller
@RequestMapping("/hello/request")
public class HelloRequestController {
@GetMapping("/form/html")
public String helloForm() {
return "hello-request-form";
}
// [Request sample]
// GET http://localhost:8080/hello/request/star/BTS/age/28
@GetMapping("/star/{name}/age/{age}")
@ResponseBody
public String helloRequestPath(@PathVariable String name, @PathVariable int age)
{
//String에서 %s 와 %d는 뒤에 변수를 넣어줄 공간임
return String.format("Hello, @PathVariable.<br> name = %s, age = %d", name, age);
}
// [Request sample]
// GET http://localhost:8080/hello/request/form/param?name=BTS&age=28
@GetMapping("/form/param")
@ResponseBody
public String helloGetRequestParam(@RequestParam String name, @RequestParam int age) {
return String.format("Hello, @RequestParam.<br> name = %s, age = %d", name, age);
}
// [Request sample]
// POST http://localhost:8080/hello/request/form/param
// Header
// Content type: application/x-www-form-urlencoded
// Body
// name=BTS&age=28
@PostMapping("/form/param")
@ResponseBody
public String helloPostRequestParam(@RequestParam String name, @RequestParam int age) {
return String.format("Hello, @RequestParam.<br> name = %s, age = %d", name, age);
}
// [Request sample]
// POST http://localhost:8080/hello/request/form/model
// Header
// Content type: application/x-www-form-urlencoded
// Body
// name=BTS&age=28
// 위와 받는 내용은 완전 동일하지만 받는 부분이 @ModelAttribute 자바 객체일 뿐임
@PostMapping("/form/model")
@ResponseBody
public String helloRequestBodyForm(@ModelAttribute Star star) {
return String.format("Hello, @RequestBody.<br> (name = %s, age = %d) ", star.name, star.age);
}
// [Request sample]
// POST http://localhost:8080/hello/request/form/json
// Header
// Content type: application/json
// Body
// {"name":"BTS","age":"28"}
@PostMapping("/form/json")
@ResponseBody
public String helloPostRequestJson(@RequestBody Star star) {
return String.format("Hello, @RequestBody.<br> (name = %s, age = %d) ", star.name, star.age);
}
}
AllInOneController의 문제점
- 한 개의 클래스에 너무 많은 양의 코드가 존재
- 코드 이해가 어려움: 처음부터 끝까지 다 읽어야 코드 내용을 이해할 수 있음
→ 3개의 클래스에 역할 별로 코드가 정리되어야 함
- 현업에서는 코드 추가 혹은 변경 요청이 계속 생김
변경 요청의 예)
- 1) 신규 상품 등록 시 Client 에게 응답 (Response) 하는 값 변경
- 등록된 Product 전체 정보 → 등록된 Product 의 id
→ Controller에 구현되어야 함- 2) 최저가 (Myprice) 업데이트 조건 변경
- Client 가 최저가를 0원 이하로 입력 → 에러 발생
→ Service에 구현되어야 함- 3) DB 테이블 이름 변경
- Product 테이블의 lprice → lowprice 변경
→ Repository에 구현되어야 함
service
즉, 비즈니스 로직을 다루는 곳
이 spring의 핵심이라고 한다.강한 결합
에 대해 알아보자public class A {
private B b;
public A() {
this.b = new B(); // 이 부분 때문에 강한 결합이 된다.
}
public void startEat() {
b.being();
}
}
public class B {
public void being() {
System.out.println("b의 메서드가 실행되었습니다.");
}
}
강한 결합
을 해결할 방법은
- 각 객체에 대한 객체 생성은 딱 1번만 한다.
- 생성된 객체를 모든 곳에서 재사용한다.
느슨한 결합
이라고 한다.느슨한 결합의 흐름은 강한 결합의 흐름과 반대로 간다.
이처럼 프로그램의 제어 흐름이 뒤바뀌는 것을 제어의 역전(IOC)이라고 한다.
토비의 스프링이 말하는 의존관계 주입의 세가지 조건
- 1) 클래스 모델이나 코드에는 런타임 시점의 의존관계가 드러나지 않는다. 그러기 위해서는 인터페이스만 의존하고 있어야 한다.
- 2) 런타임 시점의 의존관계는 컨테이너나 팩토리 같은 제3의 존재가 결정한다.
- 3) 의존관계는 사용할 오브젝트에 대한 레퍼런스를 외부에서 제공(주입)해줌으로써 만들어진다.
- 이일민, 토비의 스프링 3.1, 에이콘(2012), p114
오늘 구현했던 DI는 생성자를 이용하여 이미 정의된 객체(Bean)를 넣어주고 생성자에 @Autowired를 붙여주는 방식이었다.
//아래 @Autowired가 붙은 생성자의 역할은
//클래스 위 @RequiredArgsConstructor에 의해 완벽히 대체된다. 따라서 같이 쓰면 오류 발생
// productService라는 Bean을 넣어줌 (DI: 의존성 주입)
@Autowired
public ProductController(ProductService productService){
this.productService = productService;
}
Bean: 스프링이 관리하는 객체
Spring IoC Container: Bean을 모아둔 통
// 1. ProductService 객체 생성
ProductService productService = new ProductService();
// 2. 스프링 IoC 컨테이너에 빈 (productService) 저장
// productService -> 스프링 IoC 컨테이너
'빈' 아이콘 확인 → 스프링 IoC 에서 관리할 '빈' 클래스라는 표시
@Configuration
@ComponentScan(basePackages = "com.sparta.springcore")
class BeanConfig { ... }
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class BeanConfiguration {
@Bean
public ProductRepository productRepository() {
String dbUrl = "jdbc:h2:mem:springcoredb";
String dbId = "sa";
String dbPassword = "";
return new ProductRepository(dbUrl, dbId, dbPassword);
}
}
// 1. @Bean 설정된 함수 호출
ProductRepository productRepository = beanConfiguration.productRepository();
// 2. 스프링 IoC 컨테이너에 빈 (productRepository) 저장
// productRepository -> 스프링 IoC 컨테이너
@Component
public class ProductService {
@Autowired
private ProductRepository productRepository;
// ...
}
스프링 입문 강의에서 무지성으로 사용했던 @RequiredArgsConstructor는 final로 선언된 멤버 변수 (빈)을 담은 생성자를 자동으로 만들어주며 @Autowired 까지 붙여준다.
+) ApplicationContext -> 스프링 IoC 컨테이너에서 빈을 수동적으로 가져오는 방법
@Component
public class ProductService {
private final ProductRepository productRepository;
@Autowired
public ProductService(ApplicationContext context) {
// 1.'빈' 이름으로 가져오기
ProductRepository productRepository = (ProductRepository) context.getBean("productRepository");
// 2.'빈' 클래스 형식으로 가져오기
// ProductRepository productRepository = context.getBean(ProductRepository.class);
this.productRepository = productRepository;
}
// ...
}
오늘은 코멘트를 쓸 체력이 남지 않았다..
얼른 자고 내일도 힘내자..!!!!