Интерпретация Spring построчно (3) - Секрет запуска весны с чистыми аннотациями!

Java

Создание не простое, пожалуйста, указывайте автора в начале перепечатки: Nuggets@小西子 + ссылка на источник~

Если вы хотите узнать больше об исходном коде Spring,Нажмите, чтобы перейти к остальной части построчного анализа серии Spring

Введение

В предыдущей статье мы говорили об использованииcontext:component-scanтег, после регистрации сканера, сканер может поместить целевой пакет в соответствующие условия фильтра (по умолчанию он регистрирует@ComponentаннотированныйAnnotationTypeFilter) класс, инкапсулированный вbeanDefinitionи зарегистрировался наIOCв контейнере.

И для@Configuration,@BeanПоддержка аннотаций, в прошлой статье как раз вкратце рассказывалось о том, что он зарегистрировалConfigurationClassPostProcessorизbeanDefinitionприбытьIOCВ контейнере речь пойдет в основном об этой статьеConfigurationClassPostProcessorПринцип работы.

2. ОBeanPostProcessorиBeanFactoryPostProcessor

SpringПри проектировании было оставлено много точек расширения, и эти зарезервированные точки расширения можно будет использовать позжеSpring/ Когда пользователям нужно добавить новые функции, им не нужно переходить к основному процессу. Эти конкретные точки расширения можно условно разделить на две категории:

BeanFactoryPostProcessor:BeanFactoryPostProcessorи его подчиненные интерфейсы, этот тип точки расширения в основном используется вSpringсвязанный с контейнеромвремя называется.

BeanPostProcessor:BeanPostProcessorи его подчиненные интерфейсы, этот тип точки расширения в основном используется вbeanвызывается в течение всего жизненного цикла.

и нашConfigurationClassPostProcessor:

public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor, xxx {}

public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
	void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;

}

может видеть нашConfigurationClassPostProcessorСлишкомBeanFactoryPostProcessorпроизводный класс.

три,BeanFactoryPostProcessorвремя вызова

Мы возвращаемся к моменту запуска контейнераrefresh()метод (конкретноAbstractApplicationContext#refresh):

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        prepareRefresh();
        // 上两篇已经讲过了,这里是xml解析的入口
        // Tell the subclass to refresh the internal bean factory
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        // 这里对beanFactory做了一些预处理,感兴趣的同学可以去看下,逻辑不复杂
        // Prepare the bean factory for use in this context.
        prepareBeanFactory(beanFactory);
        // 为子类预留的一个钩子方法
        // Allows post-processing of the bean factory in context subclasses.
        postProcessBeanFactory(beanFactory);
        // !!! 调用BeanFactoryPostProcessor,这一篇我们主要讲这里!!!
        // Invoke factory processors registered as beans in the context.
        invokeBeanFactoryPostProcessors(beanFactory);
        // skip ...        
    }
}

Видно, что вbeanFactoryКогда все будет готово, у нас есть очевидный метод для вызоваBeanFactoryPostProcessor, давайте нажмем, чтобы увидеть логику:

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
    // skip ... 
}

Как видите, мы делегировали конкретную логикуPostProcessorRegistrationDelegateЧтобы разобраться, вот на самом деле, чтобы зарегистрироватьсяPostProcessorЛогика вызова объединена в класс, давайте продолжим:

public static void invokeBeanFactoryPostProcessors(
    ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessorPostProcessors) {
	// 保存所有调用过的PostProcessor的beanName
    Set<String> processedBeans = new HashSet<>();
	// 如果这个beanFactory也是一个BeanDefinitionRegistry的话,我们也需要调用
    // BeanFactoryPostProcessor的子接口BeanDefinitionRegistryPostProcessor的方法
    // 正常都是会走这个分支的,因为我们默认的DefaultListableBeanFactory实现了BeanDefinitionRegistry接口
    if (beanFactory instanceof BeanDefinitionRegistry) {
        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
        // 这两个list主要用来分别收集BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor
        List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
        List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
		// 首先我们先把传入的BeanFactoryPostProcessor实例分类-beanFactory初始化的时候
        // 会注册一下实例到AbstractApplicationContext#beanFactoryPostProcessors这个列表,
        // 这个列表的值也是这个方法的第二个入参
        for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
            if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
                BeanDefinitionRegistryPostProcessor registryProcessor =
                    (BeanDefinitionRegistryPostProcessor) postProcessor;
                // 对于已经注册到spring的beanFactoryPostProcessors的registryProcessor,直接调用
                // 优先级最高
                registryProcessor.postProcessBeanDefinitionRegistry(registry);
                registryProcessors.add(registryProcessor);
            }
            else {
                regularPostProcessors.add(postProcessor);
            }
        }
        // 一个中间容器,用来保存当前需要调用的registryProcessor
        List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
        // 接下来我们需要先把我们注册的beanDefinition中实现了BeanDefinitionRegistryPostProcessor接口的类的名称找出来,
        // 注意,这里不会直接实例化这个bean(主要是这些PostProcessor需要按顺序初始化和调用,先调用的PostProcessor是可能对后初始化的bean造成影响的)
        String[] postProcessorNames =
            beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
        // 我们找出所有实现了PriorityOrdered接口的PostProcessor
        for (String ppName : postProcessorNames) {
            if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
                // 这里直接创建了这个registryProcessor的实例,并且加入当前需要处理的容器
                // beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)
                // 逻辑之后讲bean生命周期的时候会系讲,这里先略过
                currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                // 标记为已处理
                processedBeans.add(ppName);
            }
        }
        // 对所有实现了PriorityOrdered接口的PostProcessor排序
        sortPostProcessors(currentRegistryProcessors, beanFactory);
        // 加入registryProcessors列表
        registryProcessors.addAll(currentRegistryProcessors);
        // 调用BeanDefinitionRegistryPostProcessor的方法,我们的ConfigurationClassPostProcessor的逻辑就是在这里被调用的
        invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
        // 清除currentRegistryProcessors
        currentRegistryProcessors.clear();

        // 接下来处理实现了Ordered接口的BeanDefinitionRegistryPostProcessor
        // 逻辑就不讲了,跟上面是一样的
        postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
        for (String ppName : postProcessorNames) {
            if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
                currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                processedBeans.add(ppName);
            }
        }
        sortPostProcessors(currentRegistryProcessors, beanFactory);
        registryProcessors.addAll(currentRegistryProcessors);
        invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
        currentRegistryProcessors.clear();

        // 接下来处理普通的,没实现排序接口的BeanDefinitionRegistryPostProcessor
        // 需要主要的是,这里有一个循环,主要原因是BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)方法是传入了一个BeanDefinitionRegistry的
        // 这意味着我们可以在(实际上正常实现这个接口就是为了注册beanDefinition的)这个PostProcessor注册新的beanDefinition
        // 而新注册的beanDefinition对应的类也是可能实现BeanDefinitionRegistryPostProcessor接口的,所以这里需要循环处理,知道不会注册新的BeanDefinitionRegistryPostProcessor为止
        boolean reiterate = true;
        while (reiterate) {
            reiterate = false;
            postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
            for (String ppName : postProcessorNames) {
                if (!processedBeans.contains(ppName)) {
                    // 如果还有未处理的BeanDefinitionRegistryPostProcessor,则实例化它们
                    // 并且把标记需要在循环一次,因为之后新的BeanDefinitionRegistryPostProcessor的调用可能注册新的BeanDefinitionRegistryPostProcessor
                    currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                    processedBeans.add(ppName);
                    reiterate = true;
                }
            }
            // 排序、收集、调用一条龙
            sortPostProcessors(currentRegistryProcessors, beanFactory);
            registryProcessors.addAll(currentRegistryProcessors);
            invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
            currentRegistryProcessors.clear();
        }

        // 所有的BeanDefinitionRegistryPostProcessor都调用完了,接下来要调用BeanFactoryPostProcessor的逻辑了
        // 由于BeanDefinitionRegistryPostProcessor是继承BeanFactoryPostProcessor的,所以这里registryProcessors也需要调用
        invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
        invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
    }
    else {
        // 如果当前beanFactory没有实现BeanDefinitionRegistry接口,则这里只需要调用BeanFactoryPostProcessor的逻辑就行了
        invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
    }

    // 接下来是处理注册了beanDifinition而没有实例化的BeanFactoryPostProcessor的逻辑,这里逻辑和处理BeanDefinitionRegistryPostProcessor基本是一样的,都是按顺序初始化(PriorityOrdered->Ordered->None),然后排序,调用的流程
    // 只是没有了循环的逻辑,因为从设计上来讲,BeanFactoryPostProcessor#postProcessBeanFactory里不应当负责beanDefinition的注册的逻辑的,所以也不会产生新的beanDifinition,所以这里就不需要循环处理了。
    // 你当然可以在postProcessBeanFactory逻辑里把beanFactory强转成BeanDefinitionRegistry并且注册一些BeanFactoryPostProcessor的beanDefinition,导致这里解析不全,可是,我们为什么要跟自己过不去呢?
}

