activiti5.0.alpha1之配置文件的结构及功能解析

本文深入剖析了Activiti工作流引擎的配置文件解析过程,包括Configuration类的核心功能、配置文件生成方式及解析处理机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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()
  );

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值