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 ...
随机推荐
- python包安装和使用机制
python语言的魅力之一就是大量的外置数据包,能够帮助使用者节省很多时间,提高效率.模块下载和引用是最常见的操作,现在解析内部的原理和背后发生的故事,做到心里有数. 导航: 基本定义 模块使用 模块 ...
- Canvas - 时钟绘制
导语:距离上一次写canvas,已经过去两年半,如今业务需要,再次拾起,随手记录. [思考] 时钟的绘制主要在于圆的绘制:1. 使用context.arc()方法直接绘制圆或圆弧: 2. 使用圆的方程 ...
- 2018.4.16Spring.Net入门学习内容
三大方面: IoC:Inversion of Control 控制翻转:就是创建对象的权利由开发人员自己控制New,转到了由容器来控制. DI:Dependency InjectionIt is a ...
- 报错pymysql.err.DataError: (1406, "Data too long for column 'gender' at row 1")
在Django默认的admin后台创建超级用户时, 报错pymysql.err.DataError: (1406, "Data too long for column 'gender' at ...
- [Luogu 2817]宋荣子的城堡
Description saruka有一座大大的城堡!城堡里面有n个房间,每个房间上面都写着一个数字p[i].有一天,saruka邀请他的小伙伴LYL和 MagHSK来城堡里玩耍(为什么没有妹子),他 ...
- bzoj 4945: [Noi2017]游戏
Description Solution 首先我们发现一个位置如果不是 \('x'\),那么就只有两种选择 而 \('x'\) 的个数小于等于 \(8\),直接枚举是哪个就好了 然后就是 \(2-sa ...
- hdu 3436 线段树 一顿操作
Queue-jumpers Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) To ...
- iOS 搜索记录
需求描述: 使用单独的搜索界面, 提供用户进行搜索并留下搜索记录. 搜索记录可以提供用户进行再次搜索, 或者把搜索记录清空. 方案和技术点: 存储方式使用 NSUserDefaults, 把对应的字段 ...
- sqlserver 按照特定值排序查询结果
select * from t_ss_student order by case when xm like '林%' then 1 else 2 end asc; 姓林的会排在前面
- 优化Webpack打包速度
1. Webpack 可以配置 externals 来将依赖的库指向全局变量,从而不再打包这个库,比如对于这样一个文件: import React from 'react'; console.lo ...