본문 바로가기

Spring

스프링 3.1 vol2 - 빈 등록 방법

1) XML

가장 대중적으로 사용되고 있는 방식이다.

<bean id="aaa" class="xxx.yyy.zzz.AAA">
   <property name="prop"></property>
</bean>

와 같은 방법으로 간단하게 하나의 빈을 생성 가능하다.
위는 간단하게 예시를 든것이며, 1.2.1에서 봤듯이 추가적인 옵션을 지정할 수 있다.

이러한 XML을 통한 빈 등록 방식은 대표적으로 2가지 정도의 문제점을 가진다.

첫째로, 빈의 성격구분을 하기 힘들어진다.
등록된 빈 들은 각각 성격이 존재한다. 컨트롤러, 서비스. 레파지토리, 설정 등..
하지만 위와 같은 형태로 빈을 등록하게 되면, 모든 빈이 일관되게 태그로 정의된다.
그렇게 되면 설정파일만 보고는 해당 빈의 역할을 파악하기 힘들어진다.

스프링은 이러한 상황을 위해 10여가지의 namespace와 custom-tag들을 제공해준다.
예를 들면, AOP의 포인트컷으로 등록된 빈은 아래와 같이 표현 가능하다.

<aop:pointcut id="pointcut" expression="~~~" />

네임스페이스로 가시성을 주는 것 외에도 커스텀 태그를 사용하였기 때문에
각각의 태그에 맞는 추가적인 로직을 넣어둘 수 있게 된다.
즉, 우리가 포인트컷을 하나 등록하기 위해 해야하는 많은 작업들을
저 태그 하나 선언하는 것으로 대체 가능해지는 것이다.

aop외에도 tx, context, jpa 많은 태그들을 사용할 수 있고,
이는 xml 파일 작성에 대해서 충분한 장점이 된다!

게다가 이 외에도 사용자가 직접 커스텀태그를 정의할 수 있다.
커스텀 태그를 정의하는 것은 스프링 레퍼런스 문서를 보면 어렵지 않게 작성할 수 있다고 한다(ㅋㅋ)

둘째 문제점으로, 어플리케이션의 크기가 조금만 커져도 xml 파일에 작성되는 빈의 양이 감당하기 힘들어진다는 점이다.
보통 웹 어플리케이션에서 사용하는 빈의 양은 적어도 수십개에서 많으면 수백개에 달한다.
근데 이 빈들을 전부 xml 파일에 등록한다고 생각을 해보면.. 벌써부터 힘들어진다 ㅜㅜ
xml 파일을 여러개로 나눠서 작성할 수도 있겠지만, 근본적인 해결책은 되지 못한다.

이러한 상황을 대비하여, 스프링에서는 빈 스캐닝 이라는 기능을 제공해준다.
간단하게 설명하자면, xml 파일에 모든 빈을 다 명시할 필요없이
빈으로 등록할 클래스에 스테레오 타입 어노테이션을 붙여 놓는다.
그러면 빈 스캐너가 스캐닝을 하면서, 해당 어노테이션이 붙은 클래스를 자동으로 빈으로 등록해준다!

스테레오 타입 어노테이션은 아래와 같다.

빈 스캐너는 기본이 되는 @Component 어노테이션이 붙은 클래스를 빈으로 등록한다.
이 외에 @Repository@Service@Controller는 @Component을 메타 어노테이션으로 갖는 어노테이션이다. 역할의 구분을 위해 재정의 한것이며, 사용자가 원하면 @Component 어노테이션을 구현하여 새로운 어노테이션을 정의해도 된다.

그렇다면 이제 빈 스캐너에게 스캐닝을 시켜야 하는데, 빈 스캐너는 어떻게 사용할까?

아주 편리하게도 context 스키마에서 해당 기능을 수행하는 커스텀 태그를 제공해주고 있다.
그래서 사용자는 설정파일에

<context:component-scan base-package="xxx.yyy.zzz" />

로 지정만 해주면 끝이 난다!!
이렇게 해두면 IoC 컨테이너가 초기화 될 때 base-package 내부에 있는 클래스들을 스캔하고,
스테레오타입 어노테이션이 붙은 클래스들을 전부 빈으로 등록해준다.
굉장히 편리해졌다..

물론 단점도 존재한다.
xml 처럼 상세한 설정이 불가능해지고, 하나의 클래스당 하나의 빈 만 등록할 수 있다는 점이다.

<context:component-scan /> 태그를 사용하는 방식 외에도
빈 스캐너가 들어간 어플리케이션컨텍스트 ( e.g. AnnotaionConfigWebApplicationContext 등)
을 사용하여 빈 스캐닝을 수행할 수 있다.

web.xml에 contextClass로 해당 클래스를 입력하고,
contextConfig에 base package를 입력하면 적용 가능하다.
참고로 DispatcherServlet의 default 어플리케이션컨텍스트는 XmlWebApplicationContext 이다.

