package org.hotswap.agent.plugin.spring;
import org.hotswap.agent.logging.AgentLogger;
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor;
import org.springframework.beans.factory.annotation.InjectionMetadata;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
/**
* Spring Bean post processors contain various caches for performance reasons. Clear the caches on reload.
*
* @author Jiri Bubnik
*/
public class ResetBeanPostProcessorCaches {
private static AgentLogger LOGGER = AgentLogger.getLogger(ResetBeanPostProcessorCaches.class);
private static Class<?> getReflectionUtilsClassOrNull() {
try {
//This is probably a bad idea as Class.forName has lots of issues but this was easiest for now.
return Class.forName("org.springframework.util.ReflectionUtils");
} catch (ClassNotFoundException e) {
LOGGER.trace("Spring 4.1.x or below - ReflectionUtils class not found");
return null;
}
}
/**
* Reset all post processors associated with a bean factory.
*
* @param beanFactory beanFactory to use
*/
public static void reset(DefaultListableBeanFactory beanFactory) {
Class<?> c = getReflectionUtilsClassOrNull();
if (c != null) {
try {
Method m = c.getDeclaredMethod("clearCache");
m.invoke(c);
} catch (Exception version42Failed) {
try {
// spring 4.0.x, 4.1.x without clearCache method, clear manually
Field declaredMethodsCache = c.getDeclaredField("declaredMethodsCache");
declaredMethodsCache.setAccessible(true);
((Map)declaredMethodsCache.get(null)).clear();
Field declaredFieldsCache = c.getDeclaredField("declaredFieldsCache");
declaredFieldsCache.setAccessible(true);
((Map)declaredFieldsCache.get(null)).clear();
} catch (Exception version40Failed) {
LOGGER.debug("Failed to clear Spring 4.x internal method/field cache", version40Failed);
}
}
LOGGER.trace("Cleared Spring 4.2+ internal method/field cache.");
}
for (BeanPostProcessor bpp : beanFactory.getBeanPostProcessors()) {
if (bpp instanceof AutowiredAnnotationBeanPostProcessor) {
resetAutowiredAnnotationBeanPostProcessorCache((AutowiredAnnotationBeanPostProcessor)bpp);
} else if (bpp instanceof InitDestroyAnnotationBeanPostProcessor) {
resetInitDestroyAnnotationBeanPostProcessorCache((InitDestroyAnnotationBeanPostProcessor)bpp);
}
}
}
public static void resetInitDestroyAnnotationBeanPostProcessorCache(InitDestroyAnnotationBeanPostProcessor bpp) {
try {
Field field = InitDestroyAnnotationBeanPostProcessor.class.getDeclaredField("lifecycleMetadataCache");
field.setAccessible(true);
Map lifecycleMetadataCache = (Map) field.get(bpp);
lifecycleMetadataCache.clear();
LOGGER.trace("Cache cleared: InitDestroyAnnotationBeanPostProcessor.lifecycleMetadataCache");
} catch (Exception e) {
throw new IllegalStateException("Unable to clear InitDestroyAnnotationBeanPostProcessor.lifecycleMetadataCache", e);
}
}
// @Autowired cache
public static void resetAutowiredAnnotationBeanPostProcessorCache(AutowiredAnnotationBeanPostProcessor bpp) {
try {
Field field = AutowiredAnnotationBeanPostProcessor.class.getDeclaredField("candidateConstructorsCache");
field.setAccessible(true);
// noinspection unchecked
Map<Class<?>, Constructor<?>[]> candidateConstructorsCache = (Map<Class<?>, Constructor<?>[]>) field.get(bpp);
candidateConstructorsCache.clear();
LOGGER.debug("Cache cleared: AutowiredAnnotationBeanPostProcessor.candidateConstructorsCache");
} catch (Exception e) {
throw new IllegalStateException("Unable to clear AutowiredAnnotationBeanPostProcessor.candidateConstructorsCache", e);
}
try {
Field field = AutowiredAnnotationBeanPostProcessor.class.getDeclaredField("injectionMetadataCache");
field.setAccessible(true);
//noinspection unchecked
Map<Class<?>, InjectionMetadata> injectionMetadataCache = (Map<Class<?>, InjectionMetadata>) field.get(bpp);
injectionMetadataCache.clear();
// noinspection unchecked
LOGGER.debug("Cache cleared: AutowiredAnnotationBeanPostProcessor.injectionMetadataCache");
} catch (Exception e) {
throw new IllegalStateException("Unable to clear AutowiredAnnotationBeanPostProcessor.injectionMetadataCache", e);
}
}
}