精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

深度解析@Component注解(注解+案例+時序圖+源碼)

開發 前端
首先介紹了@Component注解的源碼和使用場景,隨后介紹了@Component注解的使用案例。接下來,詳細介紹了@Component在Spring中執行的源碼時序圖和源碼流程。
  • 本章難度:★★★★☆
  • 本章重點:進一步學習并掌握@Component注解向IOC容器中注入Bean的案例和流程,從源碼級別徹底掌握@Component注解在Spring底層的執行流程。

本節目錄如下所示:

  • 學習指引
  • 注解說明

注解源碼

使用場景

  • 使用案例
  • 源碼時序圖
  • 源碼解析
  • 總結
  • 思考
  • VIP服務

一、學習指引

Spring中的@Component注解,你真的徹底了解過嗎?

@Component注解可以說是Spring中使用的比較頻繁的一個注解了。在項目開發過程中,我們自己編寫的類如果想注入到Spring中,由Spring來管理Bean的生命周期,就可以使用@Component注解將其注入到IOC容器中。

并且@Component注解還有三個衍生注解,那就是@Repository、@Service和@Controller注解,并且衍生出的注解通常會在使用MVC架構開發項目時,標注到MVC架構的分層類上。

比如:@Repository通常會被標注到表示dao層的類上,@Service注解通常會被標注到表示Service層的類上,而@Controller注解通常會被標注到表示Controller層的類上。

二、注解說明

關于@Component注解的一點點說明~~

使用Spring開發項目時,如果類上標注了@Component注解,當啟動IOC容器時,Spring掃描到標注了@Component注解的單例Bean,就會創建對應的Bean對象并注入到IOC容器中。

2.1 注解源碼

IOC容器在啟動時,如果掃描到被標注了@Component注解的類,則會將這些類的類定義信息自動注入IOC容器,并創建這些類的對象。

@Component注解的源碼詳見: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版本開始提供的注解,并且@Component注解只能標注到類上。其中只含有一個String類型的value屬性,具體含義如下所示。

  • value:用于指定注入容器時Bean的id。如果沒有指定Bean的id,默認值為當前類的名稱。

@Component注解提供了三個衍生注解:分別是:@Repository、@Service和@Controller注解。

(1)@Repository注解

@Repository注解的源碼詳見: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注解的源碼詳見: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注解注解的源碼詳見: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注解本質上還是@Component注解,這里不再贅述。

2.2 使用場景

在Spring開發項目的過程中,如果需要將自己創建的類注入到IOC容器中,就可以使用@Component注解,也可以使用@Repository、@Service和@Controller注解。

其中,@Component注解一般會被標注到非三層(非MVC架構)類上,而@Repository、@Service和@Controller注解通常會被標注到三層架構的類上。并且@Repository通常會被標注到表示dao層的類上,@Service注解通常會被標注到表示Service層的類上,而@Controller注解通常會被標注到表示Controller層的類上。

這里,需要注意的是,基于Spring的注解開發項目時,必須先將類對象交給Spring管理,然后Spring會處理類中的屬性和方法。如果類沒有被Spring接管,那么類里面的屬性和方法上的注解都不會被解析。

三、使用案例

@Component的實現案例,我們一起實現吧~~

本節,就基于@Component注解、@Repository、@Service和@Controller注解實現簡單的案例程序,觀察被上述四個注解標注的類是否注入到IOC容器中。具體實現步驟如下所示。

(1)新建ComponentBean類

ComponentBean類的源碼詳見:spring-annotation-chapter-10工程下的io.binghe.spring.annotation.chapter10.component.ComponentBean。

@Component
public class ComponentBean {
}

可以看到,ComponentBean就是一個標注了@Component注解的普通類。

(2)新建RepositoryBean類

RepositoryBean類的源碼詳見:spring-annotation-chapter-10工程下的io.binghe.spring.annotation.chapter10.component.RepositoryBean。

@Repository
public class RepositoryBean {
}

可以看到,RepositoryBean類就是一個標注了@Repository注解的普通類。

(3)新建ServiceBean類

ServiceBean類的源碼詳見:spring-annotation-chapter-10工程下的io.binghe.spring.annotation.chapter10.component.ServiceBean。

@Service
public class ServiceBean {
}

可以看到,ServiceBean類就是一個標注了@Service注解的普通類。

(4)新建ControllerBean類

