오늘은 Spring @Bean 어노테이션과 관련해서 이야기를 하려한다. 깊게 볼 것은 아니고 특이한 거나 잘 몰랐던거? 알지만 해보지 않았던거? 그런것들을 알아볼 예정이니 그냥 이렇구나 정도만 알면 되겠다. 자주 사용될 일도 없으니 한번씩만 훑고 지나가면 될 것 같다.

요즘 회사를 이직준비 중이라 회사 다닐때 보다 더 바쁘고 스트레스 받는 것 같다.ㅠㅠ 아무튼 한번 알아보자.

Interface

버전은 언제인지 잘 모르겠는데 Interface에도 @Bean 어노테이션을 작성해도 동작한다. 아마 4.x부터 됐을 것으로 예상해보지만 그게 언제인지는 확인해보지 않았다. 예를들어 다음과 같은 설정을 해도 동작한다.


public class SomeBean { public void print() { System.out.println("hello world"); } } @Configuration public interface BeanInterface { @Bean default SomeBean someBean() { return new SomeBean(); } } @Configuration public class BeanClass implements BeanInterface { //... }

위와 같이 작성해도 SomeBean이라는 클래스가 Bean으로 등록된다. 여기서 주의할 것은 그에 맞기 구현체가 있어야 한다는 것이다. 만약 구현체가 없다면 Bean으로 등록되지 않는다.

public static void main(String[] args) {
  ConfigurableApplicationContext context = SpringApplication.run(SpringBeanApplication.class, args);
  SomeBean someBean = context.getBean(SomeBean.class);
  someBean.print();
}

위와 같이 실행시키면 hello world 가 print 된 것을 알 수 있다.

@Bean 명

Bean들은 자기만의 고유한 명이 있다. 뭐 다 알겠지만 그렇다. 기본적으로 아무설정 하지 않았을 경우에는 메서드명이 빈명이 된다.

@Bean
SomeBean someBean() {
  return new SomeBean();
}

위와 같이 사용할 경우 someBean이라는 이름으로 빈명이 등록이 된다.

public static void main(String[] args) {
  ConfigurableApplicationContext context = SpringApplication.run(SpringBeanApplication.class, args);
  String[] beanNames = context.getBeanNamesForType(SomeBean.class);
  System.out.println(beanNames[0]);
}

해당 빈이름을 가져왔을 때 someBean이라는 것이 프린트 될 것이다. 만약 빈명을 변경하고자 할 때는 @Bean 어노테이션 속성 중 value 또는 name을 사용해서 변경 할 수 있다.

@Bean("nothing")
SomeBean someBean() {
  return new SomeBean();
}

close

Spring Bean의 라이프사이클 중에 destroy라는 자원을 해제하거나 빈이 소멸될 때 마지막으로 호출해주는 메서드들이 존재한다. 예를들어 DisposableBean을 구현하거나 @PreDestroy 어노테이션 또는 destroyMethod 속성을 사용해서 자원 등을 해제 할 수 있다. 3가지 방법 말고도 spring 에서는 좀 더 많은 해제 법이 존재한다. 첫 번째 방법으로는 AutoCloseable 인터페이스를 구현하면 된다.

public class SomeBean implements AutoCloseable {

  @Override
  public void close() throws Exception {
    System.out.println("close");
  }
}

위와 같이 작성 후에 아무 설정 없이도 SomeBean 이라는 빈이 소멸될 때 close 메서드를 실행 시킨다. 한번씩 확인해 보자.

두 번째 방법으로는 굳이 AutoCloseable 필요 없이 close 메서드만 있어도 된다.

public class SomeBean  {

  public void close() throws Exception {
    System.out.println("close");
  }
}

위와 같이 작성해도 별탈 없이 close 메서드가 실행 된다. 더 정확하게는 그냥 close 메서드를 실행 시키는 것이다. 솔직히 위의 AutoCloseable 여부와 상관없이 그냥 close 메서드를 호출하는 것이다.

마지막 방법으로는 shutdown() 메서드 이다. 이 또한 그냥 shutdown() 메서드를 호출 하는 것이다.

public class SomeBean  {

  public void shutdown() throws Exception {
    System.out.println("close");
  }
}

위와 같이 작성해도 close를 출력하는 것을 확인 할 수 있다. 만약 좀 더 확인하고 싶다면 DisposableBeanAdapter 클래스를 좀 더 확인하면 알 수 있다.

class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {

  private static final String CLOSE_METHOD_NAME = "close";

  private static final String SHUTDOWN_METHOD_NAME = "shutdown";

  //...
}

참고로 DisposableBean 인터페이스와 위의 세가지 방법을 같이 쓰면 DisposableBean 의 destroy 메서드가 우선순위가 높다.

Lite Mode

Spring Bean에는 lite 모드라는 것이 있다. 그게 뭐냐고 물어보면 조금 뭐라고 해야할지.. lite니까 조금 가벼운거?
아무튼 lite 모드는 해당 설정에 @Configuration 어노테이션이 아닌 @Component어노테이션을 작성하면 그게 lite mode가 된다. lite 모드로 하면 설정이 Configuration으로 하는 것보다는 빠르지만 그렇게 확 티가 날 정도는 아닌 걸로 기억하고 있다. 물론 많으면 말이 달라지겠지만.. 아무튼 그럼 차이가 뭘까? 차이는 Cglib을 사용하냐 하지 않느냐 차이가 가장 큰 차이 같다.

@Configuration 같은 경우에는 cglib을 사용하지만 @Component 경우에는 cglib을 사용하지 않는다.

@Configuration
public class Config {

  @Bean
  SomeBean someBean() {
    return new SomeBean();
  }

  @Bean
  NothingBean nothingBean() {
    return new NothingBean(someBean());
  }
}

예를 들어 위와 같은 설정이 있다고 가정하자. NothingBean은 SomeBean을 디펜더시 받고 있다. 그래서 위와 같이 설정을 했다고 하자. 그럼 someBean 의 메서드는 몇 번 호출 이 될까? 일반적으로 볼 때에는 두번 호출 되는 게 맞다고 생각한다. SomeBean을 빈으로 등록할 때와 NothingBean을 빈으로 등록할 때 someBean() 메서드를 호출하니 두번이 맞다. 하지만 @Configuration를 사용하면 아까 말했다시피 cglib을 이용해서 위의 someBean을 한번만 호출 하도록 한다.

@Component
public class Config {

  @Bean
  SomeBean someBean() {
    return new SomeBean();
  }

  @Bean
  NothingBean nothingBean() {
    return new NothingBean(someBean());
  }
}

하지만 이 경우에는 어떨까? 이 경우에는 우리가 일반적으로 생각하는게 맞다. 이 때에는 someBean() 메서드가 두번 호출된다.
@Configuration 은 cglib을 사용해서 일반적으로 @Component 를 사용할 때보다 조금 느린것 같다. 하지만 그 차이는 눈에 보이지 않는 다는 거..
만약 config을 lite mode로 사용할 경우에는 좀 더 신중하게 사용해야 될 듯 싶다. 그냥 필자는 @Configuration을 사용하련다..

오늘은 Spring의 Bean에 대해서 몇 가지 알아봤다. Spring을 사용하는데에는 아주 쓸모있는 기능은 아니지만 알아둬서 나쁠껀 없으니 한번씩 해보도록 하자.