• 欢迎访问 winrains 的个人网站!
  • 本网站主要从互联网整理和收集了与Java、网络安全、Linux等技术相关的文章,供学习和研究使用。如有侵权,请留言告知,谢谢!

@SpringBootApplication注解源码分析

Spring Boot winrains 来源:Ccww 12个月前 (11-11) 49次浏览

一、@SpringBootApplication 的作用是什么?

Q:springboot项目的启动类上,都会有个注解@SpringBootApplication,这个注解起到了什么作用?

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

​ 我们进入@SpringBootApplication注解,发现它等价于三个注解@SpringBootConfiguration @EnableAutoConfiguration @ComponentScan

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

1) @SpringBootConfiguration 就相当于 @Configuration

@Configuration
public @interface SpringBootConfiguration {
}

2) @EnabelAutoConfiguration 相当于将这两个类的实例加入到容器中AutoConfigurationImportSelector.class AutoConfigurationPackages.Registrar.class

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    ...
}
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
  • AutoConfigurationImportSelector.class的作用是,注入spring.factories文件中EnableAutoConfiguration对应的类的实例,当然要经过spring.factories文件中AutoConfigurationImportFilter对应的过滤器(OnBeanConditionOnClassConditionOnWebApplicationCondition等等)的过滤。还要排除掉@EnableAutoConfiguration中的exclude和excludeName具体见ConfigurationClassParser的getImports方法,其中调用了AutoConfigurationImportSelector的process方法和selectImports方法。
    public Iterable<Group.Entry> getImports() {
        for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
            this.group.process(deferredImport.getConfigurationClass().getMetadata(),
                    deferredImport.getImportSelector());
        }
        return this.group.selectImports();
    }
    

    AutoConfigurationImportSelector实现了DeferredImportSelector,所以它是解析@Configuration的最后一步。DeferredImportSelector可以和@Order搭配使用。AutoConfigurationImportSelector的意义在于引入其他包时,可以直接注入其他包的@Configuration,当然需要在其他包的resources文件夹下,新建META-INF目录,在META-INF目录下新建spring.factories文件,加入org.springframework.boot.autoconfigure.EnableAutoConfiguration = @Configuration标注的类的路径

  • AutoConfigurationPackages.Registrar.class的作用是,注入一个名称为AutoConfigurationPackages的BasePackages.class实例。这个实例的作用在于保存自动扫描的包路径,供以后使用(比如JPA 的entity扫描)
    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 {
            GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
            beanDefinition.setBeanClass(BasePackages.class);
            beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0,
                    packageNames);
            beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
            registry.registerBeanDefinition(BEAN, beanDefinition);
        }
    }
    private static String[] addBasePackages(
            ConstructorArgumentValues constructorArguments, String[] packageNames) {
        String[] existing = (String[]) constructorArguments
                .getIndexedArgumentValue(0, String[].class).getValue();
        Set<String> merged = new LinkedHashSet<>();
        merged.addAll(Arrays.asList(existing));
        merged.addAll(Arrays.asList(packageNames));
        return StringUtils.toStringArray(merged);
    }
    

