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 44d68b53..8ed8b566 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 @@ -292,4 +292,13 @@ public static void cglibAopProxyDisableCache(CtClass ctClass) throws NotFoundExc LOGGER.debug("org.springframework.aop.framework.CglibAopProxy - cglib Enhancer cache disabled"); } + + @OnClassLoadEvent(classNameRegexp = "org.springframework.web.servlet.DispatcherServlet") + public static void registryHandlerMappings(ClassLoader appClassLoader, CtClass ctClass, ClassPool classPool) throws NotFoundException, CannotCompileException { + CtMethod method = ctClass.getDeclaredMethod("initHandlerMappings", new CtClass[]{ + classPool.get("org.springframework.context.ApplicationContext") + }); + method.insertAfter("org.hotswap.agent.plugin.spring.core.ResetRequestMappingCaches.setHandlerMappings(this.handlerMappings);"); + LOGGER.debug("registry servlet handler mappings"); + } } diff --git a/plugin/hotswap-agent-spring-plugin/src/main/java/org/hotswap/agent/plugin/spring/core/ResetRequestMappingCaches.java b/plugin/hotswap-agent-spring-plugin/src/main/java/org/hotswap/agent/plugin/spring/core/ResetRequestMappingCaches.java index 69a4ad68..64144786 100644 --- a/plugin/hotswap-agent-spring-plugin/src/main/java/org/hotswap/agent/plugin/spring/core/ResetRequestMappingCaches.java +++ b/plugin/hotswap-agent-spring-plugin/src/main/java/org/hotswap/agent/plugin/spring/core/ResetRequestMappingCaches.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2024 the HotswapAgent authors. + * Copyright 2013-2023 the HotswapAgent authors. * * This file is part of HotswapAgent. * @@ -20,20 +20,26 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.hotswap.agent.logging.AgentLogger; +import org.hotswap.agent.util.spring.util.CollectionUtils; import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.web.servlet.HandlerMapping; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; /** * Support for Spring MVC mapping caches. */ public class ResetRequestMappingCaches { - private static AgentLogger LOGGER = AgentLogger.getLogger(ResetRequestMappingCaches.class); + private static final AgentLogger LOGGER = AgentLogger.getLogger(ResetRequestMappingCaches.class); + + public static List handlerMappings; private static Class getHandlerMethodMappingClassOrNull() { try { @@ -46,56 +52,83 @@ private static Class getHandlerMethodMappingClassOrNull() { } public static void reset(DefaultListableBeanFactory beanFactory) { - Class c = getHandlerMethodMappingClassOrNull(); - if (c == null) + if (c == null) { return; + } + try { + // 处理 Servlet 的映射 + processServletMappings(c); + // 处理 Spring 的映射 + processSpringMappings(c, beanFactory); + } catch (Exception e) { + LOGGER.error("Failed to clear HandlerMappings", e); + } + + } + + private static void processSpringMappings(Class c, DefaultListableBeanFactory beanFactory) throws Exception { Map mappings = BeanFactoryUtils.beansOfTypeIncludingAncestors(beanFactory, c, true, false); + if (mappings.isEmpty()) { LOGGER.trace("Spring: no HandlerMappings found"); } + + for (Entry e : mappings.entrySet()) { + processMappings(c, e.getValue()); + } + } + + private static void processServletMappings(Class c) throws Exception { + if (CollectionUtils.isEmpty(handlerMappings)) { + return; + } + for (HandlerMapping handlerMapping : handlerMappings) { + if (handlerMapping instanceof RequestMappingHandlerMapping) { + processMappings(c, handlerMapping); + } + } + } + + private static void processMappings(Class c, Object am) throws Exception { + LOGGER.trace("clearing HandlerMapping for {}", am.getClass()); try { - for (Entry e : mappings.entrySet()) { - Object am = e.getValue(); - LOGGER.trace("Spring: clearing HandlerMapping for {}", am.getClass()); - try { - Field f = c.getDeclaredField("handlerMethods"); - f.setAccessible(true); - ((Map)f.get(am)).clear(); - f = c.getDeclaredField("urlMap"); - f.setAccessible(true); - ((Map)f.get(am)).clear(); - try { - f = c.getDeclaredField("nameMap"); - f.setAccessible(true); - ((Map)f.get(am)).clear(); - } catch(NoSuchFieldException nsfe) { - LOGGER.trace("Probably using Spring 4.0 or below: {}", nsfe.getMessage()); - } - } catch(NoSuchFieldException nsfe) { - LOGGER.trace("Probably using Spring 4.2+", nsfe.getMessage()); - Method m = c.getDeclaredMethod("getHandlerMethods", new Class[0]); - Class[] parameterTypes = new Class[1]; - parameterTypes[0] = Object.class; - Method u = c.getDeclaredMethod("unregisterMapping", parameterTypes); - Map unmodifiableHandlerMethods = (Map) m.invoke(am); - Object[] keys = unmodifiableHandlerMethods.keySet().toArray(); - unmodifiableHandlerMethods = null; - for (Object key : keys) { - LOGGER.trace("Unregistering handler method {}", key); - u.invoke(am, key); - } - } - if (am instanceof InitializingBean) { - ((InitializingBean) am).afterPropertiesSet(); - } + Field f = c.getDeclaredField("handlerMethods"); + f.setAccessible(true); + ((Map) f.get(am)).clear(); + f = c.getDeclaredField("urlMap"); + f.setAccessible(true); + ((Map) f.get(am)).clear(); + try { + f = c.getDeclaredField("nameMap"); + f.setAccessible(true); + ((Map) f.get(am)).clear(); + } catch (NoSuchFieldException nsfe) { + LOGGER.trace("Probably using Spring 4.0 or below: {}", nsfe.getMessage()); } - } catch (Exception e) { - LOGGER.error("Failed to clear HandlerMappings", e); + } catch (NoSuchFieldException nsfe) { + LOGGER.trace("Probably using Spring 4.2+", nsfe.getMessage()); + Method m = c.getDeclaredMethod("getHandlerMethods"); + Class[] parameterTypes = new Class[1]; + parameterTypes[0] = Object.class; + Method u = c.getDeclaredMethod("unregisterMapping", parameterTypes); + Map unmodifiableHandlerMethods = (Map) m.invoke(am); + Object[] keys = unmodifiableHandlerMethods.keySet().toArray(); + unmodifiableHandlerMethods = null; + for (Object key : keys) { + LOGGER.trace("Unregistering handler method {}", key); + u.invoke(am, key); + } + } + if (am instanceof InitializingBean) { + ((InitializingBean) am).afterPropertiesSet(); } + } + public static void setHandlerMappings(List handlerMappings) { + ResetRequestMappingCaches.handlerMappings = handlerMappings; } }