diff --git a/src/org/redkale/net/http/Rest.java b/src/org/redkale/net/http/Rest.java index ec041f3cf..c3306ec5c 100644 --- a/src/org/redkale/net/http/Rest.java +++ b/src/org/redkale/net/http/Rest.java @@ -17,7 +17,7 @@ import jdk.internal.org.objectweb.asm.*; import static jdk.internal.org.objectweb.asm.ClassWriter.COMPUTE_FRAMES; import static jdk.internal.org.objectweb.asm.Opcodes.*; import jdk.internal.org.objectweb.asm.Type; -import org.redkale.convert.json.JsonConvert; +import org.redkale.convert.json.*; import org.redkale.service.*; import org.redkale.util.*; import org.redkale.source.Flipper; @@ -35,6 +35,8 @@ public final class Rest { static final String REST_SERVICE_FIELD_NAME = "_redkale_service"; + static final String REST_JSONCONVERT_FIELD_PREFIX = "_redkale_jsonconvert_"; + static final String REST_SERVICEMAP_FIELD_NAME = "_redkale_servicemap"; //如果只有name=""的Service资源,则实例中_servicemap必须为null private static final String REST_PARAMTYPES_FIELD_NAME = "_redkale_paramtypes"; //存在泛型的参数数组 Type[][] 第1维度是方法的下标, 第二维度是参数的下标 @@ -109,6 +111,20 @@ public final class Rest { } } + static JsonConvert createJsonConvert(RestConvert[] converts) { + if (converts == null || converts.length < 1) return JsonConvert.root(); + final JsonFactory childFactory = JsonFactory.root().createChild(); + List types = new ArrayList<>(); + for (RestConvert rc : converts) { + if (types.contains(rc.type())) throw new RuntimeException("@RestConvert type(" + rc.type() + ") repeat"); + childFactory.register(rc.type(), false, rc.convertColumns()); + childFactory.register(rc.type(), true, rc.ignoreColumns()); + childFactory.reloadCoder(rc.type()); + types.add(rc.type()); + } + return childFactory.getConvert(); + } + static String getWebModuleName(Class serviceType) { final RestService controller = serviceType.getAnnotation(RestService.class); if (controller == null) return serviceType.getSimpleName().replaceAll("Service.*$", "").toLowerCase(); @@ -525,6 +541,7 @@ public final class Rest { final String webServletDesc = Type.getDescriptor(WebServlet.class); final String reqDesc = Type.getDescriptor(HttpRequest.class); final String respDesc = Type.getDescriptor(HttpResponse.class); + final String convertDesc = Type.getDescriptor(JsonConvert.class); final String retDesc = Type.getDescriptor(RetResult.class); final String futureDesc = Type.getDescriptor(CompletableFuture.class); final String flipperDesc = Type.getDescriptor(Flipper.class); @@ -672,6 +689,7 @@ public final class Rest { final Map> asmParamMap = MethodParamClassVisitor.getMethodParamNames(serviceType); final Map bodyTypes = new HashMap<>(); + final List restConverts = new ArrayList<>(); for (final MappingEntry entry : entrys) { RestUploadFile mupload = null; Class muploadType = null; @@ -680,6 +698,9 @@ public final class Rest { final String methodDesc = Type.getMethodDescriptor(method); final Parameter[] params = method.getParameters(); + final RestConvert[] rcs = method.getAnnotationsByType(RestConvert.class); + if (rcs != null && rcs.length > 0) restConverts.add(rcs); + mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, entry.name, "(" + reqDesc + respDesc + ")V", null, new String[]{"java/io/IOException"})); //mv.setDebug(true); mv.debugLine(); @@ -1293,7 +1314,7 @@ public final class Rest { if (ru != null && field.getType() != byte[].class && field.getType() != File.class && field.getType() != File[].class) { throw new RuntimeException("@RestUploadFile must on byte[] or File or File[] Field in " + field); } - + if (ri != null && field.getType() != String.class) throw new RuntimeException("@RestURI must on String Field in " + field); org.redkale.util.Attribute attr = org.redkale.util.Attribute.create(loop, field); String attrFieldName; @@ -1489,15 +1510,29 @@ public final class Rest { } else if (RetResult.class.isAssignableFrom(returnType)) { mv.visitVarInsn(ASTORE, maxLocals); mv.visitVarInsn(ALOAD, 2); //response - mv.visitVarInsn(ALOAD, maxLocals); - mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + retDesc + ")V", false); + if (rcs != null && rcs.length > 0) { + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, REST_JSONCONVERT_FIELD_PREFIX + restConverts.size(), convertDesc); + mv.visitVarInsn(ALOAD, maxLocals); + mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + convertDesc + retDesc + ")V", false); + } else { + mv.visitVarInsn(ALOAD, maxLocals); + mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + retDesc + ")V", false); + } mv.visitInsn(RETURN); maxLocals++; } else if (HttpResult.class.isAssignableFrom(returnType)) { mv.visitVarInsn(ASTORE, maxLocals); - mv.visitVarInsn(ALOAD, 2); //response - mv.visitVarInsn(ALOAD, maxLocals); - mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + httprsDesc + ")V", false); + mv.visitVarInsn(ALOAD, 2); //response + if (rcs != null && rcs.length > 0) { + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, REST_JSONCONVERT_FIELD_PREFIX + restConverts.size(), convertDesc); + mv.visitVarInsn(ALOAD, maxLocals); + mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + convertDesc + httprsDesc + ")V", false); + } else { + mv.visitVarInsn(ALOAD, maxLocals); + mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + httprsDesc + ")V", false); + } mv.visitInsn(RETURN); maxLocals++; } else if (Number.class.isAssignableFrom(returnType) || CharSequence.class.isAssignableFrom(returnType)) { //returnType == String.class 必须放在前面 @@ -1511,15 +1546,29 @@ public final class Rest { } else if (CompletableFuture.class.isAssignableFrom(returnType)) { mv.visitVarInsn(ASTORE, maxLocals); mv.visitVarInsn(ALOAD, 2);//response - mv.visitVarInsn(ALOAD, maxLocals); - mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + futureDesc + ")V", false); + if (rcs != null && rcs.length > 0) { + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, REST_JSONCONVERT_FIELD_PREFIX + restConverts.size(), convertDesc); + mv.visitVarInsn(ALOAD, maxLocals); + mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + convertDesc + futureDesc + ")V", false); + } else { + mv.visitVarInsn(ALOAD, maxLocals); + mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + futureDesc + ")V", false); + } mv.visitInsn(RETURN); maxLocals++; } else { mv.visitVarInsn(ASTORE, maxLocals); mv.visitVarInsn(ALOAD, 2); //response - mv.visitVarInsn(ALOAD, maxLocals); - mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(Ljava/lang/Object;)V", false); + if (rcs != null && rcs.length > 0) { + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, REST_JSONCONVERT_FIELD_PREFIX + restConverts.size(), convertDesc); + mv.visitVarInsn(ALOAD, maxLocals); + mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + convertDesc + "Ljava/lang/Object;)V", false); + } else { + mv.visitVarInsn(ALOAD, maxLocals); + mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(Ljava/lang/Object;)V", false); + } mv.visitInsn(RETURN); maxLocals++; } @@ -1538,6 +1587,11 @@ public final class Rest { fv.visitEnd(); } + for (int i = 1; i <= restConverts.size(); i++) { + fv = cw.visitField(ACC_PRIVATE, REST_JSONCONVERT_FIELD_PREFIX + i, convertDesc, null, null); + fv.visitEnd(); + } + //classMap.put("mappings", mappingMaps); //不显示太多信息 { //toString函数 mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null)); @@ -1562,6 +1616,11 @@ public final class Rest { genField.setAccessible(true); genField.set(obj, en.getValue()); } + for (int i = 0; i < restConverts.size(); i++) { + Field genField = newClazz.getDeclaredField(REST_JSONCONVERT_FIELD_PREFIX + (i + 1)); + genField.setAccessible(true); + genField.set(obj, createJsonConvert(restConverts.get(i))); + } Field typesfield = newClazz.getDeclaredField(REST_PARAMTYPES_FIELD_NAME); typesfield.setAccessible(true); java.lang.reflect.Type[][] paramtypeArray = new java.lang.reflect.Type[paramtypes.size()][]; diff --git a/src/org/redkale/net/http/RestConvert.java b/src/org/redkale/net/http/RestConvert.java new file mode 100644 index 000000000..72edffcfc --- /dev/null +++ b/src/org/redkale/net/http/RestConvert.java @@ -0,0 +1,41 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * 只能依附在Service实现类的public方法上, 当方法的返回值以JSON输出时对指定类型的转换设定。
+ * + *

+ * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Inherited +@Documented +@Target({METHOD}) +@Retention(RUNTIME) +@Repeatable(RestConvert.RestConverts.class) +public @interface RestConvert { + + Class type(); + + String[] ignoreColumns() default {}; + + String[] convertColumns() default {}; + + @Inherited + @Documented + @Target({METHOD}) + @Retention(RUNTIME) + @interface RestConverts { + + RestConvert[] value(); + } +}