2021. 8. 3. 23:37ㆍCSE/Spring
1. 의존이란?
- 의존 : 객체 간의 의존, 한 클래스가 다른 클래스의 메서드를 실행할 때, 이를 '의존'한다고 표현한다
ex) MemberRegisterService 클래스가 MemberDao 클래스의 메서드를 실행하면, MemberRegisterService 클래스가 MemberDao 클래스에 의존한다고 표현한다
- 가장 쉬운 방법은 의존 대상의 객체를 직접 생성하여 의존대상.메서드() 형식으로 사용하는 것
=> 유지보수 관점에서 문제점 유발 가능
2. DI를 통한 의존 처리
- DI (Dependency Injection) : 의존 주입, 의존하는 객체를 직접 생성하는 대신 의존 객체를 전달받는 방식 사용
public class MemberRegisterService{
private MemberDao memberDao;
public MemberRegister(MemberDao memberdao){
this.MemberDao = memberDao;
}
}
public class Example{
public static void main(String[] args){
MemberDao memberDao = new MemberDao();
MemberRegisterService swc = new MemberRegisterService(memberDao);
//의존 객체를 생성자를 통해 주입한다 => 의존 주입
}
}
3. DI와 의존 객체 변경의 유연함
- 만약에 MemberDao클래스에 의존하는 객체가 많은데, MemberDao 클래스를 상속한 CachedMemberDao 클래스에 의존하도록 바꾸기 위해서는 DI가 편리하다.
=> DI가 없다면 모든 의존하는 클래스의 파일들을 열어서 다음과 같이 변경해야 하기 때문에 불편함
MemberDao memberDao = new MemberDao();
//위를 아래로 바꾼다
MemberDao memberDao = new CachedMemberDao();
=> DI를 사용하면 의존주입 시 사용하는 의존 객체만 바꿔주면 된다.
4. 객체 조립기
- 객체를 생성하고 의존 객체를 주입해주는 클래스를 따로 작성하고 이를 객체 조립기라고 함.
- 의존 객체를 주입한다는 것은 서로 다른 두 객체를 조립한다고 생각할 수 있기 때문
import MemberDao;
import MemberRegisterService;
import ChangePasswordService;
public class Assembler{
private MemberDao memberDao;
private MemberRegisterService regSvc;
private ChangePasswordService pwdSvc;
public Assembler(){
memberDao = new MemberDao();
regSvc = new MemberRegisterService(memberDao);
pwdSvc = new ChangePasswordService();
pwdSvc.setMemberDao(memberDao);
}
public MemberDao getMemberDao(){
return memberDao;
}
public MemberRegisterService getMemberRegisterService(){
return regSvc;
}
public ChangePasswordService getChangePasswordService(){
reutnr pwdSvc;
}
}
5. 스프링의 DI 설정
- 스프링이 DI를 지원하는 조립기와 유사한 기능을 제공한다.
import MemberDao;
import MemberRegisterService;
import ChangePasswordService;
@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());
reutnr pwdSvc;
}
}
- @Bean 애노테이션이 붙여진 각 메서드마다 한 개의 빈 객체를 메서드의 이름으로 생성한다.
-이 후, 컨테이너를 생성해서 getBean()메서드를 이용해서 객체를 사용한다.
public class MainForSpring {
private static ApplicationContext ctx = null;
public static void main(String[] args) throws IOException {
ctx = new AnnotationConfigApplicationContext(AppCtx.class);
MemberRegisterService regSvc = ctx.getBean("memberREgSvc", MemberRegisterService.class);
}
}
=> AnnotationConfigApplicationContext를 사용해서 스프링 컨테이너를 생성
=> 스프링 컨테이너로부터 이름이 "memberRegSvc"인 빈 객체를 구한다.
6. DI 방식
(1) 생성자 방식
* 장점 : 빈 객체를 생성하는 시점에 모든 객체가 주입된다.
- 생성자를 통해 의존 객체를 주입받아 필드에 할당한다.
public class MemberRegisterService {
private MemberDao memberDao;
public MemberRegisterService(MemberDao memberDao) {
this.memberDao = memberDao;
}
}
(2) 세터 메서드 방식
* 장점 : 세터 메서드 이름을 통해 어떤 의존 객체가 주입되는지 알 수 있다.
- 세터 메서드는 다음 규칙에 따라 작성한다.
- 메서드 이름이 set으로 시작한다.
- set 뒤에 첫 글자는 대문자로 시작한다.
- 파라미터가 1개이다.
- 리턴타입이 void이다.
public class MemberInfoPrinter {
private MemberDao memDao;
private MemberPrinter printer;
public void setMemberDao(MemberDao memberDao){
this.memDao = memberDao;
}
public void setPrinter(MemberPrinter printer){
this.printer = printer;
}
}
* 싱글톤 잊지 말기!
@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;
}
=>이 경우, memberRegSvc()와 ChangePwdSvc()가 둘다 새로운 MemberDao 객체를 만들어내지않나?
=> 스프링 컨테이너가 생성한 빈은 싱글톤 객체라서 memberDao()는 항상 같은 객체를 리턴함.
=> 왜? : 스프링은 설정 클래스를 상속한 새로운 설정 클래스를 만들어서 사용한다.
7. 두 개 이상의 설정 파일 사용하기
- 스프링은 한 개 이상의 설정 파일을 이용해서 컨테이너를 생성할 수 있다.
- 이 때, 한 설정파일에서 다른 설정파일에 있는 Bean을 사용하기 위해서는 다음과 같이 @Autowired 애노테이션을 사용해서 자동 주입 기능을 사용하면 된다.
@Autowired
private MemberDao memberDao;
@Autowired
private MemberPrinter memberPrinter;
@Bean
public MemberRegisterService memberRegSvc(){
return new MemberRegisterService(memberDao);
}
- 스프링 컨테이너도 거의 동일한 방식으로 생성할 수 있다. 다음과 같이 파라미터에 모든 설정클래스를 전달하면 된다.
ctx = new AnnotationConfigApplicationContext(AppConf1.class, AppConf2.class);
- 또 다른 방법으로는 @Import 애노테이션을 사용하는 방법이다. 다음과 같이 함께 사용할 설정 클래스를 지정한다.
@Configuration
@Import(AppConf2.class)
public class AppConfirmport{
//생략
}
* 스프링은 @Configuration 애노테이션이 붙은 설정 클래스를 내부적으로 스프링 빈으로 등록한다. 그리고 @Autowired가 붙은 대상에 대해 알맞은 빈을 자동으로 주입한다.
8. getBean() 메서드 사용
- getBean() 메서드는 다음과 같은 방식으로 사용 가능하다.
(1) 빈의 이름과 빈의 타입을 통해서
ctx.getBean(빈의 이름, 빈의 타입);
(2) 해당하는 빈의 타입에 하나의 빈 객체만 있을 경우에는 빈의 타입만으로 가능
ctx.getBean(빈의 타입);
+ 주입 대상 객체는 모두 빈 객체여야하나?
=> 일반 객체로 생성해서 주입할 수 있다.
=> 객체를 스프링 빈으로 등록할 때와 등록하지 않을 떄의 차이는 스프링 컨테이너가 객체를 관리하는지 여부이다.
@Configuration
public class AppCtxNoMemberPrinterBeen{
private MemberPrinter printer = new MemberPrinter();
// 생략
public MemberinfoPrinter infoPrinter(){
MemberInfoPrinter infoPrinter = new MemberInfoPrinter();
infoPrinter.setPrinter(printer); //빈이 아님
return infoPrinter;
}
}
'CSE > Spring' 카테고리의 다른 글
[Spring5입문] AOP 프로그래밍 기초 (0) | 2021.08.09 |
---|---|
[Spring5입문] 빈 라이프사이클과 범위 (0) | 2021.08.09 |
[Spring5입문] 컴포넌트 스캔 (0) | 2021.08.08 |
[Spring5입문] 의존 자동 주입 (0) | 2021.08.04 |
[Spring5 입문] Annotation, Singleton, AnnotationConfigApplicationContext (0) | 2021.07.21 |