Objot 版本23 Java端手册


版权所有2007-2015蔡倩彦,采用GNU LGPL版本2.1许可模式。

关于Javascript端开发请参考《Objot 版本23 Javascript端手册》。 关于Actionscript端开发请参考《Objot 版本23 Actionscript端手册》

范例

使用Servlet服务器部署范例。拷贝以下文件(或其他合适的版本)到WEB-INF/lib/目录中: 并且确保/tmp目录(或者C:\tmp之类)可写,或者修改hibernate.properties中hsqldb存储文件的设置。 对于Apache Tomcat,请使用6.0及以上版本,之前版本没有正确分离服务器根和web应用的commons-logging配置,这将导致无限递归。 访问demo/index.html。用Selenium访问demo/test/index.html可以进行测试。

数据编解码

数据可以编码成字符串,以便传递给Javascript、Actionscript进行处理。可以从字符串中解码出数据。 可以被编解码的数据包括:

JavaJavascriptActionscript 3
nullnull undefined(只编码)null undefined(只编码)
boolean BooleanbooleanBoolean
int long(部分) float double Integer Long(部分) Float Double Number(作为long)numberNumber int
String ClobstringString
Date Calendar(只编码)DateDate
boolean[] int[] long[] <T>T[] Collection<T>ArrayArray
其他Object Map<String,T>其他Object(非Function Boolean Number String)其他Object(非Class Function)
其中,字符串是Unicode字符集,但不能包含一个特殊字符:'\x10'(即\u0010),Objot编解码时使用了这个特殊字符。 java.util.Map的属性名也不能包含特殊字符,但Objot不做检查。

Objot支持单继承类,Java端可以为类和属性指定别名。 列表、对象之间可以任意引用,构成的整个数据图都可以被编解码。

可以定义规则,只处理局部(子图)。规则针对 被编解码的数据 和 规则key 设定。 规则、别名、对象创建等配置是代码性的,可以自行编写adapter等,支持annotation、file等配置形式,可以将业务代码与Objot完全分离。

第一次编解码某个类的对象时,会生成bytecode来访问这个类,后续编解码这个类的对象时,不再有任何reflection,优化了效率。

public class objot.codec.Codec

编解码。默认的规则配置代码采用annotation配置方式。

public StringBuilder enc(Object o, Object rukeKey) throws Exception
public StringBuilder enc(Object o, Object rukeKey, StringBuilder s) throws Exception
编码,使用的规则与 每个编码的对象 及ruleKey相关。非线程安全。
public <T>T dec(char[] s, Class<T> cla, Object ruleKey) throws Exception
public <T>T dec(char[] s, int sBegin, int sEnd1, Class<T> cla, Object ruleKey)
解码,使用的规则与 每个解码的对象 及ruleKey相关。返回数据图的根。 对根的解码将参照cla,如果不能符合cla,则抛出异常。 被解码的字符串范围是[sBegin,sEnd1)。非线程安全。
protected String name(Object o, Class c, Object ruleKey) throws Exception
编码时指定名字。c == o.getClass(),既可给每个类,也可以给每个对象指定不同的名字。 默认是:
return o instanceof HashMap ? "" : c.getName();
protected Object byName(String name, Object ruleKey) throws Exception
解码时,根据名字得到对象,或者对象的类(生成的bytecode会调用无参数构造方法)。默认是:
return name.length() == 0 ? HashMap.class : Class.forName(name);
protected long beLong(long l) throws Exception
编码long时(包括日期时间的毫秒数)检查值,并返回这个值。 默认对超出-9007199254740991L至9007199254740991L(+-53bit)的值抛异常。 因为Javascript和Actionscript中数值只有53bit精度(IEEE754)。
protected boolean arrayForList;
Java端[]和Collection都编码为列表,解码时,如果根据上下文不能确定[]还是Collection,就由此field确定。 默认是true
protected Collection<Object> newList(Class c, int len) throws Exception
根据类和列表长度创建解码的Collection。默认是:
return Set.class.isAssignableFrom(c) ? new HashSet<Object>(len)
	: new ArrayList<Object>(len);
