원문 : Chapter 2. Components

애노테이션을 사용하여 자바 구조를 계층화 하는 자바 설정은, 개발자들이 자바만을 사용하여 빈을 생성하고 설정할 수 있도록 한다. 간단하게, 개발자는 자바 코드를 사용하여 빈을 만들고 설정하며, 컨테이너가 그것들을 사용하도록 지시한다. 계속 진행하기 전에, Spring의 이상은 설정파일이 어떤 형태든(자바든 XML이든) 상관없이 우지 된다는 것을 유념하라.

JavaConfig에서 사용할 수 있는 가장 중요한 애노테이션들을 살펴보자:

2.1. @Configuration

@Configuration 애노테이션은 설정 클래스를 지칭한다:

@Configuration
public class WebConfiguration {
   // bean definitions follow
}

@Configuration은 클레스에 적용하는 애노테이션이고 설정할 bean들에 기본으로 적용할 것들을 나타낸다.

@Configuration(defaultAutowire = Autowire.BY_TYPE, defaultLazy = Lazy.FALSE)
public class DataSourceConfiguration
     extends ConfigurationSupport {
}

<beans/> 태그와 동일하게 생각하면 된다. @Configuration 애노테이션을 달고 있는 클레스는 유틸리티 메소드를 제공하는 ConfigurationSupport 를 확장함을 나타낸다.

2.2. @Bean

이름이 암시하듯이, @Bean 애노테이션은 bean 정의(<bean/> 태그)를 나타낸다. 간단한 예제로 살펴보자.

@Bean (scope = DefaultScopes.SESSION)
public ExampleBean exampleBean() {
  return new ExampleBean();
}

위의 코드는 Spring 컨테이너에게 method의 이름(bean name)을 사용하여 bean을 생성하도록 하고, 값(실제 bean 객체)을 반환한다. bean은 session 스콥이다. 즉, exampleBean() 메소드는 HTTP session 마다 새로운 객체를 생성해 줄 것이다.

순수 자바 코드를 사용하기 때문에:

- static method를 사용하면 factory-mathod를 사용할 필요가 없다.

@Bean
public ExampleBean exampleBean() {
  return ExampleFactory.createBean();
}

또한

- 복잡한 객체 생성시 필요한 FactoryBean/MethodInvokingFactoryBean도 필요없다.

@Bean(aliases = { "anniversaries" })
public List<Date> birthdays() {
  List<Date> dates = new ArrayList<Date>();
  Calendar calendar = Calendar.getInstance();

  calendar.set(1977, 05, 28);
  dates.add(calendar.getTime());
  dates.add(computeMotherInLawBirthday());

  return dates;
}

@Bean은 메소드에 붙일 수 있는 애노테이션이고 bean 객체를 생성하고 설정할 때 사용할 자바 코드를 나타낸다. 이 애노테이션은 XML 빈 설정에서 사용할 수 있는 autowiring, lazy-init, dependecy-check, depends-onscope과 같은 대부분의 설정을 지원한다. 또한, 라이프사이클 메소드와 XXXAware 인터페이스도 전부 지원한다.

public class AwareBean implements BeanFactoryAware {

  private BeanFactory factory;

  // BeanFactoryAware setter
  public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
   this.factory = beanFactory;
  }

  public void close(){
    // do clean-up
  }
}

@Bean(destroyMethodName = "close", lazy = Lazy.TRUE)
public AwareBean createBeanFactoryAwareBean() {
   return new AwareBean();
}

destroyMethodNanme 외에도, @Bean 애노테이션은 initMethodName을 제공한다. though its usage is discourage as one already has
control over the object creation and thus can call the initializing
method if needed.

2.3. @ExternalBean

@ExternalBean은 단순한 마크업 애노테이션으로 '부모 애플리케이션 컨텍스트'에 정의 되어 있는 '외부의' 빈을 인젝션 할 때 사용한다. 예제를 살펴보자:

@Configuration
public abstract class ExternalBeanConfiguration {
  @Bean
  public TestBean james() {
    TestBean james = new TestBean();
    // inject dependency from ann()
    james.setSpouse(ann());
    return james;
  }

  // Will be taken from the parent context
  @ExternalBean
  public abstract TestBean ann();
}

JavaConfig가 @ExternalBean을 만나게 되면, 소유하고 있는 메소드를 오버라이드하여 언제든 메소드를 호출할 수 있도록 한다. '부모 애플리케이션 컨텍스트'는 메소드 이름에 해당하는 bean을 찾아 준다(bean naming과 관련된 챕터에서 보다 자세한 내용을 참조). 이런 방법을, 설정 파일은 순수 자바로 남을 수 있고 익숙하게 리팩터링 할 수 있다.

@ExternalBean이 일반 메소드에도 역시 작동함을 기억하라. 위의 예제는 추상 메소드를 사용하여 실행되지도 않을 쓰레기 코드를 작성하는 것을 방지했다.

@Configuration
public class ExternalBeanOnNormalMethod {

  @ExternalBean
  public TestBean ann(){
      System.out.println("this code will not execute as the method " +
          "will be overriden with a bean look up at runtime");
  }
}

2.4. @ScopedProxy

Spring은 scoped 프록시(자세한 내용은 링크 참조)를 사용하여 scoped 종속성의 사용을 편리하게 했다. 그런 프록시를 생성하는 가장 쉬운 방법은, XML 설정을 사용할 때는 <aop:scope-proxy/> 태그를 사용하는 것이였다. JavaConfig는 그 대안으로 @ScopedProxy 애노테이션을 제공하여 동일한 semantic과 설정 옵션을 사용할 수 있다.

XML을 사용한 레퍼런스의 문서에 있는 예제를 JavaConfig를 사용하여 다음과 같이 작성할 수 있다.

// a HTTP Session-scoped bean exposed as a proxy
@Bean(scope = DefaultScopes.SESSION)
@ScopedProxy
public UserPreferences userPreferences() {
   return new UserPreferences();
}

@Bean
public Service userService() {
   UserService service = new SimpleUserService();
   // a reference to the proxied 'userPreferences' bean
   service.seUserPreferences(userPreferences());
   return service;
}