목차

    @ComponentScan

    @ComponentScan 애노테이션은 spring 3.1부터 도입됐으며 설정된 시작 지점부터 컴포넌트 클래스를 scanning하여 빈으로 등록해주는 역할을 한다.

    컴포넌트 클래스는 다음 애노테이션이 붙은 클래스를 의미한다.

    • @Component
    • @Repository
    • @Service
    • @Controller
    • @Configuration

    @ComponentScan의 가장 중요한 두 가지 속성은 component를 scan할 시작 지점을 설정하는 속성과 scan한 component 중 빈으로 등록하지 않을 클래스를 제외하는 필터 속성이다.

    component-scan은 기본적으로 @Component 어노테이션을 빈 등록 대상으로 포함한다. 그렇다면 @Controller 나 @Service는 어떻게 인식하는 걸까? 그 이유는 @Controller나 @Service가 @Component를 포함하고 있기 때문이다.

    component-scan 사용방법

    component-scan 을 사용하는 방법은
    xml 파일에 설정하는 방법, 과 자바파일안에서 설정하는 방법이 있다.

    1. xml 파일에 설정

    <context:component-scan base-package="com.rcod.lifelog"/> 

    다음과 같이 xml 파일에 설정하고, base package를 적어주면 base package 기준으로 클래스들을 스캔하여 빈으로 등록한다.
    base package에 여러개의 패키지를 쓸 수 있다.

    <context:component-scan base-package="com.rcod.lifelog, com.rcod.example"/> 

    위와 같이 설정하면, base pacakage 하위의 @Controller, @Service @Repository, @Component 클래스가 모두 빈으로 등록되므로, 특정한 객체만 빈으로 등록하여 사용하고 싶다면 include-filter나 exclude-filter를 통해 설정할 수 있다.

    • exclude-filter
    <context:component-scan base-package="com.rcod.lifelog">
        <context:exclude-filter type="annotation" 
            expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

    @Controller 를 제외하고 싶다면 위와 같이 exclude-filter를 사용하여
    org.springframework.stereotype.Controller를 명시해준다.

    • include-filter
    <context:component-scan base-package="com.rcod.lifelog" use-default="false">
        <context:include-filter type="annotation" 
            expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

    use-default="false"는 기본 어노테이션 @Controller, @Component등을 스캔하지 않는다는 것이다. 기본 어노테이션을 스캔하지 않는다고 설정하고, include-filter를 통해서 위와 같이 특정 어노테이션만 스캔할 수 있다.

    2. 자바 파일안에서 설정

    @Configuration
    @ComponentScan(basePackages = "com.rcod.lifelog")
    public class ApplicationConfig {
    }

    @Configuration 은 이 클래스가 xml을 대체하는 설정 파일임을 알려준다. 해당 클래스를 설정 파일로 설정하고 @ComponentScan을 통하여 basePackages를 설정해준다.

    • 위와 같이 component-scan을 사용하는 두 가지 방법이 있다. 만약 component-scan을 사용하지 않으면, 빈으로 설정할 클래스들을 우리가 직접 xml 파일에 일일이 등록해 주어야 한다.
    		<bean id="mssqlDAO" class="com.test.spr.MssqlDAO"></bean>
    		
    		<!-- MemberList 객체에 대한 정보 전달 및 의존성 주입 -->
    		<bean id="member" class="com.test.spr.MemberList">
    			
    			<!-- 속성의 이름을 지정하여 주입 -->
    			<property name="dao">
    				<ref bean="mssqlDAO"/>
    			</property>
    		
    		</bean>

    MssqlDAO  MemberList를 빈으로 등록하고, MemberList에 Mssql을 주입한 것이다. 위와 같이 코드가 매우 길어지고, 일일이 추가하기에 복잡해진다.

    component-scan 동작 과정

    ConfigurationClassParser 가 Configuration 클래스를 파싱한다.
    @Configuration 어노테이션 클래스를 파싱하는 것이다.
                       ⬇
    ComponentScan 설정을 파싱한다.
    base-package 에 설정한 패키지를 기준으로
    ComponentScanAnnotationParser가 스캔하기 위한 설정을 파싱한다.
                       ⬇
    base-package 설정을 바탕으로 모든 클래스를 로딩한다.
                       ⬇
    ClassLoader가 로딩한 클래스들을 BeanDefinition으로 정의한다.
    생성할 빈의 대한 정의를 하는 것이다.
                       ⬇
    생성할 빈에 대한 정의를 토대로 빈을 생성한다.


    Spring boot에서의 @ConponentScan

    이전 Xml Config 방식에서 ComponentScan을 사용하는 방법은 다음과 같았다.

    <context:component-scan base-package="com.example.a">

    applicationContext를 구성할때 이렇게 명시적으로 내가 읽어들여야하는 component들이 있는 package를 넣어줬다. 

     

    하지만 Springboot에서는 Xml Config보다는 Java Config를 사용하고 @기반의 설정을 많이 한다. 아니 이 Component Scan을 하지도 않는데 알아서 잘 된다. 어떻게 된 일일까? 바로 Springboot의 핵심! @SpringBootApplication 에 답이 있다. Springboot Main Class에 있는 @SpringBootApplication를 ctrl을 누르고 눌러서 들어가보자. 

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @SpringBootConfiguration
    @EnableAutoConfiguration
    @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
    @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
    public @interface SpringBootApplication {
    ... 생략 ...
    @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
    String[] scanBasePackages() default {};
    @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
    Class<?>[] scanBasePackageClasses() default {};
    ... 생략 ...

    들어가보면 이런식으로 구성이 되어있다. 복잡해 보이지만 쉽게 설명을 하자면 내가 아무런 ComponentScan 관련 설정을 하지 않았다면 바로 이 @SpringBootApplication 가 정의된 곳이 base package가 되는 것이다. 그래서 처음 프로젝트 구조를 만들때 이 Springboot Main Class의 package가 매우 중요하다. 

    그리고 아래에 나와있는 @AliasFor 부분에 나온 basePackages와 basePackagesClasses도 중요하다. Springboot Main Class의 위치에 구애받지 않고 내가 마음대로 ComponentScan을 할 곳을 정의할때 사용된다.



     

    'Spring' 카테고리의 다른 글

    [Spring] PHP 사용 소감 (Spring과 비교)  (0) 2022.05.05
    스프링 vs 스프링 부트  (0) 2021.03.31

    + Recent posts