自动装配介绍
- SpringBoot通过定义自动配置接口,通过SPI机制加载外部自定义的自动配置类实现自动装配功能.做到了让我们只需要极少的配置或简单的注解就可以使用所需要的功能,开箱即用.
- Springboot通过SPI方式实现了自动装配,它规定了一套接口规范和配置类信息加载规范 :
Springboot启动时会将启动类路径下所有有@Configuration注解的类注入容器中并且也会将所有依赖包中META-INF/spring-factories里定义的EnableAutoConfiguration这个key中所有的类加载至容器中(其他Bean也会加载通过@ComponentScan注解)
- 可通过
spring.boot.enableautoconfiguration=false
关闭自动配置,默认是开启的
Springboot的自动装配实现讲解
自动装配功能可从@SpringApplication注解分析,该注解功能在Springboot启动流程有讲解,其主要依赖的是@EnableAutoConfiguration上的@AutoConfigurationPackage和@Import({AutoConfigurationImportSelector.class})注解
注解负责功能
@AutoConfigurationPackage负责加载启动类路径下的@Configuration修饰的类
@Import({AutoConfigurationImportSelector.class})负责加载所有依赖包中META-INF/spring-factories里定义的EnableAutoConfiguration这个key中的类
加载限制
但是并不是所有的类都会被加载,扫描会被扫描进去,但是不符合该配置类的加载条件就不会被加载,Springboot启动中还会有一个条件筛选的过程,具体是下面这些注解条件.
满足加载条件的会被加载 @ConditionalOnBean:当容器里有指定 Bean 的条件下 @ConditionalOnMissingBean:当容器里没有指定 Bean 的情况下 @ConditionalOnSingleCandidate:当指定 Bean 在容器中只有一个,或者虽然有多个但是指定首选 Bean @ConditionalOnClass:当类路径下有指定类的条件下 @ConditionalOnMissingClass:当类路径下没有指定类的条件下 @ConditionalOnProperty:指定的属性是否有指定的值 @ConditionalOnResource:类路径是否有指定的值 @ConditionalOnExpression:基于 SpEL 表达式作为判断条件 @ConditionalOnJava:基于 Java 版本作为判断条件 @ConditionalOnJndi:在 JNDI 存在的条件下差在指定的位置 @ConditionalOnNotWebApplication:当前项目不是 Web 项目的条件下 @ConditionalOnWebApplication:当前项目是 Web 项 目的条件下
AutoConfigurationImportSelector
AutoConfigurationImportSelector实现了DeferredImportSelect继承的ImportSelector的selectImports()方法.然后主要是在118行的getCandidateConfigurations()方法完成spring-factories文件读取要加载的类全路径名交由容器加载
public String[] selectImports(AnnotationMetadata annotationMetadata) { // 加载前先看注解元数据配置是否进行配置加载 if (!this.isEnabled(annotationMetadata)) { return NO_IMPORTS; } else { AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); } } protected boolean isEnabled(AnnotationMetadata metadata) { return this.getClass() == AutoConfigurationImportSelector.class ? (Boolean)this.getEnvironment().getProperty("spring.boot.enableautoconfiguration", Boolean.class, true) : true; } protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { // 通过SpringFactoriesLoader.loadFactoryNames()提供的SPI功能完成配置类的全路径读取 List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader()); Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct."); return configurations; }
SPI概念介绍
SPI 全称为 Service Provider Interface,SPI技术原理是将接口实现类的全限定名配置在文件中,由服务加载器读取配置文件,并加载实现类。这样可以在运行时,动态为接口替换实现类.Java本身有实现一套SPI机制,但Springboot也是自己实现了一个自己的SPI机制让这个配置变更方便些.
JavaSPI机制(ServiceLoader.load(Interface.class))
JavaSPI规定在META-INF/services文件夹下创建接口全路径类名文件,在该文件中将该接口实现类加入(可多个,一行一个最好).然后通过ServiceLoader.load实现这些实现类加载,并通过返回的ServiceLoader获取或者调用
//定义一个动物接口以及两种动物实现类 public interface Animal { void speak(); } public class Cat implements Animal{ public Cat() { System.out.println("小猫类被加载了"); } @Override public void speak() { System.out.println("小猫喵喵"); } } public class Dog implements Animal{ public Dog() { System.out.println("小狗类被加载了"); } @Override public void speak() { System.out.println("小狗汪汪"); } } // SPI测试类 public class JavaSpiTest { /** * 学习查看JavaSPI机制 */ @Test public void speak(){ ServiceLoader<Animal> serviceLoader = ServiceLoader.load(Animal.class); System.out.println("Java SPI Running"); serviceLoader.forEach(Animal::speak); } }
SpringbootSPI机制
定义自动装配类,并将其写入MEAT-INF/spring.factories中的
org.springframework.boot.autoconfigure.EnableAutoConfiguration
下即可.
如果要做starter,则需要引入starter依赖,如下两个图做好配置后直接引入该项目依赖即可
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>2.1.7.RELEASE</version> </dependency>
参考:
从Java SPI机制实现到Dubbo SPI扩展
手写实现基于Spring的SPI及自动装配
淘宝一面:“说一下 Spring Boot 自动装配原理呗?”