Введение
Семейство Spring особенно велико, разработчикам требуется немало усилий, чтобы полностью завоевать семейство Spring. Как говорится, ударил змею и попал на семь дюймов, так что же такое «семь дюймов» семейства Spring? Ответ в моей голове всегда былВесенний фреймворк!
В этой статье записана небольшая часть интерпретации и разборки исходного кода в процессе изучения Spring Framework, чтобы рассказать о том, как контейнер Spring сканирует bean-компоненты в процессе запуска.
2. Методика обучения
Я считаю, что каждый разработчик, который хочет быть хорошим разработчиком, хочет понимать исходный код Spring, и я тоже. Таким образом, есть много способов найти учебные материалы по исходному коду Spring, купить книги, посмотреть видео и т. д. В конце концов, я обнаружил, что только успокоившись и следуя шаг за шагом отладке исходного кода и разбираясь в нем строка за строкой, я могу глубоко понять тайну Spring! Этот процесс очень скучный, но лучшие охотники выдерживают одиночество и порывистость!
Мы знаем, что есть много способов запустить контейнер Spring: XML-файлы, аннотации, Java Config. В реальном использовании это не выбор одного из них, а соответствие друг другу. Базовый процесс запуска контейнера остался прежним, но запись изменилась. Кроме того, лучший способ изучить 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
4.2 Диаграмма классов 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 и т. д. Просто возьмите один и выпейте горшок! Эти пункты будут разбиты по отдельности, но они не в центре внимания этой статьи. Цель этой статьи — разобраться сначала весь процесс запуска Первый шаг: процесс выполнения конструктора.