메타 애노테이션과 합성 애노테이션
@UnitTest 애노테이션 생성
@Test 메서드가 붙어있는 simpleHelloService()의 애노테이션을 @UnitTest 애노테이션으로 변경해보자. 물론 새로 생성해줘야한다.
@UnitTest
void simpleHelloService() {
SimpleHelloService helloService = new SimpleHelloService();
String ret = helloService.sayHello("Test");
Assertions.assertThat(ret).isEqualTo("Hello Test");
}
@Test
void helloDecorator() {
HelloDecorator decorator = new HelloDecorator(name -> name);
String ret = decorator.sayHello("Test");
Assertions.assertThat(ret).isEqualTo("*Test*");
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Test
@interface UnitTest {
}
애노테이션을 생성할 때 Restention과 Target 정보는 필수적이다. @Test 메서드는 메타 애노테이션을 사용하기 위해 쓰였으며, 만일 @Test 애노테이션이 없다면 전체 테스트를 실행할 시 SimpleHelloService()는 돌아가지 않는다.
만일 생성한 @UnitTest를 메타 애노테이션으로 사용하고 싶다면 @Target 정보를 추가해주면 된다.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@UnitTest
@interface FastUnitTest{
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Test
@interface UnitTest {
}
마치 상속과 비슷해보이지만 애노테이션에는 상속이라는 개념이 없으므로 상속은 아니다.
합성 애노테이션은 메타 애노테이션을 두 개 이상 사용할 때, 합성 애노테이션이라 부른다. 예를 들면 @RestController 애노테이션의 경우 @Controller와 @ResponseBody 두 개 의 메타 애노테이션을 갖고 있다.
합성 애노테이션의 적용
@MySpringBootAnnotation
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Configuration
@ComponentScan
public @interface MySpringBootAnnotation {
}
@Retention의 값은 RUNTIME으로 줘야한다. 디폴트 값은 CALSS인데, CALSS의 경우 애노테이션 정보가 컴파일 된 클래스 파일까지는 살아있지만 해당 애노테이션이 걸려있는 클래스를 메모리에 로딩할 때 사라지기 때문에 RUNTIME으로 지정해야 제대로 사용할 수 있다.
@Target은 TYPE으로 설정한다. TYPE은 클래스, 인터페이스, enum 이 세 개의 TYPE을 말한다.
이제 HellobootApplication.java에 새로 만든 애노테이션을 적용하자.
@MySpringBootAnnotation
public class HellobootApplication { .. }
서버 실행 후 API 테스트를 실행시켜보면 정상적으로 동작하는 걸 볼 수 있다.
빈 구성 정보 등록하기
HellobootApplication.java에 있는 빈 팩토리 메서드를 따로 빼보자.
HellobootApplication.java
@MySpringBootAnnotation
public class HellobootApplication {
public static void main(String[] args) {
SpringApplication.run(HellobootApplication.class, args);
}
}
Config.java
@Configuration
public class Config {
@Bean
public ServletWebServerFactory servletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
@Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
}
수정 후 서버를 실행해보면 정상적으로 실행된다.
빈 오브젝트의 역할과 구분
크게 스프링 컨테이너에서 생성 및 관리하는 빈들은 컨테이너 인프라스트럭처 빈과 애플리케이션 빈으로 나누어진다. 애플리케이션 빈은 개발자가 어떤 빈을 사용하겠다라고 명시적으로 구성 정보를 제공한 것을 말한다. 컨테이너 인프라스트럭처 빈은 스프링 컨테이너 자신이거나 혹은 컨테이너가 기능을 확장하면서 추가해 온 것들을 빈으로 등록시켜서 사용하는 빈들이다. 개발자가 요청해서 등록하는 빈이 아니라 스프링 컨테이너가 스스로 빈으로 등록해서 동작하는 방식이다.
애플리케이션 빈은 또 두 가지로 나눌 수 있다. 애플리케이션 로직 빈과 애플리케이션 인프라스트럭처 빈이다. 애플리케이션 로직 빈은 애플리케이션의 기능, 비즈니스 로직, 도메인 로직과 같은 개발 코드들로 만들어진 빈이다. 애플리케이션 인프라스트럭처 빈은 대부분 기술과 관련된 것들이며 개발자가 거의 직접 작성하진 않고 명시적으로 설정만 하는 정도이다.
- 출처 : 인프런 토비의 스프링부트 - 이해와 원리 강의