2) Java Config

스프링 3.0 부터 기존의 xml 방식을 벗어나 java로 설정 파일을 작성할 수 있게 되었다.
java로 설정파일을 작성하게 되면 IDE의 기능을 최대한 활용할 수 있기 때문에
작성하기도 쉽고, 이해하기도 쉬워진다.
게다가 IDE에서 문법 체크를 지원해주므로 실수할 일도 줄어들게 된다.

java config의 형태는 아래와 같다.

@Configuration // 설정파일임을 명시
public class rootConfig(){
	@Bean // <bean> 태그와 동일
	public DataSource dataSource(){
               DataSource dataSource = new SimpleDriverDataSource(); // new ?!
		// ...
		return dataSource;
	}

	@Bean
	public EntityManagerFactory entityManagerFactory(){
		// ..

		entityManagerFactory.setDataSource(dataSource()); // DI
		
		// ...

		return entityManagerFactory;
	}

	@Bean
	public SqlSessionFactory sqlSessionFactory(){
		// ..

		sqlSessionFactory.setDataSource(dataSource()); // DI

		// ..

		return sqlSessionFactory;
	}

       // ...
}

@Configuration과 @Bean 어노테이션만 있으면 간단하게 설정파일을 작성할 수 있다.
DI는 보다시피 메서드를 해당 메서드를 직접 호출하며 진행하고 있음을 볼 수있다.

근데 여기서 일반 자바코드와 조금 다르게 행동하는 부분이 있는데,
바로 객체를 생성하는 new 부분이다.

지금 RootConfig가 IoC 컨테이너에 의해 초기화되면서 dataSource() 메서드는 총 2번 호출되었다.
근데 dataSource 메서드의 내부를 보면, new 연산자를 사용하고 있다.
그렇다면 sqlSessionFactory에 들어간 dataSource와, entityManagerFactory에 들어간 dataSource는 서로 다른 오브젝트란 말이 된다.

하지만 그렇지 않다.
@Configuration 어노테이션을 사용하고, 스프링 컨테이너가 관리하는 설정파일이 된 순간부터 위의 자바코드는 일반적인 자바코드와는 조금 다르게 동작한다.
자바코드라고 보기전에, 메타정보라고 봐야한다.

스프링이 사용하는 메타정보에 scope 값의 default는 싱글톤이었고,
지금 이 java config 또한 메타정보기 때문에, 그리고 사용자가 따로 scope를 지정해주지 않았기때문에
스프링 컨테이너에 의해 새롭게 생성되지 않고, 기존에 있던 오브젝트를 반환해주게 된다.

빈 설정정보 구성 전략

현재 챕터에서는 3가지의 구성전략을 소개하고 있다.

  1. only xml
  2. xml + bean scanning
  3. only bean scanning

only bean scanning 방식은 java config를 필수로 사용해야 한다.
스프링은 스테레오 타입 뿐만 아니라, @Configuration이 붙은 java config 파일 또한 빈 스캐닝의 대상으로 여긴다.

위와 같은 특성을 가지고 only bean scanning 전략을 구성하면 다음과 같다.

  1. AnnotationConfigWebApplicationContext를 어플리케이션 컨텍스트로 사용한다.
  2. base package에 java config의 경로를 넣어준다.
    즉, @Configuration 파일까지 빈으로 등록하면서 내부의 @Bean 들까지 다 등록되게 하려는 전략이다.

이 챕터에서는 xml의 커스텀태그들이 java config로 넘어온것을 언급하지 않아 저 방법을 사용한 것으로 보인다..
스프링 3.1부터 component-scan 과 같은 커스텀 태그들을 java config에서도 사용가능해졌으므로, 2번의 구성전략에 java config를 사용하는것이 훨씬 수월해졌다.

※ 빈 스캐닝 유의사항

계층 컨테이너를 구성했을떄의 주의점 중 하나이다.
빈 스캐닝은 하나의 부모<>자식간에 이어지지 않고, 컨테이너 자신에 대해서만 일어나는 행위라
부모, 자식간 빈 스캐닝의 대상이 똑같으면 덮어씌워지는 문제가 발생한다!
스프링에서는 빈이 중복 등록되는것을 별로 문제삼지 않기 떄문이다.

예를 들어
부모 컨텍스트에서 빈 스캐닝을 통해 aop가 적용된 빈들이 생성되었는데
만약 자식 컨텍스트에서 똑같은 범위의 빈 스캐닝을 하게 될 경우가 있다.
이럴 경우 aop가 적용된 빈, 적용되지 않은 빈이 중복으로 생성되게 되는데
스프링이 빈을 찾을 때 자식 -> 부모의 순서로 찾기 떄문에 aop가 적용된 빈을 사용하지 못하는 경우가 발생한다 !
실제로도 빈 스캐너를 등록했더니 aop가 적용되지 않는다는 질문이 많다고 합니다..