SpringBoot示例分析讲解自动化装配机制核心注解_第1页
SpringBoot示例分析讲解自动化装配机制核心注解_第2页
SpringBoot示例分析讲解自动化装配机制核心注解_第3页
SpringBoot示例分析讲解自动化装配机制核心注解_第4页
SpringBoot示例分析讲解自动化装配机制核心注解_第5页
已阅读5页,还剩13页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第SpringBoot示例分析讲解自动化装配机制核心注解目录1.自动化装配介绍2.SpringBoot自动化配置UML图解3.SpringBoot自动化配置核心注解分析3.1@Inherited3.2@SpringBootConfiguration3.3@EnableAutoConfiguration3.4@ComponentScan3.5@ConfigurationPropertiesScan3.6@AutoConfigurationImportSelector3.7@AutoConfigurationPackages4.总结

1.自动化装配介绍

SpringBoot针对mvc做了大量封装,简化开发者的使用,内部是如何管理资源配置,Bean配置,环境变量配置以及启动配置等?实质是SpringBoot做了大量的注解封装,比如@SpringBootApplication,同时采用Spring4框架的新特性@Conditional基于条件的Bean创建管理机制来实现;

实际的工作场景中是复杂多样的,有些项目需要不同的组件,比如REDIS、MONGODB作缓存;RABBITMQ、KAFKA作消息队列;有些项目运行环境不同,比如JDK7、JDK8不同版本,面对众多复杂的需求,又要做到最大化支持,SpringBoot是如何管理实现的,这就依赖Conditional功能,基于条件的自动化配置。

2.SpringBoot自动化配置UML图解

SpringBootApplication是我们所常用熟知的注解,它是一个组合注解,依赖多个注解,共同实现SpringBoot应用功能,以下为所有依赖的UML图解,我们围绕这些注解深入研究,看下具体的实现。

3.SpringBoot自动化配置核心注解分析

SpringBootApplication注解:

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Inherited

@SpringBootConfiguration

@EnableAutoConfiguration

@ComponentScan(excludeFilters={@Filter(type=FilterType.CUSTOM,classes=TypeExcludeFilter.class),

@Filter(type=FilterType.CUSTOM,classes=AutoConfigurationExcludeFilter.class)})

@ConfigurationPropertiesScan

public@interfaceSpringBootApplication{

*需要排除的自动化配置,根据类名进行排除,比如MongoAutoConfiguration,JpaRepositoriesAutoConfiguration等

@AliasFor(annotation=EnableAutoConfiguration.class)

Class[]exclude()default{};

*需要排除的自动化配置,根据名称进行排除

@AliasFor(annotation=EnableAutoConfiguration.class)

String[]excludeName()default{};

*指定需要扫描的包路径,参数填写包名

@AliasFor(annotation=ComponentScan.class,attribute="basePackages")

String[]scanBasePackages()default{};

*指定需要扫描的包路径

@AliasFor(annotation=ComponentScan.class,attribute="basePackageClasses")

Class[]scanBasePackageClasses()default{};

*Bean方法的动态代理配置,如果没有采用工厂方法,可以标记为false,采用cglib代理。

@AliasFor(annotation=Configuration.class)

booleanproxyBeanMethods()defaulttrue;

}

3.1@Inherited

java.lang.annotation.@Inherited注解,从包名可以看出为JDK自带注解,作用是让子类能够继承父类中引用Inherited的注解,但需注意的是,该注解作用范围只在类声明中有效;如果是接口与接口的继承,类与接口的继承,是不会生效。

3.2@SpringBootConfiguration

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Configuration(proxyBeanMethods=false)

public@interfaceSpringBootConfiguration{

*Bean方法的动态代理配置

@AliasFor(annotation=Configuration.class)

booleanproxyBeanMethods()defaulttrue;

}

这是配置型处理注解,可以看到内部源码引用了@Configuration注解,

自身没有太多的实现,那为什么还需要再包装?官方给出的解释是对Spring的@Configuration的扩展,

用于实现SpringBoot的自动化配置。proxyBeanMethods属性默认为true,作用是对bean的方法是否开启代理方式调用,默认为true,如果没有采用工厂方法,可以设为false,通过cglib作动态代理。

3.3@EnableAutoConfiguration

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Inherited

@AutoConfigurationPackage

@Import(AutoConfigurationImportSelector.class)

