转载自:CSDN你就像甜甜的益达
@SpringBootApplication
这个注解是springboot的启动注解,配置了这个注解的方法就是springboot项目的入口; 一般springboot项目的启动类:
@SpringBootApplication
public class SpringBootPlusApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringBootPlusApplication.class, args);
// 打印项目信息
PrintApplicationInfo.print(context);
}
}
我们看看SpringBootApplication注解:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
@ConfigurationPropertiesScan
public @interface SpringBootApplication {
}
@ComponentScan表示包扫描,FilterType.CUSTOM表示自定义扫描;可以看一下TypeExcludeFilter这类,实现TypeFilterl类,重写match方法进行判断是否加入spring容器中;这里不做过多详解,主要讲@SpringBootConfiguration、@EnableAutoConfiguration这两个注解;
@SpringBootConfiguration
源码:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration(proxyBeanMethods = false)
public @interface SpringBootConfiguration {
@AliasFor(
annotation = Configuration.class
)
boolean proxyBeanMethods() default true;
}
这个注解就一个@Configuration注解,就相当于把当前标注了@SpringBootConfiguration注解的类标记为配置类; @Configuration类里面有@Component注解…
@EnableAutoConfiguration
EnableAutoConfiguration这个注解从名字上面来看,这个注解应该是跟自动装配相关的注解了; 先看看EnableAutoConfiguration注解的源码:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}
前四个是元注解,就不提了,后面两个@AutoConfigurationPackage和导入了入AutoConfigurationImportSelector这个类;
@AutoConfigurationPackage
这个类从名字上面看应该是自配配置包的: 先看看源码:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
这里主要导入了AutoConfigurationPackages.Registrar.class类:
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new PackageImport(metadata).getPackageName());
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImport(metadata));
}
}
这个类获取到了使用这个注解的包路径,然后扫描注册对应包路径下所有组件,注册到spring容器中;
@Import(AutoConfigurationImportSelector.class)
先看AutoConfigurationImportSelector实现的接口:
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
}
实现的aware接口,就是在类中获取spring容器的组件,这里获取的是:beanFactory,environment,beanClassLoader,resourceLoader; 最主要的接口是DeferredImportSelector,这个接口继承了ImportSelector接口,
- ImportSelector这个接口在调用地方是在,spring容器处理Import注解的时候,如果@import注解导入的类实现了ImportSelector接口,则会调用selectImports方法获取Import的类:
基于被引入的Configuration类的AnnotationMetadata信息选择并返回需要引入的类名列表; ImportSelector的实现和@import通常在处理方式上是一致的,可以在所以的配置类,就是@Configuration标记的类处理完成后在进行筛选;
- DeferredImportSelector接口实现了ImportSelector接口,表明他们作用差不多,DeferredImportSelector接口在所有的@Configuration处理完成之后才会调用,在需要筛选的引入类型具备@Conditional(条件注入指定类的时候)非常有用;最后会执行selectImports方法;
AutoConfigurationImportSelector流程分析
DeferredImportSelector会通过getImportGroup返回执行的类,然后执行内部类AutoConfigurationGroup方法的process方法,然后执行getAutoConfigurationEntry方法,我们就从AutoConfigurationImportSelector的process开始: 通过process的getAutoConfigurationEntry来获取自动装配的Entry对象:
然后进到getCandidateConfigurations方法,getCandidateConfigurations就加载出了spring-boot-autuator-atoconfig包下META-INF\spring,factories文件,第一次的时候cache里面是空的,就会通过classloader加载META-INF/spring.factories文件,通过配置的key/vlaue保存到缓存中.给后面使用:
然后spring通过加载这些类,将这些配置类加入到spring容器中,可以看ConfigurationClassParser的processGroupImports方法,通过执行getImports方法,点进去就看见是执行DeferredImportSelector的selectImports方法;
这时候通过加载meta-inf文件夹下的spring.factories文件配置的配置类路径,进行加载对应的类,这时候自动装配就基本完成了,剩下的就是这些配置类可以通过@ConditionalOnBean等一系列注解来判断加不加载当前类: 举个例子:HttpEncodingAutoConfiguration类
可通过是否配置了spring.http.encoding来进行判断是否加载当前配置类…
// 20210519更新
@EnableAutoConfiguration作用
- 相当于两个注解@AutoConfigurationPackage、@Import({AutoConfigurationImportSelector.class})
- @AutoConfigurationPackage作用扫描使用了该注解的类所在包路径及子路径中@Component、@Controller、@Service等类,生成bean放入spring容器管理。
- @Import({AutoConfigurationImportSelector.class}) 作用是把AutoConfigurationImportSelector类引入容器管理,然后扫描所有添加的jar包中META-INFO/spring.factories下配置的enableautoconfigure,通过SPI将所有配置类初始化,创建bean
什么是SPI
通过 SpringFactoriesLoader 来读取配置文件 spring.factories 中的配置文件的这种方式是一种 SPI 的思想。那么什么是 SPI 呢?
SPI,Service Provider Interface。即:接口服务的提供者。就是说我们应该面向接口(抽象)编程,而不是面向具体的实现来编程,这样一旦我们需要切换到当前接口的其他实现就无需修改代码。
在 Java 中,数据库驱动就使用到了 SPI 技术,每次我们只需要引入数据库驱动就能被加载的原因就是因为使用了 SPI 技术。
打开 DriverManager 类,其初始化驱动的代码如下:
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Iterator driversIterator = loadedDrivers.iterator();
try {
while(driversIterator.hasNext()) {
driversIterator.next();
}
} catch (Throwable var4) {
}
return null;
}
});
内部LazyClassPathLookupIterator类会去扫描"META-INF/services/"下的文件就会发现,他会以接口名为key、实现类写在文件中,同理Spring Boot也是使用这样方式实现自动配置,在对应(我们需要使用的工具)jar包里,如果需要将配置引入spring容器,那么就可以这样配置
- 在 resources 目录下新建一个文件 META-INF/spring.factories 文件,文件内新增一个如下配置:
添加入org.springframework.boot.autoconfigure.EnableAutoConfiguration = ?具体需要自动配置的类来完成
我们也可以去看其他第三方jar包很多都有这样的配置文件,供自动配
附:下面是一些condition条件的具体应用:
@Conditional扩展注解 | 作用(判断是否满足当前指定条件) |
---|---|
@ConditionalOnJava | 系统的java版本是否符合要求 |
@ConditionalOnBean | 容器中存在指定Bean |
@ConditionalOnMissingBean | 容器中不存在指定Bean |
@ConditionalOnExpression | 满足SpEL表达式指定 |
@ConditionalOnClass | 系统中有指定的类 |
@ConditionalOnMissingClass | 系统中没有指定的类 |
@ConditionalOnSingleCandidate | 容器中只有一个指定的Bean,或者这个Bean是首选Bean |
@ConditionalOnProperty | 系统中指定的属性是否有指定的值 |
@ConditionalOnResource | 类路径下是否存在指定资源文件 |
@ConditionalOnWebApplication | 当前是web环境 |
@ConditionalOnNotWebApplication | 当前不是web环境 |
@ConditionalOnJndi | JNDI存在指定项 |
评论