Spring で Hotdeploy その5
本命のキャッシュ無効化!
org.zeroturnaround.javarebel.integration.spring.InjectionMetadataCacheCBP を移植?します。
public class InjectionMetadataCacheCBP extends JavassistClassBytecodeProcessor { public void process(ClassPool cp, ClassLoader cl, CtClass ctClass) throws Exception { CtConstructor[] ctrs = ctClass.getConstructors(); for (int i = 0; i < ctrs.length; i++) { ctrs[i].insertAfter( "injectionMetadataCache = new org.zeroturnaround.javarebel.integration.spring.NopMap();"); } } }
このコード内部的に private な HashMap で保持してるキャッシュ を何もしない NopMap に 書き換えてます。private 変数をいじくりたいので、まともな方法だと手がでません。よって、あまりきれいではありませんが、リフレクションを利用して強引にprivate 変数にアクセスします。
キャッシュの無効化の対象は、以下の3つのクラスです。
- org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
- org.springframework.context.annotation.CommonAnnotationBeanPostProcessor
- org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor
全部、BeanPostProcessor インターフェース実装クラスなので、XmlWebApplicationContext#registerBeanPostProcessors メソッドをオーバーライドすることで対応します。
実際のコードは次のような感じです。一部、説明していないクラスとかがありますが、雰囲気をつかんでいただければいいかと。
public class ReloadableXmlWebApplicationContext extends XmlWebApplicationContext { // (略) /** * BeanPostProcessorの登録をおこなうと同時に、 * Hotdeployのために内部キャッシュを無効に設定します。 * @param beanFactory {@link BeanPostProcessor} を登録する {@link BeanFactory} * @see AutowiredAnnotationBeanPostProcessor * @see CommonAnnotationBeanPostProcessor * @see PersistenceAnnotationBeanPostProcessor */ protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) { super.registerBeanPostProcessors(beanFactory); if (DeploymentMode.isDevelopmentMode()) { AbstractAutowireCapableBeanFactory factory = null; if (beanFactory instanceof AbstractAutowireCapableBeanFactory) { factory = (AbstractAutowireCapableBeanFactory) beanFactory; } if (factory != null) { Iterator it = factory.getBeanPostProcessors().iterator(); while (it.hasNext()) { BeanPostProcessor processor = (BeanPostProcessor) it.next(); if (processor instanceof AutowiredAnnotationBeanPostProcessor) { disableCache(AutowiredAnnotationBeanPostProcessor.class, processor, "candidateConstructorsCache"); disableCache(AutowiredAnnotationBeanPostProcessor.class, processor, "injectionMetadataCache"); } else if (processor instanceof CommonAnnotationBeanPostProcessor) { disableCache(CommonAnnotationBeanPostProcessor.class, processor, "injectionMetadataCache"); } else if (processor instanceof PersistenceAnnotationBeanPostProcessor) { disableCache(PersistenceAnnotationBeanPostProcessor.class, processor, "injectionMetadataCache"); } } } } } /** * Reflectionを利用して、内部キャッシュを保持しているMap型の * privateフィールドに対して、{@link NoOperationMap}を設定します。 * @param klass 設定対象クラス * @param target 設定対象インスタンス * @param fieldName 設定対象フィールド */ private void disableCache(Class klass, Object target, String fieldName) { Field cache = ReflectionUtils.findField(klass, fieldName); if (cache != null) { ReflectionUtils.makeAccessible(cache); ReflectionUtils.setField(cache, target, new NoOperationMap()); } } }
ちなみにSpringでHotdeployの解説が終わり次第、まとめてどっかに公開する予定です。