activiti 动态配置 activiti 监听引擎启动和初始化(高级源码篇)

1.1.1. 前言

用户故事:现在有这样一个需求,第一个需求:公司的开发环境,测试环境以及线上环境,我们使用的数据库是不一样的,我们必须能够任意的切换数据库进行测试和发布,对数据库连接字符串我们需要加密,保证我们的数据库连接不能被发现。必须确保我们的数据库不能暴露出去,第二个需求,我们需要监控activiti 工作流引擎,在流程启动的之前,我们要保证我们的所有属性都注入进去,如果有些属性没有注入进去,我们是不能让流程启动起来的。也就是进行必要饿属性检测,如果没有期望的属性,直接报错,在流程实例化之后,输出log日志,确保我们的属性都是正确的。并且监控引擎中所有的值

第一个需求如何解决呢?

我们来看一下常规的数据库配置,程序的配置如下:

 <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">

      <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/activiti"/>

        <property name="jdbcDriver" value="com.mysql.jdbc.Driver"/>

        <property name="jdbcUsername" value="root"/>

  <property name="jdbcPassword" value="123"/>

 </bean>

看到上面的配置,是不是似曾相识的感觉,没错就是这么简单,但是如何确保能任意的切换呢,仔细想想还是很简单的,那里变化隔离那里嘛,这个时候我们可以把这些这些变化的东西隔离出去,放到jdbc.properties中,新建jdbc.properties把变化的内容设置进去到jdbc.propertiesjdbc.properties内容如下:

jdbcUrl=jdbc:mysql://localhost:3306/activiti

jdbcDriver=com.mysql.jdbc.Driver

jdbcUsername=root

jdbcPassword=123

上面把变化的东西隔离出来,那程序怎么能够使用jdbc.properties中的配置信息呢?这是一个很关键的问题,我们之前用的activiti版本是5.12,所以当时的设计就是修改源码,主要修改org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl类,在init()方法中我们设计一个钩子函数,然后子类负责实现上面的这些功能,主要就修改的类是org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl,修改ProcessEngineConfigurationImpl类中的数据库连接的信息,由于目前最新的版本activiti 5.19版本,这个版本已经支持了这种实现,具体的实现都差不多,只是我们不用修改源码,直接扩展了,这不就是软件设计中的开闭原则。对新增开放对修改关闭。

下面看下activiti 5.19是如何可以实现这种功能需求的。

1.1.2. activiti 源码

首先看一下org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl类中的init()方法哪个可以实现这种需求,了解底层内核实现才能很方便的扩展嘛。

ProcessEngineConfigurationImpl中的init()部分方法如下:

//初始化configurators集合

initConfigurators();

//调用configurator.beforeInit方法

configuratorsBeforeInit();

...

//调用configurator.configure()方法

configuratorsAfterInit();

上面的三个核心方法实现如下所示:

内部ServiceLoader可以参考java.util.ServiceLoader使用http://blog.csdn.net/qq_30739519/article/details/51164954这篇文章

