前一篇我们介绍了XML文件的解析过程,解析后的数据保存在rawPropertyMap这个Map中,数据解析完后,那么是sphinx4是如何进行创建对象的呢?
回顾我们的HelloWorld Demo,是通过ConfigurationManager的lookup方法来得到某个组件的:
Recognizer recognizer = (Recognizer) cm.lookup("recognizer");
下面我们看下lookup这个方法:
public Configurable lookup(String instanceName) throws InternalConfigurationException {
// apply all new properties to the model
instanceName = getStrippedComponentName(instanceName);
PropertySheet ps = getPropertySheet(instanceName);
if (ps == null)
return null;
if (showCreations)
getRootLogger().config("Creating: " + instanceName);
return ps.getOwner();
}
这个方法的参数是一个String,也就是之前解析得到的对象实例名称,返回类型是一个Configurable
Configurable是一个接口,它有一个抽象方法public void newProperties(PropertySheet ps):
public interface Configurable {
public void newProperties(PropertySheet ps) throws PropertyException;
}
当配置属性发生变化的时候,会回调newProperties这个方法,显然所有用到的组件都实现了Configurable接口,这样在调用lookup方法的时候才能进行强制转换。
继续回到lookup方法中来,我们看下getStrippedComponentName方法:
public String getStrippedComponentName(String propertyName) {
assert propertyName != null;
while (propertyName.startsWith("$"))
propertyName = globalProperties.get(ConfigurationManagerUtils.stripGlobalSymbol(propertyName)).toString();
return propertyName;
}
这个方法很简单,因为在xml文件中属性经常会引用到之前定义过的变量,引用的时候使用的是${}这种形式,这个方法就是对这种形式进行解析而已。
再看看getPropertySheet这个方法:
public PropertySheet getPropertySheet(String instanceName) {
if (!symbolTable.containsKey(instanceName)) {
// 如果不在symbol table中,构造
RawPropertyData rpd = rawPropertyMap.get(instanceName);
if (rpd != null) {
String className = rpd.getClassName();
try {
// 根据className 获取到它对应的Class
Class<?> cls = Class.forName(className);
// now load the property-sheet by using the class annotation
PropertySheet propertySheet = new PropertySheet(cls.asSubclass(Configurable.class), instanceName, this, rpd);
symbolTable.put(instanceName, propertySheet);
} catch (ClassNotFoundException e) {
System.err.println("class not found !" + e);
} catch (ClassCastException e) {
System.err.println("can not cast class !" + e);
} catch (ExceptionInInitializerError e) {
System.err.println("couldn't load class !" + e);
}
}
}
return symbolTable.get(instanceName);
}
这个方法首先从存放解析数据的rawPropertyMap中,通过实例名称查找对应的属性数据
RawPropertyData。然后通过RawPropertyData中的className得到它的Class,再根据Class,instanceName,rawPropertyMap等参数创建一个PropertySheet,同时把创建好的PropertySheet保存到symbolTable进行缓存,最终返回symbolTable中的PropertySheet对象。
lookup最后会调用PropertySheet的getOwner方法返回一个Configurable对象,我们分析一下getOwner方法:
public synchronized Configurable getOwner() {
try {
if (!isInstanciated()) {
// ensure that all mandatory properties are set before instantiating the component
Collection<String> undefProps = getUndefinedMandatoryProps();
if (!undefProps.isEmpty()) {
throw new InternalConfigurationException(getInstanceName(),
undefProps.toString(), "not all mandatory properties are defined");
}
owner = ownerClass.newInstance();
owner.newProperties(this);
}
} catch (IllegalAccessException e) {
throw new InternalConfigurationException(e, getInstanceName(), null, "Can't access class " + ownerClass);
} catch (InstantiationException e) {
throw new InternalConfigurationException(e, getInstanceName(), null, "Can't instantiate class " + ownerClass);
}
return owner;
}
此方法最重要的就是这两句:
owner = ownerClass.newInstance();
根据Class,通过反射来创建对象
owner.newProperties(this);
因为属性发生了变化,回调newProperties方法。
以上就是sphinx4创建对象的整个流程,本质上就是通过反射来实现的,只是加了一些自己的封装而已。