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"/>
"/>
</bean>
看到上面的配置,是不是似曾相识的感觉,没错就是这么简单,但是如何确保能任意的切换呢,仔细想想还是很简单的,那里变化隔离那里嘛,这个时候我们可以把这些这些变化的东西隔离出去,放到jdbc.properties中,新建jdbc.properties把变化的内容设置进去到jdbc.properties,jdbc.properties内容如下:
jdbcUrl=jdbc:mysql://localhost:3306/activiti
jdbcDriver=com.mysql.jdbc.Driver
jdbcUsername=root
jdbcPassword=
上面把变化的东西隔离出来,那程序怎么能够使用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类图关系如下:
上面的类图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()中的方法,所以这里可以自定义实现的功能即可。