单刀直入ComponentScan

2024-09-06 08:52

本文主要是介绍单刀直入ComponentScan,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

何为单刀直入,为何单刀直入

以大家都知道的一个知识点,逐层剖析最终与spring的核心框架相遇,很多讲解spring源码的文章都是上来一大堆晦涩难懂的基础知识,结果让人望而却步,本系列文章旨在由一个每个人都知道的点并且平时都会用到的知识,剖析spring实现的方式,从而与spring诸多的概念相遇,从具体的应用再到概念的理解,面窄一些但是更容易懂一些。

今天说的就是 @interface ComponentScan

看到这个注解大家都不陌生,知道她是干啥的吧,就是扫描指定路径下的包含对应注解的类然后加入spring容器中,那么是如何实现的呢,今天我们一起来深度了解一下。
有一些基础的朋友可能知道,这种spring的自定义注解跟自定义标签类似,都是需要有一个对应的parse类来的,要不怎么解读她呢,就是要获取注解中指定的属性,那么这个类就要闪亮登场了,ComponentScanAnnotationParser,她具体的工作就是要解析ComponentScan注解中指定的属性值.

public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :BeanUtils.instantiateClass(generatorClass));ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");if (scopedProxyMode != ScopedProxyMode.DEFAULT) {scanner.setScopedProxyMode(scopedProxyMode);}else {Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver");scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass));}scanner.setResourcePattern(componentScan.getString("resourcePattern"));for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {for (TypeFilter typeFilter : typeFiltersFor(filter)) {scanner.addIncludeFilter(typeFilter);}}for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {for (TypeFilter typeFilter : typeFiltersFor(filter)) {scanner.addExcludeFilter(typeFilter);}}boolean lazyInit = componentScan.getBoolean("lazyInit");if (lazyInit) {scanner.getBeanDefinitionDefaults().setLazyInit(true);}Set<String> basePackages = new LinkedHashSet<>();String[] basePackagesArray = componentScan.getStringArray("basePackages");for (String pkg : basePackagesArray) {String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);Collections.addAll(basePackages, tokenized);}for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {basePackages.add(ClassUtils.getPackageName(clazz));}if (basePackages.isEmpty()) {basePackages.add(ClassUtils.getPackageName(declaringClass));}scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {@Overrideprotected boolean matchClassName(String className) {return declaringClass.equals(className);}});return scanner.doScan(StringUtils.toStringArray(basePackages));
}

以上是parse类的源码,主要完成ComponentScan注解的解析,同时通过ClassPathBeanDefinitionScanner 根据注解中配置的属性进行扫描,这个parse类有一个承上启下的作用,向上要介绍spring在启动的时候是如何执行这个parse类的,我们知道包的扫描是spring启动时就开始的,向下要介绍,到底是如何进行包的扫描并且把扫描到的包加载到容器中的,我们接下来先说"向下"的过程。
scanner.doScan,这句是关键,还是上代码吧,这样直观点。

/*** Perform a scan within the specified base packages,* returning the registered bean definitions.* <p>This method does <i>not</i> register an annotation config processor* but rather leaves this up to the caller.* @param basePackages the packages to check for annotated classes* @return set of beans registered if any for tooling registration purposes (never {@code null})*/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) {ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);candidate.setScope(scopeMetadata.getScopeName());String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);if (candidate instanceof AbstractBeanDefinition) {postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);}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;}

我们在阅读代码的时候一定要阅读注释哈,可能有些英语不太好的朋友,可以通过微信扫一扫里面的翻译功能哈,比如我就一直用这个,哈哈,这里面有几句关键,Set candidates = findCandidateComponents(basePackage); 找到合适的组件,那么什么事合适的,我们都知道符合条件的是加了@Component @Controler @Service 等注解的类,就是在这里确定的,感兴趣的朋友可以自己继续向下看,也可以等我明天继续描述下,因为里面东西还不少,最终把臊面到的类准变味beanDefinition这个不陌生吧,然后通过beanRegister接口给注册进去的,这个可以自己看下。接下来说一说"向上"如何处理,一般这种启动注册bean的事都是谁干呢,可能有点基础的朋友都会想到 BeanDefinitionRegistryPostProcessor 他是BeanFactoryPostProcessor的子接口,感兴趣的可以继续看看BeanDefinitionRegistryPostProcessor 是怎么回事,spring是合适调用的他呢,这个想对简单点哈,AbstractApplicationContext这个类一般不会陌生吧,里面有个家喻户晓的方法refresh ,这个方法中有一个invokeBeanFactoryPostProcessors(beanFactory); 就是在这进行调用,的,然后通过层层调用最终调用到我们的ComponentScanAnnotationParser中的parse方法,完成上面说的注解的解析和扫描到符合条件的类,并最终注册到spring容器中的,里面涉及到的内容比较多,不放在一篇文章里了,接下来的文章会围绕着"向上"和“向下”来说,这里就主要把这条线串起来。

