- springmvc+aop+reflect+annotation
- 实现思路:将controller接口配置成切点,然后在切面环绕通知中,获取到请求的version,然后反射执行对应版本号的controller中的方法。
- 配置springmvc的Controller为切入点
<aop:config> <aop:pointcut expression="execution(* com.*.*.*.controller.*Controller.*PlatformEntry(..))" id="platformMethodPoint"/> <aop:aspect ref="platformMethodAspect"> <aop:around method="aroundAdvice" pointcut-ref="platformMethodPoint"/> </aop:aspect> </aop:config> 编写自己的切面,执行环绕通知public class PlatformMethodAspect{ protected Logger logger = LoggerFactory.getLogger(getClass()); private static final String METHOD_VERSION_BUSIDATA = "busiData"; private static final String METHOD_VERSION_BUSIDATA_BASE = "BASE"; private static final String METHOD_VERSION_REQUESTDATA = "requestData"; private static final String METHOD_VERSION_ELEMENT_NAME = "VERSION"; private Map<String,Map<String,String>> versionManager; private static final String SUPPORT_MIN_VERSION = "supportMinVersion"; private static final String DEFAULT_VERSION = "defaultVersion"; private WebApplicationContext appContext; /** * 获取到指定版本号的方法 * @param version 版本号 * @param clazz 目标Controller * @return */ public Method getMethodByVersionAndClass(String entryMethodName,String version,Class<?> clazz){ Method targetMethod = null; Method[] methods = clazz.getMethods(); if(null != methods && methods.length > 0){ for(Method method : methods){ PlatformMethod findAnnotation = AnnotationUtils.findAnnotation(method,PlatformMethod.class); if(null != findAnnotation && entryMethodName.equals(findAnnotation.entryHandlerName()) && version.equals(findAnnotation.version())){ targetMethod = method; break; } } } return targetMethod; } /** * 根据busiData获取version * @param handlerDataValue 业务数据 * @return 返回接口version * @throws DocumentException */ public String getVersionByBusiData(String handlerDataName,String handlerDataValue) throws DocumentException{ InputStream inputStream = new ByteArrayInputStream(handlerDataValue.getBytes()); SAXReader saxReader = new SAXReader(); Document document = null; try { document = saxReader.read(inputStream); } catch (DocumentException e) { logger.error("$$$$$$解析xml busiData,获取version异常,busiData={}",handlerDataValue,e); throw e; } Element rootElement = document.getRootElement(); Element versionElement = null; if(METHOD_VERSION_REQUESTDATA.equals(handlerDataName)){ versionElement = (Element) rootElement.selectSingleNode(METHOD_VERSION_ELEMENT_NAME); }else if(METHOD_VERSION_BUSIDATA.equals(handlerDataName)){ Element baseElement = rootElement.element(METHOD_VERSION_BUSIDATA_BASE); if(null != baseElement){ versionElement = (Element) baseElement.selectSingleNode(METHOD_VERSION_ELEMENT_NAME); }else{ versionElement = (Element) rootElement.selectSingleNode(METHOD_VERSION_ELEMENT_NAME); } } return versionElement == null ? null:versionElement.getText(); } /** * 获取到methodVersion * @param methodParams 请求参数 * @return version值 */ public String getMethodVersion(Map<String,Object> methodParams) throws DocumentException{ String busiDataValue = ""; String requestDataValue = ""; String handlerDataName = ""; String handlerDataValue = ""; if(null == methodParams){ return null; } busiDataValue = (String)methodParams.get(METHOD_VERSION_BUSIDATA); requestDataValue = (String)methodParams.get(METHOD_VERSION_REQUESTDATA); if(StringUtils.isNotEmpty(busiDataValue)){ handlerDataName = METHOD_VERSION_BUSIDATA; handlerDataValue = busiDataValue; }else if(StringUtils.isNotEmpty(requestDataValue)){ handlerDataName = METHOD_VERSION_REQUESTDATA; handlerDataValue = requestDataValue; }else{ return null; } //解析xml,获取到version String version = getVersionByBusiData(handlerDataName,handlerDataValue); return version; } /** * 获取参数 name、value * @param cls 当前class对象 * @param clazzName 目标控制器全类名 * @param methodName 目标控制器方法名 * @param args 目标控制器方法参数 * @return 参数名称、value的map * @throws NotFoundException */ public Map<String,Object> getTargetMethodFields(Class<?> cls, String clazzName, String methodName, Object[] args)throws NotFoundException{ Map<String,Object> map = new HashMap<String,Object>(); //获得类加载(加载所有的CtClass) ClassPool classPool = ClassPool.getDefault(); //设置类加载器的类搜索路径(默认是JVM的classpath,指定用户的classes) ClassClassPath classPath = new ClassClassPath(cls); classPool.insertClassPath(classPath); //CtClass可以操作class文件(获取target handler) CtClass cc; try { cc = classPool.get(clazzName); } catch (NotFoundException e) { logger.error("$$$$$$未找到类名为{}的class",clazzName,e); throw e; } //获取handelr method CtMethod cm; try { cm = cc.getDeclaredMethod(methodName); } catch (NotFoundException e) { logger.error("$$$$$$未找到类名:{},方法名:{}的method",clazzName,methodName,e); throw e; } MethodInfo methodInfo = cm.getMethodInfo(); //获取方法属性 CodeAttribute codeAttribute = methodInfo.getCodeAttribute(); LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag); //目标方法没有参数 if(null == attr){ return null; } //获取方法修饰符 // 非静态的成员函数的第一个参数是this int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1; try { for (int i = 0; i < cm.getParameterTypes().length; i++){ map.put( attr.variableName(i + pos),args[i]);//paramNames即参数名 } } catch (NotFoundException e) { logger.error("$$$$$$未找到类名:{},方法名:{}的参数个数",clazzName,methodName,e); throw e; } return map; } private boolean verifyVersionFormat(String version){ Pattern pattern=Pattern.compile("^([1-9]{0,1}[0-9]{0,1}+(.[0-9]{1,2}))?$"); // 判断小数点后2位的数字的正则表达式 Matcher match=pattern.matcher(version); return match.matches(); } private boolean verifyExeMethodVersion(HttpServletRequest request,String exeMethodVersion){ boolean result = false; String supportMinVersion = this.getServerVersion(request,SUPPORT_MIN_VERSION); if(StringUtils.isEmpty(supportMinVersion)){ result = true; }else{ Double exeVersion = new Double(exeMethodVersion); Double supportVersion = new Double(supportMinVersion); if(exeVersion.compareTo(supportVersion) != -1){ result = true; } } return result; } private String exeTargetMethod(Method methodObj,Class clazz,Object[] paramObjs) throws Exception{ String result = ""; //注意!: 这里不能newInstance,否则不受spring控制 result = (String)methodObj.invoke(clazz.newInstance(), paramObjs);//执行对应版本号的方法 String classSimpleName = clazz.getSimpleName(); classSimpleName = toLowerCaseFirstOne(classSimpleName); Object obj = appContext.getBean(classSimpleName); result = (String)methodObj.invoke(obj, paramObjs);//执行对应版本号的方法 return result; } private String getServerVersion(HttpServletRequest request,String targetVersion){ String versionResult = ""; String serverUrl = request.getRequestURI(); if(null != request && null != versionManager && StringUtils.isNotBlank(serverUrl)){ Map<String,String> versionMap = versionManager.get(serverUrl); if(null != versionMap) versionResult = versionMap.get(targetVersion); } return versionResult; } public static String toLowerCaseFirstOne(String s){ if(Character.isLowerCase(s.charAt(0))) return s; else return (new StringBuilder()).append(Character.toLowerCase(s.charAt(0))).append(s.substring(1)).toString(); } public Map<String, Map<String, String>> getVersionManager() { return versionManager; } public void setVersionManager(Map<String, Map<String, String>> versionManager) { this.versionManager = versionManager; } /** * 环绕通知 * @param joinpoint * @return */ public String aroundAdvice(ProceedingJoinPoint joinpoint) { ResMessage resMessage = new ResMessage(); String targetClassName = joinpoint.getTarget().getClass().getName();//获取目标方法的类名 Class<?> clazz = null;//加载目标class try { clazz = Class.forName(targetClassName); } catch (ClassNotFoundException e) { logger.error("$$$$$$加载className={},异常",targetClassName,e); } String clazzName = clazz.getName();//获取目标类名 String methodName = joinpoint.getSignature().getName(); //获取方法名称 Object[] paramObjs = joinpoint.getArgs(); //获取目标方法参数 //获取目标方法参数名称、值 Map<String, Object> methodParams = new HashMap<String,Object>(); try { methodParams = this.getTargetMethodFields(this.getClass(),clazzName,methodName,paramObjs); } catch (NotFoundException e) { } //从请求参数中,获取version String methodVersion = ""; try { methodVersion = this.getMethodVersion(methodParams); } catch (Exception e) { logger.error("$$$$$$从请求参数中,获取version异常,entryMethodName={},className={}",methodName,clazzName); } //注意!:入口方法请求参数中必须有HttpServletRequest request HttpServletRequest request = (HttpServletRequest )methodParams.get("request"); if(null == appContext){ appContext = (WebApplicationContext)request.getAttribute(DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE); } //执行handler String result = ""; if(StringUtils.isNotBlank(methodVersion)){ //验证上送版本号,是否符合规则 boolean versionFormat = verifyVersionFormat(methodVersion); if(!versionFormat){ logger.error("$$$$$$上送版本号格式不正确,entryMethodName={},version={}的HandlerMethod,className={}",methodName,methodVersion,clazzName); } //校验客户上送版本号是否满足最低版本号控制 boolean verifyMin = verifyExeMethodVersion(request,methodVersion); if(!verifyMin){ logger.error("$$$$$$上送版本号低于支持的最低版本号,entryMethodName={},version={}的HandlerMethod,className={}",methodName,methodVersion,clazzName); } Method methodObj = this.getMethodByVersionAndClass(methodName,methodVersion,clazz);//不为空,找到对应版本号方法 if(null == methodObj){//抛出异常,未找到对应version的method logger.error("$$$$$$未找到entryMethodName={},version={}的HandlerMethod,className={}",methodName,methodVersion,clazzName); }else{ try { result = exeTargetMethod(methodObj,clazz,paramObjs); } catch (Exception e) { logger.error("$$$$$$执行className={},methodName={},version={}异常",clazzName,methodName,methodVersion,e); } } }else{//版本号为空 //获取默认支持版本号 String defaultVersion = getServerVersion(request,DEFAULT_VERSION); if(StringUtils.isNotBlank(defaultVersion)){ Method methodObj = this.getMethodByVersionAndClass(methodName,defaultVersion,clazz);//不为空,找到对应版本号方法 if(null == methodObj){//抛出异常,未找到对应version的method logger.error("$$$$$$未找到默认版本号对应的method,entryMethodName={},version={}的HandlerMethod,className={}",methodName,defaultVersion,clazzName); }else{ try { result = exeTargetMethod(methodObj,clazz,paramObjs); } catch (Exception e) { logger.error("$$$$$$执行className={},methodName={},version={}异常",clazzName,methodName,methodVersion,e); } } }else{ logger.error("$$$$$$未找到entryMethodName={},className={}的默认执行版本号",methodName,clazzName); } } return result; }- 针对服务地址,在切面bean注入配置最低、默认版本号支持
- <bean id="platformMethodAspect" class="PlatformMethodAspect">
<property name="versionManager"> <!-- 管理服务接口,最低、默认版本号 -->
<map>
<entry key="/*/*/*.do">
<map>
<entry key="supportMinVersion" value="1.1"/>
<entry key="defaultVersion" value="1.1"/>
</map>
</entry>
</map>
</property>
</bean>
controller版本号控制
最新推荐文章于 2025-10-05 04:34:29 发布
该博客介绍了如何使用SpringMVC、AOP、反射和注解来实现Controller接口的版本控制。通过配置切入点、编写切面类以及处理不同版本的方法,实现了根据请求的version动态调用对应版本的Controller方法。
3585

被折叠的 条评论
为什么被折叠?



