跟踪activiti创建流程引擎源码

    默认情况下activiti是通过XML文件activiti.cfg.xml来配置Activiti流程引擎,当然如果整合了spring的话,这种做法不太适合了。

   获取流程引擎ProcessEngine最简单的方法就是借助工具类 org.activiti.engine.ProcessEngines来创建默认的流程引擎。

  ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine()
   跟踪源码,看看内部如何实现的。
   首先查找方法getDefaultProcessEngine,看到本方法内调用了类内部方法getProcessEngine来实现。

  public static ProcessEngine getDefaultProcessEngine() {
      return getProcessEngine(NAME_DEFAULT);
  }

   继续跟踪内部方法getProcessEngine 

   /** obtain a process engine by name.  
   * @param processEngineName is the name of the process engine or null for the default process engine.  */
   public static ProcessEngine getProcessEngine(String processEngineName) {
       if (!isInitialized) {
          init();
       }
       return processEngines.get(processEngineName);
   }
  关键方法init 
   /** Initializes all process engines that can be found on the classpath for
   * resources <code>activiti.cfg.xml</code> (plain Activiti style configuration)
   * and for resources <code>activiti-context.xml</code> (Spring style configuration). */
 初始化所有的在classpath路径上资源(activiti风格的文件activiti.cfg.xml)和资源(spring风格的文件activiti-context.xml)中搜索到的流程引擎
    public synchronized static void init() {
        if (!isInitialized) {
            if(processEngines == null) {
                // Create new map to store process-engines if current map is null
                processEngines = new HashMap<String, ProcessEngine>();        
            }
            ClassLoader classLoader = ReflectUtil.getClassLoader();
      Enumeration<URL> resources = null;
      try {
        resources = classLoader.getResources("activiti.cfg.xml");
      } catch (IOException e) {
        throw new ActivitiException("problem retrieving activiti.cfg.xml resources on the classpath: "+System.getProperty("java.class.path"), e);
      }
      
      // Remove duplicated configuration URL's using set. Some classloaders may return identical URL's twice, causing duplicate startups
      Set<URL> configUrls = new HashSet<URL>();
      while (resources.hasMoreElements()) {
        configUrls.add( resources.nextElement() );
      }
      for (Iterator<URL> iterator = configUrls.iterator(); iterator.hasNext();) {
        URL resource = iterator.next();
        initProcessEnginFromResource(resource);
      }
      
      try {
        resources = classLoader.getResources("activiti-context.xml");
      } catch (IOException e) {
        throw new ActivitiException("problem retrieving activiti-context.xml resources on the classpath: "+System.getProperty("java.class.path"), e);
      }
      while (resources.hasMoreElements()) {
        URL resource = resources.nextElement();
        initProcessEngineFromSpringResource(resource);
      }

      isInitialized = true;
    } else {
      log.info("Process engines already initialized");
    }
  }

    如果没有初始化
       通过类加载器获取activiti.cfg.xml的资源
       借助Set的防重性,去掉重复的资源
       遍历activiti.cfg.xml资源,调用方法initProcessEnginFromResource初始化流程引擎,待会详细说明此方法
       
       通过类加载器获取activiti-context.xml
       遍历资源,调用方法initProcessEngineFromSpringResource,来初始化流程引擎。

       设置初始化标识
     如果已初始化,log记录。
分析: 从activiti风格的文件activiti.cfg.xml或者spring风格的文件activiti-context.xml来加载流程引擎,两种方式任何一种均可完成。
疑问: 为什么文件activiti.cfg.xml资源需要去重,而文件activiti-context.xml不需要防重。
  源码描述如下:// Remove duplicated configuration URL's using set. Some classloaders may return identical URL's twice, causing duplicate startups

    接着跟踪方法initProcessEnginFromResource和initProcessEngineFromSpringResource
 
  a initProcessEnginFromResource

