敞开成长之旅!这是我参与「日新方案 2 月更文应战」的第 1 天,点击查看活动概况

咱们新建一个SpringBoot项目都会引入一个pom然后新建一个这样发动类,就能运转一个JAVA项目,而不需要像传统的SSM结构那样编写各种XML以及装备项,那么SpringBoot是怎么完结咱们之前那些事情的呢?

pom

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

发动类

@SpringBootApplication
public class SpringBootDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringBootDemoApplication.class, args);
    }
}

新建的发动类只做了两件事

  1. 增加了一个@SpringBootApplication注解
  2. 调用了SpringApplication类的run办法

首先咱们先剖析@SpringBootApplication注解做了什么

SpringBoot源码解析(一) @SpringBootApplication注解解析

@SpringBootApplication注解上组合了其他注解,红色框子中的元注解不必关注,让咱们看看其他几个注解各自有什么效果

一、@SpringBootConfiguration

@SpringBootConfiguration组合了@Configuration

SpringBoot源码解析(一) @SpringBootApplication注解解析

1.1 @Configuration的效果

SpringBoot源码解析(一) @SpringBootApplication注解解析

@Configuration是Spring的中的注解,表明当时类是一个装备类而且它组合@Component注解,意味也将会注册为bean

由此咱们能够得出的定论的是咱们新建的发动类是一个装备类,而且会作为一个Bean导入到Spring容器

二 @EnableAutoConfiguration

SpringBoot源码解析(一) @SpringBootApplication注解解析

@EnableAutoConfiguration组合了@AutoConfigurationPackage注解和向容器中导入了AutoConfigurationImportSelector这个类,咱们分隔来看这两个注解的效果

2.1 @AutoConfigurationPackage

SpringBoot源码解析(一) @SpringBootApplication注解解析
@AutoConfigurationPackage 向容器中导入了AutoConfigurationPackages.Registrar这个类, 进入Registrar这个类看他究竟干了啥

2.1.1AutoConfigurationPackages.Registrar

SpringBoot源码解析(一) @SpringBootApplication注解解析

Registrar完成了ImportBeanDefinitionRegistrar接口和DeterminableImports(测验运用不必关心)接口

假如不清楚ImportBeanDefinitionRegistrar效果的能够看下面的事例

2.1.1.1 ImportBeanDefinitionRegistrar接口

这个接口配合@Import注解向容器中注册Bean的 运用事例:

定义测验Bean

public class Teacher {
    private String[] name;
    public Teacher(String...  name) {
        this.name = name;
    }
    public void hello(){
        System.out.println(name + "你好啊!");
    }
}

注册Bean信息

public class AutoConfiguredTeacherRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        //设置Bean的类别
        beanDefinition.setBeanClass(Teacher.class);
        //增加结构参数
        beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, "张三");
        //注册bean
        registry.registerBeanDefinition("teacher", beanDefinition);
    }
}

导入注册类

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(AutoConfiguredTeacherRegistrar.class)
public @interface EnableTeacherBean {
}

测验发动类

@EnableTeacherBean
@SpringBootApplication
public class SpringBootDemoApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SpringBootDemoApplication.class);
        context.getBean(Teacher.class).hello();
    }
}

SpringBoot源码解析(一) @SpringBootApplication注解解析

所以咱们接下来就看看Registrar向容器中注册了什么

@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
      BeanDefinitionRegistry registry) {
   register(registry, new PackageImport(metadata).getPackageName());
}

入参出创建了一个PackageImport类,调取了getPackageName办法 咱们看看这个类是什么效果

SpringBoot源码解析(一) @SpringBootApplication注解解析

这个类的结构办法中会经过类工具获取当元数据地点类的包途径,也就发动类的包途径

继续进入register办法

public static void register(BeanDefinitionRegistry registry, String... packageNames) {
   //假如容器中包括了AutoConfigurationPackages这个类
   if (registry.containsBeanDefinition(BEAN)) {
      //获取Bean信息
      BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
      ConstructorArgumentValues constructorArguments = beanDefinition
            .getConstructorArgumentValues();
      //增加并合并结构参数
      constructorArguments.addIndexedArgumentValue(0,
            addBasePackages(constructorArguments, packageNames));
   }
   else {
      GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
      //设置Bean类型
      beanDefinition.setBeanClass(BasePackages.class);
      //增加结构参数
      beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0,
            packageNames);
      beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
      //注册Bean信息
      registry.registerBeanDefinition(BEAN, beanDefinition);
   }
}

这个办法是向容器中注入了一个BasePackages类,而且将根途径作为入参传入进去

所以@AutoConfigurationPackage的效果是向容器中注册了一个BasePackages的类,而且这个类存储了发动类的途径

虽然Spring和SpringBoot没有运用到这个类 可是能够作为三方Starter中运用到了,比如Mybatis集成SpringBoot的主动装备

AutoConfiguredMapperScannerRegistrar在注册Mapper装备信息的时分会读取发动类的包途径作为扫描途径

