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"/>
"/>
</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()中的方法,所以这里可以自定义实现的功能即可。
activiti 动态配置 activiti 监听引擎启动和初始化(高级源码篇)的更多相关文章
- 深入浅出Mybatis系列(三)---配置详解之properties与environments(mybatis源码篇)
上篇文章<深入浅出Mybatis系列(二)---配置简介(mybatis源码篇)>我们通过对mybatis源码的简单分析,可看出,在mybatis配置文件中,在configuration根 ...
- 深入浅出Mybatis系列三-配置详解之properties与environments(mybatis源码篇)
注:本文转载自南轲梦 注:博主 Chloneda:个人博客 | 博客园 | Github | Gitee | 知乎 上篇文章<深入浅出Mybatis系列(二)---配置简介(mybatis源码篇 ...
- Activiti 流程部署方式 activi 动态部署(高级源码篇)
Activiti的流程 部署方式有很多种方式,我们可以根据activit工作流引擎提供的ap方式进行部署. 当然了实际需求决定你要使用哪一种api操作,后面的总结详细介绍了使用场景. 下面看一下部署方 ...
- 深入浅出Mybatis系列(四)---配置详解之typeAliases别名(mybatis源码篇)
上篇文章<深入浅出Mybatis系列(三)---配置详解之properties与environments(mybatis源码篇)> 介绍了properties与environments, ...
- 深入浅出Mybatis系列四-配置详解之typeAliases别名(mybatis源码篇)
注:本文转载自南轲梦 注:博主 Chloneda:个人博客 | 博客园 | Github | Gitee | 知乎 上篇文章<深入浅出Mybatis系列(三)---配置详解之properties ...
- 配置静态监听解决ORA-12514错误的案例(转)
今天做Linux下DG配置的时候,遇到一个现象,tnsname.ora文件配置都正常,tnsping也正常,监听也正常,但是仍然报ORA-12514错误: SQL> set lin 130 ...
- 配置静态监听解决ORA-12514错误的案例
今天做Linux下DG配置的时候,遇到一个现象.tnsname.ora文件配置都正常,tnsping也正常,监听也正常.可是仍然报ORA-12514错误: SQL> set lin 130 pa ...
- Tomcat介绍、安装jdk、安装Tomcat、配置Tomcat监听80端口
1.Tomcat介绍 2.安装jdk下载:wget -c http://download.oracle.com/otn-pub/java/jdk/10.0.1+10/fb4372174a714e6b8 ...
- 配置Tomcat监听80端口、配置Tomcat虚拟主机、Tomcat日志
6月27日任务 16.4 配置Tomcat监听80端口16.5/16.6/16.7 配置Tomcat虚拟主机16.8 Tomcat日志扩展邱李的tomcat文档 https://www.linuser ...
随机推荐
- 第三届“百越杯”福建省高校网络空间安全大赛_Do you know upload?
题目在i春秋的ctf训练营 既然是上传,那就直接抓包 二话不说上来先给个00截断传个一句话助助兴 直接就成功了.... 赶紧操起菜刀去连接 进去之后发现ctf.sql是个空文件,那么flag应该在数据 ...
- Redis常用命令--Hashes
Hash是由键值对组成的map.Hashes的底层是通过字典实现的.一个哈希表里面可以有多个哈希表节点.而每个哈希节点就保存了字典中的一个键值对. 字典是一种用于保存键和值对的抽象数据结构.字典里的每 ...
- POI ZAW
要求一个最短路,担心的就是一条边被正反经过两次. 规定第一步为1到i,并把这条边设为不可经过.然后从i做最短路到1,因为这个过程是不会经历重边的(如果经历了就不是最短路了). 求最短路用SPFA,但常 ...
- ●BZOJ 4278 [ONTAK2015]Tasowanie
题链: http://www.lydsy.com/JudgeOnline/problem.php?id=4278 题解: 把两个串连接起来,用一个大数连接(必须要用大数).倍增算法求出后缀排名.然后两 ...
- NOIP提高组2010 乌龟棋
小明过生日的时候,爸爸送给他一副乌龟棋当作礼物. 乌龟棋的棋盘是一行N个格子,每个格子上一个分数(非负整数).棋盘第1格是唯一的起点,第N格是终点,游戏要求玩家控制一个乌龟棋子从起点出发走到终点. 乌 ...
- hdu 5119(2014北京)
题意: 随机选择一个数,如果后面有比他小的就进行交换,直到没有为止(算一轮).求多少轮后为递增序列 思路: 倒着找,如果有比经过的最小数大的,ans+1 #include <iostream&g ...
- [APIO2009]
来自FallDream的博客,不经允许,请勿转载,谢谢. ------------------------------------------------------ 1.Oil 给定一个n*m的矩阵 ...
- bzoj1877
1877: [SDOI2009]晨跑 Time Limit: 4 Sec Memory Limit: 64 MBSubmit: 2660 Solved: 1424[Submit][Status][ ...
- OCP 认证考试报名费技巧题库051052053解析合格线
本人于2017年4月22日通过参加OCP考试,第一次参加,一天之内考了三门,三门一次性通过,052 - 95% ,053 - 86% ,051 - 100% 一.关于考试考试报名费: 052:158$ ...
- jmeter正则表达式书写
在测试过程中,经常会有以下几种场景,如A接口的返回值,用于B接口中,而且A登陆的账户,每次登陆,这个sid值还是变化的.那么在实际工作中,如何才能A接口中提取参数到B接口中?接下来我们就可以用正则表达 ...