можно увидеть,invokeBeanFactoryPostProcessorsХотя кода больше, логика не сложная.

Мы первыми с этим разобралисьBeanDefinitionRegistryPostProcessorлогика, а порядок обработки таков:

  1. созданный экземплярregistryPostProcessor--> только зарегистрированыbeanDefinition, который еще не создал экземплярregistryPostProcessor
  2. для созданного экземпляраregistryPostProcessor, добавив их вAbstractApplicationContext#beanFactoryPostProcessorsпоследовательное выполнение списка
  3. для неустановленногоregistryPostProcessor, реализуемый согласноPriorityOrderedИнтерфейс --> реализованOrderedИнтерфейс --> Порядок нереализованного интерфейса сортировки Создание экземпляров, сортировка и выполнение пакетами. После выполнения одного пакета создайте экземпляр, отсортируйте и выполните следующий пакет.
  4. Наконец, обработайте те, которые не реализуют интерфейс сортировки.registryPostProcessor, должна быть циклическая обработка, чтобы гарантироватьregistryPostProcessorтолько что зарегистрированный в логикеregistryPostProcessorизbeanDefinitionтакже реализовано.

Далее мы имеем дело сBeanFactoryPostProcessorлогика, эта логика иBeanDefinitionRegistryPostProcessorЛогика обработки в основном такая же, но нет необходимости делать логику четвертого цикла.

во время звонкаinvokeBeanDefinitionRegistryPostProcessorsиinvokeBeanFactoryPostProcessorsКак именно это достигается?

private static void invokeBeanDefinitionRegistryPostProcessors(
    Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) {

    for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
        postProcessor.postProcessBeanDefinitionRegistry(registry);
    }
}

private static void invokeBeanFactoryPostProcessors(
    Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) {

    for (BeanFactoryPostProcessor postProcessor : postProcessors) {
        postProcessor.postProcessBeanFactory(beanFactory);
    }
}

Видно, что на самом деле это простой вызов цикла, тогдаAbstractApplicationContext#refreshсерединаinvokeBeanFactoryPostProcessorsЛогика здесь остановится.

Четыре,ConfigurationClassPostProcessorПринцип работы

Мы уже говорили раньше,@Configuration,@BeanФункция аннотации заключается вConfigurationClassPostProcessorподдержите, тогда посмотримConfigurationClassPostProcessorлогика.

Во-первых, давайте вспомнимConfigurationClassPostProcessorкогда ты зарегистрировалсяIOCКонтейнер, давайте разберем его сноваcontext:component-scanПри помечении, после создания сканера и сканирования класса, некоторые публичные компоненты зарегистрированы:

// ComponentScanBeanDefinitionParser#parse
public BeanDefinition parse(Element element, ParserContext parserContext) {
    String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
    basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
    String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
                                                              ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
	// 创建扫描器并扫描
    ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
    Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
    // 注册组件
    registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
    return null;
}

protected void registerComponents(
			XmlReaderContext readerContext, Set<BeanDefinitionHolder> beanDefinitions, Element element) {
    // ...
	// 是否开启注解,默认就是开启的
    boolean annotationConfig = true;
    if (element.hasAttribute(ANNOTATION_CONFIG_ATTRIBUTE)) {
        annotationConfig = Boolean.parseBoolean(element.getAttribute(ANNOTATION_CONFIG_ATTRIBUTE));
    }
    if (annotationConfig) {
        // 这里注册了一写支持注解的属性
        Set<BeanDefinitionHolder> processorDefinitions =
            AnnotationConfigUtils.registerAnnotationConfigProcessors(readerContext.getRegistry(), source);
		// ...
    }
    // ...
}

существуетAnnotationConfigUtils#registerAnnotationConfigProcessors, мы положилиConfigurationClassPostProcessorупаковано вbeanDefinitionи зарегистрировал его:

// 如果不存在beanName为AnnotationConfigUtils#CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME
// 的beanDefinition,则注册一个新的
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));
}

так какConfigurationClassPostProcessorреализуетсяBeanDefinitionRegistryPostProcessorинтерфейс, то мы объединяемAbstractApplicationContext#invokeBeanFactoryPostProcessorsЛогика может знать,beanFactoryПосле инициализации он сначала вызоветConfigurationClassPostProcessorизpostProcessBeanDefinitionRegistryметод, давайте посмотрим на логику этого метода:

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    // skip ...
    processConfigBeanDefinitions(registry);
}

