资讯专栏INFORMATION COLUMN

Springboot自动配置简单分析

buildupchao / 3264人阅读

摘要:简单的说,就通过具体就是类作为入口,配合注解实现了自动配置。注解让自动配置生效。接下来,从开始分析。这个同名方法中,创建了一个对象并调用了对象的成员方法。是类的对象,的方法中有解析的主要逻辑。分别对注解注解注解注解注解注解进行解析。

Springboot使用的版本是2.1.6.RELEASE。
简单的说,Springboot就通过BeanFactoryPostProcessor(具体就是ConfigurationClassPostProcessor类)作为入口,配合注解实现了自动配置。
可以先去先了解下BeanFactoryPostProcessor(bean工厂后处理器)和AnnotatedElementUtils(注解工具类)有助于理解

开始...

首先,看一下Springboot的启动代码。

@RestController
@SpringBootApplication
public class Springboot216DemoApp {

    @RequestMapping("/")
    public String home() {
        return "Hello World!";
    }

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

上面的代码中:

main()是Springboot应用的启动。

@SpringBootApplication注解让自动配置生效。

接下来,从SpringApplication.run(Springboot216DemoApp.class)开始分析。

public static ConfigurableApplicationContext run(Class primarySource, String... args) {
    return run(new Class[] { primarySource }, args);
}

SpringApplication.run()是静态方法,有两个参数primarySource和args,primarySource一般传递main()方法
所在的类,args可以不传。run()方法实际调用了另一个同名方法。

public static ConfigurableApplicationContext run(Class[] primarySources, String[] args) {
    return new SpringApplication(primarySources).run(args);
}

这个同名run()方法中,创建了一个SpringApplication对象并调用了SpringApplication对象的成员run()方法。
这里有三run()方法不要混淆了。
new SpringApplication(primarySources)中做了一些初始化的工作先不看了,先看SpringApplication对象
的成员run()方法。

public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    Collection exceptionReporters = new ArrayList<>();
    configureHeadlessProperty();
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();
    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        configureIgnoreBeanInfo(environment);
        Banner printedBanner = printBanner(environment);
        context = createApplicationContext();
        exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                new Class[] { ConfigurableApplicationContext.class }, context);
        prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        refreshContext(context);
        afterRefresh(context, applicationArguments);
        stopWatch.stop();
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
        }
        listeners.started(context);
        callRunners(context, applicationArguments);
    }
    ......
}

成员run()方法中
context = createApplicationContext();
refreshContext(context);
这两行代码是关键。

context = createApplicationContext()

protected ConfigurableApplicationContext createApplicationContext() {
    Class contextClass = this.applicationContextClass;
    if (contextClass == null) {
        try {
            switch (this.webApplicationType) {
            case SERVLET:
                contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
                break;
            case REACTIVE:
                contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
                break;
            default:
                contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
            }
        }
        catch (ClassNotFoundException ex) {
            throw new IllegalStateException(
                    "Unable create a default ApplicationContext, " 
                    + "please specify an ApplicationContextClass",
                    ex);
        }
    }
    return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}

createApplicationContext()方法中会根据不同的应用类型创建不同的Spring的ApplicationContext,
我们这个是servlet应用所以创建的是AnnotationConfigServletWebServerApplicationContext

public AnnotationConfigServletWebServerApplicationContext() {
    this.reader = new AnnotatedBeanDefinitionReader(this);
    this.scanner = new ClassPathBeanDefinitionScanner(this);
}

AnnotationConfigServletWebServerApplicationContext构造方法中,初始化了成员对象reader,reader
AnnotatedBeanDefinitionReader类的对象。

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    Assert.notNull(environment, "Environment must not be null");
    this.registry = registry;
    this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
    AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) {
    registerAnnotationConfigProcessors(registry, null);
}
public static Set registerAnnotationConfigProcessors(
        BeanDefinitionRegistry registry, @Nullable Object source) {

    DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
    if (beanFactory != null) {
        if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
            beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
        }
        if (!(beanFactory.getAutowireCandidateResolver() instanceof 
                ContextAnnotationAutowireCandidateResolver)) {
            beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
        }
    }

    Set beanDefs = new LinkedHashSet<>(8);

    if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
    }
    ......
}

