mirror of
https://gitee.com/durcframework/SOP.git
synced 2025-08-11 21:57:56 +08:00
4.0.3
This commit is contained in:
@@ -5,6 +5,7 @@
|
|||||||
- 可定义业务错误码(见`@Open`注解中的`bizCode`属性)
|
- 可定义业务错误码(见`@Open`注解中的`bizCode`属性)
|
||||||
- 文档参数可指定最大长度(使用`@Length(max = xx)`)
|
- 文档参数可指定最大长度(使用`@Length(max = xx)`)
|
||||||
- 修复返回大文本导致的错误(Exceeded limit on max bytes to buffer : 262144)
|
- 修复返回大文本导致的错误(Exceeded limit on max bytes to buffer : 262144)
|
||||||
|
- 增强参数绑定
|
||||||
|
|
||||||
## 4.0.2
|
## 4.0.2
|
||||||
|
|
||||||
|
7
pom.xml
7
pom.xml
@@ -67,6 +67,7 @@
|
|||||||
<springfox-spring-web.version>2.9.2</springfox-spring-web.version>
|
<springfox-spring-web.version>2.9.2</springfox-spring-web.version>
|
||||||
<springfox-swagger2.version>2.9.2</springfox-swagger2.version>
|
<springfox-swagger2.version>2.9.2</springfox-swagger2.version>
|
||||||
<easyopen.version>1.16.9</easyopen.version>
|
<easyopen.version>1.16.9</easyopen.version>
|
||||||
|
<asm.version>6.2</asm.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
@@ -195,6 +196,12 @@
|
|||||||
<version>${commons-logging.version}</version>
|
<version>${commons-logging.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.ow2.asm</groupId>
|
||||||
|
<artifactId>asm</artifactId>
|
||||||
|
<version>${asm.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.projectlombok</groupId>
|
<groupId>org.projectlombok</groupId>
|
||||||
<artifactId>lombok</artifactId>
|
<artifactId>lombok</artifactId>
|
||||||
|
@@ -51,6 +51,11 @@
|
|||||||
<artifactId>hibernate-validator</artifactId>
|
<artifactId>hibernate-validator</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.ow2.asm</groupId>
|
||||||
|
<artifactId>asm</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- optional -->
|
<!-- optional -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.alibaba.cloud</groupId>
|
<groupId>com.alibaba.cloud</groupId>
|
||||||
|
@@ -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<T extends Annotation> implements ValidationAnnotationBuilder<T> {
|
||||||
|
@Override
|
||||||
|
public ValidationAnnotationDefinition build(T jsr303Annotation) {
|
||||||
|
ValidationAnnotationDefinition validationAnnotationDefinition = new ValidationAnnotationDefinition();
|
||||||
|
|
||||||
|
validationAnnotationDefinition.setAnnotationClass(jsr303Annotation.annotationType());
|
||||||
|
Map<String, Object> properties = AnnotationUtils.getAnnotationAttributes(jsr303Annotation);
|
||||||
|
properties = this.formatProperties(properties);
|
||||||
|
validationAnnotationDefinition.setProperties(properties);
|
||||||
|
return validationAnnotationDefinition;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Map<String, Object> formatProperties(Map<String, Object> properties) {
|
||||||
|
Set<Map.Entry<String, Object>> entrySet = properties.entrySet();
|
||||||
|
for (Map.Entry<String, Object> 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;
|
||||||
|
}
|
||||||
|
}
|
@@ -8,6 +8,7 @@ import com.gitee.sop.servercommon.bean.OpenContext;
|
|||||||
import com.gitee.sop.servercommon.bean.OpenContextImpl;
|
import com.gitee.sop.servercommon.bean.OpenContextImpl;
|
||||||
import com.gitee.sop.servercommon.bean.ParamNames;
|
import com.gitee.sop.servercommon.bean.ParamNames;
|
||||||
import com.gitee.sop.servercommon.bean.ServiceContext;
|
import com.gitee.sop.servercommon.bean.ServiceContext;
|
||||||
|
import com.gitee.sop.servercommon.util.FieldUtil;
|
||||||
import com.gitee.sop.servercommon.util.OpenUtil;
|
import com.gitee.sop.servercommon.util.OpenUtil;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.core.MethodParameter;
|
||||||
@@ -35,6 +36,7 @@ import java.io.InputStream;
|
|||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
|
import java.lang.reflect.Parameter;
|
||||||
import java.security.Principal;
|
import java.security.Principal;
|
||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -82,8 +84,8 @@ public class ApiArgumentResolver implements SopHandlerMethodArgumentResolver {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsParameter(MethodParameter methodParameter) {
|
public boolean supportsParameter(MethodParameter methodParameter) {
|
||||||
// 是否有注解
|
Open open = methodParameter.getMethodAnnotation(Open.class);
|
||||||
if (!methodParameter.hasMethodAnnotation(Open.class)) {
|
if (open == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Class<?> paramType = methodParameter.getParameterType();
|
Class<?> paramType = methodParameter.getParameterType();
|
||||||
@@ -106,7 +108,25 @@ public class ApiArgumentResolver implements SopHandlerMethodArgumentResolver {
|
|||||||
Writer.class.isAssignableFrom(paramType)
|
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
|
@Override
|
||||||
@@ -123,8 +143,15 @@ public class ApiArgumentResolver implements SopHandlerMethodArgumentResolver {
|
|||||||
Object paramObj = this.getParamObject(methodParameter, nativeWebRequest);
|
Object paramObj = this.getParamObject(methodParameter, nativeWebRequest);
|
||||||
if (paramObj != null) {
|
if (paramObj != null) {
|
||||||
// JSR-303验证
|
// 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);
|
HandlerMethodArgumentResolver resolver = getOtherArgumentResolver(methodParameter);
|
||||||
if (resolver != null) {
|
if (resolver != null) {
|
||||||
@@ -164,6 +191,17 @@ public class ApiArgumentResolver implements SopHandlerMethodArgumentResolver {
|
|||||||
}
|
}
|
||||||
// 方法参数类型
|
// 方法参数类型
|
||||||
Class<?> parameterType = methodParameter.getParameterType();
|
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;
|
Object param;
|
||||||
// 如果是json字符串
|
// 如果是json字符串
|
||||||
if (JSONValidator.from(bizContent).validate()) {
|
if (JSONValidator.from(bizContent).validate()) {
|
||||||
|
@@ -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<T extends Annotation> extends AbstractValidationAnnotationBuilder<T> {
|
||||||
|
public BaseValidationAnnotationBuilder() {
|
||||||
|
Class<?> clazz = ReflectionUtil.getSuperClassGenricType(getClass(), 0);
|
||||||
|
ValidationAnnotationDefinitionFactory.addBuilder(clazz, this);
|
||||||
|
}
|
||||||
|
}
|
@@ -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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 包装单个参数
|
||||||
|
* <code>
|
||||||
|
* public class WrapperParam0id implements SingleValue {
|
||||||
|
* public String id;
|
||||||
|
*
|
||||||
|
* public Object fetchValue() {
|
||||||
|
* return this.id;
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @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, "<init>", "()V", null,
|
||||||
|
null);
|
||||||
|
methodVisitor.visitVarInsn(ALOAD, 0);
|
||||||
|
methodVisitor.visitMethodInsn(INVOKESPECIAL, OBJECT, "<init>", "()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<String, Object> properties = validationAnnotationDefinition.getProperties();
|
||||||
|
if (properties != null) {
|
||||||
|
try {
|
||||||
|
Set<Map.Entry<String, Object>> entrySet = properties.entrySet();
|
||||||
|
for (Map.Entry<String, Object> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -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<String, SingleParameterContextValue> 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;
|
||||||
|
}
|
||||||
|
}
|
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,16 @@
|
|||||||
|
package com.gitee.sop.servercommon.param;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author tanghc
|
||||||
|
*/
|
||||||
|
public interface ValidationAnnotationBuilder<T extends Annotation> {
|
||||||
|
/**
|
||||||
|
* 构建验证注解
|
||||||
|
*
|
||||||
|
* @param jsr303Annotation JSR-303注解
|
||||||
|
* @return 返回注解定义
|
||||||
|
*/
|
||||||
|
ValidationAnnotationDefinition build(T jsr303Annotation);
|
||||||
|
}
|
@@ -0,0 +1,27 @@
|
|||||||
|
package com.gitee.sop.servercommon.param;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author tanghc
|
||||||
|
*/
|
||||||
|
public class ValidationAnnotationDefinition {
|
||||||
|
private Class<?> annotationClass;
|
||||||
|
private Map<String, Object> properties;
|
||||||
|
|
||||||
|
public Class<?> getAnnotationClass() {
|
||||||
|
return annotationClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAnnotationClass(Class<?> annotationClass) {
|
||||||
|
this.annotationClass = annotationClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Object> getProperties() {
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProperties(Map<String, Object> properties) {
|
||||||
|
this.properties = properties;
|
||||||
|
}
|
||||||
|
}
|
@@ -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<Class<?>, ValidationAnnotationBuilder<?>> store = new HashMap<>(64);
|
||||||
|
|
||||||
|
static {
|
||||||
|
new BaseValidationAnnotationBuilder<ApiModelProperty>(){};
|
||||||
|
|
||||||
|
// validation-api-2.0.1.Final.jar下javax.validation.constraints
|
||||||
|
new BaseValidationAnnotationBuilder<AssertFalse>(){};
|
||||||
|
new BaseValidationAnnotationBuilder<AssertTrue>(){};
|
||||||
|
new BaseValidationAnnotationBuilder<DecimalMax>(){};
|
||||||
|
new BaseValidationAnnotationBuilder<DecimalMin>(){};
|
||||||
|
new BaseValidationAnnotationBuilder<Digits>(){};
|
||||||
|
new BaseValidationAnnotationBuilder<Email>(){};
|
||||||
|
new BaseValidationAnnotationBuilder<Future>(){};
|
||||||
|
new BaseValidationAnnotationBuilder<Max>(){};
|
||||||
|
new BaseValidationAnnotationBuilder<Min>(){};
|
||||||
|
new BaseValidationAnnotationBuilder<Negative>(){};
|
||||||
|
new BaseValidationAnnotationBuilder<NegativeOrZero>(){};
|
||||||
|
new BaseValidationAnnotationBuilder<NotBlank>(){};
|
||||||
|
new BaseValidationAnnotationBuilder<NotEmpty>(){};
|
||||||
|
new BaseValidationAnnotationBuilder<NotNull>(){};
|
||||||
|
new BaseValidationAnnotationBuilder<Null>(){};
|
||||||
|
new BaseValidationAnnotationBuilder<Past>(){};
|
||||||
|
new BaseValidationAnnotationBuilder<PastOrPresent>(){};
|
||||||
|
new BaseValidationAnnotationBuilder<Pattern>(){};
|
||||||
|
new BaseValidationAnnotationBuilder<Positive>(){};
|
||||||
|
new BaseValidationAnnotationBuilder<PositiveOrZero>(){};
|
||||||
|
new BaseValidationAnnotationBuilder<Size>(){};
|
||||||
|
|
||||||
|
// hibernate-validator-6.0.10.Final.jar下org.hibernate.validator.constraints
|
||||||
|
new BaseValidationAnnotationBuilder<CodePointLength>(){};
|
||||||
|
new BaseValidationAnnotationBuilder<ConstraintComposition>(){};
|
||||||
|
new BaseValidationAnnotationBuilder<CreditCardNumber>(){};
|
||||||
|
new BaseValidationAnnotationBuilder<Currency>(){};
|
||||||
|
new BaseValidationAnnotationBuilder<EAN>(){};
|
||||||
|
new BaseValidationAnnotationBuilder<ISBN>(){};
|
||||||
|
new BaseValidationAnnotationBuilder<Length>(){};
|
||||||
|
new BaseValidationAnnotationBuilder<LuhnCheck>(){};
|
||||||
|
new BaseValidationAnnotationBuilder<Mod10Check>(){};
|
||||||
|
new BaseValidationAnnotationBuilder<Mod11Check>(){};
|
||||||
|
new BaseValidationAnnotationBuilder<ParameterScriptAssert>(){};
|
||||||
|
new BaseValidationAnnotationBuilder<Range>(){};
|
||||||
|
new BaseValidationAnnotationBuilder<SafeHtml>(){};
|
||||||
|
new BaseValidationAnnotationBuilder<ScriptAssert>(){};
|
||||||
|
new BaseValidationAnnotationBuilder<UniqueElements>(){};
|
||||||
|
new BaseValidationAnnotationBuilder<URL>(){};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加注解对应的构建器
|
||||||
|
*
|
||||||
|
* @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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -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<String, Class<?>> classGenricTypeCache = new HashMap<>(16);
|
||||||
|
|
||||||
|
/** key:obj.getClass().getName() + genericClass.getName() */
|
||||||
|
private static Map<String, Field> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回集合字段的泛型类型。<br>
|
||||||
|
* 如: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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回定义类时的泛型参数的类型. <br>
|
||||||
|
* 如:定义一个BookManager类<br>
|
||||||
|
* <code>{@literal public BookManager extends GenricManager<Book,Address>}{...} </code>
|
||||||
|
* <br>
|
||||||
|
* 调用getSuperClassGenricType(getClass(),0)将返回Book的Class类型<br>
|
||||||
|
* 调用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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -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.CategoryParam;
|
||||||
import com.gitee.sop.storyweb.controller.param.LargeTextParam;
|
import com.gitee.sop.storyweb.controller.param.LargeTextParam;
|
||||||
import com.gitee.sop.storyweb.controller.param.StoryParam;
|
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.CategoryResult;
|
||||||
import com.gitee.sop.storyweb.controller.result.StoryResult;
|
import com.gitee.sop.storyweb.controller.result.StoryResult;
|
||||||
import com.gitee.sop.storyweb.controller.result.TestResult;
|
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 org.springframework.web.context.request.ServletRequestAttributes;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
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.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@@ -98,6 +102,35 @@ public class Example1001_BaseController {
|
|||||||
return result;
|
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)
|
@Open(value = "story.param.bind", mergeResult = false)
|
||||||
@RequestMapping("/get/param/v1")
|
@RequestMapping("/get/param/v1")
|
||||||
|
@@ -0,0 +1,8 @@
|
|||||||
|
package com.gitee.sop.storyweb.controller.param;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author tanghc
|
||||||
|
*/
|
||||||
|
public enum TypeEnum {
|
||||||
|
MOBILE, COMPUTER
|
||||||
|
}
|
@@ -3,6 +3,7 @@ package com.gitee.sop.gateway.interceptor;
|
|||||||
import com.gitee.sop.gatewaycommon.interceptor.RouteInterceptor;
|
import com.gitee.sop.gatewaycommon.interceptor.RouteInterceptor;
|
||||||
import com.gitee.sop.gatewaycommon.interceptor.RouteInterceptorContext;
|
import com.gitee.sop.gatewaycommon.interceptor.RouteInterceptorContext;
|
||||||
import com.gitee.sop.gatewaycommon.param.ApiParam;
|
import com.gitee.sop.gatewaycommon.param.ApiParam;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.cloud.client.ServiceInstance;
|
import org.springframework.cloud.client.ServiceInstance;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
@@ -11,20 +12,23 @@ import org.springframework.stereotype.Component;
|
|||||||
*
|
*
|
||||||
* @author tanghc
|
* @author tanghc
|
||||||
*/
|
*/
|
||||||
|
@Slf4j
|
||||||
@Component
|
@Component
|
||||||
public class MyRouteInterceptor implements RouteInterceptor {
|
public class MyRouteInterceptor implements RouteInterceptor {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void preRoute(RouteInterceptorContext context) {
|
public void preRoute(RouteInterceptorContext context) {
|
||||||
ApiParam apiParam = context.getApiParam();
|
ApiParam apiParam = context.getApiParam();
|
||||||
System.out.println(String.format("请求接口:%s, ip:%s", apiParam.fetchNameVersion(), apiParam.fetchIp()));
|
log.info("请求接口:{}, ip:{}", apiParam.fetchNameVersion(), apiParam.fetchIp());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterRoute(RouteInterceptorContext context) {
|
public void afterRoute(RouteInterceptorContext context) {
|
||||||
ServiceInstance serviceInstance = context.getServiceInstance();
|
ServiceInstance serviceInstance = context.getServiceInstance();
|
||||||
System.out.println("请求成功,serviceId:" + serviceInstance.getServiceId() + "(" + serviceInstance.getHost() + ":" + serviceInstance.getPort() + ")" +
|
log.info("请求成功,serviceId:{}({}:{}),微服务返回结果:{}",
|
||||||
",微服务返回结果:" + context.getServiceResult());
|
serviceInstance.getServiceId(),
|
||||||
|
serviceInstance.getHost(),serviceInstance.getPort(),
|
||||||
|
context.getServiceResult());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
126
sop-test/src/test/java/com/gitee/sop/test/OneParamTest.java
Normal file
126
sop-test/src/test/java/com/gitee/sop/test/OneParamTest.java
Normal file
@@ -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<String, String> params = new HashMap<String, String>();
|
||||||
|
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<String, Object> 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<String, String> params = new HashMap<String, String>();
|
||||||
|
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<String, Object> 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<String, String> params = new HashMap<String, String>();
|
||||||
|
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<String, Object> 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
Reference in New Issue
Block a user