controller版本号控制

该博客介绍了如何使用SpringMVC、AOP、反射和注解来实现Controller接口的版本控制。通过配置切入点、编写切面类以及处理不同版本的方法,实现了根据请求的version动态调用对应版本的Controller方法。
  1. springmvc+aop+reflect+annotation
  2. 实现思路:将controller接口配置成切点,然后在切面环绕通知中,获取到请求的version,然后反射执行对应版本号的controller中的方法。
  3. 配置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>
  4. 编写自己的切面,执行环绕通知
    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;
    	}
    1. 针对服务地址,在切面bean注入配置最低、默认版本号支持
    2. <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>


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值