Продолжайте следить:

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
    // 拿到当前registry中所有已经注册的beanDefinition的名称
    String[] candidateNames = registry.getBeanDefinitionNames();
	// 过滤所有的beanDefinition,把未被处理过的配置类收集起来
    for (String beanName : candidateNames) {
        BeanDefinition beanDef = registry.getBeanDefinition(beanName);
        // 如果beanDefinition中有这个ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE属性,说明这个beanDefinitiony是一个配置类,且已经被处理过了,不需要再处理
        if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
            // only log
        }
        // !!!判断当前beanDefinition是不是一个配置类,如果是,收集起来!!!
        else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
            // 如果是一个需要处理的配置类,加入列表
            configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
        }
    }
	// 完全没找到待处理的ConfigurationClass,就直接返回了
    if (configCandidates.isEmpty()) {
        return;
    }

    // 排序
    configCandidates.sort((bd1, bd2) -> {
        int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
        int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
        return Integer.compare(i1, i2);
    });

    // 这里主要是初始化一些工具类,环境变量之类的
    SingletonBeanRegistry sbr = null;
    if (registry instanceof SingletonBeanRegistry) {
        sbr = (SingletonBeanRegistry) registry;
        if (!this.localBeanNameGeneratorSet) {
            BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(
                AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
            if (generator != null) {
                this.componentScanBeanNameGenerator = generator;
                this.importBeanNameGenerator = generator;
            }
        }
    }
    if (this.environment == null) {
        this.environment = new StandardEnvironment();
    }

    // 创建一个ConfigurationClassParser
    ConfigurationClassParser parser = new ConfigurationClassParser(
        this.metadataReaderFactory, this.problemReporter, this.environment,
        this.resourceLoader, this.componentScanBeanNameGenerator, registry);

    Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
    Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
    do {
        // !!!委托给Parser来解析这些配置类!!!
        // 这里主要是把配置类上的注解信息解析封装成ConfigurationClass对象
        // 如果是配置类导入(入import/componentScan)的普通类(非配置类),将会在这里生成beanDefinition并注册
        parser.parse(candidates);
        // 校验扫描出来的beanDefinitionu是否合法,这里其实主要是校验
        // 1.proxyBeanMethods=true的情况下配置类是否可以重新(非final,需要生成cglib代理类)
        // 2.@Bean修饰的方法是否可以重写(非final,需要生成cglib代理类)
        parser.validate();
		
        Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
        configClasses.removeAll(alreadyParsed);
		// 初始化一个ConfigurationClassBeanDefinitionReader
        if (this.reader == null) {
            this.reader = new ConfigurationClassBeanDefinitionReader(
                registry, this.sourceExtractor, this.resourceLoader, this.environment,
                this.importBeanNameGenerator, parser.getImportRegistry());
        }
        // !!!把封装好的ConfigurationClass对象委托给BeanDefinitionReader处理!!!
        // 通过配置类加载注册beanDefinition
        this.reader.loadBeanDefinitions(configClasses);
        alreadyParsed.addAll(configClasses);

        candidates.clear();
        // 处理完ConfigurationClass后,可能会注册新的配置类,这里就是收集这些新注册的配置类的
        if (registry.getBeanDefinitionCount() > candidateNames.length) {
            String[] newCandidateNames = registry.getBeanDefinitionNames();
            Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
            Set<String> alreadyParsedClasses = new HashSet<>();
            for (ConfigurationClass configurationClass : alreadyParsed) {
                alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
            }
            for (String candidateName : newCandidateNames) {
                // 只有新注册的beanDefinition才需要处理
                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());

    // skip ...
}

Благодаря приведенному выше исходному коду мы в целом знаем процесс обработки классов конфигурации.Далее мы сосредоточимся на нескольких важных деталях.

1. Определите, является ли класс классом конфигурации

processConfigBeanDefinitionsВ методе мы в основном имеем дело с логикой класса конфигурации, так что же такое класс конфигурации?

Некоторые студенты могут захотеть сказать, разве это не просто, просто@ConfigurationАннотированный модифицированный класс!

Это утверждение верно, но неполно, так что давайте посмотримConfigurationClassUtils#checkConfigurationClassCandidateКак судить о классе - это класс конфигурации в методе:

public static boolean checkConfigurationClassCandidate(
    BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
    String className = beanDef.getBeanClassName();
    // 首先如果这个bean是通过factoryMethod来注册的,那它就不是一个配置类
    if (className == null || beanDef.getFactoryMethodName() != null) {
        return false;
    }

    AnnotationMetadata metadata;
    // skip ... 这里跳过了一些获取AnnotationMetadata的逻辑,我们只需要知道
    // 这个AnnotationMetadata能拿到类上的所有注解的信息就可以了
	// 获取类上@Configuration注解的属性
    Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
    // 有@Configuration注解且注解的proxyBeanMethods=true(这个是默认值)
    // 这里解释一下这个proxyBeanMethods属性,这个属性我之前也没有注意,看注解是说如果这个属性为true
    // 则这个配置类里被@Bean注解修饰的方法会被spring代理,使我们通过方法调用的时候获取到的实例也是属于spring管理的。
    if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
        // 注意这里在beanDefinition中塞入了CONFIGURATION_CLASS_ATTRIBUTE这个属性
        // 外面是通过判断这个是否有属性来确定某个beanDefinition是否是配置类
        beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
    }
    // 有@Configuration 或者 isConfigurationCandidate(metadata)
    else if (config != null || isConfigurationCandidate(metadata)) {
        beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
    }
    else {
        return false;
    }

    // 获取排序值
    Integer order = getOrder(metadata);
    if (order != null) {
        beanDef.setAttribute(ORDER_ATTRIBUTE, order);
    }

    return true;
}

Мы видим, что класс имеет@ConfigurationКласс действительно принадлежит к классу конфигурации, однако не существует@Configuration, пока

isConfigurationCandidateметод возвращаетtrue, также думайте, что этот класс является классом конфигурации, давайте взглянем на этот метод:

public static boolean isConfigurationCandidate(AnnotationMetadata metadata) {
    // Do not consider an interface or an annotation...
    if (metadata.isInterface()) {
        return false;
    }
    // 类上是否有被candidateIndicators列表中任一注解修饰?
    for (String indicator : candidateIndicators) {
        if (metadata.isAnnotated(indicator)) {
            return true;
        }
    }
    try {
        // 类中是否包含@Bean注解修饰的方法
        return metadata.hasAnnotatedMethods(Bean.class.getName());
    }
    catch (Throwable ex) {
        return false;
    }
}

тогда этоConfigurationClassUtils#candidateIndicatorsКакие аннотации включены?

private static final Set<String> candidateIndicators = new HashSet<>(8);
static {
    candidateIndicators.add(Component.class.getName());
    candidateIndicators.add(ComponentScan.class.getName());
    candidateIndicators.add(Import.class.getName());
    candidateIndicators.add(ImportResource.class.getName());
}

Тогда мы знаем, что когда класс@Configuration,@Component,@ComponentScan,@Import,@ImportResourceМодифицированный, или в классе@BeanПри аннотировании модифицированного методаspringДумайте об этом классе как о классе конфигурации (с@ComponentВ случае с аннотациями мы можем в основном думать, что большинствоbeanDefinitionбудет затронуто этоPostProcessorиметь дело с).

2. Проанализируйте информацию о конфигурации в классе конфигурации.

a.ConfigurationClassструктура

Мы только что видели,springИнформация класса конфигурации анализируется и инкапсулируется вConfigurationClassВ объекте, то давайте сначала посмотрим на структуру этого класса:

final class ConfigurationClass {
	// 配置类的注解信息
    private final AnnotationMetadata metadata;
	
    private final Resource resource;

    @Nullable
    private String beanName;
	// 当前类是哪个配置类导入的
    private final Set<ConfigurationClass> importedBy = new LinkedHashSet<>(1);
	// 这个配置类中被@Bean注解标记的方法
    private final Set<BeanMethod> beanMethods = new LinkedHashSet<>();
	// 配置类上的@ImportResource 注解中的消息,配置文件地址-对应处理器Class
    private final Map<String, Class<? extends BeanDefinitionReader>> importedResources =
        new LinkedHashMap<>();
	// 配置类上的@Import 注解导入的类,如果是实现了ImportBeanDefinitionRegistrar接口,将会封装到这里
    private final Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> importBeanDefinitionRegistrars =
        new LinkedHashMap<>();
    
    // 这里重新了equals和hashCode方法,只要配置类的全类名相等这边就认为两个对象一致了。
    @Override
    public boolean equals(@Nullable Object other) {
        return (this == other || (other instanceof ConfigurationClass &&
                                  getMetadata().getClassName().equals(((ConfigurationClass) other).getMetadata().getClassName())));
    }
    @Override
    public int hashCode() {
        return getMetadata().getClassName().hashCode();
    }
}
b.ConfigurationClassParser#processConfigurationClassОбработка записи класса конфигурации

ConfigurationClassParser#parseПосле простой инкапсуляции в методе он перейдет кConfigurationClassParser#processConfigurationClass, посмотрим непосредственно на логику этого метода:

private final Map<ConfigurationClass, ConfigurationClass> configurationClasses = new LinkedHashMap<>();
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
    // 这里是判断@Condition那些,看是否需要跳过
    if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
        return;
    }
	// 看一下这个配置类是否已经解析过了,configurationClasses是一个Map
    // 这里相当于是通过配置类的类名去获取配置类的封装信息的
    ConfigurationClass existingClass = this.configurationClasses.get(configClass);
    if (existingClass != null) {
        // 如果已经解析过,会做一些处理,这里可能会直接返回,即本次就不再解析了
        // 这里的处理逻辑不太重要,我们不看了
    }

    // 这里把configClass又包装成了一个SourceClass, 这个filter的值默认是 DEFAULT_EXCLUSION_FILTER,意思就是这两个包内的类在解析的时候会被排除
    // private static final Predicate<String> DEFAULT_EXCLUSION_FILTER = className ->(className.startsWith("java.lang.annotation.") || className.startsWith("org.springframework.stereotype."));
    SourceClass sourceClass = asSourceClass(configClass, filter);
    // 处理配置类时,处理完当前类之后,还会往上处理它的父类,直到父类是Object就不再处理了
    // 这个循环就是做这个作用的
    do {
        // 处理配置类的逻辑,配置类中的信息将会被封装到configClass
        sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
    }
    while (sourceClass != null);
    // 把解析后的配置类信息储存到configurationClasses,这里这个key可以认为就是一个类名
    this.configurationClasses.put(configClass, configClass);
}

