首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >《Spring6核心技术》第10章:深度解析@Component注解(注解+案例+时序图+源码)

《Spring6核心技术》第10章:深度解析@Component注解(注解+案例+时序图+源码)

作者头像
冰河
发布2023-05-24 11:44:31
7520
发布2023-05-24 11:44:31
举报
文章被收录于专栏:冰河技术冰河技术

一、学习指引

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-05-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 冰河技术 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、学习指引
    • 二、注解说明
      • 2.1 注解源码
      • 2.2 使用场景
    • 三、使用案例
      • 四、源码时序图
        • 五、源码解析
          • 六、总结
            • 七、思考
            相关产品与服务
            容器服务
            腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档