深度解析@Component注解(注解+案例+時(shí)序圖+源碼)
- 本章難度:★★★★☆
- 本章重點(diǎn):進(jìn)一步學(xué)習(xí)并掌握@Component注解向IOC容器中注入Bean的案例和流程,從源碼級(jí)別徹底掌握@Component注解在Spring底層的執(zhí)行流程。
本節(jié)目錄如下所示:
- 學(xué)習(xí)指引
- 注解說(shuō)明
注解源碼
使用場(chǎng)景
- 使用案例
- 源碼時(shí)序圖
- 源碼解析
- 總結(jié)
- 思考
- VIP服務(wù)
一、學(xué)習(xí)指引
Spring中的@Component注解,你真的徹底了解過(guò)嗎?
@Component注解可以說(shuō)是Spring中使用的比較頻繁的一個(gè)注解了。在項(xiàng)目開(kāi)發(fā)過(guò)程中,我們自己編寫(xiě)的類如果想注入到Spring中,由Spring來(lái)管理Bean的生命周期,就可以使用@Component注解將其注入到IOC容器中。
并且@Component注解還有三個(gè)衍生注解,那就是@Repository、@Service和@Controller注解,并且衍生出的注解通常會(huì)在使用MVC架構(gòu)開(kāi)發(fā)項(xiàng)目時(shí),標(biāo)注到MVC架構(gòu)的分層類上。
比如:@Repository通常會(huì)被標(biāo)注到表示dao層的類上,@Service注解通常會(huì)被標(biāo)注到表示Service層的類上,而@Controller注解通常會(huì)被標(biāo)注到表示Controller層的類上。
二、注解說(shuō)明
關(guān)于@Component注解的一點(diǎn)點(diǎn)說(shuō)明~~
使用Spring開(kāi)發(fā)項(xiàng)目時(shí),如果類上標(biāo)注了@Component注解,當(dāng)啟動(dòng)IOC容器時(shí),Spring掃描到標(biāo)注了@Component注解的單例Bean,就會(huì)創(chuàng)建對(duì)應(yīng)的Bean對(duì)象并注入到IOC容器中。
2.1 注解源碼
IOC容器在啟動(dòng)時(shí),如果掃描到被標(biāo)注了@Component注解的類,則會(huì)將這些類的類定義信息自動(dòng)注入IOC容器,并創(chuàng)建這些類的對(duì)象。
@Component注解的源碼詳見(jiàn):org.springframework.stereotype.Component。
/**
* @author Mark Fisher
* @since 2.5
* @see Repository
* @see Service
* @see Controller
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {
String value() default "";
}
從源碼可以看出,@Component注解是從Spring2.5版本開(kāi)始提供的注解,并且@Component注解只能標(biāo)注到類上。其中只含有一個(gè)String類型的value屬性,具體含義如下所示。
- value:用于指定注入容器時(shí)Bean的id。如果沒(méi)有指定Bean的id,默認(rèn)值為當(dāng)前類的名稱。
@Component注解提供了三個(gè)衍生注解:分別是:@Repository、@Service和@Controller注解。
(1)@Repository注解
@Repository注解的源碼詳見(jiàn):org.springframework.stereotype.Repository。
/**
* @author Rod Johnson
* @author Juergen Hoeller
* @since 2.0
* @see Component
* @see Service
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Repository {
@AliasFor(annotation = Component.class)
String value() default "";
}
(2)@Service注解
@Service注解的源碼詳見(jiàn):org.springframework.stereotype.Service。
/**
* @author Juergen Hoeller
* @since 2.5
* @see Component
* @see Repository
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
@AliasFor(annotation = Component.class)
String value() default "";
}
(3)@Controller注解
@Controller注解注解的源碼詳見(jiàn):org.springframework.stereotype.Controller。
/**
* @author Arjen Poutsma
* @author Juergen Hoeller
* @since 2.5
* @see Component
* @see org.springframework.web.bind.annotation.RequestMapping
* @see org.springframework.context.annotation.ClassPathBeanDefinitionScanner
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
@AliasFor(annotation = Component.class)
String value() default "";
}
可以看到,@Repository、@Service和@Controller注解本質(zhì)上還是@Component注解,這里不再贅述。
2.2 使用場(chǎng)景
在Spring開(kāi)發(fā)項(xiàng)目的過(guò)程中,如果需要將自己創(chuàng)建的類注入到IOC容器中,就可以使用@Component注解,也可以使用@Repository、@Service和@Controller注解。
其中,@Component注解一般會(huì)被標(biāo)注到非三層(非MVC架構(gòu))類上,而@Repository、@Service和@Controller注解通常會(huì)被標(biāo)注到三層架構(gòu)的類上。并且@Repository通常會(huì)被標(biāo)注到表示dao層的類上,@Service注解通常會(huì)被標(biāo)注到表示Service層的類上,而@Controller注解通常會(huì)被標(biāo)注到表示Controller層的類上。
這里,需要注意的是,基于Spring的注解開(kāi)發(fā)項(xiàng)目時(shí),必須先將類對(duì)象交給Spring管理,然后Spring會(huì)處理類中的屬性和方法。如果類沒(méi)有被Spring接管,那么類里面的屬性和方法上的注解都不會(huì)被解析。
三、使用案例
@Component的實(shí)現(xiàn)案例,我們一起實(shí)現(xiàn)吧~~
本節(jié),就基于@Component注解、@Repository、@Service和@Controller注解實(shí)現(xiàn)簡(jiǎn)單的案例程序,觀察被上述四個(gè)注解標(biāo)注的類是否注入到IOC容器中。具體實(shí)現(xiàn)步驟如下所示。
(1)新建ComponentBean類
ComponentBean類的源碼詳見(jiàn):spring-annotation-chapter-10工程下的io.binghe.spring.annotation.chapter10.component.ComponentBean。
@Component
public class ComponentBean {
}
可以看到,ComponentBean就是一個(gè)標(biāo)注了@Component注解的普通類。
(2)新建RepositoryBean類
RepositoryBean類的源碼詳見(jiàn):spring-annotation-chapter-10工程下的io.binghe.spring.annotation.chapter10.component.RepositoryBean。
@Repository
public class RepositoryBean {
}
可以看到,RepositoryBean類就是一個(gè)標(biāo)注了@Repository注解的普通類。
(3)新建ServiceBean類
ServiceBean類的源碼詳見(jiàn):spring-annotation-chapter-10工程下的io.binghe.spring.annotation.chapter10.component.ServiceBean。
@Service
public class ServiceBean {
}
可以看到,ServiceBean類就是一個(gè)標(biāo)注了@Service注解的普通類。
(4)新建ControllerBean類
ControllerBean類的源碼詳見(jiàn):spring-annotation-chapter-10工程下的io.binghe.spring.annotation.chapter10.component.ControllerBean。
@Controller
public class ControllerBean {
}
可以看到,ControllerBean類就是一個(gè)標(biāo)注了@Controller注解的普通類。
(5)新建ComponentConfig類
ComponentConfig類的源碼詳見(jiàn):spring-annotation-chapter-10工程下的io.binghe.spring.annotation.chapter10.config.ComponentConfig。
@Configuration
@ComponentScan(value = {"io.binghe.spring.annotation.chapter10"})
public class ComponentConfig {
}
可以看到,ComponentConfig類上標(biāo)注了@Configuration,說(shuō)明ComponentConfig類是一個(gè)Spring的配置類,并且使用@ComponentScan注解指定了掃描的包名是io.binghe.spring.annotation.chapter10。
(6)新建ComponentTest類
ComponentTest類的源碼詳見(jiàn):spring-annotation-chapter-10工程下的io.binghe.spring.annotation.chapter10.ComponentTest。
public class ComponentTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ComponentConfig.class);
String[] definitionNames = context.getBeanDefinitionNames();
Arrays.stream(definitionNames).forEach((definitionName) -> System.out.println(definitionName));
}
}
可以看到,在ComponentTest類的main()方法中打印了IOC容器中注入的Bean對(duì)象的名稱。
(7)運(yùn)行ComponentTest類
運(yùn)行ComponentTest類的main()方法,輸出的結(jié)果信息如下所示。
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
componentConfig
componentBean
controllerBean
repositoryBean
serviceBean
從輸出的結(jié)果信息可以看出,打印出了被@Component、@Repository、@Service和@Controller注解標(biāo)注的Bean的名稱。
說(shuō)明:使用Spring開(kāi)發(fā)項(xiàng)目時(shí),如果Spring掃描到類上標(biāo)注了@Component、@Repository、@Service和@Controller注解的單例Bean,就會(huì)創(chuàng)建對(duì)應(yīng)的Bean對(duì)象并注入到IOC容器中。
四、源碼時(shí)序圖
結(jié)合時(shí)序圖理解源碼會(huì)事半功倍,你覺(jué)得呢?
本節(jié),就以源碼時(shí)序圖的方式,直觀的感受下@Component注解在Spring源碼層面的執(zhí)行流程。@Component注解在Spring源碼層面執(zhí)行的時(shí)序圖如圖10-1~10~3所示。
注意:@Repository、@Service和@Controller注解本質(zhì)上還是@Component注解,這里就不再單獨(dú)分析@Repository、@Service和@Controller注解的執(zhí)行流程。
由圖10-1~10-3可以看出,@Component注解在注冊(cè)Bean的流程中涉及到ComponentTest類、AnnotationConfigApplicationContext類、AbstractApplicationContext類、PostProcessorRegistrationDelegate類、ConfigurationClassPostProcessor類、ConfigurationClassParser類、SourceClass類、ComponentScanAnnotationParser類、ClassPathBeanDefinitionScanner類、ClassPathScanningCandidateComponentProvider類、AnnotationConfigUtils類、BeanDefinitionReaderUtils類、和DefaultListableBeanFactory類。具體的源碼執(zhí)行細(xì)節(jié)參見(jiàn)源碼解析部分。
五、源碼解析
源碼時(shí)序圖整清楚了,那就整源碼解析唄!
本節(jié),主要分析@Component注解在Spring源碼層面的執(zhí)行流程,結(jié)合源碼執(zhí)行的時(shí)序圖,會(huì)理解的更加深刻。
注意:本節(jié)的源碼分析流程與第9章5.2小節(jié)的源碼分析流程大體相同,只是多了一個(gè)更加細(xì)節(jié)的分析,這里,只對(duì)這些細(xì)節(jié)點(diǎn)進(jìn)行詳細(xì)的分析。所以,本節(jié)的源碼分析可以結(jié)合第9章5.2小節(jié)的源碼分析共同理解。
(1)解析ConfigurationClassParser類的doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass, Predicatefilter)方法
源碼詳見(jiàn):org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass, Predicatefilter)。
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter) throws IOException {
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
processMemberClasses(configClass, sourceClass, filter);
}
/**************省略其他代碼****************/
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
for (AnnotationAttributes componentScan : componentScans) {
Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
/**************省略其他代碼****************/
return null;
}
可以看到,在ConfigurationClassParser類的doProcessConfigurationClass()方法中,判斷如果本質(zhì)上是@Component注解(@Repository、@Service和@Controller注解),會(huì)調(diào)用processMemberClasses()方法處理內(nèi)部類。
(2)解析ConfigurationClassParser類的processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass,Predicatefilter)方法
源碼詳見(jiàn):org.springframework.context.annotation.ConfigurationClassParser#processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass,Predicatefilter)。
private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter) throws IOException {
Collection<SourceClass> memberClasses = sourceClass.getMemberClasses();
/*****************省略其他代碼***************/
}
可以看到,在processMemberClasses()方法中,會(huì)調(diào)用sourceClass的getMemberClasses()方法獲取SourceClass的集合。
(3)解析SourceClass類的getMemberClasses()方法
源碼詳見(jiàn):org.springframework.context.annotation.ConfigurationClassParser.SourceClass#getMemberClasses()。
public Collection<SourceClass> getMemberClasses() throws IOException {
Object sourceToProcess = this.source;
if (sourceToProcess instanceof Class<?> sourceClass) {
try {
Class<?>[] declaredClasses = sourceClass.getDeclaredClasses();
List<SourceClass> members = new ArrayList<>(declaredClasses.length);
for (Class<?> declaredClass : declaredClasses) {
members.add(asSourceClass(declaredClass, DEFAULT_EXCLUSION_FILTER));
}
return members;
}
catch (NoClassDefFoundError err) {
sourceToProcess = metadataReaderFactory.getMetadataReader(sourceClass.getName());
}
}
MetadataReader sourceReader = (MetadataReader) sourceToProcess;
String[] memberClassNames = sourceReader.getClassMetadata().getMemberClassNames();
List<SourceClass> members = new ArrayList<>(memberClassNames.length);
for (String memberClassName : memberClassNames) {
try {
members.add(asSourceClass(memberClassName, DEFAULT_EXCLUSION_FILTER));
}
catch (IOException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to resolve member class [" + memberClassName + "] - not considering it as a configuration class candidate");
}
}
}
return members;
}
getMemberClasses()方法的主要作用就是處理標(biāo)注了@Component、@Repository、@Service和@Controller注解的類的內(nèi)部類,因?yàn)閮?nèi)部類也有可能會(huì)標(biāo)注這些注解。在getMemberClasses()方法中,利用反射拿到類的內(nèi)部類,將內(nèi)部類封裝成SourceClass,存放到members集合中并返回。
(4)返回ConfigurationClassParser類的processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass,Predicatefilter)方法
此時(shí)重點(diǎn)關(guān)注如下代碼。
private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter) throws IOException {
/*******************省略其他代碼*****************/
if (!memberClasses.isEmpty()) {
List<SourceClass> candidates = new ArrayList<>(memberClasses.size());
for (SourceClass memberClass : memberClasses) {
if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) && !memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) {
candidates.add(memberClass);
}
}
/*******************省略其他代碼*****************/
}
}
可以看到,在ConfigurationClassParser類的processMemberClasses()方法中,如果獲取到的內(nèi)部類集合memberClasses不為空,則遍歷獲取到的memberClasses集合,使用ConfigurationClassUtils類的isConfigurationCandidate()方法判斷內(nèi)部類上是否有需要處理的注解,如果有需要處理的注解,則將類添加到candidates集合中。
(5)解析ConfigurationClassUtils類的isConfigurationCandidate(AnnotationMetadata metadata)方法
源碼詳見(jiàn):org.springframework.context.annotation.ConfigurationClassUtils#isConfigurationCandidate(AnnotationMetadata metadata)。
static boolean isConfigurationCandidate(AnnotationMetadata metadata) {
if (metadata.isInterface()) {
return false;
}
for (String indicator : candidateIndicators) {
if (metadata.isAnnotated(indicator)) {
return true;
}
}
return hasBeanMethods(metadata);
}
isConfigurationCandidate()方法的作用主要是判斷內(nèi)部類上面是否有需要處理的注解,具體的判斷邏輯是:如果是接口,則直接返回false,如果是@Component(含@Repository、@Service和@Controller)、@ComponentScan、@Import、@ImportResource等注解,則返回true。最后判斷方法上是否標(biāo)注了@Bean注解,如果標(biāo)注了@Bean注解,則返回true。否則,返回false。
(6)返回ConfigurationClassParser類的processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass,Predicatefilter)方法
此時(shí)重點(diǎn)關(guān)注如下代碼。
private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter) throws IOException {
/**********省略其他代碼**************/
if (!memberClasses.isEmpty()) {
/**********省略其他代碼**************/
OrderComparator.sort(candidates);
for (SourceClass candidate : candidates) {
if (this.importStack.contains(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
this.importStack.push(configClass);
try {
processConfigurationClass(candidate.asConfigClass(configClass), filter);
}
finally {
this.importStack.pop();
}
}
}
}
}
在processMemberClasses()方法中,首先對(duì)獲取到的內(nèi)部類進(jìn)行排序,隨后遍歷內(nèi)部類集合,調(diào)用candidate的asConfigClass()方法將內(nèi)部類封裝成ConfigurationClass對(duì)象。并傳入processConfigurationClass()方法中解析內(nèi)部類的注解信息。
(7)返回ConfigurationClassParser類的doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass, Predicatefilter)方法。
繼續(xù)分析如下代碼片段。
Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
其他分析流程省略,直接來(lái)到ClassPathBeanDefinitionScanner類的doScan(String... basePackages)方法。
(8)解析ClassPathBeanDefinitionScanner類的doScan(String... basePackages)方法
源碼詳見(jiàn):org.springframework.context.annotation.ClassPathBeanDefinitionScanner#doScan(String... basePackages)。
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
for (String basePackage : basePackages) {
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
/***************省略其他代碼*****************/
}
return beanDefinitions;
}
可以看到,在ClassPathBeanDefinitionScanner類的doScan()中,會(huì)遍歷傳入的掃描包路徑數(shù)組,調(diào)用findCandidateComponents()方法加載符合一定條件的BeanDefinition。
(9)解析ClassPathScanningCandidateComponentProvider類的findCandidateComponents(String basePackage)方法
源碼詳見(jiàn):org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#findCandidateComponents(String basePackage)
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
}
else {
return scanCandidateComponents(basePackage);
}
}
在findCandidateComponents()方法中,會(huì)調(diào)用scanCandidateComponents()方法來(lái)掃描basePackage包下標(biāo)注了注解的類。
(10)解析ClassPathScanningCandidateComponentProvider類的scanCandidateComponents(String basePackage)方法
源碼詳見(jiàn):org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#scanCandidateComponents(String basePackage)。
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<>();
try {
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + this.resourcePattern;
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
boolean traceEnabled = logger.isTraceEnabled();
boolean debugEnabled = logger.isDebugEnabled();
for (Resource resource : resources) {
String filename = resource.getFilename();
if (filename != null && filename.contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {
continue;
}
if (traceEnabled) {
logger.trace("Scanning " + resource);
}
try {
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
if (isCandidateComponent(metadataReader)) {
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setSource(resource);
if (isCandidateComponent(sbd)) {
if (debugEnabled) {
logger.debug("Identified candidate component class: " + resource);
}
candidates.add(sbd);
}
/***************省略其他代碼*************/
}
catch (FileNotFoundException ex) {
if (traceEnabled) {
logger.trace("Ignored non-readable " + resource + ": " + ex.getMessage());
}
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to read candidate component class: " + resource, ex);
}
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
}
return candidates;
}
可以看到,在ClassPathScanningCandidateComponentProvider類的scanCandidateComponents()方法中,會(huì)加載basePackage包路徑下的資源,將其封裝成ScannedGenericBeanDefinition類的對(duì)象,并傳入isCandidateComponent()方法中對(duì)類進(jìn)行過(guò)濾。符合條件時(shí),會(huì)將當(dāng)前ScannedGenericBeanDefinition類的對(duì)象存入candidates集合中,最終返回candidates集合。
(11)解析ClassPathScanningCandidateComponentProvider類的isCandidateComponent(MetadataReader metadataReader)方法
源碼詳見(jiàn):org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#isCandidateComponent(MetadataReader metadataReader)。
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
for (TypeFilter tf : this.excludeFilters) {
if (tf.match(metadataReader, getMetadataReaderFactory())) {
return false;
}
}
for (TypeFilter tf : this.includeFilters) {
if (tf.match(metadataReader, getMetadataReaderFactory())) {
return isConditionMatch(metadataReader);
}
}
return false;
}
可以看到,在isCandidateComponent()方法中,首先遍歷excludeFilters規(guī)則列表,如果匹配到excludeFilters規(guī)則,則直接返回false。否則,遍歷includeFilters規(guī)則,如果匹配到includeFilters規(guī)則,則調(diào)用isConditionMatch()方法來(lái)匹配@Conditional注解的規(guī)則。
這里,注意的是在IOC容器啟動(dòng)調(diào)用AnnotationConfigApplicationContext類的構(gòu)造方法時(shí),就會(huì)對(duì)includeFilters規(guī)則列表進(jìn)行初始化。源碼詳見(jiàn):org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#registerDefaultFilters()
protected void registerDefaultFilters() {
this.includeFilters.add(new AnnotationTypeFilter(Component.class));
ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
try {
this.includeFilters.add(new AnnotationTypeFilter(
((Class<? extends Annotation>) ClassUtils.forName("jakarta.annotation.ManagedBean", cl)), false));
logger.trace("JSR-250 'jakarta.annotation.ManagedBean' found and supported for component scanning");
}
catch (ClassNotFoundException ex) {
// JSR-250 1.1 API (as included in Jakarta EE) not available - simply skip.
}
try {
this.includeFilters.add(new AnnotationTypeFilter(
((Class<? extends Annotation>) ClassUtils.forName("jakarta.inject.Named", cl)), false));
logger.trace("JSR-330 'jakarta.inject.Named' annotation found and supported for component scanning");
}
catch (ClassNotFoundException ex) {
// JSR-330 API not available - simply skip.
}
}
可以看到,在registerDefaultFilters()方法中,默認(rèn)會(huì)將@Component注解封裝成AnnotationTypeFilter對(duì)象并存入includeFilters規(guī)則列表中。
(12)返回ClassPathBeanDefinitionScanner類的doScan(String... basePackages)方法
源碼詳見(jiàn):org.springframework.context.annotation.ClassPathBeanDefinitionScanner#doScan(String... basePackages)。此時(shí)重點(diǎn)關(guān)注如下代碼片段。
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
for (String basePackage : basePackages) {
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
/***********省略其他代碼***********/
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
后續(xù)解析AnnotationConfigUtils類的processCommonDefinitionAnnotations()方法和解析registerBeanDefinition()方法的流程與第9章5.2小節(jié)的源碼分析流程一致,這里不再贅述。
至此,@Component注解在Spring源碼層面的執(zhí)行流程分析完畢。
六、總結(jié)
@Component注解介紹完了,我們一起總結(jié)下吧!
本章,首先介紹了@Component注解的源碼和使用場(chǎng)景,隨后介紹了@Component注解的使用案例。接下來(lái),詳細(xì)介紹了@Component在Spring中執(zhí)行的源碼時(shí)序圖和源碼流程。
七、思考
既然學(xué)完了,就開(kāi)始思考幾個(gè)問(wèn)題吧?
關(guān)于@Component注解,通常會(huì)有如下幾個(gè)經(jīng)典面試題:
- @Component注解的作用是什么?
- @Component注解有哪些使用場(chǎng)景?
- @Component注解是如何將Bean注入到IOC容器的?
- @Component注解在Spring內(nèi)部的執(zhí)行流程?
- 你在平時(shí)工作中,會(huì)在哪些場(chǎng)景下使用@Component注解?
- 你從@Component注解的設(shè)計(jì)中得到了哪些啟發(fā)?