protected List<ProcessEngineConfigurator> configurators;
protected List<ProcessEngineConfigurator> allConfigurators;
 protected boolean enableConfiguratorServiceLoader = true; // Enabled by default. In certain environments this should be set to false (eg osgi)
  protected void initConfigurators() {
  //初始化数组集合
  allConfigurators = new ArrayList<ProcessEngineConfigurator>();
  
  // Configurators that are explicitely added to the config
//如果集合存在值就循环添加进去
    if (configurators != null) {
      for (ProcessEngineConfigurator configurator : configurators) {
        allConfigurators.add(configurator);
      }
    }
    
    // Auto discovery through ServiceLoader 默认是true
    if (enableConfiguratorServiceLoader) {
    ClassLoader classLoader = getClassLoader();
    if (classLoader == null) {
    classLoader = ReflectUtil.getClassLoader();
    }
    //循环添加进去
    ServiceLoader<ProcessEngineConfigurator> configuratorServiceLoader
    = ServiceLoader.load(ProcessEngineConfigurator.class, classLoader);
    int nrOfServiceLoadedConfigurators = 0;
    for (ProcessEngineConfigurator configurator : configuratorServiceLoader) {
    allConfigurators.add(configurator);
    nrOfServiceLoadedConfigurators++;
    }
    
    if (nrOfServiceLoadedConfigurators > 0) {
    log.info("Found {} auto-discoverable Process Engine Configurator{}", nrOfServiceLoadedConfigurators++, nrOfServiceLoadedConfigurators > 1 ? "s" : "");
    }
    //如果集合不为空 存在多个值的话就按照升序排列 根据什么作为排序规则呢,很显然根据getPriority中的值进行排序 比如a类getPriority 10 b类getPriority 100 那么排序后就是 a,b现执行a类对的再执行b类的。
    if (!allConfigurators.isEmpty()) {
    
    // Order them according to the priorities (usefule for dependent configurator)
    Collections.sort(allConfigurators, new Comparator<ProcessEngineConfigurator>() {
    @Override
    public int compare(ProcessEngineConfigurator configurator1, ProcessEngineConfigurator configurator2) {
    int priority1 = configurator1.getPriority();
    int priority2 = configurator2.getPriority();
    
    if (priority1 < priority2) {
    return -1;
    } else if (priority1 > priority2) {
    return 1;
    } 
    return 0;
    }
});
    
    // Execute the configurators
    log.info("Found {} Process Engine Configurators in total:", allConfigurators.size());
    for (ProcessEngineConfigurator configurator : allConfigurators) {
    log.info("{} (priority:{})", configurator.getClass(), configurator.getPriority());
    }
    
    }
    
    }
  }
  //循环所有allConfigurators 执行beforeInit方法
  protected void configuratorsBeforeInit() {
  for (ProcessEngineConfigurator configurator : allConfigurators) {
  log.info("Executing beforeInit() of {} (priority:{})", configurator.getClass(), configurator.getPriority());
  configurator.beforeInit(this);
  }
  }
  //循环所有allConfigurators 执行configuratorsAfterInit方法
  protected void configuratorsAfterInit() {
  for (ProcessEngineConfigurator configurator : allConfigurators) {
  log.info("Executing configure() of {} (priority:{})", configurator.getClass(), configurator.getPriority());
  configurator.configure(this);
  }
  }


1.1.3. activiti 动态配置实战

好了上面说了源码的实现,下面来点干货吧,看看如何使用自定义的类实现修改配置文件。

我们使用两个类看他们的执行过程是否跟上面源码解析的一直:

定义com.daling.ch1.init.MyProcessEngineConfigurator1如下所示:

@Service
public class MyProcessEngineConfigurator1 extends  AbstractProcessEngineConfigurator{
public static int DEFAULT_CONFIGURATOR_PRIORITY = 100;
public void beforeInit(
ProcessEngineConfigurationImpl processEngineConfiguration) {
System.out.println("1111111111111");
}
 
public void configure(
ProcessEngineConfigurationImpl processEngineConfiguration) {
}
@Override
public int getPriority() {
return DEFAULT_CONFIGURATOR_PRIORITY;
}
 
}
定义com.daling.ch1.init.MyProcessEngineConfigurator2如下所示:
 
@Service
public class MyProcessEngineConfigurator2 extends  AbstractProcessEngineConfigurator{
public static int DEFAULT_CONFIGURATOR_PRIORITY = 200;
public void beforeInit(
ProcessEngineConfigurationImpl processEngineConfiguration) {
System.out.println("2222222222222222");
}
 
public void configure(
ProcessEngineConfigurationImpl processEngineConfiguration) {
}
@Override
public int getPriority() {
return DEFAULT_CONFIGURATOR_PRIORITY;
}
 
}


在spring中开启 包的扫描代码: <context:component-scan base-package="com"/> 

activiti 引擎配置如下所示:

 <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">

        <property name="jdbcUrl" value="jdbc:mysql://localhost3306/activiti"></property>

        <property name="jdbcDriver" value="com.mysql.jdbc.Driver"/>

        <property name="jdbcUsername" value="root"/>

        <property name="jdbcPassword" value="a"/>

    

    <property name="configurators">

    <list>

   <bean class="com.daling.ch1.init.MyProcessEngineConfigurator2"></bean>

     <bean class="com.daling.ch1.init.MyProcessEngineConfigurator1"></bean>

    </list>

    </property>

