Using Java Configuration

원문 : Chapter 7. Using Java Configuration

애플리케이션에서 애노테이션을 설정 파일로 사용할 수 있는 방법으로 다음과 같은 클래스가 있다.

    * AnnotationApplicationContext     

이 클래스는 Ant 스타일 패턴을 사용하여 애노테이션을 사용한 설정 클래스 이름을 받을 수 있다.

      ApplicationContext oneConfig =
             new AnnotationApplicationContext(SimpleConfiguration.class.getName());
      ApplicationContext aBunchOfConfigs =
             new AnnotationApplicationContext(“**/configuration/*Configuration.class”);

이 애플리케이션 컨텍스트는 주어진 패턴에 매칭되는 클래스 패스 내의 클래스들을 자동으로 읽어들이고 빈으로 등록한다. 이 방법의 단점은 설정 객체를 parameterization 할 수 없다는 것이다.

    * Configuration post processor

      <beans>
       <!– Spring configuration –>
       <bean class=”org.springframework.samples.petclinic.JdbcConfiguration”/>

       <!– Java Configuration post processor –>
       <bean class=”org.springframework.config.java.process.ConfigurationPostProcessor”/>
      </beans>

두 번째 방법은 설정 처리 뿐만 아리나 (ConfigurationPostProcessor를 통해서) 설정 객체 자체도 제어 하기 때문에 보다 많은 설정 옵셥을 제공한다.

설정을 빈으로 정의함으로써, Spring 컨테이너는 설정을 (속성 또는 특정 생성자를 사용해서)설정할 수 있다.

      <beans>

       <!– a possible configurable configuration –>
       <bean class=”org.my.company.config.AppConfiguration”>
          <property name=”env” value=”TESTING”/>
          <property name=”monitoring” value=”true”/>
          <property name=”certificates” value=”classpath:/META-INF/config/MyCompany.certs”/>
       </bean>

       <!– Java Configuration post processor –>
       <bean class=”org.springframework.config.java.process.ConfigurationPostProcessor”/>

      </beans>

Mixing XML and annotations

원문 : Chapter 6. Mixing XML and annotations

Java 설정과 XML 설정은 상호 베타적이지 않다. 하나의 Spring 애플리케이션에서 모두 사용될 수 있다. XML 파일에 있는 bean을 가져오기 위해서는, Spring 컨테이너를 사용해야 한다. 이전에 언급했다시피, @ExternalBean 애노테이션(추천하는 방식)을 사용하여 그렇게 할 수 있다. 이렇게 하고 싶지 않거나 이런 방법이 부적절한 경우네는, @Configuration 클래스에의해 사용되는 beanFacotry에 접근할 수 있다. 이렇게 하려면, 설정 클래스가 ConfigurationSupport 를 상속하거나 BeanFacrtoryAware 인터페이스를 구현하도록 하면 된다.

다음의 XML 설정을 살펴보자.

<bean id=”myBean” class=”MyBean”/>

myBean을 Java에서 참조하려면 다음과 같이 작성할 수 있다.

@Configuration
public class MyConfig extends ConfigurationSupport {

  @Bean
  public ExampleBean anotherBean() {
     ExampleBean bean = new ExampleBean(“anotherBean”);
     bean.setDep(getBean(“myBean”)); // use utility method to get a hold of ‘myBean’
     return bean;
  }
}

@Configuration
public class MyOtherConfig implements BeanFactoryAware {
  private BeanFactory beanFactory;

  public void setBeanFactory(BeanFactory beanFactory) {
     // get access to the owning bean factory
     this.beanFactory = beanFactory;
  }

  @Bean
  public ExampleBean yetAnotherBean() {
     ExampleBean bean = new ExampleBean(“yetAnotherBean”);
     bean.setDep(beanFactory.getBean(“myBean”)); // use dependency lookup
     return bean;
  }
}

다시 한 번 강조하지만, ConfigurationSupport나 BeanFacrtoryAware를 사용하기 전에 똑같은 기능을 제공하는 (offers the same capability in a refactoring friendly manner) @ExternalBean을 사용하고 싶지는 않은지 재고하기 바란다.

JavaConfig 배포판에 변경된 Petclinic 예제에 보면 몇몇 XML 설정 부분을 Java와 Groovy로 교체한 것이 있다. 샘플 폴더를 살펴보길 바란다.

Naming strategy

원문 : Chapter 5. Naming strategy

지금까지의 모든 예제들은, 메소드 이름으로 메소드를 호출하여 bean을 받아왔다.

@Configuration
public class ColoursConfiguration {
  // create a bean with name ‘blue’
  @Bean
  public Color blue() {
    …
  }
  …
}

// dependency lookup for the blue colour
applicationContext.getBean(“blue”);

몇몇 경우에는, 이러한 네이밍 스키마가 적합하지 않을 수도 있다. 즉 서로 다른 설정 클래스에서 같은 이름의 정의를 사용하고 있는 경우가 그렇다. 이러한 네이밍 지침을 사용하는 입장에서 맘대로 변경할 수 있도록 BeanNamingStrategy 인터페이스를 제공하고 있다.

물론, 코드를 작성하기 전에, Spring이 기본으로 제공하고 있는 MethodNameStrategy 구현체들을 살펴보는 것이 좋겠다.

 <!– Java Configuration post processor –>
 <bean class=”org.springframework.config.java.process.ConfigurationPostProcessor”>
    <property name=”namingStrategy”>
       <bean class=”org.springframework.config.java.naming.MethodNameStrategy”>
          <property name=”prefix” value=”CLASS”/>
       </bean>
    </property>
 </bean>

이런 설정을 해두면, bean을 생성할 때 메소드 이름에 클래스 이름까지 붙여주게 된다.

// 새로운 작명 지침을 사용하여 blue 빈 가져오기
applicationContext.getBean(“ColoursConfiguration.blue”);

Wire dependencies

원문 : Chapter 4. Wire dependencies

bean을 묶기 위해서, 간단하게 Java의 문법을 사용할 수 있다.

@Bean(scope = DefaultScopes.SINGLETON)
public Person rod() {
  return new Person(“Rod Johnson”);
}

@Bean(scope = DefaultScopes.PROTOTYPE)
public Book book() {
  Book book = new Book(“Expert One-on-One J2EE Design and Development”);
  book.setAuthor(rod());  // rod() method is actually a bean reference !
  return book;
}

위의 예제에서, 책의 저자는 rod 메소드의 반환값을 사용하고 있다. 하지만, book과 rod 모두 @Bean으로 마크된 메소드기 때문에, Spring의 관리하는 bean들을 반환하게 되며, 따라서 컨테이너의 관리 대상이 된다. rod 빈은 싱글톤이고 book 빈은 프로토타입이다. 설정 파일을 만들 때, Spring은 애노테이션 문맥을 알아차리고 rod() 메소드 호출을 ‘rod’라는 빈 name으로 참조하도록 한다.

이 컨테이너는 매번 book 빈을 요청할 때 마다 똑같은(싱글톤) rod 빈을 참조하고 있는 다른(프로토타입) book 빈을 반환할 것이다.

위의 코드는 아래와 동일하다:

<bean id=”rod” class=”Person” scope=”singleton”>
   <constructor-arg>Rod Johnson</constructor-arg>
</bean>

<bean id=”book” class=”Book” scope=”prototype”>
   <constructor-arg>Expert One-on-One J2EE Design and Development</constructor-arg>
   <property name=”author” ref=”rod”/>
</bean>

위의 예제는 가장 흔히 사용하는 scope을 사용하고 있지만, 어떤 타입의 scope이라도 사용할 수 있음을 알아두자.

@Bean (scope = “customer”)
public Bag shopingBag() {
  return new Basket();
}

@Bean (scope = “shift”)
public Manager shopManager() {
  …
}

Bean Visibility

원문 : Chapter 3. Bean Visibility

JavaConfig의 멋진 기능 중 하나는 bean의 가시성을 설정할 수 있다는 것이다. JavaConfig는 메소드 접근 지시자를 사용하여 메소드에서 반환할 bean에 접근 할 수 있는지 없는지 여부를 결정할 수 있다.

다음의 설정을 살펴보자.

@Configuration
public abstract class VisibilityConfiguration {

  @Bean
  public Bean publicBean() {
     Bean bean = new Bean();
     bean.setDependency(hiddenBean());
     return bean;
  }
 
  @Bean
  protected HiddenBean hiddenBean() {
     return new Bean(“protected bean”);
  }

  @Bean
  private HiddenBean secretBean() {
     Bean bean = new Bean(“private bean”);
     // hidden beans can access beans defined in the ‘owning’ context
     bean.setDependency(outsideBean());
  }

  @ExternalBean
  public abstract Bean outsideBean()
}

위 설정 내용을 바탕으로 다음과 같은 XML 설정 파일을 사용할 수 있다.(여러 설정 파일 종류를 혼용하는 전략에 대해서는 본 챕터에서 다룰 것이다.)

<beans>
 <!– the configuration above –>
 <bean class=”my.java.config.VisibilityConfiguration”/>

 <!– Java Configuration post processor –>
 <bean class=”org.springframework.config.java.process.ConfigurationPostProcessor”/>

 <bean id=”mainBean” class=”my.company.Bean”>
    <!– this will work –>
    <property name=”dependency” ref=”publicBean”/>
    <!– this will *not* work –>
    <property name=”anotherDependency” ref=”hiddenBean”/>
 </bean>
</beans>

위의 설정에서 하나의 JavaConfig가 설정에 포함되며, 3개의 빈을 생성할 책임을 지고 있다. publicBean, hiddenBean 그리고 secretBean이다. 이 세개의 bean들은 각자 서로를 참조할 수는 있지만, ApplicationContext 입장에서는 오직 publicBean만 참조할 수 있다. hiddenBean과 secretBean은 접근이 오직 VisiblilityConfiguration 내부로 제한되어 있다.

@Bean 애노테이션이 달리 메소드 중에 public이 아닌 것(protected, private, defalut)들은 모두 ‘감춰진’ bean으로 생성될 것이다.

위의 예제에서, mainBean은 publicBean과 heddenBean을 사용하여 설정하고 있다. 하지만, hiddenBean은 숨겨져 있기 때문에 런타임시에 다음과 같은 에러를 발생시킨다.

org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named ‘hiddenBean’ is defined
 …

가시성을 제공하기 위해, JavaConfig는 Spring 컨테이너가 제공하는 application context 상속구조의 장점을 받아들였다. 자식 애플리케이션 컨테이너에서 숨겨진 빈들을 소유하고있는  부모 컨텍스트에 있는 빈들에 접근할 수 있다.