Серия исходных кодов Spring — процесс запуска контейнера (1)

Spring

Введение

Семейство Spring особенно велико, разработчикам требуется немало усилий, чтобы полностью завоевать семейство Spring. Как говорится, ударил змею и попал на семь дюймов, так что же такое «семь дюймов» семейства Spring? Ответ в моей голове всегда былВесенний фреймворк!

В этой статье записана небольшая часть интерпретации и разборки исходного кода в процессе изучения Spring Framework, чтобы рассказать о том, как контейнер Spring сканирует bean-компоненты в процессе запуска.

2. Методика обучения

Я считаю, что каждый разработчик, который хочет быть хорошим разработчиком, хочет понимать исходный код Spring, и я тоже. Таким образом, есть много способов найти учебные материалы по исходному коду Spring, купить книги, посмотреть видео и т. д. В конце концов, я обнаружил, что только успокоившись и следуя шаг за шагом отладке исходного кода и разбираясь в нем строка за строкой, я могу глубоко понять тайну Spring! Этот процесс очень скучный, но лучшие охотники выдерживают одиночество и порывистость!

Мы знаем, что есть много способов запустить контейнер Spring: XML-файлы, аннотации, Java Config. В реальном использовании это не выбор одного из них, а соответствие друг другу. Базовый процесс запуска контейнера остался прежним, но запись изменилась. Кроме того, лучший способ изучить Spring — это самостоятельно собрать проект исходного кода, который удобен для чтения, пометок и изменения исходного кода. Готовый проект выглядит так:Spring源码工程

3. Ввод кода

Без лишних слов, давайте начнем! Ввод кода выглядит следующим образом:

@Configuration
@ComponentScan("com.leon.funddatahouse")
public class Config {
}

public class MyApplication {
    public static void main(String[] args) {
    	// 我们基于注解的方式
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(Config.class);
        // 如果基于XML文件配置,则也可以如下:
        // ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring-context.xml");
    }
}

В конструкторе всего делается 3 вещи. Эти три вещи включают в себя весь процесс запуска всего контейнера Spring! Разбить их — полдела!

public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
		// 1.调用默认构造方法,进行容器环境的准备
		this();
        // 2.基于配置类注册相关信息
		register(componentClasses);
		// 3.刷新整个容器
		refresh();
	}

4. Перед разбором

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

4.1 Диаграмма классов контейнера UML

容器UML类图

4.2 Диаграмма классов BeanFactoryUML

BeanFactoryUML类图

5. Анализ исходного кода

5.1 Анализ метода строительства

5.1.1 Инициализация BeanFactory в контейнере

В конструкторе явный вызовthis(), ни конструктор без параметров:

public AnnotationConfigApplicationContext() {
	// 1.实例化容器中的reader. 后面会详细解析
    this.reader = new AnnotatedBeanDefinitionReader(this);
    // 2.实例化容器中的scanner.后面会详细解析
    this.scanner = new ClassPathBeanDefinitionScanner(this);
}

На первый взгляд, этот конструктор без аргументов делает две вещи, но это не так. На самом деле это эквивалентно:

public AnnotationConfigApplicationContext() {
	// 1.调用父类构造方法
    super();
    // 2.实例化容器中的reader. 后面会详细解析
    this.reader = new AnnotatedBeanDefinitionReader(this);
    // 3.实例化容器中的scanner.后面会详细解析
    this.scanner = new ClassPathBeanDefinitionScanner(this);
}

Это критично, если вы этого не понимаетеЗдесь неявно вызывается конструктор родительского классаЕсли это так, то следующий путь не может быть выбран, потому что в конструкторе родительского класса делается большая вещь:

// 在父类的构造方法中, 创建了容器中的BeanFactory.至此,容器中有了第一个程序创建的属性:beanFactory
public GenericApplicationContext() {
    // 初始化容器的beanFactory,类型为DefaultListableBeanFactory
    this.beanFactory = new DefaultListableBeanFactory();
}

Разница между BeanFactory и FacotryBean,пожалуйста, нажмите здесь

5.1.2 Создание экземпляра Reader в контейнере

Основное назначение читалки — помощь в регистрации BeanDefinition, его конкретное использование будет описано позже, здесь нам нужно только знать, что он содержит.

// 入参registry就是容器本身。因为通过上面的UML类图可以发现,容器间接继承了BeanDefinitionRegistry
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
    // getOrCreateEnvironment() 方法最主要是获取环境。实际类型其实默认的就是StandardEnvironment类。这里的环境包括两方面:
    // 1.systemEnvironment:操作系统环境。这样,Spring就可以获取到操作系统、CPU核心数等操作系统本身的数据。
    // 2.systemProperties:JVM的环境变量。这样,Spring就可以获取到JVM的基础数据,比如我们在启动参数中手动设置的环境变量等。
    this(registry, getOrCreateEnvironment(registry));
}