<property name="activityFontName" value="宋体"></property>

   </bean>

测试代码如下:

ProcessEnginesDemodemo =new ProcessEnginesDemo();

RepositoryServicerepositoryService2 =demo.getRepositoryService();

先来运行一下程序看输出是否正确,部分输出如下所示:

1111111111111

20:20:18.245 [main] INFO  o.a.e.i.c.ProcessEngineConfigurationImpl - Executing beforeInit() of class com.daling.ch1.init.MyProcessEngineConfigurator2 (priority:200)

2222222222222222

确实说明了程序的执行顺序就是按照getPriority()方法值升序执行的。

ProcessEngineConfigurator类的定义如下:

public interface ProcessEngineConfigurator {
/**
 * Called <b>before</b> any initialisation has been done.
 * This can for example be useful to change configuration settings
 * before anything that uses those properties is created.
 *
 * Allows to tweak the process engine by passing the {@link ProcessEngineConfigurationImpl}
   * which allows tweaking it programmatically.
 *
 * An example is the jdbc url. When a {@link ProcessEngineConfigurator} instance
 * wants to change it, it needs to do it in this method, or otherwise
 * the datasource would already have been created with the 'old' value
 * for the jdbc url.
 */
void beforeInit(ProcessEngineConfigurationImpl processEngineConfiguration);
  
  /**
   * Called when the engine boots up, before it is usable, but after
   * the initialisation of internal objects is done.
   *  
   * Allows to tweak the process engine by passing the {@link ProcessEngineConfigurationImpl}
   * which allows tweaking it programmatically.
   *
   * An example is the ldap user/group manager, which is an addition to the engine.
   * No default properties need to be overridden for this (otherwise the {@link #beforeInit(ProcessEngineConfigurationImpl)} 
   * method should be used) so the logic contained in this method is executed
   * after initialisation of the default objects.
   *
   * Probably a better name would be 'afterInit' (cfr {@link #beforeInit(ProcessEngineConfigurationImpl)}),
   * but not possible due to backwards compatibility.
   */
  void configure(ProcessEngineConfigurationImpl processEngineConfiguration);
  
  /**
   * When the {@link ProcessEngineConfigurator} instances are used, they are first
   * ordered by this priority number (lowest to highest).
   * If you have dependencies between {@link ProcessEngineConfigurator}
   * instances, use the priorities accordingly to order them as needed.
   */
  int getPriority();
 
}


ProcessEngineConfigurator类图关系如下:

 activiti 动态配置 activiti 监听引擎启动和初始化(高级源码篇)

 

上面的类图AbstractProcessEngineConfigurator类实现了ProcessEngineConfigurator接口,AbstractProcessEngineConfigurator是一个抽象类,所以我们使用的时候继承AbstractProcessEngineConfigurator类复写里面的方法即可(模板方法)

上面我们自定义的类MyProcessEngineConfigurator1中的方法

public void beforeInit(

ProcessEngineConfigurationImplprocessEngineConfiguration) {

System.out.println("1111111111111");

}

 

public void configure(

ProcessEngineConfigurationImplprocessEngineConfiguration) {

}

我们可以拿到ProcessEngineConfigurationImpl这个对象其实就是StandaloneProcessEngineConfiguration的父类,里面有很多的属性我们就可以修改了。

怎么修改呢?只要拿到这个对象我们就很方便修改了。

具体的修改如下:

//伪代码 加载jdbc.properties文件 获取配置的值,进行解密然后设置进去 解密算法后续讲解,当然懂的话可以自己直接修改。

descJdbc(ProperUtils.load());

processEngineConfiguration.setJdbcDriver(jdbcDriver);

processEngineConfiguration.setJdbcPassword(jdbcPassword);

processEngineConfiguration.setJdbcUrl(jdbcUrl);

processEngineConfiguration.setJdbcUsername(jdbcUsername)

 

上面提到的引擎初始化完毕会调用configure()中的方法,所以这里可以自定义实现的功能即可。


上一篇:演示:思科IPS传感器的命令行初始配置(支持图型化管理)


下一篇:基于自定义配置文件初始化ElasticSearch客户端方法介绍