Springboot 启动原理探索-@SpringBootApplication注解解析
参考网上Springboot相关启动原理的部分讲解,总结了一下,写出来和大家分享。
1.@SpringBootApplication注解
对于Springboot项目,它有一个启动类。一般如下(此处以Springboot演示案例为例):
@SpringBootApplication
public class DemoSpringbootApplication{
public static void main(String[] args) {
SpringApplication.run(DemoSpringbootApplication.class, args);
}
}
从上述代码里看出,启动类引用了一个注解@SpringBootApplication,而
@SpringBootApplication实际上是一个复合注解,它的类定义如下所示:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration // 继承了Configuration,表示当前是注解类
@EnableAutoConfiguration // 开启自动配置
@ComponentScan(excludeFilters = { // 扫描组件
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
...
}
实际上@SpringBootApplication内的核心注解有三个:@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan
1.1 @SpringBootConfiguration
@SpringBootConfiguration实际上是引入的@Configuration,而@Configuration是Spring的注解类,用于定义配置类,并会将当前类内声明的一个或多个以@Bean注解标记的方法的实例纳入到srping容器中,并且实例名就是方法名,等同于spring的XML配置文件。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
@Configuration与XML配置的简单对比如下:
@Configuration
public class SpringDemo{
}
等价于XML形式的<beans></beans>
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
</beans>
package com.spring.demo
public class Service {
}
----------------------------------------------------------------------------------------------
package com.spring.demo
@Configuration
public class SpringDemo{
@Bean
public Service service(){
return new Service();
}
}
等价于
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="service" class="com.spring.demo.Service"/>
</beans>
1.2 @EnableAutoConfiguration
@EnableAutoConfiguration是借助@Import的帮助,将所有符合自动配置条件的bean定义加载到IoC容器,它也是复合注解,定义如下所示:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage //自动配置包
@Import({AutoConfigurationImportSelector.class}) //借助AutoConfigurationImportSelector自动配置类
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
@EnableAutoConfiguration里重要的注解分别是@AutoConfigurationPackage和@Import(AutoConfigurationImportSelector.class),看名知意,@AutoConfigurationPackage:自动配置包,AutoConfigurationImportSelector:自动配置组件的导入。
1.2.1@AutoConfigurationPackage
@AutoConfigurationPackage具体定义如下所示:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
}
由@AutoConfigurationPackage定义可以看出,实际是借助Import导入了Registrar,而Registrar中主要调用了registerBeanDefinition方法来进行bean定义的注册。
Registrar类定义如下:
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
Registrar() {
}
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
//此处是注册了一个Bean的定义。
//getPackageName()其实返回了当前主程序类的 同级以及子级的包组件。
AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());
}
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata));
}
}
Registrar里registerBeanDefinitions实际是调用AutoConfigurationPackages的register方法,其定义如下:
public static void register(BeanDefinitionRegistry registry, String... packageNames) {
if (registry.containsBeanDefinition(BEAN)) {
BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues();
constructorArguments.addIndexedArgumentValue(0, addBasePackages(constructorArguments, packageNames));
} else {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(AutoConfigurationPackages.BasePackages.class);
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);
beanDefinition.setRole(2);
registry.registerBeanDefinition(BEAN, beanDefinition);
}
}
AutoConfigurationPackages的register主要调用registerBeanDefinition,
它是接口BeanDefinitionRegistry中的方法,该接口的实现类有三个:DefaultListableBeanFactory,GenericApplicationContext,SimpleBeanDefinitionRegistry
,通过Idea里调试,实现类用的是DefaultListableBeanFactory,其定义如下:
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition)beanDefinition).validate();
} catch (BeanDefinitionValidationException var9) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", var9);
}
}
BeanDefinition existingDefinition = (BeanDefinition)this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
if (!this.isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}
if (existingDefinition.getRole() < beanDefinition.getRole()) {
if (this.logger.isInfoEnabled()) {
this.logger.info("Overriding user-defined bean definition for bean '" + beanName + "' with a framework-generated bean definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]");
}
} else if (!beanDefinition.equals(existingDefinition)) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Overriding bean definition for bean '" + beanName + "' with a different definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]");
}
} else if (this.logger.isTraceEnabled()) {
this.logger.trace("Overriding bean definition for bean '" + beanName + "' with an equivalent definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]");
}
this.beanDefinitionMap.put(beanName, beanDefinition);
} else {
if (this.hasBeanCreationStarted()) {
Map var4 = this.beanDefinitionMap;
synchronized(this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions = new ArrayList(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
if (this.manualSingletonNames.contains(beanName)) {
Set<String> updatedSingletons = new LinkedHashSet(this.manualSingletonNames);
updatedSingletons.remove(beanName);
this.manualSingletonNames = updatedSingletons;
}
}
} else {
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
this.manualSingletonNames.remove(beanName);
}
this.frozenBeanDefinitionNames = null;
}
if (existingDefinition != null || this.containsSingleton(beanName)) {
this.resetBeanDefinition(beanName);
}
}
GenericApplicationContext实现的也是DefaultListableBeanFactory的registerBeanDefinition方法。
...
public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {
private final DefaultListableBeanFactory beanFactory;
...
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {
this.beanFactory.registerBeanDefinition(beanName, beanDefinition);
}
...
}
SimpleBeanDefinitionRegistry里registerBeanDefinition方法的定义如下所示:
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {
Assert.hasText(beanName, "'beanName' must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
this.beanDefinitionMap.put(beanName, beanDefinition);
}
@AutoConfigurationPackage小结(图解)
1.2.2 AutoConfigurationImportSelector
AutoConfigurationImportSelector的定义如下所示:
AutoConfigurationImportSelector的selectImports方法,主要是自动加载配置文件META-INF/spring.factories里的类,而此处是加载spring-boot-autoconfigue2.1.3.RELEASE.jar/META-INF/spring.factories里org.springframework.boot.autoconfigure.EnableAutoConfiguration所对应的自动配置类。
spring.factories内容如下图所示(部分截图):
selectImports方法调用图解如下图所示:
selectImports实际是借助SpringFactoriesLoader类去实现自动加载功能。SpringFactoriesLoader的loadFactoryNames方法,通过传入工厂类的名字:EnableAutoConfiguration.class,去配置文件spring.factories里搜索并自动加载,loadFactoryNames相关定义如下图所示:
loadFactoryNames里通过loadSpringFactories方法实现具体加载功能,其定义如下所示:
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
if (result != null) {
return result;
} else {
try {
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
LinkedMultiValueMap result = new LinkedMultiValueMap();
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
while(var6.hasNext()) {
Entry<?, ?> entry = (Entry)var6.next();
String factoryClassName = ((String)entry.getKey()).trim();
String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
int var10 = var9.length;
for(int var11 = 0; var11 < var10; ++var11) {
String factoryName = var9[var11];
result.add(factoryClassName, factoryName.trim());
}
}
}
cache.put(classLoader, result);
return result;
} catch (IOException var13) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
}
}
}
AutoConfigurationImportSelector小结:
调用selectImports方法,传入工厂类名作为参数以借助SpringFactoriesLoader类的loadSpringFactories方法去spring.factories配置文件里根据传参去查找需要自动配置的类,注册到Spring的IoC容器里。
@EnableAutoConfiguration总结(图解):
1.3 @ComponentScan
@ComponentScan的功能其实就是自动扫描并加载符合条件的组件(比如@Component和@Repository等)或者bean定义,最终将这些bean定义加载到IoC容器中。我们可以通过basePackages等属性来细粒度的定制@ComponentScan自动扫描的范围,如果不指定,则默认Spring框架实现会从声明@ComponentScan所在类的package进行扫描。
注意:所以SpringBoot的启动类最好是放在root package下,因为默认不指定basePackages。