上面是创建AnnotatedBeanDefinitionReader对象的代码,其中向Spring的ApplicationContext(即Registry)中注册了
ConfigurationClassPostProcessor类的BeanDefinition,ConfigurationClassPostProcessor就是用来处理
Springboot的配置注解的,ConfigurationClassPostProcessor是一个BeanFactoryPostProcessor(bean工厂后处理器)。

refreshContext(context)

private void refreshContext(ConfigurableApplicationContext context) {
    refresh(context);
    if (this.registerShutdownHook) {
        try {
            context.registerShutdownHook();
        }
        catch (AccessControlException ex) {
            // Not allowed in some environments.
        }
    }
}
protected void refresh(ApplicationContext applicationContext) {
    Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
    ((AbstractApplicationContext) applicationContext).refresh();
}
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // Prepare this context for refreshing.
        prepareRefresh();

        // Tell the subclass to refresh the internal bean factory.
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // Prepare the bean factory for use in this context.
        prepareBeanFactory(beanFactory);

        try {
            // Allows post-processing of the bean factory in context subclasses.
            postProcessBeanFactory(beanFactory);

            // Invoke factory processors registered as beans in the context.
            invokeBeanFactoryPostProcessors(beanFactory);
    
    ......
}

refreshContext()方法最后调用到AbstractApplicationContext的refresh()方法,refresh()方法中的
invokeBeanFactoryPostProcessors()方法是用来调用bean工厂后处理器,前面的ConfigurationClassPostProcessor
就是在这里起作用的。
invokeBeanFactoryPostProcessors()方法中会对BeanFactoryPostProcessor分类然后分别调用,这里就不细说了。
直接看ConfigurationClassPostProcessor中的代码。

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    int registryId = System.identityHashCode(registry);
    if (this.registriesPostProcessed.contains(registryId)) {
        throw new IllegalStateException(
                "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
    }
    if (this.factoriesPostProcessed.contains(registryId)) {
        throw new IllegalStateException(
                "postProcessBeanFactory already called on this post-processor against " + registry);
    }
    this.registriesPostProcessed.add(registryId);

    processConfigBeanDefinitions(registry);
}

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    List configCandidates = new ArrayList<>();
    String[] candidateNames = registry.getBeanDefinitionNames();

    for (String beanName : candidateNames) {
        BeanDefinition beanDef = registry.getBeanDefinition(beanName);
        if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
                ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
            if (logger.isDebugEnabled()) {
                logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
            }
        }
        else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
            configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
        }
    }

    // Return immediately if no @Configuration classes were found
    if (configCandidates.isEmpty()) {
        return;
    }

    // Sort by previously determined @Order value, if applicable
    configCandidates.sort((bd1, bd2) -> {
        int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
        int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
        return Integer.compare(i1, i2);
    });

    // Detect any custom bean name generation strategy supplied through the enclosing application context
    SingletonBeanRegistry sbr = null;
    if (registry instanceof SingletonBeanRegistry) {
        sbr = (SingletonBeanRegistry) registry;
        if (!this.localBeanNameGeneratorSet) {
            BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
            if (generator != null) {
                this.componentScanBeanNameGenerator = generator;
                this.importBeanNameGenerator = generator;
            }
        }
    }

    if (this.environment == null) {
        this.environment = new StandardEnvironment();
    }

    // Parse each @Configuration class
    ConfigurationClassParser parser = new ConfigurationClassParser(
            this.metadataReaderFactory, this.problemReporter, this.environment,
            this.resourceLoader, this.componentScanBeanNameGenerator, registry);

    Set candidates = new LinkedHashSet<>(configCandidates);
    Set alreadyParsed = new HashSet<>(configCandidates.size());
    do {
        parser.parse(candidates);
        parser.validate();

        Set configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
        configClasses.removeAll(alreadyParsed);

        // Read the model and create bean definitions based on its content
        if (this.reader == null) {
            this.reader = new ConfigurationClassBeanDefinitionReader(
                    registry, this.sourceExtractor, this.resourceLoader, this.environment,
                    this.importBeanNameGenerator, parser.getImportRegistry());
        }
        this.reader.loadBeanDefinitions(configClasses);
        alreadyParsed.addAll(configClasses);

        candidates.clear();
        if (registry.getBeanDefinitionCount() > candidateNames.length) {
            String[] newCandidateNames = registry.getBeanDefinitionNames();
            Set oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
            Set alreadyParsedClasses = new HashSet<>();
            for (ConfigurationClass configurationClass : alreadyParsed) {
                alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
            }
            for (String candidateName : newCandidateNames) {
                if (!oldCandidateNames.contains(candidateName)) {
                    BeanDefinition bd = registry.getBeanDefinition(candidateName);
                    if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
                            !alreadyParsedClasses.contains(bd.getBeanClassName())) {
                        candidates.add(new BeanDefinitionHolder(bd, candidateName));
                    }
                }
            }
            candidateNames = newCandidateNames;
        }
    }
    while (!candidates.isEmpty());

    // Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
    if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
        sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
    }

    if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
        // Clear cache in externally provided MetadataReaderFactory; this is a no-op
        // for a shared cache since it"ll be cleared by the ApplicationContext.
        ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
    }
}