protected void undecodable(Object o, String prop, Object ruleKey)
在对象o上找不到属性prop,或不能解码属性prop时,调用本方法。默认是抛出RuntimeException。
protected void undecodeValue(Object o, String prop, Object ruleKey, Object value) throws Exception
对于不解码的属性,如果undecodable没有抛出异常,则调用本方法,value为属性值。默认直接返回。
public Clazz clazz(Class c) throws Exception
分析一个类,返回分析结果,用于编解码的处理。既可能在编解码时被动调用,也可以主动调用。默认是:
// 调用getClazz(),若已经分析过,则直接返回分析结果
// 对c的每个field进行@Enc、@Dec等annotation分析
// 对c的每个method进行@Enc、@Dec等annotation分析
// 调用addClazz(),将分析的field,method情况传入,然后返回最终结果
// 默认使用PropertyAnno表示属性信息,请参考源代码
protected final Clazz getClazz(Class c) throws Exception
返回类的分析结果,若尚未分析,则返回null。线程安全。
protected final Clazz addClazz(Class c, Property[] encs, Property[] decs, Map<String, Property> decNames) throws Exception
protected final Clazz addClazz(Class c, Map<?, Property> encs, Map<?, Property> decs, Map<String, Property> decNames) throws Exception
对类做最后分析,添加并返回最终结果。 encs为需要编码的属性信息,decs为需要解码的属性信息。 decNames为解码属性名和信息的map,若为null则默认根据encs生成,不为null则重用这个map。 线程安全。参见Property类。
public Codec()
默认构造方法。类分析结果独立于其他Codec对象。
public Codec(Codec sameClazz)
构造方法。类分析结果和s指定Codec共用,并保持相同。

public @interface objot.codec.Enc, Yes, No

表示被标记的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,则不编码。

public @interface objot.codec.Dec, EncDec

@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可以一起标记。

public @interface objot.codec.NameEnc, NameDec, Name

@NameEnc标记在field、getter上,表示编码时的名字,而不是field、getter名本身。 @NameDec标记在field、setter上,表示解码时的名字。 @Name表示@NameEnc和@NameDec的组合。

标记了@Name,就不能再标记@NameEnc和@NameDec,但@NameEnc和@NameDec可以一起标记。

public class Property

关于属性编解码的信息。

protected Class out;
属性所在的declaring类。
protected Field field;
field属性,非field则为null。
protected Method method;
method属性,非method则为null。
protected String name;
属性名。 默认是field.getNameClass2.propertyOrName(method, enc)
protected Class cla;
属性的类。 默认是field.getTypemethod.getReturnTypemethod.getParameterTypes()[0]
protected Class listElem;
如果是Collection,表示元素的类。默认是
Class2.typeParamClass(
	field != null ? field.getGenericType()
	: enc ? method.getGenericReturnType()
		: method.getGenericParameterTypes()[0],
	0, Object.class);
public Property(AccessibleObject fm, boolean enc)
默认构造方法,根据fm(必须为Field或Method)、enc设置各个默认值。
public void into(Map<String, Property> map)
将本属性信息放入一个map中,若已有同名的属性,抛出异常。
protected boolean encodable(Object o, Object ruleKey) throws Exception;
对应规则key,判断对象o的本属性是否允许编码。默认是true。
protected boolean decodable(Object o, Object ruleKey) throws Exception;
对应规则key,判断对象o的本属性是否允许解码。默认是true。

灵活的配置

结合使用规则、别名,可以针对不同需求,给数据进行不同的编码。例如:

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数据。

AOP

Objot利用bytecode合成子类,实现轻量、快速而强大的AOP。 合成后,执行以local变量为主,没有reflection,效率比许多interception方式要高。

允许使用常规的编程模式进行aspect编写,比before、after、catch那样分离的方式更加灵活。 配置是代码性的,可以自行编写adapter等,支持annotation、file等配置形式,可以将业务代码与Objot完全分离。

public abstract class objot.aspect.Aspect

各个aspect的直接父类。各个aspect子类weave后,合成为target类的子类。 所有对aspect子类的引用(例如常量、类型、方法等)全部被替换成 对target子类的引用。 aspect子类中的所有field、method(除了aspect方法)都一一合成为target子类的field、method。

<init>()
aspect子类的构造方法weave后,依据target类的各个构造方法,合成为target子类的各个构造方法。
protected abstract void aspect() throws Throwable
aspect的具体实现。此具体aspect方法weave后,依据各个target方法,合成为各个override方法。 参数和返回类型 被替换成target方法的参数和返回类型。 this对象被替换成target对象。 aspect方法可以抛出任何异常,给调用者,即使target方法没有声明。 target方法的调试信息指向aspect方法,因此不影响源代码调试。

