@Component

클래스 선언부 위 쪽에 @Component 어노테이션을 붙이면 @Configuration 클래스에 별도로 Bean 등록 과정을 거치지 않아도 스프링이 패키지 전체를 탐색해서 Bean으로 등록 할 수 있다.

@Component
public class AppleDao {
    ...
}

별도의 Bean 등록 메서드가 필요 없기 때문에 설정 코드가 크게 줄어든다.

@Component 어노테이션으로 Bean의 이름도 지정해줄 수 있다.
(Bean 등록 메서드의 @Qualifier("~")와 비슷한 동작)

@Component("switBananaDao")
public class BananaDao {
    ...
}

앞선 AppleDao의 경우 별도로 이름을 지정해주지 않았는데, 이 경우 자동으로 appleDao의 이름을 갖게 된다. 즉, 이름을 설정하지 않을 경우 클래스 이름의 첫 글자만 소문자로 바꾼 Bean 이름이 사용된다.


@ComponentScan

@ComponentScan - basePackages

@Component 어노테이션이 스캔 되는 범위를 지정하려면 설정 클래스에 @ComponentScan 어노테이션을 추가해야 한다.

@Configuration
@ComponentScan(basePackages = {"spring"})
public class AppCtx {
    ...
}

@ComponentScan(basePackages = {"spring"}) 설정을 통해 스캔 대상 패키지 목록을 지정한다. 배열 형태이기 때문에 여러 패키지를 동시에 지정할 수도 있다. 지정된 패키지와 그 하위 패키지에 속한 클래스를 대상으로 스캔을 진행한다.

basePackages 를 지정하지 않을 경우 전체 패키지를 스캔한다. 이 경우 @ComponentScan 어노테이션을 사용하지 않는 것과 별반 다름 없다.

@ComponentScan - excludeFilters - pattern

excludeFilters 속성을 사용하면 특정 대상을 스캔 대상에서 제외할 수 있다.

@Configuration
@ComponentScan(basePackages = {"spring"},
    excludeFilters = @Filter(type = FilterType.REGEX, pattern = "spring\\..*Dao"))
public class AppCtx {
    ...
}

위 코드는 @Filter 어노테이션의 type 속성으로 FilterType.REGEX(정규표현식)을 선택했다.

@Configuration
@ComponentScan(basePackages = {"spring"},
    excludeFilters = @Filter(type = FilterType.ASPECTJ, pattern = "spring.*Dao"))
public class AppCtx {
    ...
}

위 코드는 @Filter 어노테이션의 type 속성으로 FilterType.ASPECTJ를 선택했다. AspectJ 패턴을 사용하려면 build.gradle 파일에 aspectjweaver 모듈을 추가해주면 된다.

pattern 속성 역시도 basePackges 처럼 배열 형태를 사용해서 여러 패턴을 지정해줄 수 있다.

@ComponentScan - excludeFilters - classes

특정 어노테이션을 붙이고 있는 Bean들을 제외 시키고 싶다면 FilterType.ANNOTATION을 사용할 수 있다.

@Configuration
@ComponentScan(basePackages = {"spring"},
    excludeFilters = @Filter(type = FilterType.ANNOTATION, 
    classes = {NoProduct.class, ManualBean.class})
public class AppCtx {
    ...
}

FilterType.ANNOTATION을 사용하면 classes 속성에 필터로 사용할 어노테이션의 값을 지정해줄 수 있다.

특정 클래스나 그 하위 클래스를 스캔 대상에서 제외하려면 FilterType.ASSIGNABLE_TYPE을 사용할 수 있다.

@Configuration
@ComponentScan(basePackages = {"spring"},
    excludeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, 
    classes = MemberDao.class})
public class AppCtx {
    ...
}

역시나 배열 형태를 사용해서 여러 클래스들을 지정할 수 있다.

@ComponentScan - excludeFilters - 필터 여러개

만일 여러 @Filter를 등록하고 싶다면 아래와 같이 이용하면 된다.

@Configuration
@ComponentScan(basePackages = {"spring"},
    excludeFilters = {
        @Filter(type = FilterType.ANNOTATION, classes = ManualBean.class)
        @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = MemberDao.class)
    })
public class AppCtx {
    ...
}


스캔 대상

@Component 뿐만 아니라 다른 어노테이션을 붙인 클래스도 스캔 대상이 될 수 있다.

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

이 때 @Aspect를 제외한 나머지 어노테이션들은 모두 @Component 어노테이션을 내포하면서 특수한 기능들을 가진 어노테이션들이다.


컴포넌트 스캔 충돌 처리

Bean 이름 충돌

@Configuration
@ComponentScan(basePackages = {"spring", "spring2"})
public class AppCtx {
    ...
}

위와 같이 서로 다른 패키지에 같은 이름을 가진 Bean 클래스가 2개 존재하고, 2개의 클래스 모두 @Component 어노테이션을 갖고 있을 경우 Exception이 발생한다. 이 때문에 @Component 어노테이션에 이름을 지정하는 등의 방법으로 충돌을 피해야 한다.

@Configuration 클래스에 수동 등록한 Bean과 충돌

@Component
public class MemberDao {
    ...
}
@Configuration
@ComponentScan(basePackages = {"spring"})
public class AppCtx {
    
    @Bean
    public MemberDao memberDao() {
        return new MemberDao();
    }
}

스캔할 때 사용하는 Bean 이름과 수동 등록 Bean 이름이 같은 경우에도 충돌이 발생한다. 이 때는 Exception이 발생하지 않고 수동으로 등록된 Bean이 우선권을 가진다.

Bean 주입에서는 @Autowired가 수동보다 우선권을 가졌지만, Bean 등록에서는 반대의 양상을 보인다.

태그:

업데이트:

댓글남기기