ControllerBean類的源碼詳見:spring-annotation-chapter-10工程下的io.binghe.spring.annotation.chapter10.component.ControllerBean。

@Controller
public class ControllerBean {
}

可以看到,ControllerBean類就是一個標注了@Controller注解的普通類。

(5)新建ComponentConfig類

ComponentConfig類的源碼詳見:spring-annotation-chapter-10工程下的io.binghe.spring.annotation.chapter10.config.ComponentConfig。

@Configuration
@ComponentScan(value = {"io.binghe.spring.annotation.chapter10"})
public class ComponentConfig {
}

可以看到,ComponentConfig類上標注了@Configuration,說明ComponentConfig類是一個Spring的配置類,并且使用@ComponentScan注解指定了掃描的包名是io.binghe.spring.annotation.chapter10。

(6)新建ComponentTest類

ComponentTest類的源碼詳見: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對象的名稱。

(7)運行ComponentTest類

運行ComponentTest類的main()方法,輸出的結果信息如下所示。

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

從輸出的結果信息可以看出,打印出了被@Component、@Repository、@Service和@Controller注解標注的Bean的名稱。

說明:使用Spring開發項目時,如果Spring掃描到類上標注了@Component、@Repository、@Service和@Controller注解的單例Bean,就會創建對應的Bean對象并注入到IOC容器中。

四、源碼時序圖

結合時序圖理解源碼會事半功倍,你覺得呢?

本節,就以源碼時序圖的方式,直觀的感受下@Component注解在Spring源碼層面的執行流程。@Component注解在Spring源碼層面執行的時序圖如圖10-1~10~3所示。

注意:@Repository、@Service和@Controller注解本質上還是@Component注解,這里就不再單獨分析@Repository、@Service和@Controller注解的執行流程。

圖片

圖片

圖片

由圖10-1~10-3可以看出,@Component注解在注冊Bean的流程中涉及到ComponentTest類、AnnotationConfigApplicationContext類、AbstractApplicationContext類、PostProcessorRegistrationDelegate類、ConfigurationClassPostProcessor類、ConfigurationClassParser類、SourceClass類、ComponentScanAnnotationParser類、ClassPathBeanDefinitionScanner類、ClassPathScanningCandidateComponentProvider類、AnnotationConfigUtils類、BeanDefinitionReaderUtils類、和DefaultListableBeanFactory類。具體的源碼執行細節參見源碼解析部分。

五、源碼解析

源碼時序圖整清楚了,那就整源碼解析唄!

本節,主要分析@Component注解在Spring源碼層面的執行流程,結合源碼執行的時序圖,會理解的更加深刻。

注意:本節的源碼分析流程與第9章5.2小節的源碼分析流程大體相同,只是多了一個更加細節的分析,這里,只對這些細節點進行詳細的分析。所以,本節的源碼分析可以結合第9章5.2小節的源碼分析共同理解。

(1)解析ConfigurationClassParser類的doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass, Predicatefilter)方法

源碼詳見: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()方法中,判斷如果本質上是@Component注解(@Repository、@Service和@Controller注解),會調用processMemberClasses()方法處理內部類。

(2)解析ConfigurationClassParser類的processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass,Predicatefilter)方法

源碼詳見: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()方法中,會調用sourceClass的getMemberClasses()方法獲取SourceClass的集合。

(3)解析SourceClass類的getMemberClasses()方法

源碼詳見: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()方法的主要作用就是處理標注了@Component、@Repository、@Service和@Controller注解的類的內部類,因為內部類也有可能會標注這些注解。在getMemberClasses()方法中,利用反射拿到類的內部類,將內部類封裝成SourceClass,存放到members集合中并返回。

(4)返回ConfigurationClassParser類的processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass,Predicatefilter)方法

此時重點關注如下代碼。

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()方法中,如果獲取到的內部類集合memberClasses不為空,則遍歷獲取到的memberClasses集合,使用ConfigurationClassUtils類的isConfigurationCandidate()方法判斷內部類上是否有需要處理的注解,如果有需要處理的注解,則將類添加到candidates集合中。

(5)解析ConfigurationClassUtils類的isConfigurationCandidate(AnnotationMetadata metadata)方法

