Make writing a habit together! This is the 15th day of my participation in the “Gold Digging Day New Plan · April More text Challenge”. Click here for more details

Accumulate over a long period, constant dripping wears away a stone 😄

preface

In my first post on the Spring source code (I)-Bean definition -BeanDefinition, there is an attribute called initMethodName.

That is, the init-method property in the corresponding bean tag. So when does this property object’s method get executed? Let’s first clarify the order in which this method is executed. Spring has instantiated the bean and populated the properties, and after these steps the user defined initialization method will be called.

The source code

The method in AbstractAutowireCapableBeanFactory class.

protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
  / / ignore
  if(System.getSecurityManager() ! =null) {
    AccessController.doPrivileged((PrivilegedAction < Object > )() - > {
      invokeAwareMethods(beanName, bean);
      return null;
    }, getAccessControlContext());
  }
  else {
    // execute Aware
    invokeAwareMethods(beanName, bean);
  }
  Object wrappedBean = bean;
  if(mbd == null| |! mbd.isSynthetic()) {// before initialization
    wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
  }
  try {
    // 3
    invokeInitMethods(beanName, wrappedBean, mbd);
  }
  catch(Throwable ex) {
    throw newBeanCreationException( (mbd ! =null ? mbd.getResourceDescription() : null), beanName, "Invocation of init method failed",
      ex);
  }
  if(mbd == null| |! mbd.isSynthetic()) {// After initialization
    wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
  }
  return wrappedBean;
}
Copy the code

The branch of code in the initializeBean method is easy to understand:

  1. Activate the Aware method:invokeAwareMethods
  2. Initialization of pre – and post-processor applications:applyBeanPostProcessorsBeforeInitialization
  3. Activate init method:invokeInitMethods
  4. Application of post-initialization processor:applyBeanPostProcessorsAfterInitialization

1. Activate the Aware method

Before analyzing the source code, let’s take a look at the use of Aware. See my other article: Spring source-Aware interface

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) {
            ClassLoader bcl = getBeanClassLoader();
            if(bcl ! =null) { ((BeanClassLoaderAware) bean).setBeanClassLoader(bcl); }}if (bean instanceof BeanFactoryAware) {
            ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this); }}}Copy the code

The source code is simple. First determine whether the bean instance belongs to the Aware interface, and if so, then determine whether the bean instance belongs to the xxxAware interface. If so, call the setXxx() method of the instance to set the XXX property value of the instance. In invokeAwareMethods(), the main thing is to set BeanNameAware to beanName, Set BeanClassLoaderAware to ClassLoader and BeanFactoryAware to BeanFactory.

2. Initialize the application of the pre – and post-processor

@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException {

    Object result = existingBean;
    for (BeanPostProcessor processor : getBeanPostProcessors()) {
        Object current = processor.postProcessBeforeInitialization(result, beanName);
        if (current == null) {
            return result;
        }
        result = current;
    }
    return result;
}
Copy the code

Straighten out the above code. Get all BeanPostProcessor, loop execution BeanPostProcessor postProcessBeforeInitialization initialization of the former method. If the result set returned by the method before initialization is NULL, result is returned. If not null, it will return to the staging, the result of the current method to continue the next BeanPostProcessor postProcessBeforeInitialization initialization of the former method. If you are not familiar with BeanPostProcessor, you can take a look at another article by Xiaojie: Spring source code -BeanPostProcessor.

Activate the init method

protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd) throws Throwable {
  // Whether the current Bean implements the InitializingBean interface
  boolean isInitializingBean = (bean instanceof InitializingBean);
  if(isInitializingBean && (mbd == null| |! mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
    if(logger.isTraceEnabled()) {
      logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
    }
    // Ignore...
    if(System.getSecurityManager() ! =null) {
      try {
        AccessController.doPrivileged((PrivilegedExceptionAction < Object > )() - > {
          ((InitializingBean) bean).afterPropertiesSet();
          return null;
        }, getAccessControlContext());
      }
      catch(PrivilegedActionException pae) {
        throwpae.getException(); }}else {
      // Call afterPropertiesSet() directly((InitializingBean) bean).afterPropertiesSet(); }}if(mbd ! =null&& bean.getClass() ! = NullBean.class) {// Get the init-method property of the bean label
    String initMethodName = mbd.getInitMethodName();
    //1, the method name is not empty
    // If InitializingBean interface is implemented and the value of init-method property is equal to afterPropertiesSet
    / / 3, externallyManagedInitMethods Set whether the collection contains the init method the value of the attribute (the)
    / / externallyManagedInitMethods Set collection records only the afterPropertiesSet
    if(StringUtils.hasLength(initMethodName) && ! (isInitializingBean &&"afterPropertiesSet".equals( initMethodName)) && ! mbd.isExternallyManagedInitMethod(initMethodName)) {Init-method =" XXX"invokeCustomInitMethod(beanName, bean, mbd); }}}Copy the code

First check if the current bean implements the InitializingBean interface, if so, call its afterPropertiesSet() method, and then check if the current bean is configured with the init-method property. If configured, the specified init-method() method is called via reflection.

InitializingBean

Spring’s InitializingBean interface, which provides a way for beans to initialize methods, contains only one method: afterPropertiesSet(), which any class that implements this interface executes when initializing the bean. The definition is as follows:

public interface InitializingBean {

	void afterPropertiesSet(a) throws Exception;

}
Copy the code

Provide class A and implement the InitializingBean interface, overriding its afterPropertiesSet method

public class A implements InitializingBean {

   private String name;

   @Override
   public void afterPropertiesSet(a) throws Exception {
      System.out.println("AfterPropertiesSet initialization method execution");
      this.name = "gongj";
   }
  
   public String getName(a) {
		return name;
	}

	public void setName(String name) {
		this.name = name; }}Copy the code

Configure and start classes

<bean class="com.gongj.initializing.A" id="a"/>
    
public static void main(String[] args) {
		ClassPathXmlApplicationContext context =
				new ClassPathXmlApplicationContext("spring-config3.xml"); A bean = context.getBean(A.class); System.out.println(bean.getName()); } Result: afterPropertiesSet initialization method executes gongjCopy the code

In afterPropertiesSet(), we can change the property value of a bean, which gives us another way to change the bean instance object.

init-method

While learning Spirng, you must have learned about the init-method property of the bean tag, which is used to execute specified methods at bean initialization and can be used to replace the InitializingBean interface.

Modify class A code as follows:

public class A {

	private String name;

	public String getName(a) {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public void initMethod(a){
		System.out.println("Initialize method execution");
		this.name = "yuanj"; }}Copy the code

Modify configuration items and start

<bean class="com.gongj.initializing.A" id="a" init-method="initMethod"/> Result: The initialization method is executed yuanjCopy the code

You can achieve exactly the same effect as InitializingBean.

The question is, if I implement both the InitializingBean interface and configure init-method, which of the two initialization methods will be executed first? In fact, read the above source code, should have the answer, but here is a wave of practice.

Modify class A code and start

public class A implements InitializingBean{

	private String name;

	public String getName(a) {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public void initMethod(a){
		System.out.println("Initialize method execution");
		this.name = "yuanj";
	}

	@Override
	public void afterPropertiesSet(a) throws Exception {
		System.out.println("AfterPropertiesSet initialization method execution");
		this.name = "gongj"; }} Result: afterPropertiesSet initialization method executes yuanjCopy the code

The method to implement the InitializingBean interface is followed by the method to configure init-method.

conclusion

  • Spring provides two ways to initialize methods. One is to implement the InitializingBean interface and override the afterPropertiesSet method. The other is to specify the initialization method by configuring init-method in the configuration file. AfterPropertiesSet is executed first and init-method is executed later.

  • Implementing the InitializingBean interface by calling the afterPropertiesSet method directly is a little more efficient than calling init-method via reflection, but the init-method approach is relatively flexible. But for now, our projects should almost always abandon both approaches in favor of annotations, so we can use the @PostConstruct annotation for initialization.

  • If an error occurs when calling the afterPropertiesSet method, the method specified by init-method is not called.

The application of the post-initialization processor

@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException {

    Object result = existingBean;
    for (BeanPostProcessor processor : getBeanPostProcessors()) {
        / / execution postProcessAfterInitialization method, that is, after initialization method
        Object current = processor.postProcessAfterInitialization(result, beanName);
        if (current == null) {
            return result;
        }
        result = current;
    }
    return result;
}
Copy the code

Get all BeanPostProcessor, loop through the BeanPostProcessor postProcessAfterInitialization after initialization method. If the method returns a null result set after initialization, result is returned. If not null, it will return to the staging, the result of the current method to continue the next BeanPostProcessor postProcessBeforeInitialization after initialization method.


  • If you have any questions or errors in this article, please feel free to comment. If you find this article helpful, please like it and follow it.