반응형

Spring - Java Based Configuration


(원문 위치 : http://www.tutorialspoint.com/spring/spring_java_based_configuration.htm )

지금까지 XML설정파일을 사용하여 어떻게 Spring bean을 설정하는지 알아보았다. 만약 XML 설정으로 충분하다면, 어떤것을 사용하더라도 동일한 결과이기 때문에 Java기반 설정으로 처리하는 방법이 필요없을 수 있다.(하지만, 국내에서 순수하게 처음부터 프로젝트를 올려가지 않는 이상, 기구축된 시스템 기반으로 혹은 기존 시스템을 참고하여 개발하는 경우가 상당할 것이다. 따라서 보고 어떻게 돌아가는지는 파악할 수 있도록 해 놓아야 한다고 생각한다.)

Java 기반 설정옵션은 XML없이 대부분의 spring설정을 가능하게 한다. 몇가지 java기반 annotation을 아래에 설명한다.

@Configuration & @Bean Annotations:

@Configuration를 가진class를 선언(annotating - 주석이란 의미지만 선언한다고 해야 더 의미가 맞을것 같아 그리하였음.)은 그 class가 bean의 정의(definition) source로써 Spring IoC conatainer에 의해 사용됨을 나타낸다. @Bean annotation은 @Bean으로 선언된 함수가 Spring application context에서 bean으로써 등록(register)되어질 객체를 반환할 것이라는 걸 Spring에게 알려준다. 가장 간단한 @Configuration class는 아래와 같을 것이다.
package com.tutorialspoint;
import org.springframework.context.annotation.*;

@Configuration
public class HelloWorldConfig {

   @Bean 
   public HelloWorld helloWorld(){
      return new HelloWorld();
   }
}
위 코드는 아래 XML설정과 동일하다.
<beans>
   <bean id="helloWorld" class="com.tutorialspoint.HelloWorld" />
</beans>
@Bean으로 선언된 함수는 bean ID로써 동작하고 이것이 실제 bean을 성성하고 반환한다. 설정 class는 하나 이상의 @Bean을 선언할 수 있다. 설정 class가 정의되어진 이후부터 아래처럼 AnntationConfigApplicationContext를 사용하여 Spring container에 bean을 load하고 제공 할 수 있다.
public static void main(String[] args) {
   ApplicationContext ctx = 
   new AnnotationConfigApplicationContext(HelloWorldConfig.class);
   
   HelloWorld helloWorld = ctx.getBean(HelloWorld.class);

   helloWorld.setMessage("Hello World!");
   helloWorld.getMessage();
}
아래처럼 다양한 설정 class를 load할 수도 있다.
   AnnotationConfigApplicationContext ctx = 
   new AnnotationConfigApplicationContext();

   ctx.register(AppConfig.class, OtherConfig.class);
   ctx.register(AdditionalConfig.class);
   ctx.refresh();

   MyService myService = ctx.getBean(MyService.class);
   myService.doStuff();
}
Example:

[003] HelloWorld Example 강좌를 참조하여 프로젝트를 생성한다.
(--> 원문에서의 예제는 Maven을 사용하지 않고 직접 lib를 import하는 형식으로 진행되어 CGLIB.jar, ASM.jar를 추가적으로 import해야 하는 것으로 나오지만, Maven - Eclipse mars에서 테스트해보니 별도의 작업없이 잘 동작되었다. 단, 샘플예제에서는 annotation lib를 *로 import하고 있으나, 구체적으로 무엇인지를 물어보는 팝업이 나타나 우클릭 -> source -> orgainze imports를 이용하여 구체적으로 변경후 실행 하였다. )

HelloWorldConfig.java:

package com.tutorialspoint;
import org.springframework.context.annotation.*;

@Configuration
public class HelloWorldConfig {

   @Bean 
   public HelloWorld helloWorld(){
      return new HelloWorld();
   }
}

Here is the content of HelloWorld.java:

package com.tutorialspoint;

public class HelloWorld {
   private String message;

   public void setMessage(String message){
      this.message  = message;
   }

   public void getMessage(){
      System.out.println("Your Message : " + message);
   }
}

MainApp.java:

package com.tutorialspoint;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.*;

public class MainApp {
   public static void main(String[] args) {
      ApplicationContext ctx = 
      new AnnotationConfigApplicationContext(HelloWorldConfig.class);
   
      HelloWorld helloWorld = ctx.getBean(HelloWorld.class);

      helloWorld.setMessage("Hello World!");
      helloWorld.getMessage();
   }
}