源碼詳見: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()方法的作用主要是判斷內部類上面是否有需要處理的注解,具體的判斷邏輯是:如果是接口,則直接返回false,如果是@Component(含@Repository、@Service和@Controller)、@ComponentScan、@Import、@ImportResource等注解,則返回true。最后判斷方法上是否標注了@Bean注解,如果標注了@Bean注解,則返回true。否則,返回false。

(6)返回ConfigurationClassParser類的processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass,Predicatefilter)方法

此時重點關注如下代碼。

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()方法中,首先對獲取到的內部類進行排序,隨后遍歷內部類集合,調用candidate的asConfigClass()方法將內部類封裝成ConfigurationClass對象。并傳入processConfigurationClass()方法中解析內部類的注解信息。

(7)返回ConfigurationClassParser類的doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass, Predicatefilter)方法。

繼續分析如下代碼片段。

Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());

其他分析流程省略,直接來到ClassPathBeanDefinitionScanner類的doScan(String... basePackages)方法。

(8)解析ClassPathBeanDefinitionScanner類的doScan(String... basePackages)方法

源碼詳見: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()中,會遍歷傳入的掃描包路徑數組,調用findCandidateComponents()方法加載符合一定條件的BeanDefinition。

(9)解析ClassPathScanningCandidateComponentProvider類的findCandidateComponents(String basePackage)方法

源碼詳見: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()方法中,會調用scanCandidateComponents()方法來掃描basePackage包下標注了注解的類。

(10)解析ClassPathScanningCandidateComponentProvider類的scanCandidateComponents(String basePackage)方法

源碼詳見: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()方法中,會加載basePackage包路徑下的資源,將其封裝成ScannedGenericBeanDefinition類的對象,并傳入isCandidateComponent()方法中對類進行過濾。符合條件時,會將當前ScannedGenericBeanDefinition類的對象存入candidates集合中,最終返回candidates集合。

(11)解析ClassPathScanningCandidateComponentProvider類的isCandidateComponent(MetadataReader metadataReader)方法

源碼詳見: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規則列表,如果匹配到excludeFilters規則,則直接返回false。否則,遍歷includeFilters規則,如果匹配到includeFilters規則,則調用isConditionMatch()方法來匹配@Conditional注解的規則。

這里,注意的是在IOC容器啟動調用AnnotationConfigApplicationContext類的構造方法時,就會對includeFilters規則列表進行初始化。源碼詳見: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()方法中,默認會將@Component注解封裝成AnnotationTypeFilter對象并存入includeFilters規則列表中。

(12)返回ClassPathBeanDefinitionScanner類的doScan(String... basePackages)方法

源碼詳見: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);
        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;
}

后續解析AnnotationConfigUtils類的processCommonDefinitionAnnotations()方法和解析registerBeanDefinition()方法的流程與第9章5.2小節的源碼分析流程一致,這里不再贅述。

至此,@Component注解在Spring源碼層面的執行流程分析完畢。

六、總結

@Component注解介紹完了,我們一起總結下吧!

本章,首先介紹了@Component注解的源碼和使用場景,隨后介紹了@Component注解的使用案例。接下來,詳細介紹了@Component在Spring中執行的源碼時序圖和源碼流程。

七、思考

既然學完了,就開始思考幾個問題吧?

關于@Component注解,通常會有如下幾個經典面試題:

  • @Component注解的作用是什么?
  • @Component注解有哪些使用場景?
  • @Component注解是如何將Bean注入到IOC容器的?
  • @Component注解在Spring內部的執行流程?
  • 你在平時工作中,會在哪些場景下使用@Component注解?
  • 你從@Component注解的設計中得到了哪些啟發?
責任編輯:武曉燕 來源: 冰河技術
相關推薦

2023-03-27 08:12:40

源碼場景案例

2023-03-06 11:13:20

Spring注解加載

2023-03-13 08:12:25

@DependsOn源碼場景

2023-02-27 08:10:00

代理對象Spring

2022-12-22 08:14:54

2023-05-29 08:11:42

@Value注解Bean

2022-12-21 07:56:30

@Component注解派生性

2021-03-26 09:37:12

Java開發代碼

2024-12-17 00:00:00

Spring線程

2021-07-09 07:52:34

SpringContextEventListen

2023-03-29 08:09:51

Spring@Service@Component

2017-09-30 16:06:28

代碼注解分析

2010-07-01 14:04:23

UML時序圖

2021-12-30 12:30:01

Java注解編譯器

2025-07-24 06:34:46

