浅谈springboot自动配置原理
从main函数说起
一切的开始要从springbootapplication注解说起。
@springbootapplication public class mybootapplication { public static void main(string[] args) { springapplication.run(mybootapplication.class); } } @springbootconfiguration @enableautoconfiguration @componentscan public @interface springbootapplication { }
其中最重要的就是enableautoconfiguration注解,开启自动配置。
@target({elementtype.type}) @retention(retentionpolicy.runtime) @documented @inherited @autoconfigurationpackage @import({autoconfigurationimportselector.class}) public @interface enableautoconfiguration { string enabled_override_property = "spring.boot.enableautoconfiguration"; class<?>[] exclude() default {}; string[] excludename() default {}; }
通过import注解导入autoconfigurationimportselector。在这个类中加载/meta-inf/spring.factories文件的信息,然后筛选出以enableautoconfiguration为key的数据,加载到ioc容器中,实现自动配置功能。
@target(elementtype.type) @retention(retentionpolicy.runtime) @documented @inherited @import(autoconfigurationpackages.registrar.class) public @interface autoconfigurationpackage { }
从表面看就是自动配置包,主要使用了import注解,导入了registrar类。这里registrar类的registerbeandefinitions方法导包,也就是导入当前main函数所在路径的包地址,我这里是com.zhangfei。
怎么自动装配其他n个类
import({autoconfigurationimportselector.class})该注解给当前配置类导入另外n个自动配置类。
这里既然导入n个自动配置类,那么都导入哪些类呢?
//autoconfigurationimportselector实现deferredimportselector接口,而deferredimportselector接口又继承了importselector public interface importselector { string[] selectimports(annotationmetadata var1); }
autoconfigurationimportselector通过实现接口importselector的selectimports方法返回需要导入的组件,selectimports方法返回一个全类名字符串数组。
主角上场
//autoconfigurationimportselector.java @override public string[] selectimports(annotationmetadata annotationmetadata) { if (!isenabled(annotationmetadata)) { return no_imports; } autoconfigurationmetadata autoconfigurationmetadata = autoconfigurationmetadataloader.loadmetadata(this.beanclassloader); autoconfigurationentry autoconfigurationentry = getautoconfigurationentry(autoconfigurationmetadata,annotationmetadata); return stringutils.tostringarray(autoconfigurationentry.getconfigurations()); } protected autoconfigurationentry getautoconfigurationentry(autoconfigurationmetadata autoconfigurationmetadata,annotationmetadata annotationmetadata) { if (!isenabled(annotationmetadata)) { return empty_entry; } annotationattributes attributes = getattributes(annotationmetadata); list<string> configurations = getcandidateconfigurations(annotationmetadata, attributes); configurations = removeduplicates(configurations); set<string> exclusions = getexclusions(annotationmetadata, attributes); checkexcludedclasses(configurations, exclusions); configurations.removeall(exclusions); configurations = filter(configurations, autoconfigurationmetadata); fireautoconfigurationimportevents(configurations, exclusions); return new autoconfigurationentry(configurations, exclusions); } protected list<string> getcandidateconfigurations(annotationmetadata metadata, annotationattributes attributes) { list<string> configurations = springfactoriesloader.loadfactorynames(getspringfactoriesloaderfactoryclass(),getbeanclassloader()); return configurations; }
这里又开始调用springfactoriesloader.loadfactorynames。
springfactoriesloader.loadfactorynames方法中关键的三步:
(1)从当前项目的类路径中获取所有 meta-inf/spring.factories 这个文件下的信息.
(2)将上面获取到的信息封装成一个 map 返回,enableautoconfiguration为key。
(3)从返回的map中通过刚才传入的 enableautoconfiguration.class参数,获取该 key 下的所有值。
public static list<string> loadfactorynames(class<?> factoryclass, @nullable classloader classloader) { string factoryclassname = factoryclass.getname(); return (list)loadspringfactories(classloader).getordefault(factoryclassname, collections.emptylist()); } private static map<string, list<string>> loadspringfactories(@nullable classloader classloader) { multivaluemap<string, string> result = (multivaluemap)cache.get(classloader); if (result != null) { return result; } else { try { enumeration<url> urls = classloader != null ? classloader.getresources("meta-inf/spring.factories") : classloader.getsystemresources("meta-inf/spring.factories"); linkedmultivaluemap result = new linkedmultivaluemap(); while(urls.hasmoreelements()) { url url = (url)urls.nextelement(); urlresource resource = new urlresource(url); properties properties = propertiesloaderutils.loadproperties(resource); iterator var6 = properties.entryset().iterator(); while(var6.hasnext()) { entry<?, ?> entry = (entry)var6.next(); string factoryclassname = ((string)entry.getkey()).trim(); string[] var9 = stringutils.commadelimitedlisttostringarray((string)entry.getvalue()); int var10 = var9.length; for(int var11 = 0; var11 < var10; ++var11) { string factoryname = var9[var11]; result.add(factoryclassname, factoryname.trim()); } } } cache.put(classloader, result); return result; } catch (ioexception var13) { throw new illegalargumentexception("unable to load factories from location [meta-inf/spring.factories]", var13); } } }
自动配置都有哪些内容呢?
# auto configure org.springframework.boot.autoconfigure.enableautoconfiguration=\ org.springframework.boot.autoconfigure.admin.springapplicationadminjmxautoconfiguration,\ org.springframework.boot.autoconfigure.aop.aopautoconfiguration,\ org.springframework.boot.autoconfigure.amqp.rabbitautoconfiguration,\ org.springframework.boot.autoconfigure.batch.batchautoconfiguration,\ org.springframework.boot.autoconfigure.cache.cacheautoconfiguration,\ ...其他省略
xxxautoconfiguration和xxproperties
在spring.factories文件中看到的都是自动配置类,那么自动配置用到的属性值在那里呢?我们拿出redis为例
@configuration @conditionalonclass(redisoperations.class) //判断当前项目有没有这个类redisoperations.class @enableconfigurationproperties(redisproperties.class) //启用配置属性,这里看到了熟悉的xxxproperties @import({ lettuceconnectionconfiguration.class, jedisconnectionconfiguration.class }) //导入这两个类 public class redisautoconfiguration { @bean @conditionalonmissingbean(name = "redistemplate") public redistemplate<object, object> redistemplate(redisconnectionfactory redisconnectionfactory) throws unknownhostexception { redistemplate<object, object> template = new redistemplate<>(); template.setconnectionfactory(redisconnectionfactory); return template; } @bean @conditionalonmissingbean public stringredistemplate stringredistemplate(redisconnectionfactory redisconnectionfactory) throws unknownhostexception { stringredistemplate template = new stringredistemplate(); template.setconnectionfactory(redisconnectionfactory); return template; } } //这里则保存redis初始化时的属性 @configurationproperties(prefix = "spring.redis") public class redisproperties { private int database = 0; private string url; private string host = "localhost"; private string password; private int port = 6379; private boolean ssl; }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
推荐阅读
-
浅谈springboot自动配置原理
-
浅谈SpringBoot内嵌Tomcat的实现原理解析
-
springboot @ConfigurationProperties @EnableConfigurationProperties 自动配置
-
SpringBoot中SpringMVC的自动配置以及扩展
-
【springboot】之自动配置原理
-
springboot介绍项目(springboot自动配置原理)
-
springboot介绍项目(springboot自动配置原理)
-
springboot2.0.3源码篇 - 自动配置的实现,发现也不是那么复杂
-
springboot介绍项目(springboot自动配置原理)
-
如何通过一张图搞懂springBoot自动注入原理