Реальная логика обработки все еще тамdoProcessConfigurationClass, продолжаем:

protected final SourceClass doProcessConfigurationClass(
    ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
    throws IOException {

    if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
        // 如果配置类被@Component修饰,先处理内部类
        processMemberClasses(configClass, sourceClass, filter);
    }

    // 这里是处理配置类上的@PropertySources注解的
    // 简单来说就是把properties文件中的内容加载到内存中的Environment中了
	// 我们不细看
    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");
        }
    }

    // 这里开始是处理类上的@ComponentScan注解的逻辑
    Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
        sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
    if (!componentScans.isEmpty() &&
        !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
        // 循环处理每一个注解(可以用@ComponentScans包装多个注解-jdk<java8,或者直接打上多个@ComponentScan注解-jdk>=java8)
        for (AnnotationAttributes componentScan : componentScans) {
            // 委托给componentScanParser处理,这里处理完之后返回了一批已注册的BeanDefinition
            // 这里的parse逻辑其实就是创建了一个扫描器并且进行扫描,毕竟这个注解就是做这个事的。
            // 我们之后会看一下
            Set<BeanDefinitionHolder> scannedBeanDefinitions =
                this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
            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());
                }
            }
        }
    }

    // 处理@Import注解
    processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

    // 处理@ImportResource注解
    AnnotationAttributes importResource =
        AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
    if (importResource != null) {
        String[] resources = importResource.getStringArray("locations");
        // 这个值默认是 BeanDefinitionReader.class
        Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
        for (String resource : resources) {
            String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
            // 把@ImportResource注解上的信息封装到configClass
            configClass.addImportedResource(resolvedResource, readerClass);
        }
    }

    // 处理有@Bean注解的方法
    // 这里找到类里所有有@Bean注解修饰的方法
    Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
    for (MethodMetadata methodMetadata : beanMethods) {
        // 封装成BeanMethod并且也放入configClass
        configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
    }
    // 由于java8之后接口也可以有默认方法(default修饰的方法)
    // 这里会找到所有接口中被@Bean修饰的非抽象的方法,也封装成BeanMethod放入configClass
    processInterfaces(configClass, sourceClass);

    // 如果有父类的话,会返回父类的sourceClass,继续在外层循环中解析
    // 并且把解析的信息封装到当前这个configClass
    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();
        }
    }

    // 没有父类了就处理完了
    return null;
}

Так как аннотаций для обработки много, логика здесь относительно сложная, поэтому давайте поговорим о них по порядку.

в. Обращение@Componentаннотация

ВходитьdoProcessConfigurationClassметод, мы сначала будем иметь дело с@Componentаннотация:

if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
    // 如果配置类被@Component修饰,先处理内部类
    processMemberClasses(configClass, sourceClass, filter);
}

Давайте взглянемprocessMemberClassesЛогика:

