설정정보에 직접 스프링 빈을 등록하는 방법
VS스프링 컴포넌트 스캔으로 스프링 빈 자동 등록+@Autowired기능 (의존관계 자동 주입)
ComponentScan 은 @SpringBootApplication 에 들어있다. 그래서 직접 사용하지 않고 앞에서 애노테이션만으로 스프링빈 자동 등록을 사용할 수 있었다.
package hello.core;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import static org.springframework.context.annotation.ComponentScan.*;
@Configuration // 설정파일 이다.
@ComponentScan( // @SpringBootApplication 에 들어있는 애노테이션이라 직접 사용하지 않았었음.
excludeFilters = @Filter(type = FilterType.ANNOTATION, classes = Configuration.class))
public class AutoAppConfig {
}
➡️ excludeFilter 는 내가 만들어 놓은 설정파일들에 @Configuration 이 있어서 직접 만든 설정파일을 사용하지 않고 스프링이 해주는 컴포넌트 스캔을 사용하기 위해서 직접 만든 거는 제외시킨 것.
여기에 @Component 를 각각 스프링 빈 클래스에 적어두면 스프링이 알아서 생성해 스프링 컨테이너에 넣어둔다.
그리고 @Autowired 로 의존관계를 주입한다.
컴포넌트를 가진 애노테이션들을 가지면 스프링이 부가 기능을 수행한다.
@Controller : 스프링 MVC 컨트롤러로 인식@Repository : 스프링 데이터 접근 계층으로 인식하고, 데이터 계층의 예외를 스프링 예외로 변환해준다.데이터 계층의 예외와 스프링 예외가 뭘까?
데이터를 저장하고 있는 리포지토리는 DB 라는 외부 기능과 상호작용한다. 이때 DB 는 Oracle 이나 MySQL 등의 여러 종류가 있는데 각각의 DB들이 던지는 예외 방식이 다 다르다. 다 다른 예외를 스프링이 예외를 추상화해서 신경쓰지 않고 예외를 다룰 수 있도록 변환해준다.
이렇게 스프링이 추상화를 해준 덕분에 나중에 DB를 바꿨을 때 예외가 변경되도 클라이언트는 신경쓰지 않아도 된다.
변하는 것(기술) 과 변하지 않는 것(비지니스 로직) 을 철저히 분리하려는 노력의 결과다.
@Service : 핵심 비지니스 로직의 위치만 알리는 역할만 한다. 서비스는 중간에 위치해서 외부 기능과 접촉하지 않는 완충지대에 위치하고 있어서 추가적인 기능을 가질 필요가 없고, 또 중요한 비지니스 로직을 다루기 때문에 어떤 기술에 종속적이면 안된다.includeFilter : 컴포넌트 스캔 대상을 추가로 지정
excludeFilter : 컴포넌트 스캔에서 제외할 대상을 지정
package hello.core.scan.filter;
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyIncludeComponent {
}
package hello.core.scan.filter;
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyExcludeComponent {
}
package hello.core.scan.filter;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.context.annotation.ComponentScan.*;
public class ComponentFilterAppConfigTest {
@Test
void filterScan() {
ApplicationContext ac = new AnnotationConfigApplicationContext(ComponentFilterAppConfig.class);
BeanA beanA = ac.getBean("beanA", BeanA.class);
assertThat(beanA).isNotNull();
org.junit.jupiter.api.Assertions.assertThrows(NoSuchBeanDefinitionException.class,
() -> ac.getBean("beanB", BeanB.class));
}
@Configuration
@ComponentScan(includeFilters = @Filter(type = FilterType.ANNOTATION, classes = MyIncludeComponent.class))
@ComponentScan(excludeFilters = @Filter(type = FilterType.ANNOTATION, classes = MyExcludeComponent.class))
static class ComponentFilterAppConfig {
}
}
컴포넌트 스캔 대상으로 설정한 BeanA 는 null 이 발생하지 않았고, 제외시킨 BeanB 는 NoSuchBeanDefinitionException 이 발생하는 걸 확인 할 수 있었다. 직접 애노테이션을 만드는 방법을 알게되서 유익했다.
⚠️ 직접 애노테이션을 만들 때 인터페이스에서 사용할 애노테이션들은 직접 치기보다 복사해오는게 좋다 (import 자동추가 안되서)