здесь черезthis()Другой конструктор внутри считывателя называется:

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    Assert.notNull(environment, "Environment must not be null");
    // 设置registry,已经知道它的就是容器本身:AnnotationConfigApplicationContext
    this.registry = registry;
    // 创建条件处理器
    this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
    // 非常关键! 提前往容器中注册一些必要的后置处理器
    AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}

Этот конструктор важен, потому что он включает в себя два важных элемента контейнера Spring: условные преобразователи и постпроцессоры!

5.1.2.1 Создание экземпляров обработчиков условий

Я считаю, что люди, знакомые со Spring, должны знать или использовать условные аннотации, такие как @ConditionalOnBean / @ConditionalOnClass, Анализом этих условных аннотаций является ConditionEvaluator.

public ConditionEvaluator(@Nullable BeanDefinitionRegistry registry,
        @Nullable Environment environment, @Nullable ResourceLoader resourceLoader) {
    // 实际上是委托给内部类ConditionContextImpl
    this.context = new ConditionContextImpl(registry, environment, resourceLoader);
}

// ------------分割线------------------

// 内部的ConditionContextImpl构造器
public ConditionContextImpl(@Nullable BeanDefinitionRegistry registry,
        @Nullable Environment environment, @Nullable ResourceLoader resourceLoader) {
    // 再说一遍,registry的实际类型就是 AnnotationConfigApplicationCont
    this.registry = registry;
    // 获取beanFactory,我们也知道了beanFactory其实就是 ConfigurableListableBeanFactory
    this.beanFactory = deduceBeanFactory(registry);
    // 从容器中获取environment,前面介绍过,容器中的environment的封装类是 StandardEnvironment
    this.environment = (environment != null ? environment : deduceEnvironment(registry));
    // 资源加载器. 通过UML类图可以发现,resourceLoader就是容器, 因为容器间接继承了ResourceLoader
    this.resourceLoader = (resourceLoader != null ? resourceLoader : deduceResourceLoader(registry));
    // 类加载器. 实际上就是获取beanFactory的类加载器。理应如此,容器当中的类加载器肯定要一致才行
    this.classLoader = deduceClassLoader(resourceLoader, this.beanFactory);
}

Позже, при разборе BeanDefinition, мы также столкнемся с ConditionEvaluator, и его конкретный разбор исходного кода будет разобран специальной статьей, в которой нам нужно знать только его функцию.

5.1.2.2 Регистрация некоторых постпроцессоров

После того, как ConditionEvaluator инициализирован, следующий шаг особенно важен, потому что здесь заранее будут внедрены некоторые постпроцессоры:

public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) {
    // 空壳方法,实际委托给重载的方法
    registerAnnotationConfigProcessors(registry, null);
}

Перегруженный метод выглядит следующим образом (Предупреждение о высокой энергии):

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
        BeanDefinitionRegistry registry, @Nullable Object source) {
    // 获取容器中的beanFactory,通过前面的解析,我们知道,这里一定会获取到。因此将进入if分支
    DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
    if (beanFactory != null) {
        // 此时beanFactory的属性dependencyComparator为null,因为初始化过程中,内部成员变量如果没有默认值,则默认为null,
        // 所以如果第一次进来, 这里的判断一定成立,对dependencyComparator进行设置。
        // AnnotationAwareOrderComparator继承了OrderComparator,
        // 因此可以对实现了Ordered接口、打上@Order或者@Priority注解的类进行排序。
        // 也就是说,在这里设置beanFactory中的orderComparator,以支持解析bean的排序功能。
        if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
            beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
        }
        // beanFactory初始化时,默认为SimpleAutowireCandidateResolver,因此第一次进来时这里的判断也一定成立。
        // ContextAnnotationAutowireCandidateResolver最主要的作用就是支持@Lazy注解的类的处理。
        if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
            beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
        }
    }

    // 初始化一个bdh容器,用于盛放接下来将解析出来的后置处理器的bd。
    Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);

    // 容器在第一次初始化时,内部一个bd都没有的。
    // 也就是说,从这里开始,容器将第一次装载bd,而这里的这些bd都是spring自带的后置处理器。

    // 获取并注册ConfigurationClassPostProcessor后置处理器 的bd
    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));
    }

    // 获取并注册AutowiredAnnotationBeanPostProcessor后置处理器 的bd
    if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
    }

    // 获取并注册CommonAnnotationBeanPostProcessor后置处理器 的bd
    if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
    }

    // 获取并注册PersistenceAnnotationBeanPostProcessor后置处理器 的bd
    if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition();
        try {
            def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME,
                    AnnotationConfigUtils.class.getClassLoader()));
        }
        catch (ClassNotFoundException ex) {
            throw new IllegalStateException(
                    "Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);
        }
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
    }

    // 获取并注册EventListenerMethodProcessor后置处理器 的bd
    if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
    }

    // 获取并注册DefaultEventListenerFactory后置处理器 的bd
    if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
    }

    return beanDefs;
}

