@SpringBootApplication源码分析

@SpringBootApplication包含的注解

@Target(ElementType.TYPE)   //指定在何处写入注释的合法位置
@Retention(RetentionPolicy.RUNTIME)  //RetentionPolicy这个枚举类型的常量描述保留注释的各种策略,它们与元注释(@Retention)一起指定注释要保留多长时间
@Documented    //表明这个注释是由 javadoc记录的,在默认情况下也有类似的记录工具。 如果一个类型声明被注释了文档化,它的注释成为公共API的一部分。
@Inherited     //它指明被注解的类会自动继承
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),    //扫描器
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })     //扫描器的过滤方式

一、@SpringBootConfiguration包含的注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration

  @Configuration 1、表明该注解是一个配置类,可以用@Configuration注解来代替以前传统的spring.xml配置文件;

            2、@Configuration注解的类会自动加入到spring容器中。

二、@EnableAutoConfiguration :使spring boot 实现自动配置。(约定优于配置)

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)

  @AutoConfigurationPackage:实现自动将包放入到扫描中,它有一个注解

@Import(AutoConfigurationPackages.Registrar.class)

  其实现的方法:

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

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

        @Override
        public Set<Object> determineImports(AnnotationMetadata metadata) {
            return Collections.singleton(new PackageImport(metadata));
        }

    }

  metadata可以获取被@SpringBootApplication注解的类,然后通过该类获取包名。通过这个方法将该包以及其子包所包含的类全部放入到spring容器中,这也是为什么spring boot的代码都要放在@SpringBootApplication注解的类的包或其子包中,才能被spring容器识别的原因。

  其中@EnableAutoConfiguration本身也有一个@Import(AutoConfigurationImportSelector.class),其中有一个方法:

@Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        }
        AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
                .loadMetadata(this.beanClassLoader);
        AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
                annotationMetadata);
        return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
            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 = filter(configurations, autoConfigurationMetadata);
        fireAutoConfigurationImportEvents(configurations, exclusions);
        return new AutoConfigurationEntry(configurations, exclusions);
    }

  通过DEBUG可以发现configurations中通过LinkedList链表数据结构,放入我们第三方依赖的jar包,这种链表结构使依赖的jar包增删速度很快。

  List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes)又是怎么引入jar包的呢?

  进入getCandidateConfigurations:

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

  继续进入SpringFactoriesLoader.loadFactoryNames:

public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
        String factoryClassName = factoryClass.getName();
        return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
    }

  继续进入loadSpringFactories:

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        MultiValueMap<String, String> result = cache.get(classLoader);
        if (result != null) {
            return result;
        }

        try {
            Enumeration<URL> urls = (classLoader != null ?
                    classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                    ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
            result = new LinkedMultiValueMap<>();
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                UrlResource resource = new UrlResource(url);
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                for (Map.Entry<?, ?> entry : properties.entrySet()) {
                    String factoryClassName = ((String) entry.getKey()).trim();
                    for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                        result.add(factoryClassName, factoryName.trim());
                    }
                }
            }
            cache.put(classLoader, result);
            return result;
        }
        catch (IOException ex) {
            throw new IllegalArgumentException("Unable to load factories from location [" +
                    FACTORIES_RESOURCE_LOCATION + "]", ex);
        }
    }

  可以很明显看到FACTORIES_RESOURCE_LOCATION这个枚举类型就是引入jar包的关键,继续进入:

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

  发现jar包最终是由这个配置文件:spring-boot-autoconfigure-2.1.13.RELEASE.jar中META-INF/spring.factories,来进行声明,然后通过@EnableAutoConfiguration来开启使用。

三、具体看看自动装配原理

  以spring-boot-autoconfigure-2.1.13.RELEASE.jar中META-INF/spring.factories中的org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration举例,进入该类看其注解信息:

@Configuration                 //标识为配置类,将其纳入spring容器
@EnableConfigurationProperties(HttpProperties.class)      //进入该注解,发现将默认编码设置为UTF_8
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)     //如果是servlet类型则该类生效
@ConditionalOnClass(CharacterEncodingFilter.class)           //项目中存在字节码过滤器则成立
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)   //当属性满足要求时,此条件成立
public class HttpEncodingAutoConfiguration {...
}

  3.1、进入@EnableConfigurationProperties(HttpProperties.class):

@ConfigurationProperties(prefix = "spring.http")

/**
* Configuration properties for http encoding.
*/

  发现设置有“ "spring.http"”这个前缀,注释信息为encoding,加上属性信息Charset将其写入到application.properties中:

spring.http.encoding.charset=US_ASCII

  发现可以通过此前缀来更改其约定。

  3.2、解读@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)

  prefix = "spring.http.encoding", value = "enabled":前缀为spring.http.encoding,名为enabled;

  matchIfMissing :当前述中的属性不存在时成立。

  即在application.properties中没有配置spring.http.encoding.enabled这个属性,则该类生效。

  总结:即当自动配置类中的@ConditionalOnXXXX 注解中的条件都满足时,自动装配即生效。也可从@ConditionalOnProperty中prefix+value 得知更改该自动装配全局配置文件中的key。

  附:

  

  tip: 在全局配置文件中设置 debug=true,可以在控制台快速获取自动装配的信息。

    Positive matches 列表表示spring boot开启的自动装配;

    Negative matches 列表表示spring boot没有开启的自动装配。

猜你喜欢

转载自www.cnblogs.com/709539062rao/p/12605702.html