Spring (11) - BeanPostProcessor that can be used to modify bean objects

11 BeanPostProcessor

11.1 Introduction

BeanPostProcessor is an interface defined in Spring, which is similar to the InitializingBean and DisposableBean interfaces introduced earlier, and is also used for Spring to call back. Spring will call back the BeanPostProcessor implementation class before and after bean initialization. Unlike the InitializingBean and DisposableBean interfaces, the BeanPostProcessor interface will work on all beans, that is, all beans will call back the BeanPostProcessor implementation class before and after initialization, while the InitializingBean and DisposableBean interfaces are For a single bean, that is, only when the corresponding bean implements the InitializingBean or DisposableBean interface will it be called back.

The definition of the BeanPostProcessor interface is as follows:

public interface BeanPostProcessor {

	Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;

	Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;

}

As you can see, two methods are defined in the BeanPostProcessor interface. The method postProcessBeforeInitialization()will be called back before a bean is fully initialized. At this time, the corresponding bean has been instantiated, but the corresponding attribute injection has not been carried out, that is, when the call is made The method of InitializingBean afterPropertiesSet()or the corresponding init-method of the bean; and the method postProcessAfterInitialization()will be called after the bean is completely initialized, and the corresponding dependency injection has been completed at this time, that is, after calling the afterPropertiesSet()method of InitializingBean or the corresponding init-method method. The meanings of the parameters and return values ​​of the two methods are the same. The parameter bean represents the bean in the current state, the parameter beanName represents the name of the current bean, and the return value corresponding to the method represents the bean that needs to be put into the bean container. , so the user can completely modify the bean in these two methods if necessary, that is, encapsulate his own bean for return.

The following is the logic of bean initialization in the Spring source code. From the source code, we can see that the applyBeanPostProcessorsBeforeInitialization()method is used to call back the registered BeanPostProcessor in postProcessBeforeInitialization()turn, and then invokeInitMethods()the initialization method corresponding to the current bean is called in turn by the method, and then applyBeanPostProcessorsAfterInitializationregistered by the method. The methods of the BeanPostProcessor in postProcessorAfterInitialization()turn make callbacks.

	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 {
			invokeAwareMethods(beanName, bean);
		}

		Object wrappedBean = bean;
		if (mbd == null || !mbd.isSynthetic()) {
			wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
		}

		try {
			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()) {
			wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
		}
		return wrappedBean;
	}

11.2 Registration

The registration of BeanPostProcessor is very simple, we just need to define it as a normal bean in Spring's bean container, Spring will be able to detect it automatically and register it in the current bean container. BeanPostProcessor is container-bound, that is, BeanPostProcessor can only call back beans belonging to the same bean container, that is, BeanPostProcessor cannot call back beans belonging to its parent container or child container.

After the BeanPostProcessor is defined in the bean container, Spring will first instantiate the bean corresponding to the BeanPostProcessor. If we specify the lazy-initialization=”true”or of the BeanPostProcessor default-lazy-initialization=”true”, Spring will ignore it, that is, these configurations have no effect on the BeanPostProcessor. This is also understandable, because only then will these BeanPostProcessors come in handy when instantiating other beans. In view of this mechanism, there is a problem that needs to be noted here. Spring will first initialize the bean specified by the depends-on attribute when initializing the bean, so when our BeanPostProcessor specifies dependencies on other beans through depends-on, other beans It will not be called back by BeanPostProcessor. Of course, it also includes the bean corresponding to the brief depends-on. In addition, beans that need to be injected directly or indirectly after the BeanPostProcessor is instantiated will not be called back by the BeanPostProcessor due to the early instantiation time. There is no callback between BeanPostProcessors, that is, BeanPostProcessorA will not call back when BeanPostProcessorB is initialized.

BeanPostProcessor is also used more inside Spring, especially the AOP proxy part. Including that users need to implement the BeanPostProcessor themselves to implement the proxy function, they also need to pay attention that the beans directly or indirectly associated with the BeanPostProcessor will not be called back, that is, they will not be successfully proxied.

11.3 Examples

Next, let's look at a simple example of customizing our own BeanPostProcessor. In the example, we will simply implement a BeanPostProcessor, return the corresponding bean directly in the postProcessBeforeInitialization()and method, and then simply print the corresponding bean name in the method.postProcessAfterInitialization()postProcessBeforeInitialization()

public class HelloBeanPostProcessor implements BeanPostProcessor {

	public Object postProcessBeforeInitialization(Object bean, String beanName)
			throws BeansException {
		System.out.println("beanName-----------" + beanName);
		return bean;
	}

	public Object postProcessAfterInitialization(Object bean, String beanName)
			throws BeansException {
		return bean;
	}

}

After implementing BeanPostProcessor, it can be defined in the bean container, and its definition method is the same as that of ordinary beans.

<bean class="com.app.HelloBeanPostProcessor"/>

11.4 Callback order

In the bean container, we can define multiple BeanPostProcessors at the same time, so that after a new bean is instantiated, each BeanPostProcessor will be used to call back again. Of course, if the returned bean after a BeanPostProcessor callback is null, it will not continue. The callback will directly return null. At this time, the bean corresponding to the beanName in the bean container is also null. When multiple BeanPostProcessors are defined in a bean container at the same time, by default, the newly instantiated beans will be called back according to the order in which the BeanPostProcessors are defined in the bean container. Another way to define the order of BeanPostProcessor callbacks is to implement our custom BeanPostProcessor implementation class to implement the Ordered interface at the same time, and then Spring will getOrder()determine the order of BeanPostProcessor callbacks according to the return value of the method defined by the Ordered interface. The getOrder()smaller the return value, the first Make a callback. In addition, the BeanPostProcessor that implements the Ordered interface always calls back before the BeanPostProcessor that does not implement the Ordered interface. For ease of management, it is recommended to implement either the Ordered interface or none at all.

The following is an example that implements the Ordered interface and getOrder()configures the method's return value as a parameter.

public class HelloBeanPostProcessor implements BeanPostProcessor, Ordered {

	private int order;
	
	public Object postProcessBeforeInitialization(Object bean, String beanName)
			throws BeansException {
		System.out.println("beanName-----------" + beanName);
		return bean;
	}

	public Object postProcessAfterInitialization(Object bean, String beanName)
			throws BeansException {
		return bean;
	}

	public void setOrder(int order) {
		this.order = order;
	}
	
	public int getOrder() {
		return order;
	}

}

After that, we can specify getOrder()the return value of our method through the parameter order during configuration.

<bean class="com.app.HelloBeanPostProcessor" p:order="3"/>

(Note: This article is written based on Spring 4.1.0)

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326333789&siteId=291194637