private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass,
                                  Predicate<String> filter) throws IOException {
	// 获取所有的内部类
    Collection<SourceClass> memberClasses = sourceClass.getMemberClasses();
    if (!memberClasses.isEmpty()) {
        List<SourceClass> candidates = new ArrayList<>(memberClasses.size());
        // 循环处理每个内部类
        for (SourceClass memberClass : memberClasses) {
			// 如果内部类也是一个配置类,且内部类与当前类不一致(其实我也不知道为什么会有这种情况?)
    	    // 则加入待处理的配置类列表
            if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) &&
                !memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) {
                candidates.add(memberClass);
            }
        }
        // 对待处理的配置类排序
        OrderComparator.sort(candidates);
        // 循环按顺序处理每个配置类
        for (SourceClass candidate : candidates) {
            // 这里是判断是否有循环import的配置类的,如果有循环导入会直接报错
            if (this.importStack.contains(configClass)) {
                this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
            }
            else {
                this.importStack.push(configClass);
                try {
                    // 把每一个内部类也需要处理一下,这里其实又回到上层了,是一个递归
                    processConfigurationClass(candidate.asConfigClass(configClass), filter);
                }
                finally {
                    this.importStack.pop();
                }
            }
        }
    }
}

Как видите, обработка класса конфигурации@ComponentAnnotation, по сути, состоит в том, чтобы получить все внутренние классы в классе конфигурации, и разобрать в нем класс конфигурации, то есть вызватьprocessConfigurationClassметод.

г. Обработка@ComponentScanаннотация

@ComponentScanРоль состоит в том, чтобы сканировать в классе@ComponentОбратите внимание, эта функция не имеет отношения к намcontext:component-scanЭтикетка работает так же? На самом деле их функции на этапе сканирования действительно одинаковы. Как видите, мы имеем дело с@ComponentScanПри аннотировании он делегируетсяcomponentScanParserОбработанный:

Set<BeanDefinitionHolder> scannedBeanDefinitions =
    this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());

Давайте посмотрим на этоComponentScanAnnotationParser#parseЛогика:

public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
    // 第一行就创建了一个扫描器Scanner
    ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
                                                                                componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);

    // skip ... 中间就是解析了@ComponentScan注解中的信息,并通过这些配置信息配置scanner的属性
    // 这些信息其实跟自定义标签context:component-scan中的属性/子标签是对应的,有兴趣的同学可以看一下我的上一篇博客
	// 新增了一个ExcludeFilter,意思就是扫描的时候就不需要处理当前类了-毕竟当前类已经在处理了。
    scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
        @Override
        protected boolean matchClassName(String className) {
            return declaringClass.equals(className);
        }
    });
    // 扫描器扫描~
    return scanner.doScan(StringUtils.toStringArray(basePackages));

Ха-ха, видите ли, вот сканер создал, а потом просканировал, логика следует за намиcontext:component-scanПроцесс сканирования такой же, и здесь он не рассматривается.

Следует отметить, что сканер сканируетbeanDifinition, если это также класс конфигурации, он вызоветparseМетод анализирует этот класс конфигурации и, наконец, переходит кprocessConfigurationClassметодОбрабатывать:

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());
    }
}
е. Обработка@Importаннотация

@ImportАннотации обычно используются для представления третьих лиц.jarупаковать класс вspringконтейнер, конечно@beanтоже имеет эту функцию, но@Importи@BeanРазница в том,@Importиспользуется для модификации класса,springполно@EnableXxxАннотация через@ImportФункция реализована, теперь посмотрим на процесс ее обработки:

processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

getImports()используется для сбора всех@importДля импортированных классов давайте взглянем на эту логику коллекции:

private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
    //  用来保存import导入的类
    Set<SourceClass> imports = new LinkedHashSet<>();
    // 用来标记哪些类是已经处理过的
    Set<SourceClass> visited = new LinkedHashSet<>();
    // 递归收集
    collectImports(sourceClass, imports, visited);
    return imports;
}

private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
    throws IOException {
	// 把当前类标记为已处理过
    if (visited.add(sourceClass)) {
        // 拿到并循环处理类上的所有注解
        for (SourceClass annotation : sourceClass.getAnnotations()) {
            String annName = annotation.getMetadata().getClassName();
            // 如果当前类上的这个注解不是@Import
            if (!annName.equals(Import.class.getName())) {
                // 则继续递归收集这个注解上的注解(有点绕...)
                collectImports(annotation, imports, visited);
            }
        }
        // 把当前类上的所有@Import注解的value属性(即import的Class<?>)封装成sourceClass
        // 并且加入收集到的容器
        imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
    }
}

Логика здесь может быть немного окольной, но ее функция состоит в том, чтобы поместить@ImportИнформация об аннотациях собирается, а аннотации к аннотациям собираются рекурсивно, например, наша@EnableAsyncаннотация:

@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {}

@Configuration
@EnableAsync
public class Test {}

Мы собираемTestВверх@ImportПри аннотировании информации введите в первый разcollectImportsметод,sourceClass=Test, на этот раз помимо сбораTestкласс@ImportВ дополнение к аннотационной информации вы также получитеTestДругие аннотации к классу, например здесь@EnableAsyncаннотировать, а затем поставить@EnableAsyncИнформация о классе аннотации какsourceClass(которыйsourceClass=EnableAsync) продолжать звонитьcollectImportsрекурсивная коллекция методов@ImportАннотационная информация, на этот разEnableAsyncВверх@Import(AsyncConfigurationSelector.class)Собирается аннотационная информация.

Идем дальше, смотрим на коллекцию@ImportКак обрабатывается ~ после импортированного класса:

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
                            Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
                            boolean checkForCircularImports) {
	// 这里把循环导入的处理逻辑和异常处理逻辑去掉了    
	// 循环每一个@Import导入的类
    for (SourceClass candidate : importCandidates) {
        if (candidate.isAssignable(ImportSelector.class)) {
            // 如果实现了ImportSelector接口
			// 这里把这个ImportSelector实例化了
            Class<?> candidateClass = candidate.loadClass();
            ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
                                                                           this.environment, this.resourceLoader, this.registry);
            Predicate<String> selectorFilter = selector.getExclusionFilter();
            if (selectorFilter != null) {
                exclusionFilter = exclusionFilter.or(selectorFilter);
            }
            // 这里判断是否是延迟导入,如果是延迟导入的话,会在parse方法中,所有配置类都处理完之后再处理,有兴趣的同学可以自己看一下
            // 具体代码在ConfigurationClassParser#parse(java.util.Set<org.springframework.beans.factory.config.BeanDefinitionHolder>)
            if (selector instanceof DeferredImportSelector) {
                this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
            }
            else {
                // 如果是一个普通的ImportSelector
                String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
                // 调用ImportSelector.selectImports方法,将获取到的类名作为参数递归调用当前方法
                // 也就是说这个ImportSelector接口和@Import注解实现的功能是一样的
                // 估计@Import是spring支持注解之后对ImportSelector接口做的注解版吧
                processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
            }
        }
        else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
            // 如果导入的类实现了ImportBeanDefinitionRegistrar接口
            // 这里也会把这个类先实例化
            Class<?> candidateClass = candidate.loadClass();
            ImportBeanDefinitionRegistrar registrar =
                ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
                                                     this.environment, this.resourceLoader, this.registry);
            // 然后加入到当前配置类的属性中了
            configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
        }
        else {
            // 如果导入的类既没实现ImportSelector接口,又没实现ImportBeanDefinitionRegistrar接口
            // 则认为是一个普通的配置类,进行配置类的处理逻辑
            processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
        }
    }
}

