1.@ComponentScan注解作用
@ComponentScan
用于
类或接口
上主要是
指定扫描路径
,spring会把指定路径下带有
指定注解的类
自动装配到bean容器里。会被自动装配的注解包括
@Controller
、
@Service
、
@Component
、
@Repository
等等。与
ComponentScan
注解相对应的XML配置就是<
context:component-scan/>
, 根据指定的配置自动扫描
package
,将符合条件的组件加入到IOC容器中;
XML的配置方式如下:
<context:component-scan
base-package="com.example.test" use-default-filters="false"><context:exclude-filter type="custom"
expression="com.example.test.filter.MtyTypeFilter"/></context:component-scan>
2. @ComponentScan注解属性
@ComponentScan有如下常用属性:
- basePackages和value:指定要扫描的路径(
package
),如果为空则以@ComponentScan
注解的类所在的包为基本的扫描路径。 - basePackageClasses:指定具体扫描的类。
- includeFilters:指定满足
Filter
条件的类。 - excludeFilters:指定排除
Filter
条件的类。 - useDefaultFilters=true/false:指定是否需要使用
Spring
默认的扫描规则:被@Component, @Repository, @Service, @Controller
或者已经声明过@Component
自定义注解标记的组件;
在过滤规则Filter中:
FilterType:指定过滤规则,支持的过滤规则有:
- ANNOTATION:按照注解规则,过滤被指定注解标记的类(默认);
- ASSIGNABLE_TYPE:按照给定的类型;
- ASPECTJ:按照ASPECTJ表达式;
- REGEX:按照正则表达式;
- CUSTOM:自定义规则,自定义的Filter需要实现TypeFilter接口;
value和classes:指定在该规则下过滤的表达式;
@ComponentScan的常见的配置如下:
@ComponentScan(
excludeFilters ={@Filter(
type =FilterType.CUSTOM,
classes ={TypeExcludeFilter.class}),@Filter(
type =FilterType.CUSTOM,
classes ={AutoConfigurationExcludeFilter.class})})
3. @ComponentScan过滤规则说明
规则表达式说明
1. 扫描指定类文件
@ComponentScan(basePackageClasses =Person.class)2. 扫描指定包,使用默认扫描规则,即被@Component,@Repository,@Service,@Controller或者已经声明过@Component自定义注解标记的组件;
@ComponentScan(value ="com.example")3. 扫描指定包,加载被@Component注解标记的组件和默认规则的扫描(因为useDefaultFilters默认为true)
@ComponentScan(value ="com.example", includeFilters ={@Filter(type =FilterType.ANNOTATION, value =Component.class)})4. 扫描指定包,只加载Person类型的组件
@ComponentScan(value ="com.example", includeFilters ={@Filter(type =FilterType.ASSIGNABLE_TYPE, value =Person.class)}, useDefaultFilters =false)5. 扫描指定包,过滤掉被@Component标记的组件
@ComponentScan(value ="com.example", excludeFilters ={@Filter(type =FilterType.ANNOTATION, value =Component.class)})6. 扫描指定包,自定义过滤规则
@ComponentScan(value ="com.example", includeFilters ={@Filter(type =FilterType.CUSTOM, value =MtyTypeFilter.class)}, useDefaultFilters =true)
4. 自定义扫描过滤规则
- 用户自定义扫描过滤规则,需要实现
org.springframework.core.type.filter.TypeFilter
接口。
//1.自定义类实现TypeFilter接口并重写match()方法publicclassMtyTypeFilterimplementsTypeFilter{/**
*
* @param metadataReader:读取到当前正在扫描的类的信息
* @param metadataReaderFactory:可以获取到其他任何类的信息
* @return
* @throws IOException
*/@Overridepublicbooleanmatch(MetadataReader metadataReader,MetadataReaderFactory metadataReaderFactory)throwsIOException{System.out.println("========MtyTypeFilter===========");//获取当前类的注解的信息AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();System.out.println("annotationMetadata: "+annotationMetadata);//输出结果:annotationMetadata: com.example.test.bean.Color//获取当前正在扫描的类的类信息ClassMetadata classMetadata = metadataReader.getClassMetadata();System.out.println("classMetadata: "+classMetadata);//输出结果: classMetadata: com.example.test.bean.Color//获取当前类资源(类的路径)Resource resource = metadataReader.getResource();System.out.println("resource: "+resource);//输出结果:resource: file [D:\idea\demo-02\target\classes\com\example\test\bean\Color.class]//获取类名String className = classMetadata.getClassName();System.out.println("className: "+className);//输出结果:className: com.example.test.bean.ColorClass<?> forName =null;try{
forName =Class.forName(className);if(Color.class.isAssignableFrom(forName)){// 如果是Color的子类,就加载到IOC容器returntrue;}}catch(ClassNotFoundException e){
e.printStackTrace();}System.out.println("========MtyTypeFilter===========");returnfalse;}}
5. @ComponentScans
可以一次声明多个@ComponentScan
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Documented@Repeatable(ComponentScans.class)//指定ComponentScan可以被ComponentScans作为数组使用public@interfaceComponentScan{}@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Documentedpublic@interfaceComponentScans{ComponentScan[]value();}
@ComponentScans(value ={@ComponentScan(value ="com.example.test"),@ComponentScan(value ="com.example.test", includeFilters ={@Filter(type =FilterType.CUSTOM, value =MtyTypeFilter.class)})})publicclassMainConfig{@Bean(name ="pers", initMethod ="init", destroyMethod ="destory")publicPersonperson(){returnnewPerson();}}
6. spring boot处理@ComponentScan源码分析
spring
创建
bean
对象的基本流程是先创建对应的
BeanDefinition
对象,然后在基于
BeanDefinition
对象来创建
Bean
对象,
SpringBoot
也是如此,只不过通过注解创建BeanDefinition对象的时机和解析方式不同而已。
SpringBoot
是通过
ConfigurationClassPostProcessor
这个
BeanFactoryPostProcessor
类来处理。
本演示的demo涉及到4个演示类,分别是:
- 带有@SpringBootApplication注解的启动类Demo02Application。
- 带有@RestController注解的类HelloController。
- 带有@Configuration注解且有通过@Bean注解来创建addInterceptors的方法的MyMvcConfig类。
- Account实体类无任何注解。
本文的最后会贴出所有代码。 先从启动类为入口,SpringBoot启动类如下:
@SpringBootApplicationpublicclassDemo02Application{publicstaticvoidmain(String[] args){//1、返回我们IOC容器ConfigurableApplicationContext run =SpringApplication.run(Demo02Application.class, args);}}
从
SpringApplication.run(Demo02Application.class, args);
一路断点到核心方法
SpringApplication.ConfigurableApplicationContext run(String... args)
方法
run方法干了两件事:
- 创建SpringApplication对象
- 利用创建好的SpringApplication对象调用run方法
publicConfigurableApplicationContextrun(String... args){long startTime =System.nanoTime();DefaultBootstrapContext bootstrapContext =this.createBootstrapContext();ConfigurableApplicationContext context =null;this.configureHeadlessProperty();//初始化监听器SpringApplicationRunListeners listeners =this.getRunListeners(args);//发布ApplicationStartingEven
listeners.starting(bootstrapContext,this.mainApplicationClass);try{//装配参数和环境ApplicationArguments applicationArguments =newDefaultApplicationArguments(args);//发布ApplicationEnvironmentPreparedEventConfigurableEnvironment environment =this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);this.configureIgnoreBeanInfo(environment);Banner printedBanner =this.printBanner(environment);//创建ApplicationContext,并装配
context =this.createApplicationContext();
context.setApplicationStartup(this.applicationStartup);//发布ApplicationPreparedEventthis.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);this.refreshContext(context);this.afterRefresh(context, applicationArguments);Duration timeTakenToStartup =Duration.ofNanos(System.nanoTime()- startTime);if(this.logStartupInfo){(newStartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), timeTakenToStartup);}//发布ApplicationStartedEven
listeners.started(context, timeTakenToStartup);//执行Spring中@Bean下的一些操作,如静态方法this.callRunners(context, applicationArguments);}catch(Throwable var12){this.handleRunFailure(context, var12, listeners);thrownewIllegalStateException(var12);}try{Duration timeTakenToReady =Duration.ofNanos(System.nanoTime()- startTime);
listeners.ready(context, timeTakenToReady);return context;}catch(Throwable var11){this.handleRunFailure(context, var11,(SpringApplicationRunListeners)null);thrownewIllegalStateException(var11);}}
- 重点方法一:本方法法实现的重点功能:
- 本
demo
是web
工程,springboot
通过反射创建上下文context:AnnotationConfigServletWebServerApplicationContext
类 - 在构建context的无参构造方法中构建成员变量reader=new AnnotatedBeanDefinitionReader(this),在AnnotatedBeanDefinitionReader的无参构造方法中会beanFactory对象,并向beanFactory中注册5个BeanDefinition对象,重点关注ConfigurationClassPostProcessor。
context =this.createApplicationContext();
- 重点方法二:本方法实现的重点功能 本方法会构建启动类Demo02Application对应的BeanDefinition对象,并注册到beanFactory中,此时的context对象可见下图
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
- 重点方法三:本方法实现的重点功能 该方法实际调用applicationContext的refresh方法,代码分析详见我的另一篇博客,本文后面只会分析ConfigurationClassPostProcessor对象的创建和postProcessBeanDefinitionRegistry方法的执行
this.refreshContext(context);this.afterRefresh(context, applicationArguments);
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);代码执行后的截图如下:
ConfigurationClassPostProcessor实现BeanFactoryPostProcessor,关于BeanFactoryPostProcessor扩展接口的作用在《spring初始化源码浅析之关键类和扩展接口》一文中有详细介绍。
ConfigurationClassPostProcessor对象的创建和方法执行的断点如下:
this.refreshContext(context);–> AbstractApplicationContext.refresh() --> invokeBeanFactoryPostProcessors() -->PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()->invokeBeanDefinitionRegistryPostProcessors()
下面重点看ConfigurationClassPostProcessor类的postProcessBeanDefinitionRegistry()方法如何处理@ComponentScan注解:
同过源代码发现最终是由ConfigurationClassParser的解析类来处理,继续查看ConfigurationClassParser.doProcessConfigurationClass
原来在这里对@ComponentScan注解做了判断,上面一段代码做了核心的几件事:
- 扫描@ComponentScan注解包下面的所有的可自动装备类,生成BeanDefinition对象,并注册到beanFactory对象中。
- 通过DeferredImportSelectorHandler处理@EnableAutoConfiguration注解,后续会有专文介绍。
- 将带有@Configuration 注解的类解析成ConfigurationClass对象并缓存,后面创建@Bean注解的Bean对象所对应的BeanDefinition时会用到 到此为止MyFilter2对应的BeanDefinition已创建完毕,如下图:
版权归原作者 qq_39093474 所有, 如有侵权,请联系我们删除。