-
[스프링] 의존성 주입2Spring 2022. 10. 3. 10:05
들어가기 전에
- 강한 결합 : 객체 내부에서 다른 객체를 생성하는 것은 강한 결합도를 갖는다 즉, A클래스 내부에서 B라는 객체를 직접 생성하고 있다면 B객체를 C객체로 바꾸고 싶은 경우 A클래스를 수정해야하는 방식이기에 이런 경우를 강한 결합도를 갖는다라고 일컫는다.
- 느슨한 결합 : 객체를 주입 받는다는 것은 외부에서 생성된 객체를 인터페이스를 통해 넘겨받는것이다. 이렇게하면 객체간 결합도를 낮출 수 있고, 런타임시에 의존관계가 결정되기에 유연한 구조를 갖을 수 있다. SOLID원칙에서 O에 해당하는 Open-Close Principle을 지키기 위해 디자인패턴 중 전략패턴을 사용하게 되는데, 생성자 주입을 사용하게되면 전략패턴을 사용하게 된다.
6. 스프링 DI 설정
- 스프링은 의존성 주입을 지원하는 조립기이다.
package config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import spring.ChangePasswordService; import spring.MemberDao; import spring.MemberRegisterService; @Configurtion // -> 스프링 설정 클래스로 사용하려면 붙여줘야함 public class AppCtx { @Bean // -> 스프링 빈 public MemberDao memberDao() { return new MemberDao(); } @Bean public MemberRegisterService memberRegSvc() { return new MemberRegisterService(memberDao()); } @Bean public ChangePasswordService changePwdSvc() { ChangePasswordService pwdSvc = new ChangePasswordServie(); pwdSvc.setMemberDao(memberDao); // -> Setter 메서드를 통해 memberDao 객체 주입 return pwdSvc; } }- 어노테이션 @Bean이 메서드에 붙어있으면 해당 메서드의 이름으로 한 개의 빈 객체를 생성한다. 여러 메서드에 붙어있다면 각각의 이름으로 빈 객체가 생성된다.
- 왜 의존성 주입을 해야하는가??
- 코드의 재사용성을 높이려고
- 객체간 결합도를 낮추면서 유연한 코드를 작성하려고
- 객체간 종속성을 줄이거나 없애려고
- 코드의 테스트를 쉽게 하려고
- 의존성 주입의 방법
- 생성자를 통한 주입
- 수정자를 통한 주입
- 필드 주입
방식1. 생성자를 통한 주입
public class MemberRegisterService { private MemberDao memberDao; public MemberRegisterService(MemberDao memberDao) { //-> 생성자를 통해 DI 주입 this.memberDao = memberDao; //-> 주입받은 Object를 필드에 할당 } public Long regist(RegisterRequest req) { //-> 주입받은 객체의 메서드 사용 Member member = memberDao.selectByEmail(req.getEmail()); ... memberDao.insert(newMember); return newMember.getId(); } }- 생성자 주입을 통해 생기는 이점
- 컴파일시 빈 객체를 먼저 생성하지 않고, 먼저 어디서 사용하는지 전부 찾은다음 생성하기에 순환참조를 사전에 방지한다.
- final 키워드를 사용할 수 있다. 따라서 클래스 내부에 객체를 바꿔치기 할 수 없어 객체에 대한 불변성이 보장된다.
- 의존관계에 대한 내용을 외부로 노출시켜 컴파일시 오류를 잡아낼 수 있다.
- Null을 주입하지 않는 한 NullPointerException(NPE)는 발생하지 않는다.
방식2. 수정자를 통한 주입
public class MemberRegisterService { private MemberDao memberDao; public Long regist(RegisterRequest req) { Member member = memberDao.selectByEmail(req.getEmail()); ... memberDao.insert(newMember); return newMember.getId(); } public void setMemberDao(MemberDao memberDao) { this.memberDao = memberDao; } }- Setter 메서드는 다음과 같은 규칙을 따라 작성한다.
- 매서드 이름은 set으로 시작한다
- set다음 문자는 대문자이다
- 파라미터는 1개이다
- 리턴 타입은 void여야 한다
- 수정자 주입을 통해 생기는 이점
- 런타임시 주입되어 낮은 결합도를 갖게한다.
- setter메서드를 통해 어떤 의존 객체가 주입되는지 파악하기 쉽다.
- 수정자 주입이 갖는 단점
- 주입이 필요한 객체가 주입되지 않아도 얼마든지 객체가 생성될 수 있다. 즉, NPE가 발생할 수 있다.
방식3. 필드 주입
public class MemberRegisterService { @Autowired MemberDao memberDao; public Long regist(RegisterRequest req) { Member member = memberDao.selectByEmail(req.getEmail()); ... memberDao.insert(newMember); return newMember.getId(); } }- 필드 주입이 갖는 이점
- @Autowired 어노테이션만 적어주면 스프링 설정 클래스의 @Bean메서드에서 의존 주입을 위한 코드를 생성하지 않아도되서 사용이 간편하다.
- 필드 주입의 단점
- 빈 객체를 먼저 생성한 뒤에 의존성을 주입하기에 빈 객체를 생성하는 시점에서 순환참조가 발생하는지 알 방법이 없다.
- 스프링 설정 파일을 읽고 모든 빈이 설정되어야 빈 객체가 주입되므로 의존 주입 컨테이너와 결합이 매우 강하게 되어 외부에서 접근이 불가하다.
7. @Configuration 설정 클래스의 @Bean설정과 싱글톤 패턴
@Configuration public class AppCtx { @Bean public MemberDao memberDao() { return new MemberDao(); } @Bean public MemberRegisterService memberRegSvc() { return new MemberRegisterService(memberDao()); } @Bean public ChangePasswordService changePwdSvc() { ChangePasswordService pwdSvc = new ChangePasswordService(); pwdSvc.setMemberDao(memberDao()); return pwdSvc; } }- 위의 코드를 보다 갑자기 드는 의문
Q : memberRegSvc메서드와 changePwdSvc메서드가 호출 될 때마다 memberDao()메서드를 실행하면 매번 새로운 객체를 생성하여 리턴하는데 서로 다른 객체를 사용하는 것이 아닐까?
A : 스프링 컨테이서는 @Bean어노테이션이 붙은 메서드에 대해 오직 한 개의 객체만 생성된다. 스프링 컨테이너가 생성한 빈은 싱글톤 패턴을 따르므로 여러 메서드에서 호출될 때 memberDao()메서드는 최초에 한번 생성한 객체를 보관했다가 이후 동일한 객체를 리턴한다.
8. 두 개 이상의 설정 파일 사용하기 (실습이라 패스)
9. getBean() 메서드 사용
빈 인스턴스는 BeanUtils클래스에서 생성된다.AnnotationConfigApplicationContext객체를 생성할 때, finishBeanFactoryInitialization(beanFactory);에서 이미 getBean()메서드를 호출하여 인스턴스가 생성되고 스프링 컨테이너의 싱글톤으로 등록이된다.
10. 주입 대상 객체를 모두 빈 객체로 설정해야 하는가?
객체를 스프링 빈으로 등록할 때와 하지 않을 때의 차이는 스프링 컨테이너가 객체를 관리하냐의 여부이므로 스프링 컨테이너가 제공하는 관리 기능은 필요없고 getBean()메서드로 구할 필요가 없다면 굳이 빈 객체로 등록할 필요는 없다.

'Spring' 카테고리의 다른 글
[스프링] Aspect Oriented Programming (AOP을 이용한 로깅파일 구성) (0) 2023.08.05 SOLID 원칙 (0) 2023.02.21 MVC모델의 정의, 장점 (0) 2023.02.08 [스프링] 컴포넌트 스캔 (0) 2022.10.03 [스프링] 의존성 주입1 (0) 2022.10.03