public static class objot.aspect.Aspect.Target

在Aspect.aspect中,与target方法相关的功能。 在aspect之外(未weave)直接调用都抛出AbstractMethodError异常。 weave后,被替换成常量或指令。

public static String name()
target方法的名字。
public static String descript()
target方法的描述符。
public static String target()
target方法的名字及描述符。
public static <T>T thiz()
target对象,== aspect方法的this。
public static <T>Class<T> clazz()
target类。
public static void invoke()
执行target方法,参数不变。可以多次执行。
public static <T>Class<T> returnClass()
target方法声明的返回类。
public static <T>T getReturn()
target方法的返回值,原始类型为其box值,void为null。 必须在invoke()之后执行,注意try finally等情况,否则加载target子类时抛出某个Error异常。
public static void setReturn(Object o)
指定返回值,原始类型为其box值,void必须为null。 如果在invoke()之前执行,则invoke()执行后的返回值优先。 如果invoke()不执行且非void,则必须指定一个返回值,否则加载target子类时抛出某个Error异常。
public static <T>T data()
获得为本override方法指定的数据。参见objot.aspect.Weaver.forWeave。

public abstract class objot.aspect.Weaver

合成target子类。合成后没有额外的reflection。

public Weaver(Class... aspectClasses) throws Exception
创建一个weaver,指定aspect。 指定多个aspect则合成多个target子类,在前的aspect在最外层,target在最内层。
public synchronized <T>Class<T> weave(Class<T> target) throws Exception
合成target子类。target类不能是Aspect或子类,不能是原始类、接口、abstract或final类。 必须有至少一个public或protected的构造方法。合成的每个target子类分别在每个aspect的package中。 返回的类是最外层的target子类。
protected abstract Object forWeave(Class<? extends Aspect> ac, Method m) throws Exception
针对每个aspect和每个target方法的组合,判断是否需要weave,并指定一个数据。 如果返回this(即Weaver对象本身),表示不要weave。 返回其他则weave,并作为override方法的相关数据,供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
	}
};

IOC

Objot提供了轻量的基于bytecode的高效容器来实现IOC。 合成子类后,创建容器没有reflection,没有线程同步,注入过程中也没有hash计算,效率很高。

支持多种注入模式,为对象作用域的控制提供了相当灵活的方式。 配置是代码性的,可以自行编写adapter等,支持annotation、file等配置形式,可以将业务代码与Objot完全分离。

public abstract class objot.container.Container

各个容器的父类,子类是由bytecode合成的容器。各个容器对象可以组成层次关系,相互为父子关系。

public final Container parent()
本容器的父容器。null表示没有父容器。线程安全。
public final Container rootParent()
本容器的根容器(即最上层的父容器)。如果没有父容器,则返回自身。线程安全。
public final Container create()
创建副本(与本容器配置相同的容器),共享父容器。线程安全。
public final Container create(Container parent_)
创建副本,指定父容器。线程安全。
public final Container createBubble()
创建本容器、父容器、递归到根容器的每个副本,他们构成独立的层次。 与createBubble(null)效果相同。线程安全。
public final Container createBubble(Container until)
创建本容器、父容器、递归 未 到容器until的每个副本,他们构成独立的层次,共享容器until为父容器。 如果until不在本容器层次中,则抛出异常。线程安全。
public final Container createBubble(Container until, Container to)
创建本容器、父容器、递归 未 到容器until的每个副本,他们构成独立的层次,容器to替代until为父容器。 如果until不在本容器层次中,则抛出异常。线程安全。
public boolean bound(Class c)
判断本容器是否绑定了某个类。线程安全。
public Container contain(Class c)
返回绑定了某个类的本容器、父容器直到根容器。如果均无绑定,返回null。线程安全。
public final <T>T get(Class<T> c)
获取某个类的对象。对于Container.class,返回本容器。 如果本容器没有绑定此类,则递归到父容器。从lazy容器或lazy父容器中获得single对象非线程安全,其他情况线程安全。
public final <T>T getNew(Class<T> c)
获取某个类的对象。对于Container.class,则创建副本,共享父容器。对于single模式,则创建新对象。 如果本容器没有绑定此类,则递归到父容器。线程安全。
public final <T>T set(Class<T> c, T o)
设置某个类的对象,只能用于set模式,其他则抛出异常。 返回设置的对象。如果本容器没有绑定此类,则递归到父容器。线程安全。