public@interfaceEnableAutoConfiguration{

//设置注解支持重载的标识

StringENABLED_OVERRIDE_PROPERTY="spring.boot.enableautoconfiguration";

*排除自动化配置的组件,如MongoAutoConfiguration,JpaRepositoriesAutoConfiguration等

Class[]exclude()default{};

*排除自动化配置的组件,根据名称设置

String[]excludeName()default{};

}

用于管理开启SpringBoot的各种自动化配置注解,如datasource,mongodb,redis等,也是spring-boot-autoconfigure工程的核心注解。

AutoConfigurationPackage

它的主要作用是扫描主程序同级及下级的包路径所有Bean与组件注册到SpringIoc容器中。

Import

它可以把没有声明配置的类注册到SpringIoc容器中管理引用。导入的AutoConfigurationImportSelector类实现BeanClassLoaderAware、ResourceLoaderAware、EnvironmentAware等接口,管理类装载器,资源装载器及环境配置等,是一个负责处理自动化配置导入的选择管理器。在下面【@AutoConfigurationImportSelector剖析】进行详解。

3.4@ComponentScan

这是我们在Spring下面常用的一个注解,它可以扫描Spring定义的注解,如@Componment,@Service等,常用的属性有basePackages扫描路径,includeFilters包含路径过滤器,excludeFilters排除路径过滤器,lazyInit是否懒加载等,能够非常灵活的扫描管理需要注册组件。

3.5@ConfigurationPropertiesScan

作用是扫描指定包及子包路径下面的ConfigurationProperties注解,管理工程配置属性信息。主要属性为basePackages扫描路径,支持多个路径,数组形式;basePackageClasses属性也可以具体到包下面的类,

支持多个配置。

3.6@AutoConfigurationImportSelector

AutoConfigurationImportSelector实现DeferredImportSelector、BeanClassLoaderAware、ResourceLoaderAware、BeanFactoryAware、EnvironmentAware、Ordered接口,为自动化配置的核心处理类,主要负责自动化配置规则的一系列处理逻辑:

