스프링Bean과 의존성 주입(DI)
의존성 주입이란?
필요한 객체를 함수 내에서 직접 생성해서 사용하면, 다른 객체로 변경하고 싶은 경우에 소스를 수정해야 하는 번거로운 일이 발생한다.
이러한 현상을 방지하기 위해 의존성 주입을 통해, 객체 간의 결합도를 낮추고 수정을 용이하게 해 준다.
DI 방식
3가지 방식이 있지만 실행 중 변경될 일이 없기 때문에, 생성자로 1번만 실행하고 변경이 되지 않게 하는 게 좋다.
1) 필드 주입
@Autowired
private MemeberService memberService;
2) Setter 방식
@Autowired
private MemeberService memberService;
public setMemberService(MemeberService memberService){
this.memberService = memberService;
}
3) 생성자 방식
private final MemberService memberService;
@Autowired
public MemberController() {
this.memberService = new memberService();
}
예제 - 수정 전 Controller
package hello.hellospring.controller;
import hello.hellospring.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class MemberController {
private final MemberService memberService;
// 객체 생성
public MemberController() {
this.memberService = new memberService();
}
// 함수 호출
public test(){
memberService.join();
}
}
해당 내용을 보면 MemberService를 생성자에서 new 하였고, memberService의 함수를 호출하는 메서드를 생성해서 사용했다.
하지만 MemberService가 아닌 ManagerService, CustomerService 등 다른 객체로 변경을 하고 싶은 경우에, memberService 생성, 호출 부분을 모두 수정해야 하는 번거로움이 발생한다.
Service Interface 생성
public interface Service{
}
public class MemberService implements Service{
}
public class CustomerService implements Service{
}
public class ManagerService implements Service{
}
수정 후 Controller
package hello.hellospring.controller;
import hello.hellospring.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class MemberController {
private final Service service;
@Autowired
public MemberController(Service service) {
this.service = service;
}
public test(){
service.join();
}
}
Member, Customer, ManagerService를 모두 포함하는 Service라는 객체를 만들고, 해당 객체를 의존성 주입(DI)을 통해 입력받아 처리한다.
생성자에서 Service라는 파라미터로 처리되기 때문에, 어떤 Class가 들어오던지 전혀 고려할 필요가 없다.
Bean
스프링 컨테이너가 관리하는 객체
Bean 등록(1) - 어노테이션 (컴포넌트 스캔)
controller, service, repository에 해당하는 어노테이션을 붙여 Spring이 실행될 때 스캔을 통해 자동으로 컨테이너에 등록되게 하는 방식
@Controller
public class MemberController {}
@Repository
public class MemoryMemberRepository implements MemberRepository {}
@Service
public class MemberService {
private final MemberRepository memberRepository;
@Autowired
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
}
@Component : 해당 어노테이션을 갖고 있는 함수는 자동으로 컴포넌트 스캔을 통해, 컨테이너에 등록된다.
ㄴ @Controller : 컨트롤러에 사용
ㄴ @Repository : 리포지토리에 사용
ㄴ @Service : 서비스에 사용
@Autowired : 스프링 컨테이너에서 연관된 객체를 찾아서 의존성 주입을 시켜주는 어노테이션. 생성자가 1개인 경우에는 생략 가능
스프링 컨테이너에 스프링 bean 등록할 때, 기본으로 싱글톤으로 등록한다. 같은 bean은 같은 인스턴스.
설정으로 싱글톤이 아니게 설정할 수 있지만, 특별한 경우를 제외하면 대부분 싱글톤을 사용
Bean 등록(2) - Config
service, repository 어노테이션이 아닌 @Configuration에서 bean등록을 관리하는 방식
Q. Controller도 @Bean으로 관리가 가능한가?
Controller도 어노테이션을 사용하지 않고 Config에서 @Bean으로 관리해도 상관은 없지만, Controller의 역할이 비즈니스 로직처리가 아닌 Mapping기능이 주이기 때문에, Config관리할 만큼 변경이 잦지 않은 항목이라 @Controller를 통해 관리한다.
@Configuration
public class SpringConfig {
@Bean
public MemberService memberService() {
return new MemberService(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
}
Bean 등록(3) - XML
예전에는 자주 사용했지만, 현재는 거의 사용하지 않는 추세
해당 내용은 "인프런" - "스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술"을 학습하며 정리한 내용입니다.
틀린 사항이 있을 경우, 알려주시면 감사하겠습니다 :)