Component Scan
์คํ๋ง ๋น์ ๋ฑ๋กํ ๋๋ ์๋ฐ ์ฝ๋์ @Bean
์ด๋ XML์ <bean>
๋ฑ์ ํตํด์ ์ค์ ์ ๋ณด์ ์ง์ ๋ฑ๋กํ ์คํ๋ง ๋น์ ๋์ดํ์ฌ ๋ฑ๋กํ ์ ์๋ค. ํ์ง๋ง ์ด๋ ๊ฒ ๋ฑ๋กํด์ผ ํ ์คํ๋ง ๋น์ด ์์ญ, ์๋ฐฑ๊ฐ๊ฐ ๋๋ฉด ์ผ์ผ์ด ๋ฑ๋กํ๊ธฐ๋ ํ๋ค๋ฉฐ, ์ค์ ์ ๋ณด๋ ์ปค์ง๊ณ , ๋๋ฝํ๋ ๋ฌธ์ ๋ ๋ฐ์ํ๋ค.
@Configuration
public class AppConfig {
@Bean
public MemberService memberService() {
return new MemberServiceImpl(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
}
์คํ๋ง์ ์ค์ ์ ๋ณด๊ฐ ์์ด๋ ์๋์ผ๋ก ์คํ๋ง ๋น์ ๋ฑ๋กํ๋ ์ปดํฌ๋ํธ ์ค์บ์ด๋ผ๋ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค.
@ComponentScan
: @Component
์ด๋
ธํ
์ด์
์ด ๋ถ์ ๋น์ ๋ค ๋ฑ๋กํด์ค๋ค.
excludeFilters
: ์ ์ธํ Component ์ค์ includeFilters
: ํฌํจํ Component ์ค์ basePackages
: ํ์ํ ๊ธฐ๋ณธ ํจํค์ง ๊ฒฝ๋ก(์ค์ ์ํ ๊ฒฝ์ฐ ํด๋น ์ด๋ ธํ ์ด์ ํจํค์ง ํ์๋ก ์ค์ )basePackages = {"dh0023.springcore.order", "dh0023.springcore.member"}
: ์ฌ๋ฌ ์์ ์์น ์ง์ ๊ฐ๋ฅ
basePackageClassses
: ์ง์ ํ ํด๋์ค์ ํจํค์ง๋ฅผ ํ์ ์์ ์์น๋ก ์ง์ (default :@ComponentScan
์ด ๋ถ์ ์ค์ ์ ๋ณด ํด๋์ค ํจํค์ง ํ์๋ก ์ค์ )
package dh0023.springcore.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
/**
* @ComponentScan์ @Component ์ด๋
ธํ
์ด์
์ด ๋ถ์ ํด๋์ค๋ฅผ ๋น์ผ๋ก ๋ฑ๋กํด์ค๋ค.
* ๊ธฐ๋ณธํจํค์ง๋ฅผ ์ค์ ํด์ฃผ์ง ์์ผ๋ฉด, ํ์ฌ ํจํค์ง ํ์๋ก ์ค์ ๋๋ค.
* ์์ธํ๊ณ ์ถ์ ํด๋์ค๊ฐ ์๋ ๊ฒฝ์ฐ excludeFilters๋ก ์ค์ ํ ์ ์๋ค.
*/
@Configuration
@ComponentScan(
basePackages = "dh0023.springcore",
excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Configuration.class)
)
public class AutoAppConfig {
}
๊ทธ๋ฌ๋ฉด ์์กด๊ด๊ณ๋ ์ด๋ป๊ฒ ์ฃผ์
ํ๋ ๊ฑธ๊น? @Autowired
๋ก ์์กด๊ด๊ณ๋ฅผ ์๋์ผ๋ก ์ฃผ์
ํ ์ ์๋ค.
@Bean
์ผ๋ก ์์ฑํด ์ง์ ์์กด๊ด๊ณ๋ฅผ ์ค์ ํ๋ ์ฝ๋์์, @Component
์ @Autowired
๋ง์ผ๋ก ์์กด๊ด๊ณ์ ๋น์ผ๋ก ๋ฑ๋กํ ์ ์๋ค.
package dh0023.springcore.member.service;
import dh0023.springcore.member.domain.Member;
import dh0023.springcore.member.repository.MemberRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class MemberServiceImpl implements MemberService{
/**
* ์์ฑ์ DI๋ฅผ ํตํด ๊ตฌํํด๋์ค ์์กด์ฑ ์ ๊ฑฐ => ์คํ์๋ง ์ง์ค ๊ฐ๋ฅ
*/
private final MemberRepository memberRepository;
/**
* Autowired๋ก ์๋ ์์กด๊ด๊ณ ์ฃผ์
๊ฐ๋ฅ
*/
@Autowired
public MemberServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
}
๋ณ๋๋ก ๋น ์ด๋ฆ์ ์ค์ ํ๊ณ ์ถ์ ๊ฒฝ์ฐ์๋
@Component("์ค์ ํ ๋น ์ด๋ฆ")
๊ณผ ๊ฐ์ด ์ค์ ํ ์ ์๋ค.@Autowired
๋ฅผ ์ง์ ํ๋ฉด ์คํ๋ง ์ปจํ ์ด๋๊ฐ ํด๋น ์คํ๋ง ๋น์ ์ฐพ์์ ์ฃผ์ ํ๋๋ฐ ์ด๋, ํ์ ์ด ๊ฐ์ ๋น์ ์ฐพ์์ ์ฃผ์ ์ ํ๋ค.
FilterType ์ต์
type
์ค๋ช
์
ANNOTATION
default ์ด๋ ธํ ์ด์ ์ ์ธ์ํด์ ๋์
type = FilterType.ANNOTATION, classes = Configuration.class
ASSIGNABLE_TYPE
์ง์ ํ ํ์ ๊ณผ ์์ ํ์ ์ ์ธ์ํด์ ๋์ ํด๋์ค ์ง์ ์ง์
org.example.ExampleService
ASPECTJ
AspectJ ํจํด ์ฌ์ฉ
org.example..*Service+
REGEX
์ ๊ท ํํ์
org.example.Default.*
CUSTOM
TypeFilter ์ด๋ผ๋ ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํด์ ์ฒ๋ฆฌ
org.example.MyTypeFilter
@ComponentScan
๊ถ์ฅ ์์น
@ComponentScan
๊ถ์ฅ ์์น@ComponentScan
๊ถ์ฅ ์์น
@ComponentScan
๊ถ์ฅ ์์นํจํค์ง ์์น๋ฅผ ๋ณ๋๋ก ์ง์ ํ์ง ์๊ณ , ์ค์ ์ ๋ณด ํด๋์ค ์์น๋ฅผ ํ๋ก์ ํธ ์ต์๋จ์ ๋๋ ๊ฒ์ ๊ถ์ฅํ๋ค.(์คํ๋ง ๋ถํธ๋ ์ด ๋ฐฉ๋ฒ์ผ๋ก ์์)
ํ๋ก์ ํธ ๋ฉ์ธ ์ค์ ์ ๋ณด๋ ํ๋ก์ ํธ๋ฅผ ๋ํํ๋ ์ ๋ณด์ด๊ธฐ ๋๋ฌธ์ ํ๋ก์ ํธ ์์ ๋ฃจํธ ์์น์ ๋๋ ๊ฒ์ ๊ถ์ฅํ๋ค.
@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 {
/**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
@AliasFor(annotation = EnableAutoConfiguration.class)
Class<?>[] exclude() default {};
/**
* Exclude specific auto-configuration class names such that they will never be
* applied.
* @return the class names to exclude
* @since 1.3.0
*/
@AliasFor(annotation = EnableAutoConfiguration.class)
String[] excludeName() default {};
...
}
์คํ๋ง ๋ถํธ๋ฅผ ์ฌ์ฉํ๋ฉด ์คํ๋ง ๋ถํธ์ ๋ํ ์์ ์ ๋ณด์ธ @SpringBootApplication
์์ @ComponentScan
์ด ํฌํจ๋์ด์์ผ๋ฉฐ, ๋ณดํต ์ต์๋จ์ ํด๋น ํด๋์ค๊ฐ ์์นํด์๋ค.
@ComponentScan
๋์
@ComponentScan
๋์@Component
๋ฟ๋ง ์๋๋ผ ๋ค๋ฅธ ์ด๋
ธํ
์ด์
๋ค๋ ์ถ๊ฐ๋ก ๋์์ ํฌํจ๋๋ค.
์๋ฅผ ๋ค์ด @Configuration
์ด๋
ธํ
์ด์
์ ์ดํด๋ณด์.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
//...
}
ํด๋น ์ด๋
ธํ
์ด์
๋ด๋ถ์ @Component
์ด๋
ธํ
์ด์
์ ํฌํจํ๊ณ ์๋ ๊ฒ์ ๋ณผ ์ ์๋ค. @ComponentScan
์ @Component
์ด๋
ธํ
์ด์
์ด ๋ถ์ด์๋ ํด๋์ค๋ ๋ชจ๋ ๋น์ผ๋ก ๋ฑ๋กํ๋ฏ๋ก, @Controller
, @Service
, @Repository
๋ฑ๋ฑ ์ด๋
ธํ
์ด์
์ด ๋ถ์ ํด๋์ค๋ ๋น์ผ๋ก ๋ฑ๋กํ๋ ๊ฒ์ ์ ์ ์๋ค.
์ด๋ ธํ ์ด์ ์๋ ์์๊ด๊ณ๋ผ๋ ๊ฒ์ด ์์ผ๋ฉฐ, ํน์ ์ ๋ ธํ ์ด์ ์ ๋ค๊ณ ์๋ ๊ฒ์ ์ธ์ํ ์ ์๋ ๊ฒ์ ์๋ฐ ์ธ์ด๊ฐ ์ง์ํ๋ ๊ธฐ๋ฅ์ ์๋๊ณ , ์คํ๋ง์ด ์ง์ํ๋ ๊ธฐ๋ฅ์ด๋ค.
์ค๋ณต ๋ฑ๋ก๊ณผ ์ถฉ๋
์๋๋น๋ฑ๋ก vs ์๋ ๋น๋ฑ๋ก
์ปดํฌ๋ํธ ์ค์บ์ ์ํด ์๋์ผ๋ก ์คํ๋ง ๋น์ด ๋ฑ๋ก๋๋๋ฐ, ๊ทธ ์ด๋ฆ์ด ๊ฐ์ ๊ฒฝ์ฐ ์คํ๋ง์ ConflictingBeanDefinitionException
์์ธ ๋ฐ์์ํจ๋ค.
์ด๋ฌํ ๊ฒฝ์ฐ๋ ๊ฑฐ์ ๋ฐ์ํ์ง ์๋๋ค.
์๋ ๋น ๋ฑ๋ก vs ์๋ ๋น ๋ฑ๋ก
@Component
public class MemoryMemberRepository implements MemberRepository {}
@Configuration
@ComponentScan(
excludeFilters = @Filter(type = FilterType.ANNOTATION, classes = Configuration.class)
)
public class AutoAppConfig {
@Bean(name = "memoryMemberRepository")
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
}
๊ฐ์ ์ด๋ฆ์ผ๋ก ์๋๋น๊ณผ ์๋๋น์ด ๋ฑ๋ก๋ ๊ฒฝ์ฐ์๋, ์๋ ๋น๋ฑ๋ก์ด ์ฐ์ ๊ถ์ ๊ฐ์ง๋ค.
Overriding bean definition for bean 'memoryMemberRepository' with a different
definition: replacing
์๋๋น์ด ์๋ ๋น์ ์ค๋ฒ๋ผ์ด๋ฉ ํ๋ค.
์ต๊ทผ ์คํ๋ง ๋ถํธ์์๋ ์๋ ๋น ๋ฑ๋ก๊ณผ ์๋ ๋น ๋์ญ์ด ์ถฉ๋๋๋ฉด ์ค๋ฅ๊ฐ ๋ฐ์ํ๋๋ก ๊ธฐ๋ณธ ๊ฐ์ ๋ฐ๊พธ์์ผ๋ฉฐ, ๋ง์ฝ ์ค๋ฒ๋ผ์ด๋ฉ์ ๊ฐ๋ฅํ๊ฒ ํ๊ณ ์ถ์ผ๋ฉด spring.main.allow-bean-definition-overriding=true
๋ก ์ต์
์ ์ค์ ํ๋ผ๊ณ ๊ฐ์ด๋๋ฅผ ์ฃผ๊ณ ์๋ค.
Consider renaming one of the beans or enabling overriding by setting
spring.main.allow-bean-definition-overriding=true
ํ
์คํธ ์ฝ๋๋ก ํ์ธ
๊ธฐ๋ณธ @ComponentScan
๋น๋ฑ๋ก ํ์ธ
@ComponentScan
๋น๋ฑ๋ก ํ์ธpackage dh0023.springcore.scan;
import dh0023.springcore.config.AutoAppConfig;
import dh0023.springcore.member.service.MemberService;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class AutoAppConfigTest {
@Test
void basicScan() {
ApplicationContext ac = new AnnotationConfigApplicationContext(AutoAppConfig.class);
MemberService memberService = ac.getBean(MemberService.class);
Assertions.assertThat(memberService).isInstanceOf(MemberService.class);
}
}
23:44:34.697 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
23:44:34.701 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
23:44:34.713 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
23:44:34.716 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
23:44:34.739 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'autoAppConfig'
23:44:34.748 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'rateDiscountPolicy'
23:44:34.749 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'memoryMemberRepository'
23:44:34.750 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'memberServiceImpl'
23:44:34.858 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Autowiring by type from bean name 'memberServiceImpl' via constructor to bean named 'memoryMemberRepository'
23:44:34.860 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'orderServiceImpl'
23:44:34.865 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Autowiring by type from bean name 'orderServiceImpl' via constructor to bean named 'memoryMemberRepository'
23:44:34.866 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Autowiring by type from bean name 'orderServiceImpl' via constructor to bean named 'rateDiscountPolicy'
๋ก๊ทธ๋ฅผ ๋ณด๋ฉด singleton bean์ด ๋ฑ๋ก๋๋ ๊ฒ์ ๋ณผ ์ ์์ผ๋ฉฐ, Autowired๋ ํ์ธํ ์ ์๋ค.
์์ธ/ํฌํจ ํ์ธ
MyExcludeComponent
package dh0023.springcore.scan.filter;
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyExcludeComponent {
}
MyIncludeComponent
package dh0023.springcore.scan.filter;
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyIncludeComponent {
}
BeanA
package dh0023.springcore.scan.filter;
@MyIncludeComponent
public class BeanA {
}
BeanB
package dh0023.springcore.scan.filter;
@MyExcludeComponent
public class BeanB {
}
Test
package dh0023.springcore.scan.filter;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import static org.assertj.core.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.*;
public class ComponentFilterAppConfigTest {
@Test
void filterScan() {
ApplicationContext ac = new AnnotationConfigApplicationContext(ComponentFilterAppConfig.class);
BeanA beanA = ac.getBean("beanA", BeanA.class);
assertThat(beanA).isNotNull();
assertThrows(
NoSuchBeanDefinitionException.class, () -> ac.getBean("beanB", BeanB.class)
);
}
@Configuration
@ComponentScan(
includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = MyIncludeComponent.class),
excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = MyExcludeComponent.class)
)
static class ComponentFilterAppConfig {
}
}
์ฐธ๊ณ
Last updated
Was this helpful?