일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 |
- 선형판별분석
- graphical models
- vector미분
- 1차예선
- secant
- 근구하기
- Fisher discriminant analysis
- chapter01
- MySQL
- 2018
- 선형분류
- falsePosition
- 이것이 MySQL이다
- 인공지능
- 스터디
- directed graphical model
- 로지스틱 회귀
- undirected graphical model
- bisection
- chapter02
- Perceptron Convergence theorem
- CH01
- 알고리즘
- 개발순서
- SCPC
- 5397번
- 델타 rule
- 자바ORM표준JPA프로그래밍
- Numerical optimization
- 알고리즘대회
- Today
- Total
computer_study
[Spring] 03. 스프링 DI 본문
목차
1. 의존이란?
2. DI를 통한 의존 처리
3. DI와 의존 객체 변경의 유연함
4. 예제 프로젝트 만들기
5. 객체 조립기
6. 스프링의 DI설정
7. @Configuration 설정 클래스의 @Bean 설정과 싱글톤
8. 두 개 이상의 설정 파일 사용하기
9. getBean() 메서드 사용
10. 주입 대상 객체를 모두 빈 객체로 설정해야 하나?
1. 의존이란?
한 클래스가 다른 클래스의 메서드를 실행할 때를 의존이라 표현.
예시
- MemberRegisterService라는 class에서 memberDao라는 객체를 생성
- MemberRegisterService에서 현재 요청이 들어온 사용자가 이미 존재하는지 여부를 확인하고, 없다면 DB에 추가하기 위해 memberDao의 selectByEmail(), insert() 메서드를 사용
- 이 때 MemberRegisterService 클래스가 MemberDao클래스에 의존한다고 한다.
위와같은 경우처럼 클래스 안에서 의존 할 객체를 직접 생성해서 할당할 수 있음.
하지만 유지보수 관점에서 문제 발생 가능성이 존재.
해결방법
- DI (Dependency Injection) -> 스프링 관련
- 서비스 로케이터
2. DI를 통한 의존 처리
DI 사용 안했을 때 (위의 객체 생성 예시)
public class MemberRegisterService {
private MemberDao memberDao = new MemberDao();
public void regis(RegisterRequest req){
Member member = memberDao.selectByEmail(req.getEmail());
}
}
DI 사용 했을 때
public class MemberRegisterService {
private MemberDao memberDao;
public MemberReigsterService(MemberDao memberDao){
this.memberDao = memberDao;
}
public void regis(RegisterRequest req){
Member member = memberDao.selectByEmail(req.getEmail());
}
}
이후 MemberRegisterService 클래스를 사용한다면 아래와 같이 의존 객체를 생성자를 통해 주입해야 된다.
MemberDao dao = new MemberDao();
MemberRegisterService svc = new MemberRegisterService(dao);
복잡해보이지만 수정의 유연함 때문에 DI를 사용한다.
3. DI와 의존 객체 변경의 유연함
DI를 사용하면, 코드 수정 시 수정 할 부분이 줄어든다.
예시
- MemberRegisterService , ChangePasswordService가 있고 각각 MemberDao객체를 생성했다 가정.
- 캐시 기능 적용을 위해 MemberDao를 상속받은 CachedMemberDao클래스가 생성됐다면
- MemberRegisterService , ChangePasswordService 두 곳 모두 코드를 변경해주어야 된다.
public class MemberRegisterService{
private MemberDao memberDao = new MemberDao();
}
public class ChangePasswordrService{
private MemberDao memberDao = new MemberDao();
}
/////////////// 아래처럼 변경해야됨 ////////////////////////
public class MemberRegisterService{
private MemberDao memberDao = new CachedMemberDao();
}
public class ChangePasswordrService{
private MemberDao memberDao = new CachedMemberDao();
}
DI를 사용했다면 다음처럼 바꿀 수 있다.
MemberDao memberDao = new MemberDao();
MemberRegisterService regSvc = new MemberRegisterService(memberDao);
ChangePasswordrService pwdSvc = new ChangePasswordrService(memberDao);
/////////////// 아래처럼 변경하면 됨 /////////////////////
MemberDao memberDao = new CachedMemberDao(); // <- 얘만 변경
MemberRegisterService regSvc = new MemberRegisterService(memberDao);
ChangePasswordrService pwdSvc = new ChangePasswordrService(memberDao);
4. 예제 프로젝트 만들기
5. 객체 조립기
위 3번에서 클래스 변경 시 객체를 주입하는 곳 하나만 변경하면, 쉽게 변경할 수 있다고 하였다.
이렇게 변경을 할 수 있는 곳이 객체 조립기(assembler)이다.
main 메서드에서 의존 대상 객체를 생성하고 주입 할 수도 있지만, assembler 객체를 따로 작성하는 것이 더 좋은 방법이다.
위 예제들을 assembler class에 따로 작성할 수 있다는 것이다.
public class Assmebler {
private MemberDao memberDao;
private MemberRegisterService regSvc;
private ChangePasswordService pwdSvc;
public Assembler(){
memberDao = new MemberDao();
regSvc = new MemberRegisterService(memberDao);
pwdeSvc = new ChangePasswordService();
}
}
캐시 기능 적용을 위해 MemberDao를 상속받은 CachedMemberDao클래스가 생성됐다면
public class Assmebler {
private MemberDao memberDao;
private MemberRegisterService regSvc;
private ChangePasswordService pwdSvc;
public Assembler(){
memberDao = new CachedMemberDao(); // -> 해당 부분만 바꾸면 되는 것
regSvc = new MemberRegisterService(memberDao);
pwdeSvc = new ChangePasswordService();
}
}
6. 스프링의 DI설정
스프링은 위 조립기와 유사한 기능을 제공하고있다.(범용 조립기 같은 느낌)
스프링을 이용한 객체 조립과 사용
스프링 사용 전 간단 개념
- @Configuration 애노테이션을 붙여야 스프링 설정 클래스를 사용할 수 있다
- @Bean 애노테이션은 해당 메서드가 생성한 객체를 스프링 빈이라 설정한다.
- @Bean
public MemberDao memberDao(){
...
}
라면 memberDao라는 이름으로 스프링에 등록된다.
- @Bean
- 스프링 컨테이너에서 객체 생성, 의존객체 주입 등을 해주는 것이므로 스프링 컨테이너를 생성해주어야 된다
AnnotationConfigApplicationContext 클래스 사용 - getBean()메서드를 이용해서 사용할 객체를 구할 수 있다.
5번에서 작성했던 객체 조립기 코드를 스프링 스타일로 작성하면
package main;
...
import java.io.IOException
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import config.AppCtx
public class MainForSpring{
private static ApplicationContext ctx = null;
public static void main(String[] args) throws IOException{
ctx = new AnnotationConfigApplicationContext(AppCtx.class);
...
}
privavte static void processNewCommand(String[] arg){
MemberRegisterService regSvc = ctx.getBean("memberRegSvc", MemberRegisterService.class);
...
}
private static void processChangeCommand(String[] arg){
ChangePasswordService changePwdSvc = ctx.getBean("changePwdSvc", ChangePasswordService.class);
...
}
}
이 떄 AppCtx는
package config
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import spring.ChangePawwrodService;
import spring.MemberDao;
import spring.MemberRegisterService;
@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;
}
}
DI방식
- 생성자 방식
- 위 예제들에서 주입한 방식들이다. (2번, 3번에서 DI를 사용한 경우)
- 생성자에 전달 할 의존 객체가 두 개 이상이어도 동일한 방식으로 주입할 수 있다.
- 장점
- 빈 객체를 생성하는 시점에 모든 의존 객체가 주입된다. (객체를 사용할 때 완전한 상태로 사용 가능)
- 단점
- 생성자의 파라미터 개수가 많을 경우 각 인자가 어떤 의존 객체를 설정하는지 알아내려면 생성자 코드를 확인해야 한다.
private MemberDao memberDao;
private MemberPrinter printer;
public MemberListPrinter(MemberDao memberDao, MemberPrinter printer){
this.memberDao = memberDao;
this.printer = printer;
}
- 세터(setter) 메서드 방식
- 세터 메서드, 자바빈 규칙
- 메서드 이름이 set으로 시작한다.
- set 뒤에 첫 글자는 대문자로 시작한다.
- 파라미터가 1개이다.
- 리턴 타입이 void이다.
- 장점
- 세터 메서드 이름을 통해 어떤 의존 객체가 주입되는지 알 수 있다.(생성자의 파라미터 개수가 많을 때 유리)
- 단점
- 의존객체를 전달하지 않아도 빈 객체가 생성되기에 객체를 사용하는 시점에 NullPointerException이 발생할 수 있다.
- 세터 메서드, 자바빈 규칙
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;
}
}
...
import sprint MemberInfoPrinter;
@Configuration
public class AppCtx{
@Bean
public MemberInfoPrinter infoPrinter(){
MemberInfoPrinter infoPrinter = new MemberInfoPrinter();
infoPrinter.setMemberDao(memberDao());
infoPrinter.setPrinter(memberPrinter());
return infoPrinter;
}
}
7. @Configuration 설정 클래스의 @Bean 설정과 싱글톤
스프링 컨테이너는 @Bean이 붙은 메서드에 대해 한 개의 객체만 생성
예를들어
@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;
}
이 때, memberDao()를 볓 번 호출하더라도 항상 같은 객체를 리턴한다.
8. 두 개 이상의 설정 파일 사용하기
두 개 이상의 설정파일 일 때, 스프링 컨테이너를 생성하는 예시
ctx = new AnnotationConfigApplicationContext(AppConf1.class, AppConf2.class);
각 설정파일 별 @Autowired 애노테이션을 사용해서 자동 주입을 할 수 있다.
자동주입 시 @Bean메서드에서 의존 주입을 위한 코드를 작성하지 않아도 된다.(알맞는 Bean을 자동 주입)
...
@Configuration
public class AppConf1{
@Bean
public MemberDao memberDao(){
return new MemberDao();
}
@Bean
public MemberPrinter memberPrinter(){
return new MemberPrinter();
}
}
...
import org.spring framework.beans.factory.annotation.Autowired;
...
@Configuration
public class AppConf2{
@Autowired
private MemberDao memberDao;
@Autowired
private MemberPrinter memberPrinter;
...
}
위 예시에서 스프링 컨테이너는 AppConf2객체를 빈으로 등록하고 @Autowired 애조테이션이 붙은 두 필드 memberDao와 memberPrinter에 해당 타입의 빈 객체를 주입한다.
@Import 애노테이션을 사용하여 두 개 이상의 설정파일을 사용 할 수도 있다.
@Configuration
@Import(AppConf2.class)
public class AppConfImport{
@Bean
public MemberDao memberDao(){
return new MemberDao();
}
@Bean
public MemberPrinter memberPrinter(){
return new MemberPrinter();
}
}
위와 같이 작성했다면, AppConfImport설정클래스 사용 시 AppConf2설정 클래스도 함께 사용할 수 있다.
ctx = new AnnotationConfigApplicationContext(AppConfImport.class);
@Import 애노테이션에는 두 개 이상의 설정클래스도 지정할 수 있다
@Configuration
@Import({AppConf1.class, AppConf2.class})
public class AppConfImport{
....
}
9. getBean() 메서드 사용
getBean() 메서드로 사용할 빈 객체를 구할 수 있다.
VersionPrinter versionPrinter = ctx.getBean("versionPrinter", VersionPrinter.class);
빈 이름 지정 없이 타입만으로 구할 수도 있다.
VersionPrinter versionPrinter = ctx.getBean(VersionPrinter.class);
다만, 타입이 하나고 빈이 두 개로 설정되어있다면, 익셉션이 발생한다.
10. 주입 대상 객체를 모두 빈 객체로 설정해야 하나?
- 빈으로 설정하지 않아도 동작은 한다.
- 빈으로 등록한다는 의미 = 스프링 컨테이너가 객체를 관리한다.
- 자동 주입, 라이프 사이클 관리 등 객체 관리를 제공받고 싶지 않고, getBean() 메서드로 구할 필요가 없다면 사용하지 않아도 된다.
- 최근엔 의존 자동주입 기능을 거의 사용하는 추세기에 빈으로 등록
'스터디 > 스프링5 프로그래밍 입문' 카테고리의 다른 글
[Spring] 07. AOP 프로그래밍 (0) | 2022.04.18 |
---|---|
[Spring] 06. 빈 라이프사이클과 범위 (0) | 2022.04.17 |
[Spring] 05. 컴포넌트 스캔 (0) | 2022.04.09 |
[Spring] 04. 의존 자동 주입 (0) | 2022.04.09 |
[Spring] 00.스프링 개요 (0) | 2022.03.31 |