μ€νλ§ λΉμ λ±λ‘ν λλ μλ° μ½λμ @Bean
μ΄λ XMLμ <bean>
λ±μ ν΅ν΄μ μ€μ μ 보μ μ§μ λ±λ‘ν μ€νλ§ λΉμ λμ΄νμ¬ λ±λ‘ν μ μλ€. νμ§λ§ μ΄λ κ² λ±λ‘ν΄μΌ ν μ€νλ§ λΉμ΄ μμ, μλ°±κ°κ° λλ©΄ μΌμΌμ΄ λ±λ‘νκΈ°λ νλ€λ©°, μ€μ μ 보λ 컀μ§κ³ , λλ½νλ λ¬Έμ λ λ°μνλ€.
Copy @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
μ΄ λΆμ μ€μ μ 보 ν΄λμ€ ν¨ν€μ§ νμλ‘ μ€μ )
Copy 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
λ§μΌλ‘ μμ‘΄κ΄κ³μ λΉμΌλ‘ λ±λ‘ν μ μλ€.
Copy 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 μ΅μ
default
μ΄λ
Έν
μ΄μ
μ μΈμν΄μ λμ
type = FilterType.ANNOTATION, classes = Configuration.class
μ§μ ν νμ
κ³Ό μμ νμ
μ μΈμν΄μ λμ
ν΄λμ€ μ§μ μ§μ
org.example.ExampleService
TypeFilter μ΄λΌλ μΈν°νμ΄μ€λ₯Ό ꡬνν΄μ μ²λ¦¬
@ComponentScan
κΆμ₯ μμΉ
@ComponentScan
κΆμ₯ μμΉ
ν¨ν€μ§ μμΉλ₯Ό λ³λλ‘ μ§μ νμ§ μκ³ , μ€μ μ 보 ν΄λμ€ μμΉλ₯Ό νλ‘μ νΈ μ΅μλ¨μ λλ κ²μ κΆμ₯νλ€.(μ€νλ§ λΆνΈλ μ΄ λ°©λ²μΌλ‘ μμ)
νλ‘μ νΈ λ©μΈ μ€μ μ 보λ νλ‘μ νΈλ₯Ό λννλ μ 보μ΄κΈ° λλ¬Έμ νλ‘μ νΈ μμ λ£¨νΈ μμΉμ λλ κ²μ κΆμ₯νλ€.
Copy @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
λμ
@Component
λΏλ§ μλλΌ λ€λ₯Έ μ΄λ
Έν
μ΄μ
λ€λ μΆκ°λ‘ λμμ ν¬ν¨λλ€.
μλ₯Ό λ€μ΄ @Configuration
μ΄λ
Έν
μ΄μ
μ μ΄ν΄λ³΄μ.
Copy @Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
//...
}
ν΄λΉ μ΄λ
Έν
μ΄μ
λ΄λΆμ @Component
μ΄λ
Έν
μ΄μ
μ ν¬ν¨νκ³ μλ κ²μ λ³Ό μ μλ€. @ComponentScan
μ @Component
μ΄λ
Έν
μ΄μ
μ΄ λΆμ΄μλ ν΄λμ€λ λͺ¨λ λΉμΌλ‘ λ±λ‘νλ―λ‘, @Controller
, @Service
, @Repository
λ±λ± μ΄λ
Έν
μ΄μ
μ΄ λΆμ ν΄λμ€λ λΉμΌλ‘ λ±λ‘νλ κ²μ μ μ μλ€.
μ΄λ
Έν
μ΄μ
μλ μμκ΄κ³λΌλ κ²μ΄ μμΌλ©°, νΉμ μ λ
Έν
μ΄μ
μ λ€κ³ μλ κ²μ μΈμν μ μλ κ²μ μλ° μΈμ΄κ° μ§μνλ κΈ°λ₯μ μλκ³ , μ€νλ§μ΄ μ§μνλ κΈ°λ₯μ΄λ€.
μ€λ³΅ λ±λ‘κ³Ό μΆ©λ
μλλΉλ±λ‘ vs μλ λΉλ±λ‘
μ»΄ν¬λνΈ μ€μΊμ μν΄ μλμΌλ‘ μ€νλ§ λΉμ΄ λ±λ‘λλλ°, κ·Έ μ΄λ¦μ΄ κ°μ κ²½μ° μ€νλ§μ ConflictingBeanDefinitionException
μμΈ λ°μμν¨λ€.
μ΄λ¬ν κ²½μ°λ κ±°μ λ°μνμ§ μλλ€.
μλ λΉ λ±λ‘ vs μλ λΉ λ±λ‘
Copy @Component
public class MemoryMemberRepository implements MemberRepository {}
Copy @Configuration
@ComponentScan(
excludeFilters = @Filter(type = FilterType.ANNOTATION, classes = Configuration.class)
)
public class AutoAppConfig {
@Bean(name = "memoryMemberRepository")
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
}
κ°μ μ΄λ¦μΌλ‘ μλλΉκ³Ό μλλΉμ΄ λ±λ‘λ κ²½μ°μλ, μλ λΉλ±λ‘μ΄ μ°μ κΆμ κ°μ§λ€.
Copy Overriding bean definition for bean 'memoryMemberRepository' with a different
definition: replacing
μλλΉμ΄ μλ λΉμ μ€λ²λΌμ΄λ© νλ€.
μ΅κ·Ό μ€νλ§ λΆνΈμμλ μλ λΉ λ±λ‘κ³Ό μλ λΉ λμμ΄ μΆ©λλλ©΄ μ€λ₯κ° λ°μνλλ‘ κΈ°λ³Έ κ°μ λ°κΎΈμμΌλ©°, λ§μ½ μ€λ²λΌμ΄λ©μ κ°λ₯νκ² νκ³ μΆμΌλ©΄ spring.main.allow-bean-definition-overriding=true
λ‘ μ΅μ
μ μ€μ νλΌκ³ κ°μ΄λλ₯Ό μ£Όκ³ μλ€.
Copy Consider renaming one of the beans or enabling overriding by setting
spring.main.allow-bean-definition-overriding=true
ν
μ€νΈ μ½λλ‘ νμΈ
κΈ°λ³Έ @ComponentScan
λΉλ±λ‘ νμΈ
Copy 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);
}
}
Copy 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λ νμΈν μ μλ€.
μμΈ/ν¬ν¨ νμΈ
Copy package dh0023.springcore.scan.filter;
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyExcludeComponent {
}
Copy package dh0023.springcore.scan.filter;
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyIncludeComponent {
}
Copy package dh0023.springcore.scan.filter;
@MyIncludeComponent
public class BeanA {
}
Copy package dh0023.springcore.scan.filter;
@MyExcludeComponent
public class BeanB {
}
Copy 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 {
}
}
μ°Έκ³