실행 결과는 아래와 같다.

Your Message : Hello World!

Injecting Bean Dependencies:

@Bean이 다른 bean과 의존관계가 있을때, 그 의존관계를 표현하는 것은 아래와 같이 다른 bean을 호출하는 bean함수를 갖는 것만큰 간단하다.
package com.tutorialspoint;
import org.springframework.context.annotation.*;

@Configuration
public class AppConfig {
   @Bean
   public Foo foo() {
      return new Foo(bar());
   }
   @Bean
   public Bar bar() {
      return new Bar();
   }
}
위 예제는 foo bean이 생성자 삽입(constructor injection)을 통해 bar의 참조를 받는다.

Example:


[003] HelloWorld Example 강좌를 참조하여 프로젝트를 생성한다.
(이건 Maven환경에서 아직 돌려보지는 않았지만, 당연히 동작할거라 생각한다. - 물론 원문에서 기술된 기준으로는 잘 돌아간다. 이 예제 또한 annotation lib를 구체적으로 해야 할 듯 싶다.)

TextEditorConfig.java생성자(textEditor)내부에서 spellChecker() 호출을 통해 의존관계가 삽입되었다.

package com.tutorialspoint;
import org.springframework.context.annotation.*;

@Configuration
public class TextEditorConfig {

   @Bean 
   public TextEditor textEditor(){
      return new TextEditor( spellChecker() );
   }

   @Bean 
   public SpellChecker spellChecker(){
      return new SpellChecker( );
   }
}

TextEditor.java:

package com.tutorialspoint;

public class TextEditor {
   private SpellChecker spellChecker;

   public TextEditor(SpellChecker spellChecker){
      System.out.println("Inside TextEditor constructor." );
      this.spellChecker = spellChecker;
   }
   public void spellCheck(){
      spellChecker.checkSpelling();
   }
}

SpellChecker.java:

package com.tutorialspoint;

public class SpellChecker {
   public SpellChecker(){
      System.out.println("Inside SpellChecker constructor." );
   }

   public void checkSpelling(){
      System.out.println("Inside checkSpelling." );
   }
   
}

MainApp.java:

package com.tutorialspoint;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.*;

public class MainApp {
   public static void main(String[] args) {
      ApplicationContext ctx = 
      new AnnotationConfigApplicationContext(TextEditorConfig.class);

      TextEditor te = ctx.getBean(TextEditor.class);

      te.spellCheck();
   }
}

실행 결과는 아래와 같다.

Inside SpellChecker constructor.
Inside TextEditor constructor.
Inside checkSpelling.

The @Import Annotation:

@Import annotation은 다른 설정 clas로부터 @Bean을 load할 수 있도록 한다. 아래 ConfingA class를 보자
@Configuration
public class ConfigA {
   @Bean
   public A a() {
      return new A(); 
   }
}
아래처럼 다른 Bean 선언에서 위 Bean 선언을 import할 수 있다.
@Configuration
@Import(ConfigA.class)
public class ConfigB {
   @Bean
   public B a() {
      return new A(); 
   }
}
이제는 context를 인스턴스화(instantiating)할 때 ConfigA.class와 ConfigB.class 모두를 명시하는 것이 아니라, 아래처럼 단지 사용되어지는 ConfigB만 필요하다.
   ApplicationContext ctx = 
   new AnnotationConfigApplicationContext(ConfigB.class);
   // now both beans A and B will be available...
   A a = ctx.getBean(A.class);
   B b = ctx.getBean(B.class);
}
(--> 둘다 context를 통해 load한 후 사용한 것이 아니라 ConfigB.class만 로드해도 ConfigA.class가 자동으로 로드되어진다는 소리..)

Lifecycle Callbacks:

@Bean annotation은 임의의 초기화(initialization)와 소멸(destruction) callback 함수를 지원한다.(XML 기반에서 bean 요소의 init-method/destroy-method 속설처럼.)
public class Foo {
   public void init() {
      // initialization logic
   }
   public void cleanup() {
      // destruction logic
   }
}

@Configuration
public class AppConfig {
   @Bean(initMethod = "init", destroyMethod = "cleanup" )
   public Foo foo() {
      return new Foo();
   }
}

Specifying Bean Scope:

bean의 기본(default) scope는 'singleton'이지만, @Scope annotation으로 변경할 수 있다.
@Configuration
public class AppConfig {
   @Bean
   @Scope("prototype")
   public Foo foo() {
      return new Foo();
   }
}


반응형

+ Recent posts