컴포넌트 스캔

이한주·2023년 3월 13일
0

컴포넌트 스캔으로 빈 등록

컴포넌트 스캔은 스프링이 직접 클래스를 검색해서 빈으로 등록해주는 기능이다.

설정 클래스에 빈으로 직접 등록하지 않아도 원하는 클래스를 빈으로 등록할 수 있다.
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 {

}
  • basPackageClasses : class가 위치한 패키지 하위로 스캔
  • basePackages : 지정한 패키지 하위로 스캔
  • excludeFilters : 지정한 Filter 타입에 따라서 빈을 스캔 대상에서 제거

코드

https://github.com/yanJuicy/kdt-spring-order/tree/2d64b3c1fb07d230a35b44a2e828cd78ee0ec43c

0개의 댓글