위 사진을 보자.
여러개의 컨트롤러가 하나의 서비스와 결합되어 있고 그 서비스는 하나의 레파지토리와 결합되어 있다.
근데 그림을 딱 봤을 때 "?"가 그려진다면 당신은 천상 개발자에 가까워진것이다.
"컨트롤은 여러개 근데 Service1은 하나인데 여러개의 새로운 객체로 각각 만들어 할당 할 필요가 있을까..?"
맞는 말이다.위 처럼 용도에 맞게 필요한 객체를 가져다가 사용하는 형태가 좋을것이다.
이러한 형태를 느슨한 결합 or IoC(Inversion of Control) 라고 한다.
왜 제어의 역전이라고 부를까?
사진을 다시보면 화살표 방향이 역순인걸 볼 수 있다.
일반적으로는 사용자가 필요한 객체를 생성해서 사용하는 방식으로 제어했다면
객체를 먼저 생성하고 필요 할때마다 가져오는 방식이다.
이때 가져오는 방식이 DI(Dependency Injection) 의존성 주입 방식이라고한다.
되게 있는척 용어 써가면서 설명했는데,
필요 할때마다 생성하는거 보다 하나 만들고 가져오는게
최적화 측면에서 좋다는 이야기가 핵심이다.
그럼 어떻게 객체를 만들고 가져올까? 사실 우린 IoC패턴을 쭉 써왔다.
저번에 MVC 패턴게시물에서 Service, Repository등의 객체를 선언하고 사용했던걸 기억 할 것이다.
곰곰히 생각해보자. 우리가 저 객체들을 한번 선언하고, 더 생성해서 썼던 적이 있었나?
아마 이런식으로 그냥 가져다가 썻을 것이다.
왜 이게 가능 했던건지 건지 지금부터 살펴보겠다.
...
@Component //이녀석
public @interface Service {
@AliasFor(annotation = Component.class)
String value() default "";
}
@Service
,@Repository
등의 어노테이션의 내부 구조를 보면 @Component
가 존재한다.
Spring의 핵심 어노테이션이니 설명을 하고 넘어가겠다. Spring의 Bean으로 등록하기 위해 사용하는 어노테이션으로 Bean은 스프링의 IoC 컨테이너에서 관리하게된다.
Spring Singleton패턴 핵심이기도 하다.
// @Configuration
@Component
public class ProductService { ... }
public class ProductService
-> productService@ComponentScan
은 등록된 Bean의 적용 범위를 정할 수 있다.
@Configuration
@ComponentScan(basePackages = "com.study.artgloy_study")
class BeanConfig { ... }
근데 우리는 지금까지 @ComponentScan
을 사용한적이 없다.
하지만 @Service
, @Repository
등의 @Component
를 내장한 Bean들을 잘가져와 쓰고 있는데, 그 비밀은 SpringBootApplication
에 있다.
@SpringBootApplication // <- 이녀석을 해체하여 분석해보자.
@EnableJpaAuditing
public class ArtlogySpringApplication {
public static void main(String[] args) {
SpringApplication.run(ArtlogySpringApplication.class, args);
}
}
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
우리가 직접 만들지 않은 외부라이브러리는 IoC컨테이너에 어떻게 등록 할까?
이땐 @Bean
, @Configuration
을 이용하면 된다.
@Bean
public ObjectMapper objectMapper(){}
그럼 이런 생각을 할 수 있다. "그럼 @Bean
쓰면 내가 만든거나 외부라이브러리 둘다 가능한거아닌가? @Component
는 왜 알려준거야?" 할 수 있는데 아니다. 그건 @Bean
, @Component
의 설정을 보면 알 수 있다.
그 비밀은 @Target
어노테이션에 있는데 @Target
은 해당 객체가 선언 될 수 있는 타입을 정해주는거다. 보면 Bean은 Method(함수형), Component은 Type(객체형)에만 선언 할 수 있게 정해져있다.
뭐 요약하자면 IoC 컨테이너 등록을 위해 함수형
@Bean
, 객체형@Component
를 쓴다.
@Component
public class ProductService {
private final ProductRepository productRepository;
@Autowired
public ProductService(ApplicationContext context) {
// 1.'빈' 이름으로 가져오기
ProductRepository productRepository = (ProductRepository) context.getBean("productRepository");
// 2.'빈' 클래스 형식으로 가져오기
// ProductRepository productRepository = context.getBean(ProductRepository.class);
this.productRepository = productRepository;
}
// ...
}