Programming/Spring framework

DI의 두가지 방식: 생성자 방식, 세터 메서드 방식, @Configuration 설정클래스의 @Bean 설정은 항상 스프링에서 싱글톤객체로 등록함. [최범균 스프링 5] 2021-05-29

최동훈1 2021. 5. 28. 14:53

우선 빈 객체를 등록할때 설정클래스에서(@Configuration) 어떤 빈 객체의 의존 주입하는 방식은 2가지가 있다. 

 

1.첫번째는, 앞 포스트에서 설명했던 것 처럼 생성자의 파라미터로 의존의 대상이되는 객체를 넣어주는 것이다.[ 실제@Configuration 클래스에서는 객체설정메서드(@Bean) 를 파라미터로 넘겨줌]

 

장점: 빈 객체를 설정할때 이미 그 클래스가 의존하는 모든 객체를 주입받기 때문에, 완벽한 상태(그 클래스가 의존하는 모든 클래스의 메서드 들을 사용가능) 에서 빈 객체를 사용 가능하다.

 

단점: 생성자의 파라미터 개수가 많을 경우, 어떤 객체가 들어가야할지 모를 수 있어서 생성자 코드를 직접 확인해 봐야한다....라고 책에서 주장하는데 나는 그렇게 생각하지 않는다.IntelliJ는 애초에 입력하는 과정에서 다 알려주지 않나..?? 아무래도 3년 전에 쓰여진 책이라서 이런걸 단점이라고 생각하는거 같다.

 

2. 두번째는 설정 메서드 Setter를 이용하는 방식이다. 그 클래스의 인스턴스변수가 Private일때 set 함수를 사용자가 만들어서 인스턴스 변수를 초기화 해주는 방법은 이미 내가 자바 공부하면서 많이 써봐서, 특별히 다른점은 없다.

 

장점:

자바빈 규칙

-메서드 이름이 set으로 시작한다.

-set뒤에 첫 글자는 대문자로 시작한다.

-파라미터가 1개이다.

-리턴타입이 void 이다.

 

자바빈 규칙에 의거해서 setter을 만들어 주기 때문에, 이름을 통해 어떤 객체가 주입되는지 쉽게 유추할 수 있다.

 

단점: 생성자를 이용한 주입처럼 한번에 모든 객체가 다 주입되는것이 아니고, 세터 방식은 세터 메서드를 사용해서 필요한 의존객체를 전달하지 않아도 빈 객체가 생성되기 때문에 객체를 사용하는 시점에 NullPointerException이 발생할 수 있다.

 

아래의 코드는 빈 설정클래스@Configuration에서 생성자 방식과 설정메서드 방식을 보여준다. 비교해 보자.

 

@Configuration
public class AppConf2 {
	@Autowired
	private MemberDao memberDao;
	@Autowired
	private MemberPrinter memberPrinter;
	
	@Bean
	public MemberRegisterService memberRegSvc() {
		return new MemberRegisterService(memberDao);
	}
	
	@Bean
	public ChangePasswordService changePwdSvc() {
		ChangePasswordService pwdSvc = new ChangePasswordService();
		pwdSvc.setMemberDao(memberDao);
		return pwdSvc;
	}
	
	@Bean
	public MemberListPrinter listPrinter() {
		return new MemberListPrinter(memberDao, memberPrinter);//바로 앞 포스트에서 설명한, 생성자의
        //파라미터가 2개일때 의존주입하는 것.
	}
	
	@Bean
	public MemberInfoPrinter infoPrinter() {
		MemberInfoPrinter infoPrinter = new MemberInfoPrinter();
		infoPrinter.setMemberDao(memberDao);//설정 메서드 세터를 사용한 DI. 이 메서드가 없어도 빈객체 설정됨.
		infoPrinter.setPrinter(memberPrinter);
		return infoPrinter;
	}
	
	@Bean
	public VersionPrinter versionPrinter() {
		VersionPrinter versionPrinter = new VersionPrinter();
		versionPrinter.setMajorVersion(5);
		versionPrinter.setMinorVersion(0);
		return versionPrinter;
	}
}

 

여기서 궁금증이 일어날 수 있다. 바로 @Bean메서드에 생성자 방식이던, 세터방식이던 설정메서드를 호출함으로써, 의존주입 하는 것을 볼 수 있다. 예) memberDao()메서드는 거의 모든 클래스의 생성자에 호출된다.(의존주입된다.) 

그렇다면, 각각 memberDao() 는 클래스마다 서로다른 객체를 새로 생성해서 의존주입되는 것일까? 그러니깐 내 궁금증을 이해하기 쉽게 풀어쓰자면, memberRegSvc()빈 객체 가 의존하는 memberDao()와 changePwdSvc() 빈객체가 의존하는 memberDao()는 다른 것이냔 말이다.

 

결론은 아니다. 왜냐하면, 스프링 컨테이너에 @Configuration(설정클래스)을 통해 빈객체를 등록할때,

[AnnotationConfigApplicationContext 

컨테이너에서 알아서 각 @Bean 메서드에 구현된 객체들을 오로지 1개만 생성해서 빈객체로 등록시킨다.

왜냐하면, 스프링은 설정클래스를 그대로 사용하지 않고, 설정 클래스를 새로 만들어서 사용하기 때문이다.

 

또 알게된 단편적 지식들

1. 스프링 컨테이너에 등록할때, 여러개의 설정클래스로 등록 가능하다. 

AbstractApplicationContext ctx=
  new AnnotationConfigApplicationContext(AppConf1.class,AppConfig2.class)

2.@Autowired 이 붙은 참조변수는 스프링이 자동으로 스프링 빈에 등록된 객체들 중 해당 타입의 빈을 찾아서 필드에 할당한다. 즉, 따로 @Configuration 클래스 설정정보에서 새로운 @Bean을 일일이 등록 할 필요 없다는 이야기이다. 

즉 ,위 예시로 예로 들자면, 만약 AppConf1 설정클래스에 memberDao가 등록되 있으면, AppConfig2 설정클래스에서 빈 객체를 등록할때 memberDao 객체를 의존주입 해야 한다면, AppConfig2 설정 클래스에서 다시 @Bean 이렇게 memberDao를 설정하는 것이 아니라, @AutoWired 애너테이션을 사용하면, 이미 AppConf1에 memberDao 빈 객체가 등록되어 있으므로 스프링이 알아서 찾아서 AppConfg2에 주입해준다.

 

3.  @Import 애너테이션을 다른 설정 클래스를 사용하면 아예 함께 사용할 설정 클래스를 지정해서, 한개의 클래스만 컨테이너에 등록해도, 여러개의 설정 클래스를 등록한 효과를 보여준다.

 

page 102 공부완료.

순공부 /실습시간 2시간.