본문 바로가기

TIL/2021

[TIL] 0818 Spring 컴포넌트


배운 것

1. Component Scan

 

기존 코드는 아래와 같이 config에서 직접 bean을 등록해주었다.

@Configuration
public class AppConfiguration {

    @Bean
    public VoucherRepository voucherRepository() {
        return new VoucherRepositoryImpl();
    }    
}

해당 부분을 아래와 같이 어노테이션기반으로 코드를 바꾸었는데, 빈을 찾을 수 없다는 오류를 뿜뿜..하더라.. 이유가 뭘까싶어서 이것저것 검색해보았는데, 대부분 어노테이션 설정을 빼먹어서 빈등록이 안되어있었다. 나는 제대로 해줬는데 왜 안되지? 싶었다.

 

@Configuration
public class AppConfiguration {
 
}
@Repository
public class MemoryVoucherRepository implements VoucherRepository{

    //something
    
}
@Service
public class VoucherService {
	
    //something

}

알고보니 congif 위치가 최상단에 위치하지 않아서 전체 스캔을 못했던것..!

디렉토리 계층에 상관없이 어노테이션 기반으로 연결될거라 생각했는데, 디렉토리 계층에 영향을 받았다.

컴포넌트 스캔을 할 때 다양한 설정이 가능하다.

@ComponentScan(basePackages =  {"packageName", "packageName"}) -> 설정해준 패키지 단위로 스캔 된다.
@ComponentScan(basePackages =  {package.class}) -> 스트링 기반이 아닌 클래스 기반으로 설정, 설정해준 패키지 단위로 스캔된다.
@ComponentScan(excludeFilters = {@ComponentScan.Filter(type = FilterType.~)}) -> 원하지 않는 디펜던시, 패키지, 클래스 타입 단위 등등 제외하고 스캔된다.

ex)

@ComponentScan (basePackages = { "org.prgrms.kdt.voucher", "org.prgrms.kdt.order" }, excludeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = MemoryVoucherRepository.class)}) -> org.prgrms.kdt.voucher 와 org.prgrms.kdt.order 하위에 있는 클래스를 스캔하는데, excludeFilters로 설정되어있는 voucher 하위에 있는 MemoryVoucherRepository는 제외하고 스캔한다.

공식 문서부터 찾는 습관을 들이자!!!!!!!

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/ComponentScan.html

 

ComponentScan (Spring Framework 5.3.9 API)

The BeanNameGenerator class to be used for naming detected components within the Spring container. The default value of the BeanNameGenerator interface itself indicates that the scanner used to process this @ComponentScan annotation should use its inherite

docs.spring.io

 

2. Autowired

https://madplay.github.io/post/why-constructor-injection-is-better-than-field-injection

 

생성자 주입을 @Autowired를 사용하는 필드 주입보다 권장하는 하는 이유

@Autowired를 사용하는 의존성 주입보다 생성자 주입(Constructor Injection)을 더 권장하는 이유는 무엇일까?

madplay.github.io

 

아래와 같이 생성자를 2개 이상 만들면 어떻게 될까 ?

public VoucherService(VoucherRepository voucherRepository) {
    this.voucherRepository = voucherRepository;
}

public VoucherService(VoucherRepository voucherRepository, String testString) {
    this.voucherRepository = voucherRepository;
    String test = testString;
}

 

스프링에 자동으로 주입이되는 생성자에 @Autowired를 붙여주면 된다! 라고 강사님이 말하셨는데. 자동으로 주입되는 생성자가 무슨말이지? 자동으로 주입되는 생성자 기준을 누가 어디서 어떻게 판단하는거지? 라는 생각이 들 찰나에 해당 부분에 대해서 설명해주셨는데,

 

그러니까 최소한 1개의 생성자에 @Autowired를 붙여서 컨테이너에 주입해야하는게 무엇인지 알려줘야하는데, 이때 쓸 수 있는 어노테이션으로는 @Primary 와 @Qualifier 가 있다.

@Primary : 가장 우선되어야 하는 Repository에 달아준다.
@Qualifier("") : 우선순위가 동등하나 다른용도일때 Repository에 달아준다.

@Qualifier 에 대해서 더 자세히 알아보자면

가령 다음과 같은 클래스가 있다고 하자. Service 는 어떤 빈을 주입해야할까? MemoryVoucherRepository ? JDBCVoucherRepository? 

@Repository
public class MemoryVoucherRepository implements VoucherRepository{

    //something
    
}
@Repository
public class JDBCVoucherRepository implements VoucherRepository{

    //something
    
}
@Service
public class VoucherService {

    private final VoucherRepository voucherRepository;

    public VoucherService(VoucherRepository voucherRepository) {
        this.voucherRepository = voucherRepository;
    }

}
public class OrderTester {

    public static void main(String[] args) {
    
        var applicationContext = new AnnotationConfigApplicationContext(AppConfiguration.class);
        var voucherRepository = applicationContext.getBean(VoucherRepository.class);
    
    }

}

이럴땐 @Qualifier 를 이용하여 서비스에서 어떤 빈을 주입받아야하는지 알려주면 된다.

@Repository
@Qualifier("memory")
public class MemoryVoucherRepository implements VoucherRepository{

    //something
    
}
@Repository
@Qualifier("jdbc")
public class JDBCVoucherRepository implements VoucherRepository{

    //something
    
}
@Service
public class VoucherService {

    private final VoucherRepository voucherRepository;

    public VoucherService(@Qualifier("memory") VoucherRepository voucherRepository) {
        this.voucherRepository = voucherRepository;
    }

}
public class OrderTester {

    public static void main(String[] args) {
    
        //BeanFactory 에서 "memory"에 해당하는 VoucherRepository를 가져옴
        var voucherRepository = BeanFactoryAnnotationUtils.qualifiedBeanOfType(applicationContext.getBeanFactory(), VoucherRepository.class, "memory")
    
    }

}

 

3. Beans Scope

기본적으로 singleton, prototype, request, session, application websocket scope 이 존재한다.

그 중에서도 singleton, prototype 에 대해  적어보자면 스프링 빈은 기본적으로 싱글톤 스콥이다.

싱글톤 스콥이기 때문에 객체가 단 하나만 존재하게 된다. 그럼 다른 객체를 가져야할땐 어떻게 해야할까?

그럴 때 프로토타입 스콥을 이용하면 된다. 프로토타입 스콥을 이용하려면 프로토타입 스콥이라는걸 알려줘야 한다.

@Repository
@Qualifier("memory")
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class MemoryVoucherRepository implements VoucherRepository{

    //something
    
}

궁금증

스프링 내부 코드를 보면 많은 부분이 스트링으로 관리되고 무언가를 구별하고? 하는 코드가 많은 것 같다.

스트링은 실수의 여지가 너무 크다고 생각하는데, 이런건 그냥 안고 가는건지 궁금하다.


고민

오늘 강의는 스프링의 토대가 되는 핵심 개념들을 배운 것 같다. 모르는 부분은 그때 그때 찾아가면서 학습하느라 강의 소화하기까지 평상시보다 아주 오래 걸렸는데, 학습 시간 분배를 어떻게 해야할지 고민스러웠다.  

'TIL > 2021' 카테고리의 다른 글

[TIL] 0822 Logger  (0) 2021.08.22
[TIL] 0820 우선순위를 명확하게  (0) 2021.08.21
[TIL] 0813 Database 이것저것5  (0) 2021.08.13
[TIL] 0812 Database 이것저것4  (0) 2021.08.12
[TIL] 0811 Database 이것저것3  (0) 2021.08.11