In the previous chapter, the ApplicationContext, which is the application context, was introduced. We say that BeanFactory is for developers, ApplicationContext is for users, and in fact it is the same. In ApplicationContest, we further integrate BeanFactory, implement automated XML file reading, register BeanFacoryPostProcess, register BeanPostProcess, and initialize single-column Beans in advance.
The above are part of the Bean life cycle and are automatically managed through Spring. In addition, Spring also allows users to customize initialization and destruction methods, and can also be called automatically through Spring.
1. Bean initialization
Before implementing initialization and destruction, we need to consider when to initialize and when to destroy it.
It's very simple. Initialization occurs when the bean is created, while destruction occurs when the container or virtual machine is closed. So for initialization, it is for the corresponding bean, that is, it is executed when created, and destruction is a unified behavior. When the container is closed, the specified bean needs to be uniformly destroyed.
Then after understanding the above logic, we start our logic implementation
First of all, the initialization behavior of the container is directly against the Bean itself. In fact, it is very similar to the BeanPostProcessor, except that the initialization behavior is more specific.
First, we need to add the initialization method name in BeanDefinition, which is used to customize the initialization method by users through XML/annotation.
private String initMethodName;
The second step is to define the parent class used for initialization, so that our bean can integrate the class and override the initialization method.
public interface InitializingBean { void afterPropertiesSet(); }
Now let’s go back to the initializeBean in the AbstractAutowireCapableBeanFactory. After executing the pre-enhancement of BeanPostProcess, execute the user-defined initialization method.
private void initializeBean(String beanName, Object bean,BeanDefinition beanDefinition) { // Execute BeanPostProcessor preprocessor before initialization Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName); // Execute the initialization method try { invokeInitMethods(beanName , wrappedBean , beanDefinition); } catch (Exception e) { throw new BeansException("Calling the init method of bean[" + beanName + "] fail", e); } // BeanPostProcessor post-set before initialization wrappedBean = applyBeanPostProcessorsAfterInitialization(bean , beanName); }
The logic of initialization is also very simple. First, determine whether the user has implemented the InitializingBean interface. If it is implemented, call its rewritten afterPropertiesSet initialization method. Then determine whether the user has specified a custom initialization method. If specified, the method can be obtained through reflection and executed.
private void invokeInitMethods(String beanName, Object bean, BeanDefinition beanDefinition) throws Exception{ if (bean instanceof InitializingBean){ ((InitializingBean) bean).afterPropertiesSet(); } // Handle user-defined initialization methods if ((())){ Method initMethod = ((), ()); if (initMethod == null) { throw new BeansException(("The initialization method named: %s cannot be found in Bean: %s",beanName,())); } (bean); } }
2. The destruction of the bean
The biggest difference between destruction of a bean compared to initialization is that it is directed to a single bean when initializing, and destruction is aimed at all beans to be destroyed.
In other words, we need to set a Map collection to store all beans to be destroyed, traverse the collection when the container is closed, and execute its destruction method.
First, we need to add the destruction method name in BeanDefinition, which is used to customize the destruction method by users through XML/annotation.
private String destroyMethodName;
The second step is to define the parent class used for destruction, so that our bean can integrate the class and override the destruction method.
public interface DisposableBean { public void destroy(); }
Then we need to go back to the ConfigurableApplicationContext, because the application context behavior is actually performed when the bean is destroyed, and it will be executed when we close the ApplicationContext.
public interface ConfigurableApplicationContext extends ApplicationContext { /** * Refresh the container, reload and initialize all configurations and beans */ void refresh(); /** * Close ApplicationContext */ void close(); void registerShutdownHook(); }
To implement these methods in AbstractApplicationContext, first look at the code below. There may be a question here that getBeanFactory().destroySingletons(); Through the complete code, we can know that getBeanFactory() returns a DefaultListableBeanFactory object, so we also need to implement this method in its parent class.
/** * Close ApplicationContext */@Override public void close() { doClose(); } private void doClose() { destroyBeans(); } private void destroyBeans() { getBeanFactory().destroySingletons(); } @Override public void registerShutdownHook() { Thread shutdownHook = new Thread(this::doClose); ().addShutdownHook(shutdownHook); }
First of all, we need to make it clear that in Spring, all interfaces are accused, that is, different interfaces have different functions, and the destruction of beans also belongs to the life cycle of a single-column bean, so we need to implement this method in the DefaultSingletonBeanRegistry. In addition, we also need to define the destroySingletons method in the ConfigurableBeanFactory.
Among them, both ConfigurableBeanFactory and DefaultSingletonBeanRegistry are inherited from SingletonBeanRegistry, but we do not define the destroySingletons interface in SingletonBeanRegistry. The two are implemented through combination inheritance.
public void destroySingletons(){ Set<String> beanNames = (); for (String beanName : beanNames) { DisposableBean disposableBean = (beanName); // Execute the destruction method (); } }
public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, SingletonBeanRegistry { /** * Add a BeanPostProcessor to the factory. * BeanPostProcessor can execute custom logic before and after bean initialization. * * @param beanPostProcessor instance to add */ void addBeanPostProcessor(BeanPostProcessor beanPostProcessor); /** * Destroy singleton beans */ void destroySingletons(); }
Actually, all the logic is over here, so write a test below
package ; import ; import ; /** * @author jixu * @title People * @date 2025/4/7 09:54 */public class People implements DisposableBean, InitializingBean { private String name; private Integer age; private Car car; public Car getCar() { return car; } public void setCar(Car car) { = car; } public String getName() { return name; } public void setName(String name) { = name; } public Integer getAge() { return age; } public void setAge(Integer age) { = age; } @Override public String toString() { return "People{" + "name='" + name + '\'' + ", age=" + age + ", car=" + car + '}'; } @Override public void destroy() { ("People destroy"); } @Override public void afterPropertiesSet() { ("People init"); } }
public class InitAndDestroyMethodTest { @Test public void testInitAndDestroy(){ ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("classpath:"); (); } }
Summarize
The above is personal experience. I hope you can give you a reference and I hope you can support me more.