processConfigBeanDefinitions()方法中String[] candidateNames = registry.getBeanDefinitionNames()先从ApplicationContext
中取出所有的BeanDefinition,然后循环遍历出有@Configuration注解的BeanDefinition,这里用到了AnnotatedElementUtils类的
isAnnotated()方法,可以递归的方式去找一个注解。如:@SpringBootApplication注解上还注解,会一直往里面找。注解上还有注解的方式
在Spring中很常见,Spring经常利用这一特性。

@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 {

}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {

}

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

}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {

}

找出有@Configuration注解的BeanDefinition后,开始解析解析这些配置。
parser.parse()方法就是来解析配置的。parser是ConfigurationClassParser类的对象,
ConfigurationClassParser的doProcessConfigurationClass()方法中有解析的主要逻辑。
分别对@Component注解、@PropertySource注解、@ComponentScan注解、@Import注解、@ImportResource注解、
@Bean注解进行解析。
spring.factories中的EnableAutoConfiguration类(各种自动配置类)就是通过@Import(AutoConfigurationImportSelector.class)
和@Import(AutoConfigurationPackages.Registrar.class)实现的。

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
        throws IOException {

    if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
        // Recursively process any member (nested) classes first
        processMemberClasses(configClass, sourceClass);
    }

    // Process any @PropertySource annotations
    for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
            sourceClass.getMetadata(), PropertySources.class,
            org.springframework.context.annotation.PropertySource.class)) {
        if (this.environment instanceof ConfigurableEnvironment) {
            processPropertySource(propertySource);
        }
        else {
            logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
                    "]. Reason: Environment must implement ConfigurableEnvironment");
        }
    }

    // Process any @ComponentScan annotations
    Set componentScans = AnnotationConfigUtils.attributesForRepeatable(
            sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
    if (!componentScans.isEmpty() &&
            !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
        for (AnnotationAttributes componentScan : componentScans) {
            // The config class is annotated with @ComponentScan -> perform the scan immediately
            Set scannedBeanDefinitions =
                    this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
            // Check the set of scanned definitions for any further config classes and parse recursively if needed
            for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
                BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
                if (bdCand == null) {
                    bdCand = holder.getBeanDefinition();
                }
                if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
                    parse(bdCand.getBeanClassName(), holder.getBeanName());
                }
            }
        }
    }

    // Process any @Import annotations
    processImports(configClass, sourceClass, getImports(sourceClass), true);

    // Process any @ImportResource annotations
    AnnotationAttributes importResource =
            AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
    if (importResource != null) {
        String[] resources = importResource.getStringArray("locations");
        Class readerClass = importResource.getClass("reader");
        for (String resource : resources) {
            String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
            configClass.addImportedResource(resolvedResource, readerClass);
        }
    }

    // Process individual @Bean methods
    Set beanMethods = retrieveBeanMethodMetadata(sourceClass);
    for (MethodMetadata methodMetadata : beanMethods) {
        configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
    }

    // Process default methods on interfaces
    processInterfaces(configClass, sourceClass);

    // Process superclass, if any
    if (sourceClass.getMetadata().hasSuperClass()) {
        String superclass = sourceClass.getMetadata().getSuperClassName();
        if (superclass != null && !superclass.startsWith("java") &&
                !this.knownSuperclasses.containsKey(superclass)) {
            this.knownSuperclasses.put(superclass, configClass);
            // Superclass found, return its annotation metadata and recurse
            return sourceClass.getSuperClass();
        }
    }

    // No superclass -> processing is complete
    return null;
}

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/75619.html

