diff --git a/src/main/java/org/redkale/net/sncp/SncpActionServlet.java b/src/main/java/org/redkale/net/sncp/SncpActionServlet.java index 392bbc79c..bbd1660eb 100644 --- a/src/main/java/org/redkale/net/sncp/SncpActionServlet.java +++ b/src/main/java/org/redkale/net/sncp/SncpActionServlet.java @@ -53,13 +53,13 @@ public abstract class SncpActionServlet extends SncpServlet { protected final Class paramHandlerClass; // CompletionHandler参数的类型 - protected final java.lang.reflect.Type paramHandlerResultType; // CompletionHandler.completed第一个参数的类型 + protected final java.lang.reflect.Type paramHandlerType; // CompletionHandler.completed第一个参数的类型 @ClassDepends protected final java.lang.reflect.Type returnObjectType; // 返回结果类型 void必须设为null @ClassDepends - protected final java.lang.reflect.Type returnFutureResultType; // 返回结果的CompletableFuture的结果泛型类型 + protected final java.lang.reflect.Type returnFutureType; // 返回结果的CompletableFuture的结果泛型类型 @ClassDepends protected SncpActionServlet( @@ -119,7 +119,7 @@ public abstract class SncpActionServlet extends SncpServlet { this.paramTypes = types; this.paramHandlerIndex = handlerFuncIndex; this.paramHandlerClass = handlerFuncClass; - this.paramHandlerResultType = handlerResultType; + this.paramHandlerType = handlerResultType; this.returnObjectType = originalReturnType == void.class || originalReturnType == Void.class ? null : originalReturnType; if (Future.class.isAssignableFrom(method.getReturnType())) { @@ -134,9 +134,9 @@ public abstract class SncpActionServlet extends SncpServlet { } else { throw new SncpException(service.getClass() + " had unknown return genericType in " + method); } - this.returnFutureResultType = returnType; + this.returnFutureType = returnType; } else { - this.returnFutureResultType = null; + this.returnFutureType = null; } NonBlocking non = method.getAnnotation(NonBlocking.class); if (non == null) { @@ -150,7 +150,7 @@ public abstract class SncpActionServlet extends SncpServlet { @Override public final void execute(SncpRequest request, SncpResponse response) throws IOException { if (paramHandlerIndex > 0) { - response.paramAsyncHandler(paramHandlerClass, paramHandlerResultType); + response.paramAsyncHandler(paramHandlerClass, paramHandlerType); } try { action(request, response); @@ -194,7 +194,7 @@ public abstract class SncpActionServlet extends SncpServlet { * * public void update(long show, short v2, CompletionHandler<Boolean, TestBean> handler, TestBean bean, String name, int id); * - * public CompletableFuture<String> changeName(TestBean bean, String name, int id); + * public CompletableFuture<String> changeName(TestBean bean, String name, int id); * * } * @@ -304,14 +304,14 @@ public abstract class SncpActionServlet extends SncpServlet { * @Override * public void action(SncpRequest request, SncpResponse response) throws Throwable { * Convert<Reader, Writer> convert = request.getConvert(); - * Reader in = request.getReader(); - * TestBean arg1 = convert.convertFrom(paramTypes[1], in); - * String arg2 = convert.convertFrom(paramTypes[2], in); - * int arg3 = convert.convertFrom(paramTypes[3], in); - * TestService serviceObj = (TestService) service(); - * CompletableFuture future = serviceObj.changeName(arg1, arg2, arg3); - * response.finishFuture(paramHandlerResultType, future); - * } + * Reader in = request.getReader(); + * TestBean arg1 = convert.convertFrom(paramTypes[1], in); + * String arg2 = convert.convertFrom(paramTypes[2], in); + * int arg3 = convert.convertFrom(paramTypes[3], in); + * TestService serviceObj = (TestService) service(); + * CompletableFuture future = serviceObj.changeName(arg1, arg2, arg3); + * response.finishFuture(paramHandlerType, future); + * } * } * * @@ -349,6 +349,9 @@ public abstract class SncpActionServlet extends SncpServlet { final String requestDesc = Type.getDescriptor(SncpRequest.class); final String responseDesc = Type.getDescriptor(SncpResponse.class); final String serviceDesc = Type.getDescriptor(Service.class); + final String handlerDesc = Type.getDescriptor(CompletionHandler.class); + final String futureDesc = Type.getDescriptor(Future.class); + final String reflectTypeDesc = Type.getDescriptor(java.lang.reflect.Type.class); final boolean boolReturnTypeFuture = Future.class.isAssignableFrom(method.getReturnType()); final String newDynName = "org/redkaledyn/sncp/servlet/action/_DynSncpActionServlet__" + resourceType.getSimpleName() + "_" + method.getName() + "_" + actionid; @@ -368,6 +371,27 @@ public abstract class SncpActionServlet extends SncpServlet { final java.lang.reflect.Type originalReturnType = TypeToken.getGenericType(method.getGenericReturnType(), serviceClass); + final Class[] paramClasses = method.getParameterTypes(); + java.lang.reflect.Type paramComposeBeanType0 = SncpRemoteAction.createParamComposeBeanType( + serviceImplClass, method, actionid, originalParamTypes, paramClasses); + if (paramComposeBeanType0 != null && paramComposeBeanType0 == originalParamTypes[0]) { + paramComposeBeanType0 = null; + } + int handlerFuncIndex = -1; + for (int i = 0; i < paramClasses.length; i++) { // 反序列化方法的每个参数 + if (CompletionHandler.class.isAssignableFrom(paramClasses[i])) { + if (boolReturnTypeFuture) { + throw new SncpException(method + " have both CompletionHandler and CompletableFuture"); + } + if (handlerFuncIndex >= 0) { + throw new SncpException(method + " have more than one CompletionHandler type parameter"); + } + Sncp.checkAsyncModifier(paramClasses[i], method); + handlerFuncIndex = i; + } + } + final java.lang.reflect.Type paramComposeBeanType = paramComposeBeanType0; + if (newClazz == null) { // ------------------------------------------------------------- ClassWriter cw = new ClassWriter(COMPUTE_FRAMES); @@ -434,107 +458,193 @@ public abstract class SncpActionServlet extends SncpServlet { mv.visitMethodInsn(INVOKEVIRTUAL, requestName, "getReader", "()" + readerDesc, false); mv.visitVarInsn(ASTORE, 4); } - int iconst = ICONST_1; - int intconst = 1; - int store = 5; // action的参数个数+2 - final Class[] paramClasses = method.getParameterTypes(); - int[][] codes = new int[paramClasses.length][2]; - int handlerFuncIndex = -1; - for (int i = 0; i < paramClasses.length; i++) { // 反序列化方法的每个参数 - if (CompletionHandler.class.isAssignableFrom(paramClasses[i])) { - if (boolReturnTypeFuture) { - throw new SncpException(method + " have both CompletionHandler and CompletableFuture"); + if (paramComposeBeanType == null) { + int iconst = ICONST_1; + int intconst = 1; + int store = 5; // action的参数个数+2 + int[][] codes = new int[paramClasses.length][2]; + for (int i = 0; i < paramClasses.length; i++) { // 反序列化方法的每个参数 + if (CompletionHandler.class.isAssignableFrom(paramClasses[i])) { + mv.visitVarInsn(ALOAD, 2); + mv.visitMethodInsn( + INVOKEVIRTUAL, + responseName, + "getParamAsyncHandler", + "()Ljava/nio/channels/CompletionHandler;", + false); + mv.visitTypeInsn( + CHECKCAST, paramClasses[i].getName().replace('.', '/')); + mv.visitVarInsn(ASTORE, store); + codes[i] = new int[] {ALOAD, store}; + store++; + iconst++; + intconst++; + mv.visitVarInsn(ALOAD, 3); + mv.visitLdcInsn(Type.getType(Type.getDescriptor(CompletionHandler.class))); + mv.visitVarInsn(ALOAD, 4); + mv.visitMethodInsn(INVOKEVIRTUAL, convertName, "convertFrom", convertFromDesc, false); + mv.visitInsn(POP); + continue; } - if (handlerFuncIndex >= 0) { - throw new SncpException(method + " have more than one CompletionHandler type parameter"); + mv.visitVarInsn(ALOAD, 3); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, "paramTypes", "[Ljava/lang/reflect/Type;"); + + if (intconst < 6) { + mv.visitInsn(ICONST_0 + intconst); + } else if (iconst <= Byte.MAX_VALUE) { + mv.visitIntInsn(BIPUSH, intconst); + } else if (iconst <= Short.MAX_VALUE) { + mv.visitIntInsn(SIPUSH, intconst); + } else { + mv.visitLdcInsn(intconst); } - Sncp.checkAsyncModifier(paramClasses[i], method); - handlerFuncIndex = i; - mv.visitVarInsn(ALOAD, 2); - mv.visitMethodInsn( - INVOKEVIRTUAL, - responseName, - "getParamAsyncHandler", - "()Ljava/nio/channels/CompletionHandler;", - false); - mv.visitTypeInsn(CHECKCAST, paramClasses[i].getName().replace('.', '/')); - mv.visitVarInsn(ASTORE, store); - codes[i] = new int[] {ALOAD, store}; - store++; + mv.visitInsn(AALOAD); + mv.visitVarInsn(ALOAD, 4); + + mv.visitMethodInsn(INVOKEVIRTUAL, convertName, "convertFrom", convertFromDesc, false); + int load = ALOAD; + int v = 0; + if (paramClasses[i].isPrimitive()) { + int storecode = ISTORE; + load = ILOAD; + if (paramClasses[i] == long.class) { + storecode = LSTORE; + load = LLOAD; + v = 1; + } else if (paramClasses[i] == float.class) { + storecode = FSTORE; + load = FLOAD; + v = 1; + } else if (paramClasses[i] == double.class) { + storecode = DSTORE; + load = DLOAD; + v = 1; + } + Class bigPrimitiveClass = TypeToken.primitiveToWrapper(paramClasses[i]); + String bigPrimitiveName = + bigPrimitiveClass.getName().replace('.', '/'); + try { + Method pm = bigPrimitiveClass.getMethod(paramClasses[i].getSimpleName() + "Value"); + mv.visitTypeInsn(CHECKCAST, bigPrimitiveName); + mv.visitMethodInsn( + INVOKEVIRTUAL, + bigPrimitiveName, + pm.getName(), + Type.getMethodDescriptor(pm), + false); + } catch (Exception ex) { + throw new SncpException(ex); // 不可能会发生 + } + mv.visitVarInsn(storecode, store); + } else { + mv.visitTypeInsn( + CHECKCAST, paramClasses[i].getName().replace('.', '/')); + mv.visitVarInsn(ASTORE, store); // + } + codes[i] = new int[] {load, store}; + store += v; iconst++; intconst++; - mv.visitVarInsn(ALOAD, 3); - mv.visitLdcInsn(Type.getType(Type.getDescriptor(CompletionHandler.class))); - mv.visitVarInsn(ALOAD, 4); - mv.visitMethodInsn(INVOKEVIRTUAL, convertName, "convertFrom", convertFromDesc, false); - mv.visitInsn(POP); - continue; + store++; } - mv.visitVarInsn(ALOAD, 3); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynName, "paramTypes", "[Ljava/lang/reflect/Type;"); + { // 调用service + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "service", "()" + serviceDesc, false); + mv.visitTypeInsn(CHECKCAST, serviceImpTypeName); + mv.visitVarInsn(ASTORE, store); - if (intconst < 6) { - mv.visitInsn(ICONST_0 + intconst); - } else if (iconst <= Byte.MAX_VALUE) { - mv.visitIntInsn(BIPUSH, intconst); - } else if (iconst <= Short.MAX_VALUE) { - mv.visitIntInsn(SIPUSH, intconst); - } else { - mv.visitLdcInsn(intconst); - } - mv.visitInsn(AALOAD); - mv.visitVarInsn(ALOAD, 4); - - mv.visitMethodInsn(INVOKEVIRTUAL, convertName, "convertFrom", convertFromDesc, false); - int load = ALOAD; - int v = 0; - if (paramClasses[i].isPrimitive()) { - int storecode = ISTORE; - load = ILOAD; - if (paramClasses[i] == long.class) { - storecode = LSTORE; - load = LLOAD; - v = 1; - } else if (paramClasses[i] == float.class) { - storecode = FSTORE; - load = FLOAD; - v = 1; - } else if (paramClasses[i] == double.class) { - storecode = DSTORE; - load = DLOAD; - v = 1; + mv.visitVarInsn(ALOAD, store); + for (int[] j : codes) { + mv.visitVarInsn(j[0], j[1]); } - Class bigPrimitiveClass = TypeToken.primitiveToWrapper(paramClasses[i]); - String bigPrimitiveName = bigPrimitiveClass.getName().replace('.', '/'); - try { - Method pm = bigPrimitiveClass.getMethod(paramClasses[i].getSimpleName() + "Value"); - mv.visitTypeInsn(CHECKCAST, bigPrimitiveName); + mv.visitMethodInsn( + serviceImplClass.isInterface() ? INVOKEINTERFACE : INVOKEVIRTUAL, + serviceImpTypeName, + method.getName(), + Type.getMethodDescriptor(method), + serviceImplClass.isInterface()); + store++; + } + + if (method.getReturnType() != void.class) { + final Class returnClass = method.getReturnType(); + if (returnClass.isPrimitive()) { + Class bigClass = TypeToken.primitiveToWrapper(returnClass); + try { + Method vo = bigClass.getMethod("valueOf", returnClass); + mv.visitMethodInsn( + INVOKESTATIC, + bigClass.getName().replace('.', '/'), + vo.getName(), + Type.getMethodDescriptor(vo), + false); + } catch (Exception ex) { + throw new SncpException(ex); // 不可能会发生 + } + } + mv.visitVarInsn(ASTORE, store); // 11 + + if (boolReturnTypeFuture) { // 返回类型为Future + mv.visitVarInsn(ALOAD, 2); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, "returnFutureType", reflectTypeDesc); + mv.visitVarInsn(ALOAD, store); mv.visitMethodInsn( - INVOKEVIRTUAL, bigPrimitiveName, pm.getName(), Type.getMethodDescriptor(pm), false); - } catch (Exception ex) { - throw new SncpException(ex); // 不可能会发生 + INVOKEVIRTUAL, + responseName, + "finishFuture", + "(" + reflectTypeDesc + futureDesc + ")V", + false); + } else if (handlerFuncIndex >= 0) { // 参数有CompletionHandler + mv.visitVarInsn(ALOAD, 2); + mv.visitMethodInsn(INVOKEVIRTUAL, responseName, "finishVoid", "()V", false); + } else { // 普通对象 + mv.visitVarInsn(ALOAD, 2); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, "returnObjectType", reflectTypeDesc); + mv.visitVarInsn(ALOAD, store); + mv.visitMethodInsn( + INVOKEVIRTUAL, + responseName, + "finish", + "(" + reflectTypeDesc + "Ljava/lang/Object;)V", + false); } - mv.visitVarInsn(storecode, store); - } else { - mv.visitTypeInsn(CHECKCAST, paramClasses[i].getName().replace('.', '/')); - mv.visitVarInsn(ASTORE, store); // + } else { // void返回类型 + mv.visitVarInsn(ALOAD, 2); + mv.visitMethodInsn(INVOKEVIRTUAL, responseName, "finishVoid", "()V", false); } - codes[i] = new int[] {load, store}; - store += v; - iconst++; - intconst++; - store++; - } - { // 调用service + } else { // 动态生成的参数组合类 + Class paramComposeBeanClass = TypeToken.typeToClass(paramComposeBeanType); + String paramComposeBeanName = + paramComposeBeanClass.getName().replace('.', '/'); + mv.visitVarInsn(ALOAD, 3); // convert + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, "paramComposeType", reflectTypeDesc); + mv.visitVarInsn(ALOAD, 4); // reader + mv.visitMethodInsn(INVOKEVIRTUAL, convertName, "convertFrom", convertFromDesc, false); + mv.visitTypeInsn(CHECKCAST, paramComposeBeanName); + mv.visitVarInsn(ASTORE, 5); // paramBean + + // 给CompletionHandler参数赋值 + mv.visitVarInsn(ALOAD, 5); + mv.visitVarInsn(ALOAD, 2); + mv.visitMethodInsn(INVOKEVIRTUAL, responseName, "getParamAsyncHandler", "()" + handlerDesc, false); + mv.visitFieldInsn(PUTFIELD, paramComposeBeanName, "arg3", handlerDesc); + + // 调用service() mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "service", "()" + serviceDesc, false); mv.visitTypeInsn(CHECKCAST, serviceImpTypeName); - mv.visitVarInsn(ASTORE, store); + mv.visitVarInsn(ASTORE, 6); // service - mv.visitVarInsn(ALOAD, store); - for (int[] j : codes) { - mv.visitVarInsn(j[0], j[1]); + // 执行service方法 + mv.visitVarInsn(ALOAD, 6); // service + for (int i = 1; i <= paramClasses.length; i++) { + mv.visitVarInsn(ALOAD, 5); // paramBean + mv.visitFieldInsn( + GETFIELD, paramComposeBeanName, "arg" + i, Type.getDescriptor(paramClasses[i - 1])); } mv.visitMethodInsn( serviceImplClass.isInterface() ? INVOKEINTERFACE : INVOKEVIRTUAL, @@ -542,58 +652,60 @@ public abstract class SncpActionServlet extends SncpServlet { method.getName(), Type.getMethodDescriptor(method), serviceImplClass.isInterface()); - store++; - } - if (method.getReturnType() != void.class) { - final Class returnClass = method.getReturnType(); - if (returnClass.isPrimitive()) { - Class bigClass = TypeToken.primitiveToWrapper(returnClass); - try { - Method vo = bigClass.getMethod("valueOf", returnClass); - mv.visitMethodInsn( - INVOKESTATIC, - bigClass.getName().replace('.', '/'), - vo.getName(), - Type.getMethodDescriptor(vo), - false); - } catch (Exception ex) { - throw new SncpException(ex); // 不可能会发生 - } - } - mv.visitVarInsn(ASTORE, store); // 11 - if (boolReturnTypeFuture) { // 返回类型为Future - mv.visitVarInsn(ALOAD, 2); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynName, "returnFutureResultType", "Ljava/lang/reflect/Type;"); - mv.visitVarInsn(ALOAD, store); - mv.visitMethodInsn( - INVOKEVIRTUAL, - responseName, - "finishFuture", - "(Ljava/lang/reflect/Type;Ljava/util/concurrent/Future;)V", - false); - } else if (handlerFuncIndex >= 0) { // 参数有CompletionHandler - mv.visitVarInsn(ALOAD, 2); + // 返回 + if (method.getReturnType() != void.class) { + final Class returnClass = method.getReturnType(); + if (returnClass.isPrimitive()) { + Class bigClass = TypeToken.primitiveToWrapper(returnClass); + try { + Method vo = bigClass.getMethod("valueOf", returnClass); + mv.visitMethodInsn( + INVOKESTATIC, + bigClass.getName().replace('.', '/'), + vo.getName(), + Type.getMethodDescriptor(vo), + false); + } catch (Exception ex) { + throw new SncpException(ex); // 不可能会发生 + } + } + mv.visitVarInsn(ASTORE, 7); // returnObject + + if (boolReturnTypeFuture) { // 返回类型为Future + mv.visitVarInsn(ALOAD, 2); // response + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, "returnFutureType", reflectTypeDesc); + mv.visitVarInsn(ALOAD, 7); // returnObject + mv.visitMethodInsn( + INVOKEVIRTUAL, + responseName, + "finishFuture", + "(" + reflectTypeDesc + futureDesc + ")V", + false); + } else if (handlerFuncIndex >= 0) { // 参数有CompletionHandler + mv.visitVarInsn(ALOAD, 2); // response + mv.visitMethodInsn(INVOKEVIRTUAL, responseName, "finishVoid", "()V", false); + } else { // 普通对象 + mv.visitVarInsn(ALOAD, 2); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, "returnObjectType", reflectTypeDesc); + mv.visitVarInsn(ALOAD, 7); // returnObject + mv.visitMethodInsn( + INVOKEVIRTUAL, + responseName, + "finish", + "(" + reflectTypeDesc + "Ljava/lang/Object;)V", + false); + } + } else { // void返回类型 + mv.visitVarInsn(ALOAD, 2); // response mv.visitMethodInsn(INVOKEVIRTUAL, responseName, "finishVoid", "()V", false); - } else { // 普通对象 - mv.visitVarInsn(ALOAD, 2); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynName, "returnObjectType", "Ljava/lang/reflect/Type;"); - mv.visitVarInsn(ALOAD, store); - mv.visitMethodInsn( - INVOKEVIRTUAL, - responseName, - "finish", - "(Ljava/lang/reflect/Type;Ljava/lang/Object;)V", - false); } - } else { // void返回类型 - mv.visitVarInsn(ALOAD, 2); - mv.visitMethodInsn(INVOKEVIRTUAL, responseName, "finishVoid", "()V", false); } + mv.visitInsn(RETURN); - mv.visitMaxs(8, store); + mv.visitMaxs(8, 8); mv.visitEnd(); } cw.visitEnd(); diff --git a/src/main/java/org/redkale/net/sncp/SncpRemoteAction.java b/src/main/java/org/redkale/net/sncp/SncpRemoteAction.java index fe2f03111..f05346da7 100644 --- a/src/main/java/org/redkale/net/sncp/SncpRemoteAction.java +++ b/src/main/java/org/redkale/net/sncp/SncpRemoteAction.java @@ -60,6 +60,9 @@ public final class SncpRemoteAction { // 参数数量为0: 值为null; 参数数量为1且参数类型为JavaBean: 值为第一个参数的类型; 其他情况: 动态生成的Object类 protected final Type paramComposeBeanType; + // 只有paramComposeBeanType为动态生成的组合类时才有值; + protected final Creator paramComposeBeanCreator; + protected final int paramHandlerIndex; protected final int paramHandlerAttachIndex; @@ -72,9 +75,9 @@ public final class SncpRemoteAction { protected final Class paramHandlerClass; // CompletionHandler参数的类型 - protected final java.lang.reflect.Type paramHandlerResultType; // CompletionHandler.completed第一个参数的类型 + protected final java.lang.reflect.Type paramHandlerType; // CompletionHandler.completed第一个参数的类型 - protected final java.lang.reflect.Type returnFutureResultType; // 返回结果的CompletableFuture的结果泛型类型 + protected final java.lang.reflect.Type returnFutureType; // 返回结果的CompletableFuture的结果泛型类型 protected final Class returnFutureClass; // 返回结果的CompletableFuture类型 @@ -95,8 +98,10 @@ public final class SncpRemoteAction { this.returnObjectType = rt == void.class || rt == Void.class ? null : rt; this.paramTypes = TypeToken.getGenericType(method.getGenericParameterTypes(), serviceImplClass); this.paramClasses = method.getParameterTypes(); - this.paramComposeBeanType = - createParamComposeBeanType(serviceImplClass, method, actionid, paramTypes, paramClasses); + Type pt = createParamComposeBeanType(serviceImplClass, method, actionid, paramTypes, paramClasses); + this.paramComposeBeanType = pt; + this.paramComposeBeanCreator = + (pt == null || pt == paramTypes[0]) ? null : Creator.create(TypeToken.typeToClass(pt), 1); this.method = method; Annotation[][] anns = method.getParameterAnnotations(); int topicAddrIndex = -1; @@ -186,7 +191,7 @@ public final class SncpRemoteAction { this.paramAddressSourceIndex = sourceAddrIndex; this.paramHandlerIndex = handlerFuncIndex; this.paramHandlerClass = handlerFuncClass; - this.paramHandlerResultType = handlerResultType; + this.paramHandlerType = handlerResultType; this.paramHandlerAttachIndex = handlerAttachIndex; this.header = SncpHeader.create( sncpClient == null ? null : sncpClient.getClientSncpAddress(), @@ -209,7 +214,7 @@ public final class SncpRemoteAction { } else { throw new SncpException(serviceImplClass + " had unknown return genericType in " + method); } - this.returnFutureResultType = returnType; + this.returnFutureType = returnType; this.returnFutureClass = method.getReturnType().isAssignableFrom(CompletableFuture.class) ? CompletableFuture.class : (Class) method.getReturnType(); @@ -220,7 +225,7 @@ public final class SncpRemoteAction { throw new SncpException(serviceImplClass + " return must be CompletableFuture or subclass"); } } else { - this.returnFutureResultType = null; + this.returnFutureType = null; this.returnFutureClass = null; this.returnFutureCreator = null; } @@ -247,7 +252,7 @@ public final class SncpRemoteAction { // 动态生成组合JavaBean类 final Class serviceClass = resourceType.getClass(); final String columnDesc = org.redkale.asm.Type.getDescriptor(ConvertColumn.class); - final String newDynName = "org/redkaledyn/sncp/servlet/action/_DynSncpActionParamBean__" + final String newDynName = "org/redkaledyn/sncp/servlet/action/_DynSncpActionParamBean_" + resourceType.getSimpleName() + "_" + method.getName() + "_" + actionid; try { Class clz = RedkaleClassLoader.findDynClass(newDynName.replace('/', '.')); @@ -278,7 +283,7 @@ public final class SncpRemoteAction { av.visitEnd(); fv.visitEnd(); } - { // 空参数构造函数 + { // 空参数的构造函数 mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "", "()V", null, null)); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); @@ -286,7 +291,7 @@ public final class SncpRemoteAction { mv.visitMaxs(1, 1); mv.visitEnd(); } - { // 带参数构造函数 + { // 一个参数的构造函数 mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "", "([Ljava/lang/Object;)V", null, null)); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); @@ -306,13 +311,16 @@ public final class SncpRemoteAction { cw.visitEnd(); byte[] bytes = cw.toByteArray(); - Class newClazz = new ClassLoader(serviceClass.getClassLoader()) { + Class newClazz = new ClassLoader(Thread.currentThread().getContextClassLoader()) { public final Class loadClass(String name, byte[] b) { return defineClass(name, b, 0, b.length); } }.loadClass(newDynName.replace('/', '.'), bytes); RedkaleClassLoader.putDynClass(newDynName.replace('/', '.'), bytes, newClazz); RedkaleClassLoader.putReflectionDeclaredConstructors(newClazz, newDynName.replace('/', '.')); + Creator.load(newClazz); + ProtobufFactory.root().loadDecoder(newClazz); + ProtobufFactory.root().loadEncoder(newClazz); return newClazz; } } diff --git a/src/main/java/org/redkale/net/sncp/SncpRemoteInfo.java b/src/main/java/org/redkale/net/sncp/SncpRemoteInfo.java index 1829b659c..894664c02 100644 --- a/src/main/java/org/redkale/net/sncp/SncpRemoteInfo.java +++ b/src/main/java/org/redkale/net/sncp/SncpRemoteInfo.java @@ -133,7 +133,7 @@ public class SncpRemoteInfo { handler.completed( v == null ? null - : convert.convertFrom(action.paramHandlerResultType, v, 1, v.length - 1), + : convert.convertFrom(action.paramHandlerType, v, 1, v.length - 1), attach); } else { handler.failed(t, attach); @@ -144,7 +144,7 @@ public class SncpRemoteInfo { if (action.returnFutureClass == CompletableFuture.class) { // v,length-1为了读掉(byte)0 return (T) future.thenApply( - v -> v == null ? null : convert.convertFrom(action.returnFutureResultType, v, 1, v.length - 1)); + v -> v == null ? null : convert.convertFrom(action.returnFutureType, v, 1, v.length - 1)); } else { final CompletableFuture returnFuture = action.returnFutureCreator.create(); future.whenComplete((v, t) -> { @@ -153,7 +153,7 @@ public class SncpRemoteInfo { returnFuture.complete( v == null ? null - : convert.convertFrom(action.returnFutureResultType, v, 1, v.length - 1)); + : convert.convertFrom(action.returnFutureType, v, 1, v.length - 1)); } else { returnFuture.completeExceptionally(t); } @@ -256,8 +256,13 @@ public class SncpRemoteInfo { byte[] body = null; if (myParamTypes.length > 0) { // 存在参数 ProtobufWriter writer = convert.pollWriter(); - for (int i = 0; i < params.length; i++) { // service方法的参数 - convert.convertTo(writer, myParamTypes[i], params[i]); + if (action.paramComposeBeanCreator != null) { + Object paramBean = action.paramComposeBeanCreator.create(params); + convert.convertTo(writer, action.paramComposeBeanType, paramBean); + } else { + for (int i = 0; i < params.length; i++) { // service方法的参数 + convert.convertTo(writer, myParamTypes[i], params[i]); + } } body = writer.toByteArray().content(); convert.offerWriter(writer); diff --git a/src/main/java/org/redkale/util/Creator.java b/src/main/java/org/redkale/util/Creator.java index 1cb0e0f43..975196bbe 100644 --- a/src/main/java/org/redkale/util/Creator.java +++ b/src/main/java/org/redkale/util/Creator.java @@ -4,9 +4,7 @@ */ package org.redkale.util; -import java.io.*; import java.lang.reflect.*; -import java.net.*; import java.util.*; import java.util.AbstractMap.SimpleEntry; import java.util.concurrent.*; @@ -360,6 +358,16 @@ public interface Creator { break; } } + if (constructor0 == null && paramCount == 1) { + for (Constructor c : cs) { + int cc = c.getParameterCount(); + if (cc == 1 && c.getParameterTypes()[0] == Object[].class) { + constructor0 = c; + constructorParameters0 = new SimpleEntry[1]; + break; + } + } + } } if (constructor0 == null) { // 4、查找非private带ConstructorParameters的构造函数 for (Constructor c : clazz.getDeclaredConstructors()) { @@ -439,12 +447,14 @@ public interface Creator { int paramLen = constructorParameters.length; Asms.visitInsn(mv, paramLen); mv.visitTypeInsn(ANEWARRAY, "java/lang/Class"); - for (int i = 0; i < constructorParameters.length; i++) { - final Class pt = constructorParameters[i].getValue(); mv.visitInsn(DUP); Asms.visitInsn(mv, i); - Asms.visitFieldInsn(mv, pt); + if (constructorParameters[i] == null) { + mv.visitLdcInsn(Type.getType("[Ljava/lang/Object;")); + } else { + Asms.visitFieldInsn(mv, constructorParameters[i].getValue()); + } mv.visitInsn(AASTORE); } mv.visitInsn(ARETURN); @@ -454,7 +464,7 @@ public interface Creator { { // create 方法 mv = cw.visitMethod( ACC_PUBLIC + ACC_VARARGS, "create", "([Ljava/lang/Object;)L" + interName + ";", null, null); - if (constructorParameters.length > 0) { + if (constructorParameters.length > 0 && constructorParameters[0] != null) { av0 = mv.visitAnnotation(Type.getDescriptor(ConstructorParameters.class), true); AnnotationVisitor av1 = av0.visitArray("value"); for (SimpleEntry n : constructorParameters) { @@ -463,81 +473,84 @@ public interface Creator { av1.visitEnd(); av0.visitEnd(); } - { // 有Primitive数据类型且值为null的参数需要赋默认值 - for (int i = 0; i < constructorParameters.length; i++) { - final Class pt = constructorParameters[i].getValue(); - if (!pt.isPrimitive()) { - continue; - } - mv.visitVarInsn(ALOAD, 1); - Asms.visitInsn(mv, i); - mv.visitInsn(AALOAD); - Label lab = new Label(); - mv.visitJumpInsn(IFNONNULL, lab); - mv.visitVarInsn(ALOAD, 1); - Asms.visitInsn(mv, i); - if (pt == int.class) { - mv.visitInsn(ICONST_0); - mv.visitMethodInsn( - INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false); - } else if (pt == long.class) { - mv.visitInsn(LCONST_0); - mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false); - } else if (pt == boolean.class) { - mv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "FALSE", "Ljava/lang/Boolean;"); - } else if (pt == short.class) { - mv.visitInsn(ICONST_0); - mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;", false); - } else if (pt == float.class) { - mv.visitInsn(FCONST_0); - mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", false); - } else if (pt == byte.class) { - mv.visitInsn(ICONST_0); - mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;", false); - } else if (pt == double.class) { - mv.visitInsn(DCONST_0); - mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false); - } else if (pt == char.class) { - mv.visitInsn(ICONST_0); - mv.visitMethodInsn( - INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;", false); - } - mv.visitInsn(AASTORE); - mv.visitLabel(lab); + // 有Primitive数据类型且值为null的参数需要赋默认值 + for (int i = 0; i < constructorParameters.length; i++) { + if (constructorParameters[i] == null) { + continue; } + final Class pt = constructorParameters[i].getValue(); + if (!pt.isPrimitive()) { + continue; + } + mv.visitVarInsn(ALOAD, 1); + Asms.visitInsn(mv, i); + mv.visitInsn(AALOAD); + Label lab = new Label(); + mv.visitJumpInsn(IFNONNULL, lab); + mv.visitVarInsn(ALOAD, 1); + Asms.visitInsn(mv, i); + if (pt == int.class) { + mv.visitInsn(ICONST_0); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false); + } else if (pt == long.class) { + mv.visitInsn(LCONST_0); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false); + } else if (pt == boolean.class) { + mv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "FALSE", "Ljava/lang/Boolean;"); + } else if (pt == short.class) { + mv.visitInsn(ICONST_0); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;", false); + } else if (pt == float.class) { + mv.visitInsn(FCONST_0); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", false); + } else if (pt == byte.class) { + mv.visitInsn(ICONST_0); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;", false); + } else if (pt == double.class) { + mv.visitInsn(DCONST_0); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false); + } else if (pt == char.class) { + mv.visitInsn(ICONST_0); + mv.visitMethodInsn( + INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;", false); + } + mv.visitInsn(AASTORE); + mv.visitLabel(lab); } mv.visitTypeInsn(NEW, interName); mv.visitInsn(DUP); // --------------------------------------- - { - for (int i = 0; i < constructorParameters.length; i++) { + for (int i = 0; i < constructorParameters.length; i++) { + if (constructorParameters[i] == null) { mv.visitVarInsn(ALOAD, 1); - Asms.visitInsn(mv, i); - mv.visitInsn(AALOAD); - final Class ct = constructorParameters[i].getValue(); - if (ct.isPrimitive()) { - final Class bigct = TypeToken.primitiveToWrapper(ct); - mv.visitTypeInsn(CHECKCAST, bigct.getName().replace('.', '/')); - try { - Method pm = bigct.getMethod(ct.getSimpleName() + "Value"); - mv.visitMethodInsn( - INVOKEVIRTUAL, - bigct.getName().replace('.', '/'), - pm.getName(), - Type.getMethodDescriptor(pm), - false); - } catch (Exception ex) { - throw new RedkaleException(ex); // 不可能会发生 - } - } else { - mv.visitTypeInsn(CHECKCAST, ct.getName().replace('.', '/')); + break; + } + mv.visitVarInsn(ALOAD, 1); + Asms.visitInsn(mv, i); + mv.visitInsn(AALOAD); + final Class ct = constructorParameters[i].getValue(); + if (ct.isPrimitive()) { + final Class bigct = TypeToken.primitiveToWrapper(ct); + mv.visitTypeInsn(CHECKCAST, bigct.getName().replace('.', '/')); + try { + Method pm = bigct.getMethod(ct.getSimpleName() + "Value"); + mv.visitMethodInsn( + INVOKEVIRTUAL, + bigct.getName().replace('.', '/'), + pm.getName(), + Type.getMethodDescriptor(pm), + false); + } catch (Exception ex) { + throw new RedkaleException(ex); // 不可能会发生 } + } else { + mv.visitTypeInsn(CHECKCAST, ct.getName().replace('.', '/')); } } // --------------------------------------- mv.visitMethodInsn(INVOKESPECIAL, interName, "", Type.getConstructorDescriptor(constructor), false); mv.visitInsn(ARETURN); - mv.visitMaxs((constructorParameters.length > 0 ? (constructorParameters.length + 3) : 2), 2); + mv.visitMaxs(3, 2); mv.visitEnd(); } { // 虚拟 create 方法 @@ -555,52 +568,17 @@ public interface Creator { mv.visitEnd(); } cw.visitEnd(); - final byte[] bytes = cw.toByteArray(); - final boolean ispub = Modifier.isPublic(constructor.getModifiers()); - Class resultClazz = null; - if (loader instanceof URLClassLoader && !ispub) { - try { - final URLClassLoader urlLoader = (URLClassLoader) loader; - final URL url = new URL( - "memclass", "localhost", -1, "/" + newDynName.replace('/', '.') + "/", new URLStreamHandler() { - @Override - protected URLConnection openConnection(URL u) throws IOException { - return new URLConnection(u) { - @Override - public void connect() throws IOException { - // do nothing - } - @Override - public InputStream getInputStream() throws IOException { - return new ByteArrayInputStream(bytes); - } - }; - } - }); - Method addURLMethod = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); - addURLMethod.setAccessible(true); - addURLMethod.invoke(urlLoader, url); - resultClazz = urlLoader.loadClass(newDynName.replace('/', '.')); - } catch (Throwable t) { // 异常无需理会, 使用下一种loader方式 - t.printStackTrace(); - } - } - if (!ispub && resultClazz == null) { - throw new RedkaleException( - "[" + clazz + "] have no public or ConstructorParameters-Annotation constructor."); - } + byte[] bytes = cw.toByteArray(); try { - if (resultClazz == null) { - resultClazz = new ClassLoader(loader) { - public final Class loadClass(String name, byte[] b) { - return defineClass(name, b, 0, b.length); - } - }.loadClass(newDynName.replace('/', '.'), bytes); - } - RedkaleClassLoader.putDynClass(newDynName.replace('/', '.'), bytes, resultClazz); - RedkaleClassLoader.putReflectionDeclaredConstructors(resultClazz, newDynName.replace('/', '.')); - return (Creator) resultClazz.getDeclaredConstructor().newInstance(); + Class newClazz = new ClassLoader(loader) { + public final Class loadClass(String name, byte[] b) { + return defineClass(name, b, 0, b.length); + } + }.loadClass(newDynName.replace('/', '.'), bytes); + RedkaleClassLoader.putDynClass(newDynName.replace('/', '.'), bytes, newClazz); + RedkaleClassLoader.putReflectionDeclaredConstructors(newClazz, newDynName.replace('/', '.')); + return (Creator) newClazz.getDeclaredConstructor().newInstance(); } catch (Exception ex) { throw new RedkaleException(ex); } diff --git a/src/test/java/org/redkale/test/sncp/SncpSleepTest.java b/src/test/java/org/redkale/test/sncp/SncpSleepTest.java index c07a2147f..0ea1a2400 100644 --- a/src/test/java/org/redkale/test/sncp/SncpSleepTest.java +++ b/src/test/java/org/redkale/test/sncp/SncpSleepTest.java @@ -29,6 +29,7 @@ public class SncpSleepTest { @Test public void run() throws Exception { + Creator.create(List.class); System.out.println("------------------- 并发调用 -----------------------------------"); final Application application = Application.create(true); final ExecutorService workExecutor = WorkThread.createWorkExecutor(10, "Thread-Work-%s"); diff --git a/src/test/java/org/redkale/test/sncp/dyn/DynActionTestService_change.java b/src/test/java/org/redkale/test/sncp/dyn/DynActionTestService_change.java index 11c051744..f9437d382 100644 --- a/src/test/java/org/redkale/test/sncp/dyn/DynActionTestService_change.java +++ b/src/test/java/org/redkale/test/sncp/dyn/DynActionTestService_change.java @@ -36,17 +36,17 @@ public class DynActionTestService_change extends SncpActionServlet { public void action(SncpRequest request, SncpResponse response) throws Throwable { Convert convert = request.getConvert(); Reader in = request.getReader(); - DynActionTestService_change_paramBean bean = convert.convertFrom(paramComposeBeanType, in); + DynSncpActionParamBean_TestService_change bean = convert.convertFrom(paramComposeBeanType, in); TestService serviceObj = (TestService) service(); Object rs = serviceObj.change(bean.arg1, bean.arg2, bean.arg3); response.finish(boolean.class, rs); } - public static class DynActionTestService_change_paramBean { + public static class DynSncpActionParamBean_TestService_change { - public DynActionTestService_change_paramBean() {} + public DynSncpActionParamBean_TestService_change() {} - public DynActionTestService_change_paramBean(Object[] params) { + public DynSncpActionParamBean_TestService_change(Object[] params) { this.arg1 = (TestBean) params[0]; this.arg2 = (String) params[1]; this.arg3 = (int) params[2]; diff --git a/src/test/java/org/redkale/test/sncp/dyn/DynActionTestService_changeName.java b/src/test/java/org/redkale/test/sncp/dyn/DynActionTestService_changeName.java index 4b1f0a93a..70a61e55b 100644 --- a/src/test/java/org/redkale/test/sncp/dyn/DynActionTestService_changeName.java +++ b/src/test/java/org/redkale/test/sncp/dyn/DynActionTestService_changeName.java @@ -37,17 +37,17 @@ public class DynActionTestService_changeName extends SncpActionServlet { public void action(SncpRequest request, SncpResponse response) throws Throwable { Convert convert = request.getConvert(); Reader in = request.getReader(); - DynActionTestService_changeName_paramBean bean = convert.convertFrom(paramComposeBeanType, in); + DynSncpActionParamBean_TestService_changeName bean = convert.convertFrom(paramComposeBeanType, in); TestService serviceObj = (TestService) service(); CompletableFuture future = serviceObj.changeName(bean.arg1, bean.arg2, bean.arg3); - response.finishFuture(paramHandlerResultType, future); + response.finishFuture(paramHandlerType, future); } - public static class DynActionTestService_changeName_paramBean { + public static class DynSncpActionParamBean_TestService_changeName { - public DynActionTestService_changeName_paramBean() {} + public DynSncpActionParamBean_TestService_changeName() {} - public DynActionTestService_changeName_paramBean(Object[] params) { + public DynSncpActionParamBean_TestService_changeName(Object[] params) { this.arg1 = (TestBean) params[0]; this.arg2 = (String) params[1]; this.arg3 = (int) params[2]; diff --git a/src/test/java/org/redkale/test/sncp/dyn/DynActionTestService_insert.java b/src/test/java/org/redkale/test/sncp/dyn/DynActionTestService_insert.java index 25fd85ac6..ebd222c8e 100644 --- a/src/test/java/org/redkale/test/sncp/dyn/DynActionTestService_insert.java +++ b/src/test/java/org/redkale/test/sncp/dyn/DynActionTestService_insert.java @@ -36,18 +36,18 @@ public class DynActionTestService_insert extends SncpActionServlet { public void action(SncpRequest request, SncpResponse response) throws Throwable { Convert convert = request.getConvert(); Reader in = request.getReader(); - DynActionTestService_insert_paramBean bean = convert.convertFrom(paramComposeBeanType, in); + DynSncpActionParamBean_TestService_insert bean = convert.convertFrom(paramComposeBeanType, in); bean.arg0 = response.getParamAsyncHandler(); TestService serviceObj = (TestService) service(); serviceObj.insert(bean.arg0, bean.arg1, bean.arg2, bean.arg3); response.finishVoid(); } - public static class DynActionTestService_insert_paramBean { + public static class DynSncpActionParamBean_TestService_insert { - public DynActionTestService_insert_paramBean() {} + public DynSncpActionParamBean_TestService_insert() {} - public DynActionTestService_insert_paramBean(Object[] params) { + public DynSncpActionParamBean_TestService_insert(Object[] params) { this.arg0 = (BooleanHandler) params[0]; this.arg1 = (TestBean) params[1]; this.arg2 = (String) params[2]; diff --git a/src/test/java/org/redkale/test/sncp/dyn/DynActionTestService_update.java b/src/test/java/org/redkale/test/sncp/dyn/DynActionTestService_update.java index 0f927caa8..4290ce218 100644 --- a/src/test/java/org/redkale/test/sncp/dyn/DynActionTestService_update.java +++ b/src/test/java/org/redkale/test/sncp/dyn/DynActionTestService_update.java @@ -37,18 +37,18 @@ public class DynActionTestService_update extends SncpActionServlet { public void action(SncpRequest request, SncpResponse response) throws Throwable { Convert convert = request.getConvert(); Reader in = request.getReader(); - DynActionTestService_update_paramBean bean = convert.convertFrom(paramComposeBeanType, in); + DynSncpActionParamBean_TestService_update bean = convert.convertFrom(paramComposeBeanType, in); bean.arg3 = response.getParamAsyncHandler(); TestService serviceObj = (TestService) service(); serviceObj.update(bean.arg1, bean.arg2, bean.arg3, bean.arg4, bean.arg5, bean.arg6); response.finishVoid(); } - public static class DynActionTestService_update_paramBean { + public static class DynSncpActionParamBean_TestService_update { - public DynActionTestService_update_paramBean() {} + public DynSncpActionParamBean_TestService_update() {} - public DynActionTestService_update_paramBean(Object[] params) { + public DynSncpActionParamBean_TestService_update(Object[] params) { this.arg1 = (long) params[0]; this.arg2 = (short) params[1]; this.arg3 = (CompletionHandler) params[2]; diff --git a/src/test/java/org/redkale/test/util/CreatorRecord.java b/src/test/java/org/redkale/test/util/CreatorRecord.java index 40328eb2f..94c9828d3 100644 --- a/src/test/java/org/redkale/test/util/CreatorRecord.java +++ b/src/test/java/org/redkale/test/util/CreatorRecord.java @@ -31,6 +31,10 @@ public class CreatorRecord { public CreatorRecord() {} + public CreatorRecord(Object[] arg) { + System.out.println("creat CreatorRecord!"); + } + @ConstructorParameters({"id", "name", "lval", "tval", "bval", "sval", "cval", "fval", "dval"}) public CreatorRecord( int id, String name, long lval, boolean tval, byte bval, short sval, char cval, float fval, double dval) { diff --git a/src/test/java/org/redkale/test/util/CreatorTest.java b/src/test/java/org/redkale/test/util/CreatorTest.java index 60b90f7c7..5e4d482bd 100644 --- a/src/test/java/org/redkale/test/util/CreatorTest.java +++ b/src/test/java/org/redkale/test/util/CreatorTest.java @@ -19,6 +19,7 @@ public class CreatorTest { public static void main(String[] args) throws Throwable { CreatorTest test = new CreatorTest(); test.run1(); + test.run2(); } @Test @@ -35,4 +36,10 @@ public class CreatorTest { Assertions.assertEquals("ss", record.getName()); Assertions.assertEquals(json, json2); } + + @Test + public void run2() throws Throwable { + Creator creator = Creator.create(CreatorRecord.class, 1); + creator.create(null, null); + } }