3) @ComponentScan 当然是将路径下合适的类加载到容器中
Q:它为什么要用到TypeExcludeFilter.classAutoConfigurationExcludeFilter.class 这两个过滤器

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
        @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
    ...
}
  • 这两个过滤器在ComponentScanAnnotationParser的parse方法中,被加入
    public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
        //此处加入了三个默认的includeFilter,一个用来筛选@Componment标记的类,一个用来筛选javax.annotation.ManagedBean标记的类,一个用来筛选JSR-330中javax.inject.Named标记的类(如果引入了JSR-330依赖注入标准的话,即引入javax.inject包)
        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
                    componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
        ...
        for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
            for (TypeFilter typeFilter : typeFiltersFor(filter)) {
                scanner.addExcludeFilter(typeFilter);
            }
        }
        ...
        scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
            @Override
            protected boolean matchClassName(String className) {
                return declaringClass.equals(className);
            }
        });
        return scanner.doScan(StringUtils.toStringArray(basePackages));
    }
    

    **也是在parse这个方法里调用了 typeFiltersFor方法,对`TypeFilter.class`的实现类进行了实例化**。(也就是说`TypeExcludeFilter.class` `AutoConfigurationExcludeFilter.class` `AbstractTypeHierarchyTraversingFilter.class`都在这里进行了实例化,但是没有加入spring的bean池)

    private List<TypeFilter> typeFiltersFor(AnnotationAttributes filterAttributes) {
        List<TypeFilter> typeFilters = new ArrayList<>();
        FilterType filterType = filterAttributes.getEnum("type");
        for (Class<?> filterClass : filterAttributes.getClassArray("classes")) {
            switch (filterType) {
                case ANNOTATION:
                    Assert.isAssignable(Annotation.class, filterClass,
                            "@ComponentScan ANNOTATION type filter requires an annotation type");
                    @SuppressWarnings("unchecked")
                    Class<Annotation> annotationType = (Class<Annotation>) filterClass;
                    typeFilters.add(new AnnotationTypeFilter(annotationType));
                    break;
                case ASSIGNABLE_TYPE:
                    typeFilters.add(new AssignableTypeFilter(filterClass));
                    break;
                case CUSTOM:
                    Assert.isAssignable(TypeFilter.class, filterClass,
                            "@ComponentScan CUSTOM type filter requires a TypeFilter implementation");
                    TypeFilter filter = BeanUtils.instantiateClass(filterClass, TypeFilter.class);
                    ParserStrategyUtils.invokeAwareMethods(
                            filter, this.environment, this.resourceLoader, this.registry);
                    typeFilters.add(filter);
                    break;
                default:
                    throw new IllegalArgumentException("Filter type not supported with Class value: " + filterType);
            }
        }
        for (String expression : filterAttributes.getStringArray("pattern")) {
            switch (filterType) {
                case ASPECTJ:
                    typeFilters.add(new AspectJTypeFilter(expression, this.resourceLoader.getClassLoader()));
                    break;
                case REGEX:
                    typeFilters.add(new RegexPatternTypeFilter(Pattern.compile(expression)));
                    break;
                default:
                    throw new IllegalArgumentException("Filter type not supported with String pattern: " + filterType);
            }
        }
        return typeFilters;
    }
    

    ComponentScanAnnotationParser的parse方法中,scanner最后为什么又加了一个AbstractTypeHierarchyTraversingFilter呢?我们看一下它的match方法,发现是过滤掉启动类,不让它作为@configuration标记的候选类,避免再一次解析启动类上的各种注解(因为它的两个参数considerInherited和considerInterfaces在scanner.addExcludeFilter这条语句中,设置成了false,导致下面的判断语句不生效)。

    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
                throws IOException {
        ...
        ClassMetadata metadata = metadataReader.getClassMetadata();
        if (matchClassName(metadata.getClassName())) {
            return true;
        }
        if (this.considerInherited) {
            ...
        }
        if (this.considerInterfaces) {
            ...
        }
        return false;
    }
    

    注:启动类本身还是会注入到spring的bean池中的,具体见SpringApplication的load方法

  • ExcludeFilter在ClassPathScanningCandidateComponentProvider的isCandidateComponent方法中被使用
    protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
        for (TypeFilter tf : this.excludeFilters) {
            if (tf.match(metadataReader, getMetadataReaderFactory())) {
                return false;
            }
        }
        for (TypeFilter tf : this.includeFilters) {
            if (tf.match(metadataReader, getMetadataReaderFactory())) {
                return isConditionMatch(metadataReader);
            }
        }
        return false;
    }
    
  • AutoConfigurationExcludeFilter 的作用是过滤掉会自动配置的配置类,避免重复
    @Override
    public boolean match(MetadataReader metadataReader,
            MetadataReaderFactory metadataReaderFactory) throws IOException {
        //如果这个类被@Configuration标注,且属于自动加载的配置,那么过滤它,避免重复
        return isConfiguration(metadataReader) && isAutoConfiguration(metadataReader);
    }
    private boolean isConfiguration(MetadataReader metadataReader) {
        return metadataReader.getAnnotationMetadata()
                .isAnnotated(Configuration.class.getName());
    }
    private boolean isAutoConfiguration(MetadataReader metadataReader) {
        return getAutoConfigurations()
                .contains(metadataReader.getClassMetadata().getClassName());
    }
    protected List<String> getAutoConfigurations() {
        if (this.autoConfigurations == null) {
            /**
            从META-INF/spring.factories文件中,找出EnableAutoConfiguration.class
            多个jar包中,都存在spring.factories文件
            其中包含EnableAutoConfiguration.class的spring.factories文件,位于spring-boot-autoconfigure
            **/
            this.autoConfigurations = SpringFactoriesLoader.loadFactoryNames(
                    EnableAutoConfiguration.class, this.beanClassLoader);
        }
        return this.autoConfigurations;
    }
    
  • TypeExcludeFilter 的作用是加载spring bean池中所有针对TypeExcludeFilter的扩展,并循环遍历这些扩展类调用其match方法
    public boolean match(MetadataReader metadataReader,
            MetadataReaderFactory metadataReaderFactory) throws IOException {
        if (this.beanFactory instanceof ListableBeanFactory
                && getClass() == TypeExcludeFilter.class) {
            //加载spring bean池中所有针对TypeExcludeFilter的拓展
            Collection<TypeExcludeFilter> delegates = ((ListableBeanFactory) this.beanFactory)
                    .getBeansOfType(TypeExcludeFilter.class).values();
            // 循环遍历,调用其match方法
            for (TypeExcludeFilter delegate : delegates) {
                if (delegate.match(metadataReader, metadataReaderFactory)) {
                    return true;
                }
            }
        }
        return false;
    }
    

作者:Ccww

来源:https://juejin.im/post/5d1f15ee51882514bf5bef8b


版权声明:文末如注明作者和来源,则表示本文系转载,版权为原作者所有 | 本文如有侵权,请及时联系,承诺在收到消息后第一时间删除 | 如转载本文,请注明原文链接。
喜欢 (0)