После разбора конфигурации посмотрим, как загружается бин
предисловие
с тех пор, как мыSpring
трудоголикbean
Зарегистрировано, конечно, нужно взять его в пользование, и перед использованием нужно пройти шаг, т.е.bean
нагрузка.
Упоминается в первой заметке, сделаноbean
зарегистрироваться наbeanDefinitionMap
После реестра также вызываются многие методы постпроцессора, один из которыхfinishBeanFactoryInitialization()
, говорится в запискеInstantiate all remaining (non-lazy-init) singletons
, что означает, что на этом шаге будут созданы неленивые загруженные классы для завершения загрузки класса.
и мы используемcontext.getBean("beanName")
метод, если соответствующийbean
Это не ленивая загрузка, тогда ее можно использовать напрямую, в то время как ленивая загрузкаbean
Вышеуказанные шаги необходимы для загрузки класса, и его можно использовать после загрузки~
Давайте посмотрим на эти два шага вместе.bean
как загрузить.
Временная диаграмма
Наш анализ кода вращается вокруг этого метода, пожалуйста, определите местоположение заранее:
org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
этоbean
Объем загруженного кода немного велик и превышает 100 строк, поэтому я организовал диаграмму последовательности и надеюсь получить четкое представление о процессе загрузки:
Эта временная диаграмма описываетbean
Общий процесс загрузки и многие детали на рисунке не показаны. Давайте сначала разберемся в общем процессе, а затем проследим за кодом, чтобы подробно проанализировать его.
анализ кода
Еще одно напоминание: из-за большого количества кода будет трудно вставить большой кусок кода каждый раз, поэтому код ключа, который я думаю, отображается. Загрузить проект, чтобы увидеть полный комментарий и анализировать его вместе с Исходный код ~
Использование FactoryBeans
Прежде чем анализировать процесс загрузки, необходимо понять предварительную концепцию.Spring
используется через механизм отраженияbean
изclass
Атрибут указывает класс реализации для создания экземпляраbean
.
Цитирование книг:
В некоторых случаях создается экземпляр
bean
Это сложнее: например, при наличии нескольких параметров традиционный метод требует записи большого количества конфигурационной информации в конфигурационный файл, что не очень гибко. В этом случае вы можете использоватьSpring
который предоставилFactoryBean
Интерфейс, пользователи могут настроить атмосферу, реализуя этот интерфейсbean
логика.
FactoryBean
Интерфейс определяет три метода:
public interface FactoryBean<T> {
T getObject() throws Exception;
Class<?> getObjectType();
default boolean isSingleton() {
return true;
}
}
Поговорим об использовании:
когда файл конфигурации
<bean>
изclass
Класс реализации свойстваFactoryBean
когда черезgetBean()
метод возвращает неFactoryBean
сам, ноFactoryBean#getObject()
Объект, возвращаемый методом.
использоватьdemo
См. код ниже:
расширятьFactoryBean
После этого два метода в графе должны быть перегружены, чтобы возвращать тип через универсальное соглашение. В перегруженном методе сделайте свою собственную персонализированную обработку.
в стартовом классеDemo
, получить метод класса через контекстcontext.getBean("beanName")
, используя разницуbeanName
Использовать ли префикс &, если префикс & отсутствует, он распознается как возвращенный FactoryBean.getObjectcar
введите, если префикс &, он вернетFactoryBean
тип класса.
Чтобы проверить и изучить концепции, изложенные в книге, самый быстрый способ — запустить образец кода один раз, чтобы увидеть, соответствуют ли выходные результаты ожиданиям.Поэтому обратитесь к примерам в книге, наберите код самостоятельно, посмотрите на окончательный вывод. результаты и обнаружили, что они согласуются с тем, что говорится в книге.FactoryBean
понимание.
почему первыйBeanFactory
А как насчет этой концепции?
Из диаграммы последовательности на шаге 1.5 вызывается метод:
org.springframework.beans.factory.support.AbstractBeanFactory#getObjectForBeanInstance
На этом этапе будет оцениватьсяsharedInstance
тип, если он принадлежитFactoryBean
, который вызовет пользовательскийFactoryBean
изgetObject()
методbean
инициализация.
Реальный тип экземпляраgetObjectType()
тип определения метода, а неFactoryBean
Сам оригинальный тип. То, что наконец зарегистрировано в контейнере,getObject()
вернутьbean
.
Я говорил об этой концепции заранее, надеюсь, вас это не смутит на последнем шаге.
Получить одноэлементный компонент из кеша
// Eagerly check singleton cache for manually registered singletons.
// 检查缓存中或者实例工厂是否有对应的实例或者从 singletonFactories 中的 ObjectFactory 中获取
Object sharedInstance = getSingleton(beanName);
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
// 检查缓存中是否存在实例
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 记住,公共变量都需要加锁操作,避免多线程并发修改
synchronized (this.singletonObjects) {
// 如果此 bean 正在加载则不处理
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
// 当某些方法需要提前初始化,调用 addSingletonFactory 方法将对应的
// objectFactory 初始化策略存储在 singletonFactories
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
Одноэлементный шаблон часто используется при проектировании кода, вSpring
, синглтон одного и того же контейнера будет создан только один раз, а затем извлеченbean
непосредственно из одноэлементного кешаsingletonObjects
получено в.
А поскольку одноэлементный кеш является общедоступной переменной, он блокируется при работе, что позволяет избежать операции перезаписи многопоточного одновременного изменения или чтения.
А вотearlySingletonObjects
Переменные, которые также являются одноэлементными кэшами, также используются для сохраненияbeanName
и создатьbean
отношения между экземплярами.
а такжеsingletonFactories
Разница в том, что когда синглтонbean
положить сюдаearly
После кэширования синглтона необходимо начать сsingletonFactories
Они являются взаимоисключающими и в основном используются для решения проблемы циклических зависимостей. (Круговые зависимости будут подробно рассмотрены в следующей статье)
Получить объект из экземпляра компонента
существуетgetBean
метод,getObjectForBeanInstance
это высокочастотный метод, полученный в одноэлементном кешеbean
или по разнымscope
Загрузка политикиbean
, есть такой метод, так в сочетании с тем, что я только что сказалBeanFactory
концепции, давайте посмотрим, что делает этот метод.
org.springframework.beans.factory.support.AbstractBeanFactory#getObjectForBeanInstance
// 返回对应的实例,有时候存在诸如 BeanFactory 的情况并不是直接返回实例本身
// 而是返回指定方法返回的实例
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
Реализация конкретного метода, поискПримечание 4.6Посмотрите на комментарии в коде:
Объясните процесс этого метода:
-
проверять
bean
Типы: определить, является ли это фабрикойbean
- в Африку
FactoryBean
не иметь дело с - правильно
bean
для преобразования -
иметь дело с
FactoryBean
Типы: порученоgetObjectFromFactoryBean
способ обработки.
В этом методе фабрикаbean
Есть специальная обработка, способ обработки такой же, как указано выше.FactoryBean
Используя то же самое, окончательный результатFactoryBean.getObject()
Тип, возвращаемый методом.
На четвертом шаге делегируйтеgetObjectFromFactoryBean
Метод обработки подробно не анализируется, но стоит упомянуть три метода:
// 单例操作,前置操作
beforeSingletonCreation(beanName);
try {
object = postProcessObjectFromFactoryBean(object, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName,
"Post-processing of FactoryBean's singleton object failed", ex);
}
finally {
// 单例模式,后置操作
afterSingletonCreation(beanName);
}
В коде при загрузке класса есть предоперация и постоперация.Я видел в первой заметке до того, что многие пре- и постоперации это пустые методы, ожидающие пользовательских расширений.
Но здесь не пустой метод, два метода используются для сохранения и удаления состояния загрузки класса, которое используется для обнаружения циклических зависимостей.
В то же время эти два метода отличаютсяscope
нагрузкаbean
Он также используется время от времени, и это также высокочастотный метод.
try {
object = postProcessObjectFromFactoryBean(object, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
}
Это метод выполнения постобработки, я мало с ним контактирую, сначала обратите внимание на концепцию:
Существует одно правило для Spring для получения bean-компонентов: постарайтесь убедиться, что все bean-компоненты будут обработаны, вызвав метод postProcessAfterInitialization зарегистрированного BeanPostProcessor после инициализации. В реальной разработке эта функция может быть расширена.
получить синглтон
Теперь перейдем к диаграмме последовательности1.3шаг:
// Create bean instance. 创建 bean 实例
// singleton 单例模式(最常使用)
if (mbd.isSingleton()) {
// 第二个参数的回调接口,接口是 org.springframework.beans.factory.ObjectFactory#getObject
// 接口实现的方法是 createBean(beanName, mbd, args)
sharedInstance = getSingleton(beanName, () -> {
return createBean(beanName, mbd, args);
// 省略了 try / catch
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
приходите посмотретьgetSingleton
Что делает метод:
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
// 注释 4.7 全局变量,加锁
synchronized (this.singletonObjects) {
// 检查是否已经被加载了,单例模式就是可以复用已经创建的 bean
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
// 初始化前操作,校验是否 beanName 是否有别的线程在初始化,并加入初始化状态中
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
// 初始化 bean,这个就是刚才的回调接口调用的方法,实际执行的是 createBean 方法
singletonObject = singletonFactory.getObject();
newSingleton = true;
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
// 初始化后的操作,移除初始化状态
afterSingletonCreation(beanName);
if (newSingleton) {
// 加入缓存
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
Разберем процесс:
- Проверьте, загружен ли уже кеш
- не загружается, логирование
beanName
состояние загрузки - Вызовите интерфейс обратного вызова, создайте экземпляр
bean
- Вызов метода обработчика после загрузки синглтона: этот шаг для удаления состояния загрузки
- Логируем результат в кеш и снимаем нагрузку
bean
Различные вспомогательные состояния, записанные в процессе
Поскольку второй и четвертый этапы, которые были упомянуты ранее, используются для записиbean
Состояние загрузки используется длякруговая зависимостьОбнаружение здесь опущено.
Ключевой способ лежит на третьем шаге, вызываяObjectFactory
изgetObject()
метод, реальный интерфейс обратного вызова реализуетcreateBean()
метод, необходимо понять и изучитьcreateBean()
.
готов создатьbean
В книге есть очень точная фраза:
существует
Spring
Среди исходников реально рабочая функция естьdo
начало, напримерdoGetBean
,doGEtObjectFromFactoryBean
, и функция входа, такая какgetObjectFromFactoryBean
, на самом деле, с глобальной точки зрения, чтобы сделать координацию работы.
Получив эту концепцию, см. следующееSpring
Исходный код, мы все знаем эту процедуру, понимаем общий процесс в функции входа, а затем сосредоточимся наdo
Как начать работать.
Согласно этой процедуре, давайте посмотрим на этот метод входаcreateBean()
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition, java.lang.Object[])
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
RootBeanDefinition mbdToUse = mbd;
// 有道翻译:确保此时bean类已经被解析,并且克隆 bean 定义,以防动态解析的类不能存储在共享合并 bean 定义中。
// 锁定 class,根据设置的 class 属性或者根据 className 来解析 Class
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
mbdToUse = new RootBeanDefinition(mbd);
mbdToUse.setBeanClass(resolvedClass);
}
// Prepare method overrides.
// 验证及准备覆盖的方法
mbdToUse.prepareMethodOverrides();
// 让 beanPostProcessor 有机会返回代理而不是目标bean实例。
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
// 短路操作,如果代理成功创建 bean 后,直接返回
return bean;
}
// 创建 bean
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
return beanInstance;
}
Сначала подведем итоги процесса:
- Анализировать класс в соответствии с установленным атрибутом класса или в соответствии с className
-
Методы проверки и подготовки к покрытиюЭтот метод используется для обработки следующих двух конфигураций: когда мы анализируем теги по умолчанию, мы идентифицируем
lookup-method
а такжеreplaced-method
свойства, то загрузка этих двух конфигураций будет храниться в единомbeanDefinition
серединаmethodOverrides
в свойствах. - Примените постпроцессор предварительной инициализации, чтобы решить, имеет ли указанный bean-компонент операцию короткого замыкания перед инициализацией.
- создать компонент
Ниже приведены основные шаги
Обработка свойства Override
public void prepareMethodOverrides() throws BeanDefinitionValidationException {
// Check that lookup methods exists.
if (hasMethodOverrides()) {
Set<MethodOverride> overrides = getMethodOverrides().getOverrides();
synchronized (overrides) {
for (MethodOverride mo : overrides) {
// 处理 override 属性
prepareMethodOverride(mo);
}
}
}
}
Видно, что для списка классов перегружен метод, затем итерат, один за другим для обработки. Конкретное лечениеlookup-method
а такжеreplaced-method
свойство, конфигурация, проанализированная на этом шаге, будет сохранена вbeanDefinition
серединаmethodOverrides
В атрибуте это подготовка к созданию экземпляра позже.
еслиbean
При создании обнаруженmethodOverrides
свойство, которое будет динамически позиционировать текущийbean
Создайте прокси и используйте соответствующий перехватчик в качествеbean
Делайте улучшения.
(Я не рекомендую использовать этот метод в бизнес-коде. Слишком хлопотно локализовать проблемы и звонить, и вы сделаете ошибки, если не будете осторожны =-=)
Предварительная обработка перед созданием экземпляра
// 让 beanPostProcessor 有机会返回代理而不是目标bean实例。
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
// 短路操作,如果代理成功创建 bean 后,直接返回
return bean;
}
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
Object bean = null;
if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
// Make sure bean class is actually resolved at this point.
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
Class<?> targetType = determineTargetType(beanName, mbd);
if (targetType != null) {
// 执行前置拦截器的操作
bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
if (bean != null) {
// 执行后置拦截器的操作
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
}
}
mbd.beforeInstantiationResolved = (bean != null);
}
return bean;
}
существуетdoCreateBean
Перед методом происходит операция короткого замыкания, если постпроцессор сработает, он вернет проксиbean
.
существуетresolveBeforeInstantiation
метод, убедившисьbean
Информация проанализирована, и выполняются два ключевых метода, как видно из комментариев, один — это работа пре-перехватчика, а другой — работа пост-перехватчика.
Если первый предварительный перехватчик создан успешно, синглтон былbean
в кеш, нормально не пройдетbean
Процесс создания , нет возможности сделать вызов постпроцессора, поэтому второй шаг здесь для этогоbean
Также после прикладного процессораpostProcessAfterInitialization
метод.
создать компонент
Наконец дошел до ключевого метода работы:doGetBean
. После проверки предыдущим методом особой предобработки нет, поэтому это обычныйbean
, обычныйbean
создать вdoGetBean
метод завершен.
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
// 注释 4.8 根据指定 bean 使用对应的策略创建新的实例 例如跟进方法去看,有工厂方法,构造函数自动注入,简单初始化
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
// 允许后处理程序修改合并的bean定义
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
mbd.postProcessed = true;
}
}
// 是否需要提前曝光,用来解决循环依赖时使用
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
// 第二个参数是回调接口,实现的功能是将切面动态织入 bean
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
Object exposedObject = bean;
// 对 bean 进行填充,将各个属性值注入
// 如果存在对其它 bean 的依赖,将会递归初始化依赖的 bean
populateBean(beanName, mbd, instanceWrapper);
// 调用初始化方法,例如 init-method
exposedObject = initializeBean(beanName, exposedObject, mbd);
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
// earlySingletonReference 只有在检测到有循环依赖的情况下才 不为空
if (earlySingletonReference != null) {
if (exposedObject == bean) {
// 如果 exposedObject 没有在初始化方法中被改变,也就是没有被增强
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
// 检查依赖
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
// bean 创建后,它所依赖的 bean 一定是已经创建了
// 在上面已经找到它有依赖的 bean,如果 actualDependentBeans 不为空
// 表示还有依赖的 bean 没有初始化完成,也就是存在循环依赖
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
}
// Register bean as disposable.
// 根据 scope 注册 bean
registerDisposableBeanIfNecessary(beanName, bean, mbd);
return exposedObject;
}
Увидев такой длинный код, у меня немного закружилась голова, поэтому давайте сразимся с процессом этого метода:
- Если загруженный компонент является синглтоном, очистите кеш
- Создайте экземпляр bean-компонента и преобразуйте BeanDifinition в BeanWrapper.
- Постпроцессор изменяет определение объединенного компонента.: После объединения bean-компонента аннотация Autowired официально реализует предварительное разрешение, такое как тип, с помощью этого метода.
- Обработка зависимостей
- заполнение свойства: заполняет все свойства экземпляра компонента
- Циклическая проверка зависимостей
- Зарегистрировать DisposableBean: этот шаг используется для обработки свойства метода уничтожения, которое регистрируется на этом шаге и вызывается при уничтожении объекта.
- полное создание и возврат.
Как видно из приведенного выше процесса, этот метод делает много вещей, так что код превышает более 100 строк, давая людям плохой опыт чтения, поэтому старайтесь разбивать маленькие методы, старайтесь быть кратким в методе ввода, объясните что делать, конкретно сделано мелкими методами.
Поскольку кода этого процесса создания много и он сложен, я выбираю ключевые моменты для понимания и изучения, а детали еще требуют глубокого изучения.:
Создайте экземпляр компонента
На втором шаге, описанном выше, создается экземпляр того, что делается.bean
, затем вернутьсяBeanWrapper
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
// Make sure bean class is actually resolved at this point.
Class<?> beanClass = resolveBeanClass(mbd, beanName);
Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
// Shortcut when re-creating the same bean...
boolean resolved = false;
boolean autowireNecessary = false;
if (args == null) {
synchronized (mbd.constructorArgumentLock) {
// 如果一个类有多个构造函数,每个构造函数都有不同的参数,调用前需要进行判断对应的构造函数或者工厂方法
if (mbd.resolvedConstructorOrFactoryMethod != null) {
resolved = true;
autowireNecessary = mbd.constructorArgumentsResolved;
}
}
}
// 如果已经解析过,不需要再次解析
if (resolved) {
if (autowireNecessary) {
// 实际解析的是 org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor
// 构造函数自动注入(如果参数有很多个,在匹配构造函数可复杂了,不敢细看=-=)
return autowireConstructor(beanName, mbd, null, null);
}
else {
// 使用默认的构造函数
return instantiateBean(beanName, mbd);
}
}
// Candidate constructors for autowiring? 需要根据参数解析构造函数
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
return autowireConstructor(beanName, mbd, ctors, args);
}
// Preferred constructors for default construction?
ctors = mbd.getPreferredConstructors();
if (ctors != null) {
// 构造函数注入
return autowireConstructor(beanName, mbd, ctors, null);
}
// No special handling: simply use no-arg constructor. 没有特殊的处理,使用默认构造函数构造
return instantiateBean(beanName, mbd);
}
Общее введение в функцию:
- Если фабричный метод существует, используйте фабричный метод для инициализации
-
Класс имеет несколько конструкторов, и каждый конструктор имеет разные параметры, поэтому необходимо заблокировать конструктор в соответствии с параметрами для создания экземпляра компонента.:
На этом этапе я действительно убежден. Чтобы соответствовать конкретному конструктору, я приложил много усилий. Если вам интересно, вы можете найти эту функцию для просмотра.
org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor
- Если нет ни фабричного метода, ни конструктора с параметрами, для создания экземпляра компонента используется конструктор по умолчанию.
В этом процессе есть два способа: один — фабричный метод, а другой — конструктор, входящийRootBeanDefinition
Конфигурация в поколении два к одномуbean
Пример
Не отслеживайте подробности, смотрите на следующем шаге
Обработка циклических зависимостей
// 是否需要提前曝光,用来解决循环依赖时使用
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
// 第二个参数是回调接口,实现的功能是将切面动态织入 bean
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
Ключевой метод -addSingletonFactory
, роль завершения: вbean
Экземпляр, который будет создан до завершения инициализацииObjectFactory
Добавить фабрику-одиночку
Я сказал в начале,ObjectFactory
это фабрика, используемая при создании объектов. Когда объект создается, он оценивает, был ли создан объект, от которого он зависит.Основой для суждения является просмотр зависимого объекта.ObjectFactory
Будь то в кэше singleton, если он не создан, то сначала будет создан зависимый объект, а потомObjectFactory
Поместить в одноэлементный кеш.
В настоящее время, если существует циклическая зависимость, ее необходимо раскрыть заранее, чтобы проверяющая сторона могла найти ее и создать ее экземпляр в обычном режиме.
Решение циклической зависимости будет подробно рассмотрено в следующей статье.
инъекция свойств
Это тоже высокочастотный метод, при инициализации нужно проверять свойства.property
Для внедрения вставьте несколько фрагментов кода:
populateBean(beanName, mbd, instanceWrapper);
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
// 给 awareBeanPostProcessor 后处理器最后一次机会,在属性设置之前修改bean的属性
boolean continueWithPropertyPopulation = true;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
...
if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
continueWithPropertyPopulation = false;
break;
}
...
}
PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
int resolvedAutowireMode = mbd.getResolvedAutowireMode();
if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
// Add property values based on autowire by name if applicable.
if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
// 根据名字自动注入
autowireByName(beanName, mbd, bw, newPvs);
}
// Add property values based on autowire by type if applicable.
if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
// 根据类型自动注入
autowireByType(beanName, mbd, bw, newPvs);
}
pvs = newPvs;
}
// 后处理器已经初始化
boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
// 需要依赖检查
boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
PropertyDescriptor[] filteredPds = null;
// 从 beanPostProcessors 对象中提取 BeanPostProcessor 结果集,遍历后处理器
for (BeanPostProcessor bp : getBeanPostProcessors()) {
...
}
// 在前面也出现过,用来进行依赖检查
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
checkDependencies(beanName, mbd, filteredPds, pvs);
// 将属性应用到 bean 中,使用深拷贝,将子类的属性一并拷贝
applyPropertyValues(beanName, mbd, bw, pvs);
}
Поскольку код слишком длинный, заинтересованные друзья находятся по адресуПримечание 4.11Проверить местоположение
Представьте процесс обработки:
- передача
InstantiationAwareBeanPostProcessor
процессорpostProcessAfterInstantiation
метод определения того, продолжает ли управляющая программа выполнять заполнение атрибутов - В зависимости от типа впрыска (
byName/byType
), извлеките зависимыйbean
, единый депозитPropertyValues
середина - определить, необходимо ли
BeanPostProcessor
и проверка зависимости:
- Если есть постпроцессор, он будет применен
InstantiationAwareBeanPostProcessor
процессорpostProcessProperties
метод для повторной обработки свойства, прежде чем свойство будет получено и заполнено. - использовать
checkDependencies
метод проверки зависимостей
- разобрать все
PropertyValues
Свойства в заполнены доBeanWrapper
середина.
В этом методе свойства заполняются в соответствии с различными типами впрыска, а затем вызывается постпроцессор для обработки, и, наконец, свойства применяются кbean
середина.
Не буду здесь вдаваться в подробности, продолжайте спускаться и смотрите следующий способ
инициализировать бин
В файле конфигурации после использования<bean>
При использовании этикеткиinit-method
атрибут, роль этого атрибута используется в этом месте:bean
Перед созданием звонитеinit-method
Указанный метод создается в соответствии с бизнесом пользователя. Давайте посмотрим, как войтиinitializeBean
:
// 调用初始化方法,例如 init-method
exposedObject = initializeBean(beanName, exposedObject, mbd);
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
// 注释 4.12 securityManage 是啥,不确定=-=
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareMethods(beanName, bean);
return null;
}, getAccessControlContext());
}
else {
// 如果没有 securityManage,方法里面校验了 bean 的类型,需要引用 Aware 接口
// 对特殊的 bean 处理:Aware/ BeanClassLoader / BeanFactoryAware
invokeAwareMethods(beanName, bean);
}
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
// 熟悉么,后处理器又来了
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
// 激活用户自定义的 init-method 方法
invokeInitMethods(beanName, wrappedBean, mbd);
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
Этот метод в основном используется для вызова заданного нами метода инициализации, но внутри метода выполняются и другие операции, поэтому поговорим о самом процессе:
1,Активировать метод осведомленности
Spring
несколько изAware
интерфейс, который реализует этот интерфейсbean
, после инициализации можно получить некоторые соответствующие ресурсы, такие какBeanFactoryAware
, после инициализацииSpring
Контейнер будет вводитьBeanFactory
пример. Поэтому, если вам нужно получить эти ресурсы, пожалуйста, укажитеAware
интерфейс.
2,Постпроцессор
Я верю, что все знакомы с этим, мы можемPostProcessor
Подождите, пока настройка в постпроцессоре реализует модификацию и расширение. НапримерBeanPostProcessor
класс имеетpostProcessBeforeInitialization
а такжеpostProcessAfterInitialization
, даbean
Логическое расширение до и после загрузки можно понимать как срезAOP
подумал о.
3.Активировать пользовательский метод инициализации
Назначение этого метода очевидно, то есть найти пользовательский конструктор и вызвать его. Обратите внимание, что еслиbean
даInitializingBean
типа, вам нужно позвонитьafterPropertiesSet
метод.
Порядок выполнения первыйafterPropertiesSet
, а потомinit-method
определенный метод.
регистрация одноразового компонента
ЭтоSpring
Предоставляет запись расширения для метода уничтожения,Spring
Папа зарезервировал все возможности, о которых мы можем думать и которые мы хотим расширить. кроме как черезdestroy-method
Помимо метода уничтожения конфигурации свойств, вы также можете прописать постпроцессорыDestructionAwareBeanPostProcessor
Равномерное обращениеbean
Метод уничтожения:
protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {
AccessControlContext acc = (System.getSecurityManager() != null ? getAccessControlContext() : null);
if (!mbd.isPrototype() && requiresDestruction(bean, mbd)) {
if (mbd.isSingleton()) {
// 单例模式
// 注册 DisposableBean
registerDisposableBean(beanName,
new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));
}
else {
// A bean with a custom scope...
Scope scope = this.scopes.get(mbd.getScope());
scope.registerDestructionCallback(beanName,
new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));
}
}
}
Вот к разнымscope
вниз, продолжитьdisposableBean
Регистрация.
Суммировать
Это примечаниеОбобщил процесс загрузки классов в сочетании с временной диаграммой и анализом кода., надеюсь получить более глубокое понимание этого.
В то же время у меня также есть небольшое представление о кодировании:
- Не пишите слишком длинные методы, старайтесь разбивать их на маленькие методы с четкими намерениями.
с самого началаSpring
Когда я читаю исходный код, я поражаюсь чистоте и четкой логике его кода, метод входа показывает, что нужно сделать, а затем работа подразделяется на конкретную логику, что отражает превосходный замысел дизайнера кода.
Поэтому, когда я увидел, что есть несколько методов с более чем 100 строками, я немного пожаловался в душе.Похоже, код, который я писал с большими парнями, тоже имеет что-то общее, то есть его тоже можно оптимизировать хахаха~
- Бревна должны быть отмечены в ключевых местах, чтобы облегчить исследование и позиционирование
В перехваченных фрагментах кода некоторые логические суждения и обработка логов были удалены из-за недостатка места, но управление логами — очень важная часть.Распечатка логов в ключевых местах, устранение неполадок и анализ данных позже будут полезны.
Если лень печатать лог и не печатать лог в ключевых местах, то даже при наличии проблемы не знаешь с чего начать, а причину проблемы долго не могут выявить, в результате в жалобах пользователей.
Из-за ограниченных личных технологий, если есть какие-либо недоразумения или ошибки, оставьте комментарий, и я внесу исправления на основе предложений друзей.
Облако кода spring-analysis-note Адрес Gitee
spring-analysis-note Адрес Github
использованная литература
-
Третий анализ исходного кода Spring Core Container: анализ процесса инициализации Spring Beans
-
Углубленный анализ исходного кода Spring / под редакцией Хао Цзя -- Пекин: издательство People's Posts and Telecommunications Publishing House
Портал:
-
Изучение исходного кода Spring (1) инфраструктуры контейнера
-
Изучение исходного кода Spring (2) анализ тегов по умолчанию
-
Функция расширения изучения исходного кода Spring (6), часть 1
-
Функция расширения изучения исходного кода Spring (семь), часть 2
-
Изучение исходного кода Spring (8) Принципы использования и реализации АОП
-
Изучение исходного кода Spring (9) Транзакционная транзакция