摘要:所以,所谓的自动装配,实际上就是如何自动将装载到容器中来。实际上在版本中,模块驱动注解的出现,已经有了一定的自动装配的雏形,而真正能够实现这一机制,还是在版本中,条件注解的出现。,我们来看一下的自动装配是怎么一回事。
在前面的分析中,Spring Framework一直在致力于解决一个问题,就是如何让bean的管理变得更简单,如何让开发者尽可能的少关注一些基础化的bean的配置,从而实现自动装配。所以,所谓的自动装配,实际上就是如何自动将bean装载到Ioc容器中来。
实际上在spring 3.x版本中,Enable模块驱动注解的出现,已经有了一定的自动装配的雏形,而真正能够实现这一机制,还是在spirng 4.x版本中,conditional条件注解的出现。ok,我们来看一下spring boot的自动装配是怎么一回事。
org.springframework.boot spring-boot-starter-data-redis
spring: redis: host: 127.0.0.1 port: 6379
@Autowired private RedisTemplateredisTemplate;
按照下面的顺序添加starter,然后添加配置,使用RedisTemplate就可以使用了? 那大家想没想过一个问题,为什么RedisTemplate可以被直接注入?它是什么时候加入到Ioc容器的呢? 这就是自动装配。自动装配可以使得classpath下依赖的包相关的bean,被自动装载到Spring Ioc容器中,怎么做到的呢?
EnableAutoConfiguration的主要作用其实就是帮助springboot应用把所有符合条件的@Configuration配置都加载到当前SpringBoot创建并使用的IoC容器中。
再回到EnableAutoConfiguration这个注解中,我们发现它的import是这样
@Import(AutoConfigurationImportSelector.class)public @interface EnableAutoConfiguration {
但是从EnableAutoCOnfiguration上面的import注解来看,这里面并不是引入另外一个Configuration。而是一个ImportSelector。这个是什么东西呢?
Enable注解不仅仅可以像前面演示的案例一样很简单的实现多个Configuration的整合,还可以实现一些复杂的场景,比如可以根据上下文来激活不同类型的bean,@Import注解可以配置三种不同的class
实现ImportBeanDefinitionRegistrar接口进行动态注入
public class CacheService {
}
public class LoggerService {
}
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented @Inherited --允许被继承@Import({GpDefineImportSelector.class})public @interface EnableDefineService { String[] packages() default "";}
public class GpDefineImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { //获得指定注解的详细信息。我们可以根据注解中配置的属性来返回不同的class, //从而可以达到动态开启不同功能的目的 annotationMetadata.getAllAnnotationAttributes(EnableDefineService.class.getName(),true) .forEach((k,v) -> { log.info(annotationMetadata.getClassName()); log.info("k:{},v:{}",k,String.valueOf(v)); }); return new String[]{CacheService.class.getName()}; }}
@SpringBootApplication@EnableDefineService(name = "gupao",value = "gupao")public class EnableDemoTest { public static void main(String[] args) { ConfigurableApplicationContext ca=SpringApplication.run(EnableDemoTest.class,args); System.out.println(ca.getBean(CacheService.class)); System.out.println(ca.getBean(LoggerService.class)); }}
了解了selector的基本原理之后,后续再去分析AutoConfigurationImportSelector的原理就很简单了,它本质上也是对于bean的动态加载。
了解了ImportSelector和ImportBeanDefinitionRegistrar后,对于EnableAutoConfiguration的理解就容易一些了
它会通过import导入第三方提供的bean的配置类:AutoConfigurationImportSelector
@Import(AutoConfigurationImportSelector.class)
从名字来看,可以猜到它是基于ImportSelector来实现基于动态bean的加载功能。之前我们讲过Springboot @Enable*注解的工作原理ImportSelector接口selectImports返回的数组(类的全类名)都会被纳入到spring容器中。
那么可以猜想到这里的实现原理也一定是一样的,定位到AutoConfigurationImportSelector这个类中的selectImports方法
public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; }// 从配置文件(spring-autoconfigure-metadata.properties)中加载 AutoConfigurationMetadata AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader);// 获取所有候选配置类EnableAutoConfiguration 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);//使用SpringFactoriesLoader 加载classpath路径下META-INF/spring.factories中,//key= org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的value List configurations = getCandidateConfigurations(annotationMetadata, attributes);//去重 configurations = removeDuplicates(configurations);//应用exclusion属性 Set exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions);//过滤,检查候选配置类上的注解@ConditionalOnClass,如果要求的类不存在,则这个候选类会被过滤不被加载 configurations = filter(configurations, autoConfigurationMetadata); //广播事件fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationEntry(configurations, exclusions);}
本质上来说,其实EnableAutoConfiguration会帮助springboot应用把所有符合@Configuration配置都加载到当前SpringBoot创建的IoC容器,而这里面借助了Spring框架提供的一个工具类SpringFactoriesLoader的支持。以及用到了Spring提供的条件注解@Conditional,选择性的针对需要加载的bean进行条件过滤
为了给大家补一下基础,我在这里简单分析一下SpringFactoriesLoader这个工具类的使用。它其实和java中的SPI机制的原理是一样的,不过它比SPI更好的点在于不会一次性加载所有的类,而是根据key进行加载。
首先,SpringFactoriesLoader的作用是从classpath/META-INF/spring.factories文件中,根据key来加载对应的类到spring IoC容器中。接下来带大家实践一下
org.springframework spring-context 4.3.13.RELEASE
public class GuPaoCore { public String study(){ System.out.println("good good study, day day up"); return "GuPaoEdu.com"; }}@Configurationpublic class GuPaoConfig { @Bean public GuPaoCore guPaoCore(){ return new GuPaoCore(); }}
把前面的工程打包成jar,当前项目依赖该jar包
com.gupaoedu.practice Gupao-Core 1.0-SNAPSHOT
运行结果会报错,原因是GuPaoCore并没有被Spring的IoC容器所加载,也就是没有被EnableAutoConfiguration导入
@SpringBootApplicationpublic class SpringBootStudyApplication { public static void main(String[] args) throws IOException { ConfigurableApplicationContext ac=SpringApplication.run(SpringBootStudyApplication.class, args); GuPaoCore gpc=ac.getBean(GuPaoCore.class); System.out.println(gpc.study()); }}
在GuPao-Core项目resources下新建文件夹META-INF,在文件夹下面新建spring.factories文件,文件中配置,key为自定配置类EnableAutoConfiguration的全路径,value是配置类的全路径
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.gupaoedu.practice.GuPaoConfig
重新打包,重新运行SpringBootStudyApplication这个类。
可以发现,我们编写的那个类,就被加载进来了。
了解了ImportSelector和ImportBeanDefinitionRegistrar后,对于EnableAutoConfiguration的理解就容易一些了
它会通过import导入第三方提供的bean的配置类:AutoConfigurationImportSelector
@Import(AutoConfigurationImportSelector.class)
从名字来看,可以猜到它是基于ImportSelector来实现基于动态bean的加载功能。之前我们讲过Springboot @Enable*注解的工作原理ImportSelector接口selectImports返回的数组(类的全类名)都会被纳入到spring容器中。
那么可以猜想到这里的实现原理也一定是一样的,定位到AutoConfigurationImportSelector这个类中的selectImports方法
public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; }// 从配置文件(spring-autoconfigure-metadata.properties)中加载 AutoConfigurationMetadata AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader);// 获取所有候选配置类EnableAutoConfiguration 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);//使用SpringFactoriesLoader 加载classpath路径下META-INF/spring.factories中,//key= org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的value List configurations = getCandidateConfigurations(annotationMetadata, attributes);//去重 configurations = removeDuplicates(configurations);//应用exclusion属性 Set exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions);//过滤,检查候选配置类上的注解@ConditionalOnClass,如果要求的类不存在,则这个候选类会被过滤不被加载 configurations = filter(configurations, autoConfigurationMetadata); //广播事件fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationEntry(configurations, exclusions);}
本质上来说,其实EnableAutoConfiguration会帮助springboot应用把所有符合@Configuration配置都加载到当前SpringBoot创建的IoC容器,而这里面借助了Spring框架提供的一个工具类SpringFactoriesLoader的支持。以及用到了Spring提供的条件注解@Conditional,选择性的针对需要加载的bean进行条件过滤
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自
Mic带你学架构
!
如果本篇文章对您有帮助,还请帮忙点个关注和赞,您的坚持是我不断创作的动力。欢迎关注「跟着Mic学架构」公众号公众号获取更多技术干货!
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/125380.html
摘要:引入了新的环境和概要信息,是一种更揭秘与实战六消息队列篇掘金本文,讲解如何集成,实现消息队列。博客地址揭秘与实战二数据缓存篇掘金本文,讲解如何集成,实现缓存。 Spring Boot 揭秘与实战(九) 应用监控篇 - HTTP 健康监控 - 掘金Health 信息是从 ApplicationContext 中所有的 HealthIndicator 的 Bean 中收集的, Spring...
摘要:是的简称,运行环境,为的运行提供了所需的环境。分割字符串,返回分割后的字符串数组。当计算的值相同时,我们称之为冲突,的做法是用链表和红黑树存储相同的值的。迭代器取代了集合框架中的,迭代器允许调用者在迭代过程中移除元素。 Java基础1.JDK和JRE有什么区别? JDK 是java development kit的简称,java开发工具包,提供java的开发环境和运行环境。JRE 是j...
摘要:的简称,运行环境,为的运行提供了所需环境。分割字符串,返回一个分割后的字符串数组。线程安全是线程安全的,而是非线程安全的。迭代器取代了集合框架中的,迭代器允许调用者在迭代过程中移除元素。 本文分为十九个模块,分别是: Java 基础、容器、多线程、反射、对象拷贝、Java Web 、异常、网络、设计模式、Spring/Spring MVC、Spring Boot/Spring Clou...
摘要:你可以试着沿着调用栈代码一层一层的深入进去,如果你不打断点,你根本不知道接下来程序会往哪里流动。接下来再看看运行时堆栈,看看一个请求的调用栈有多深。就是如此被自动装配进的。 摘要: 神奇的SpringBoot。 原文:SpringBoot 究竟是如何跑起来的? 作者:老钱 Fundebug经授权转载,版权归原作者所有。 不得不说 SpringBoot 太复杂了,我本来只想研究一下...
阅读 3733·2023-01-11 11:02
阅读 4243·2023-01-11 11:02
阅读 3049·2023-01-11 11:02
阅读 5179·2023-01-11 11:02
阅读 4733·2023-01-11 11:02
阅读 5532·2023-01-11 11:02
阅读 5312·2023-01-11 11:02
阅读 3985·2023-01-11 11:02