本文主要讲解@SpringBootApplication注解的一个底层原理,也就是springboot启动类的那个注解,run()方法会在下一篇文章进行讲解。
1.什么是SpringBoot?
SpringBoot是一个能够快速开发应用的一个框架,它简化了基于 Spring 的应用开发,通过少量的代码就能创建一个独立的、产品级别的 Spring 应用。为 Spring 平台及第三方库提供开箱即用的设置,它的核心思想就是约定大于配置,其中的应用只需要很少的 Spring 配置,就能够直接使用。这样就大大的简化了你的开发模式。
2.SpringBoot的自动装配原理
启动类:
@SpringBootApplication
public class ApplicationTest {
public static void main(String[] args) {
SpringApplication.run(ApplicationTest.class);
}
}
点开@SpringBootApplication注解,你会看到如下代码,说明该注解是一个组合注解,虽然有多个注解,但是最主要的注解就是以下三个:
@SpringBootConfiguration(里面应用了@Configuration)
@EnableAutoConfiguration
@ComponentScan
@Target(ElementType.TYPE) // 注解的适用范围,其中TYPE用于描述类、接口(包括包注解类型)或enum声明
@Retention(RetentionPolicy.RUNTIME) // 注解的生命周期,保留到class文件中(三个生命周期)
@Documented // 表明这个注解应该被javadoc记录
@Inherited // 子类可以继承该注解
@SpringBootConfiguration // 继承了Configuration,表示当前是配置类
@EnableAutoConfiguration // 开启springboot的注解功能,springboot的四大神器之一,其借助@import的帮助
@ComponentScan(excludeFilters = { // 扫描路径设置(具体使用待确认)
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type=FilterType.CUSTOM,classes=AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
...
}
所以说你用这个三个注解来代替@SpringBootApplication注解,你的启动类也是可以启动的。
@Configuration
@EnableAutoConfiguration
@ComponentScan
public class ApplicationTest {
public static void main(String[] args) {
SpringApplication.run(ApplicationTest.class);
}
}
接下来讲解各个注解的含义。
2.1 SpringBootConfiguration注解:
@SpringBootConfiguration注解点开查看发现里面应用了@Configuration注解
@Configuration:
1.这里的@Configuration,它就是JavaConfig形式的Spring Ioc容器的配置类所使用的那个注解@Configuration,SpringBoot社区推荐使用基于JavaConfig的配置形式,所以,这里的启动类标注了@Configuration之后,它本身也是一个IoC容器的配置类。
2.配置spring容器有两种方式,一种是javaConfig形式,一种是XML配置文件形式。
javaConfig形式:
任何一个标注了@Configuration的Java类定义都是一个JavaConfig配置类
任何一个标注了@Bean的方法,其返回值将作为一个bean定义注册到Spring的IoC容器,方法名将默认成该bean定义的id。
@Configuration
public class UserConfiguration{
@Bean
public UserService userService(){
return new UserServiceImpl();
}
}
xml形式:
<bean id="userService" class="UserServiceImpl">
...
</bean>
2.2 @ComponentScan注解:
1.它对应XML配置中的元素,@ComponentScan的功能其实就是自动扫描并加载符合条件的组件(比如@Component和@Repository等)或者bean定义,最终将这些bean定义加载到IoC容器中。
2.我们可以通过basePackages等属性来细粒度的定制@ComponentScan自动扫描的范围,如果不指定,则Spring框架默认会从声明@ComponentScan所在类的 package下进行扫描。
3.所以SpringBoot的启动类最好是放在root package下,因为默认不指定basePackages。
2.3 @EnableAutoConfiguration:
该注解也是一个组合注解,主要是用到@AutoConfigurationPackage和@Import,这两个注解
- @AutoConfigurationPackage:
这个注解主要是通过@Import(AutoConfigurationPackages.Registrar.class)注解,它将Registrar类导入到容器中,而Registrar类作用是扫描主程序类同级包以及子级包,将同级包及所有子包下的 组件 放到spring容器中
以下是源码:
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
register(registry, new PackageImport(metadata).getPackageName());
}
new PackageImport(metadata).getPackageName()方法,它其实返回了当前主程序类的同级以及子级的包组件
- @Import(AutoConfigurationImportSelector.class):
该注解主要是通过导入AutoConfigurationImportSelector 类,借助该类中重写了父类的一个方法,名为selectImports( ),这个方法它先通过@EnableAutoConfiguration的类路径
org.springframework.boot.autoconfigure.EnableAutoConfiguration作为查找的Key,从classpath中搜寻所有的META-INF/spring.factories配置文件,来获取value值,值就是一组@Configuration自动配置类,然后通过反射(Java Refletion)实例化为对应 的标注了@Configuration的IoC容器配置类,然后汇总为一个并通过SpringFactoriesLoader来进行一系列的容器创建过程。
下图为 AutoConfigurationImportSelector类的继承关系:
可以从图中看出 AutoConfigurationImportSelector 继承了 DeferredImportSelector 继承了 ImportSelector
而ImportSelector有一个方法为:selectImports( )。
下图为 selectImports( )方法的源码:
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata,attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return StringUtils.toStringArray(configurations);
}
可以看到方法中第六行代码,getCandidateConfigurations(annotationMetadata,attributes);
从classpath中搜寻所有的META-INF/spring.factories配置文件。这个外部文件,有很多自动配置的类。
如下图:
该文件可以在spring-boot-autoconfigure.jar包中的MATE-INF找到
- 其实在这些配置类中都有类似@Conditional注解,像@ConditionalOnClass的含义是指定的类必须存在于类路径下,
比如:MongoDB的自动配置类
MongoDataAutoConfiguration类中声明了类路径下必须含有Mongo.class, Mongo Template.class这两个类,否则该自动配置类不会被加载。