SpringBoot自动装配原理详解
首先对于一个SpringBoot工程来说,最明显的标志的就是@SpringBootApplication它标记了这是一个SpringBoot工程,所以今天的SpringBoot自动装配原理也就是从它开始说起。
自动装配流程
首先我们来看下@SpringBootApplication这个注解的背后又有什么玄机呢,我们按下ctrl+鼠标左键,轻轻的点一下,此时见证奇迹的时刻..
我们看到如下优雅的代码:
这其中有两个比较容易引起我们注意的地方,一个是@SpringBootConfiguration注解,另一个是@EnableAutoConfiguration注解;之所以说这个两个注解比较吸引我们的眼球,不是因为它们长大的好看,而是因为其他的注解太难看了(主要是因为其他的注解我们都是比较熟悉,即使不知道他们是干什么的,可以肯定更自动装配是没有关系的)。然后我们又伸出了邪恶的小手,开启了熟悉的操作,按下了Ctrt+鼠标左键,瞪着色咪咪的小眼睛,瞳孔放大了百倍等待着奇迹的出现...擦...擦...擦...
什么也没有...
那我要你有何用,这么顶级的世界级的开源项目,怎么会让一个没用的家伙存在呢?于是动用了上亿的脑细胞大军,经过复杂的运算,得出了一个不靠谱的结论:它可能使用来标记这是一个SpringBoot工程的配置。因为SpringBootConfiguration翻译过来就是SpringBoot的配置,于是心中又是几万只羊驼在万马奔腾,大漠飞扬。
气定神闲之后,秉承着·失败是成功之母"的信念,熟练的左手行云流水般的按下了Ctrl+Table键,回到了最初的的地方。眼睛盯着@EnableAutoConfiguration,环顾左右,在地址栏输入了谷歌翻译,结果显示自动装配。我找的就是你,真是众里寻他千百度,那人却在灯火阑珊处。熟练的按下了Ctrl+左键,迫不及待的想要进入;心里默默背诵起了《桃花源记》的经典诗句∶
林尽水源,便得一山,山有小口,仿佛若有光。便舍船,从口入。初极狭,才通人。复行数十步,豁然开朗
此时此刻心情愉悦,有过前面的经历之后,在面对新的世界时候,我们淡定了许多。此时大脑高速运转,没有再纠结,直捣黄龙,进入了AutoConfigurationImportSelector.class类,因为谷歌翻译告诉我们,这个是自动配置导入选择器。于是我们发现了—片新天地
publicclassAutoConfigurationImportSelectorimplementsDeferredImportSelector,BeanClassLoaderAware, ResourceLoaderAware,BeanFactoryAware,EnvironmentAware,Ordered{ @Override publicString[]selectImports(AnnotationMetadataannotationMetadata){ if(!isEnabled(annotationMetadata)){ returnNO_IMPORTS; } //获取自动配置的实体 AutoConfigurationEntryautoConfigurationEntry=getAutoConfigurationEntry(annotationMetadata); returnStringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); } //具体用来加载自动配置类得方法 protectedAutoConfigurationEntrygetAutoConfigurationEntry(AnnotationMetadataannotationMetadata){ if(!isEnabled(annotationMetadata)){ returnEMPTY_ENTRY; } AnnotationAttributesattributes=getAttributes(annotationMetadata); //获取候选的配置类,即使后宫佳丽三千,也是要筛选的 Listconfigurations=getCandidateConfigurations(annotationMetadata,attributes); //根据情况,自动配置需要的配置类和不需要的配置了 configurations=removeDuplicates(configurations); Set exclusions=getExclusions(annotationMetadata,attributes); checkExcludedClasses(configurations,); configurations.removeAll(exclusions); configurations=getConfigurationClassFilter().filter(configurations); fireAutoConfigurationImportEvents(configurations,exclusions); //返回最终需要的配置 returnnewAutoConfigurationEntry(configurations,exclusions); } }
而这个自动配置的实体AutoConfigurationEntry里面有两个属性,configurations和exclusions。
protectedstaticclassAutoConfigurationEntry{ //用来存储需要的配置项 privatefinalListconfigurations; //用来存储排除的配置项 privatefinalSet exclusions; privateAutoConfigurationEntry(){ this.configurations=Collections.emptyList(); this.exclusions=Collections.emptySet(); } }
在后面可以看到getAutoConfigurationEntry()方法返回了一个对象returnnewAutoConfigurationEntry(configurations,exclusions);这里也就是把我们需要的配置都拿到了。
那他是怎么拿到的候选的配置类呢?我们接着看这个获取候选配置类的方法List
进到方法后我们看到下面这个方法具体获取候选配置类的方法内容
这里我们跟着断点去走,首先进入getSpringFactoriesLoaderFactoryClass()方法
protectedClass>getSpringFactoriesLoaderFactoryClass(){ //返回的是EnableAutoConfiguration字节码对象 returnEnableAutoConfiguration.class; }
接着我们在进入getBeanClassLoader()方法,这里就是一个类加载器
protectedClassLoadergetBeanClassLoader(){ returnthis.beanClassLoader; }
最后我们在进入loadFactoryNames()方法,这个方法就是根据刚才的字节码文件和类加载器来找到候选的配置类。传递过来的字节码
publicstaticListloadFactoryNames(Class>factoryType,@NullableClassLoaderclassLoader){ ClassLoaderclassLoaderToUse=classLoader; if(classLoaderToUse==null){ classLoaderToUse=SpringFactoriesLoader.class.getClassLoader(); } //获取的EnableAutoConfiguration.class的权限定名 //org.springframework.boot.autoconfigure.EnableAutoConfiguration StringfactoryTypeName=factoryType.getName(); returnloadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName,Collections.emptyList()); }
如下图:
最后通过loadSpringFactories()来获取到所有的配置类
privatestaticMap>loadSpringFactories(ClassLoaderclassLoader){ //缓存加载的配置类 Map >result=cache.get(classLoader); if(result!=null){ returnresult; } result=newHashMap<>(); try{ //去资源目录下找 Enumeration urls=classLoader.getResources(FACTORIES_RESOURCE_LOCATION); while(urls.hasMoreElements()){ URLurl=urls.nextElement(); UrlResourceresource=newUrlResource(url); Propertiesproperties=PropertiesLoaderUtils.loadProperties(resource); for(Map.Entry,?>entry:properties.entrySet()){ StringfactoryTypeName=((String)entry.getKey()).trim(); String[]factoryImplementationNames= StringUtils.commaDelimitedListToStringArray((String)entry.getValue()); for(StringfactoryImplementationName:factoryImplementationNames){ result.computeIfAbsent(factoryTypeName,key->newArrayList<>()) .add(factoryImplementationName.trim()); } } } //Replacealllistswithunmodifiablelistscontaininguniqueelements result.replaceAll((factoryType,implementations)->implementations.stream().distinct() .collect(Collectors.collectingAndThen(Collectors.toList(),Collections::unmodifiableList))); //加载完成放到缓存中 cache.put(classLoader,result); } catch(IOExceptionex){ thrownewIllegalArgumentException("Unabletoloadfactoriesfromlocation["+ FACTORIES_RESOURCE_LOCATION+"]",ex); } //返回加载到的配置类 returnresult; }
这里我们要看下怎么从资源目录下FACTORIES_RESOURCE_LOCATION加载的。下面是加载配置文件的路径:
也就是项目启动的时候会去加载所有META-INF下的所有的spring.factories文件,我们搜一下这个这个文件,我搭建的是一个很简单的SpringBoot工程,它会去这几个jar里面找相关的配置类
但是最后自动装配的类是这个spring-boot-autoconfigure-2.4.3.RELEASE.jar
而根据EnabLeAutoConfiguration.class字节码加载的配置类就只有这118自动配置类
小结
实际上SpringBoot的自动装配原理,其实就是在项目启动的时候去加载META-INF下的spring.factories文件,好像也没有那么高大上。当然在启动的过程中还会有其他的配置项的加载,这里咱么直说了自动装配的加载过程。希望对大家可以有所启发。
以上就是SpringBoot自动装配原理详解的详细内容,更多关于SpringBoot自动装配原理的资料请关注毛票票其它相关文章!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。