package org.hotswap.agent.plugin.spring; import org.hotswap.agent.logging.AgentLogger; import org.hotswap.agent.util.ReflectionHelper; import org.springframework.beans.CachedIntrospectionResults; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.core.GenericTypeResolver; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.util.ReflectionUtils; import java.lang.reflect.Field; import java.util.Map; /** * Reset various Spring static caches. It is safe to run multiple times, * basically every time any configuration is changed. * * @author Jiri Bubnik */ public class ResetSpringStaticCaches { private static AgentLogger LOGGER = AgentLogger.getLogger(ResetSpringStaticCaches.class); /** * Spring bean by type cache. * * Cache names change between versions, call via reflection and ignore errors. */ public static void resetBeanNamesByType(DefaultListableBeanFactory defaultListableBeanFactory) { try { Field field = DefaultListableBeanFactory.class.getDeclaredField("singletonBeanNamesByType"); field.setAccessible(true); // noinspection unchecked Map singletonBeanNamesByType = (Map) field.get(defaultListableBeanFactory); singletonBeanNamesByType.clear(); } catch (Exception e) { LOGGER.trace("Unable to clear DefaultListableBeanFactory.singletonBeanNamesByType cache (is Ok for pre 3.1.2 Spring version)", e); } try { Field field = DefaultListableBeanFactory.class.getDeclaredField("allBeanNamesByType"); field.setAccessible(true); // noinspection unchecked Map allBeanNamesByType = (Map) field.get(defaultListableBeanFactory); allBeanNamesByType.clear(); } catch (Exception e) { LOGGER.trace("Unable to clear allBeanNamesByType cache (is Ok for pre 3.2 Spring version)"); } try { Field field = DefaultListableBeanFactory.class.getDeclaredField("nonSingletonBeanNamesByType"); field.setAccessible(true); // noinspection unchecked Map nonSingletonBeanNamesByType = (Map) field.get(defaultListableBeanFactory); nonSingletonBeanNamesByType.clear(); } catch (Exception e) { LOGGER.debug("Unable to clear nonSingletonBeanNamesByType cache (is Ok for pre 3.2 Spring version)"); } } /** * Reset all caches. */ public static void reset() { resetTypeVariableCache(); resetAnnotationUtilsCache(); resetReflectionUtilsCache(); resetPropetyCache(); CachedIntrospectionResults.clearClassLoader(ResetSpringStaticCaches.class.getClassLoader()); } private static void resetTypeVariableCache() { try { Field field = GenericTypeResolver.class.getDeclaredField("typeVariableCache"); field.setAccessible(true); // noinspection unchecked Map<Class, Map> typeVariableCache = (Map<Class, Map>) field.get(null); typeVariableCache.clear(); LOGGER.trace("Cache cleared: GenericTypeResolver.typeVariableCache"); } catch (Exception e) { throw new IllegalStateException("Unable to clear GenericTypeResolver.typeVariableCache", e); } } private static void resetReflectionUtilsCache() { Map declaredMethodsCache = (Map) ReflectionHelper.getNoException(null, ReflectionUtils.class, "declaredMethodsCache"); if (declaredMethodsCache != null) { declaredMethodsCache.clear(); LOGGER.trace("Cache cleared: ReflectionUtils.declaredMethodsCache"); } else { LOGGER.trace("Cache NOT cleared: ReflectionUtils.declaredMethodsCache not exists"); } } private static void resetAnnotationUtilsCache() { Map annotatedInterfaceCache = (Map) ReflectionHelper.getNoException(null, AnnotationUtils.class, "annotatedInterfaceCache"); if (annotatedInterfaceCache != null) { annotatedInterfaceCache.clear(); LOGGER.trace("Cache cleared: AnnotationUtils.annotatedInterfaceCache"); } else { LOGGER.trace("Cache NOT cleared: AnnotationUtils.annotatedInterfaceCache not exists in target Spring verion (pre 3.1.x)"); } Map findAnnotationCache = (Map) ReflectionHelper.getNoException(null, AnnotationUtils.class, "findAnnotationCache"); if (findAnnotationCache != null) { findAnnotationCache.clear(); LOGGER.trace("Cache cleared: AnnotationUtils.findAnnotationCache"); } else { LOGGER.trace("Cache NOT cleared: AnnotationUtils.findAnnotationCache not exists in target Spring version (pre 4.1)"); } } private static void resetPropetyCache() { try { ClassLoader classLoader = ResetSpringStaticCaches.class.getClassLoader(); Map annotationCache = (Map) ReflectionHelper.get(null, classLoader.loadClass("org.springframework.core.convert.Property"), "annotationCache"); annotationCache.clear(); LOGGER.trace("Cache cleared: Property.annotationCache"); } catch (Exception e) { LOGGER.trace("Unable to clear Property.annotationCache (ok before Spring 3.2.x)", e); } } }