Concise and easy to understand @ springbootapplication annotation source code analysis

Time:2020-5-15

springApplication

I@SpringBootApplicationWhat is the role of?

Q: There will be a comment on the startup class of the springboot project@SpringBootApplicationWhat does this annotation do?

@SpringBootApplication
public class MicroServiceApplication {

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

}

We enter@SpringBootApplicationAnnotation, find itEquivalent to three notes@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) @SpringBootConfigurationIt’s like@Configuration

@Configuration
public @interface SpringBootConfiguration {

}

2) @EnabelAutoConfiguration Equivalent to adding instances of these two classes to the containerAutoConfigurationImportSelector.class AutoConfigurationPackages.Registrar.class

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    ...
}
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {

}
  • AutoConfigurationImportSelector.classTo inject spring.factories The instance of the class corresponding to enableautoconfiguration in the file must pass the spring.factories Filter corresponding to autoconfigurationimportfilter in the file(OnBeanConditionOnClassConditionOnWebApplicationConditionAnd so on). And get rid of it@EnableAutoConfigurationExclude and excludename in

    See detailsConfigurationClassParserThe getImports method is called.AutoConfigurationImportSelectorProcess method and selectimports method of.

    public Iterable<Group.Entry> getImports() {
                for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
                    this.group.process(deferredImport.getConfigurationClass().getMetadata(),
                            deferredImport.getImportSelector());
                }
                return this.group.selectImports();
            }

    AutoConfigurationImportSelectorAchievedDeferredImportSelector, so it’s parsing@ConfigurationThe last step.DeferredImportSelectorCan and@OrderUse together.AutoConfigurationImportSelectorThe meaning is that when other packages are introduced, they can be directly injected into other packages@Configuration, of course, you need to create a new meta-inf directory in the Resources folder of other packages, a new spring.factories file in the meta-inf directory, and add the path of the class marked org.springframework.boot.autoconfigure.enableautoconfiguration = @ configuration

  • AutoConfigurationPackages.Registrar.classThe purpose of is to inject a file named autoconfigurationpackagesBasePackages.classexample. The function of this instance is to save the package path of automatic scanning for later use (such as JPA entity scanning)

    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) @ComponentScanOf course, load the appropriate class under the path into the container

Q: Why is it usedTypeExcludeFilter.classandAutoConfigurationExcludeFilter.classThese two filters

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
        @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
    ...
}
  • These two filters areComponentScanAnnotationParserIs added to the parse method of

    public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
        //Three default include filters are added here, one for filtering @ component tags, one for filtering javax.annotation.managedbean tags, and one for filtering javax.inject.named tags in jsr-330 (if the jsr-330 dependency injection standard is introduced, the javax.inject package is introduced)
        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));
        }
    **The typefiltersfor method is also called in the parse method to instantiate the implementation class of 'typefilter. Class' *. (that is to say, 'typeexcludefilter. Class',' autoconfigurationexcludefilter. Class', 'abstracttypehierarchytraversingfilter. Class' are instantiated here, but they are not added to the spring bean pool.)
    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;
        }

    ComponentScanAnnotationParserIn the parse method of, why does scanner add anotherAbstractTypeHierarchyTraversingFilterWhat about it? Let’s take a look at its match method and find out thatFilter out the startup class and prevent it from being a candidate class for @ configuration tag, so as to avoid parsing various annotations on the startup class again(because its two parameters considerinherited and considerinterfaces are in the scanner.addExcludeFilter In this statement, it is set to false, which causes the following judgment statement not to take effect).

    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;
        }

    Note: the startup class itself will be injected into the bean pool of spring. For details, seeSpringApplicationLoad method of

  • ExcludeFilter inClassPathScanningCandidateComponentProviderUsed in iscandidatecomponent method of

    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;
        }
  • AutoConfigurationExcludeFilterIts function is to filter out the configuration classes that will be automatically configured to avoid duplication

    @Override
        public boolean match(MetadataReader metadataReader,
                MetadataReaderFactory metadataReaderFactory) throws IOException {
            //If this class is labeled by @ configuration and belongs to the auto loaded configuration, filter it to avoid repetition
            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) {
                /**
                From the meta-inf / spring.factories file, locate enableautoconfiguration.class
                Spring.factories file exists in multiple jar packages
                It contains the spring.factories file of enableautoconfiguration.class, located in spring boot autoconfigure
                **/
                this.autoConfigurations = SpringFactoriesLoader.loadFactoryNames(
                        EnableAutoConfiguration.class, this.beanClassLoader);
            }
            return this.autoConfigurations;
        }
  • TypeExcludeFilterThe function of is to load all extensions in the spring bean pool for typeexcludefilter, and loop through these extension classes to call their match methods

    public boolean match(MetadataReader metadataReader,
                MetadataReaderFactory metadataReaderFactory) throws IOException {
            if (this.beanFactory instanceof ListableBeanFactory
                    && getClass() == TypeExcludeFilter.class) {
                //Load all extensions for typeexcludefilter in spring bean pool
                Collection<TypeExcludeFilter> delegates = ((ListableBeanFactory) this.beanFactory)
                        .getBeansOfType(TypeExcludeFilter.class).values();
                //Loop traversal, calling its match method
                for (TypeExcludeFilter delegate : delegates) {
                    if (delegate.match(metadataReader, metadataReaderFactory)) {
                        return true;
                    }
                }
            }
            return false;
        }

Recommended Today

[introduction to SQL foundation] 36 questions and Solutions

This paper deals with aggregate function, analysis function, condition function, and self mapping.There are the following tables (EMP, Dept), the structure of the table and the following 36 basic questions. Next, I will use the * sign to show them specifically. Those are more important and should be given priority. 1.1 list all departments with […]