原创 SpringBoot starter自动配置原理

发布时间:2021-06-24 11:21:42 浏览 52 来源:猿笔记 作者:HansRt

    本文首先介绍了如何自定义一个springbootstarter:并在项目中使用:然后对springboot自动配置原理进行了简要的说明,1这个注解放在启动类上。而且包含了多个注解。2事实上只是`@Configuration`的另一种声明,标识是SpringBoot的配置类。同时该类上面也加了`@Configuration`注解。启动自动配置,先看源码。如果某些自动配置类不需要用到,接下来看注解`@AutoConfigurationPackage`”表示了需要注册的注解类所在的包路径,这里使用了另外一个注解`@Import`,先看看这个注解有什么用。


    #主题列表:juejin,github,smartblue,cyanosis,channing-cyan,fancy,hydrogen,condensed-night-purple,greenwillow,v-green,vue-pro,healer-readable,mk-cute,jzman,geek-black,awesome-green,qklhk-chocolate

    #投稿主题:

    theme:cyanosis

    highlight:

    本文首先介绍了如何定制一个回弹启动器并在项目中使用它。然后,简要说明了回弹的自动配置原理。

    本文基于SpringBoot2.2.4.RELEASE版本

    ##自定义一个starter的步骤

    请参考:

    有时间再收拾

    ##starter的自动配置原理

    先抛出结论:

    通过`@EnableAutoConfiguration`上导入的`AutoConfigurationImportSelector`,会其中selectImports()方法通过`SpringFactoriesLoader`扫描所有具有`META-INF/spring.factories`的jar包。这个`spring.factories`文件是一组一组的key=value的形式,其中一个key是`EnableAutoConfiguration`类的全类名,而它的value是一个`xxxxAutoConfiguration`的类名的列表,这些类名以逗号分隔。

    在SpringApplication.run(...)的内部就会执行selectImports()方法,找到所有JavaConfig自动配置类的全限定名对应的class,然后将所有自动配置类加载到Spring容器中。

    在每一个starter中都有一个xxxxAutoConfiguration的类,该类会被加载到容器,该类上有一个注解`@EnableConfigurationProperties`会将这个starter的配置类也同样加载到容器中,这样就能读取application.yml中的配置项,并赋值到这个Properties类中。

    SpringBoot启动的时候会通过@EnableAutoConfiguration注解找到META-INF/spring.factories配置文件中的所有自动配置类,并对其进行加载,而这些自动配置类都是以AutoConfiguration结尾来命名的,它实际上就是一个JavaConfig形式的Spring容器配置类,它能通过以Properties结尾命名的类中取得在全局配置文件中配置的属性如:server.port,而XxxxProperties类是通过@ConfigurationProperties注解与全局配置文件中对应的属性进行绑定的。

    ###注解`@SpringBootApplication`

    java@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan(excludeFilters={@Filter(type=FilterType.CUSTOM,classes=TypeExcludeFilter.class),\t\t@Filter(type=FilterType.CUSTOM,classes=AutoConfigurationExcludeFilter.class)})public@interfaceSpringBootApplication{\t...}

    这个注释在启动类上被释放,并且包含几个注释。让我们一个一个来看。

    ###注解`@SpringBootConfiguration`

    java@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Configurationpublic@interfaceSpringBootConfiguration{\t...}其实只是`@Configuration '的另一种声明,身份是SpringBoot的配置类。同时,在这个类中添加了`@Configuration `批注。

    java@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Componentpublic@interfaceConfiguration{\t...}

    ###注解`@EnableAutoConfiguration`

    按照字面意思,启动自动配置,这个名字应该是重点,先看源码。

    java@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@AutoConfigurationPackage@Import(AutoConfigurationImportSelector.class)public@interfaceEnableAutoConfiguration{\tStringENABLED_OVERRIDE_PROPERTY="spring.boot.enableautoconfiguration";\t/**\t*Excludespecificauto-configurationclassessuchthattheywillneverbeapplied.\t*/\tClass<>[]exclude()default{};\t/**\t*Excludespecificauto-configurationclassnamessuchthattheywillneverbe\t*applied.\t*/\tString[]excludeName()default{};}

    根据属性,如果不需要一些自动配置类,可以在这里手动排除。请看注释`@AutoConfigurationPackage `下一步。

    java@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@Import(AutoConfigurationPackages.Registrar.class)public@interfaceAutoConfigurationPackage{}这个类的描述如下,表示要注册的标注类所在的包路径。请暂时理解这一点,继续往下看。此处使用了另一个注释“@Import”。

    Indicatesthatthepackagecontainingtheannotatedclassshouldberegisteredwith

    让我们来看看这个注释的用法。根据文档描述,它的作用相当于spring中的`\ u 003 import/> '标记,表示要导入的组件类,它的作用是将一个组件导入到容器中。以上意味着您需要加载` autoconfigurationpackages。注册器. class `放入容器,注册器被翻译为Registrar,并临时翻译为“自动配置包名称注册器”。

    java@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic@interfaceImport{\tClass<>[]value();}从启动时的断点可以看出,启动类所在的包实际上是注册为组件所在的根包名,也就是说,只有spring-boot所在的主类所在的包下的组件(用@ SpringBootApplication标记的类)才能加载到容器中。

    ###注解`AutoConfigurationImportSelector`

    里面有个process方法

    java@Overridepublicvoidprocess(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()){this.entries.putIfAbsent(importClassName,annotationMetadata);}}

    这里有个常量:publicstaticfinalStringFACTORIES_RESOURCE_LOCATION="META-INF/spring.factories";

    javaprivatestaticMap>loadSpringFactories(@NullableClassLoaderclassLoader){\t\tMultiValueMapresult=cache.get(classLoader);\t\tif(result!=null){\t\t\treturnresult;\t\t}\t\ttry{\t\t\tEnumerationurls=(classLoader!=null\t\t\t\t\tclassLoader.getResources(FACTORIES_RESOURCE_LOCATION):\t\t\t\t\tClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));\t\t\tresult=newLinkedMultiValueMap<>();\t\t\twhile(urls.hasMoreElements()){\t\t\t\tURLurl=urls.nextElement();\t\t\t\tUrlResourceresource=newUrlResource(url);\t\t\t\tPropertiesproperties=PropertiesLoaderUtils.loadProperties(resource);\t\t\t\tfor(Map.Entry<,>entry:properties.entrySet()){\t\t\t\t\tStringfactoryTypeName=((String)entry.getKey()).trim();\t\t\t\t\tfor(StringfactoryImplementationName:StringUtils.commaDelimitedListToStringArray((String)entry.getValue())){\t\t\t\t\t\tresult.add(factoryTypeName,factoryImplementationName.trim());\t\t\t\t\t}\t\t\t\t}\t\t\t}\t\t\tcache.put(classLoader,result);\t\t\treturnresult;\t\t}\t\tcatch(IOExceptionex){\t\t\tthrownewIllegalArgumentException("Unabletoloadfactoriesfromlocation["+\t\t\t\t\tFACTORIES_RESOURCE_LOCATION+"]",ex);\t\t}\t}

    看到这里,大概就明白了,每个starter在META-INF/spring.factories定义好需要加载的类,然后springboot会自动读取里面的内容,将对应的AutoConfiguration配置类加载到容器中,从而实现了自动配置。

    ##参考

    -[SpringBoot自动配置原理](

    -[SpringBoot自定义starter](

作者信息

HansRt [等级:3] 软件开发
发布了 1 篇专栏 · 获得点赞 1 · 获得阅读 48

相关推荐 更多