diff --git a/plugin/hotswap-agent-spring-plugin/src/main/java/org/hotswap/agent/plugin/spring/SpringPlugin.java b/plugin/hotswap-agent-spring-plugin/src/main/java/org/hotswap/agent/plugin/spring/SpringPlugin.java index 562b82ae..7319b1f1 100644 --- a/plugin/hotswap-agent-spring-plugin/src/main/java/org/hotswap/agent/plugin/spring/SpringPlugin.java +++ b/plugin/hotswap-agent-spring-plugin/src/main/java/org/hotswap/agent/plugin/spring/SpringPlugin.java @@ -51,15 +51,7 @@ import org.hotswap.agent.plugin.spring.reload.XmlsChangedCommand; import org.hotswap.agent.plugin.spring.reload.YamlChangedCommand; import org.hotswap.agent.plugin.spring.scanner.SpringBeanWatchEventListener; -import org.hotswap.agent.plugin.spring.transformers.BeanFactoryTransformer; -import org.hotswap.agent.plugin.spring.transformers.ClassPathBeanDefinitionScannerTransformer; -import org.hotswap.agent.plugin.spring.transformers.ConfigurationClassPostProcessorTransformer; -import org.hotswap.agent.plugin.spring.transformers.InitDestroyAnnotationBeanPostProcessorTransformer; -import org.hotswap.agent.plugin.spring.transformers.PlaceholderConfigurerSupportTransformer; -import org.hotswap.agent.plugin.spring.transformers.PostProcessorRegistrationDelegateTransformer; -import org.hotswap.agent.plugin.spring.transformers.ProxyReplacerTransformer; -import org.hotswap.agent.plugin.spring.transformers.ResourcePropertySourceTransformer; -import org.hotswap.agent.plugin.spring.transformers.XmlBeanDefinitionScannerTransformer; +import org.hotswap.agent.plugin.spring.transformers.*; import org.hotswap.agent.util.HotswapTransformer; import org.hotswap.agent.util.IOUtils; import org.hotswap.agent.util.PluginManagerInvoker; @@ -81,7 +73,8 @@ XmlBeanDefinitionScannerTransformer.class, PostProcessorRegistrationDelegateTransformer.class, BeanFactoryTransformer.class, - InitDestroyAnnotationBeanPostProcessorTransformer.class}) + InitDestroyAnnotationBeanPostProcessorTransformer.class, + SpringMvcTransformer.class}) @Versions(manifest = {@Manifest(value = "[3.1.0,)", versionName= Name.ImplementationVersion, names={ @Name(key=Name.ImplementationTitle,value="spring-core")})}) public class SpringPlugin { diff --git a/plugin/hotswap-agent-spring-plugin/src/main/java/org/hotswap/agent/plugin/spring/core/ResetRequestParameterCaches.java b/plugin/hotswap-agent-spring-plugin/src/main/java/org/hotswap/agent/plugin/spring/core/ResetRequestParameterCaches.java new file mode 100644 index 00000000..d116a3d0 --- /dev/null +++ b/plugin/hotswap-agent-spring-plugin/src/main/java/org/hotswap/agent/plugin/spring/core/ResetRequestParameterCaches.java @@ -0,0 +1,79 @@ +/* + * Copyright 2013-2024 the HotswapAgent authors. + * + * This file is part of HotswapAgent. + * + * HotswapAgent is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 2 of the License, or (at your + * option) any later version. + * + * HotswapAgent is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with HotswapAgent. If not, see http://www.gnu.org/licenses/. + */ +package org.hotswap.agent.plugin.spring.core; + +import org.hotswap.agent.logging.AgentLogger; +import org.hotswap.agent.util.ReflectionHelper; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * Support for Spring MVC Parameter caches. + */ +public class ResetRequestParameterCaches { + + private static AgentLogger LOGGER = AgentLogger.getLogger(ResetRequestParameterCaches.class); + + private static Set localParameterNameDiscovers = new HashSet<>(); + + private static Class getLocalVariableTableParameterNameDiscovererClassOrNull() { + 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.core.LocalVariableTableParameterNameDiscoverer"); + } catch (ClassNotFoundException e) { + LOGGER.trace("LocalVariableTableParameterNameDiscoverer class not found"); + return null; + } + } + + public static void setLocalParameterNameDiscovers(Object localVariableNameDiscoverer) { + localParameterNameDiscovers.add(localVariableNameDiscoverer); + } + + public static void reset(DefaultListableBeanFactory beanFactory, Set> clazzes) { + + Class c = getLocalVariableTableParameterNameDiscovererClassOrNull(); + if (c == null) { + return; + } + + try { + for (Object localParameterNameDiscover : localParameterNameDiscovers) { + Object parameterNamesCacheObj = ReflectionHelper.getNoException(localParameterNameDiscover, c, "parameterNamesCache"); + if (parameterNamesCacheObj == null) { + return; + } + if (!(parameterNamesCacheObj instanceof Map)) { + return; + } + + Map parameterNamesCache = (Map) parameterNamesCacheObj; + for (Class clazz : clazzes) { + parameterNamesCache.remove(clazz); + } + } + } catch (Exception e) { + LOGGER.error("Failed to clear parameterNamesCache", e); + } + } + +} diff --git a/plugin/hotswap-agent-spring-plugin/src/main/java/org/hotswap/agent/plugin/spring/reload/SpringBeanReload.java b/plugin/hotswap-agent-spring-plugin/src/main/java/org/hotswap/agent/plugin/spring/reload/SpringBeanReload.java index c92e6602..56203e5f 100644 --- a/plugin/hotswap-agent-spring-plugin/src/main/java/org/hotswap/agent/plugin/spring/reload/SpringBeanReload.java +++ b/plugin/hotswap-agent-spring-plugin/src/main/java/org/hotswap/agent/plugin/spring/reload/SpringBeanReload.java @@ -527,6 +527,7 @@ private void clearSpringCache() { // ResetBeanFactoryCaches.reset(beanFactory); ConfigurationClassPostProcessorEnhance.getInstance(beanFactory).resetConfigurationClassPostProcessor(beanFactory); ResetAnnotationCache.resetAnnotationScanner(beanFactory); + ResetRequestParameterCaches.reset(beanFactory, classes); } private void clearLocalCache() { diff --git a/plugin/hotswap-agent-spring-plugin/src/main/java/org/hotswap/agent/plugin/spring/transformers/SpringMvcTransformer.java b/plugin/hotswap-agent-spring-plugin/src/main/java/org/hotswap/agent/plugin/spring/transformers/SpringMvcTransformer.java new file mode 100644 index 00000000..8b7a1fa1 --- /dev/null +++ b/plugin/hotswap-agent-spring-plugin/src/main/java/org/hotswap/agent/plugin/spring/transformers/SpringMvcTransformer.java @@ -0,0 +1,38 @@ +/* + * Copyright 2013-2024 the HotswapAgent authors. + * + * This file is part of HotswapAgent. + * + * HotswapAgent is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 2 of the License, or (at your + * option) any later version. + * + * HotswapAgent is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with HotswapAgent. If not, see http://www.gnu.org/licenses/. + */ +package org.hotswap.agent.plugin.spring.transformers; + +import org.hotswap.agent.annotation.OnClassLoadEvent; +import org.hotswap.agent.javassist.*; +import org.hotswap.agent.logging.AgentLogger; + +public class SpringMvcTransformer { + + private static final AgentLogger LOGGER = AgentLogger.getLogger(SpringMvcTransformer.class); + + @OnClassLoadEvent(classNameRegexp = "org.springframework.core.LocalVariableTableParameterNameDiscoverer") + public static void replaceLocalVariableTableParameterNameDiscoverer(CtClass ctClass) throws NotFoundException, CannotCompileException { + CtConstructor[] constructors = ctClass.getConstructors(); + for (CtConstructor ctConstructor : constructors) { + ctConstructor.insertAfter(" {org.hotswap.agent.plugin.spring.core.ResetRequestMappingCaches.setLocalParameterNameDiscovers(this);} "); + } + + LOGGER.debug("LocalVariableTableParameterNameDiscoverer patched"); + } +} \ No newline at end of file