컴포넌트 스캔은 스프링이 직접 클래스를 검색해서 빈으로 등록해주는 기능이다.
설정 클래스에 빈으로 직접 등록하지 않아도 원하는 클래스를 빈으로 등록할 수 있다.
Stereotype 애노테이션을 이용하면 스캔대상을 지정한다.
스프링에서는 @Component, @Controller, @Service, @Configuration 등을 사용한다.
기존 AppConfiguration 코드에서 VoucherService, OrderService를 빈으로 등록하는 코드를 제거한다.
@Configuration
@ComponentScan
public class AppConfiguration {
}@Configuration
public class AppConfiguration {
@Bean
public VoucherRepository voucherRepository() {
return new VoucherRepository() {
@Override
public Optional<Voucher> findById(UUID voucherId) {
return Optional.empty();
}
};
}
@Bean
public OrderRepository orderRepository() {
return new OrderRepository() {
@Override
public void insert(Order order) {
}
};
}
@Bean
public VoucherService voucherService(VoucherRepository voucherRepository) {
return new VoucherService(voucherRepository);
}
@Bean
public OrderService orderService(VoucherService voucherService, OrderRepository orderRepository) {
return new OrderService(voucherService, orderRepository);
}
}
다음과 같이 AppConfiguration 코드에 @ComponentScan 어노테이션을 붙여 컴포넌트 스캔으로 빈을 등록한다.
AppConfiguration에서 등록한 빈들은 컴포넌트 스캔에 대상이 되도록 @Service, @Repository 어노테이션을 붙여준다.
@Configuration
@ComponentScan
public class AppConfiguration {
}
@Service
public class OrderService {
private final VoucherService voucherService;
private final OrderRepository orderRepository;
public OrderService(VoucherService voucherService, OrderRepository orderRepository) {
this.voucherService = voucherService;
this.orderRepository = orderRepository;
}
public Order createOrder(UUID customerId, List<OrderItem> orderItems) {
var order = new Order(UUID.randomUUID(), customerId, orderItems);
orderRepository.insert(order);
return order;
}
public Order createOrder(UUID customerId, List<OrderItem> orderItems, UUID voucherId) {
var voucher = voucherService.getVoucher(voucherId);
var order = new Order(UUID.randomUUID(), customerId, orderItems, voucher);
orderRepository.insert(order);
voucherService.userVoucher(voucher);
return order;
}
}
@Service
public class VoucherService {
private final VoucherRepository voucherRepository;
public VoucherService(VoucherRepository voucherRepository) {
this.voucherRepository = voucherRepository;
}
public Voucher getVoucher(UUID voucherId) {
return voucherRepository.findById(voucherId)
.orElseThrow(() -> new RuntimeException("Can not find a voucher for " + voucherId));
}
public void userVoucher(Voucher voucher) {
}
}
@Repository
public class MemoryOrderRepository implements OrderRepository {
private final Map<UUID, Order> storage = new ConcurrentHashMap<>();
@Override
public Order insert(Order order) {
storage.put(order.getOrderId(), order);
return order;
}
}
@Repository
public class MemoryVoucherRepository implements VoucherRepository {
private final Map<UUID, Voucher> storage = new ConcurrentHashMap<>();
@Override
public Optional<Voucher> findById(UUID voucherId) {
return Optional.ofNullable(storage.get(voucherId));
}
@Override
public Voucher insert(Voucher voucher) {
storage.put(voucher.getVoucherId(), voucher);
return voucher;
}
}
실행을 하면 다음과 같이 Autowired로 DI를 해주는 것도 볼 수 있다.
02:05:52.003 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Autowiring by type from bean name 'voucherService' via constructor to bean named 'voucherRepository'
02:05:52.005 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Autowiring by type from bean name 'orderService' via constructor to bean named 'voucherService'
02:05:52.005 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Autowiring by type from bean name 'orderService' via constructor to bean named 'orderRepository'
또한 다음과 같이 패키지 구조를 바꾼경우 @ComponentScan에 옵션을 설정해 컴포넌트 스캔을 할 수 있다.
@Configuration
@ComponentScan(basePackageClasses = {Order.class, Voucher.class})
@ComponentScan(basePackages = {"org.prgrms.kdt.order", "org.prgrms.kdt.voucher"})
@ComponentScan(basePackages = {"org.prgrms.kdt.voucher"},
excludeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = MemoryVoucherRepository.class)})
public class AppConfiguration {
}
https://github.com/yanJuicy/kdt-spring-order/tree/2d64b3c1fb07d230a35b44a2e828cd78ee0ec43c