Как видите, для импортированных классов есть три логики обработки:

  1. ДостигнутоImportSelectorИмпорт класса интерфейса
    • перечислитьImportSelector.selectImportsметод, который рекурсивно вызывает метод, в данный момент обрабатывающий импортированный класс, с полученным именем класса в качестве параметра
    • ты мог бы так сказать@Importверсия интерфейса
  2. выполнитьImportBeanDefinitionRegistrarинтерфейс
    • создан какImportBeanDefinitionRegistrarПосле объекта поставьте текущийconfigClassсередина
    • ImportBeanDefinitionRegistrarИнтерфейс имеет входящийBeanDefinitionRegistryизregisterBeanDefinitionsметод, нетрудно догадаться, что этот интерфейс можно использовать для регистрацииbeanDefinitionДа, этот метод должен быть только вызван.
  3. общий класс
е. Обработка@ImportResourceаннотация

@ImportResourceфункция импортаspringизxmlФайлы конфигурации, обычно используемые для доступа к некоторым более старым, используютxmlопределениеbeanсторонняя библиотека.

AnnotationAttributes importResource =
    AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
if (importResource != null) {
    String[] resources = importResource.getStringArray("locations");
    Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
    for (String resource : resources) {
        String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
        // 这里是直接把配置文件路径和处理类类型放入了configClass对象
        configClass.addImportedResource(resolvedResource, readerClass);
    }
}
г. лечение@Beanаннотация

@BeanАннотации могут быть наиболее часто используемыми аннотациями в этом наборе аннотаций, и мы часто используем их, чтобы представить некоторые классы сторонних библиотек, чтобы сделатьspringуправлять. Давайте взглянемparseПоэтапная логика его обработки:

// 收集当前类里所有有@bean注解的方法
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
    // 封装成BeanMethod对象,加入configClass
    configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}

// 处理当前类的接口,接口是可能有有默认实现(jdk>=1.8)的@Bean修饰方法
processInterfaces(configClass, sourceClass);

в коллекции@BeanМетод модификации аннотации на самом деле очень прост, мы можем получить его через отражение, ноspringДля того, чтобы определить последовательность этих методов обработки, использованиеasmМетоды байт-кода для получения порядка, в котором методы объявляются в классе:

private Set<MethodMetadata> retrieveBeanMethodMetadata(SourceClass sourceClass) {
    AnnotationMetadata original = sourceClass.getMetadata();
    // 获取所有被@Bean修饰的方法
    Set<MethodMetadata> beanMethods = original.getAnnotatedMethods(Bean.class.getName());
    if (beanMethods.size() > 1 && original instanceof StandardAnnotationMetadata) {
        // Try reading the class file via ASM for deterministic declaration order...
        // Unfortunately, the JVM's standard reflection returns methods in arbitrary
        // order, even between different runs of the same application on the same JVM.
        // 这里注释是说由于jvm返回的方法列表顺序不能保证,这里尝试使用asm字节码技术拿到方法在类中的声明顺序,以此来为这些被@Bean修饰的方法排序
        try {
            AnnotationMetadata asm =
                this.metadataReaderFactory.getMetadataReader(original.getClassName()).getAnnotationMetadata();
            Set<MethodMetadata> asmMethods = asm.getAnnotatedMethods(Bean.class.getName());
            // 排序
            if (asmMethods.size() >= beanMethods.size()) {
                Set<MethodMetadata> selectedMethods = new LinkedHashSet<>(asmMethods.size());
                for (MethodMetadata asmMethod : asmMethods) {
                    for (MethodMetadata beanMethod : beanMethods) {
                        if (beanMethod.getMethodName().equals(asmMethod.getMethodName())) {
                            selectedMethods.add(beanMethod);
                            break;
                        }
                    }
                }
                if (selectedMethods.size() == beanMethods.size()) {
                    // All reflection-detected methods found in ASM method set -> proceed
                    beanMethods = selectedMethods;
                }
            }
        }
        catch (IOException ex) {
            logger.debug("Failed to read class file via ASM for determining @Bean method order", ex);
            // No worries, let's continue with the reflection metadata we started with...
        }
    }
    return beanMethods;
}

Давайте посмотрим на логику обработки интерфейса:

private void processInterfaces(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
    for (SourceClass ifc : sourceClass.getInterfaces()) {
        // 获取接口上所有被@Bean修饰的方法
        Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(ifc);
        for (MethodMetadata methodMetadata : beanMethods) {
            if (!methodMetadata.isAbstract()) {
                // A default method or other concrete method on a Java 8+ interface...
                // 只有不是抽象的方法才封装成BeanMethod加入configClass
                configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
            }
        }
        // 递归处理
        processInterfaces(configClass, ifc);
    }
}
h.parseКраткое описание метода

можно увидеть,parser.parse(candidates)Логика обработки довольно сложна. Этот метод в основном обрабатывает аннотации, которые мы обычно используем, и он содержит много логики рекурсивных вызовов. Я также прочитал его много раз, прежде чем понял. Заинтересованные студенты могут захотеть прочитать больше несколько раз.

3. Зарегистрируйтесь, настроив загрузку классовbeanDefinition

После анализа класса конфигурации нам нужно загрузить регистрацию через информацию о классе конфигурации.beanDefinition:

parser.parse(candidates);
parser.validate();
// 获取所有解析出来的配置类
Set<ConfigurationClass> 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());
}
// 加载注册beanDefinition
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);

Пойдем прямоloadBeanDefinitionsЛогика:

public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
    TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
    for (ConfigurationClass configClass : configurationModel) {
        // 循环解析
        loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
    }
}