get、create、set方法均可能抛出任何异常,特别是容器内部调用绑定类的方法时,由这些方法抛出。

public @interface objot.container.Inject, Inject.New, Inject.Single, Inject.Set, Inject.Parent

标记需要注入的地方。标记类的注入模式。 也可以使用完全分离的配置方式,消除业务代码对这些annotation的依赖。

new模式的类,每次注入都创建新对象。single模式的类,每次注入都使用同一个对象。 set模式的类,每次注入都使用Container.set方法指定的对象,调用set方法前是null。 parent模式的类,每次注入都从父容器获取,或者从可能的缓存中获取。

对于single和set模式,即使是相同的绑定,在每个容器和每个副本之间,对象都是相互独立的。 如果不同容器需要共享同一个single或set的对象,则用parent模式、或不绑定类,并且共享同一个父容器,由父容器提供这个对象。

可以将类静态绑定到一个对象上,这个对象在相同绑定的容器和副本间共享。如果绑定对象,则各个模式无效。

可以对field、方法parameter的注入独立配置,允许使用某个类绑定,或单独绑定到一个对象,但不能单独指定注入模式。

public class objot.container.Factory

容器工厂。通过bind()和forBind()完成配置,然后合成容器。

public Factory()
默认的注入模式为single。默认绑定Container类为容器自身,single模式。
public Factory(Class<? extends Annotation> defaultMode_)
指定默认注入模式。绑定的类,如果没有标记注入模式,则使用这个默认模式。 默认绑定Container类为容器自身,single模式。
public final synchronized boolean bound(Class cla)
判断是否绑定了一个类。
public final synchronized Factory bind(Class... cla)
绑定多个类。返回本工厂。
public final synchronized Factory bind(Class cla)
绑定一个类。不能绑定Container的子类,不能绑定非public类,允许绑定原始类型。返回本工厂。
protected Object forBind(Class c, Bind b) throws Exception
配置一个类绑定。传入的Bind已经根据标记,设置了模式。 可以绑定到另外一个类,可以指定注入模式,可以绑定到一个对象。 对于原始类型,因为没有构造方法,只能使用set、parent模式,或者绑定box对象。 返回值被忽略。默认是return null;
protected Constructor forBind(Class c, Constructor[] ts) throws Exception
配置构造方法。从类的所有构造方法中,选择一个返回。默认是:
for (Constructor t: ts)
	if (t.isAnnotationPresent(Inject.class))
		return t;
return c.getDeclaredConstructor(); // 无参数的构造方法
选择的构造方法必须是public not static。
protected AccessibleObject[] forBind(Class c, AccessibleObject[] fms) throws Exception
返回一个数组,由Field、Method、null组成,表示要按顺序注入field、方法,null跳过。 传入的fms数组,是一个由全部field、然后全部方法组成的数组,包括继承下来的。默认是:
for (int i = 0; i < fms.length; i++)
	if ( !fms[i].isAnnotationPresent(Inject.class))
		fms[i] = null;
return fms;
选择的field、方法必须是public not static。
protected Object forBind(Class cc, AccessibleObject fp, Class c, Type generic, Bind b) throws Exception
为选择的构造方法、field、方法配置各个参数。 cc为绑定类。fp为field,或方法参数objot.util.Parameter。 c为field.getType(),或parameter.cla。 generic为field.getGenericType(),或parameter.generic。 传入的Bind默认绑定到c类上。可以绑定到其他类,或绑定到一个对象。 返回值被忽略。默认是return null;

绑定到其他类时,如果此类尚未配置绑定,则将在forBind方法返回后,由工厂主动绑定。

注意:new模式的类之间不能循环依赖(例如A、B类相互引用,又都是new模式),否则注入时会导致无限递归,栈溢出。 构造方法参数之间不能循环依赖(例如A类构造方法参数引用B类,B类构造方法参数引用A类),否则注入时会导致无限递归,栈溢出。 对于single模式的类,可能因为构造方法中循环依赖而重复创建,容器发现这种情况后会抛出异常。

