[Spring] 스프링 의존성 주입, 관리 (DI)
DI
의존 주입
Dependency Injection
- 의존
: 한 클래스가 다른 클래스의 메서드를 실행 하는 것을 뜻한다. - 주입
: 의존 하는 객체를 직접 생성하지 않고 의존 객체를 전달 받는 방식 - ex)
public class MemberfindService{
private MemberDao memberDao;
public MemberfindService(MemberDao memberDao) {
this.memberDao = memberDao;
}
public Long findMemberId(RegisterRequest req) {
Member member = memberDao.selectByEmail(req.getEmail());
return member .getId();
}
}
MemberDao 클래스의 메소드가 수정이 된다면 MemberfindService 영향을 받는다.
→ 즉, MemberfindService는 MemberDado에 의존한다.
조립기
스프링을 사용하지 않은 조립
ex ) Assembler 클래스 생성
public class Assembler {
private MemberDao memberDao;
private MemberRegisterService regSvc;
public Assembler() {
memberDao = new MemberDao();
regSvc = new MemberRegisterService(memberDao);
}
public MemberDao getMemberDao() {
return memberDao;
}
public MemberRegisterService getMemberRegisterService() {
return regSvc;
}
}
ex ) Assembler 클래스 생성
private static Assembler assembler = new Assembler();
MemberRegisterService regSvc = assembler.getMemberRegisterService();
스프링을 사용한 조립
- 스프링에서 annotation 방식으로 주로 사용, 과거에는 XML로 설정하는 방법도 사용하였다.
1) @Configuration : 빈 팩토리를 위한 오브젝트 설정을 담당하는 클래스
2) @Bean : 스프링 컨테이너에 의해서 객체가 만들어짐
@Configuration
public class BeanConfig{
@Bean
public MemberDao memberDao() {
return new MemberDao();
}
@Bean
public MemberRegisterService memberRegSvc() {
return new MemberRegisterService(memberDao());
}
@Bean
public MemberInfoPrinter infoPrinter() {
MemberInfoPrinter infoPrinter = new MemberInfoPrinter();
infoPrinter.setMemberDao(memberDao());
infoPrinter.setPrinter(memberPrinter());
return infoPrinter;
}
}
스프링에서의 DI의 방법
필드 주입
Field -Based Dependency Injection - 지양하는 방법
장점
- 선언이 가장 간단하다.
단점
- 의존 주입이 많이 질 수록 관계가 잘 보이지 않는다.
→ 순환 참조를 Compile 과정에서 발견하지 못한다. - SRP - 단일 책임 원칙에 반한다
- SRP (Single Responsibility Principle)
: 클래스는 단 한개의 책임을 가져야한다. - Autowired 선언 아래 개수 제한 없이 무한히 추가가 가능하기 때문이다.
- SRP (Single Responsibility Principle)
- 강한 결합
: 의존 되고 있는 클래스를 수정하려면 의존 하는 클래스도 수정해줘야한다.
생성자 주입
Constructor - Based Dependency Injection - 권장 방법
장점
- 빈 객체를 생성하는 시점에 모든 의존 객체가 주입된다
- 필수적으로 사용해야 하는 레퍼런스 없이는 인스턴스를 만들지 못하게 강제한다.
- 순환 참조를 방지 할 수 있다.
→ 순환 참조를 Compile 과정에서 발견 가능하다. - 테스트 코드를 작성하기 쉽다.
단점
- 어떤 의존 객체가 주입되는지 보려면 생성자를 확인해야한다
ex )
//MemberRegisterService.java
public class MemberRegisterService {
private MemberDao memberDao;
//생성자를 통해 의존 객체를 주입 받음
public MemberRegisterService(MemberDao memberDao) {
//주입 받은 객체를 필드에 할당
this.memberDao = memberDao;
}
public Long regist(RegisterRequest req) {
//주입 받은 의존 객체의 메서드를 활용
Member member = memberDao.selectByEmail(req.getEmail());
...
memberDao.insert(newMember);
return newMember.getId();
}
}
//BeanConfig.java
@Bean
public MemberRegisterService memberRegSvc() {
return new MemberRegisterService(memberDao());
}
수정자 메서드를 통한 주입
Setter - Based Dependency Injection
장점
- 메서드 이름을 통해 어떤 의존 객체가 주입되는지 바로 알 수 있다.
- 의존성 주입이 선택적으로 필요한 경우에 사용 가능하다.
단점
- 필요한 의존 객체를 전달하지 않아도 빈 객체가 생성되기 때문에 NullPointerException 이 발생 가능하다.
- final 선언 불가능
ex)
//Service.java
public void setMemberDao(MemberDao memberDao) {
this.memDao = memberDao;
}
public void setPrinter(MemberPrinter printer) {
this.printer = printer;
}
//AppCtx.java
@Bean
public MemberInfoPrinter infoPrinter() {
MemberInfoPrinter infoPrinter = new MemberInfoPrinter();
infoPrinter.setMemberDao(memberDao());
infoPrinter.setPrinter(printer);
return infoPrinter;
}
//main.java
private static void processInfoCommand(String[] arg) {
if (arg.length != 2) {
printHelp();
return;
}
MemberInfoPrinter infoPrinter =
ctx.getBean("infoPrinter", MemberInfoPrinter.class);
infoPrinter.printMemberInfo(arg[1]);
}
@Bean
- 객체를 Singleton 빈 객체로 생성해 놓고 어디서든 불러서 사용 가능하게 만든 것
- 클래스를 재사용하기 편하게 함
- IoC방식으로 관리하는 오브젝트. Managed object라고 불리기도 한다. 스프링은 Bean 객체의 생성 및 제어를 담당한다.
-
IOC 방식(Inversion of Control) - 제어의 역전
: 객체의 생성, 생명주기 관리를 컨테이너가 제어 하는 것- 분류
- DL : 저장소에 저장되어 있는 Bean에 접근하기 위해 컨테이너가 제공하는 API를 이용하여 Bean을 Lookup 하는 것
- DI : 각 클래스간의 의존관계를 빈 설정 정보를 바탕으로 컨테이너가 자동으로 연결해주는 것
- 분류
-
Spring DI 컨테이너
- Spring DI 컨테이너가 관리하는 객체를 Bean이라고 하고 Bean을 관리하는 컨테이너를 BeanFactory라고 부른다.
- BeanFactory : Bean을 등록 생성, 조회, 반환 관리를 한다.
- ApplicationContext : BeanFactory에 확장판으로 각종 부가 서비스를 제공한다.
- Spring DI 컨테이너가 관리하는 객체를 Bean이라고 하고 Bean을 관리하는 컨테이너를 BeanFactory라고 부른다.
-
결론
생성자 주입을 사용한다.
- 테스트 코드 작성하기 좋다.
: Class들이 많아지고, 외부 의존성(Jpa 등)이 발생하다 보면, 생성자 주입을 통해 Test 코드 작성이 쉬워지는 것이 매우 큰 장점이라는 것을 경험 할 수 있다.
- 순환 참조, NullPointException(없는 객체 주입) 예방 가능하다.
: class들이 커지고 Layer들이 깊어지면 Human Error로 순환 참조, NullPointException 이 발생 할 수 있는데, 이 때 Compile 과정에서 감지할 수 있다.
- final 선언 가능
: 생성자를 통해 주입하기 때문에 final로 필드 선언 가능하고, immutable(불변) 하다는 것을 드러낼 수 있다.
: Class들이 많아지고, 외부 의존성(Jpa 등)이 발생하다 보면, 생성자 주입을 통해 Test 코드 작성이 쉬워지는 것이 매우 큰 장점이라는 것을 경험 할 수 있다.
: class들이 커지고 Layer들이 깊어지면 Human Error로 순환 참조, NullPointException 이 발생 할 수 있는데, 이 때 Compile 과정에서 감지할 수 있다.
: 생성자를 통해 주입하기 때문에 final로 필드 선언 가능하고, immutable(불변) 하다는 것을 드러낼 수 있다.
출처: 초보 웹 개발자를 위한 스프링 5 프로그래밍 입문 - 최범균 저'
Author And Source
이 문제에 관하여([Spring] 스프링 의존성 주입, 관리 (DI)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@jisoo/Spring-DI저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)