private void loadBeanDefinitionsForConfigurationClass(
    ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {

    if (trackedConditionEvaluator.shouldSkip(configClass)) {
        // skip ...
    }

    if (configClass.isImported()) {
        // 如果是导入的配置类,先把自己的beanDefinition注册到spring
        // 里面就是一个简单的beanDefinition封装注册的流程,我们就不看了
        registerBeanDefinitionForImportedConfigurationClass(configClass);
    }
    for (BeanMethod beanMethod : configClass.getBeanMethods()) {
        // 从beanMethod注册beanDefinition
        loadBeanDefinitionsForBeanMethod(beanMethod);
    }
	// 从导入的配置文件注册beanDefinition
    loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
    // 从导入的ImportBeanDefinitionRegistrar注册beanDefinition
    loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}

Как видите, загружаем регистрациюbeanDefinitionЛогика вполне ясна, в основном это инкапсулировать нашConfigurationClassИнформация в объекте загружается одна за другой. Давайте рассмотрим каждый из них один за другим.

пропускBeanMethodЗагрузить регистрациюbeanDefinition

BeanMethodОбъект находится в классе конфигурации, который@BeanМетод, модифицированный аннотацией, инкапсулирован, рассмотрим логику его обработки:

private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
    // 我这里把代码简化了一下,因为一个beanDefinition的封装过程,无非是把@Bean注解中的信息获取封装一遍
    // 那些信息的封装我们在讲解xml标签的时候已经讲过了,这些属性都是一一对应的
    // 这里我只把我们需要额外关注的地方写出来了 -- 即我们一般意义上说的@Bean的实现原理
    ConfigurationClass configClass = beanMethod.getConfigurationClass();
    MethodMetadata metadata = beanMethod.getMetadata();
    // skip ...
    // 新建一个ConfigurationClassBeanDefinition
    ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata);
    // skip ...
    // !!!关键点!!!
    if (metadata.isStatic()) {
        // 如果是静态的@Bean方法,需要设置beanClass/beanClassName,用于在bean初始化时调用
        if (configClass.getMetadata() instanceof StandardAnnotationMetadata) {
            beanDef.setBeanClass(((StandardAnnotationMetadata) configClass.getMetadata()).getIntrospectedClass());
        }
        else {
            beanDef.setBeanClassName(configClass.getMetadata().getClassName());
        }
        // 设置FactoryMethodName
        beanDef.setUniqueFactoryMethodName(methodName);
    }
    else {
        // 如果是非静态的@Bean方法,还需要设置工厂类的beanName
        beanDef.setFactoryBeanName(configClass.getBeanName());
        // 设置FactoryMethodName
        beanDef.setUniqueFactoryMethodName(methodName);
    }
    // skip ...
    // 注册beanDefinition
    this.registry.registerBeanDefinition(beanName, beanDefToRegister);
}

Как видите, наш@BeanсозданныйbeanDefinition, с обычнымbeanDefinitionОтличие в том, что он установленfactoryMethodName, то есть он говорит, что с помощьюxmlв путиfactory-bean,factory-methodфункция этикетки для достиженияbeanсоздано!

б. пройти@ImportedResourceРегистрация загрузки информации аннотацииbeanDefinition

@ImportedResourceАннотация содержит путь к импортируемому файлу, а также файлReaderинформация, поэтому мы загружаем регистрациюbeanDefinition, но и из этой информации:

private void loadBeanDefinitionsFromImportedResources(
			Map<String, Class<? extends BeanDefinitionReader>> importedResources) {
	// reader实例缓存
    Map<Class<?>, BeanDefinitionReader> readerInstanceCache = new HashMap<>();
	// 循环处理<配置文件路径-reader>
    importedResources.forEach((resource, readerClass) -> {
        // 如果注解配置的Reader是默认的(我们一般其实也不改)
        if (BeanDefinitionReader.class == readerClass) {
            if (StringUtils.endsWithIgnoreCase(resource, ".groovy")) {
                // 如果文件名是.groovy结尾,则使用GroovyBeanDefinitionReader
                // 说实话我也第一次知道还可以用groovy脚本来做spring的配置文件
                // 后面我去看了一下BeanDefinitionReader这个接口的实现类,发现还一个
                // PropertiesBeanDefinitionReader,感兴趣的同学可以去研究一下
                readerClass = GroovyBeanDefinitionReader.class;
            }
            else {
                // 默认情况下我们使用XmlBeanDefinitionReader 
                // 有没有点眼熟这个类?xml配置解析的时候就是用的它呀
                readerClass = XmlBeanDefinitionReader.class;
            }
        }
		// 先从缓存拿
        BeanDefinitionReader reader = readerInstanceCache.get(readerClass);
        if (reader == null) {
            try {
                // 拿不到就新建一个,配置的reader类必须有一个只有BeanDefinitionRegistry参数的构造器
                reader = readerClass.getConstructor(BeanDefinitionRegistry.class).newInstance(this.registry);
                // Delegate the current ResourceLoader to it if possible
                if (reader instanceof AbstractBeanDefinitionReader) {
                    AbstractBeanDefinitionReader abdr = ((AbstractBeanDefinitionReader) reader);
                    abdr.setResourceLoader(this.resourceLoader);
                    abdr.setEnvironment(this.environment);
                }
                readerInstanceCache.put(readerClass, reader);
            }
            catch (Throwable ex) {
                throw new IllegalStateException(
                    "Could not instantiate BeanDefinitionReader class [" + readerClass.getName() + "]");
            }
        }
		// 使用reader从文件加载bean
        reader.loadBeanDefinitions(resource);
    });
}

По умолчанию (в большинстве случаев мы сами не настраиваемBeanDefinitionReader) Создайте одинXmlBeanDefinitionReaderдля анализа нагрузки, определенной в нашем файле конфигурацииbeanДа, это с намиxmlСодержимое секции парсинга такое же, поэтому здесь она обсуждаться не будет.

в. пройти@ImportАннотированный импортImportBeanDefinitionRegistrarрегистрация загрузки классаbeanDefinition

когда@ImportИмпортированный класс имеет реализациюImportBeanDefinitionRegistrarКогда мы будем взаимодействовать, мы соберем класс и не будем работать с ним до этого момента:

private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
    registrars.forEach((registrar, metadata) ->
                       // 直接调用registerBeanDefinitions方法
                       registrar.registerBeanDefinitions(metadata, this.registry, this.importBeanNameGenerator));
}

Логика обработки также очень проста, то есть вызывать напрямуюImportBeanDefinitionRegistrar.registerBeanDefinitionsметод, проводитьbeanDefinitionРегистрация.

В этом месте, по сути, весьConfigurationClassPostProcessorМы закончили с логикой.springэто через этоPostProcessorпредусмотрено@Bean,@Importи т.д. общийIOCПоддержка аннотаций.

Пять, начало чистой аннотацииspring

Мы говорили только чтоConfigurationClassPostProcessorПоддержка аннотаций, но этот компонент действительно разрешен к веснеxmlв файле конфигурацииcontext:component-scanОн был введен при внедрении метки, и пусть студенты его вспомнят (логика поиска класса обработки через пользовательскую метку здесь повторяться не будет, а заинтересованные студенты могут прочитать мой последний пост в блоге):

