diff --git a/README.md b/README.md index f841b2a2b..331160d71 100644 --- a/README.md +++ b/README.md @@ -6,17 +6,17 @@   Redkale (中文名: 红菜苔,一种湖北特产蔬菜) 是基于Java 11全新的微服务框架, 包含HTTP、WebSocket、TCP/UDP、数据序列化、数据缓存、依赖注入等功能。 本框架致力于简化集中式和微服务架构的开发,在增强开发敏捷性的同时保持高性能。 主要特点:: -* 大量使用Java 8+新特性(接口默认值、Stream、Lambda、内置的ASM、HttpClient等) +* 提供了动态依赖注入和动态字节码生成类功能 * 提供HTTP服务,同时内置JSON功能与限时缓存功能 * TCP层完全使用NIO,并统一TCP与UDP的接口换 * 提供分布式与集中式部署的无缝切换 * 提供类似JPA功能,包含数据缓存自动同步、分表分库与简洁的数据层操作接口 -* 可以动态修改已依赖注入的资源 +* 提供了MQ消息消费与生产简洁化API ## 开发文档 * [快速入门](docs/quick-start.md) * [Service组件](docs/service.md) -* [Json序列化](docs/convert-json.md) +* [序列化](docs/convert.md) * [DB数据源组件](docs/datasource.md) * [Cache数据源组件](docs/cachesource.md) * [WebSocket](docs/websocket.md) @@ -28,7 +28,7 @@ * [FAQ](docs/faq.md) ## 设计理念 -  作为一个全新的微服务框架,Redkale在接口定义上使用了Java 8以上版本的大量新特性,接口有默认实现、接口带静态方法、重复注解等特性,同时在设计上与主流框架有很大不同。Redkale是按组件形式设计的,而非以容器为主,几乎每个子包都是能提供独立功能的组件。如Tomcat是按容器设计的,所有web资源/配置由Tomcat控制,开发者很能难控制到Tomcat内部,而Redkale的HTTP服务只是个组件,开发者既可以自己启动和配置HttpServer,也可以把Redkale当成容器通过Redkale进程来初始化服务。Spring的Ioc容器也是如此,Redkale提供的依赖注入仅通过ResouceFactory一个类来控制,非常轻量,并且可动态更改已注入的资源。Spring提倡控制反转思想,而自身的容器却让开发者很难控制。Redkale是一个既能以组件形式也能以容器形式存在的框架。从整体上看,Redkale的架构分两层:接口和默认实现。开发者若想替换掉Redkale内置的HTTP服务而使用符合JavaEE规范的HttpServlet, 可以采用自定义协议基于JSR 340(Servlet 3.1)来实现自己的HTTP服务;若想使用Hibernate作为数据库操作,可以写一个自己的DataSource实现类;JSON的序列化和反序列化也可以使用第三方的实现;Memcached或Redis也可以作为另一个CacheSource的实现替换Redkale的默认实现。这其实包含了控制反转的思想,让框架里的各个组件均可让开发者控制。 +  作为一个全新的微服务框架,Redkale在接口定义上使用了默认实现、接口带静态方法、重复注解等特性,同时在设计上与主流框架有很大不同。Redkale是按组件形式设计的,而非以容器为主,几乎每个子包都是能提供独立功能的组件。如Tomcat是按容器设计的,所有web资源/配置由Tomcat控制,开发者很能难控制到Tomcat内部,而Redkale的HTTP服务只是个组件,开发者既可以自己启动和配置HttpServer,也可以把Redkale当成容器通过Redkale进程来初始化服务。Spring的Ioc容器也是如此,Redkale提供的依赖注入仅通过ResouceFactory一个类来控制,非常轻量,并且可动态更改已注入的资源。Spring提倡控制反转思想,而自身的容器却让开发者很难控制。Redkale是一个既能以组件形式也能以容器形式存在的框架。从整体上看,Redkale的架构分两层:接口和默认实现。开发者若想替换掉Redkale内置的HTTP服务而使用符合JavaEE规范的HttpServlet, 可以采用自定义协议基于JSR 340(Servlet 3.1)来实现自己的HTTP服务;若想使用Hibernate作为数据库操作,可以写一个自己的DataSource实现类;JSON的序列化和反序列化也可以使用第三方的实现;Memcached或Redis也可以作为另一个CacheSource的实现替换Redkale的默认实现。这其实包含了控制反转的思想,让框架里的各个组件均可让开发者控制。   与主流框架比,功能上Redkale显得很简单,这体现了Redkale的简易性,而并非是不足,从一个良好的设计习惯或架构上来看,有些常用功能是不需要提供的,如Redkale的HTTP服务不支持JSP, JSP其实算是一个落后的技术,现在是一个多样化终端的时代,终端不只局限于桌面程序和PC浏览器,还有原生App、混合式App、微信端、移动H5、提供第三方接口等各种形式的终端,这些都不是JSP能方便兼顾的,而HTTP+JSON作为通用性接口可以避免重复开发,模版引擎的功能加上各种强大的JS框架足以取代JSP。Redkale在功能上做了筛选,不会为了迎合主流而提供,而是以良好的设计思想为指导。这是Redkale的主导思维。    详情请访问:  https://redkale.org diff --git a/docs/convert-json.md b/docs/convert-json.md deleted file mode 100644 index f9443a3b7..000000000 --- a/docs/convert-json.md +++ /dev/null @@ -1,2 +0,0 @@ -# Json序列化 -文档完善中…… \ No newline at end of file diff --git a/docs/convert-protobuf.md b/docs/convert-protobuf.md deleted file mode 100644 index f1c8470a4..000000000 --- a/docs/convert-protobuf.md +++ /dev/null @@ -1 +0,0 @@ -文档完善中…… \ No newline at end of file diff --git a/docs/convert.md b/docs/convert.md index f9443a3b7..d954895f6 100644 --- a/docs/convert.md +++ b/docs/convert.md @@ -1,2 +1,395 @@ -# Json序列化 -文档完善中…… \ No newline at end of file +# 序列化 +  Convert提供Java对象的序列化与反序列化功能。支持JSON(JavaScript Object Notation)、BSON(Binary Stream Object Notation)、PROTOBUF三种格式化。 三种格式使用方式完全一样,其性能都大幅度超过其他JSON框架。同时JSON内置于HTTP服务中,BSON也是SNCP协议数据序列化的基础。 +## 基本API +  JSON序列化其操作类主要是JsonConvert,配置类主要是JsonFactory、ConvertColumn。JsonFactory采用同ClassLoader类似的双亲委托方式设计。 + JsonConvert 序列化encode方法: +```java + public String convertTo(Object value); + + public String convertTo(Type type, Object value); + + public void convertTo(OutputStream out, Object value); + + public void convertTo(OutputStream out, Type type, Object value); + + public ByteBuffer[] convertTo(Supplier supplier, Object value); + + public ByteBuffer[] convertTo(Supplier supplier, Type type, Object value); + + public void convertTo(JsonWriter writer, Object value); + + public void convertTo(JsonWriter writer, Type type, Object value); +``` +JsonConvert 反序列化decode方法: +```java + public T convertFrom(Type type, String text); + + public T convertFrom(Type type, char[] text); + + public T convertFrom(Type type, char[] text, int start, int len); + + public T convertFrom(Type type, InputStream in); + + public T convertFrom(Type type, ByteBuffer... buffers); + + public T convertFrom(Type type, JsonReader reader); +``` +## Json基本用法 +```java + @Data + public class UserRecord { + + private int userid; + + private String username = ""; + + private String password = ""; + + public UserRecord() { + } + } + + public static void main(String[] args) throws Exception { + UserRecord user = new UserRecord(); + user.setUserid(100); + user.setUsername("redkalename"); + user.setPassword("123456"); + final JsonConvert convert = JsonConvert.root(); + String json = convert.convertTo(user); + System.out.println(json); //应该是 {"password":"123456","userid":100,"username":"redkalename"} + UserRecord user2 = convert.convertFrom(UserRecord.class, json); + System.out.println(convert.convertTo(user2)); //应该也是 {"password":"123456","userid":100,"username":"redkalename"} + + /** + * 以下功能是为了屏蔽password字段。 + * 等价于 public String getPassword() 加上 @ConvertColumn : + * + * @ConvertColumn(ignore = true, type = ConvertType.JSON) + * public String getPassword() { + * return password; + * } + **/ + final JsonFactory childFactory = JsonFactory.root().createChild(); + childFactory.register(UserRecord.class, true, "password"); //屏蔽掉password字段使其不输出 + childFactory.reloadCoder(UserRecord.class); //重新加载Coder使之覆盖父Factory的配置 + final JsonConvert childConvert = childFactory.getConvert(); + json = childConvert.convertTo(user); + System.out.println(json); //应该是 {"userid":100,"username":"redkalename"} + user2 = childConvert.convertFrom(UserRecord.class, json); + System.out.println(childConvert.convertTo(user2)); //应该也是 {"userid":100,"username":"redkalename"} + } +``` +  在Redkale里存在默认的JsonConvert、BsonConvert、ProtobufConvert对象。 只需在所有Service、Servlet中增加依赖注入资源。 +```java +public class XXXService implements Service { + + @Resource + private JsonConvert jsonConvert; + + @Resource + private BsonConvert bsonConvert; + + @Resource + private ProtobufConvert protobufConvert; +} + +public class XXXServlet extends HttpServlet { + + @Resource + private JsonConvert jsonConvert; + + @Resource + private BsonConvert bsonConvert; + + @Resource + private ProtobufConvert protobufConvert; + +} +``` +   同一类型数据通过设置新的JsonFactory可以有不同的输出。 +```java +public class UserSimpleInfo { + + private int userid; + + private String username = ""; + + @ConvertColumn(ignore = true, type = ConvertType.JSON) + private long regtime; //注册时间 + + @ConvertColumn(ignore = true, type = ConvertType.JSON) + private String regaddr = ""; //注册IP + + /** 以下省略getter setter方法 */ +} + + + +public class UserInfoServlet extends HttpBaseServlet { + + @Resource + private UserSerice service; + + @Resource + private JsonFactory jsonRootFactory; + + @Resource + private JsonConvert detailConvert; + + @Override + public void init(HttpContext context, AnyValue config) { + final JsonFactory childFactory = jsonRootFactory.createChild(); + childFactory.register(UserSimpleInfo.class, false, "regtime", "regaddr"); //允许输出注册时间与注册地址 + childFactory.reloadCoder(UserSimpleInfo.class); //重新加载Coder使之覆盖父Factory的配置 + this.detailConvert = childFactory.getConvert(); + } + + // 获取他人基本信息 + @HttpMapping(url = "/user/info/") + public void info(HttpRequest req, HttpResponse resp) throws IOException { + int userid = Integer.parseInt(req.getRequstURILastPath()); + UserSimpleInfo user = service.findUserInfo(userid); + resp.finishJson(user); // 不包含用户的注册时间和注册地址字段信息 + } + + //获取用户自己的信息 + @HttpMapping(url = "/user/myinfo") + public void mydetail(HttpRequest req, HttpResponse resp) throws IOException { + int userid = req.currentUser().getUserid(); //获取当前用户ID + UserSimpleInfo user = service.findUserInfo(userid); + resp.finishJson(detailConvert, user); // 包含用户的注册时间和注册地址字段信息 + } +} +``` + +  Convert 支持带参数构造函数。 + 1. public 带参数的构造函数加上 @ConstructorParameters 注解: +```java +public class UserRecord { + + private int userid; + + private String username = ""; + + private String password = ""; + + @ConstructorParameters({"userid", "username", "password"}) + public UserRecord(int userid, String username, String password) { + this.userid = userid; + this.username = username; + this.password = password; + } + + public int getUserid() { + return userid; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } +} +``` + 2. 自定义Creator: +```java +public class UserRecord { + + private int userid; + + private String username = ""; + + private String password = ""; + + UserRecord(int userid, String username, String password) { + this.userid = userid; + this.username = username; + this.password = password; + } + + public int getUserid() { + return userid; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + /** + * 自定义Creator方法。 + * 1) 方法名可以随意。 + * 2) 方法必须是static。 + * 3)方法的参数必须为空。 + * 4)方法的返回类型必须是Creator。 + * + * @return + */ + private static Creator creator() { + return new Creator() { + @Override + @ConstructorParameters({"userid", "username", "password"}) //带参数的构造函数必须有ConstructorParameters注解 + public UserRecord create(Object... params) { + return new UserRecord((params[0] == null ? 0 : (Integer) params[0]), (String) params[1], (String) params[2]); + } + }; + } +} + +``` +  通常JavaBean类默认有个public空参数的构造函数,因此大部分情况下不要自定义Creator,其实只要不是private的空参数构造函数Convert都能自动支持(其他的框架都仅支持public的构造函数),只有仅含private的构造函数才需要自定义Creator。带参数的构造函数就需要标记@ConstructorParameters,常见于Immutable Object。 + +## 自定义 +  Convert 支持自定义Decode、Encode。 + 1. 通过ConvertFactory显式的注册: +```java +public class FileSimpleCoder extends SimpledCoder { + + public static final FileSimpleCoder instance = new FileSimpleCoder(); + + @Override + public void convertTo(W out, File value) { + out.writeString(value == null ? null : value.getPath()); + } + + @Override + public File convertFrom(R in) { + String path = in.readString(); + return path == null ? null : new File(path); + } +} + +JsonFactory.root().register(File.class, FileSimpleCoder.instance); + +BsonFactory.root().register(File.class, FileSimpleCoder.instance); +``` + 2. 通过JavaBean类自定义静态方法自动加载: +```java +public class InnerCoderEntity { + + private final String val; + + private final int id; + + private InnerCoderEntity(int id, String value) { + this.id = id; + this.val = value; + } + + public static InnerCoderEntity create(int id, String value) { + return new InnerCoderEntity(id, value); + } + + /** + * 该方法提供给Convert组件自动加载。 + * 1) 方法名可以随意。 + * 2) 方法必须是static + * 3)方法的参数有且只能有一个, 且必须是org.redkale.convert.ConvertFactory或子类。 + * —3.1) 参数类型为org.redkale.convert.ConvertFactory 表示适合JSON和BSON。 + * —3.2) 参数类型为org.redkale.convert.json.JsonFactory 表示仅适合JSON。 + * —3.3) 参数类型为org.redkale.convert.bson.BsonFactory 表示仅适合BSON。 + * 4)方法的返回类型必须是org.redkale.convert.Decodeable/org.redkale.convert.Encodeable/org.redkale.convert.SimpledCoder + * 若返回类型不是org.redkale.convert.SimpledCoder, 就必须提供两个方法: 一个返回Decodeable 一个返回 Encodeable。 + * + * @param factory + * @return + */ + private static SimpledCoder createConvertCoder(final org.redkale.convert.ConvertFactory factory) { + return new SimpledCoder() { + + //必须与EnMember[] 顺序一致 + private final DeMember[] deMembers = new DeMember[]{ + DeMember.create(factory, InnerCoderEntity.class, "id"), + DeMember.create(factory, InnerCoderEntity.class, "val")}; + + //必须与DeMember[] 顺序一致 + private final EnMember[] enMembers = new EnMember[]{ + EnMember.create(factory, InnerCoderEntity.class, "id"), + EnMember.create(factory, InnerCoderEntity.class, "val")}; + + @Override + public void convertTo(Writer out, InnerCoderEntity value) { + if (value == null) { + out.writeObjectNull(InnerCoderEntity.class); + return; + } + out.writeObjectB(value); + for (EnMember member : enMembers) { + out.writeObjectField(member, value); + } + out.writeObjectE(value); + } + + @Override + public InnerCoderEntity convertFrom(Reader in) { + if (in.readObjectB(InnerCoderEntity.class) == null) return null; + int index = 0; + final Object[] params = new Object[deMembers.length]; + while (in.hasNext()) { + DeMember member = in.readFieldName(deMembers); //读取字段名 + in.readBlank(); //读取字段名与字段值之间的间隔符,JSON则是跳过冒号: + if (member == null) { + in.skipValue(); //跳过不存在的字段的值, 一般不会发生 + } else { + params[index++] = member.read(in); + } + } + in.readObjectE(InnerCoderEntity.class); + return InnerCoderEntity.create(params[0] == null ? 0 : (Integer) params[0], (String) params[1]); + } + }; + } + + public int getId() { + return id; + } + + public String getVal() { + return val; + } + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } +} +``` + +## Bson数据格式 +  BSON类似Java自带的Serializable, 其格式如下: + +    1). 基本数据类型: 直接转换成byte[] + +    2). SmallString(无特殊字符且长度小于256的字符串): length(1 byte) + byte[](utf8); 通常用于类名、字段名、枚举。 + +    3). String: length(4 bytes) + byte[](utf8); + +    4). 数组: length(4 bytes) + byte[]... + +    5). Object: + +      1. realclass (SmallString) (如果指定格式化的class与实体对象的class不一致才会有该值, 该值可以使用@ConvertEntity给其取个别名) + +      2. 空字符串(SmallString) + +      3. SIGN_OBJECTB 标记位,值固定为0xBB (short) + +      4. 循环字段值: + +        4.1 SIGN_HASNEXT 标记位,值固定为1 (byte) + +        4.2 字段类型; 1-9为基本类型&字符串; 101-109为基本类型&字符串的数组; 127为Object + +        4.3 字段名 (SmallString) + +        4.4 字段的值Object + +      5. SIGN_NONEXT 标记位,值固定为0 (byte) + +       6. SIGN_OBJECTE 标记位,值固定为0xEE (short) + diff --git a/docs/source.md b/docs/source.md deleted file mode 100644 index 15ea37cbf..000000000 --- a/docs/source.md +++ /dev/null @@ -1,2 +0,0 @@ -# 数据源 -文档完善中…… \ No newline at end of file diff --git a/src/main/java/org/redkale/convert/ConvertEntity.java b/src/main/java/org/redkale/convert/ConvertEntity.java index b5527e19c..ef3d20692 100644 --- a/src/main/java/org/redkale/convert/ConvertEntity.java +++ b/src/main/java/org/redkale/convert/ConvertEntity.java @@ -5,14 +5,14 @@ */ package org.redkale.convert; +import java.lang.annotation.*; import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.RUNTIME; -import java.lang.annotation.*; - /** * 用于类名的别名, 该值必须是全局唯一
- * 使用场景: 当BSON序列化为了不指定class可以使用@ConvertEntity来取个别名。关联方法: Reader.readClassName() 和 Writer.writeClassName(String value) 。 + * 使用场景: 当BSON序列化为了不指定class可以使用@ConvertEntity来取个别名。
+ * 关联方法: {@link org.redkale.convert.Reader#readClassName()} 和 {@link org.redkale.convert.Writer#writeClassName(java.lang.String) } 。 * *

详情见: https://redkale.org *