Обзор
Spring для разработчиков Java, он очень хорошо известен как основа и ядро фреймворка, в определенном опыте разработки после прочтения исходного кода можно лучше улучшить нашу способность кодировать и дать им больше понимания. Как говорится, познай себя, познай себя. Как только вы созреете к весне, вас ничего не остановит, и вы тоже сможете двигаться вперед в пути развития.
В общем, IOC имеет два наиболее важных места: одно — создание контейнера компонентов, а другое — инициализация. В этой статье я в основном объясняю процесс создания контейнера IOC Bean. Знания части инициализации будут дополнены позже.
Чтобы сохранить строгость статьи, если читатель найдет что-то не так со мной, пожалуйста, не стесняйтесь указать на это, и я очень надеюсь услышать голос читателя. В то же время вы можете исправить свои недоразумения.
В этой статье в основном используется ClassPathXmlApplication в качестве контейнера Spring IOC и анализируется процесс создания контейнера из ClassPathXmlApplication.Во-первых, давайте посмотрим на зависимости ClassPathXmlApplication:
Во-первых, давайте посмотрим на основной магистраль:
Мы можем понять его основные функции через имя родительского класса ClassPathXmlApplicationContext:
- DefaultResourceLoader: Предоставляет метод для получения файла конфигурацииполучитьресурс(), возвращает информацию о ресурсахResource
- AbstractApplicationContext: Предоставляет основной метод создания контейнеров и инициализации объектов.Обновить ()
- AbstractRefreshableApplicationContext: обеспечить обновлениеобновитьBeanFactory()метод для инициализации BeanFactory.
- AbstractRefreshableConfigApplicationContext: Сохранить информацию о профиле.
- AbstractXmlApplicationContext:поставкаloadBeanDefinitions()Прочтите метод BeanDefinitions, чтобы преобразовать ресурс в конфигурацию.
- ClassPathXmlApplicationContext: Класс внедрения, используемый для определения местоположения файла ресурса.
Я считаю, что благодаря анализу приведенных выше основных классов у всех есть общее представление об инициализации Spring IOC.
Короче говоря, конкретный процесс создания контейнера Spring IOC выглядит следующим образом:
Мы сопоставляем изображение с вызовом класса Конкретные шаги:
1. ClassPathXmlApplicationContext -> Через конструктор прочитать информацию об адресе файла конфигурации XML
2. AbstractApplicationContext -> refresh() для инициализации контейнера
3, AbstractRefreshableApplicationContext -> Инициализация BeanFactory
4. АбстрактноеxmlaPlaphationContext -> LoadBeandifinition для чтения информации о ресурсах (загрузка бобов)
5. XmlBeanDefinitionReader -> разобрать файл ресурсов в BeanDefinition
6. DefaultBeanDefinitionDocumentReader -> Зарегистрировать BeanDefinition в BeanFactory
Поскольку часть исходного кода Spring IOC в основном включает в себя: создание контейнера и инициализацию bean-компонента. В этой статье я в основном расскажу о процессе создания контейнера!
1. Экземпляр контейнера
Файл конфигурации ApplicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="User" class="com.charles.business.model.User">
<property name="username" value="jaycekon"/>
<property name="phone" value="1881412***"/>
</bean>
</beans>
Код запускает вход:
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("ApplicationContext.xml");
User user = (User) applicationContext.getBean("User");
Assert.notNull(user, "容器初始化异常!");
logger.info("初始化结果:{}", JSONObject.toJSONString(user));
}
Далее мы анализируем каждый процесс один за другим в соответствии с графом зависимостей ClassPathXmlApplication.
2. ClassPathXmlApplicationContext
Основной метод:
org.springframework.context.support.ClassPathXmlApplicationContext#ClassPathXmlApplicationContext(java.lang.String[], boolean, org.springframework.context.ApplicationContext)
Как видно из рисунка выше, класс ClassPathXmlApplicationContext не предоставляет никаких специальных методов, кроме конструктора, есть только один метод получения Resource (аналогично, AbstractRefreshableConfigApplicationContext предоставляет метод получения местоположения ресурса по классу), который в основном используется для экономии ресурсов информации.
Основной конструктор в ClassPathXmlApplicationContext:
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
// 调用父类构造方法
super(parent);
// AbstractRefreshableConfigApplicationContext 具体实现,保存资源定位信息
setConfigLocations(configLocations);
if (refresh) {
// AbstractApplicationContext 具体实现,初始化容器与Bean
refresh();
}
}
Как видите, класс ClassPathXmlApplicationContext довольно прост, и его основными функциями являются:
- Resource[] configResources: хранить файлы конфигурации как ресурсы в этом массиве.
- setConfigLocations(): сохранить информацию о расположении файла конфигурации (AbstractRefreshableConfigApplicationContext).
- Refresh(): запуск основного метода инициализации контейнера (AbstractApplicationContext).
3. Абстрактный контекст приложения
Основной метод:
org.springframework.context.support.AbstractApplicationContext#refresh
AbstractApplicationContext — это основной класс в контейнере Spring IOC. Он предоставляет конкретные реализации для родительских классов и интерфейсов. Методы очень богаты. Здесь мы в основном представляем основные методы.refresh(). Вот метод обновления () вместо init () это имя. Поскольку ApplicationContext построен, мы можем реконструированы путем вызова метода Refresh (), который уничтожит исходный ApplicationContext перед повторным выполнением операции инициализации.
public void refresh() throws BeansException, IllegalStateException {
//对容器初始化进行加锁操作,比免在创建的同时,重复操作。
synchronized (this.startupShutdownMonitor) {
// 设置容器初始化世界,表示容器现在isActive,初始化上下文环境中的任何占位符属性源
prepareRefresh();
// 核心步骤,主要功能是:让子类进行BeanFactory 初始化,并且将Bean信息 转换为BeanFinition,最后注册到容器中
// 当然,这里说的 Bean 还没有初始化,只是配置信息都提取出来了
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 设置 BeanFactory 的类加载器,添加几个 BeanPostProcessor,手动注册几个特殊的 bean
prepareBeanFactory(beanFactory);
try {
// 后续步骤将在下次进行分析,本文主要核心为上面几个步骤
...
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
От обновления () в абстрактных панциклетахContext мы можем примерно понимать, что основной процесс весны IOC в основном завершен здесь, от создания контейнера, разрешения ресурсов, создания компонентов компонентов и серии шагов. контролируются этим методом.
В этой статье одной из наших главных проблем является:
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
На приведенном выше рисунке мы можем интуитивно понять взаимосвязь между ApplicationContext и BeanFactory. Созданная здесь ConfigurableListableBeanFactory на самом деле содержит большинство функций трех ветвей BeanFactory.
1. ApplicationContext наследует ListableBeanFactory. Этот список означает, что через этот интерфейс мы можем получить несколько бобов. Методы верхнего фасочного интерфейса - это все для получения одной фасоли.
2. ApplicationContext наследует HierarchicalBeanFactory, и само слово Hierarchical может объяснить проблему, то есть мы можем запустить несколько BeanFactory в приложении, а затем установить каждую BeanFactory как отношение родитель-потомок.
3. Autowire в названии AutowireCapableBeanFactory всем очень знаком, используется для автоматической сборки бинов, но если внимательно посмотреть на картинку выше, то ApplicationContext его не наследует, но не волнуйтесь, не использовать наследование не значит что вы не можете использовать композицию.Вы узнаете об этом, если увидите последний метод getAutowireCapableBeanFactory() в определении интерфейса ApplicationContext.
4. ConfigurableListableBeanFactory тоже особый интерфейс.Глядя на картинку, особенность в том, что он наследует все три интерфейса второго уровня, а ApplicationContext — нет. Это будет использовано позже.
Давайте взглянем на основное содержимое методаgetFreshBeanFactory():
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
//AbstractRefreshableApplicationContext 中实现,主要用于刷新BeanFactory,加载 Bean 定义、注册 Bean 等等
refreshBeanFactory();
//获取上述步骤中 创建好的 BeanFactory
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
4. Абстрактный обновляемый контекст приложения
Основной метод:
org.springframework.context.support.AbstractRefreshableApplicationContext#refreshBeanFactory
На самом деле, в процессе чтения исходного кода чтение имени класса может нам очень помочь.На примере класса AbstractRefreshableApplicationContext видно, что он наследует AbstractApplicationContext и в основном реализует функцию обновления.Конечно, обновление здесь не относится к методу обновления(). Вместо этого он относится к:обновитьBeanFactory()
@Override
protected final void refreshBeanFactory() throws BeansException {
//判断当前ApplicationContext是否已经有 BeanFactory ,如果有,销毁所有 Bean,关闭 BeanFactory
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
// 初始化一个 DefaultListableBeanFactory
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
// 设置 BeanFactory 的两个配置属性:是否允许 Bean 覆盖、是否允许循环引用
customizeBeanFactory(beanFactory);
// 读取配置文件信息,加载 Bean 到 BeanFactory 中
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
Возможно, вы подумаете, что отношение между ApplicationContext и BeanFactory является отношением наследования, но в практических приложениях его не следует понимать как класс реализации BeanFactory, ApplicationContext — это экземпляр BeanFactory, а все BeanFactory, связанные в будущем. на самом деле для этого экземпляра для обработки.
Кратко представим метод CustomizeBeanFactory:
- allowBeanDefinitionOverriding: установить, можно ли переопределить bean-компонент.
- allowCircularReferences: установите, разрешены ли циклические зависимости
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
if (this.allowBeanDefinitionOverriding != null) {
beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.allowCircularReferences != null) {
beanFactory.setAllowCircularReferences(this.allowCircularReferences);
}
}
5. АннотацияXMLApplicationContext.
Основной метод:
org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.support.DefaultListableBeanFactory)
После такого долгого путешествия я наконец достигли шага XML -> Resource -> BeanDefinition:
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
//给这个 BeanFactory 实例化一个 XmlBeanDefinitionReader BeanFactory.
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// 设置默认环境配置
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// 初始化 BeanDefinitionReader
initBeanDefinitionReader(beanDefinitionReader);
// 将配置文件读取,解析成BeanDefinition
loadBeanDefinitions(beanDefinitionReader);
}
Этот метод относительно прост, мы идем прямо вниз и вхожу в метод Neadbeandefinitions ():
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
Можно видеть, что описанное выше в основном разделено на два шага: один анализирует Resource, а другой анализирует configLocations (этот шаг в основном включает процесс замены локаторов ресурсов на Resource).
Последующий анализ Bean будет анализироваться следующими методами:
org.springframework.beans.factory.xml.XmlBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.support.EncodedResource)
6. Резюме
Из-за недостатка места в этой статье в основном представлен процесс создания исходного кода контейнера Spring IOC, а анализ исходного кода синтаксического анализа Bean и последующие шаги будут добавлены позже. Наконец, давайте рассмотрим основной процесс:
- Инициализация ClassPathXmlApplicationContext.ClassPathXmlApplicationContext()
- Основной метод AbstractApplicationContext.refresh()
- AbstractRefreshableApplicationContext.refreshBeanFactory () инициализация BeanFactory
- AbstractXmlApplicationContext.loadBeanDefinitions() разрешает ресурс
- Продолжение следует. . .
Использованная литература:
- https://docs.spring.io/spring/docs/5.0.5.BUILD-SNAPSHOT/spring-framework-reference/core.html#spring-core
- http://www.importnew.com/27469.html
- http://www.importnew.com/19243.html