commit
e21d75e4dd
@ -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; |
||||
} |
||||
} |
@ -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; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,8 @@ |
||||
package com.gitee.sop.storyweb.controller.param; |
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
public enum TypeEnum { |
||||
MOBILE, COMPUTER |
||||
} |
@ -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); |
||||
} |
||||
|
||||
|
||||
} |
Loading…
Reference in new issue