public final Container create(Container parent)
合成容器子类,并创建容器对象。指定父容器。容器创建采用eager模式。
public final Container create(Container parent, boolean lazy_)
合成容器子类,并创建容器对象。指定父容器。指定容器创建的模式。 推荐eager模式:容器创建时,也创建每个single模式的对象,这样Container.get和注入就是线程安全的。 但对于从父容器获取的对象,线程安全取决于父容器。 lazy模式:容器创建时,不创建single模式的对象,他们在第一次获取时创建。这样对他们的访问非线程安全。 容器创建后,还可以继续绑定类,然后再合成、创建增加了配置的容器。

public class objot.container.Bind

关于绑定的具体配置。

public Class cla
public Class box
public Bind cla(Class c)
获取绑定的类,默认是自身,可以绑定到其他类。box是Class2.boxTry(cla, true)。 cla()返回对象自身。
public Class<? extends Annotation> mode
public Bind mode(Class<? extends Annotation> m)
获取预判断的注入模式,可以改变注入模式。 如果绑定到其他类,或绑定到对象,或配置field、方法参数时,此模式将被忽略。
public Object obj
public Bind obj(Object o)
可以绑定到对象。对象的类型必须符合注入的要求,可以是原始类型的box对象。

灵活的配置

使用默认的绑定参数做配置,则不需要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 */);

Service辅助

Objot还提供一些辅助类,方便开发面向服务的应用。 可以将业务代码发布成service,由Codec提供单个至多个参数的解码、返回结果和异常的编码。 支持文件http上传和下载,以及进度查询。

public class objot.service.ServiceInfo

关于service的一些基本信息。参看源代码。

public class objot.service.ServiceHandler

独立于传输协议的serivce调用处理。参考源代码,和范例的代码。

用Codec将调用的参数字符串解码成参数对象,用reflection调用service方法。 将serivce方法返回对象做编码,返回字符串。抛出的异常默认编码为objot.util.Err。 调用时可以指定上下文容器,用于获取外部数据,可以附加参数字符串之外的参数用于文件上传等功能。

通常编写子类,增加与持久会话、单次请求相对应的Container层次,并使用AOP辅助service方法。 处理service方法返回的byte[]和InputStream等情况。

public class objot.service.CodecServlet implements Servlet

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格式,并返回进度文本。

辅助功能

参见源代码。

public class objot.util.Array2

0长度的常用数组。按长度创建数组,并复用0长度数组。按长度和类型创建数组。转换null数组为0长度数组。

在数组中查找。二分查找。可指定Comparator来查找。

数组按需扩容。按需收缩。按范围复制数组。数组拼接。

Collection转换为数组。添加数组到Collection。

数组、Collection拼接成字符串。

public class objot.util.Bytes

对byte[]、范围的封装。扩容。

多种创建方式。从InputStream中读取全部字节。

复制。比较。

按byte、short、char、int、unsigned int、long读写。

public class objot.util.Chars, Ints, Longs, Objects

与Bytes相似。

public class objot.util.Class2

取对象的系统hash。类型转换、判断、box、unbox。

类名、包名、路径名、资源名。类描述符。bean属性名。

获得generic类的generic参数。按名称、modifier、参数类型查找field、method。 判断方法override。请求accessible特权。

读取类的字节码。在ClassLoader中根据名称、bytecode定义类。

查找一个包里的所有类。

Throwable,转换为Exception,转换为RuntimeException。

annotation互斥判断。

public class objot.util.Err, Errs, ErrThrow

对异常的封装,供objot.service.ServiceHandler使用。

public class objot.util.Input, Input.Line, Input.Upload

从InputStream中读取全部字节。读InputStream写OutputStream。

依据分割符,按行读取InputStream。读取字节计数。

解析http上传格式。

public class objot.util.InvalidValueException, InvalidLengthException, InvalidRangeException

意义更明确的几个异常。

public class objot.util.Math2

三个值取最大、最小。限制数值在一个范围中。

对数值做index、length、range等检查。

判断数值大小和范围。

限制正、负数。加、减法溢出处理。

floor、ceiling模式的整除、取余、对齐。2的幂。

数值转换成字符、字符串。

public class objot.util.Mod2 extends Modifier

给出更详细的modifier的定义。补充一些分类定义。

分类。匹配判断。转换成字符串。

public class objot.util.Parameter extends AccessibleObject implements Member

表示方法参数。

public class objot.util.String2

空串判断。转换null字符串。

查找字符、子字符串。提取子串。

UTF-8转换。