Spring(10)——beanオブジェクトの変更に使用できるBeanPostProcessor


もっと読む
10 BeanPostProcessor
10.1概要
BeanPostProcessorはSpringで定義されているインターフェースであり、これは前に紹介したInitializingBenとDispposable Beanインターフェースと類似しています.Springは、初期化されたbeanの前後にBenPostProcessorの実現類をフィードバックし、InitializingBeanとDispsable Beanインターフェースとは異なり、BenPostProcessorインターフェースは、すべてのBeanに対して役割を果たします.つまり、Bean初期化前後にBenPostProcessorの実現類をリセットします.対応するbeanでInitializingBeanまたはDispposable Beanインターフェースを実現してこそ、それをリピートすることができる.
BeanPostProcessorインターフェースの定義は以下の通りです.
public interface BeanPostProcessor {

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

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

}
参照したように、BenPostProcessorインターフェースでは、方法postProcessBeforeInitialization()は、1つのbeanが完全に初期化される前にリピートされ、このとき対応するbeanはすでに実装されているが、対応する属性注入などはまだ行われていない.すなわち、InitializingBenのafterPropertiesSet()方法またはbean対応するinit-methodを呼び出す前に.一方、方法postProcessAfterInitialization()は、beanが完全に初期化された後にリフローされ、このとき対応する依存注入が完了した、すなわち、InitializingBenを呼び出すafterPropertiesSet()方法または対応するinit-method方法の後である.両方法のパラメータ及び戻り値は同じ意味であり、パラメータbeanは現在の状態のbeanを表し、パラメータbean Nameは現在のbeanの名前を表し、方法に対応する戻り値はbean容器に入れる必要があるbeanを表しているので、ユーザはこの2つの方法の中でbeanを修正し、即ち自分のbeanをカプセル化して返すことができる.
以下はSpringソースの中でbeanを初期化するロジックです.ソースから見ればapplyBeanPostProcessorsBeforeInitialization()方法で登録されたBenPostProcessorのpostProcessBeforeInitialization()方法を使って順次にフィードバックし、invokeInitMethods()方法で現在のbean対応する初期化方法を順次呼び出します.applyBeanPostProcessorsAfterInitialization方法により、登録されたBeaBenPostProcessorの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;
	}
10.2登録
BeanPostProcessorの登録はとても簡単です.普通のbeanとしてSpringのbean容器に定義するだけで、Springは自動的にそれを検出し、現在のbean容器に登録することができます.BenPostProcessorは容器バインディングであり、BenPostProcessorは同じビーン容器の中のbeanだけをリピートすることができます.つまり、BenPostProcessorは父容器または子容器の中のbeanをリピートすることができません.
Bean容器にBenPostProcessorを定義した後、Springは最初にBenPostProcessorに対応するbeanを具体化します.BenPostProcessorのlazy-initialization=”true”またはdefault-lazy-initialization=”true”を制定したら、Springはそれらを無視します.これもよく分かります.他のbeanを実装する時にのみこれらのBenPostProcessorが役に立ちます.このような仕組みを考えると、Springはbeanを初期化する際にdepends-on属性に指定されたbeanを優先的に初期化します.だから、私達のBenPostProcessorがdepends-onを通じて他のbeanに対する依存を指定した時、他のbeanはBenPostProcessorによって回されないです.もちろんここにも紹介のdepends-on対応するbeanが含まれます.また、BeanPostProcessorの実装後に直接または間接的に注入を行う必要があるbeanも、実用化時間が早まるため、BenPostProcessorにフィードバックされない.また、BenPostProcessorの間ではリピーターが行われません.つまり、BeanPostProcessorsorAはBenPostProcessor初期化時にリピートしません.
BeanPostProcessorはSpring内部でも多く使われています.特にAOPエージェント部分です.ユーザがビーンPostProcessorを実現する必要がある場合も含め、直接または間接的に関連するビーンは、プロキシに成功しないように注意してください.
10.3例
次に簡単に自分をカスタマイズするBeanPostProcessorの例を見て、例としてはBenPostProcessorを簡単に実現し、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;
	}

}
BenPostProcessorを実現したら、bean容器に定義することができます.その定義は普通のbeanの定義と同じです.
<bean class="com.app.HelloBeanPostProcessor"/>
10.4コールバック順序
bean容器の中で複数のBenPostProcessorを同時に定義することができます.このように、新たな実用化の後に各BenPostProcessorを順次使用します.もちろん、あるBenPostProcessorが折り返したら、nullに戻ります.一つのbean容器の中で複数のBenPostProcessorが同時に定義されている場合、デフォルトではBenPostProcessorがbean容器で定義している順序に従って、新しい実用化されたbeanをリピートします.もう一つの方法は、BenPostProcessorのフィードバック順序を定義する方法であり、私たちがカスタマイズしたBeaBenPostProcessorのクラスを同時にOrderedインターフェースを実現し、その後、SpringはOrderedインターフェースによって定義されたgetOrder()方法の戻り値に基づいて、BenPostProcessorのフィードバックの順序を決定し、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;
	}

}
その後、構成時には、getOrder()方法の戻り値をパラメータorderによって指定することができる.
<bean class="com.app.HelloBeanPostProcessor" p:order="3"/>
(注:本文はSpring 4.1.0に基づいて書いています.)
 
 
本論文の転送先:https://elim.iteye.com/blog/2386920