読者です 読者をやめる 読者になる 読者になる

かまたま日記3

プログラミングメイン、たまに日常

SpringAOP(JDK Dynamic Proxy)のDI仕様にハマった

Java Spring

SpringAOPのプロキシ化の仕組みには
JDK dynamic proxyCGLIBという二つの仕組みがあるんですが(デフォルトはJDK dynamic proxy)
JDK dynamic proxyでProxy化されたbeanのインスタンスを直接実装クラス指定でAutowired出来ない仕様らしいです。

※この辺が参考になりました
spring - @Override and @Transactional annotations - Stack Overflow
spring - AspectJ and NoSuchBeanDefinitionException at least 1 bean which qualifies as autowire candidate for this dependency - Stack Overflow

なぜそういう仕様なのか?(の考察)

上記記事によると、インターフェースを介さないメソッドをProxy化する場合のみ
CGLIB proxyingを使いましょうというのがSpringAOPのポリシーらしいです。
それを鑑みて、UserDaoをProxy化したbeanはUserDaoImplではないという判断になっているのではないでしょうか?

CGLIBにした場合の弊害

proxy-target-class="true"でCGLIBに強制した場合、実装クラスに対するAutowiredは上手くいくんですが
今度はProxyingの対象クラスが多いとOOMエラーになってしまうという現象も発生しました。
JDK〜の場合は起こらない。詳細は未調査)

今回の結論

SpringAOPを使う時は基本的にはJDK dynamic proxyでインターフェースに対してプログラミングするのがよさそう。

Javaのコード
@Component
public class UserDaoImpl implements UserDao {
	public void save(User user) {
		// do something
	}

}

@Service
public class SampleService {
	// これはダメ
	//@Autowired
	//UserDaoImpl userDaoImpl;

	@Autowired
	UserDao userDao;

}

@Aspect
public class DaoAspect {
	@Pointcut("execution(public void com.kamatama41.dao.*.*(..))")
	public void executeAnyDaoMethod() {}

	@AfterThrowing(pointcut="executeAnyDaoMethod()", throwing = "ex")
	public void doSomething(DaoException ex) throws Throwable {
	    System.out.println("Dao error!!!!");
	}

}
xml
<context:annotation-config />

<!-- JDK dynamic proxyの場合 -->
<aop:aspectj-autoproxy/>
<!-- CGLIBの場合 -->
<!--<aop:aspectj-autoproxy proxy-target-class="true"/>-->

<bean id="daoAspect " class="com.kamatama41.aspect.DaoAspect " />
発生する例外の例
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: com.kamatama41.UserDaoImpl com.kamatama41.service.SampleService.userDaoImpl; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [com.kamatama41.UserDaoImpl] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:506)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:284)
    ... 14 more