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