可用来修改bean对象的BeanPostProcessor

Posted moxiaotao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了可用来修改bean对象的BeanPostProcessor相关的知识,希望对你有一定的参考价值。

 可用来修改bean对象的BeanPostProcessor

11.1 简介

BeanPostProcessor是Spring中定义的一个接口,其与之前介绍的InitializingBean和DisposableBean接口类似,也是供Spring进行回调的。Spring将在初始化bean前后对BeanPostProcessor实现类进行回调,与InitializingBean和DisposableBean接口不同的是BeanPostProcessor接口将对所有的bean都起作用,即所有的bean初始化前后都会回调BeanPostProcessor实现类,而InitializingBean和DisposableBean接口是针对单个bean的,即只有在对应的bean实现了InitializingBean或DisposableBean接口才会对其进行回调。

BeanPostProcessor接口的定义如下:

public interface BeanPostProcessor {

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

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

}

如你所见,BeanPostProcessor接口中定义了两个方法,其中方法postProcessBeforeInitialization()将在一个bean被完全初始化前进行回调,此时对应的bean已经实例化了,但是对应的属性注入等还没有进行,即在调用InitializingBean的afterPropertiesSet()方法或bean对应的init-method之前;而方法postProcessAfterInitialization()将在bean被完全初始化后进行回调,此时对应的依赖注入已经完成,即在调用InitializingBean的afterPropertiesSet()方法或对应init-method方法之后。两个方法的参数以及返回值对应的意义都是一样的,其中参数bean表示当前状态的bean,参数beanName表示当前bean的名称,而方法对应的返回值即表示需要放入到bean容器中的bean,所以用户如果有需要完全可以在这两个方法中对bean进行修改,即封装自己的bean进行返回。

以下是Spring源码中对bean进行初始化的逻辑,从源码中我们可以看到是先通过applyBeanPostProcessorsBeforeInitialization()方法使用注册的BeanPostProcessor的postProcessBeforeInitialization()方法依次回调,然后是通过invokeInitMethods()方法依次调用当前bean对应的初始化方法,再通过applyBeanPostProcessorsAfterInitialization方法使用注册的BeanPostProcessor的postProcessorAfterInitialization()方法依次进行回调。

	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 注册

BeanPostProcessor的注册是非常简单的,我们只需要把它当做一个普通的bean定义到Spring的bean容器中,Spring将能够自动检测到它,并将它注册到当前的bean容器中。BeanPostProcessor是容器绑定的,即BeanPostProcessor只能对跟它属于同一个bean容器中的bean进行回调,即BeanPostProcessor不能对属于它父容器或子容器中的bean进行回调。

在bean容器中定义了BeanPostProcessor之后,Spring将最先将BeanPostProcessor对应的bean进行实例化,如果我们制定BeanPostProcessor的lazy-initialization=”true”default-lazy-initialization=”true”,Spring将对其进行忽略,即这些配置对BeanPostProcessor不起作用。这也很好理解,因为只有这样之后在实例化其它bean的时候这些BeanPostProcessor才能派上用场。鉴于这种机制,所以这里有一个问题需要注意,Spring在初始化bean的时候将优先初始化depends-on属性指定的bean,所以当我们的BeanPostProcessor通过depends-on指定了对其它bean的依赖时,其它bean是不会被BeanPostProcessor所回调的,当然这里也包括简介的depends-on对应的bean。此外,在BeanPostProcessor实例化后需要直接或间接的进行注入的bean也由于实例化时间提前不会被BeanPostProcessor回调。还有就是BeanPostProcessor之间不会进行回调,即BeanPostProcessorA不会在BeanPostProcessorB初始化时对其进行回调。

BeanPostProcessor在Spring内部也是用的比较多的,尤其是AOP代理部分。包括用户需要自己实现BeanPostProcessor实现代理功能时也需要注意BeanPostProcessor直接或间接关联的bean是不会被回调的,即不会被代理成功的。

11.3 示例

接下来看一个简单的定制自己的BeanPostProcessor的示例,在示例中我们将仅仅简单的实现一个BeanPostProcessor,在postProcessBeforeInitialization()postProcessAfterInitialization()方法中都直接返回对应的bean,然后在postProcessBeforeInitialization()方法中简单的打印一下对应的bean名称。

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;
	}

}

实现了BeanPostProcessor之后就可以将其定义到bean容器中,其定义方式跟普通bean的定义方式是一样的。

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

11.4 回调顺序

在bean容器中我们可以同时定义多个BeanPostProcessor,这样在新实例化一个bean后将依次使用每个BeanPostProcessor回调一遍,当然,如果某一个BeanPostProcessor回调后的返回的bean为null,则不再继续往下回调,将直接返回null,这个时候bean容器中对应beanName对应的bean也是null。当在一个bean容器中同时定义有多个BeanPostProcessor时,默认将根据BeanPostProcessor在bean容器中定义的先后顺序对新实例化的bean进行回调。还有一种定义BeanPostProcessor回调顺序的方法是将我们自定义的BeanPostProcessor实现类同时实现Ordered接口,然后Spring将根据Ordered接口定义的getOrder()方法的返回值来决定BeanPostProcessor回调的先后顺序,getOrder()返回值越小的越先进行回调。此外,实现了Ordered接口的BeanPostProcessor总是比没有实现Ordered接口的BeanPostProcessor先进行回调,为了便于管理,推荐要么都实现Ordered接口,要么都不实现。

以下是一个实现了Ordered接口,并把getOrder()方法的返回值作为一个参数进行配置的示例。

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;
	}

}

之后就可以在配置的时候通过参数order来指定我们的getOrder()方法的返回值。

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

(注:本文是基于Spring4.1.0所写)

以上是关于可用来修改bean对象的BeanPostProcessor的主要内容,如果未能解决你的问题,请参考以下文章

复现一个典型的线上Spring Bean对象的线程安全问题(附三种解决办法)

远程调用接口修改某个单位jsonwe文件读取后转为bean对象

java记录对象前后修改的内容(工具类)

Spring详解Bean的六种作用域执行流程生命周期

远程调用接口修改某个单位jsonwe文件读取后转为bean对象

远程调用接口修改某个单位jsonwe文件读取后转为bean对象