springboot2 自动装配原理

发布时间 2023-06-16 21:47:37作者: LiusCraft

springboot自动装配

Spring支持两种bean配置方式:XML配置、JavaConfig配置

@SpringBootApplication 注解

我们创建一个springboot项目后,一般要用该注解,然后在springbootApplication.run方法传入标注了该注解的类,这样就可以去加载spring的相关操作

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

内部结构

内部注释了三个注解:

@SpringBootConfiguration 配置类注解
@EnableAutoConfiguration 开启自动装配的注解
@ComponentScan进行包扫描,默认是扫描当前类所在的包下的所有组件

@SpringBootConfiguration(Spring 配置类,Bean)

该注解简单来说就是把当前类作为一个配置类,因为它内部就是一个@configuration注解,它也是一个@Component(组件)

它内部还有个@Indexed注解。

@Indexed注解

在应用中有大量使用@ComponentScan扫描的package包含的类越多的时候,Spring模式注解解析耗时就越长。因为需要通过反射扫描包嘛,然后一个一个遍历。

那么它在这里就体现出了优势,我们只需要在类或者接口(当然是有注解标注当前类或者接口是组件的)加上该注解即可提升启动性能

内部做了什么:

在项目中使用了@Indexed之后,编译打包的时候会在项目中自动生成META-INT/spring.components文件。
    当Spring应用上下文执行ComponentScan扫描时,META-INT/spring.components将会被CandidateComponentsIndexLoader 读取并加载,转换为CandidateComponentsIndex对象,这样的话@ComponentScan不在扫描指定的package,而是读取CandidateComponentsIndex对象,从而达到提升性能的目的。

@EnableAutoConfiguration(启用自动化装配, 重点)

该注解就是开启自动装配。

内部注释了三个注解:

@AutoConfigurationPackage自动配置包注解,扫描第三方的依赖注解

@Import(AutoConfigurationImportSelector.class)导入自动化配置类

@AutoConfigurationPackage(自动配置包)

@AutoConfigurationPackage是自动配置的提醒,是Spring Boot中的注解

该注解跟@ComponentScan一样,都是将Spring Boot启动类所在的包及其子包里面的组件扫描到IOC容器中。

但是区别是:

  • @AutoConfigurationPackage扫描@Enitity、@Mapper等第三方依赖的注解,

  • @ComponentScan只扫描@Controller/@Service/@Component/@Repository这些常见注解。

@Import(AutoConfigurationImportSelector.class)(导入自动化配置类)

AutoConfigurationImportSelector类实现了ImportSelector接口。

AutoConfigurationImportSelector有一个方法叫selectImports()就是我们到底要给容器中导哪些类。

然后所有的东西都是调用getAutoConfigurationEntry()方法得到的,然后再通过调用该方法得到的autoConfigurationEntry对象去获取Configurations集合,并且返回出去,最终交给容器。

@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
        return NO_IMPORTS;
    }
    AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
    return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}

getAutoConfigurationEntry(annotationMetadata):

该方法用于获取自动配置类的元数据。

注: 自动配置类是通过@Configuration和@Conditional注解来实现的。

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    }
    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 = getConfigurationClassFilter().filter(configurations);
    fireAutoConfigurationImportEvents(configurations, exclusions);
    return new AutoConfigurationEntry(configurations, exclusions);
}

该方法代码解析:

  1. isEnabled(annotationMetadata)是判断是否启用自动装配,启用就继续,不起用则直接返回空的AutoConfigurationEntry

  2. getAttributes(annotationMetadata)是获取EnableAutoConfiguration注解的元数据,里面包含exclude(排除特定的自动配置类)和excludeName(排除特定的自动配置类名)

  3. getCandidateConfigurations(annotationMetadata, attributes);是获取自动装配配置类的,内部有SpringFactoriesLoader.loadFactoryNames获取,获取"META-INF/spring.factories"资源里的配置类。(注意的是,该文件是properties文件,我们在使用的时候全类名要写在org.springframework.boot.autoconfigure.EnableAutoConfiguration key名的后面)

  4. removeDuplicates(configurations);通过该方法去重,避免重复的全类名

  5. getExclusions(annotationMetadata, attributes);然后再对他们进行一个排除,排除条件就是通过getAttributes()方法获取的排除类或者类名

  6. checkExcludedClasses(configurations, exclusions);排除额外的类,不适合自动装配的类,若发现有这样的类则会抛出异常,让程序员处理。

  7. getConfigurationClassFilter().filter(configurations);过滤到不符合条件的类,只有符合条件的类才能在configurations里。

  8. fireAutoConfigurationImportEvents(configurations, exclusions);它通知所有监听器,自动装配的过程已经完成,并且告诉它们哪些AutoConfiguration类被应用了,哪些被排除了。

  9. 最终new AutoConfigurationEntry(configurations, exclusions);封装成AutoConfigurationEntry对象。

@ComponentScan(将指定路径上扫描)

@ComponentScan用于类或接口上主要是指定扫描路径,spring会把指定路径下带有指定注解的类自动装配到bean容器里。会被自动装配的注解包括@Controller@Service@Component@Repository等等。与ComponentScan注解相对应的XML配置就是<context:component-scan/>, 根据指定的配置自动扫描package,将符合条件的组件加入到IOC容器中;

该注解的常用属性:

  • basePackages和value:指定要扫描的路径(package),如果为空则以-@ComponentScan注解的类所在的包为基本的扫描路径。

  • basePackageClasses:指定具体扫描的类。

  • includeFilters:指定满足Filter条件的类。

  • excludeFilters:指定排除Filter条件的类。

  • useDefaultFilters=true/false:指定是否需要使用Spring默认的扫描规则:被@Component, @Repository, @Service, @Controller或者已经声明过@Component自定义注解标记的组件;

扩展

对本文里面提到的一些内容的补充。

@Import注解

import注解传入类可是单个类,也可指定多个类(数组形式)

三种用法:

  1. @Import一个普通类 spring会将该类加载到spring容器中

  2. @Import一个类,该类实现了ImportBeanDefinitionRegistrar接口,在重写的registerBeanDefinitions方法里面,能拿到BeanDefinitionRegistry的注册器,能手工往beanDefinitionMap中注册 beanDefinition

  3. @Import一个类 该类实现了ImportSelector 重写selectImports方法该方法返回了String[]数组的对象,数组里面的类都会注入到spring容器当中