spring boot 自动配置功能解剖
spring boot 自动配置功能解剖
说起spring boot和spring的区别,大家第一反应就是spring boot 少了很多配置,但不是说少了很多配置很多功能就没有了,或者比spring就少了很多功能,而是spring boot 自己约定了很多默认配置,让大家感觉不到其中的一些信息
问题来了,spring boot的自动配置怎么实现的?今天我将自己这2天学习到的东西分享一下,有不对的地方可以说出来一起讨论
首先我们先看一下下面这个注解:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({})
public @interface EnableAutoConfiguration {String EABLED_OVERRIDE_PROPERTY = spring.;Class<?>[] exclude() default {};String[] excludeame() default {};
}
看到@import那个注解了吗?这个注解就是会把第三方jar的类加载到当前spring容器,接下来我们看一下import的这个类,源码如下
@Deprecated
public class EnableAutoConfigurationImportSelector extends AutoConfigurationImportSelector {public EnableAutoConfigurationImportSelector() {}protected boolean isEnabled(AnnotationMetadata metadata) {return this.getClass().equals() ? ((Boolean)this.getEnvironment().getProperty(spring., , true)).booleanValue() : true;}
}
下面是父类AutoConfigurationImportSelector 中关键2个方法的源码
public String[] selectImports(AnnotationMetadata annotationMetadata) {if (!this.isEnabled(annotationMetadata)) { //判断 enableautoconfiguration注解有没有开启,默认开启return O_IMPORTS;} else {try {
//第一部分 :获取 META-IF/spring-autoconfigure-metadata.properties 的配置数据AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
//第二部分 :获取 META-IF/spring.factoies 的类相关数据List<String> configurati = this.getCandidateConfigurati(annotationMetadata, attributes);
//去重configurati = this.removeDuplicates(configurati);、
//排序configurati = this.sort(configurati, autoConfigurationMetadata);
//第三部分:去除一些多余的类,根据EnableAutoConfiguration 注解的一个exclusi属性Set<String> exclusi = this.getExclusi(annotationMetadata, attributes);(configurati, exclusi);configurati.removeAll(exclusi);
//第四部:根据OnClassCondition注解过滤调一些条件没有满足的configurati = this.filter(configurati, autoConfigurationMetadata);
//第五部:广播AutoConfigurationImportEvents事件(最下面有详解)this.fireAutoConfigurationImportEvents(configurati, exclusi);return (String[])(new String[configurati.size()]);} catch (IOException var6) {throw new IllegalStateException(var6);}}}protected boolean isEnabled(AnnotationMetadata metadata) {return true;}
首先程序是先调用EnableAutoConfigurationImportSelector 的selectImport方法,也就是父类的这个方法,下面我们先研究一下代码
大家看上面源码的第一部分,获取spring-autoconfigure-metadata.properties的代码,深究进去的代码如下
public static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {return loadMetadata(classLoader, META-IF/spring-autoconfigure-metadata.properties);}static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) {try {Enumeration<URL> urls = classLoader != null ? classLoader.getResources(path) : ClassLoader.getSystemResources(path);Properties properties = new Properties();while(urls.hasMoreElements()) {properties.putAll(PropertiesLoaderUtils.loadProperties(new UrlResource((URL)())));}return loadMetadata(properties);} catch (IOException var4) {throw new IllegalArgumentException(Unable to load @ConditionalOnClass location [ path ], var4);}}
ClassLoader.getResource()方法到具有给定名称的资源。资源是一些数据(图像,音频,文本等),返回URL对象读取资源。
该方法就是获取该目录下的配置数据
第二部分:道理跟第一部分一样获取相关类的数据
第三步就是去除一些不用的class,这是具体过滤的代码
private List<String> filter(List<String> configurati, AutoConfigurationMetadata autoConfigurationMetadata) {long startTime = ();String[] candidates = (String[])(new String[configurati.size()]);boolean[] skip = new boolean[candidates.length];boolean skipped = false;Iterator var8 = this.getAutoConfigurationImportFilters().iterator();while(var8.hat()) {AutoConfigurationImportFilter filter = (AutoConfigurationImportFilter)();this.invokeAwareMethods(filter);boolean[] match = (candidates, autoConfigurationMetadata);for(int i = 0; i < match.length; i) {if (!match[i]) {skip[i] = true;skipped = true;}}}if (!skipped) {return configurati;} else {List<String> result = new ArrayList(candidates.length);int numberFiltered;for(numberFiltered = 0; numberFiltered < candidates.length; numberFiltered) {if (!skip[numberFiltered]) {result.add(candidates[numberFiltered]);}}if (logger.isTraceEnabled()) {numberFiltered = configurati.size() - result.size();(Filtered numberFiltered auto configuration class in TimeUnit.(() - startTime) ms);}return new ArrayList(result);}}
AutoConfigurationImportFilter 是一个接口,OnClassCondition才是它的实现类,主要功能就是第二部加载的类中不是所有都是要加载的,spring boot 提供了很多条件注解,具体如下
@ConditionalOnClass : classpath中存在该类时起效
@ConditionalOnMissingClass : classpath中不存在该类时起效
@ConditionalOnBean : DI容器中存在该类型Bean时起效
@ConditionalOnMissingBean : DI容器中不存在该类型Bean时起效
@ConditionalOnSingleCandidate : DI容器中该类型Bean只有一个或@Primary的只有一个时起效
@ConditionalOnExpression : SpEL表达式结果为true时
@ConditionalOnProperty : 参数设置或者值一致时起效
@ConditionalOnResource : 指定的文件存在时起效
@ConditionalOnJndi : 指定的JDI存在时起效
@ConditionalOnJava : 指定的Java版本存在时起效
@ConditionalOnWebApplication : Web应用环境下起效
@ConditionalOnotWebApplication : 非Web应用环境下起效
以上注解都是元注解@Conditional
演变而来的,过滤调一些没有满足条件的class
第五步:广播事件
private void fireAutoConfigurationImportEvents(List<String> configurati, Set<String> exclusi) {// 通过SpringFactoriesLoader.loadFactories()方法获取所有实现 // AutoConfigurationImportListener的实例化对象List<AutoConfigurationImportListener> listeners = this.getAutoConfigurationImportListeners();if (!listeners.isEmpty()) {
// 生成一个Even事件,给listener发送AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurati, exclusi);Iterator var5 = listeners.iterator();while(var5.hat()) {AutoConfigurationImportListener listener = (AutoConfigurationImportListener)();// 如果实现了或者继承了一些Aware,则设置相应的值。这个大家可以去百度Awarethis.invokeAwareMethods(listener);
// 给AutoConfigurationImportListener发送事件(event);}}}
关键的一个问题来了,回到源头,项目启动之后什么时候会去执行AutoConfigurationImportSelector的selectImports方法?
查了一下资料,其实spring boot是从我们SpringApplication.run方法开始最终执行最终执行到selectImports方法,然后将selectImports方法得到的数据注入到容器里面
springApplication.run----->refreshContext()----->AbstractApplicationContext.refresh---->PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors----->ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry----------->ConfigurationClassPostProcessor.processConfigBeanDefiniti------>ConfigurationClassParser.parse
最终到如下代码
private void processDeferredImportSelectors() {List<ConfigurationClassParser.DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;this.deferredImportSelectors = null;Collecti.sort(deferredImports, DEFERRED_IMPORT_COMPARATOR);Iterator var2 = deferredImports.iterator();while(var2.hat()) {ConfigurationClassParser.DeferredImportSelectorHolder deferredImport = (ConfigurationClassParser.DeferredImportSelectorHolder)();ConfigurationClass configClass = deferredImport.getConfigurationClass();try {
//执行导入自动化数据配置String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata());
//处理这些数据,注入容器this.processImports(configClass, this.asSourceClass(configClass), this.asSourceClasses(imports), false);} catch (BeanDefinitionStoreException var6) {throw var6;} catch (Throwable var7) {throw new BeanDefinitionStoreException(Failed to process import candidates for configuration class [ configClass.getMetadata().getClassame() ], var7);}}}
好了,这个如何注入的问题是解决了,spring boot的自动化注入大致讲解完了,
现在又有一个问题了,如果自己要做一个类似于第三方的jar,让当前的容器加载我这个第三方的bean怎么做呢?其实也是非常简单。
第一步:肯定是新建一个spring boot项目A
第二步:定义类
public class people{
}
@Configuration
public class MyConfig {@Beanpublic People people (){return new People();}
}
第三步:将A的jar放到B中
第四部:在B中写如下测试代码,并启动
@EnableAutoConfiguration
@ComponentScan
public class Application {public static void main(String[] args) {ConfigurableApplicationContext context =SpringApplication.run(,args);People people = context.getBean(People .class);println(people );}
}
不过这样启动会报错,不到这个类,那是因为你的第五步没有做
第五步:在core-bean项目resource下新建文件夹META-IF,在文件夹下面新建spring.factories文件,文件中配置,key为自定配置类EnableAutoConfiguration的全路径,value是配置类的全路径(就上面分析spring boot自动配置的那个spring.factories文件一样的道理)
这样之后就可以实现了
到此为止大部分该讲的也都讲完了,如果有什么不对的地方,可以提出来讨论
#感谢您对电脑配置推荐网 - 最新i3 i5 i7组装电脑配置单推荐报价格的认可,转载请说明来源于"电脑配置推荐网 - 最新i3 i5 i7组装电脑配置单推荐报价格
上一篇:最强翻译?Facebook AI可以直接翻译100种语言
下一篇:Cloud任务
推荐阅读
留言与评论(共有 16 条评论) |
本站网友 燕窝的食用方法 | 5分钟前 发表 |
args);People people = context.getBean(People .class);println(people );} } 不过这样启动会报错,不到这个类,那是因为你的第五步没有做 第五步 | |
本站网友 马坡二手房 | 4分钟前 发表 |
var7);}}} 好了,这个如何注入的问题是解决了,spring boot的自动化注入大致讲解完了, 现在又有一个问题了,如果自己要做一个类似于第三方的jar,让当前的容器加载我这个第三方的bean怎么做呢?其实也是非常简单 | |
本站网友 周志文 | 4分钟前 发表 |
spring boot 自动配置功能解剖 说起spring boot和spring的区别,大家第一反应就是spring boot 少了很多配置,但不是说少了很多配置很多功能就没有了,或者比spring就少了很多功能,而是spring boot 自己约定了很多默认配置,让大家感觉不到其中的一些信息 问题来了,spring boot的自动配置怎么实现的?今天我将自己这2天学习到的东西分享一下,有不对的地方可以说出来一起讨论 首先我们先看一下下面这个注解: @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUTIME) @Documented @Inherited @AutoConfigurationPackage @Import({}) public @interface EnableAutoConfiguration {String EABLED_OVERRIDE_PROPERTY = spring.;Class<?>[] exclude() default {};String[] excludeame() default {}; } 看到@import那个注解了吗?这个注解就是会把第三方jar的类加载到当前spring容器,接下来我们看一下import的这个类,源码如下 @Deprecated public class EnableAutoConfigurationImportSelector extends AutoConfigurationImportSelector {public EnableAutoConfigurationImportSelector() {}protected boolean isEnabled(AnnotationMetadata metadata) {return this.getClass().equals() ? ((Boolean)this.getEnvironment().getProperty(spring. | |
本站网友 tftpd32 | 7分钟前 发表 |
var7);}}} 好了,这个如何注入的问题是解决了,spring boot的自动化注入大致讲解完了, 现在又有一个问题了,如果自己要做一个类似于第三方的jar,让当前的容器加载我这个第三方的bean怎么做呢?其实也是非常简单 | |
本站网友 华普报价 | 2分钟前 发表 |
args);People people = context.getBean(People .class);println(people );} } 不过这样启动会报错,不到这个类,那是因为你的第五步没有做 第五步 | |
本站网友 文峰广场 | 25分钟前 发表 |
autoConfigurationMetadata); //第三部分 | |
本站网友 沙发十大品牌排行榜 | 24分钟前 发表 |
attributes);(configurati | |
本站网友 fedex快递电话 | 30分钟前 发表 |
资源是一些数据(图像,音频,文本等) | |
本站网友 游戏原画线稿 | 16分钟前 发表 |
返回URL对象读取资源 | |
本站网友 农工商红利卡 | 3分钟前 发表 |
true)).booleanValue() | |
本站网友 彩虹心水论坛 | 15分钟前 发表 |
根据OnClassCondition注解过滤调一些条件没有满足的configurati = this.filter(configurati | |
本站网友 一点点的蓝 | 13分钟前 发表 |
this.asSourceClasses(imports) | |
本站网友 秀山房屋出租 | 23分钟前 发表 |
第一步:肯定是新建一个spring boot项目A 第二步:定义类 public class people{ } @Configuration public class MyConfig {@Beanpublic People people (){return new People();} } 第三步:将A的jar放到B中 第四部:在B中写如下测试代码,并启动 @EnableAutoConfiguration @ComponentScan public class Application {public static void main(String[] args) {ConfigurableApplicationContext context =SpringApplication.run( | |
本站网友 ze | 2分钟前 发表 |
exclusi);Iterator var5 = listeners.iterator();while(var5.hat()) {AutoConfigurationImportListener listener = (AutoConfigurationImportListener)();// 如果实现了或者继承了一些Aware,则设置相应的值 | |
本站网友 百度可穿戴设备 | 18分钟前 发表 |
configurati |