package com.zzwtec.wechat.rpc.inject; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.jfinal.kit.StrKit; import com.zzwtec.wechat.common.ErrorMsg; import com.zzwtec.wechat.config.ServiceConfig; import com.zzwtec.wechat.config.WeChatConfig; import com.zzwtec.wechat.rpc.APIResponse; import com.zzwtec.wechat.rpc.annotation.ZZWApiAction; import com.zzwtec.wechat.util.HttpUtil; import com.zzwtec.wechat.util.security.EncoderHandler; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.lang.invoke.MethodHandles; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.lang.reflect.Proxy; import java.text.MessageFormat; import java.util.HashMap; import java.util.Map; /** *@author 毛文超 * */ public class ProxyBuilder { private static final Map PROXY_MAP = new HashMap<>(10); @SuppressWarnings("unchecked") public static T build(Class target, ServiceConfig serviceConfig){ String name = target.getName(); String serviceName = serviceConfig.getServiceName(); Object proxyObject = PROXY_MAP.get(name + ":" + serviceName); if(proxyObject != null){ return (T)proxyObject; } Map methodMaps = AbstractMethodUtils.getMethodMaps(target); Object proxyInstance = Proxy.newProxyInstance(target.getClassLoader(), new Class[]{target}, (o, method, objects) -> { //对default方法做特殊处理 if (method.isDefault()) { Constructor constructor = MethodHandles.Lookup.class .getDeclaredConstructor(Class.class, int.class); constructor.setAccessible(true); Class declaringClass = method.getDeclaringClass(); int allModes = MethodHandles.Lookup.PUBLIC | MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED | MethodHandles.Lookup.PACKAGE; return constructor.newInstance(declaringClass, allModes) .unreflectSpecial(method, declaringClass) .bindTo(o) .invokeWithArguments(objects); } //非默认方法 ZZWApiAction zzwApiAction = method.getAnnotation(ZZWApiAction.class); if (zzwApiAction == null) { throw new RuntimeException("作为服务接口的方法必须使用ZZWApiAction注解修饰"); } //返回值类型 Class returnType = method.getReturnType(); //如果返回值类型不对就抛出异常 if (returnType != APIResponse.class) { throw new RuntimeException("作为服务接口的方法返回值必须是APIResponse!"); } //开始进入调用逻辑 //获取服务接口 String url = zzwApiAction.value(); //组装参数 String[] paraNames = methodMaps.get(method.toString()); JSONObject data = AbstractApiServiceUtils.buildData(url,paraNames, objects); return AbstractApiServiceUtils.post(serviceConfig,url, data); }); PROXY_MAP.put(target.getName() + ":" + serviceName,proxyInstance); return (T)proxyInstance; } /** * 方法反射工具类,用于获取方法的参数名称 * */ abstract static class AbstractMethodUtils { static Map getMethodMaps(Class clazz){ Method[] methods = clazz.getMethods(); Map result = new HashMap<>(methods.length); for (Method method : methods) { Parameter[] parameters = method.getParameters(); String[] parameterNames = new String[parameters.length]; for (int i = 0; i < parameters.length; i++) { Parameter parameter = parameters[i]; parameterNames[i] = parameter.getName(); } result.put(method.toString(),parameterNames); } return result; } } abstract static class AbstractApiServiceUtils { private static final Logger logger = LoggerFactory.getLogger(AbstractApiServiceUtils.class); private static String getRequestId() { return String.format("%s-%d", "zzwtec-wechat", System.currentTimeMillis()); } static JSONObject buildData(String url,String[] paraNames, Object... args){ JSONObject requestJSON = new JSONObject(); requestJSON.put("requestId",getRequestId()); int indexOf = url.indexOf("$"); String substring = url.substring(0, indexOf); //如果参数只有一个或者没有参数 if(args == null || args.length <= 1){ //没有参数直接返回 if(args == null || args.length == 0){ return requestJSON; } if(!StringUtils.equalsIgnoreCase(substring,"other")){ requestJSON.put("data",args[0]); return requestJSON; } } JSONObject dataJson = new JSONObject(); for (int i = 0; i < args.length; i++) { dataJson.put(paraNames[i],args[i]); } if(StringUtils.equalsIgnoreCase(substring,"api")){ requestJSON.putAll(dataJson); }else{ requestJSON.put("data",dataJson); } return requestJSON; } public static APIResponse post(ServiceConfig serviceConfig, String path, JSONObject postJson) { String json = postJson.toJSONString(); //开始拼装服务的url int indexOf = path.indexOf("$"); String baseUrlKey = path.substring(0, indexOf); String servicePath = path.substring(indexOf + 1, path.length()); String serviceUrl = createURL(serviceConfig,baseUrlKey,servicePath); if(StrKit.isBlank(serviceUrl)){ return new APIResponse(1, ErrorMsg._1); } Map headers = createHeaders(json); String resultJson = HttpUtil.postJSON(serviceUrl, headers, json); JSONObject jsonObject = JSON.parseObject(resultJson); // 验证响应状态 if (jsonObject.containsKey("status") && jsonObject.getInteger("status") != 200) { logger.warn(MessageFormat.format("\n value:{0} \n request:{1} \n response:{2}", serviceUrl, postJson, resultJson)); return new APIResponse(1, ErrorMsg._1); } APIResponse response; if(jsonObject.containsKey("error")){ response = new APIResponse(jsonObject.getIntValue("error"), jsonObject.getString("errormsg")); }else{ response = new APIResponse(jsonObject.getIntValue("code"), jsonObject.getString("msg"), jsonObject.getString("data")); } // 转换执行结果 // 执行成功则转换msg,执行失败则记录结果 if (response.getCode() == 0) { response.setMsg(ErrorMsg._0); } else { if (response.getCode() == 1) { response.setMsg(ErrorMsg._1); } } logger.info(MessageFormat.format("\n value:{0} \n request:{1} \n response:{2}", serviceUrl, postJson, resultJson)); return response; } static String createURL(ServiceConfig serviceConfig, String baseServiceKey, String path) { return String.format("%s/%s", serviceConfig.getBaseServiceUrl(baseServiceKey), path); } static Map createHeaders(String postData) { Map headers = new HashMap<>(); headers.put("zzw-sign", createSign(postData)); return headers; } /** * 入参序列化成json格式 * str1 = {"requestId":"xxx","data":{"id":"11","name":"z3"}} * str2 = str1 + key * str3 = md5/sha1/sha512(str2).toUpperCase() * sign = str3 */ static String createSign(String str1) { String appid = WeChatConfig.weChatConfig.getAppId(); String secretKey = WeChatConfig.weChatConfig.getZzwtecSecretKey(); String str2 = str1 + secretKey; String str3 = EncoderHandler.MD5(str2).toUpperCase(); return String.format("appid=%s;sign=%s", appid, str3); } } }