activiti的配置文件主体类是Configuration,它的源码分成几部分,首先是类属性:
//日志初始化
private static Logger log = Logger.getLogger(Configuration.class.getName());
//定义通用的常量,用于各项初始化
public static final String NAME_COMMANDEXECUTOR = "CommandExecutor";
public static final String NAME_DBSCHEMA = "DbSchema";
public static final String NAME_IBATISSQLSESSIONFACTORY = "IbatisSqlSessionFactory";
public static final String NAME_DBIDGENERATOR = "DbidGenerator";
public static final String NAME_DEPLOYERMANAGER = "DeployerManager";
public static final String NAME_PROCESSCACHE = "ProcessCache";
public static final String NAME_PERSISTENCETYPEISSQL = "PersistenceTypeIsSQL";
public static final String NAME_TRANSACTIONALOBJECTDESCRIPTORS = "TransactionalObjectDescriptors";
public static final String NAME_JOBEXECUTOR = "JobExecutor";
//实例属性
protected String name = null;//配置属性名
protected String type = null;//配置属性类型
protected Map<String, Object> configurations = new HashMap<String, Object>();//配置属性集合
然后是构造函数:
//默认构造函数,日志输出流程引擎版本
public Configuration() {
log.info("activiti version "+ProcessEngine.VERSION);
}
然后是功能初始化方法
//添加配置属性,以键值对的形式放入配置属性集合中
public Configuration configurationObject(String name, Object object) {
configurations.put(name, object);
return this;
}
//通过资源名生成配置文件
public Configuration configurationResource(String resourceName) {
ConfigurationParser.INSTANCE
.createParse()
.configuration(this)
.sourceResource(resourceName)
.execute();
return this;
}
//通过url生成配置文件
public Configuration configurationUrl(URL url) {
ConfigurationParser.INSTANCE
.createParse()
.configuration(this)
.sourceUrl(url)
.execute();
return this;
}
剩余的就是get/set方法和初始化流程引擎的方法了。
从上面我们可以看出,生成配置文件的方法就是Configuration 类的核心方法。这个生成过程也很有意思,当我们对源码进行追踪的时候,我们发现关联的有4个类:解析器父类Parser和解析器子类ConfigurationParser,解析处理父类Parse和解析处理子类ConfigurationParse。
单从名字来看,这个4个类两两对应;从功能来看,解析器类为解析处理类提供解析器,解析处理类负责具体配置文件的解析。
首先来看解析器父类Parser:
public class Parser {
//protected 的静态类属性,初始化XML解析器工厂
protected static SAXParserFactory defaultSaxParserFactory = SAXParserFactory.newInstance();
//提供自身的常量单例对象
public static final Parser INSTANCE = new Parser();
//以自身为参数提供新的解析处理类
public Parse createParse() {
return new Parse(this);
}
//从XML解析器工厂中获取XML解析器提供给子类
protected SAXParser getSaxParser() throws Exception {
return getSaxParserFactory().newSAXParser();
}
//为子类提供XML解析器工厂
protected SAXParserFactory getSaxParserFactory() {
return defaultSaxParserFactory;
}
}
由解析器父类Parser的源码我们可以看出,它的主要特征是自身单例,生成静态XML解析器工厂实例(SAXParserFactory),然后为解析处理父类Parse提供XML解析器(SAXParser )并生成新的解析处理父类Parse的实例。
那么,接下来我们就看看解析器父类Parse:
//解析处理父类Parse继承了SAX2的默认XML事件处理基类
public class Parse extends DefaultHandler {
protected Parser parser;//解析器
protected String name;//资源类型名
protected StreamSource streamSource;//流资源
protected Element rootElement = null;//XML中默认根元素
protected List<Problem> problems = new ArrayList<Problem>();//问题列表
protected String schemaResource;//XML中schema
protected Stack<Object> contextStack;//上下文栈
//构造函数,参数为解析器父类Parser
public Parse(Parser parser) {
this.parser = parser;
}
//构造函数,参数为资源名
public Parse name(String name) {
this.name = name;
return this;
}
//以输入流作为资源构造解析处理父类Parse
public Parse sourceInputStream(InputStream inputStream) {
if (name==null) {
name("inputStream");
}
setStreamSource(new InputStreamSource(inputStream));
return this;
}
//通过资源名构造解析处理父类Parse
public Parse sourceResource(String resource) {
return sourceResource(resource, null);
}
//通过URL构造解析处理父类Parse
public Parse sourceUrl(URL url) {
if (name==null) {
name(url.toString());
}
setStreamSource(new UrlStreamSource(url));
return this;
}
//通过资源路径构造解析处理父类Parse
public Parse sourceUrl(String url) {
try {
return sourceUrl(new URL(url));
} catch (MalformedURLException e) {
throw new ActivitiException("malformed url: "+url, e);
}
}
//通过资源名初始化资源流
public Parse sourceResource(String resource, ClassLoader classLoader) {
if (name==null) {
name(resource);
}
setStreamSource(new ResourceStreamSource(resource, classLoader));
return this;
}
//通过资源名构造解析处理父类Parse
public Parse sourceString(String string) {
if (name==null) {
name("string");
}
setStreamSource(new StringStreamSource(string));
return this;
}
//通过资源流初始化资源流
protected void setStreamSource(StreamSource streamSource) {
if (this.streamSource!=null) {
throw new ActivitiException("invalid: multiple sources "+this.streamSource+" and "+streamSource);
}
this.streamSource = streamSource;
}
//验证并解析资源
public Parse execute() {
try {
InputStream inputStream = streamSource.getInputStream();
if (schemaResource != null) {
//采用W3C的scheme对输入流进行验证
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = factory.newSchema(Thread.currentThread().getContextClassLoader().getResource(schemaResource));
parser.getSaxParserFactory().setSchema(schema);
Validator validator = schema.newValidator();
validator.validate(new javax.xml.transform.stream.StreamSource(inputStream));
// The validator will read the stream until the end, rendering it useless for further usage
// PLEASE do not remove this comment. It has cost me 2 hours to figure it out.
//验证完毕会把输入流的指针置到流末尾,导致无法解析,耗费了代码作者两个小时来查错^_^
inputStream.reset();
}
//将输入流解析到一个新的ParseHandler对象中去
SAXParser saxParser = parser.getSaxParser();
saxParser.parse(inputStream, new ParseHandler(this));
} catch (Exception e) {
throw new ActivitiException("couldn't parse " + streamSource + ": "+e.getMessage(), e);
}
return this;
}
//获取根节点元素
public Element getRootElement() {
return rootElement;
}
//获取异常列表
public List<Problem> getProblems() {
return problems;
}
//添加异常
public void addProblem(SAXParseException e) {
problems.add(new Problem(e, name));
}
//设置schema资源
public void setSchemaResource(String schemaResource) {
parser.getSaxParserFactory().setNamespaceAware(true);
parser.getSaxParserFactory().setValidating(true);
this.schemaResource = schemaResource;
}
//入栈
public void pushContextObject(Object obj) {
if (contextStack == null) {
contextStack = new Stack<Object>();
}
contextStack.push(obj);
}
//出栈
public Object popContextObject() {
if (contextStack != null) {
return contextStack.pop();
} else {
throw new ActivitiException("Context stack was never initialised, so calling the pop() operation is invalid");
}
}
/**
* 通过类在上下文栈中获取实例
* Searches the contextual stack from top to bottom for an object of the given class
*/
public <T> T findContextualObject(Class<T> clazz) {
if (contextStack != null) {
ListIterator<Object> iterator = contextStack.listIterator(contextStack.size());
while (iterator.hasPrevious()) {
Object obj = iterator.previous();
if (clazz.isInstance(obj)) {
return clazz.cast(obj);
}
}
}
return null;
}
}
看完解析处理父类Parse的源码后,当前主要的问题就是 saxParser.parse(inputStream, new ParseHandler(this));ParseHandler里面到底怎么处理的, 接下来我们看看ParseHandler的源码就能解决以上的问题了:
public class ParseHandler extends DefaultHandler {
private static Logger log = Logger.getLogger(ParseHandler.class.getName());
protected Parse parse;
protected Locator locator;
protected Stack<Element> elementStack = new Stack<Element>();
public ParseHandler(Parse parse) {
this.parse = parse;
}
//override父类的startElement方法,将解析出的元素入栈
//peek()方法: 查看栈顶对象而不移除它
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
Element element = new Element(uri, localName, qName, attributes, locator);
if (elementStack.isEmpty()) {
parse.rootElement = element;
} else {
elementStack.peek().add(element);//加入子元素,保持xml的树形结构
}
elementStack.push(element);
}
//设置栈顶对象文本
public void characters(char[] ch, int start, int length) throws SAXException {
elementStack.peek().setText(String.valueOf(ch, start, length));
}
//出栈,获取最后加入的元素
public void endElement(String uri, String localName, String qName) throws SAXException {
elementStack.pop();
}
public void error(SAXParseException e) {
parse.addProblem(e);
}
public void fatalError(SAXParseException e) {
parse.addProblem(e);
}
public void warning(SAXParseException e) {
log.warning(e.toString());
}
public void setDocumentLocator(Locator locator) {
this.locator = locator;
}
}
从ParseHandler的实现我们可以看出,这儿是把XML解析出的东西封装到自定义的Element对象之中,然后入栈或者出栈Element元素。
而Element类相关简单,用了一个Map<String, String> attributes把XML对应的Attribute封装,用了一个List<Element> elements把子元素封装。
这时,我们再回到最开始的Configuration中的configurationResource方法
ConfigurationParser.INSTANCE
.createParse()
.configuration(this)
.sourceResource(resourceName)
.execute();
结构就非常清晰了,首先ConfigurationParser.INSTANCE获取了配置解析器ConfigurationParser的单例,然后createParse()方法以ConfigurationParser的单例为参数获取了一个新的配置解析处理ConfigurationParse实例。之后的configuration(this)方法是将Configuration配置对象为参数注入到ConfigurationParse实例中,然后执行ConfigurationParse实例的sourceResource(resourceName)方法通过资源名解析XML资源,这儿ConfigurationParse实例实际上是调用的Parse的sourceResource(resourceName)方法将resourceName解析为ResourceStreamSource获得XML的输入流并注入到ConfigurationParse实例中,最后再执行ConfigurationParse的execute()方法。
在这个execute方法中,首先实行了Parse 的execute方法,将XML资源解析进入了Element值栈中,并在Element中保持了树形结构。然后再执行了parseConfiguration方法:
protected void parseConfiguration() {
//从根元素中取出name和type属性并注入到配置类中
String processManagerFactoryName = rootElement.attribute("name");
if (processManagerFactoryName!=null) {
configuration.setName(processManagerFactoryName);
}
String processManagerFactoryType = rootElement.attribute("type");
if (processManagerFactoryType!=null) {
configuration.setType(processManagerFactoryType);
}
// Handle defaults这是绑定默认处理。当前这个版本这儿只做了一件事情,如果当前是基于SQL的,
//则在Configuration中往configurations这个map中添加了一个键值对,键是PersistenceTypeIsSQL
//,值是Boolean.TRUE
for(ConfigurationBinding binding : bindings) {
if(binding instanceof DefaultProvidingConfigurationBinding) {
((DefaultProvidingConfigurationBinding)binding).supplyDefaults(this);
}
}
// Process the main tags 配置属性解析处理
//对解析出的XML资源元素,获取属于配置的元素,并将名和值放到Configuration中的configurationsmap中
for (Element element: rootElement.elements()) {
Object object = parseObject(element);
if (object!=null) {
String name = element.attribute("name");
configuration.configurationObject(name, object);
}
}
//对配置文件中import的其他配置文件进行读取和处理
for (Element element: rootElement.elements()) {
if ("import".equals(element.getTagName())) {
String resource = element.attribute("resource");
String url = element.attribute("url");
if (resource!=null) {
configurationParser.createParse()
.configuration(configuration)
.sourceResource(resource)
.execute();
} else if (url!=null) {
configurationParser.createParse()
.configuration(configuration)
.sourceUrl(url)
.execute();
}
}
}
}
从以上源代码可以看出,对配置文件主要的解析处理都集中在
// Process the main tags 配置属性解析处理
//对解析出的XML资源元素,获取属于配置的元素,并将名和值放到Configuration中的configurationsmap中
for (Element element: rootElement.elements()) {
Object object = parseObject(element);
if (object!=null) {
String name = element.attribute("name");
configuration.configurationObject(name, object);
}
}
这一块中,我们详细来看看这一块究竟做了些什么。
首先是Object object = parseObject(element);
parseObject方法如下:
protected Object parseObject(Element element) {
ConfigurationBinding binding = getBinding(element);
if (binding!=null) {
return binding.parse(element, this);
}
return null;
}
第一步是将XML资源元素解析为配置绑定ConfigurationBinding ,看看getBinding方法:
protected ConfigurationBinding getBinding(Element element) {
for (ConfigurationBinding binding: bindings) {
if (binding.matches(element, this)) {
return binding;
}
}
return null;
}
我们看到一个bindings集合,这个bindings集合是ConfigurationParse的实例变量,并且置为了常量
protected static final List<ConfigurationBinding> bindings = Arrays.asList(
new ObjectBinding(),
new StringBinding(),
new ListBinding(),
new CommandExecutorBinding(),
new SqlSessionFactoryBinding(),
new PersistenceTypeBinding(),
new TransactionalObjectDescriptorsBinding()
);