предисловие
Эта статья в основном посвящена тому, как управлять жизненным циклом Spring Bean в контейнере Spring IoC.
При разработке приложений часто необходимо выполнить некоторые специфические задачи инициализации, которые являются относительно фиксированными, например, установить соединение с базой данных, открыть сетевое соединение и т. д. фиксированные задачи уничтожения, которые необходимо выполнить. Чтобы облегчить проектирование этих задач, контейнер Spring IoC предоставляет соответствующие функции, которые позволяют приложениям настраивать процесс инициализации и уничтожения компонентов.
Жизненный цикл Spring Bean
Описание изображения
Давайте сначала посмотрим на блок-схему жизненного цикла Spring Bean. Будет легче увидеть следующее описание в сочетании с изображением.
описание слова
- Контейнер компонентов находит определение Spring Bean в файле конфигурации.
- Контейнеры компонентов используют Java Reflection API для создания экземпляров компонентов bean.
- Если объявлены какие-либо свойства, объявленные свойства будут установлены. Если само свойство является bean-компонентом, оно будет проанализировано и установлено.
- Если класс Bean реализует
BeanNameAware
интерфейс, он будет вызываться путем передачи имени бинаsetBeanName()
метод. - Если класс Bean реализует
BeanClassLoaderAware
интерфейс, он будет вызываться путем передачи экземпляра объекта ClassLoader, который загружает этот компонентsetBeanClassLoader()
метод. - Если класс Bean реализует
BeanFactoryAware
интерфейс, он будет вызываться путем передачи экземпляра объекта BeanFactorysetBeanFactory()
метод. - Если какие-либо объекты BeanPostProcessors, связанные с BeanFactory, имеют загруженные bean-компоненты, они будут вызываться до того, как будут установлены свойства bean-компонентов.
postProcessBeforeInitialization()
метод. - Если класс Bean реализует
InitializingBean
интерфейс, после установки всех свойств компонента, определенных в файле конфигурации, он вызоветafterPropertiesSet()
метод. - Если определение bean-компонента в файле конфигурации содержит
init-method
свойство, значение свойства будет преобразовано в имя метода в классе компонента, и метод будет вызван. - Если к объекту Bean Factory подключены какие-либо постпроцессоры, он будет называться
postProcessAfterInitialization()
метод. - Если класс Bean реализует
DisposableBean
интерфейс, когда приложению больше не нужна ссылка на Bean, оно будет вызыватьсяdestroy()
метод. - Если определение bean-компонента в файле конфигурации содержит
destroy-method
свойство, то будет вызвано соответствующее определение метода в классе Bean.
Пример демонстрации
Затем мы используем простую ДЕМОНСТРАЦИЮ, чтобы продемонстрировать ход всего жизненного цикла, чтобы углубить ваше впечатление.
- определить
Person
класс, реализующийDisposableBean, InitializingBean, BeanFactoryAware, BeanNameAware
Эти 4 интерфейса, а также пользовательскиеinit-method
иdestroy-method
. Здесь, если вы не знакомы с этими интерфейсами, вы можете сначала взглянуть на определения этих интерфейсов.
public class Person implements DisposableBean, InitializingBean, BeanFactoryAware, BeanNameAware {
private String name;
Person() {
System.out.println("Constructor of person bean is invoked!");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("setBeanFactory method of person is invoked");
}
@Override
public void setBeanName(String name) {
System.out.println("setBeanName method of person is invoked");
}
public void init() {
System.out.println("custom init method of person bean is invoked!");
}
//Bean initialization code equals to
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("afterPropertiesSet method of person bean is invoked!");
}
//Bean destruction code
@Override
public void destroy() throws Exception {
System.out.println("DisposableBean Destroy method of person bean is invoked!");
}
public void destroyMethod() {
System.out.println("custom Destroy method of person bean is invoked!");
}
}
- определить
MyBeanPostProcessor
выполнитьBeanPostProcessor
интерфейс.
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("post Process Before Initialization is invoked");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("post Process after Initialization is invoked");
return bean;
}
}
- конфигурационный файл, указав
init-method
иdestroy-method
Атрибуты
<?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 name="myBeanPostProcessor" class="ric.study.demo.ioc.life_cycle_demo_set.MyBeanPostProcessor" />
<bean name="personBean" class="ric.study.demo.ioc.life_cycle_demo_set.Person"
init-method="init" destroy-method="destroyMethod">
<property name="name" value="Richard Yi" />
</bean>
</beans>
- Запустить контейнер, уничтожить контейнер
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config-1.xml");
((ClassPathXmlApplicationContext) context).destroy();
}
}
- вывод
Constructor of person bean is invoked!
setBeanName method of person is invoked
setBeanFactory method of person is invoked
post Process Before Initialization is invoked
afterPropertiesSet method of person bean is invoked!
custom init method of person bean is invoked!
post Process after Initialization is invoked
DisposableBean Destroy method of person bean is invoked!
custom Destroy method of person bean is invoked!
Вы можете видеть, что этот результат совпадает с тем, что мы описали выше.
Анализ исходного кода
Давайте посмотрим, как описанный выше вызов реализован с точки зрения исходного кода.
На самом деле, если вы читали мою предыдущую статьюАнализ исходного кода внедрения зависимостей Spring IoCЕсли это так, вы должны знать конкретную реализацию вышеуказанного вызова.
Это эквивалентно повторному упоминанию соответствующих частей.
инициализация контейнера
На этапе внедрения зависимости Spring IoC существует три ключевых шага для создания bean-компонента.
- Создание экземпляра createBeanInstance()
- populateBean(); сборка свойств
- initializeBean() обрабатывает различные события обратного вызова после инициализации Bean.
в,initializeBean()
Отвечает за обработку различных событий обратного вызова после инициализации Бина.
protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
invokeAwareMethods(beanName, bean);
return null;
}
}, getAccessControlContext());
}
else {
// 涉及到的回调接口点进去一目了然,代码都是自解释的
// BeanNameAware、BeanClassLoaderAware或BeanFactoryAware
invokeAwareMethods(beanName, bean);
}
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
// BeanPostProcessor 的 postProcessBeforeInitialization 回调
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
// init-methods
// 或者是实现了InitializingBean接口,会调用afterPropertiesSet() 方法
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
if (mbd == null || !mbd.isSynthetic()) {
// BeanPostProcessor 的 postProcessAfterInitialization 回调
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
вinvokeAwareMethods
сначала вызовет серию***Aware
реализация интерфейса
private void invokeAwareMethods(final String beanName, final Object bean) {
if (bean instanceof Aware) {
if (bean instanceof BeanNameAware) {
((BeanNameAware) bean).setBeanName(beanName);
}
if (bean instanceof BeanClassLoaderAware) {
((BeanClassLoaderAware) bean).setBeanClassLoader(getBeanClassLoader());
}
if (bean instanceof BeanFactoryAware) {
((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
}
}
}
затем выполнитьBeanPostProcessor
изpostProcessBeforeInitialization
Перезвони
@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
result = beanProcessor.postProcessBeforeInitialization(result, beanName);
if (result == null) {
return result;
}
}
return result;
}
Затем вызовите метод инициализации, который включаетInitializingBean
изafterPropertiesSet
метод и указанныйinit-method
метод,
protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd)
throws Throwable {
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isDebugEnabled()) {
logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
if (System.getSecurityManager() != null) {
try {
AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
@Override
public Object run() throws Exception {
((InitializingBean) bean).afterPropertiesSet();
return null;
}
}, getAccessControlContext());
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
((InitializingBean) bean).afterPropertiesSet();
}
}
if (mbd != null) {
String initMethodName = mbd.getInitMethodName();
if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
invokeCustomInitMethod(beanName, bean, mbd);
}
}
}
наконец выполнитьBeanPostProcessor
изpostProcessAfterInitialization
Перезвони
@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
result = beanProcessor.postProcessAfterInitialization(result, beanName);
if (result == null) {
return result;
}
}
return result;
}
Хорошо, здесь мы представили различные реализации обратного вызова в процессе загрузки bean-компонента процесса инициализации контейнера Spring.Ниже описан этап уничтожения контейнера Spring.
контейнер закрыт
Подобно инициализации бина, когда контейнер закрывается, можно увидеть вызов метода уничтожения бина. Процесс разрушения такой. следитьclose()-> doClose() -> destroyBeans() -> destroySingletons() -> destroySingleton() -> destroyBean() -> bean.destroy()
, вы увидите, что наконец вызывается метод уничтожения компонента.
protected void destroyBean(String beanName, DisposableBean bean) {
// 忽略
// Actually destroy the bean now...
if (bean != null) {
try {
bean.destroy();
}
catch (Throwable ex) {
logger.error("Destroy method on bean with name '" + beanName + "' threw an exception", ex);
}
}
// 忽略
}
Обратите внимание, что тип этого компонента на самом делеDisposableBeanAdapter
,DisposableBeanAdapter
Он управляет уничтожением Spring Beans, фактически здесь используется шаблон адаптера. посмотри сноваdestroy()
конкретный метод.
@Override
public void destroy() {
if (!CollectionUtils.isEmpty(this.beanPostProcessors)) {
for (DestructionAwareBeanPostProcessor processor : this.beanPostProcessors) {
processor.postProcessBeforeDestruction(this.bean, this.beanName);
}
}
if (this.invokeDisposableBean) {
if (logger.isDebugEnabled()) {
logger.debug("Invoking destroy() on bean with name '" + this.beanName + "'");
}
try {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
@Override
public Object run() throws Exception {
((DisposableBean) bean).destroy();
return null;
}
}, acc);
}
else {
// 调用 DisposableBean 的 destroy()方法
((DisposableBean) bean).destroy();
}
}
catch (Throwable ex) {
String msg = "Invocation of destroy method failed on bean with name '" + this.beanName + "'";
if (logger.isDebugEnabled()) {
logger.warn(msg, ex);
}
else {
logger.warn(msg + ": " + ex);
}
}
}
if (this.destroyMethod != null) {
// 调用 设置的destroyMethod
invokeCustomDestroyMethod(this.destroyMethod);
}
else if (this.destroyMethodName != null) {
Method methodToCall = determineDestroyMethod();
if (methodToCall != null) {
invokeCustomDestroyMethod(methodToCall);
}
}
}
Когда BeanPostProcessor регистрируется в контейнере?
Предыдущий только представил реализацию обратного вызова класса BeanPostProcessor в жизненном цикле Spring Bean, но не объяснил, когда BeanPostProcessor был зарегистрирован в контейнере. Давайте представим это ниже.
Когда контейнер Spring IoC инициализируется, контейнер будет выполнять некоторые операции инициализации, включая процесс регистрации BeanPostProcessor. Подробный процесс вы можете посмотреть в моей статьеИнициализация контейнера IoC.
Просто поместите исходный код здесь.
Местоположение источникаAbstractApplicationContext#refresh()
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
// 在这里
registerBeanPostProcessors(beanFactory);
// ....忽略
}
}
protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
}
Местоположение источникаPostProcessorRegistrationDelegate#registerBeanPostProcessors()
public static void registerBeanPostProcessors(
ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
// step1
// Register BeanPostProcessorChecker that logs an info message when
// a bean is created during BeanPostProcessor instantiation, i.e. when
// a bean is not eligible for getting processed by all BeanPostProcessors.
int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));
// step2
// Separate between BeanPostProcessors that implement PriorityOrdered,
// Ordered, and the rest.
List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanPostProcessor>();
List<BeanPostProcessor> internalPostProcessors = new ArrayList<BeanPostProcessor>();
List<String> orderedPostProcessorNames = new ArrayList<String>();
List<String> nonOrderedPostProcessorNames = new ArrayList<String>();
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
priorityOrderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
orderedPostProcessorNames.add(ppName);
}
else {
nonOrderedPostProcessorNames.add(ppName);
}
}
// step3
// First, register the BeanPostProcessors that implement PriorityOrdered.
sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);
// Next, register the BeanPostProcessors that implement Ordered.
List<BeanPostProcessor> orderedPostProcessors = new ArrayList<BeanPostProcessor>();
for (String ppName : orderedPostProcessorNames) {
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
orderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
sortPostProcessors(orderedPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, orderedPostProcessors);
// Now, register all regular BeanPostProcessors.
List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<BeanPostProcessor>();
for (String ppName : nonOrderedPostProcessorNames) {
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
nonOrderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);
// Finally, re-register all internal BeanPostProcessors.
sortPostProcessors(internalPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, internalPostProcessors);
// Re-register post-processor for detecting inner beans as ApplicationListeners,
// moving it to the end of the processor chain (for picking up proxies etc).
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
}
Описанный выше процесс можно разделить на четыре этапа:
- пройти через
beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
Метод получает коллекцию имен в beanFactory, которые наследуют интерфейс BeanPostProcessor; - Разделите бины Poster на
PriorityOrdered、Ordered、nonOrdered
Есть три категории, первые две — это пост-кондиционеры, добавляющие условия сортировки; (Spring может пройтиPriorityOrdered
иOrdered
Интерфейс управляет приоритетом процессора), здесь на самом деле есть еще один класс, которыйMergedBeanDefinitionPostProcessor
, а не основной момент, не расширять. - Третий шаг можно разделить на следующие маленькие шаги
-
priorityOrderedPostProcessors
, сначала отсортируйте, а затем зарегистрируйте -
orderedPostProcessors
, сначала отсортируйте, а затем зарегистрируйте - регистр
nonOrderedPostProcessors
, который является общим процессором -
internalPostProcessors
, сначала отсортируйте, а затем зарегистрируйте - Зарегистрировать
ApplicationListenerDetector
процессор
-
Когда DisposableBeanAdapter регистрируется в контейнере?
DisposableBeanAdapter
и вышеBeanPostProcessor
Уровень абстракции другой.Это привязано к Bean, поэтому время его регистрации находится на этапе внедрения зависимостей Spring Bean.Для получения подробного исходного кода, пожалуйста, обратитесь к этой моей статье.Анализ исходного кода внедрения зависимостей Spring IoC.
Местоположение источника:AbstractAutowireCapableBeanFactory#doCreateBean()
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
throws BeanCreationException {
// 省略前面的超多步骤,想了解的可以去看源码或者我的那篇文章
// Register bean as disposable.
// 这里就是DisposableBeanAdapter的注册步骤了
try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
return exposedObject;
}
Местоположение источника:AbstractBeanFactory#registerDisposableBeanIfNecessary()
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实现,该实现将执行给定bean的所有销毁工作。
// 包括:DestructionAwareBeanPostProcessors,DisposableBean接口,自定义destroy方法。
registerDisposableBean(beanName,
new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));
}
else {
// A bean with a custom scope...
Scope scope = this.scopes.get(mbd.getScope());
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + mbd.getScope() + "'");
}
scope.registerDestructionCallback(beanName,
new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));
}
}
}
Эпилог
До сих пор был объяснен весь жизненный цикл Spring Bean, от инициализации контейнера до уничтожения контейнера, а также время регистрации событий обратного вызова и т. д., я надеюсь, что это может быть вам полезно.
Эта статья опубликована в блогеOpenWriteвыпуск!