2021-11-18学习记录 浅谈@SpringBootApplication

通过昨天的学习知道了启动SpringBoot项目是通过启动一个被@SpringBootApplication表示的类的main方法来启动的,启动时会加载自动装配类,来装配我们导入依赖的相关组件。今天就来大致看一下@SpringBootApplication是如何做到这些的。

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication

进入@SpringBootApplication,我们可以看到除去java自带的元注解外,@SpringBootApplication实际上是@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan的组合。

@ComponentScan是用于包扫描,进入@SpringBootConfiguration可以看到

@Configuration
@Indexed
public @interface SpringBootConfiguration {
    @AliasFor(
        annotation = Configuration.class
    )
    boolean proxyBeanMethods() default true;
}

是一个Spring的配置类。

剩下的@EnableAutoConfiguration应该就是实现@SpringBootApplication主要功能的注解。

进入@EnableAutoConfiguration

@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class[] exclude() default {};

    String[] excludeName() default {};
}

除了元注解之外,它也被两个注解所修饰,先来看看@AutoConfigurationPackage

@Import({Registrar.class})
public @interface AutoConfigurationPackage {
    String[] basePackages() default {};

    Class[] basePackageClasses() default {};
}

可以看到它将Register类放入了Ioc容器中,这个Register类的功能是什么呢?

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
        Registrar() {
        }

        public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            AutoConfigurationPackages.register(registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0]));
        }

        public Set determineImports(AnnotationMetadata metadata) {
            return Collections.singleton(new AutoConfigurationPackages.PackageImports(metadata));
        }
    } 
  

进来后我们看到它是AutoConfigurationPackages的一个静态内部类,实现了两个接口的方法。

从方法名registerBeanDefinitions我们可以猜测该方法时用来注册指定的Bean对象的,再看方法体。

  AutoConfigurationPackages.register(registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0])

它获得了注解的元数据后,调用getPackageNames方法获取了报名并转化为了数组来进行注册。这也是为什么默认情况下SpringBoot会扫描main方法所在的包及其下级的包的原因。

对@AutoConfigurationPackage的分析先到这,接下来来看@Import({AutoConfigurationImportSelector.class})

它将AutoConfigurationImportSelector放入了容器,进入该类,通过debug我们定位到了getAutoConfigurationEntry方法,从方法名猜测,它是用来获得自动配置实体的方法

    protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        } else {
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
            List configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
            configurations = this.removeDuplicates(configurations);
            Set exclusions = this.getExclusions(annotationMetadata, attributes);
            this.checkExcludedClasses(configurations, exclusions);
            configurations.removeAll(exclusions);
            configurations = this.getConfigurationClassFilter().filter(configurations);
            this.fireAutoConfigurationImportEvents(configurations, exclusions);
            return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
        }
    }

先进行判断,若不运行自动配置,则返回空,否则继续进行

首先获得了注解属性,接着获得了一个List型的configurations,紧接着对这个configurations做各种操作,可见

List configurations = this.getCandidateConfigurations(annotationMetadata, attributes);

是整个方法的重点。进入方法内部,发现它又调用了另一个方法来获取configurations

    protected List getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        List configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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;
    }

继续深入

    public static List loadFactoryNames(Class factoryType, @Nullable ClassLoader classLoader) {
        ClassLoader classLoaderToUse = classLoader;
        if (classLoader == null) {
            classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
        }

        String factoryTypeName = factoryType.getName();
        return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
    }

首先获得类加载器,若为空,则重新获取。

最后通过(List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());获取返回的configurations

private static Map> loadSpringFactories(ClassLoader classLoader) {
        Map> result = (Map)cache.get(classLoader); //首先从缓存中查询
        if (result != null) {
            return result;  //若缓存中有,则返回
        } else { 
            HashMap result = new HashMap(); //若没有,则新建一个HashMap进行封装

            try {
                Enumeration urls = classLoader.getResources("META-INF/spring.factories");//获取所有META-INF/spring.factors的信息

                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 factoryTypeName = ((String)entry.getKey()).trim();
                        String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                        String[] var10 = factoryImplementationNames;
                        int var11 = factoryImplementationNames.length;

                        for(int var12 = 0; var12 < var11; ++var12) {
                            String factoryImplementationName = var10[var12];
                            ((List)result.computeIfAbsent(factoryTypeName, (key) -> {
                                return new ArrayList();
                            })).add(factoryImplementationName.trim());
                        }
                    }
                }

                result.replaceAll((factoryType, implementations) -> {
                    return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
                });
                cache.put(classLoader, result); //获取结束后,放入缓存中一份
                return result;  //并返回
            } catch (IOException var14) {
                throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);
            }
        }
    }

2021-11-18学习记录 浅谈@SpringBootApplication_第1张图片

 随便打开一个META-INF/spring.factories文件,发现里面是各种含有AutoConfiguration后缀的信息

随便打开一个AutoConfiguration后缀的文件,发现其为配置文件,并写出了各种初始信息

2021-11-18学习记录 浅谈@SpringBootApplication_第2张图片

我们猜测,SpringBoot再获得result后,会一个个对这些AutoConfiguration进行解析,并将其放入Ioc容器中,这样就实现了自动装配。

你可能感兴趣的:(学习记录,Spring,java,spring,开发语言)