美文网首页程序员Java编程
源码学习系列之SpringBoot自动配置(篇一)

源码学习系列之SpringBoot自动配置(篇一)

作者: smileNicky | 来源:发表于2019-11-02 19:22 被阅读0次

    源码学习系列之SpringBoot自动配置源码学习(篇一)

    ok,本博客尝试跟一下Springboot的自动配置源码,做一下笔记记录,自动配置是Springboot的一个很关键的特性,也容易被忽略的属性,因为这个属性被包括在@SpringBootApplication注解里,所以不去跟一下源码都不知道还有这个属性,ps:本博客源码基于SpringBoot1.5.7版本

    @SpringBootApplication

    ok,跟一下@SpringBootApplication,发现@SpringBootApplication其实是一个复合的注解,由很多注解构成,@EnableAutoConfiguration其实只是其一部分,@EnableAutoConfiguration就是开启自动配置的注解

    //
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by Fernflower decompiler)
    //
    
    package org.springframework.boot.autoconfigure;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Inherited;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    import org.springframework.boot.SpringBootConfiguration;
    import org.springframework.boot.context.TypeExcludeFilter;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.FilterType;
    import org.springframework.context.annotation.ComponentScan.Filter;
    import org.springframework.core.annotation.AliasFor;
    
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @SpringBootConfiguration 
    @EnableAutoConfiguration //自动配置注解
    @ComponentScan(
        excludeFilters = {@Filter(
        type = FilterType.CUSTOM,
        classes = {TypeExcludeFilter.class}
    ), @Filter(
        type = FilterType.CUSTOM,
        classes = {AutoConfigurationExcludeFilter.class}
    )}
    )
    public @interface SpringBootApplication {
        ....
        Class<?>[] scanBasePackageClasses() default {};
    }
    
    

    @EnableAutoConfiguration

    点进@EnableAutoConfiguration,比较重要的列出来:

    • @AutoConfigurationPackage
    • @Import({EnableAutoConfigurationImportSelector.class})
    package org.springframework.boot.autoconfigure;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Inherited;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    import org.springframework.context.annotation.Import;
    
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @AutoConfigurationPackage
    @Import({EnableAutoConfigurationImportSelector.class})
    public @interface EnableAutoConfiguration {
        String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
    
        Class<?>[] exclude() default {};
    
        String[] excludeName() default {};
    }
    

    @AutoConfigurationPackage

    先看@AutoConfigurationPackage源码,这个注解是开启自动配置包的,关注点在@Import({Registrar.class}),核心在Registrar类

    备注:@import注解是Spring的底层注解,作用是导入一个组件到容器里

    
    package org.springframework.boot.autoconfigure;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Inherited;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    import org.springframework.boot.autoconfigure.AutoConfigurationPackages.Registrar;
    import org.springframework.context.annotation.Import;
    
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @Import({Registrar.class})
    public @interface AutoConfigurationPackage {
    }
    

    Registrar 类是AutoConfigurationPackages类的静态内部类,

     static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
            Registrar() {
            }
    
            public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
                AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());
            }
    
            public Set<Object> determineImports(AnnotationMetadata metadata) {
                return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata));
            }
        }
    

    看一下(new AutoConfigurationPackages.PackageImport(metadata)).getPackageName()获取的是什么,用idea的工具,计算表达式,可以看到其实获取的是SpringBoot启动类上面的包名

    在这里插入图片描述 在这里插入图片描述

    AnnotationMetadata:SpringBoot注解元数据

    所以,@AutoConfigurationPackage开启后,就可以将主配置类(@SpringBootApplication)所在包及其子包里面的所有组件都扫描到Spring容器里

    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 {// Spring容器里没有找到对应组件
                /* 将组件注册到Spring容器里 */
                GenericBeanDefinition beanDefinition = new GenericBeanDefinition();                  beanDefinition.setBeanClass(AutoConfigurationPackages.BasePackages.class);
    beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);
                beanDefinition.setRole(2);
                registry.registerBeanDefinition(BEAN, beanDefinition);
            }
    
        }
    

    ok,跟了源码,当然只是简单跟一下,没有特别细的跟,这个过程可以看出这里的组件扫描只是扫描主配置类(@SpringBootApplication)所在包及其子包里面的所有组件,所以,写了例子验证一下:
    在Application类包外写个Controller测试类


    在这里插入图片描述
    @RestController
    public class TestController {
    
        @GetMapping("/hello")
        public String hello(){
            return "hello world!";
        }
    }
    

    启动项目,进行访问,发现都是404找不到这个接口,再将这个Controller放在包里面,才可以扫描到

    在这里插入图片描述

    @Import({EnableAutoConfigurationImportSelector.class})

    package org.springframework.boot.autoconfigure;
    
    import org.springframework.core.type.AnnotationMetadata;
    
    /** @deprecated */
    @Deprecated
    public class EnableAutoConfigurationImportSelector extends AutoConfigurationImportSelector {
        public EnableAutoConfigurationImportSelector() {
        }
    
        protected boolean isEnabled(AnnotationMetadata metadata) {
            return this.getClass().equals(EnableAutoConfigurationImportSelector.class) ? (Boolean)this.getEnvironment().getProperty("spring.boot.enableautoconfiguration", Boolean.class, true) : true;
        }
    }
    
    

    主要看一下其基类AutoConfigurationImportSelector代码,看一下selectImport方法:

    /**装载很多自动配置类,以全类名的方式返回一个字符数组**/
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
            if (!this.isEnabled(annotationMetadata)) {
                return NO_IMPORTS;
            } else {
                try {
                    //获取自动配置的元数据
                    AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
                    //装载配置属性
                    AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
                    //通过类加载器读取所有的配置类全类名
                    List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
                    configurations = this.removeDuplicates(configurations);
                    configurations = this.sort(configurations, autoConfigurationMetadata);
                    Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
                    this.checkExcludedClasses(configurations, exclusions);
                    configurations.removeAll(exclusions);
                    configurations = this.filter(configurations, autoConfigurationMetadata);
                    this.fireAutoConfigurationImportEvents(configurations, exclusions);
                    return (String[])configurations.toArray(new String[configurations.size()]);
                } catch (IOException var6) {
                    throw new IllegalStateException(var6);
                }
            }
        }
    
    • AutoConfigurationMetadata信息,获取整个JavaEE体系的一些配置类,当然是Springboot集成的,比如有WebMvcAutoConfiguration自动配置类


      在这里插入图片描述

    跟一下getCandidateConfigurations方法,SpringFactoriesLoader是Spring-code工程的工厂加载类,使用SpringFactoriesLoader,需要在模块的META-INF/spring.factories文件自己配置属性,这个Properties格式的文件中的key是接口、注解、或抽象类的全名,value是以逗号 “ , “ 分隔的实现类,使用SpringFactoriesLoader可以实现将相应的实现类注入Spirng容器

    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
            List<String> 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;
        }
    

    loadFactoryNames方法代码,

    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
        /* 将spring.factories的类都装载到Spring容器*/
         public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
            String factoryClassName = factoryClass.getName();
    
            try {
            //将META-INF/spring.factories文件里配置的属性都装载到Enumeration数据结构里
                Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                ArrayList result = new ArrayList();
                //遍历获取属性,然后再获取对应的配置类全类名
                while(urls.hasMoreElements()) {
                    URL url = (URL)urls.nextElement();
                    Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
                    String factoryClassNames = properties.getProperty(factoryClassName);
                    result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
                }
    
                return result;
            } catch (IOException var8) {
                throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + "META-INF/spring.factories" + "]", var8);
            }
        }
    

    然后META-INF/spring.factories文件放在哪?很显然是放在Springboot的自动配置模块里,如图:


    在这里插入图片描述

    所以,@Import({EnableAutoConfigurationImportSelector.class})开启之后,主要是EnableAutoConfigurationImportSelector这个类的作用就是在SpringBoot启动时候将从SpringBoot自动配置工程的META-INF/spring.factories文件中获取指定的值,经过SpringFactoriesLoader加载之后将很多自动配置类加载到Spring容器,所以我们不需要配置,mvc等等默认配置就已经随着SpringBoot启动而自动生效

    ok,Springboot的自动配置类都在这个包里,源码很多,所以本博客只是简单跟一下源码


    在这里插入图片描述

    相关文章

      网友评论

        本文标题:源码学习系列之SpringBoot自动配置(篇一)

        本文链接:https://www.haomeiwen.com/subject/mmshbctx.html