private static ProcessEngineInfo initProcessEnginFromResource(URL resourceUrl) {
    ProcessEngineInfo processEngineInfo = processEngineInfosByResourceUrl.get(resourceUrl);
    // if there is an existing process engine info
    if (processEngineInfo!=null) {
      // remove that process engine from the member fields
      processEngineInfos.remove(processEngineInfo);
      if (processEngineInfo.getException()==null) {
        String processEngineName = processEngineInfo.getName();
        processEngines.remove(processEngineName);
        processEngineInfosByName.remove(processEngineName);
      }
      processEngineInfosByResourceUrl.remove(processEngineInfo.getResourceUrl());
    }

    String resourceUrlString = resourceUrl.toString();
    try {
      log.info("initializing process engine for resource " + resourceUrl);
      ProcessEngine processEngine = buildProcessEngine(resourceUrl);
      String processEngineName = processEngine.getName();
      log.info("initialised process engine " + processEngineName);
      processEngineInfo = new ProcessEngineInfoImpl(processEngineName, resourceUrlString, null);
      processEngines.put(processEngineName, processEngine);
      processEngineInfosByName.put(processEngineName, processEngineInfo);
    } catch (Throwable e) {
      log.log(Level.SEVERE, "Exception while initializing process engine :" + e.getMessage(), e);
      processEngineInfo = new ProcessEngineInfoImpl(null, resourceUrlString, getExceptionString(e));
    }
    processEngineInfosByResourceUrl.put(resourceUrlString, processEngineInfo);
    processEngineInfos.add(processEngineInfo);
    return processEngineInfo;
  }
    总体来说比较好理解,先查找缓存map中是否存在此url资源,如果存在就清理掉,然后根据新传来的url来获取流程引擎,并缓存起来。我们可以注意到方法buildProcessEngine(String urlString),
 private static  ProcessEngine buildProcessEngine(URL resource) {
    InputStream inputStream = null;
    try {
      inputStream = resource.openStream();
      ProcessEngineConfiguration processEngineConfiguration = ProcessEngineConfiguration.createProcessEngineConfigurationFromInputStream(inputStream);
      return processEngineConfiguration.buildProcessEngine();
      
    } catch (IOException e) {
      throw new ActivitiException("couldn't open resource stream: "+e.getMessage(), e);
    } finally {
      IoUtil.closeSilently(inputStream);
    }
  }

  

    借助类ProcessEngineConfiguration的方法createProcessEngineConfigurationFromInputStream来完成资源的读取。

public static ProcessEngineConfiguration createProcessEngineConfigurationFromInputStream(InputStream inputStream) {
    return createProcessEngineConfigurationFromInputStream(inputStream, "processEngineConfiguration");
  }

  public static ProcessEngineConfiguration createProcessEngineConfigurationFromInputStream(InputStream inputStream, String beanName) {
    return BeansConfigurationHelper.parseProcessEngineConfigurationFromInputStream(inputStream, beanName);
  }
  马上就要揭开庐山真面目了。
public static ProcessEngineConfiguration parseProcessEngineConfiguration(Resource springResource, String beanName) {
    DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
    XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
    xmlBeanDefinitionReader.setValidationMode(XmlBeanDefinitionReader.VALIDATION_XSD);
    xmlBeanDefinitionReader.loadBeanDefinitions(springResource);
    ProcessEngineConfigurationImpl processEngineConfiguration = (ProcessEngineConfigurationImpl) beanFactory.getBean(beanName);
    processEngineConfiguration.setBeans(new SpringBeanFactoryProxyMap(beanFactory));
    return processEngineConfiguration;
  }

  public static ProcessEngineConfiguration parseProcessEngineConfigurationFromInputStream(InputStream inputStream, String beanName) {
    Resource springResource = new InputStreamResource(inputStream);
    return parseProcessEngineConfiguration(springResource, beanName);
  }

  public static ProcessEngineConfiguration parseProcessEngineConfigurationFromResource(String resource, String beanName) {
    Resource springResource = new ClassPathResource(resource);
    return parseProcessEngineConfiguration(springResource, beanName);
  }

   从上面的代码可获知,真正读取文件的方式,是使用spring的beanFacotry来完成的。相信大家读到这里已经明白了activiti资源的加载过程。

  b spring风格的方式 initProcessEngineFromSpringResource
 
protected static void initProcessEngineFromSpringResource(URL resource) {
    try {
      Class< ? > springConfigurationHelperClass = ReflectUtil.loadClass("org.activiti.spring.SpringConfigurationHelper");
      Method method = springConfigurationHelperClass.getMethod("buildProcessEngine", new Class<?>[]{URL.class});
      ProcessEngine processEngine = (ProcessEngine) method.invoke(null, new Object[]{resource});
      
      String processEngineName = processEngine.getName();
      ProcessEngineInfo processEngineInfo = new ProcessEngineInfoImpl(processEngineName, resource.toString(), null);
      processEngineInfosByName.put(processEngineName, processEngineInfo);
      processEngineInfosByResourceUrl.put(resource.toString(), processEngineInfo);
      
    } catch (Exception e) {
      throw new ActivitiException("couldn't initialize process engine from spring configuration resource "+resource.toString()+": "+e.getMessage(), e);
    }
  }

  写到这里,我们来尝试解决刚才提到的疑问。activiti风格的实现并不是程序启动的时候,把所有配置文件加载起来了,更像是调用静态方法,主动加载资源,假设某些类加载器可能对同一个url返回两次,那么主动加载两次就会引起重复加载资源,进而导致流程引擎启动两次。而spring风格的方式是从applicationContext上下文中来获取bean的,是程序启动加载的时候,已经把这些配置文件分析并读取完毕,只等着调用方来使用。另外spring配置的bean默认情况下均是单例,获取的bean也是同一个。所以spring风格的资源才没有过滤吧。

   通过阅读源码,有很多值得我们学习的地方,首先是编写可阅读,可维护的代码。比如上述的类及方法,每个方法基本只做属于自己的事情-单一职能,方法尽量短小。返回接口或抽象类等等;其次是记录认为处理比较好的方法,比如加载并解析文件等功能。

   要勤于思考,勤于总结。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值