public BeanDefinition parse(Element element, ParserContext parserContext) {
    // 获取标签上配置并处理的base-package属性
    String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
    // 处理占位符
    basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
    // 最终获取到的是一个数组 - 因为我们配置的时候是可以配置多个的
    String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
                                                              ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);

    // 获取一个扫描器 - 这个东西很重要,我们以后还会看到
    ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
    // 嗯,扫描器进行扫描,看来就是这个方法会扫描那些注解了
    Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
    // !!!注册一些组件!!! 就是这里注入的ConfigurationClassPostProcessor
    registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
    return null;
}

protected void registerComponents(
    XmlReaderContext readerContext, Set<BeanDefinitionHolder> beanDefinitions, Element element) {
    // skip ...
    // 默认是true
    if (annotationConfig) {
        // 注意这个AnnotationConfigUtils.registerAnnotationConfigProcessors
        Set<BeanDefinitionHolder> processorDefinitions =
            AnnotationConfigUtils.registerAnnotationConfigProcessors(readerContext.getRegistry(), source);
        // skip ...
    }
    // skip ...
}
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
    BeanDefinitionRegistry registry, @Nullable Object source) {
    // skip ...
    if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
        // 就是里注册了一个ConfigurationClassPostProcessor
        RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
    }
	// skip ...
}

Как видите, мы разбираемcontext:component-scanКогда лейбл, наконец, называетсяAnnotationConfigUtils.registerAnnotationConfigProcessorsМетод регистрирует пакет компонентов, поддерживающих аннотации с помощью spring, включаяConfigurationClassPostProcessor.

Затем возникает проблема, так как я хочу определить ее через аннотации, объявитьbean, то почему я должен иметьxmlфайл, но и для разбораxmlсередина

context:component-scanА этикетки? Есть ли чистый способ аннотации, который позволил бы мне начатьspringШерстяная ткань?

Конечно, да, но в настоящее время мы должны использоватьAnnotationConfigApplicationContextначатьspring.

1. ИспользуйтеAnnotationConfigApplicationContextзапускатьspring

Сначала определим бизнес-класс:

@Data
@Service
public class MyAnnoClass {
    public String username = "xiaoxizi";
}

затем используйтеAnnotationConfigApplicationContextзапускать:

@Test
public void test() {
    applicationContext = new AnnotationConfigApplicationContext("com.xiaoxizi.spring");
    MyAnnoClass myAnnoClass = applicationContext.getBean(MyAnnoClass.class);
    System.out.println(myAnnoClass);
}

результат операции:

MyAnnoClass(username=xiaoxizi)

springСтарт прошел успешно, и текущие результаты соответствовали нашим ожиданиям.MyAnnoClassдействительно былоspringудалось.

2. AnnotationConfigApplicationContextЧистый старт аннотацииspringПринципиальный анализ

Без лишних слов, давайте проанализируем междуAnnotationConfigApplicationContextПоддержка чистого запуска аннотацийspringпринцип.

Давайте сначала посмотримAnnotationConfigApplicationContextконструктор:

public AnnotationConfigApplicationContext(String... basePackages) {
    // 调用自己的无参构造器
    this();
    // 这个scan,看着就像扫描的意思啊
    scan(basePackages);
    // 这个refresh就是我们常说的spring启动的核心流程了
    refresh();
}
// 看一下无参构造器
public AnnotationConfigApplicationContext() {
    // 创建一个AnnotatedBeanDefinitionReader
    this.reader = new AnnotatedBeanDefinitionReader(this);
    // 创建一个ClassPathBeanDefinitionScanner
    this.scanner = new ClassPathBeanDefinitionScanner(this);
}

Как видите, наше творениеAnnotationConfigApplicationContextобъект, создать

AnnotatedBeanDefinitionReaderиClassPathBeanDefinitionScanner. подожди, этоClassPathBeanDefinitionScannerЭто кажется немного знакомым? это не наш анализcontext:component-scanСканер, который создавался при маркировке? Затем конструктор вызываетscanРазве логика метода не...?

@Override
public void scan(String... basePackages) {
    Assert.notEmpty(basePackages, "At least one base package must be specified");
    // 使用扫描器扫描
    this.scanner.scan(basePackages);
}

Ты угадал, одноклассник, просканируй соответствующий пакет через сканер (╹▽╹). иcontext:component-scanЭтикетки обрабатываются таким же образом.

Итак, пока что мы можемbasePackageпод одеялом@ComponentДекорированный класс, в котором зарегистрирован пакет сканированияspringДа, но мы, кажется, еще не в состоянии справиться с этим@BeanДождитесь этикетки, ведь я не видел, куда вкалыватьConfigurationClassPostProcessor.

В этот момент нужно оглянуться.AnnotationConfigApplicationContextВ конструкторе есть только одно действие, с которым мы не знакомы, а именно созданиеAnnotatedBeanDefinitionReader, то давайте посмотрим на логику построения этого класса:

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
    this(registry, getOrCreateEnvironment(registry));
}

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
    this.registry = registry;
    this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
    // !!! 注册支持注解的组件 !!!
    AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}

Ладно, дело раскрыто, оказалосьAnnotatedBeanDefinitionReaderКогда он будет создан, он будет называтьсяAnnotationConfigUtils.registerAnnotationConfigProcessorsМетод регистрирует компонент, поддерживающий аннотацию с помощью spring, среди которых естьConfigurationClassPostProcessor. Так что мы можем с радостью начать использовать чистые аннотации.springЛа.

6. Резюме

Этот пост в блоге отxmlМетод синтаксического анализа говорит о чистом методе аннотации для началаspring, и в пареConfigurationClassPostProcessorАнализ исходного кода , понятьspringКак поддерживать аннотации.

уже,springв процессе запускаbeanDefinitionЗагрузка, парсинг и регистрация завершены, об этом мы поговорим позже.springПроцесс запуска, включая создание экземпляра класса singleton, жизненный цикл и т. д.

В целом написание поста в блоге кажется довольно многословным, и, по оценкам, мало кто его читает, но старайтесь придерживаться его самостоятельно.Процесс написания также является хорошей сортировкой знаний.Писать сложно много вещей, когда вы не знаете много об этом.Таким образом, вы можете заставить себя посмотреть на исходный код более подробно и понять логику.

Создание не простое, пожалуйста, указывайте автора в начале перепечатки: Nuggets@小西子 + ссылка на источник~

Если вы хотите узнать больше об исходном коде Spring,Нажмите, чтобы перейти к остальной части построчного анализа серии Spring

٩(* ఠO ఠ)=3⁼³₌₃⁼³₌₃⁼³₌₃du ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла. . .

Вот блогер-новичок Сяо Сизи, большие парни видели это, пожалуйста, поставьте лайк в левом верхнем углу~~