Этот метод впервые появился в классе BeanDefinition, Spring BeanDefinition эквивалентен классу Java.

После прохождения этого метода в beanFactory существуют 6 вышеперечисленных bds:Кто-то однажды сказал мне, что если вы освоите постпроцессор Spring, вы освоите 10% всего Spring!Вы видите его важность.
Но не будем здесь расширять постпроцессор (слишком много), основная линия этой статьи — процесс запуска контейнера.

5.1.2.3 Краткое описание процесса инициализации считывателя

На этом инициализация части считывателя окончательно завершена. Подводя итог, инициализация считывателя в основном делает следующие вещи:
1. Создайте и задайте свойство Environment в контейнере. То есть по умолчанию используется класс StandardEnvironment.
2. Создайте и установите в контейнере условный синтаксический анализатор, то есть ConditionEvaluator, который фактически делегирован внутреннему классу ConditionContextImpl.
3. Зарегистрируйте в контейнере 6 постпроцессоров. Обратите внимание, что это только BeanDefinition, в котором создается постпроцессор. Разбор бина и постобработка еще не выполняются.

5.1.3 Создание экземпляра сканера в контейнере

После разбора считывателя продолжите разбор сканера. Фактический тип сканера здесь — ClassPathBeanDefinitionScanner. Его основная цель — проверить, все ли файлы классов в пути к классам могут быть преобразованы в bd. Конструктор финального вызова выглядит следующим образом:

public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {
	// 1.委托给内部的另一个构造方法
    this(registry, true);
}
// ------------------------分割线-------------------------
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) {
	// 2.又委托给内部的另一个构造方法 >_<
    // 从上面的入参可以知道 入参的registry实际就是容器本身, 并使用默认的filter.这个filter干什么的,下面会解析
    this(registry, useDefaultFilters, getOrCreateEnvironment(registry));
}
// ------------------------分割线-------------------------
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
        Environment environment) {
    // 3.又委托给内部的另一个构造方法 T^T
    this(registry, useDefaultFilters, environment,
            (registry instanceof ResourceLoader ? (ResourceLoader) registry : null));
}
// ------------------------分割线-------------------------
// 4. 终于见到了庐山真面目(不容易) ^_^
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
        Environment environment, @Nullable ResourceLoader resourceLoader) {

    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    // 再说一遍, registry就是容器!
    this.registry = registry;
    // 重要!!! 是否包括默认过滤器。从上面的入参可以知道, 这里的useDefaultFilters = true,因此会进入if分支
    if (useDefaultFilters) {
        registerDefaultFilters();
    }
    // 设置环境变量
    setEnvironment(environment);
    // 设置资源加载器
    setResourceLoader(resourceLoader);
}

5.1.3.1 Метод registerDefaultFilters()

Из окончательного метода построения мы знаем, что сканер будет использовать стратегию фильтрации во время процесса сканирования и использовать стратегию фильтрации по умолчанию.Стратегия по умолчанию — это следующий метод анализа.

protected void registerDefaultFilters() {
    // 扫描@Component注解的类
    this.includeFilters.add(new AnnotationTypeFilter(Component.class));
    ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
    try {
        // 扫描所有@ManageBean的类
        this.includeFilters.add(new AnnotationTypeFilter(
                ((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
        logger.trace("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
    }
    catch (ClassNotFoundException ex) {
    }
    try {
        // 扫描所有@Named的类
        this.includeFilters.add(new AnnotationTypeFilter(
                ((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
        logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
    }
    catch (ClassNotFoundException ex) {
    }
}

Точка знаний здесь:@ManageBean и @Named — это то же самое, что и @Component.. Просто мы обычно привыкли использовать @Component.

Почему здесь не добавлено сканирование по умолчанию @Service, @Repository, @Controller? Причина очень проста, эти аннотации косвенно унаследованы от @Component.
На этом этапе сканер анализируется, и главное, что он делает, — это добавляет стратегию фильтрации по умолчанию, чтобы аннотированные классы @Component можно было сканировать в дальнейшем.

Резюме шести конструкторов по умолчанию

Теперь давайте снова посмотрим на конструктор:

public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
    // 1.调用默认构造方法,进行容器环境的准备
    this();
    register(componentClasses);
    refresh();
}

От входа только эти три строчки кода, но в первой строчке столько подготовки сделано для вызова дефолтного конструктора, который также задействует некоторые из самых важных компонентов во всей системе Spring, такие как BeanFactory/ BeanDefinition / BeanDefinitionReader / BeanDefinitionScanner / Environment / ConditionEveluator / PostProcessor и т. д. Просто возьмите один и выпейте горшок! Эти пункты будут разбиты по отдельности, но они не в центре внимания этой статьи. Цель этой статьи — разобраться сначала весь процесс запуска Первый шаг: процесс выполнения конструктора.