版权所有2007-2015蔡倩彦,采用GNU LGPL版本2.1许可模式。
关于Javascript端开发请参考《Objot 版本23 Javascript端手册》。 关于Actionscript端开发请参考《Objot 版本23 Actionscript端手册》。
数据可以编码成字符串,以便传递给Javascript、Actionscript进行处理。可以从字符串中解码出数据。 可以被编解码的数据包括:
Java | Javascript | Actionscript 3 |
---|---|---|
null | null undefined(只编码) | null undefined(只编码) |
boolean Boolean | boolean | Boolean |
int long(部分) float double Integer Long(部分) Float Double Number(作为long) | number | Number int |
String Clob | string | String |
Date Calendar(只编码) | Date | Date |
boolean[] int[] long[] <T>T[] Collection<T> | Array | Array |
其他Object Map<String,T> | 其他Object(非Function Boolean Number String) | 其他Object(非Class Function) |
Objot支持单继承类,Java端可以为类和属性指定别名。 列表、对象之间可以任意引用,构成的整个数据图都可以被编解码。
可以定义规则,只处理局部(子图)。规则针对 被编解码的数据 和 规则key 设定。 规则、别名、对象创建等配置是代码性的,可以自行编写adapter等,支持annotation、file等配置形式,可以将业务代码与Objot完全分离。
第一次编解码某个类的对象时,会生成bytecode来访问这个类,后续编解码这个类的对象时,不再有任何reflection,优化了效率。
编解码。默认的规则配置代码采用annotation配置方式。
c == o.getClass()
,既可给每个类,也可以给每个对象指定不同的名字。
默认是:return o instanceof HashMap ? "" : c.getName();
return name.length() == 0 ? HashMap.class : Class.forName(name);
true
return Set.class.isAssignableFrom(c) ? new HashSet<Object>(len) : new ArrayList<Object>(len);
// 调用getClazz(),若已经分析过,则直接返回分析结果 // 对c的每个field进行@Enc、@Dec等annotation分析 // 对c的每个method进行@Enc、@Dec等annotation分析 // 调用addClazz(),将分析的field,method情况传入,然后返回最终结果 // 默认使用PropertyAnno表示属性信息,请参考源代码
表示被标记的field、getter需要编码。
field、getter不能是private,getter可以是getName()
也可以是name()
这样的形式。
可以设置参数,表示指定哪些key时需要编码,哪些禁止编码,作为编码规则。规则在后的优先。
@Enc(A.class) public String getName() { return name; } @Enc({Yes.class, A.class}) public String name() { return name; }表示:当key为A类或子类或对象时,需要编码;其他则禁止编码。
@Enc({A.class, B.class}) public String name;表示:当key为B类或子类或对象时,需要编码;当key为A类或子类或对象时,需要编码;其他则禁止编码。
@Enc({No.class, C.class}) public String name;表示:当key为C类或子类或对象时,禁止编码;其他则需要编码。
@Enc({No.class, C.class, Yes.class, A.class}) public String name;表示:当key为A类或子类或对象时,需要编码;当key为C类或子类或对象时,禁止编码;其他则需要编码。
@Enc({D.class, Yes.class, A.class, B.class, No.class, C.class}) public String name;表示:当key为C类或子类或对象时,禁止编码;当key为B类、A类或子类或对象时,需要编码;当key为D类或子类或对象时,禁止编码;其他则需要编码。
当标记类时,其规则作用于类的各field、getter的@Enc、@EncDec的前端,对@Dec不起作用。
@Enc({A.class, No.class}) public class Foo { @Enc(C.class) public String name; public String name2; }对于name,当key为C类或子类或对象时,禁止编码;当key为A类或子类或对象时,需要编码;其他则禁止编码。 而name2,则不编码。
@Dec与@Enc类似,表示被标记的field、setter允许解码。
field、setter不能是private,setter可以是setName(String v)
也可以是name(String v)
这样的形式。
@EncDec表示@Enc和@Dec的组合。
当在类上标记@Dec时,其规则作用于各个field、setter的@Dec、@EncDec的前端,对@Enc不起作用。 当在类上标记@EncDec时,其规则作用于各个field、getter、setter的@Enc、@Dec、@EncDec的前端。
标记了@EncDec,就不能再标记@Enc和@Dec,但@Enc和@Dec可以一起标记。
@NameEnc标记在field、getter上,表示编码时的名字,而不是field、getter名本身。 @NameDec标记在field、setter上,表示解码时的名字。 @Name表示@NameEnc和@NameDec的组合。
标记了@Name,就不能再标记@NameEnc和@NameDec,但@NameEnc和@NameDec可以一起标记。
关于属性编解码的信息。
field.getName
或Class2.propertyOrName(method, enc)
。
field.getType
、method.getReturnType
或method.getParameterTypes()[0]
。
Class2.typeParamClass( field != null ? field.getGenericType() : enc ? method.getGenericReturnType() : method.getGenericParameterTypes()[0], 0, Object.class);
结合使用规则、别名,可以针对不同需求,给数据进行不同的编码。例如:
package demo; ... public class User { @EncDec // 对于任何key,均可编解码 public Integer id; protected String name; @Enc({DoUser.class, DoChat.class}) public String name() { return name; } @Dec({DoSign.class, DoUser.class}) public void name(String v) { name = v; } @Dec(DoUser.class) public List<User> friends; @Enc(DoUser.class) // key为DoUser类或子类或对象,需编码 @Name("friends") // 编码名称为friends,因为不可解码,所以解码名称没有意义 public List<User> friends_; }
也可以编写访问配置文件的代码,从而支持文件式配置。例如:
// 读取配置文件 // 根据配置文件生成 相关类的创建模式,例如 使用IOC和AOP创建 Codec c = new Codec() { @Override protected Object byName(String name) throws Exception { // 根据已生成的创建模式和name创建对象 } @Override protected Object clazz(Class c) throws Exception { Object z = getClazz(c); if (z != null) return z; Property[] encs, decs; // 根据c从配置文件中寻找相关配置 // 如果无相关配置,则抛出异常,或者return super.clazz(c) // 找到配置,则根据编码配置,依次创建Prop类,生成encs数组 // 找到配置,则根据解码配置,依次创建Prop类,生成decs数组 return addClazz(c, false, encs, decs, null); } }; class Prop extends Property { Prop(AccessibleObject fm, boolean enc) throws Exception { super(fm, enc); // 其他属性分析工作 } @Override protected boolean encodable(Object o, Object ruleKey) throws Exception { // 根据key判断是否允许编码本属性 } @Override protected boolean decodable(Object o, Object ruleKey) throws Exception { // 根据key判断是否允许解码本属性 } }
编解码前,先创建Codec对象:
Codec codec = new Codec();或创建Codec子类对象,并可改变默认行为:
Codec codec = new Codec() { // 加上包名 @Override protected Object byName(String name) throws Exception { return name.length() == 0 ? HashMap.class : Class.forName("demo.".concat(name)); } // 去掉包名 @Override protected String name(Object o, Class<?> c) throws Exception { return o instanceof HashMap ? "" : objot.util.Class2.selfName(c); } // override clazz, addClazz 等方法,即可采用不同的配置方式 }
然后用这个codec对象来编解码:
User u1 = new User(); u1.id = 1; u1.name = "user1"; u1.friends = new ArrayList<User>(); User u2 = new User(); u2.id = 2; u2.name = "user2"; u2.friends = new ArrayList<User>(); ... u1.friends.add(u1); u1.friends.add(u2); u2.friends.add(...); ... u1.friends_ = u1.friends; // 设置临时数据 CharSequence s = codec.enc(u1, DoUser.class); // 编码u1,规则key为DoUser类 u1.friends_ = null; // 清除临时数据 v1 = codec.dec(s.toString().toCharArray(), Object.class, DoUser.class); // 规则key为DoUser,解码为v1,限制v1的类必须是Object只有u1设置了friends_,因此编码u1时,只有u1的friends_以friends的名字编码。 解码时,只有v1具有friends属性,因为编码字符串中,只有u1有friends数据。
Objot利用bytecode合成子类,实现轻量、快速而强大的AOP。 合成后,执行以local变量为主,没有reflection,效率比许多interception方式要高。
允许使用常规的编程模式进行aspect编写,比before、after、catch那样分离的方式更加灵活。 配置是代码性的,可以自行编写adapter等,支持annotation、file等配置形式,可以将业务代码与Objot完全分离。
各个aspect的直接父类。各个aspect子类weave后,合成为target类的子类。 所有对aspect子类的引用(例如常量、类型、方法等)全部被替换成 对target子类的引用。 aspect子类中的所有field、method(除了aspect方法)都一一合成为target子类的field、method。
在Aspect.aspect中,与target方法相关的功能。 在aspect之外(未weave)直接调用都抛出AbstractMethodError异常。 weave后,被替换成常量或指令。
合成target子类。合成后没有额外的reflection。
Target.data()
使用,每个aspect每个target方法的组合一个数据。
可以在forWeave方法中编写标记访问代码,从而支持标记式配置。例如:
Weaver w = new Weaver(SignAspect.class, TransactionAspect.class) { @Override protected Object forWeave(Class<? extends Aspect> ac, Method m) throws Exception { if (ac == SignAspect.class) return m.isAnnotationPresent(Sign.class) ? this : null; if (ac == TransactionAspect.class) return m.isAnnotationPresent(Transaction.class) ? this : new TransactionAspect.Config(m); return this; } };
也可以编写访问配置文件的代码,从而支持文件式配置。例如:
// 读取配置文件 Weaver w = new Weaver(/* 配置中列举的全部aspect */) { @Override protected Object forWeave(Class<? extends Aspect> ac, Method m) throws Exception { // 根据m从配置文件中寻找相关配置 // 如果无相关配置,则返回 this // 找到配置,则进一步判断需要weave的aspect,与ac比较 // 相同,则创建数据,并返回数据 // 无相同的,则返回this } };
Objot提供了轻量的基于bytecode的高效容器来实现IOC。 合成子类后,创建容器没有reflection,没有线程同步,注入过程中也没有hash计算,效率很高。
支持多种注入模式,为对象作用域的控制提供了相当灵活的方式。 配置是代码性的,可以自行编写adapter等,支持annotation、file等配置形式,可以将业务代码与Objot完全分离。
各个容器的父类,子类是由bytecode合成的容器。各个容器对象可以组成层次关系,相互为父子关系。
createBubble(null)
效果相同。线程安全。
get、create、set方法均可能抛出任何异常,特别是容器内部调用绑定类的方法时,由这些方法抛出。
标记需要注入的地方。标记类的注入模式。 也可以使用完全分离的配置方式,消除业务代码对这些annotation的依赖。
new模式的类,每次注入都创建新对象。single模式的类,每次注入都使用同一个对象。 set模式的类,每次注入都使用Container.set方法指定的对象,调用set方法前是null。 parent模式的类,每次注入都从父容器获取,或者从可能的缓存中获取。
对于single和set模式,即使是相同的绑定,在每个容器和每个副本之间,对象都是相互独立的。 如果不同容器需要共享同一个single或set的对象,则用parent模式、或不绑定类,并且共享同一个父容器,由父容器提供这个对象。
可以将类静态绑定到一个对象上,这个对象在相同绑定的容器和副本间共享。如果绑定对象,则各个模式无效。
可以对field、方法parameter的注入独立配置,允许使用某个类绑定,或单独绑定到一个对象,但不能单独指定注入模式。
容器工厂。通过bind()和forBind()完成配置,然后合成容器。
return null;
for (Constructor t: ts) if (t.isAnnotationPresent(Inject.class)) return t; return c.getDeclaredConstructor(); // 无参数的构造方法选择的构造方法必须是public not static。
for (int i = 0; i < fms.length; i++) if ( !fms[i].isAnnotationPresent(Inject.class)) fms[i] = null; return fms;选择的field、方法必须是public not static。
return null;
绑定到其他类时,如果此类尚未配置绑定,则将在forBind方法返回后,由工厂主动绑定。
注意:new模式的类之间不能循环依赖(例如A、B类相互引用,又都是new模式),否则注入时会导致无限递归,栈溢出。 构造方法参数之间不能循环依赖(例如A类构造方法参数引用B类,B类构造方法参数引用A类),否则注入时会导致无限递归,栈溢出。 对于single模式的类,可能因为构造方法中循环依赖而重复创建,容器发现这种情况后会抛出异常。
关于绑定的具体配置。
Class2.boxTry(cla, true)
。
cla()返回对象自身。
使用默认的绑定参数做配置,则不需要override方法:
Container a = new Factory().bind(A.class, B.class).create(null, true); Container b = new Factory(Inject.Set.class).bind(A.class, B.class).create(null);
可以按需override几个Factory.forBind方法,自定义配置。 例如单元测试test.container里的代码,以及文件式配置:
// 读取配置文件 Container a = new Factory() { { bind(/* 配置中列举的类 */); } @Override protected Object forBind(Class c, Bind b) throws Exception { // 根据c从配置文件中寻找相关类 // 如无相关类,则抛出异常 // 找到类,则根据配置设置b,返回null } @Override protected Constructor forBind(Class c, Constructor[] ts) throws Exception { // 根据c从配置文件中寻找相关类(已找到) // 根据配置返回类的构造方法 } @Override protected AccessibleObject[] forBind(Class c, AccessibleObject[] fms) throws Exception { // 根据c从配置文件中寻找相关类(已找到) // 根据配置返回类的field、方法数组 } @Override protected Object forBind(Class cc, AccessibleObject fp, Class c, Type generic, Bind b) throws Exception { // 根据cc、fp从配置文件中寻找相关field、方法(已找到) // 根据配置设置b,返回null } }.create(/* 根据配置指定父容器 */, /* 根据配置指定eager、lazy */);
Objot还提供一些辅助类,方便开发面向服务的应用。 可以将业务代码发布成service,由Codec提供单个至多个参数的解码、返回结果和异常的编码。 支持文件http上传和下载,以及进度查询。
关于service的一些基本信息。参看源代码。
独立于传输协议的serivce调用处理。参考源代码,和范例的代码。
用Codec将调用的参数字符串解码成参数对象,用reflection调用service方法。 将serivce方法返回对象做编码,返回字符串。抛出的异常默认编码为objot.util.Err。 调用时可以指定上下文容器,用于获取外部数据,可以附加参数字符串之外的参数用于文件上传等功能。
通常编写子类,增加与持久会话、单次请求相对应的Container层次,并使用AOP辅助service方法。 处理service方法返回的byte[]和InputStream等情况。
servlet处理。参考源代码,和范例的代码。
支持http post的serivce请求,并按照UTF-8提取字符串,调用ServiceHandler子类。 如果ServiceHandler返回的是CharSequence对象,则以UTF-8 test/plain输出。 否则当做byte[]或InputStream,直接输出。
请求时,提供上下文容器,包含HttpServletRequest, HttpServletSession等供ServiceHandler处理。 对于http文件上传,生成Input.Upload对象,既用于参数字符串提取和解码,也传递给ServiceHandler供serivce读取。 对于返回byte[]和InputStream,ServiceHandler可以在上下文容器中设置字符串值,指定mime类型,默认是application/octet-stream。
对于进度查询使用固定的url格式,并返回进度文本。
参见源代码。
0长度的常用数组。按长度创建数组,并复用0长度数组。按长度和类型创建数组。转换null数组为0长度数组。
在数组中查找。二分查找。可指定Comparator来查找。
数组按需扩容。按需收缩。按范围复制数组。数组拼接。
Collection转换为数组。添加数组到Collection。
数组、Collection拼接成字符串。
对byte[]、范围的封装。扩容。
多种创建方式。从InputStream中读取全部字节。
复制。比较。
按byte、short、char、int、unsigned int、long读写。
与Bytes相似。
取对象的系统hash。类型转换、判断、box、unbox。
类名、包名、路径名、资源名。类描述符。bean属性名。
获得generic类的generic参数。按名称、modifier、参数类型查找field、method。 判断方法override。请求accessible特权。
读取类的字节码。在ClassLoader中根据名称、bytecode定义类。
查找一个包里的所有类。
Throwable,转换为Exception,转换为RuntimeException。
annotation互斥判断。
对异常的封装,供objot.service.ServiceHandler使用。
从InputStream中读取全部字节。读InputStream写OutputStream。
依据分割符,按行读取InputStream。读取字节计数。
解析http上传格式。
意义更明确的几个异常。
三个值取最大、最小。限制数值在一个范围中。
对数值做index、length、range等检查。
判断数值大小和范围。
限制正、负数。加、减法溢出处理。
floor、ceiling模式的整除、取余、对齐。2的幂。
数值转换成字符、字符串。
给出更详细的modifier的定义。补充一些分类定义。
分类。匹配判断。转换成字符串。
表示方法参数。
空串判断。转换null字符串。
查找字符、子字符串。提取子串。
UTF-8转换。