循環参照?

Seasar使ってたら、こんなエラーが出てきた。

org.seasar.framework.container.CyclicReferenceRuntimeException: [ESSR0047]test.impl.HogeLogicImplで循環参照が発生しました

でも調べても、循環参照なんてしてない。
全然分かんなかったんだが、Tomcatのログを見たら、起動時にdiconに記述したクラスのインスタンス化に失敗していた。

これが関係あるんだろうなぁ、と思い、さらに調査してみて原因を突き止めた。
diconの指定でsingletonと指定されたコンポーネントは、SingletonComponentDeployerによってインスタンス化されるのだけど、そのコードはこういうコードになってる。

public class SingletonComponentDeployer extends AbstractComponentDeployer {

	private Object component_;
	private boolean instantiating_ = false;
...
	private void assemble() {
		if (instantiating_) {
			throw new CyclicReferenceRuntimeException(
				getComponentDef().getComponentClass());
		}
		instantiating_ = true;
		component_ = getConstructorAssembler().assemble();
		instantiating_ = false;
		getPropertyAssembler().assemble(component_);
		getInitMethodAssembler().assemble(component_);
	}
...
}

このSingletonComponentDeployerは、dicon内でsingleton指定された(singletonがデフォルトだけど)クラス1つに対し、1インスタンス出来るようになってる模様。
ここで、実際にコンポーネントインスタンスが作られるのが、この部分。

		component_ = getConstructorAssembler().assemble();

この内部では、インスタンス生成に必要な他のクラスのインスタンスがあればそれも生成している。そのクラスをインスタンス化するには、(そのクラスがシングルトンで指定されていれば)やはりSingletonComponentDeployerが使われるので、再帰的に呼び出されることになる。
そのため、このクラスをインスタンス化するのに必要なクラスはこれで、さらにそのクラスをインスタンス化するにはこれが必要で……と辿っていくと循環参照があったときに無限ループなんてことになるので、循環参照を検知する仕組みがある。それが上のinstantiating_フラグ。インスタンスを生成する上記箇所の前後でこのフラグをオン・オフすることで、循環参照を防いでる。仮に循環参照があったら、CyclicReferenceRuntimeExceptionをスローする仕組みになってる。


さて、ここでインスタンス化する時点(component_ = getConstructor〜〜の時点)で例外が発生してしまうと、フラグが立ったままインスタンス化処理が中断してしまう。


そしてその後インスタンス化しようとしていたコンポーネントを利用しようとすると、最初のインスタンス化時に失敗しているため、もう一回生成しようとする。
だけど生成前に、フラグのチェックをこのように行っているので、

		if (instantiating_) {
			throw new CyclicReferenceRuntimeException(
				getComponentDef().getComponentClass());
		}

インスタンス化途中なのにもう一回呼び出された!これは循環参照に違いない!と勘違いされ、敢え無く例外がスローされることに。

この挙動はちょっと変えて欲しいんですが……、どうでしょうか>ひがさん(id:higayasuo)ほかコミッタの方々。循環参照では無いのだから循環参照というエラーメッセージは出さないで欲しいです。