相关文章

  • SpringBoot原理深入篇

    摘要:启动原理和执行原理分析一的启动原理我们打开,注意看下面两个依赖我们第一步是继承了父项目,然后在添加启动器的依赖,项目就会自动给我们导入关于项目所需要的配置和。 上一篇我们看到,我们很轻松的完成了项目的构建,那么SpringBoot是如何做到的呢,在使用的使用又有哪些通用配置和注意事项呢? 其实SpringBoot给我们做了大量的自动配置功能,我们只需要引入对应的启动器就可以直接使用,作...

    gotham 评论0 收藏0
  • SpringBoot学习日记1 第一个SpringBoot程序

    摘要:学习日记简单的小程序配置引入依赖编写代码最后通过这两个简单的配置,就可以使用浏览器访问访问到这个页面了分析和主要的作用是告知渲染结果直接返回给调用者。数据相当于就是在中作为路由功能的注解。 SpringBoot学习日记 简单的Hello Word小程序 配置pom引入依赖 org.springframework.boot spring-boot-s...

    ybak 评论0 收藏0
  • springboot(二)——springboot自动配置解析

    摘要:前言用过的肯定很熟悉,它其中有个重要的特性,就是自动配置平时习惯的一些设置的配置作为默认配置。提倡无配置文件的理念,使用生成的应用完全不会生成任何配置代码与配置文件。 前言 用过springboot的肯定很熟悉,它其中有个重要的特性,就是自动配置(平时习惯的一些设置的配置作为默认配置)。springboot提倡无XML配置文件的理念,使用springboot生成的应用完全不会生成任何配...

    张率功 评论0 收藏0
  • SpringBoot源码分析系列(一)--核心注解

    摘要:用于主类上最最最核心的注解,表示这是一个项目,用于开启的各项能力。下面我们来分析一下这个注解的组成以及作用通过上面的代码我们可以看出来是一个组合注解,主要由和这三个注解组成的。通过源码可以看出也是一个组合注解。   SpringBoot项目一般都会有Application的入口类,入口类中会有main方法,这是一个标准的java应用程序的入口方法。@SpringBootApplicat...

    seanlook 评论0 收藏0
  • 深度剖析Spring Boot自动装配机制实现原理

    摘要:所以,所谓的自动装配,实际上就是如何自动将装载到容器中来。实际上在版本中,模块驱动注解的出现,已经有了一定的自动装配的雏形,而真正能够实现这一机制,还是在版本中,条件注解的出现。,我们来看一下的自动装配是怎么一回事。在前面的分析中,Spring Framework一直在致力于解决一个问题,就是如何让bean的管理变得更简单,如何让开发者尽可能的少关注一些基础化的bean的配置,从而实现自动装...

    不知名网友 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<