parent
f8b8e5fca1
commit
a16a7aa2d3
@ -1,186 +0,0 @@ |
|||||||
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); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,44 +0,0 @@ |
|||||||
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 parameterName = parameter.getName(); |
|
||||||
String key = open.value() + version + parameterName; |
|
||||||
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; |
|
||||||
} |
|
||||||
} |
|
@ -1,24 +0,0 @@ |
|||||||
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; |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,27 +0,0 @@ |
|||||||
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; |
|
||||||
} |
|
||||||
} |
|
@ -1,128 +0,0 @@ |
|||||||
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; |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
Loading…
Reference in new issue