diff --git a/changelog.md b/changelog.md
index 551ac68e..e29b698c 100644
--- a/changelog.md
+++ b/changelog.md
@@ -5,6 +5,7 @@
- 可定义业务错误码(见`@Open`注解中的`bizCode`属性)
- 文档参数可指定最大长度(使用`@Length(max = xx)`)
- 修复返回大文本导致的错误(Exceeded limit on max bytes to buffer : 262144)
+- 增强参数绑定
## 4.0.2
diff --git a/pom.xml b/pom.xml
index 989d13f2..1f8546a2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -67,6 +67,7 @@
2.9.2
2.9.2
1.16.9
+ 6.2
@@ -195,6 +196,12 @@
${commons-logging.version}
+
+ org.ow2.asm
+ asm
+ ${asm.version}
+
+
org.projectlombok
lombok
diff --git a/sop-common/sop-service-common/pom.xml b/sop-common/sop-service-common/pom.xml
index 651adae6..5633391c 100644
--- a/sop-common/sop-service-common/pom.xml
+++ b/sop-common/sop-service-common/pom.xml
@@ -51,6 +51,11 @@
hibernate-validator
+
+ org.ow2.asm
+ asm
+
+
com.alibaba.cloud
diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/AbstractValidationAnnotationBuilder.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/AbstractValidationAnnotationBuilder.java
new file mode 100644
index 00000000..8546d641
--- /dev/null
+++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/AbstractValidationAnnotationBuilder.java
@@ -0,0 +1,37 @@
+package com.gitee.sop.servercommon.param;
+
+import org.springframework.core.annotation.AnnotationUtils;
+
+import java.lang.annotation.Annotation;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author tanghc
+ */
+public abstract class AbstractValidationAnnotationBuilder implements ValidationAnnotationBuilder {
+ @Override
+ public ValidationAnnotationDefinition build(T jsr303Annotation) {
+ ValidationAnnotationDefinition validationAnnotationDefinition = new ValidationAnnotationDefinition();
+
+ validationAnnotationDefinition.setAnnotationClass(jsr303Annotation.annotationType());
+ Map properties = AnnotationUtils.getAnnotationAttributes(jsr303Annotation);
+ properties = this.formatProperties(properties);
+ validationAnnotationDefinition.setProperties(properties);
+ return validationAnnotationDefinition;
+ }
+
+ protected Map formatProperties(Map properties) {
+ Set> entrySet = properties.entrySet();
+ for (Map.Entry entry : entrySet) {
+ Object value = entry.getValue();
+ if (value.getClass().isArray()) {
+ Object[] arr = (Object[])value;
+ if (arr.length == 0) {
+ properties.put(entry.getKey(), null);
+ }
+ }
+ }
+ return properties;
+ }
+}
diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ApiArgumentResolver.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ApiArgumentResolver.java
index da92232e..bed09eaf 100644
--- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ApiArgumentResolver.java
+++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ApiArgumentResolver.java
@@ -8,6 +8,7 @@ import com.gitee.sop.servercommon.bean.OpenContext;
import com.gitee.sop.servercommon.bean.OpenContextImpl;
import com.gitee.sop.servercommon.bean.ParamNames;
import com.gitee.sop.servercommon.bean.ServiceContext;
+import com.gitee.sop.servercommon.util.FieldUtil;
import com.gitee.sop.servercommon.util.OpenUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
@@ -35,6 +36,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
+import java.lang.reflect.Parameter;
import java.security.Principal;
import java.time.ZoneId;
import java.util.ArrayList;
@@ -82,8 +84,8 @@ public class ApiArgumentResolver implements SopHandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter methodParameter) {
- // 是否有注解
- if (!methodParameter.hasMethodAnnotation(Open.class)) {
+ Open open = methodParameter.getMethodAnnotation(Open.class);
+ if (open == null) {
return false;
}
Class> paramType = methodParameter.getParameterType();
@@ -106,7 +108,25 @@ public class ApiArgumentResolver implements SopHandlerMethodArgumentResolver {
Writer.class.isAssignableFrom(paramType)
);
// 除此之外都匹配
- return !exclude;
+ boolean support = !exclude;
+ if (support) {
+ this.wrapSingleParam(methodParameter, open);
+ }
+ return support;
+ }
+
+ /**
+ * 包装单个值参数
+ * @param methodParameter 参数信息
+ * @param open open注解
+ */
+ private void wrapSingleParam(MethodParameter methodParameter, Open open) {
+ Parameter parameter = methodParameter.getParameter();
+ boolean isNumberStringEnumType = FieldUtil.isNumberStringEnumType(parameter.getType());
+ if (isNumberStringEnumType) {
+ log.debug("包装参数,方法:{},参数名:{}", methodParameter.getMethod(), parameter.getName());
+ SingleParameterContext.add(parameter, open);
+ }
}
@Override
@@ -123,8 +143,15 @@ public class ApiArgumentResolver implements SopHandlerMethodArgumentResolver {
Object paramObj = this.getParamObject(methodParameter, nativeWebRequest);
if (paramObj != null) {
// JSR-303验证
- paramValidator.validateBizParam(paramObj);
- return paramObj;
+
+ if (paramObj instanceof SingleParameterWrapper) {
+ SingleParameterWrapper parameterWrapper = (SingleParameterWrapper) paramObj;
+ paramValidator.validateBizParam(parameterWrapper.getWrapperObject());
+ return parameterWrapper.getParamValue();
+ } else {
+ paramValidator.validateBizParam(paramObj);
+ return paramObj;
+ }
}
HandlerMethodArgumentResolver resolver = getOtherArgumentResolver(methodParameter);
if (resolver != null) {
@@ -164,6 +191,17 @@ public class ApiArgumentResolver implements SopHandlerMethodArgumentResolver {
}
// 方法参数类型
Class> parameterType = methodParameter.getParameterType();
+ SingleParameterContextValue singleValue = SingleParameterContext.get(openContext.getMethod(), openContext.getVersion());
+ // 如果是单值参数
+ if (singleValue != null) {
+ JSONObject jsonObj = JSON.parseObject(bizContent);
+ Object paramValue = jsonObj.getObject(singleValue.getParameterName(), parameterType);
+ Object wrapperObject = jsonObj.toJavaObject(singleValue.getWrapClass());
+ SingleParameterWrapper singleParameterWrapper = new SingleParameterWrapper();
+ singleParameterWrapper.setParamValue(paramValue);
+ singleParameterWrapper.setWrapperObject(wrapperObject);
+ return singleParameterWrapper;
+ }
Object param;
// 如果是json字符串
if (JSONValidator.from(bizContent).validate()) {
diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/BaseValidationAnnotationBuilder.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/BaseValidationAnnotationBuilder.java
new file mode 100644
index 00000000..3a3b5c0f
--- /dev/null
+++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/BaseValidationAnnotationBuilder.java
@@ -0,0 +1,15 @@
+package com.gitee.sop.servercommon.param;
+
+import com.gitee.sop.servercommon.util.ReflectionUtil;
+
+import java.lang.annotation.Annotation;
+
+/**
+ * @author tanghc
+ */
+public abstract class BaseValidationAnnotationBuilder extends AbstractValidationAnnotationBuilder {
+ public BaseValidationAnnotationBuilder() {
+ Class> clazz = ReflectionUtil.getSuperClassGenricType(getClass(), 0);
+ ValidationAnnotationDefinitionFactory.addBuilder(clazz, this);
+ }
+}
\ No newline at end of file
diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/SingleParameterClassCreator.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/SingleParameterClassCreator.java
new file mode 100644
index 00000000..157166b4
--- /dev/null
+++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/SingleParameterClassCreator.java
@@ -0,0 +1,186 @@
+package com.gitee.sop.servercommon.param;
+
+
+import com.gitee.sop.servercommon.util.FieldUtil;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang.StringUtils;
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Parameter;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * 包装单个参数
+ *
+ * public class WrapperParam0id implements SingleValue {
+ * public String id;
+ *
+ * public Object fetchValue() {
+ * return this.id;
+ * }
+ * }
+ *
+ *
+ * @author tanghc
+ */
+public class SingleParameterClassCreator implements Opcodes {
+
+ private static final Logger logger = LoggerFactory.getLogger(SingleParameterClassCreator.class);
+
+ private static final String FOLDER_END_CHAR = "/";
+
+ private static final String CLASS_FILE_SUFFIX = ".class";
+
+ private static final String WRAPPER_PARAM = "WrapperParam";
+
+ private static final String PKG = "com/gitee/sop/servercommon/gen/";
+ private static final String OBJECT = "java/lang/Object";
+
+ private static final ParameterClassLoader CLASS_LOADER = new ParameterClassLoader();
+
+ private static final AtomicInteger i = new AtomicInteger();
+
+ // 生成class文件的保存路径
+ private String savePath;
+
+ /**
+ * 生成一个类,里面指放这个字段
+ *
+ * @param parameter 字段,只能是基本类型或字符串类型
+ * @return 如果不是基本类型或字符串类型,返回null
+ */
+ public Class> create(Parameter parameter, String paramName) {
+ Class> paramType = parameter.getType();
+ if (!FieldUtil.isNumberStringEnumType(paramType)) {
+ return null;
+ }
+ /* *******************************class********************************************** */
+ // 创建一个ClassWriter, 以生成一个新的类
+ ClassWriter classWriter = new ClassWriter(0);
+ // 类名
+ String className = WRAPPER_PARAM + i.incrementAndGet() + paramName;
+ // 类路径名:com/gitee/sop/service/gen/WrapperParam
+ String classpathName = PKG + className;
+ classWriter.visit(V1_8, ACC_PUBLIC, classpathName, null, OBJECT, null);
+
+ /* ********************************constructor********************************************* */
+ MethodVisitor methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "", "()V", null,
+ null);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitMethodInsn(INVOKESPECIAL, OBJECT, "", "()V", false);
+ methodVisitor.visitInsn(RETURN);
+ methodVisitor.visitMaxs(1, 1);
+ methodVisitor.visitEnd();
+
+
+ /* ************************************parameter***************************************** */
+ // 生成String name字段, Ljava/lang/String;
+ Type type = Type.getType(paramType);
+ FieldVisitor fieldVisitor = classWriter.visitField(ACC_PUBLIC, paramName, type.getDescriptor(), null, null);
+ // 生成验证注解
+ Annotation[] annotations = parameter.getAnnotations();
+ for (Annotation annotation : annotations) {
+ ValidationAnnotationDefinition validationAnnotationDefinition = ValidationAnnotationDefinitionFactory.build(annotation);
+ if (validationAnnotationDefinition == null) {
+ continue;
+ }
+ Class> annoClass = validationAnnotationDefinition.getAnnotationClass();
+ Type annoType = Type.getType(annoClass);
+ AnnotationVisitor annotationVisitor = fieldVisitor.visitAnnotation(annoType.getDescriptor(), true);
+ Map properties = validationAnnotationDefinition.getProperties();
+ if (properties != null) {
+ try {
+ Set> entrySet = properties.entrySet();
+ for (Map.Entry entry : entrySet) {
+ Object val = entry.getValue();
+ if (val != null) {
+ // 设置枚举值
+ if (val.getClass().isEnum()) {
+ Type eType = Type.getType(val.getClass());
+ annotationVisitor.visitEnum(entry.getKey(), eType.getDescriptor(), val.toString());
+ } else if (val instanceof Class>) {
+ // val是Class类型
+ Type vType = Type.getType((Class>) val);
+ annotationVisitor.visit(entry.getKey(), vType);
+ } else {
+ annotationVisitor.visit(entry.getKey(), val);
+ }
+ }
+ }
+ } catch (Exception e) {
+ logger.error("ASM生成注解出错", e);
+ }
+ }
+ // 结束生成注解
+ annotationVisitor.visitEnd();
+ logger.debug("ASM生成参数注解,参数:{},注解:{},注解属性:{}", paramName, annoClass.getName(), properties);
+ }
+ fieldVisitor.visitEnd();
+
+ classWriter.visitEnd();
+
+ /* **********************************generate and load******************************************* */
+ byte[] code = classWriter.toByteArray();
+
+ if (StringUtils.isNotBlank(savePath)) {
+ if (!savePath.endsWith(FOLDER_END_CHAR)) {
+ savePath = savePath + FOLDER_END_CHAR;
+ }
+ this.writeClassFile(code, savePath + className + CLASS_FILE_SUFFIX);
+ }
+
+ Class> clazz = CLASS_LOADER.defineClass(code);
+ logger.debug("生成参数包装类:{},包装参数名:{},参数类型:{}", clazz.getName(), paramName, paramType);
+ return clazz;
+ }
+
+ protected void writeClassFile(byte[] code, String filepath) {
+ // 将二进制流写到本地磁盘上
+ try {
+ FileUtils.writeByteArrayToFile(new File(filepath), code);
+ } catch (IOException e) {
+ logger.error("写文件错误,filepath:" + filepath, e);
+ }
+ }
+
+ public void setSavePath(String savePath) {
+ this.savePath = savePath;
+ }
+
+
+ // 自定义类加载器
+ static class ParameterClassLoader extends ClassLoader {
+ public ParameterClassLoader() {
+ /*
+ 指定父加载器,不指定默认为AppClassLoader
+ springboot启动会使用自带的org.springframework.boot.loader.LaunchedURLClassLoader
+ 如果不指定,会出现加载器不一致,导致ASM生成的class获取不到字段的注解。
+ 因此ASM生成的class必须使用当前ClassLoader进行生成。
+ */
+ super(Thread.currentThread().getContextClassLoader());
+ }
+
+ /**
+ * 加载class
+ *
+ * @param clazz 字节码
+ * @return
+ */
+ public Class> defineClass(byte[] clazz) {
+ return this.defineClass(null, clazz, 0, clazz.length);
+ }
+ }
+
+}
diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/SingleParameterContext.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/SingleParameterContext.java
new file mode 100644
index 00000000..d2574df6
--- /dev/null
+++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/SingleParameterContext.java
@@ -0,0 +1,44 @@
+package com.gitee.sop.servercommon.param;
+
+import com.gitee.sop.servercommon.annotation.Open;
+import com.gitee.sop.servercommon.bean.ServiceConfig;
+
+import java.lang.reflect.Parameter;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 存放单个参数
+ * @author tanghc
+ */
+public class SingleParameterContext {
+
+ private static SingleParameterClassCreator singleFieldWrapper = new SingleParameterClassCreator();
+
+ private static final Map context = new ConcurrentHashMap<>(16);
+
+ /**
+ * 添加单个参数
+ * @param parameter 接口单个参数
+ * @param open open注解
+ */
+ public static void add(Parameter parameter, Open open) {
+ String version = open.version();
+ version = "".equals(version) ? ServiceConfig.getInstance().getDefaultVersion() : version;
+ String key = open.value() + version;
+ String parameterName = parameter.getName();
+ Class> wrapClass = singleFieldWrapper.create(parameter, parameterName);
+ SingleParameterContextValue value = new SingleParameterContextValue();
+ value.setParameterName(parameterName);
+ value.setWrapClass(wrapClass);
+ context.put(key, value);
+ }
+
+ public static SingleParameterContextValue get(String name, String version) {
+ return context.get(name + version);
+ }
+
+ public static void setSingleFieldWrapper(SingleParameterClassCreator singleFieldWrapper) {
+ SingleParameterContext.singleFieldWrapper = singleFieldWrapper;
+ }
+}
diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/SingleParameterContextValue.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/SingleParameterContextValue.java
new file mode 100644
index 00000000..a8d8af93
--- /dev/null
+++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/SingleParameterContextValue.java
@@ -0,0 +1,24 @@
+package com.gitee.sop.servercommon.param;
+
+public class SingleParameterContextValue {
+ /** 参数名称 */
+ private String parameterName;
+ private Class> wrapClass;
+
+ public String getParameterName() {
+ return parameterName;
+ }
+
+ public void setParameterName(String parameterName) {
+ this.parameterName = parameterName;
+ }
+
+ public Class> getWrapClass() {
+ return wrapClass;
+ }
+
+ public void setWrapClass(Class> wrapClass) {
+ this.wrapClass = wrapClass;
+ }
+
+}
\ No newline at end of file
diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/SingleParameterWrapper.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/SingleParameterWrapper.java
new file mode 100644
index 00000000..91b8ceec
--- /dev/null
+++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/SingleParameterWrapper.java
@@ -0,0 +1,27 @@
+package com.gitee.sop.servercommon.param;
+
+/**
+ * @author tanghc
+ */
+public class SingleParameterWrapper {
+ /** 包裹类实例 */
+ private Object wrapperObject;
+ /** 参数值 */
+ private Object paramValue;
+
+ public Object getWrapperObject() {
+ return wrapperObject;
+ }
+
+ public void setWrapperObject(Object wrapperObject) {
+ this.wrapperObject = wrapperObject;
+ }
+
+ public Object getParamValue() {
+ return paramValue;
+ }
+
+ public void setParamValue(Object paramValue) {
+ this.paramValue = paramValue;
+ }
+}
diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ValidationAnnotationBuilder.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ValidationAnnotationBuilder.java
new file mode 100644
index 00000000..8d92b313
--- /dev/null
+++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ValidationAnnotationBuilder.java
@@ -0,0 +1,16 @@
+package com.gitee.sop.servercommon.param;
+
+import java.lang.annotation.Annotation;
+
+/**
+ * @author tanghc
+ */
+public interface ValidationAnnotationBuilder {
+ /**
+ * 构建验证注解
+ *
+ * @param jsr303Annotation JSR-303注解
+ * @return 返回注解定义
+ */
+ ValidationAnnotationDefinition build(T jsr303Annotation);
+}
diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ValidationAnnotationDefinition.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ValidationAnnotationDefinition.java
new file mode 100644
index 00000000..97475c50
--- /dev/null
+++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ValidationAnnotationDefinition.java
@@ -0,0 +1,27 @@
+package com.gitee.sop.servercommon.param;
+
+import java.util.Map;
+
+/**
+ * @author tanghc
+ */
+public class ValidationAnnotationDefinition {
+ private Class> annotationClass;
+ private Map properties;
+
+ public Class> getAnnotationClass() {
+ return annotationClass;
+ }
+
+ public void setAnnotationClass(Class> annotationClass) {
+ this.annotationClass = annotationClass;
+ }
+
+ public Map getProperties() {
+ return properties;
+ }
+
+ public void setProperties(Map properties) {
+ this.properties = properties;
+ }
+}
diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ValidationAnnotationDefinitionFactory.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ValidationAnnotationDefinitionFactory.java
new file mode 100644
index 00000000..83bf8428
--- /dev/null
+++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ValidationAnnotationDefinitionFactory.java
@@ -0,0 +1,117 @@
+package com.gitee.sop.servercommon.param;
+
+import io.swagger.annotations.ApiModelProperty;
+import org.hibernate.validator.constraints.CodePointLength;
+import org.hibernate.validator.constraints.ConstraintComposition;
+import org.hibernate.validator.constraints.CreditCardNumber;
+import org.hibernate.validator.constraints.Currency;
+import org.hibernate.validator.constraints.EAN;
+import org.hibernate.validator.constraints.ISBN;
+import org.hibernate.validator.constraints.Length;
+import org.hibernate.validator.constraints.LuhnCheck;
+import org.hibernate.validator.constraints.Mod10Check;
+import org.hibernate.validator.constraints.Mod11Check;
+import org.hibernate.validator.constraints.ParameterScriptAssert;
+import org.hibernate.validator.constraints.Range;
+import org.hibernate.validator.constraints.SafeHtml;
+import org.hibernate.validator.constraints.ScriptAssert;
+import org.hibernate.validator.constraints.URL;
+import org.hibernate.validator.constraints.UniqueElements;
+
+import javax.validation.constraints.AssertFalse;
+import javax.validation.constraints.AssertTrue;
+import javax.validation.constraints.DecimalMax;
+import javax.validation.constraints.DecimalMin;
+import javax.validation.constraints.Digits;
+import javax.validation.constraints.Email;
+import javax.validation.constraints.Future;
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.Negative;
+import javax.validation.constraints.NegativeOrZero;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Null;
+import javax.validation.constraints.Past;
+import javax.validation.constraints.PastOrPresent;
+import javax.validation.constraints.Pattern;
+import javax.validation.constraints.Positive;
+import javax.validation.constraints.PositiveOrZero;
+import javax.validation.constraints.Size;
+import java.lang.annotation.Annotation;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author tanghc
+ */
+public class ValidationAnnotationDefinitionFactory {
+
+ private static final Map, ValidationAnnotationBuilder>> store = new HashMap<>(64);
+
+ static {
+ new BaseValidationAnnotationBuilder(){};
+
+ // validation-api-2.0.1.Final.jar下javax.validation.constraints
+ new BaseValidationAnnotationBuilder(){};
+ new BaseValidationAnnotationBuilder(){};
+ new BaseValidationAnnotationBuilder(){};
+ new BaseValidationAnnotationBuilder(){};
+ new BaseValidationAnnotationBuilder(){};
+ new BaseValidationAnnotationBuilder(){};
+ new BaseValidationAnnotationBuilder(){};
+ new BaseValidationAnnotationBuilder(){};
+ new BaseValidationAnnotationBuilder(){};
+ new BaseValidationAnnotationBuilder(){};
+ new BaseValidationAnnotationBuilder(){};
+ new BaseValidationAnnotationBuilder(){};
+ new BaseValidationAnnotationBuilder(){};
+ new BaseValidationAnnotationBuilder(){};
+ new BaseValidationAnnotationBuilder(){};
+ new BaseValidationAnnotationBuilder(){};
+ new BaseValidationAnnotationBuilder(){};
+ new BaseValidationAnnotationBuilder(){};
+ new BaseValidationAnnotationBuilder(){};
+ new BaseValidationAnnotationBuilder(){};
+ new BaseValidationAnnotationBuilder(){};
+
+ // hibernate-validator-6.0.10.Final.jar下org.hibernate.validator.constraints
+ new BaseValidationAnnotationBuilder(){};
+ new BaseValidationAnnotationBuilder(){};
+ new BaseValidationAnnotationBuilder(){};
+ new BaseValidationAnnotationBuilder(){};
+ new BaseValidationAnnotationBuilder(){};
+ new BaseValidationAnnotationBuilder(){};
+ new BaseValidationAnnotationBuilder(){};
+ new BaseValidationAnnotationBuilder(){};
+ new BaseValidationAnnotationBuilder(){};
+ new BaseValidationAnnotationBuilder(){};
+ new BaseValidationAnnotationBuilder(){};
+ new BaseValidationAnnotationBuilder(){};
+ new BaseValidationAnnotationBuilder(){};
+ new BaseValidationAnnotationBuilder(){};
+ new BaseValidationAnnotationBuilder(){};
+ new BaseValidationAnnotationBuilder(){};
+ }
+
+ /**
+ * 添加注解对应的构建器
+ *
+ * @param annoClass
+ * @param builder
+ */
+ public static void addBuilder(Class> annoClass, ValidationAnnotationBuilder> builder) {
+ store.put(annoClass, builder);
+ }
+
+ public static ValidationAnnotationDefinition build(Annotation annotation) {
+ Class> jsr303Anno = annotation.annotationType();
+ ValidationAnnotationBuilder validationAnnotationBuilder = store.get(jsr303Anno);
+ if (validationAnnotationBuilder == null) {
+ return null;
+ }
+ return validationAnnotationBuilder.build(annotation);
+ }
+
+}
diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/util/FieldUtil.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/util/FieldUtil.java
new file mode 100644
index 00000000..c74d25a6
--- /dev/null
+++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/util/FieldUtil.java
@@ -0,0 +1,128 @@
+package com.gitee.sop.servercommon.util;
+
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.ClassUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+
+/**
+ * @author tanghc
+ */
+public class FieldUtil {
+
+ private static final Logger log = LoggerFactory.getLogger(FieldUtil.class);
+
+ private static final String CLASS_SUFFIX = ".class";
+
+ private FieldUtil() {
+ }
+
+ /**
+ * 判断这个字段是否是数字类型或字符串类型或枚举类型
+ *
+ * @param type 字段类型
+ * @return true:是数字或字符串类型或枚举类型
+ */
+ public static boolean isNumberStringEnumType(Class> type) {
+ if (type == String.class) {
+ return true;
+ }
+ if (type.getGenericSuperclass() == Number.class) {
+ return true;
+ }
+ if (type.isPrimitive()) {
+ return true;
+ }
+ if (type.isEnum()) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * 获取指定类指定方法的参数名
+ * @param clazz 要获取参数名的方法所属的类
+ * @param method 要获取参数名的方法
+ * @param index 参数索引,从0开始
+ * @return 返回指定类指定方法的参数名,没有返回空字符串
+ */
+ public static String getMethodParameterName(Class> clazz, final Method method, int index) {
+ String[] names = getMethodParameterNamesByAsm(clazz, method);
+ if (names.length == 0) {
+ return "";
+ }
+ return names[index];
+ }
+
+ /**
+ * 获取指定类指定方法的参数名
+ *
+ * @param clazz 要获取参数名的方法所属的类
+ * @param method 要获取参数名的方法
+ * @return 按参数顺序排列的参数名列表,如果没有参数,则返回空数组
+ */
+ public static String[] getMethodParameterNamesByAsm(Class> clazz, final Method method) {
+ final Class>[] parameterTypes = method.getParameterTypes();
+ if (parameterTypes == null || parameterTypes.length == 0) {
+ return new String[0];
+ }
+ final Type[] types = new Type[parameterTypes.length];
+ for (int i = 0; i < parameterTypes.length; i++) {
+ types[i] = Type.getType(parameterTypes[i]);
+ }
+ final String[] parameterNames = new String[parameterTypes.length];
+ // 解决clazz对象是cglib对象导致空指针异常
+ // 获取真实的class,如果是cglib类,则返回父类class
+ Class> realClass = ClassUtils.getUserClass(clazz);
+ String className = realClass.getName();
+ int lastDotIndex = className.lastIndexOf('.');
+ className = className.substring(lastDotIndex + 1) + CLASS_SUFFIX;
+ InputStream is = realClass.getResourceAsStream(className);
+ try {
+ ClassReader classReader = new ClassReader(is);
+ classReader.accept(new ClassVisitor(Opcodes.ASM6) {
+ @Override
+ public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
+ // 只处理指定的方法
+ Type[] argumentTypes = Type.getArgumentTypes(desc);
+ if (!method.getName().equals(name) || !Arrays.equals(argumentTypes, types)) {
+ return null;
+ }
+ return new MethodVisitor(Opcodes.ASM6) {
+ @Override
+ public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
+ int i = index - 1;
+ // 如果是静态方法,则第一就是参数
+ // 如果不是静态方法,则第一个是"this",然后才是方法的参数
+ if (Modifier.isStatic(method.getModifiers())) {
+ i = index;
+ }
+ if (i >= 0 && i < parameterNames.length) {
+ parameterNames[i] = name;
+ }
+ super.visitLocalVariable(name, desc, signature, start,
+ end, index);
+ }
+
+ };
+
+ }
+ }, 0);
+ } catch (IOException e) {
+ log.error("生成asm失败,oriClass:{}, realClass:{} method:{}", clazz.getName(), realClass.getName(), method.toGenericString(), e);
+ }
+ return parameterNames;
+ }
+
+}
diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/util/ReflectionUtil.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/util/ReflectionUtil.java
new file mode 100644
index 00000000..ade0ad96
--- /dev/null
+++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/util/ReflectionUtil.java
@@ -0,0 +1,199 @@
+package com.gitee.sop.servercommon.util;
+
+import org.springframework.beans.FatalBeanException;
+import org.springframework.context.ApplicationContext;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 反射相关
+ * @author tanghc
+ */
+public class ReflectionUtil {
+
+ public static final String PREFIX_SET = "set";
+
+ private static final String[] EMPTY_STRING_ARRAY = {};
+
+ private static Map> classGenricTypeCache = new HashMap<>(16);
+
+ /** key:obj.getClass().getName() + genericClass.getName() */
+ private static Map genericTypeFieldCache = new HashMap<>();
+
+ /**
+ * 设置某个字段的值
+ * @param target 实体类,必须有字段的set方法
+ * @param fieldName 字段名
+ * @param val 值
+ */
+ public static void invokeFieldValue(Object target,String fieldName, Object val) {
+ String setMethodName = getSetMethodName(fieldName);
+ Method[] methods = target.getClass().getDeclaredMethods();
+ for (Method method : methods) {
+ String methodName = method.getName();
+ Class>[] methodParams = method.getParameterTypes();
+
+ if (setMethodName.equals(methodName)) {
+ // 能否拷贝
+ boolean canCopy =
+ // 并且只有一个参数
+ methodParams.length == 1
+ // val是methodParams[0]或他的子类
+ && methodParams[0].isInstance(val) || Number.class.isInstance(val);
+
+ if (canCopy) {
+ try {
+ if (!Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
+ method.setAccessible(true);
+ }
+ method.invoke(target, val);
+ break;
+ } catch (Throwable ex) {
+ throw new FatalBeanException(
+ "Could not set property '" + fieldName + "' value to target", ex);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * 返回实体类中具有指定泛型的字段
+ * @param obj 实体类
+ * @param genericClass 指定泛型
+ * @return 没有返回null
+ */
+ public static Field getListFieldWithGeneric(Object obj, Class> genericClass) {
+ Class> objClass = obj.getClass();
+ String key = objClass.getName() + genericClass.getName();
+ Field value = genericTypeFieldCache.get(key);
+ if (value != null) {
+ return value;
+ }
+ Field[] fields = objClass.getDeclaredFields();
+ for (Field field : fields) {
+ Type genericType = getListGenericType(field);
+ if (genericType == genericClass) {
+ genericTypeFieldCache.put(key, field);
+ return field;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * 返回集合字段的泛型类型。
+ * 如:List<User> list;返回User.class
+ *
+ * @param field
+ * 类中的一个属性
+ * @return 返回类型
+ */
+ public static Type getListGenericType(Field field) {
+ if (isListType(field.getType())) {
+ Type genericType = field.getGenericType();
+
+ if (genericType instanceof ParameterizedType) {
+ Type[] params = ((ParameterizedType) genericType).getActualTypeArguments();
+ if (params.length == 1) {
+ return params[0];
+ }
+ }
+ }
+ return Object.class;
+ }
+
+ public static boolean isListType(Type type) {
+ return type == List.class;
+ }
+
+ /**
+ * 返回set方法名。name -> setName
+ * @param fieldName
+ * @return 返回方法名
+ */
+ public static String getSetMethodName(String fieldName) {
+ return PREFIX_SET + Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1);
+ }
+
+ /**
+ * 构建字段名称
+ * @param methodName 根据get或set方法返回字段名称
+ * @return 字段名称
+ */
+ public static String buildFieldName(String methodName) {
+ return methodName.substring(3, 4).toLowerCase() + methodName.substring(4);
+ }
+
+ /**
+ * 返回定义类时的泛型参数的类型.
+ * 如:定义一个BookManager类
+ * {@literal public BookManager extends GenricManager}{...}
+ *
+ * 调用getSuperClassGenricType(getClass(),0)将返回Book的Class类型
+ * 调用getSuperClassGenricType(getClass(),1)将返回Address的Class类型
+ *
+ * @param clazz 从哪个类中获取
+ * @param index 泛型参数索引,从0开始
+ * @return 返回泛型参数类型
+ */
+ public static Class> getSuperClassGenricType(Class> clazz, int index) throws IndexOutOfBoundsException {
+ String cacheKey = clazz.getName() + index;
+ Class> cachedClass = classGenricTypeCache.get(cacheKey);
+ if (cachedClass != null) {
+ return cachedClass;
+ }
+
+ Type genType = clazz.getGenericSuperclass();
+
+ // 没有泛型参数
+ if (!(genType instanceof ParameterizedType)) {
+ throw new RuntimeException("class " + clazz.getName() + " 没有指定父类泛型");
+ } else {
+ Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
+
+ if (index >= params.length || index < 0) {
+ throw new RuntimeException("泛型索引不正确,index:" + index);
+ }
+ if (!(params[index] instanceof Class)) {
+ throw new RuntimeException(params[index] + "不是Class类型");
+ }
+
+ Class> retClass = (Class>) params[index];
+ // 缓存起来
+ classGenricTypeCache.put(cacheKey, retClass);
+
+ return retClass;
+ }
+ }
+
+ /**
+ * 找到所有被注解标记的类名
+ * @param ctx ApplicationContext
+ * @param annotationClass 注解class
+ * @return 返回类名称数组,没有返回空数组
+ */
+ public static String[] findBeanNamesByAnnotationClass(ApplicationContext ctx, Class extends Annotation> annotationClass) {
+ String[] beans = ctx.getBeanNamesForAnnotation(annotationClass);
+ // 如果没找到,去父容器找
+ if (beans == null || beans.length == 0) {
+ ApplicationContext parentCtx = ctx.getParent();
+ if (parentCtx != null) {
+ beans = parentCtx.getBeanNamesForAnnotation(annotationClass);
+ }
+ }
+ if (beans == null) {
+ beans = EMPTY_STRING_ARRAY;
+ }
+ return beans;
+ }
+
+}
diff --git a/sop-example/sop-story/src/main/java/com/gitee/sop/storyweb/controller/Example1001_BaseController.java b/sop-example/sop-story/src/main/java/com/gitee/sop/storyweb/controller/Example1001_BaseController.java
index 7e70af33..238e2370 100644
--- a/sop-example/sop-story/src/main/java/com/gitee/sop/storyweb/controller/Example1001_BaseController.java
+++ b/sop-example/sop-story/src/main/java/com/gitee/sop/storyweb/controller/Example1001_BaseController.java
@@ -10,6 +10,7 @@ import com.gitee.sop.servercommon.exception.ServiceException;
import com.gitee.sop.storyweb.controller.param.CategoryParam;
import com.gitee.sop.storyweb.controller.param.LargeTextParam;
import com.gitee.sop.storyweb.controller.param.StoryParam;
+import com.gitee.sop.storyweb.controller.param.TypeEnum;
import com.gitee.sop.storyweb.controller.result.CategoryResult;
import com.gitee.sop.storyweb.controller.result.StoryResult;
import com.gitee.sop.storyweb.controller.result.TestResult;
@@ -27,6 +28,9 @@ import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
@@ -98,6 +102,35 @@ public class Example1001_BaseController {
return result;
}
+ // 参数绑定,少量参数可以这样写,参数多了建议放进类里面
+ @Open(value = "story.oneparam")
+ @GetMapping("/oneParam/v1")
+ public StoryResult oneParam(@NotBlank(message = "id不能为空") String id, @NotBlank(message = "name不能为空") String name) {
+ StoryResult result = new StoryResult();
+ result.setName("id:" + id + ", name:" + name);
+ return result;
+ }
+
+ // 参数绑定
+ @Open(value = "story.oneparam", version = "1.1")
+ @GetMapping("/oneParam/v2")
+ public StoryResult oneParamV2(
+ @NotNull(message = "id不能为空")
+ @Min(value = 1, message = "id必须大于0") Integer id) {
+ StoryResult result = new StoryResult();
+ result.setName("id:" + id);
+ return result;
+ }
+
+ // 参数绑定,枚举
+ @Open(value = "story.oneparam", version = "1.2")
+ @GetMapping("/oneParam/v3")
+ public StoryResult oneParamV2(@NotNull(message = "typeEnum不能为空") TypeEnum typeEnum) {
+ StoryResult result = new StoryResult();
+ result.setName("typeEnum:" + typeEnum.name());
+ return result;
+ }
+
// 参数绑定
@Open(value = "story.param.bind", mergeResult = false)
@RequestMapping("/get/param/v1")
diff --git a/sop-example/sop-story/src/main/java/com/gitee/sop/storyweb/controller/param/TypeEnum.java b/sop-example/sop-story/src/main/java/com/gitee/sop/storyweb/controller/param/TypeEnum.java
new file mode 100644
index 00000000..56e387f5
--- /dev/null
+++ b/sop-example/sop-story/src/main/java/com/gitee/sop/storyweb/controller/param/TypeEnum.java
@@ -0,0 +1,8 @@
+package com.gitee.sop.storyweb.controller.param;
+
+/**
+ * @author tanghc
+ */
+public enum TypeEnum {
+ MOBILE, COMPUTER
+}
diff --git a/sop-gateway/src/main/java/com/gitee/sop/gateway/interceptor/MyRouteInterceptor.java b/sop-gateway/src/main/java/com/gitee/sop/gateway/interceptor/MyRouteInterceptor.java
index fcaab14f..9a017ce9 100644
--- a/sop-gateway/src/main/java/com/gitee/sop/gateway/interceptor/MyRouteInterceptor.java
+++ b/sop-gateway/src/main/java/com/gitee/sop/gateway/interceptor/MyRouteInterceptor.java
@@ -3,6 +3,7 @@ package com.gitee.sop.gateway.interceptor;
import com.gitee.sop.gatewaycommon.interceptor.RouteInterceptor;
import com.gitee.sop.gatewaycommon.interceptor.RouteInterceptorContext;
import com.gitee.sop.gatewaycommon.param.ApiParam;
+import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.stereotype.Component;
@@ -11,20 +12,23 @@ import org.springframework.stereotype.Component;
*
* @author tanghc
*/
+@Slf4j
@Component
public class MyRouteInterceptor implements RouteInterceptor {
@Override
public void preRoute(RouteInterceptorContext context) {
ApiParam apiParam = context.getApiParam();
- System.out.println(String.format("请求接口:%s, ip:%s", apiParam.fetchNameVersion(), apiParam.fetchIp()));
+ log.info("请求接口:{}, ip:{}", apiParam.fetchNameVersion(), apiParam.fetchIp());
}
@Override
public void afterRoute(RouteInterceptorContext context) {
ServiceInstance serviceInstance = context.getServiceInstance();
- System.out.println("请求成功,serviceId:" + serviceInstance.getServiceId() + "(" + serviceInstance.getHost() + ":" + serviceInstance.getPort() + ")" +
- ",微服务返回结果:" + context.getServiceResult());
+ log.info("请求成功,serviceId:{}({}:{}),微服务返回结果:{}",
+ serviceInstance.getServiceId(),
+ serviceInstance.getHost(),serviceInstance.getPort(),
+ context.getServiceResult());
}
@Override
diff --git a/sop-test/src/test/java/com/gitee/sop/test/OneParamTest.java b/sop-test/src/test/java/com/gitee/sop/test/OneParamTest.java
new file mode 100644
index 00000000..407a05b7
--- /dev/null
+++ b/sop-test/src/test/java/com/gitee/sop/test/OneParamTest.java
@@ -0,0 +1,126 @@
+package com.gitee.sop.test;
+
+import com.alibaba.fastjson.JSON;
+import com.gitee.sop.test.alipay.AlipaySignature;
+import org.junit.Test;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 模仿支付宝客户端请求接口
+ */
+public class OneParamTest extends TestBase {
+
+ String url = "http://localhost:8081";
+ String appId = "2019032617262200001";
+ // 平台提供的私钥
+ String privateKey = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCXJv1pQFqWNA/++OYEV7WYXwexZK/J8LY1OWlP9X0T6wHFOvxNKRvMkJ5544SbgsJpVcvRDPrcxmhPbi/sAhdO4x2PiPKIz9Yni2OtYCCeaiE056B+e1O2jXoLeXbfi9fPivJZkxH/tb4xfLkH3bA8ZAQnQsoXA0SguykMRZntF0TndUfvDrLqwhlR8r5iRdZLB6F8o8qXH6UPDfNEnf/K8wX5T4EB1b8x8QJ7Ua4GcIUqeUxGHdQpzNbJdaQvoi06lgccmL+PHzminkFYON7alj1CjDN833j7QMHdPtS9l7B67fOU/p2LAAkPMtoVBfxQt9aFj7B8rEhGCz02iJIBAgMBAAECggEARqOuIpY0v6WtJBfmR3lGIOOokLrhfJrGTLF8CiZMQha+SRJ7/wOLPlsH9SbjPlopyViTXCuYwbzn2tdABigkBHYXxpDV6CJZjzmRZ+FY3S/0POlTFElGojYUJ3CooWiVfyUMhdg5vSuOq0oCny53woFrf32zPHYGiKdvU5Djku1onbDU0Lw8w+5tguuEZ76kZ/lUcccGy5978FFmYpzY/65RHCpvLiLqYyWTtaNT1aQ/9pw4jX9HO9NfdJ9gYFK8r/2f36ZE4hxluAfeOXQfRC/WhPmiw/ReUhxPznG/WgKaa/OaRtAx3inbQ+JuCND7uuKeRe4osP2jLPHPP6AUwQKBgQDUNu3BkLoKaimjGOjCTAwtp71g1oo+k5/uEInAo7lyEwpV0EuUMwLA/HCqUgR4K9pyYV+Oyb8d6f0+Hz0BMD92I2pqlXrD7xV2WzDvyXM3s63NvorRooKcyfd9i6ccMjAyTR2qfLkxv0hlbBbsPHz4BbU63xhTJp3Ghi0/ey/1HQKBgQC2VsgqC6ykfSidZUNLmQZe3J0p/Qf9VLkfrQ+xaHapOs6AzDU2H2osuysqXTLJHsGfrwVaTs00ER2z8ljTJPBUtNtOLrwNRlvgdnzyVAKHfOgDBGwJgiwpeE9voB1oAV/mXqSaUWNnuwlOIhvQEBwekqNyWvhLqC7nCAIhj3yvNQKBgQCqYbeec56LAhWP903Zwcj9VvG7sESqXUhIkUqoOkuIBTWFFIm54QLTA1tJxDQGb98heoCIWf5x/A3xNI98RsqNBX5JON6qNWjb7/dobitti3t99v/ptDp9u8JTMC7penoryLKK0Ty3bkan95Kn9SC42YxaSghzqkt+uvfVQgiNGQKBgGxU6P2aDAt6VNwWosHSe+d2WWXt8IZBhO9d6dn0f7ORvcjmCqNKTNGgrkewMZEuVcliueJquR47IROdY8qmwqcBAN7Vg2K7r7CPlTKAWTRYMJxCT1Hi5gwJb+CZF3+IeYqsJk2NF2s0w5WJTE70k1BSvQsfIzAIDz2yE1oPHvwVAoGAA6e+xQkVH4fMEph55RJIZ5goI4Y76BSvt2N5OKZKd4HtaV+eIhM3SDsVYRLIm9ZquJHMiZQGyUGnsvrKL6AAVNK7eQZCRDk9KQz+0GKOGqku0nOZjUbAu6A2/vtXAaAuFSFx1rUQVVjFulLexkXR3KcztL1Qu2k5pB6Si0K/uwQ=";
+
+ @Test
+ public void testGet() throws Exception {
+
+ // 公共请求参数
+ Map params = new HashMap();
+ params.put("app_id", appId);
+ params.put("method", "story.oneparam");
+ params.put("format", "json");
+ params.put("charset", "utf-8");
+ params.put("sign_type", "RSA2");
+ params.put("timestamp", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
+ params.put("version", "1.0");
+
+ // 业务参数
+ Map bizContent = new HashMap<>();
+ bizContent.put("id", "aaa");
+ bizContent.put("name", "jim");
+
+ params.put("biz_content", JSON.toJSONString(bizContent));
+ String content = AlipaySignature.getSignContent(params);
+ String sign = AlipaySignature.rsa256Sign(content, privateKey, "utf-8");
+ params.put("sign", sign);
+
+ System.out.println("----------- 请求信息 -----------");
+ System.out.println("请求参数:" + buildParamQuery(params));
+ System.out.println("商户秘钥:" + privateKey);
+ System.out.println("待签名内容:" + content);
+ System.out.println("签名(sign):" + sign);
+ System.out.println("URL参数:" + buildUrlQuery(params));
+
+ System.out.println("----------- 返回结果 -----------");
+ String responseData = get(url, params);// 发送请求
+ System.out.println(responseData);
+ }
+
+ @Test
+ public void testGet2() throws Exception {
+
+ // 公共请求参数
+ Map params = new HashMap();
+ params.put("app_id", appId);
+ params.put("method", "story.oneparam");
+ params.put("format", "json");
+ params.put("charset", "utf-8");
+ params.put("sign_type", "RSA2");
+ params.put("timestamp", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
+ params.put("version", "1.1");
+
+ // 业务参数
+ Map bizContent = new HashMap<>();
+ bizContent.put("id", "1");
+
+ params.put("biz_content", JSON.toJSONString(bizContent));
+ String content = AlipaySignature.getSignContent(params);
+ String sign = AlipaySignature.rsa256Sign(content, privateKey, "utf-8");
+ params.put("sign", sign);
+
+ System.out.println("----------- 请求信息 -----------");
+ System.out.println("请求参数:" + buildParamQuery(params));
+ System.out.println("商户秘钥:" + privateKey);
+ System.out.println("待签名内容:" + content);
+ System.out.println("签名(sign):" + sign);
+ System.out.println("URL参数:" + buildUrlQuery(params));
+
+ System.out.println("----------- 返回结果 -----------");
+ String responseData = get(url, params);// 发送请求
+ System.out.println(responseData);
+ }
+
+ @Test
+ public void testGet3() throws Exception {
+
+ // 公共请求参数
+ Map params = new HashMap();
+ params.put("app_id", appId);
+ params.put("method", "story.oneparam");
+ params.put("format", "json");
+ params.put("charset", "utf-8");
+ params.put("sign_type", "RSA2");
+ params.put("timestamp", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
+ params.put("version", "1.2");
+
+ // 业务参数
+ Map bizContent = new HashMap<>();
+ bizContent.put("typeEnum", "MOBILE");
+
+ params.put("biz_content", JSON.toJSONString(bizContent));
+ String content = AlipaySignature.getSignContent(params);
+ String sign = AlipaySignature.rsa256Sign(content, privateKey, "utf-8");
+ params.put("sign", sign);
+
+ System.out.println("----------- 请求信息 -----------");
+ System.out.println("请求参数:" + buildParamQuery(params));
+ System.out.println("商户秘钥:" + privateKey);
+ System.out.println("待签名内容:" + content);
+ System.out.println("签名(sign):" + sign);
+ System.out.println("URL参数:" + buildUrlQuery(params));
+
+ System.out.println("----------- 返回结果 -----------");
+ String responseData = get(url, params);// 发送请求
+ System.out.println(responseData);
+ }
+
+
+}