public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    if (!AutoConfigurationPackages.has(this.beanFactory)) {
        MybatisAutoConfiguration.logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.");
    } else {
        MybatisAutoConfiguration.logger.debug("Searching for mappers annotated with @Mapper");
        //获取发动类的根途径
        List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
        builder.addPropertyValue("processPropertyPlaceHolders", true);
        //设置注解类型
        builder.addPropertyValue("annotationClass", Mapper.class);
        //设置包扫描途径
        builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(packages));
        //注册Bean信息
        registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition());
    }
}

2.2 AutoConfigurationImportSelector

SpringBoot源码解析(一) @SpringBootApplication注解解析
熟悉Spring的都知道Aware接口是获取Spring加载中的一些信息没有实质效果,Ordered接口是用来排序的,所以只剩下了一个接口DeferredImportSelector,那么这个接口是用来干什么的?

它是ImportSelector的子接口,它的文档注释原文和翻译

A variation of ImportSelector that runs after all @Configuration beans have been processed. This type of selector can be particularly useful when the selected imports are @Conditional . Implementations can also extend the org.springframework.core.Ordered interface or use the org.springframework.core.annotation.Order annotation to indicate a precedence against other DeferredImportSelectors . Implementations may also provide an import group which can provide additional sorting and filtering logic across different selectors.
ImportSelector 的一种扩展,在处理完一切 @Configuration 类型的Bean之后运转。当所选导入为 @Conditional 时,这种类型的选择器特别有用。
完成类还能够扩展 Ordered 接口,或运用 @Order 注解来指示相对于其他 DeferredImportSelector 的优先级。
完成类也能够供给导入组,该导入组能够供给跨不同选择器的其他排序和挑选逻辑。

DeferredImportSelectorHandler中维护了DeferredImportSelector, DeferredImportSelectorGrouping调用getImportGroup获取一切SelectorHandler拿到待处理的DeferredImportSelector(其中就包括AutoConfigurationGroup),接着调用 process 办法,AutoConfigurationGroup 会调用咱们导入的AutoConfigurationImportSelector类的getAutoConfigurationEntry办法,拿到要引入的装备集合(Entry类型的集合),最终遍历这个集合逐个解析装备类。

接下来让咱们看下是怎么拿到一切主动的装备的

protected AutoConfigurationEntry getAutoConfigurationEntry(
      AutoConfigurationMetadata autoConfigurationMetadata,
      AnnotationMetadata annotationMetadata) {
   if (!isEnabled(annotationMetadata)) {
      return EMPTY_ENTRY;
   }
   //1.获取@EnableAutoConfiguration注解一切特点信息
   AnnotationAttributes attributes = getAttributes(annotationMetadata);
   //2.加载主动装备类
   List<String> configurations = getCandidateConfigurations(annotationMetadata,
         attributes);
   //3.移除重复装备
   configurations = removeDuplicates(configurations);
   //4.根据注解的特点信息获取被排除的装备类
   Set<String> exclusions = getExclusions(annotationMetadata, attributes);
   checkExcludedClasses(configurations, exclusions);
   //5.移除一切被排除的主动装备类
   configurations.removeAll(exclusions);
   configurations = filter(configurations, autoConfigurationMetadata);
   fireAutoConfigurationImportEvents(configurations, exclusions);
   return new AutoConfigurationEntry(configurations, exclusions);
}

进入getCandidateConfigurations看看是怎么拿到需要导入的主动装备类

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
   //运用Spring的SPI机制获取META-INF/spring.factories下一切key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的类型
   List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
         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;
}

获取@EnableAutoConfiguration注解的类信息

protected Class<?> getSpringFactoriesLoaderFactoryClass() {
   return EnableAutoConfiguration.class;
}

所以AutoConfigurationImportSelector类的效果就是获取一切META-INF/spring.factories下的主动装备类信息,之后这些主动装备类会被装配到Spring容器中,所以即使没有任何装备文件,SpringBoot的Web使用都能正常运转。

三、@ComponentScan

@ComponentScan这个注解在Spring中的效果是指定包扫描的根途径,让Spring来扫描指定包及子包下的组件,也能够不指定途径,默许扫描当时装备类地点包及子包里的一切组件,所以Springboot只会扫描发动类当时包和以下的包

SpringBoot源码解析(一) @SpringBootApplication注解解析
excludeFilters特点的效果是指定包扫描的时分根据规矩指定要排除的组件,type = FilterType.CUSTOM是自定义过滤,classes 指定的类要完成TypeFilter接口,在match办法中能够获取当时扫描到的类的信息,比如注解、类名和类途径。接下来咱们看看这两个类的match的效果

3.1 TypeExcludeFilter

它是一种扩展机制,能让咱们向IOC容器中注册一些自定义的组件过滤器,以在包扫描的过程中过滤它们。首要用于测验

3.2AutoConfigurationExcludeFilter

它是用来判断是否是装备类和主动装备类

总结

1. 当时类被@Configuration标示,所以发动类也是一个装备类

2. 当时类被@ComponentScan标示,默许扫描当时装备类地点包及子包里的一切组件,所以Springboot只会扫描发动类当时包和以下的包

3. 当时类被@EnableAutoConfiguration标示,导入了AutoConfigurationImportSelector类,这个类会使用Spring的SPI机制扫描加载META-INF/spring.factories下一切key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的主动装备类

SpringBoot源码解析(一) @SpringBootApplication注解解析