Springboot 启动原理探索-@SpringBootApplication注解解析

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 @SpringBootApplication 注解分解图

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中的方法,该接口的实现类有三个:DefaultListableBeanFactoryGenericApplicationContextSimpleBeanDefinitionRegistry
,通过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小结(图解)

图2 @AutoConfigurationPackage

1.2.2 AutoConfigurationImportSelector

AutoConfigurationImportSelector的定义如下所示:

图3 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内容如下图所示(部分截图):

图4 spring.factories内容解析

selectImports方法调用图解如下图所示:


图 5 selectImports方法调用关系图

selectImports实际是借助SpringFactoriesLoader类去实现自动加载功能。SpringFactoriesLoader的loadFactoryNames方法,通过传入工厂类的名字:EnableAutoConfiguration.class,去配置文件spring.factories里搜索并自动加载,loadFactoryNames相关定义如下图所示:

图6 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总结(图解):

图7 @ EnableAutoConfiguration简析图

1.3 @ComponentScan

@ComponentScan的功能其实就是自动扫描并加载符合条件的组件(比如@Component和@Repository等)或者bean定义,最终将这些bean定义加载到IoC容器中。我们可以通过basePackages等属性来细粒度的定制@ComponentScan自动扫描的范围,如果不指定,则默认Spring框架实现会从声明@ComponentScan所在类的package进行扫描。

注意:所以SpringBoot的启动类最好是放在root package下,因为默认不指定basePackages。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • springboot 概述 SpringBoot能够快速开发,简化部署,适用于微服务 参考嘟嘟大神SpringBo...
    一纸砚白阅读 5,515评论 2 20
  • 1. Spring Boot 入口——main方法 从上面代码可以看出,Annotation定义(@SpringB...
    徐志毅阅读 20,285评论 9 52
  • SpringMVC原理分析 Spring Boot学习 5、Hello World探究 1、POM文件 1、父项目...
    jack_jerry阅读 1,363评论 0 1
  • Part III. Using Spring Boot 文档说明:文档对应的版本为 2.1.0.M3这不是文档的完...
    icameisaw阅读 2,175评论 0 0
  • 7 vue-cli初步 1.安装node.js下载安装node.js,一路默认安装即可,安装完毕,启动命令行,输入...
    诅咒猎豹阅读 126评论 0 0