SpringBoot代碼開發

2023-03-30 16:16:00

Java自定義注解開發

2013-08-14 13:11:10

2022-12-08 17:12:34

注解源碼場景

2021-03-11 11:14:39

鴻蒙HarmonyOS應用

2025-02-28 08:16:14

Spring框架注解
點贊
收藏

51CTO技術棧公眾號

成人动漫免费在线观看| 精品在线视频免费观看| 国产一级一片免费播放| 精品久久ai电影| 日韩欧美中文免费| 亚洲午夜精品久久久久久浪潮| 一区二区三区www污污污网站| 免费**毛片在线| 99成人在线| 亚洲日本aⅴ片在线观看香蕉| 在线观看国产一级片| 欧美卡一卡二| 国产女主播在线一区二区| 成人自拍性视频| 中文字幕亚洲高清| 99久久精品费精品国产| 日韩av网站导航| 特黄视频免费观看| 电影在线观看一区| 亚洲天堂免费在线观看视频| 久久爱av电影| 国产乱淫av免费| 噜噜爱69成人精品| 久久视频在线播放| 精品无码国产污污污免费网站| 成人日韩视频| 91国在线观看| 欧美又粗又长又爽做受| 在线播放日本| 91网站最新网址| 亚洲在线观看视频| 中文av免费观看| 亚洲一区二区三区免费在线观看| 久久色在线播放| 日本二区在线观看| 狼人综合视频| 亚洲欧美福利一区二区| 欧美激情导航| 色婷婷av一区二区三区之e本道| 国产亚洲精品v| 欧美激情国产精品| 国产精品视频一区二区在线观看| 亚洲三级性片| 亚洲国产黄色片| 欧美成人免费va影院高清| 亚洲高清视频一区| 日本人妖在线| 波多野结衣中文字幕一区二区三区| 91精品国产综合久久香蕉922| 日韩人妻精品中文字幕| 尤物tv在线精品| 欧美成人a∨高清免费观看| 日本中文字幕亚洲| dy888亚洲精品一区二区三区| 欧美激情中文字幕| 欧美一区二区综合| 97超碰人人草| 久久精品国产第一区二区三区| 日韩av电影免费观看高清| 久久久久久久久99| 欧美日韩午夜| 欧美日韩福利电影| 国产精品成人免费观看| 一区二区蜜桃| 欧美成人午夜激情视频| 男人在线观看视频| 亚洲最大av| 九九精品视频在线观看| 国产一级一片免费播放放a| 伊人影院久久| 欧美一区深夜视频| 一级片在线观看免费| 久久中文字幕一区二区三区| 国产精品久久久久久久久久小说| 亚洲天堂视频网| 黄网站免费久久| 999热视频| 男人天堂综合网| 久久久亚洲精品一区二区三区| 日本欧美精品久久久| 波多野结衣在线网站| 中文字幕中文字幕一区二区| 男人天堂成人网| 欧美日韩在线视频免费观看| 亚洲线精品一区二区三区| 黄色一级片播放| 日本中文字幕一区二区| 欧美一级久久久| 亚洲自偷自拍熟女另类| 色www亚洲国产阿娇yao| 国产乱视频在线观看| 国产精品福利一区二区三区| 看全色黄大色大片| av手机在线观看| 色av成人天堂桃色av| 欧美一级特黄aaa| 哺乳一区二区三区中文视频| 日韩电视剧免费观看网站| 国产熟女一区二区| 欧美激情四色| 国产成人一区二区三区小说| 国产毛片毛片毛片毛片毛片| www..com久久爱| 香蕉久久夜色| 免费在线黄色影片| 1000部国产精品成人观看| 欧美a级免费视频| 国产精品亚洲一区二区三区在线观看| 欧美精品自拍偷拍| 一级做a爰片毛片| 久久精品一区二区不卡| 9.1国产丝袜在线观看| 一本色道久久综合亚洲| 91在线小视频| 色爽爽爽爽爽爽爽爽| 美女扒开腿让男人桶爽久久软| 欧美色涩在线第一页| 美女伦理水蜜桃4| 日韩一区欧美| 69av在线播放| av免费观看在线| 亚洲国产精品传媒在线观看| 人妻夜夜添夜夜无码av | 久久伊人精品视频| 天天干天天干天天干天天| 国产一区二区免费在线| 欧美高清一区二区| 日本片在线看| 欧美日韩国产高清一区二区 | 高潮按摩久久久久久av免费| 伊人伊成久久人综合网小说 | 亚洲福利国产| 91夜夜揉人人捏人人添红杏| 国产剧情在线观看| 先锋影音国产精品| 一二三四区精品视频| 污色网站在线观看| 你懂的一区二区三区| 久久久噜噜噜久久| 精品人妻一区二区三区换脸明星| 精品制服美女丁香| 欧洲亚洲一区| 亚洲精品福利电影| 91成人免费在线视频| 天天躁日日躁狠狠躁免费麻豆| 91精品国产91久久综合| 国产精品亚洲自拍| 91caoporm在线视频| 在线欧美日韩国产| a级大片在线观看| 国产美女一区| 免费日韩av电影| 日本三级一区| 日韩精品www| 探花视频在线观看| 久久久综合激的五月天| 亚洲人成无码www久久久| 亚洲桃色综合影院| 欧美最猛性xxxxx亚洲精品| 香蕉av一区二区三区| 午夜久久电影网| 三叶草欧洲码在线| 久久亚洲精选| 亚洲精品不卡| 亚洲爽爆av| 美女啪啪无遮挡免费久久网站| 国产一区二区波多野结衣| 《视频一区视频二区| 国产九九九视频| 国产精品大片| 日韩综合视频在线观看| 四虎精品欧美一区二区免费| 亚洲精品三区| 久久97精品久久久久久久不卡| 亚洲国产欧美另类| 精品福利樱桃av导航| 国产jk精品白丝av在线观看| 蜜臀av一区二区在线观看| 中文字幕免费在线不卡| 精灵使的剑舞无删减版在线观看| 欧美一区二区在线观看| 久久一区二区三| 91美女在线视频| 天天操天天爱天天爽| 天天射综合网视频| 激情视频在线观看一区二区三区| 超碰国产一区| 久久亚洲国产精品成人av秋霞| 后进极品白嫩翘臀在线视频| 色8久久精品久久久久久蜜| 日韩av手机在线免费观看| 国产精品18久久久久久久网站| 国产日韩欧美亚洲一区| 三上悠亚激情av一区二区三区| 中文字幕亚洲自拍| 午夜精品久久久久久久99老熟妇| 午夜av区久久| 欧美一级片免费播放| 成年人av网站| 国产视频一区二区三区在线观看| 久久久无码中文字幕久...| 国产伦精品一区二区三区免费优势 | 中文字幕 日韩 欧美| 影音先锋一区| 亚洲人成人77777线观看| 国产精品chinese在线观看| 国产精品毛片a∨一区二区三区|国| 99福利在线| 亚洲视频在线免费观看| 亚洲精品国偷拍自产在线观看蜜桃| 欧美午夜精品伦理| 免费中文字幕在线观看| 国产拍揄自揄精品视频麻豆| 欧美成人精品一区二区综合免费| 奇米精品一区二区三区在线观看| 精品国产av无码一区二区三区| 精品国产午夜| 精品一区在线播放| 亚洲性视频在线| 国产精品丝袜久久久久久不卡| av伦理在线| 久久av资源网站| 国产中文字幕在线| 亚洲黄色在线观看| av在线资源观看| 精品视频免费在线| 亚洲乱码国产乱码精品| 亚洲福利电影网| 国产精品偷伦视频免费观看了| 日韩中文字幕亚洲一区二区va在线| 成人区一区二区| 国产精品久久久久9999赢消| 精品久久久久久久久中文字幕 | 欧美国产中文高清| 国产精品极品在线| 欧美大片高清| 欧美在线一区二区三区四| wwwwxxxx在线观看| 欧美人与性动交a欧美精品| 美女写真理伦片在线看| 一区二区三区四区精品| 国产中文在线视频| 亚洲天堂免费在线| 国产在线一二三| 亚洲欧美日韩综合| 欧美成熟毛茸茸| 日韩麻豆第一页| 三级理论午夜在线观看| 日韩的一区二区| 免费一级毛片在线观看| 亚洲欧美日韩综合| 国产精品一区二区三区四区色| 亚洲欧美在线磁力| 国产资源在线播放| 深夜福利亚洲导航| 日本中文字幕视频在线| 在线观看日韩视频| 欧美另类极品| 精品中文字幕乱| 超碰成人av| 国产成人av在线| 日韩欧美一区二区三区在线观看 | 青青视频免费在线| 中文字幕亚洲精品乱码| 黄色特一级视频| 黄色欧美成人| 色欲av无码一区二区人妻| 丝袜美腿亚洲综合| 亚洲精品久久久久久宅男| 国产麻豆精品在线观看| 亚洲一区和二区| av福利精品导航| 国产人妻一区二区| 国产精品精品国产色婷婷| 老熟妇高潮一区二区三区| 一区二区三区四区蜜桃| 日韩黄色一级大片| 欧美艳星brazzers| 99精品国产99久久久久久97| 亚洲欧美色图| 97av视频在线| 你懂得影院夜精品a| 成人福利在线观看| 国产精品美女在线观看直播| 欧美日韩一区二区视频在线观看 | 永久免费在线看片视频| 国产精品a级| 青青在线视频观看| 国产最新精品免费| 国产精品无码永久免费不卡| 国产精品久久久久婷婷| 久久久久久久伊人| 欧美性生活一区| 国产综合视频在线| 亚洲图中文字幕| bestiality新另类大全| 日韩av成人在线观看| 麻豆精品一区| 欧美日韩无遮挡| 欧美69wwwcom| 成人亚洲视频在线观看| 高清日韩电视剧大全免费| 国产亚洲精品熟女国产成人| 一区二区三区中文字幕电影| 中文字幕在线看人| 日韩欧美不卡一区| 最近高清中文在线字幕在线观看| 久久免费视频网站| 亚洲人成777| 欧美一级二级三级九九九| 一区二区免费不卡在线| 亚洲欧美另类动漫| 不卡一二三区首页| 午夜免费激情视频| 欧美亚洲综合在线| 欧洲成人av| 久久久久久亚洲精品不卡| 五月天色综合| 日本高清视频一区二区三区| 在线日韩欧美| 国偷自产av一区二区三区麻豆| 中文字幕乱码日本亚洲一区二区 | 92看片淫黄大片欧美看国产片| 亚洲第一福利社区| 日本福利视频一区| 国产suv精品一区二区三区| 美女网站视频色| 色天使色偷偷av一区二区| 色婷婷av一区二区三| 欧美精品videosex性欧美| 国产中文欧美日韩在线| 亚洲一二三区在线| 日韩高清一级片| 给我看免费高清在线观看| 亚洲福利视频三区| 免费观看毛片网站| 性欧美办公室18xxxxhd| 狠狠一区二区三区| av免费看网址| 成人av网站免费| 国产亚洲欧美精品久久久久久 | 欧美尤物美女在线| 97超级碰在线看视频免费在线看| 99久久免费精品国产72精品九九| 中文字幕精品在线播放| 久久精品72免费观看| 一级黄色毛毛片| 欧美性猛片xxxx免费看久爱| 国产黄色片在线播放| 国产精品极品美女粉嫩高清在线| 亚洲8888| 国产精品亚洲二区在线观看| 久久亚洲捆绑美女| 亚洲 欧美 中文字幕| 国产亚洲精品美女久久久| 成人黄色在线| 国产精品69久久| 国产日产精品一区二区三区四区的观看方式| 精品无码一区二区三区在线| 99久久精品国产一区| 好看的av在线| 国产亚洲a∨片在线观看| 成人高清一区| 国产高清免费在线| 国产成人综合在线| 91av在线免费视频| 亚洲区中文字幕| 草民电影神马电影一区二区| mm131午夜| 波多野结衣视频一区| 久久久久在线视频| www.欧美精品一二三区| 国产精品一区二区三区av| 无颜之月在线看| av一区二区三区四区| 日韩一级片中文字幕| 日韩一区二区欧美| 91麻豆精品激情在线观看最新| 日韩欧美一区二| 亚洲国产精品黑人久久久| 国产情侣一区二区| 久久免费福利视频| 成人系列视频| 成年人性生活视频| 欧美日韩激情视频8区| 国产午夜视频在线观看| 亚洲qvod图片区电影| 国产亚洲福利| 国产喷水在线观看| 亚洲大胆人体在线| 国产一区二区三区影视| 日韩中文字幕在线不卡| 2023国产精品| 国产乱码精品一区二区| 97视频免费看| 91精品推荐| 性欧美13一14内谢| 日韩免费高清视频| 日本黄色一区| 狠狠干 狠狠操|