/**

*{@linkDeferredImportSelector}tohandle{@linkEnableAutoConfiguration

*auto-configuration}.Thisclasscanalsobesubclassedifacustomvariantof

*{@linkEnableAutoConfiguration@EnableAutoConfiguration}isneeded.

*@authorPhillipWebb

*@authorAndyWilkinson

*@authorStephaneNicoll

*@authorMadhuraBhave

*@since1.3.0

*@seeEnableAutoConfiguration

publicclassAutoConfigurationImportSelectorimplementsDeferredImportSelector,BeanClassLoaderAware,

ResourceLoaderAware,BeanFactoryAware,EnvironmentAware,Ordered{

}

讲解几个技术点:

getCandidateConfigurations方法

protectedListStringgetCandidateConfigurations(AnnotationMetadatametadata,AnnotationAttributesattributes){

ListStringconfigurations=SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),

getBeanClassLoader());

Assert.notEmpty(configurations,"NoautoconfigurationclassesfoundinMETA-INF/spring.factories.Ifyou"

+"areusingacustompackaging,makesurethatfileiscorrect.");

returnconfigurations;

}

该方法是获取所有SpringBoot声明定义的自动化配置类。

看下具体有哪些信息:

这些实际是配置在Spring-boot-autoconfigure工程下的META-INF/spring.factories文件中:

看到这里,我们应该可以明白,为什么AOP,RABBIT,DATASOURCE,REIDS等组件SPRINGBOOT都能帮我们快速配置实现,其实它内部遵循SPI机制,已经把自动化配置做好了封装。

AutoConfigurationGroup类

它是AutoConfigurationImportSelector的内部类,实现了DeferredImportSelector.Group、BeanClassLoaderAware、BeanFactoryAware、ResourceLoaderAware接口,是一个重要的核心类。主要作用是负责自动化配置条目信息的记录,排序,元数据处理等。它通过getImportGroup方法获取返回,该方法实现DeferredImportSelector的接口。

privatestaticclassAutoConfigurationGroup

implementsDeferredImportSelector.Group,BeanClassLoaderAware,BeanFactoryAware,ResourceLoaderAware{

//记录注解的元数据

privatefinalMapString,AnnotationMetadataentries=newLinkedHashMap();

//记录自动化配置条目,放入集合

privatefinalListAutoConfigurationEntryautoConfigurationEntries=newArrayList();

//设置bean的类加载器

privateClassLoaderbeanClassLoader;

//设置bean工厂信息

privateBeanFactorybeanFactory;

//设置资源加载器信息

privateResourceLoaderresourceLoader;

//设置自动化配置的元数据记录

privateAutoConfigurationMetadataautoConfigurationMetadata;

}

属性主要定义了一些自动化配置类目信息、BEAN工厂、类和资源加载器信息。entries条目有22条,具体内容如下:

里面是主要的自动化配置类的元数据信息,autoConfigurationEntries属性就是具体的自动化配置条目。这些主要自动化类配置是Springboot帮助我们实现mvc的核心功能,如请求分发,文件上传,参数验证,编码转换等功能。还有一部分是定制条件自动化配置类,

autoConfigurationMetadata元数据内容较多,包含各种组件,根据环境配置和版本不同,这里可以看到共有705个:

由于SpringBoot支持众多插件,功能丰富,数量较多;这里存在些疑问,这里面的元数据和上面的entries条目都是AutoConfiguration自动化配置类,那有什么区别?其实这里面的,都是基于条件的自动化配置。

我们就拿KafkaAutoConfiguration来看:

可以看到注解ConditionalOnClass,意思是KafkaAutoConfiguration生效的前提是基于KafkaTemplate类的初始化成功,这就是定制条件,也就是基于条件的自动化配置类,虽然有七百多个,但其实是根据工程实际用到的组件,才会触发加载对应的配置。有关Conditional基于条件的自动化配置实现原理,在下面我们再作深入研究。

继续看AutoConfigurationImportSelector内部类的selectImports方法:

@Override

publicIterableEntryselectImports(){

if(this.autoConfigurationEntries.isEmpty()){

returnCollections.emptyList();

//将所有自动化条目根据配置的Exclusion条件作过滤,并转换为SET集合

SetStringallExclusions=this.autoConfigurationEntries.stream()

.map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());

//SET集合,记录所有需要处理的自动化配置

SetStringprocessedConfigurations=this.autoConfigurationEntries.stream()

.map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream)

.collect(Collectors.toCollection(LinkedHashSet::new));

//两个SET,做交集过滤,排除不需要的配置

processedConfigurations.removeAll(allExclusions);

//最后进行排序处理

returnsortAutoConfigurations(processedConfigurations,getAutoConfigurationMetadata()).stream()

.map((importClassName)-newEntry(this.entries.get(importClassName),importClassName))

.collect(Collectors.toList());

}

该方法是针对autoConfigurationEntries自动化配置条目做过滤,根据指定的排除规则处理;再根据设置的启动的优先级做排序整理。从代码中可以看到,先获取所有的allExclusions排除配置信息,再获取所有需要处理的processedConfigurations配置信息,然后做过滤处理,最后再调用sortAutoConfigurations方法,根据order顺序做排序整理。

AutoConfigurationImportSelector内部类的process方法:

@Override

publicvoidprocess(AnnotationMetadataannotationMetadata,DeferredImportSelectordeferredImportSelector){

Assert.state(deferredImportSelectorinstanceofAutoConfigurationImportSelector,

()-String.format("Only%simplementationsaresupported,got%s",

AutoConfigurationImportSelector.class.getSimpleName(),

deferredImportSelector.getClass().getName()));

//获取自动化配置条目

AutoConfigurationEntryautoConfigurationEntry=((AutoConfigurationImportSelector)deferredImportSelector)

.getAutoConfigurationEntry(getAutoConfigurationMetadata(),annotationMetadata);

//记录获取的条目

this.autoConfigurationEntries.add(autoConfigurationEntry);

for(StringimportClassName:autoConfigurationEntry.getConfigurations()){

//放入成员变量entries中

this.entries.putIfAbsent(importClassName,annotationMetadata);

}

该方法是扫描获取autoConfigurationEntries自动化配置条目信息。

annotationMetadata参数:

为注解元数据,有也就是被@SpringBootApplication修饰的类信息,在这里就是我们的启动入口类信息。

deferredImportSelector参数:

通过@EnableAutoConfiguration注解定义的@Import的类,也就是AutoConfigurationImportSelector对象。根据配置,会加载指定的beanFactory、classLoader、resourceLoader和environment对象。

AutoConfigurationImportSelector内部类的getAutoConfigurationEntry方法:

protectedAutoConfigurationEntrygetAutoConfigurationEntry(AutoConfigurationMetadataautoConfigurationMetadata,

AnnotationMetadataannotationMetadata){

//1、判断是否开对应注解

if(!isEnabled(annotationMetadata)){

returnEMPTY_ENTRY;

//2、获取注解定义的属性

AnnotationAttributesattributes=getAttributes(annotationMetadata);

//3、获取符合规则的SpringBoot内置的自动化配置类,并做去重处理

ListStringconfigurations=getCandidateConfigurations(annotationMetadata,attributes);

configurations=removeDuplicates(configurations);

//4、做排除规则匹配,过滤处理

SetStringexclusions=getExclusions(annotationMetadata,attributes);

checkExcludedClasses(configurations,exclusions);

configurations.removeAll(exclusions);

configurations=filter(configurations,autoConfigurationMetadata);

//5、触发自动导入处理完成事件

fireAutoConfigurationImportEvents(configurations,exclusions);

returnnewAutoConfigurationEntry(configurations,exclusions);

}

该方法主要作用是获取SpringBoot内置的自动化条目,例AopAutoConfiguration等,该方法会调用上面讲解的getCandidateConfigurations方法。主要步骤逻辑如下:

判断是否开启元注解扫描,对应属性为spring.boot.enableautoconfiguration,默认情况下,是开启自动配置。获取定义的注解属性,跟踪内部源码,里面会返回exclude和excludeName等属性。获取符合规则的SpringBoot内置的自动化配置,并做去重处理,也就是我们上面讲解的getCandidateConfigurations方法,从中我们就可以理解其中的关联关系。做排除规则检查与过滤处理,根据上面第2个步骤获取的exclude等属性以及配置属性spring.autoconfigure.exclude做过滤处理。触发自动导入完成事件,该方法内部逻辑正常处理完成才会触发,会调用AutoConfigurationImportListener监听器做通知处理。

3.7@AutoConfigurationPackages

AutoConfigurationPackages是EnableAutoConfiguration上的另一个核心注解类,官方解释为:

Indicatesthatthepackagecontainingtheannotatedclassshouldberegistered

意思是包含该注解的类,所在包下面的class,都会注册到SpringIoc容器中。对应源码:

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Inherited

@Import(AutoConfigurationPackages.Registrar.class)

public@interfaceAutoConfigurationPackage{

Import注解,导入AutoConfigurationPackages抽象类下面的内部静态类Registrar,研究Registrar实现原理:

Registrar实现ImportBeanDefinitionRegistrar、DeterminableImports接口,它负责存储从@AutoConfigurationPackage注解扫描到的信息。源码如下:

staticclassRegistrarimplementsImportBeanDefinitionRegistrar,DeterminableImports{

//注册BEAN的定义信息

@Override

publicvoidregisterBeanDefinitions(AnnotationMetadatametadata,BeanDefinitionRegistryregistry){

register(registry,newPackageImport(metadata).getPackageName());

//决定是否导入注解中的配置内容

@Override

publicSetObjectdetermineImports(AnnotationMetadatametadata){

returnCollections.singleton(newPackageImport(metadata));

}

这里面主要涉及到PackageImport类,它是AutoConfigurationPackages的内部私有静态类,主要是记录导入的报名信息,源码如下:

/**

*Wrapperforapackageimport.

privatestaticfinalclassPackageImport{

privatefinalStringpackageName;

//构造方法,记录注解内容

PackageImport(AnnotationMetadatametadata){

this.packageName=ClassUtils.getPackageName(metadata.getClassName());

//获取指定包名称

publicStringgetPackageName(){

returnthis.packageName;

//重载父类比较逻辑,根据包名判断

@Override

publicbooleanequals(Objectobj){

if(obj==null||getClass()!=obj.getClass()){

returnfalse;

returnthis.packageName.equals(((PackageImport)obj).packageName);

//重载hash标识,以包名的HASH值为准

@Override

publicinthashCode(){

returnthis.packageName.hashCode();

//重载toString,打印内容

@Override

publicStringtoString(){

return"PackageImport"+this.packageName;

}

内部断点跟踪的话,可以看到它记录的是我们启动类所在的包名。这也就是为什么不需要指定扫描包路径,也会加载启动类所在包下面的JavaConfig配置信息。

回到上面Registrar的registerBeanDefinitions方法,内部调用的是register方法:

它是处理记录AutoConfigurationPackages扫描包信息,源码如下:

publicstaticvoidregister(BeanDefinitionRegistryregistry,String...packageNames){

//判断是否包含BEAN定义信息,如果包含,更新packageNames信息

if(registry.containsBeanDefinition(BEAN)){

BeanDefinitionbeanDefinition=registry.getBeanDefinition(BEAN);

ConstructorArgumentValuesconstructorArguments=beanDefinition.getConstructorArgumentValues();

constructorArguments.addIndexedArgumentValue(0,addBasePackages(constructorArguments,packageNames));

//如果registry中

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论