diff --git a/src/main/java/org/redkale/convert/ObjectEncoder.java b/src/main/java/org/redkale/convert/ObjectEncoder.java index cba9a15fd..4f05ccd9c 100644 --- a/src/main/java/org/redkale/convert/ObjectEncoder.java +++ b/src/main/java/org/redkale/convert/ObjectEncoder.java @@ -184,7 +184,8 @@ public class ObjectEncoder implements Encodeable { if (small != null && method.getReturnType() == String.class) { fieldCoder = StringSimpledCoder.SmallStringSimpledCoder.instance; } else { - fieldCoder = colFactory.findFieldCoder(clazz, ConvertFactory.readGetSetFieldName(method)); + String fieldName = ConvertFactory.readGetSetFieldName(method); + fieldCoder = colFactory.findFieldCoder(clazz, fieldName); } if (fieldCoder == null) { Type t = TypeToken.createClassType( @@ -279,8 +280,9 @@ public class ObjectEncoder implements Encodeable { if (member.index > 0) { member.position = member.index; } else { - while (pos.contains(++pidx)) - ; + while (pos.contains(++pidx)) { + // do nothing + } member.position = pidx; } initForEachEnMember(factory, member); @@ -288,7 +290,6 @@ public class ObjectEncoder implements Encodeable { this.members = list.toArray(new EnMember[list.size()]); Arrays.sort(this.members, (a, b) -> a.compareTo(factory.isFieldSort(), b)); - afterInitEnMember(factory); } catch (Exception ex) { throw new ConvertException("ObjectEncoder init type=" + this.type + " error", ex); diff --git a/src/main/java/org/redkale/convert/json/JsonDynEncoder.java b/src/main/java/org/redkale/convert/json/JsonDynEncoder.java index 707eaa0b5..95e4db731 100644 --- a/src/main/java/org/redkale/convert/json/JsonDynEncoder.java +++ b/src/main/java/org/redkale/convert/json/JsonDynEncoder.java @@ -209,6 +209,9 @@ public abstract class JsonDynEncoder implements Encodeable { if (ref != null && ref.ignore()) { continue; } + if (factory.findFieldCoder(clazz, field.getName()) != null) { + return null; + } if (!(checkMemberType(factory, clazz, field.getGenericType(), field.getType()))) { return null; } @@ -267,6 +270,9 @@ public abstract class JsonDynEncoder implements Encodeable { if (names.contains(name)) { continue; } + if (factory.findFieldCoder(clazz, name) != null) { + return null; + } names.add(name); if (members == null) { members = new ArrayList<>(); diff --git a/src/main/java/org/redkale/net/http/Rest.java b/src/main/java/org/redkale/net/http/Rest.java index a92221c3a..d239c969c 100644 --- a/src/main/java/org/redkale/net/http/Rest.java +++ b/src/main/java/org/redkale/net/http/Rest.java @@ -125,29 +125,30 @@ public final class Rest { Set reloadTypes = new HashSet<>(); if (coders != null) { for (RestConvertCoder rcc : coders) { + Creator creator = Creator.create(rcc.coder()); + childFactory.register(rcc.type(), rcc.field(), creator.create()); reloadTypes.add(rcc.type()); - childFactory.register(rcc.type(), rcc.field(), (SimpledCoder) - Creator.create(rcc.coder()).create()); } } if (converts != null) { for (RestConvert rc : converts) { if (rc.type() == void.class || rc.type() == Void.class) { - return JsonFactory.create().skipAllIgnore(true); + childFactory.skipAllIgnore(true); + break; } if (types.contains(rc.type())) { throw new RestException("@RestConvert type(" + rc.type() + ") repeat"); } if (rc.skipIgnore()) { childFactory.registerSkipIgnore(rc.type()); - childFactory.reloadCoder(rc.type()); + reloadTypes.add(rc.type()); } else if (rc.onlyColumns().length > 0) { childFactory.registerIgnoreAll(rc.type(), rc.onlyColumns()); - childFactory.reloadCoder(rc.type()); + reloadTypes.add(rc.type()); } else { childFactory.register(rc.type(), false, rc.convertColumns()); childFactory.register(rc.type(), true, rc.ignoreColumns()); - childFactory.reloadCoder(rc.type()); + reloadTypes.add(rc.type()); } types.add(rc.type()); if (rc.features() > -1) { @@ -1396,7 +1397,8 @@ public final class Rest { final Parameter[] params = method.getParameters(); final RestConvert[] rcs = method.getAnnotationsByType(RestConvert.class); final RestConvertCoder[] rcc = method.getAnnotationsByType(RestConvertCoder.class); - if ((rcs != null && rcs.length > 0) || (rcc != null && rcc.length > 0)) { + final boolean hasResConvert = Utility.isNotEmpty(rcs) || Utility.isNotEmpty(rcc); + if (hasResConvert) { restConverts.add(new Object[] {rcs, rcc}); } // 解析方法中的每个参数 @@ -1749,11 +1751,8 @@ public final class Rest { Field genField = newClazz.getDeclaredField(REST_CONVERT_FIELD_PREFIX + (i + 1)); genField.setAccessible(true); Object[] rc = restConverts.get(i); - - genField.set( - obj, - createJsonFactory((RestConvert[]) rc[0], (RestConvertCoder[]) rc[1]) - .getConvert()); + JsonFactory childFactory = createJsonFactory((RestConvert[]) rc[0], (RestConvertCoder[]) rc[1]); + genField.set(obj, childFactory.getConvert()); } Field typesfield = newClazz.getDeclaredField(REST_PARAMTYPES_FIELD_NAME); typesfield.setAccessible(true); @@ -1844,7 +1843,7 @@ public final class Rest { } catch (ClassNotFoundException e) { // do nothing } catch (Throwable e) { - //do nothing + // do nothing } // ------------------------------------------------------------------------------ final String defModuleName = getWebModuleNameLowerCase(serviceType); @@ -2117,7 +2116,8 @@ public final class Rest { final RestConvert[] rcs = method.getAnnotationsByType(RestConvert.class); final RestConvertCoder[] rcc = method.getAnnotationsByType(RestConvertCoder.class); - if ((rcs != null && rcs.length > 0) || (rcc != null && rcc.length > 0)) { + final boolean hasResConvert = Utility.isNotEmpty(rcs) || Utility.isNotEmpty(rcc); + if (hasResConvert) { restConverts.add(new Object[] {rcs, rcc}); } if (dynsimple && entry.rpcOnly) { // 需要读取http header @@ -3710,7 +3710,7 @@ public final class Rest { } else if (RetResult.class.isAssignableFrom(returnType)) { mv.visitVarInsn(ASTORE, maxLocals); mv.visitVarInsn(ALOAD, 2); // response - if (rcs != null && rcs.length > 0) { + if (hasResConvert) { mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn( GETFIELD, newDynName, REST_CONVERT_FIELD_PREFIX + restConverts.size(), convertDesc); @@ -3739,7 +3739,7 @@ public final class Rest { } else if (HttpResult.class.isAssignableFrom(returnType)) { mv.visitVarInsn(ASTORE, maxLocals); mv.visitVarInsn(ALOAD, 2); // response - if (rcs != null && rcs.length > 0) { + if (hasResConvert) { mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn( GETFIELD, newDynName, REST_CONVERT_FIELD_PREFIX + restConverts.size(), convertDesc); @@ -3768,7 +3768,7 @@ public final class Rest { } else if (HttpScope.class.isAssignableFrom(returnType)) { mv.visitVarInsn(ASTORE, maxLocals); mv.visitVarInsn(ALOAD, 2); // response - if (rcs != null && rcs.length > 0) { + if (hasResConvert) { mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn( GETFIELD, newDynName, REST_CONVERT_FIELD_PREFIX + restConverts.size(), convertDesc); @@ -3786,7 +3786,7 @@ public final class Rest { mv.visitVarInsn(ALOAD, 2); // response Class returnNoFutureType = TypeToken.typeToClassOrElse(returnGenericNoFutureType, Object.class); if (returnNoFutureType == HttpScope.class) { - if (rcs != null && rcs.length > 0) { + if (hasResConvert) { mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn( GETFIELD, newDynName, REST_CONVERT_FIELD_PREFIX + restConverts.size(), convertDesc); @@ -3809,7 +3809,7 @@ public final class Rest { && !((returnGenericNoFutureType instanceof Class) && (((Class) returnGenericNoFutureType).isPrimitive() || CharSequence.class.isAssignableFrom((Class) returnGenericNoFutureType)))) { - if (rcs != null && rcs.length > 0) { + if (hasResConvert) { mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn( GETFIELD, newDynName, REST_CONVERT_FIELD_PREFIX + restConverts.size(), convertDesc); @@ -3840,7 +3840,7 @@ public final class Rest { false); } } else { - if (rcs != null && rcs.length > 0) { + if (hasResConvert) { mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn( GETFIELD, newDynName, REST_CONVERT_FIELD_PREFIX + restConverts.size(), convertDesc); @@ -3876,7 +3876,7 @@ public final class Rest { } else if (Flows.maybePublisherClass(returnType)) { // Flow.Publisher mv.visitVarInsn(ASTORE, maxLocals); mv.visitVarInsn(ALOAD, 2); // response - if (rcs != null && rcs.length > 0) { + if (hasResConvert) { mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn( GETFIELD, newDynName, REST_CONVERT_FIELD_PREFIX + restConverts.size(), convertDesc); @@ -3909,7 +3909,7 @@ public final class Rest { } else if (returnType == retvalType) { // 普通JavaBean或JavaBean[] mv.visitVarInsn(ASTORE, maxLocals); mv.visitVarInsn(ALOAD, 2); // response - if (rcs != null && rcs.length > 0) { + if (hasResConvert) { mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn( GETFIELD, newDynName, REST_CONVERT_FIELD_PREFIX + restConverts.size(), convertDesc); @@ -3942,7 +3942,7 @@ public final class Rest { } else { mv.visitVarInsn(ASTORE, maxLocals); mv.visitVarInsn(ALOAD, 2); // response - if (rcs != null && rcs.length > 0) { + if (hasResConvert) { mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn( GETFIELD, newDynName, REST_CONVERT_FIELD_PREFIX + restConverts.size(), convertDesc); @@ -4239,10 +4239,8 @@ public final class Rest { Field genField = newClazz.getDeclaredField(REST_CONVERT_FIELD_PREFIX + (i + 1)); genField.setAccessible(true); Object[] rc = restConverts.get(i); - genField.set( - obj, - createJsonFactory((RestConvert[]) rc[0], (RestConvertCoder[]) rc[1]) - .getConvert()); + JsonFactory childFactory = createJsonFactory((RestConvert[]) rc[0], (RestConvertCoder[]) rc[1]); + genField.set(obj, childFactory.getConvert()); RedkaleClassLoader.putReflectionField(newDynName.replace('/', '.'), genField); } Field typesfield = newClazz.getDeclaredField(REST_PARAMTYPES_FIELD_NAME); diff --git a/src/main/java/org/redkale/net/http/RestConvert.java b/src/main/java/org/redkale/net/http/RestConvert.java index a1012b4b0..bda5c7a73 100644 --- a/src/main/java/org/redkale/net/http/RestConvert.java +++ b/src/main/java/org/redkale/net/http/RestConvert.java @@ -5,11 +5,10 @@ */ package org.redkale.net.http; +import java.lang.annotation.*; import static java.lang.annotation.ElementType.*; import static java.lang.annotation.RetentionPolicy.RUNTIME; -import java.lang.annotation.*; - /** * 只能依附在Service实现类的public方法上, 当方法的返回值以JSON输出时对指定类型的转换设定。
* 注意: 如果 type() == void.class 则无视其他参数固定返回 JsonFactory.create().skipAllIgnore(true).getConvert(); @@ -32,7 +31,8 @@ public @interface RestConvert { int features() default -1; /** - * 是否忽略ConvertColumn.ignore=true的设置, 优先级最高 + * 是否忽略ConvertColumn.ignore=true的设置, 优先级最高
+ * 值为true时会忽略onlyColumns、ignoreColumns、convertColumns的值 * * @return boolean */ @@ -46,7 +46,8 @@ public @interface RestConvert { Class type(); /** - * 仅显示的字段, 优先级其次,有值就会忽略ignoreColumns、convertColumns值 + * 仅显示的字段, 优先级低于skipIgnore
+ * 有值就会忽略ignoreColumns、convertColumns值 * * @since 2.7.0 * @return String[] diff --git a/src/test/java/org/redkale/test/http/HttpRequestDesc.java b/src/test/java/org/redkale/test/http/HttpRequestDesc.java index 4d56a1bfa..86e929f87 100644 --- a/src/test/java/org/redkale/test/http/HttpRequestDesc.java +++ b/src/test/java/org/redkale/test/http/HttpRequestDesc.java @@ -341,5 +341,5 @@ public interface HttpRequestDesc { public void setAttribute(String name, Object value); // 获取request创建时间 - public long getCreatetime(); + public long getCreateTime(); } diff --git a/src/test/java/org/redkale/test/http/RestConvertBean.java b/src/test/java/org/redkale/test/http/RestConvertBean.java new file mode 100644 index 000000000..64540532c --- /dev/null +++ b/src/test/java/org/redkale/test/http/RestConvertBean.java @@ -0,0 +1,59 @@ +/* + +*/ + +package org.redkale.test.http; + +import org.redkale.convert.json.JsonConvert; + +/** + * + * @author zhangjx + */ +public class RestConvertBean { + + private int id; + + private boolean enable; + + private String name; + + private RestConvertItem content; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public boolean isEnable() { + return enable; + } + + public void setEnable(boolean enable) { + this.enable = enable; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public RestConvertItem getContent() { + return content; + } + + public void setContent(RestConvertItem content) { + this.content = content; + } + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } +} diff --git a/src/test/java/org/redkale/test/http/RestConvertBoolCoder.java b/src/test/java/org/redkale/test/http/RestConvertBoolCoder.java new file mode 100644 index 000000000..c988927a6 --- /dev/null +++ b/src/test/java/org/redkale/test/http/RestConvertBoolCoder.java @@ -0,0 +1,28 @@ +/* + +*/ + +package org.redkale.test.http; + +import org.redkale.convert.Reader; +import org.redkale.convert.SimpledCoder; +import org.redkale.convert.Writer; + +/** + * + * @author zhangjx + * @param Reader + * @param Writer + */ +public class RestConvertBoolCoder extends SimpledCoder { + + @Override + public void convertTo(W out, Boolean value) { + out.writeInt(value == null || !value ? 0 : 1); + } + + @Override + public Boolean convertFrom(R in) { + return in.readInt() == 1; + } +} diff --git a/src/test/java/org/redkale/test/http/RestConvertItem.java b/src/test/java/org/redkale/test/http/RestConvertItem.java new file mode 100644 index 000000000..09b779afc --- /dev/null +++ b/src/test/java/org/redkale/test/http/RestConvertItem.java @@ -0,0 +1,41 @@ +/* + +*/ + +package org.redkale.test.http; + +import org.redkale.convert.ConvertColumn; +import org.redkale.convert.json.JsonConvert; + +/** + * + * @author zhangjx + */ +public class RestConvertItem { + + private long createTime; + + @ConvertColumn(ignore = true) + private String aesKey; + + public long getCreateTime() { + return createTime; + } + + public void setCreateTime(long createTime) { + this.createTime = createTime; + } + + public String getAesKey() { + return aesKey; + } + + public void setAesKey(String aesKey) { + this.aesKey = aesKey; + } + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } +} diff --git a/src/test/java/org/redkale/test/http/RestConvertService.java b/src/test/java/org/redkale/test/http/RestConvertService.java new file mode 100644 index 000000000..5506641ba --- /dev/null +++ b/src/test/java/org/redkale/test/http/RestConvertService.java @@ -0,0 +1,49 @@ +/* + +*/ + +package org.redkale.test.http; + +import org.redkale.net.http.RestConvert; +import org.redkale.net.http.RestConvertCoder; +import org.redkale.net.http.RestService; +import org.redkale.service.AbstractService; + +/** + * + * @author zhangjx + */ +@RestService(name = "test", autoMapping = true) +public class RestConvertService extends AbstractService { + + public RestConvertBean load1() { + return createBean(); + } + + @RestConvert(type = RestConvertItem.class, skipIgnore = true) + public RestConvertBean load2() { + return createBean(); + } + + @RestConvert(type = RestConvertBean.class, onlyColumns = "id") + public RestConvertBean load3() { + return createBean(); + } + + @RestConvertCoder(type = RestConvertBean.class, field = "enable", coder = RestConvertBoolCoder.class) + public RestConvertBean load4() { + return createBean(); + } + + private RestConvertBean createBean() { + RestConvertBean bean = new RestConvertBean(); + bean.setId(123); + bean.setName("haha"); + bean.setEnable(true); + RestConvertItem item = new RestConvertItem(); + item.setCreateTime(100); + item.setAesKey("keykey"); + bean.setContent(item); + return bean; + } +} diff --git a/src/test/java/org/redkale/test/http/RestConvertTest.java b/src/test/java/org/redkale/test/http/RestConvertTest.java new file mode 100644 index 000000000..3fb3dbbe1 --- /dev/null +++ b/src/test/java/org/redkale/test/http/RestConvertTest.java @@ -0,0 +1,129 @@ +/* + +*/ + +package org.redkale.test.http; + +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Method; +import java.net.InetSocketAddress; +import java.net.Socket; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.redkale.boot.Application; +import org.redkale.convert.bson.BsonConvert; +import org.redkale.convert.json.JsonConvert; +import org.redkale.inject.ResourceFactory; +import org.redkale.net.AsyncIOGroup; +import org.redkale.net.http.HttpServer; +import org.redkale.net.http.HttpServlet; +import org.redkale.net.sncp.Sncp; +import org.redkale.util.AnyValueWriter; + +/** + * + * @author zhangjx + */ +public class RestConvertTest { + + public static void main(String[] args) throws Throwable { + RestConvertTest test = new RestConvertTest(); + test.run(); + } + + @Test + public void run() throws Exception { + System.out.println("------------------- 并发调用 -----------------------------------"); + final Application application = Application.create(true); + final AsyncIOGroup asyncGroup = new AsyncIOGroup(8192, 16); + asyncGroup.start(); + final ResourceFactory resFactory = ResourceFactory.create(); + resFactory.register(JsonConvert.root()); + resFactory.register(BsonConvert.root()); + Method method = Application.class.getDeclaredMethod("initWorkExecutor"); + method.setAccessible(true); + method.invoke(application); + + // ------------------------ 初始化 CService ------------------------------------ + RestConvertService service = Sncp.createSimpleLocalService(RestConvertService.class, resFactory); + HttpServer server = new HttpServer(application, System.currentTimeMillis(), resFactory); + server.getResourceFactory().register(application); + System.out.println("servlet = " + server.addRestServlet(null, service, null, HttpServlet.class, "")); + server.init(AnyValueWriter.create("port", 0)); + server.start(); + + int port = server.getSocketAddress().getPort(); + System.out.println("服务器启动端口: " + port); + InetSocketAddress httpAddress = new InetSocketAddress("127.0.0.1", port); + Socket socket = new Socket(httpAddress.getAddress(), port); + OutputStream out = socket.getOutputStream(); + byte[] bytes = new byte[8192]; + long s, e; + { + out.write(("GET /test/load1 HTTP/1.1\r\nConnection: Keep-Alive\r\nContent-Length: 0\r\n\r\n").getBytes()); + InputStream in = socket.getInputStream(); + s = System.currentTimeMillis(); + int pos = in.read(bytes); + e = System.currentTimeMillis() - s; + String rs = new String(bytes, 0, pos); + System.out.println("返回字节: " + rs); + System.out.println("耗时: " + e + " ms"); + String json = rs.substring(rs.lastIndexOf('\n') + 1); + System.out.println("返回的json结果: " + json); + Assertions.assertEquals( + "{\"content\":{\"createTime\":100},\"enable\":true,\"id\":123,\"name\":\"haha\"}", json); + System.out.println("------------------1--------------------"); + Assertions.assertTrue(e < 100); + } + { + out.write(("GET /test/load2 HTTP/1.1\r\nConnection: Keep-Alive\r\nContent-Length: 0\r\n\r\n").getBytes()); + InputStream in = socket.getInputStream(); + s = System.currentTimeMillis(); + int pos = in.read(bytes); + e = System.currentTimeMillis() - s; + String rs = new String(bytes, 0, pos); + System.out.println("返回字节: " + rs); + System.out.println("耗时: " + e + " ms"); + String json = rs.substring(rs.lastIndexOf('\n') + 1); + System.out.println("返回的json结果: " + json); + Assertions.assertEquals( + "{\"content\":{\"aesKey\":\"keykey\",\"createTime\":100},\"enable\":true,\"id\":123,\"name\":\"haha\"}", + json); + System.out.println("------------------2--------------------"); + Assertions.assertTrue(e < 100); + } + { + out.write(("GET /test/load3 HTTP/1.1\r\nConnection: Keep-Alive\r\nContent-Length: 0\r\n\r\n").getBytes()); + InputStream in = socket.getInputStream(); + s = System.currentTimeMillis(); + int pos = in.read(bytes); + e = System.currentTimeMillis() - s; + String rs = new String(bytes, 0, pos); + System.out.println("返回字节: " + rs); + System.out.println("耗时: " + e + " ms"); + String json = rs.substring(rs.lastIndexOf('\n') + 1); + System.out.println("返回的json结果: " + json); + Assertions.assertEquals("{\"id\":123}", json); + System.out.println("-----------------3---------------------"); + Assertions.assertTrue(e < 100); + } + { + out.write(("GET /test/load4 HTTP/1.1\r\nConnection: Keep-Alive\r\nContent-Length: 0\r\n\r\n").getBytes()); + InputStream in = socket.getInputStream(); + s = System.currentTimeMillis(); + int pos = in.read(bytes); + e = System.currentTimeMillis() - s; + String rs = new String(bytes, 0, pos); + System.out.println("返回字节: " + rs); + System.out.println("耗时: " + e + " ms"); + String json = rs.substring(rs.lastIndexOf('\n') + 1); + System.out.println("返回的json结果: " + json); + Assertions.assertEquals( + "{\"content\":{\"createTime\":100},\"enable\":1,\"id\":123,\"name\":\"haha\"}", json); + System.out.println("-----------------4---------------------"); + Assertions.assertTrue(e < 100); + } + server.shutdown(); + } +}