package com.sishuok.spring.dynamic; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConstructorArgumentValues; import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.GenericBeanDefinition; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.ApplicationContext; import org.springframework.scripting.groovy.GroovyScriptFactory; import org.springframework.scripting.support.ScriptFactoryPostProcessor; import org.springframework.util.*; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.method.HandlerMethodSelector; import org.springframework.web.servlet.mvc.method.RequestMappingInfo; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Map; import java.util.Set; /** * <p>User: Zhang Kaitao * <p>Date: 14-1-3 * <p>Version: 1.0 */ public class DynamicDeployBeans2 { protected static final Log logger = LogFactory.getLog(DynamicDeployBeans2.class); //RequestMappingHandlerMapping private static Method detectHandlerMethodsMethod = ReflectionUtils.findMethod(RequestMappingHandlerMapping.class, "detectHandlerMethods", Object.class); private static Method getMappingForMethodMethod = ReflectionUtils.findMethod(RequestMappingHandlerMapping.class, "getMappingForMethod", Method.class, Class.class); private static Method getMappingPathPatternsMethod = ReflectionUtils.findMethod(RequestMappingHandlerMapping.class, "getMappingPathPatterns", RequestMappingInfo.class); private static Method getPathMatcherMethod = ReflectionUtils.findMethod(RequestMappingHandlerMapping.class, "getPathMatcher"); private static Field handlerMethodsField = ReflectionUtils.findField(RequestMappingHandlerMapping.class, "handlerMethods", Map.class); private static Field urlMapField = ReflectionUtils.findField(RequestMappingHandlerMapping.class, "urlMap", MultiValueMap.class); private static final String SCRIPT_FACTORY_POST_PROCESSOR_BEAN_NAME = "org.springframework.scripting.config.scriptFactoryPostProcessor"; private Long refreshCheckDelay = -1L; static { detectHandlerMethodsMethod.setAccessible(true); getMappingForMethodMethod.setAccessible(true); getMappingPathPatternsMethod.setAccessible(true); getPathMatcherMethod.setAccessible(true); handlerMethodsField.setAccessible(true); urlMapField.setAccessible(true); } private ApplicationContext ctx; private DefaultListableBeanFactory beanFactory; private boolean hasRegisterScriptFactoryPostProcessor = false; @Autowired public void setApplicationContext(ApplicationContext ctx) { if (!DefaultListableBeanFactory.class.isAssignableFrom(ctx.getAutowireCapableBeanFactory().getClass())) { throw new IllegalArgumentException("BeanFactory must be DefaultListableBeanFactory type"); } this.ctx = ctx; this.beanFactory = (DefaultListableBeanFactory) ctx.getAutowireCapableBeanFactory(); } public void setRefreshCheckDelay(Long refreshCheckDelay) { this.refreshCheckDelay = refreshCheckDelay; } public void registerBean(Class<?> beanClass) { registerBean(null, beanClass); } public void registerBean(String beanName, Class<?> beanClass) { Assert.notNull(beanClass, "register bean class must not null"); GenericBeanDefinition bd = new GenericBeanDefinition(); bd.setBeanClass(beanClass); if (StringUtils.hasText(beanName)) { beanFactory.registerBeanDefinition(beanName, bd); } else { BeanDefinitionReaderUtils.registerWithGeneratedName(bd, beanFactory); } } public void registerController(Class<?> controllerClass) { Assert.notNull(controllerClass, "register controller bean class must not null"); if (!WebApplicationContext.class.isAssignableFrom(ctx.getClass())) { throw new IllegalArgumentException("applicationContext must be WebApplicationContext type"); } GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); beanDefinition.setBeanClass(controllerClass); String controllerBeanName = BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, beanFactory); addHandler(controllerBeanName); } public void registerGroovyController(String scriptLocation) { registerScriptFactoryPostProcessorIfNecessary(); // Create script factory bean definition. GenericBeanDefinition bd = new GenericBeanDefinition(); bd.setBeanClassName(GroovyScriptFactory.class.getName()); bd.setAttribute(ScriptFactoryPostProcessor.LANGUAGE_ATTRIBUTE, "groovy"); bd.setAttribute(ScriptFactoryPostProcessor.REFRESH_CHECK_DELAY_ATTRIBUTE, refreshCheckDelay); bd.setAttribute(ScriptFactoryPostProcessor.PROXY_TARGET_CLASS_ATTRIBUTE, true); ConstructorArgumentValues cav = bd.getConstructorArgumentValues(); int constructorArgNum = 0; cav.addIndexedArgumentValue(constructorArgNum++, scriptLocation); String controllerBeanName = scriptLocation; beanFactory.registerBeanDefinition(controllerBeanName, bd); addHandler(controllerBeanName); } private void addHandler(String controllerBeanName) { RequestMappingHandlerMapping requestMappingHandlerMapping = requestMappingHandlerMapping(); //remove old Class<?> handlerType = ctx.getType(controllerBeanName); final Class<?> userType = ClassUtils.getUserClass(handlerType); Map handlerMethods = (Map) ReflectionUtils.getField(handlerMethodsField, requestMappingHandlerMapping); MultiValueMap urlMapping = (MultiValueMap) ReflectionUtils.getField(urlMapField, requestMappingHandlerMapping); final RequestMappingHandlerMapping innerRequestMappingHandlerMapping = requestMappingHandlerMapping; Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new ReflectionUtils.MethodFilter() { @Override public boolean matches(Method method) { return ReflectionUtils.invokeMethod( getMappingForMethodMethod, innerRequestMappingHandlerMapping, method, userType) != null; } }); for (Method method : methods) { RequestMappingInfo mapping = (RequestMappingInfo) ReflectionUtils.invokeMethod(getMappingForMethodMethod, requestMappingHandlerMapping, method, userType); handlerMethods.remove(mapping); Set<String> patterns = (Set<String>) ReflectionUtils.invokeMethod(getMappingPathPatternsMethod, requestMappingHandlerMapping, mapping); PathMatcher pathMatcher = (PathMatcher) ReflectionUtils.invokeMethod(getPathMatcherMethod, requestMappingHandlerMapping); for (String pattern : patterns) { if (!pathMatcher.isPattern(pattern)) { urlMapping.remove(pattern); } } } //spring 3.1 开始 ReflectionUtils.invokeMethod(detectHandlerMethodsMethod, requestMappingHandlerMapping, controllerBeanName); } private RequestMappingHandlerMapping requestMappingHandlerMapping() { try { return ctx.getBean(RequestMappingHandlerMapping.class); } catch (Exception e) { throw new IllegalArgumentException("applicationContext must has RequestMappingHandlerMapping"); } } private void registerScriptFactoryPostProcessorIfNecessary() { if (!hasRegisterScriptFactoryPostProcessor) { hasRegisterScriptFactoryPostProcessor = beanFactory.containsBeanDefinition(SCRIPT_FACTORY_POST_PROCESSOR_BEAN_NAME); if (!hasRegisterScriptFactoryPostProcessor) { BeanDefinition beanDefinition = new RootBeanDefinition(ScriptFactoryPostProcessor.class); beanFactory.registerBeanDefinition(SCRIPT_FACTORY_POST_PROCESSOR_BEAN_NAME, beanDefinition); beanFactory.addBeanPostProcessor(beanFactory.getBean(ScriptFactoryPostProcessor.class)); } } } }