这篇关于单刀直入ComponentScan的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1141561

相关文章

单刀直入@ComponentScan之 资源加载

欢迎大家入坑,所谓师傅领进坑爬出去靠个人,首先我要说的是这个是上一篇《单刀直入@ComponentScan》的姊妹篇哈,接着把没聊透的事说明白,咱不是虎头蛇尾的人。 资源加载是啥意思 scan ,都认识吧,小学词汇连我都认识,扫到的是啥,扫到的是资源啊,如何让资源为我所用,就需要把资源搞进来,这就是资源加载。 spring如何加载资源的 首先不得不承认spring本身是很专一的,她把所有的

@ComponentScan注解

作用与功能‌:‌ -‌自动扫描组件‌:‌@ComponentScan注解用于指定Spring在初始化时应该扫描哪些包来寻找带有@Component、‌@Service、‌@Repository、‌@Controller等注解的类,‌并把这些类注册为Bean。‌ -‌重要属性‌:‌ -‌basePackages‌:‌指定需要扫描的包名或类,‌Spring会扫描这些包及其子包下的注解。‌ -‌bas

@ComponentScan扫描多个包配置

Springboot会自动扫描启动类同级包及其同级包的子包所有的注解。 如果想自己控制扫描哪些包的话,使用@ComponentScan注解,多个包的话使用逗号分隔。 代码示例: @ComponentScan("com.package1,cn.package2")public class APP {public static void main(String[] args) {SpringA

@ComponentScan 和 @Configuration

@ComponentScan 如果不设置basePackage的话 默认会扫描包的所有类,所以最好还是写上basePackage ,减少加载时间。默认扫描**/*.class路径 比如这个注解在com.wuhulala 下面 ,那么会扫描这个包下的所有类还有子包的所有类,比如com.wuhulala.service包的应用 @Configuration 表示这个类是一个spring 配置类,一般

【报错解决】引入@ComponentScan注解注册bean容器后,导致的接口404问题

引入@ComponentScan注解注册bean容器后,导致的接口404问题 背景 由于微服务开发中,经常需要在公共模块在引入一些公共模块,供其他服务使用,但是其他服务需要在启动类中配置@ComponentScan注解扫描这个公共模块下注册的 bean,但是,如果我们配置了@ComponentScan并且扫描了公共模块下的 bean,那么将会破坏@ComponentScan默认扫描的包,也就是

@ComponentScan({})和@MapperScan({})爆红问题

@ComponentScan({})和@MapperScan({})爆红问题 01 异常的发生场景 当我配置启动类时发生的错误 02 异常的产生及其原因 这个问题我知道是没有在配置环境xml文件中导入对应包导致的 03 解决方式 在启动类模块的pom.xml文件的中配置

spring源码------`@ComponentScans`,`@ComponentScan`注解解析以及spring5.0新特性META-INF/spring.components文件...

1.@ComponentScans,@ComponentScan作用  @ComponentScan这个注解作用大家应该都熟悉,这里的作用大家应该都知道。用来指定spring注册bean的时候需要扫描的包或者类,还可以指定我们定义的bean名称生成器,代理类型,扫描过滤器等与xml配置形式的<context:component-scan>标签作用一样。看看其中的元素就知道。 @Retentio

SpringBoot 源码解析5:ConfigurationClassPostProcessor整体流程和@ComponentScan源码分析

SpringBoot 源码解析5:ConfigurationClassPostProcessor整体流程和@ComponentScan源码分析 1. 知道以下几点,读ConfigurationClassPostProcessor源码会更轻松2. 源码解析 ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry2.1

【spring源码分析】@ComponentScan的使用以及分析

@ComponentScan @ComponentScan 一、基本信息二、注解描述三、注解源码四、主要功能五、最佳实践六、时序图七、源码分析八、注意事项九、总结 最佳实践总结源码分析总结 一、基本信息 转载自github,在此作为个人备份 二、注解描述 @ComponentScan 注解,用于自动扫描特定包(和其子包)中的组件,并自动注册为 Spring 容器中的 bean。当我们

Spring 中 ConfigurationClassPostProcessor 类扫描解析之 @ComponentScan 解析

ConfigurationClassPostProcessor 简单概述 Spring 中类的解析是非常重要的,因为工程中有很多类,并且被一些注解修饰,比如:@Component、@Bean、@Import、@PropertySource、@ImportSource、@Scope 等。 你在类或者方法上标注这些注解,Spring 想要认识它,就需要通过 ConfigurationClassPo