本文节选自《疯狂工作流讲义(第2版)》
疯狂Activiti电子书:https://my.oschina.net/JavaLaw/blog/1570397
工作流Activiti教学视频:https://my.oschina.net/JavaLaw/blog/1577577
Activiti整合Spring
Activiti的配置文件就是一份Spring配置文件,但是默认情况下,只将ProcessEngineConfiguration作为一个bean来读取和使用,如果想使用Spring的更多功能(例如声明式事务管理),可以与Spring容器进行进一步的整合,将ProcessEngineConfiguration和其他的对象都交由Spring进行管理,Spring容器中的其他组件,可以通过依赖注入的方式来使用Activiti的这些对象,例如前面章节讲述的多个Service。
SpringProcessEngineConfiguration
一个ProcessEngineConfiguration实例表示流程引擎的配置,Activiti提供了StandaloneProcessEngineConfiguration、StandaloneInMemProcessEngineConfiguration等类,在流程引擎的配置文件中,可以使用这些子类作为bean的class,这些类的描述见本书的流程引擎配置章节。
如果需要和Spring整合,就要使用Activiti的Spring模块中的SpringProcessEngineConfiguration类作为流程引擎配置类, 该类类继承ProcessEngineConfigurationImpl,在配置这个类时,可以为ProcessEngineConfiguration注入TransactionManager,也可以在配置中指定自动部署的资源文件及部署模式,代码清单16-6为SpringProcessEngineConfiguration的部分源代码。
代码清单16-6:org.activiti.spring.SpringProcessEngineConfiguration
public class SpringProcessEngineConfiguration extends ProcessEngineConfigurationImpl { protected PlatformTransactionManager transactionManager; protected String deploymentName = "SpringAutoDeployment"; protected Resource[] deploymentResources = new Resource[0]; @Override public ProcessEngine buildProcessEngine() { ProcessEngine processEngine = super.buildProcessEngine(); ProcessEngines.setInitialized(true); autoDeployResources(processEngine); return processEngine; } ...省略其他源代码 }
SpringProcessEngineConfiguration中有一个transactionManager属性,使用这个类作为流程引擎bean的class,可以在配置文件中指定TransactionManager,另外还有一个deploymentResources属性,可以为流程引擎的bean指定流程文件资源。
如果Activiti不与Spring进行整合,那么默认情况下,将会使用myBatis的事务管理,使用了SpringProcessEngineConfiguration后,在配置中必须指定一个Spring的TransactionManager,SpringProcessEngineConfiguration的配置如代码清单16-7所示。
代码清单16-7:codes\16\16.2\integrate-spring\resource\activiti.cfg.xml
<!-- 配置数据源 --> <bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource"> <property name="driverClass" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/act" /> <property name="username" value="root" /> <property name="password" value="123456" /> </bean> <!-- 配置事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!-- 流程引擎的配置bean --> <bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration"> <property name="dataSource" ref="dataSource"/> <property name="databaseSchemaUpdate" value="true" /> <property name="transactionManager" ref="transactionManager"/> <property name="deploymentResources" value="bpmn/EngineConfigurationTest.bpmn" /> </bean>
在代码清单16-7中,配置了三个bean,其中使用了Spring的数据源来设置“dataSource”bean,使用了Spring的DataSourceTransactionManager作为事务管理器,在事务管理器中注入了数据源,在配置processEngineConfiguration时,将数据源和事务管理器注入。
本例的数据源使用了Spring的SimpleDriverDataSource,在实际应用中,可以选择C3P0、DBCP等数据源连接池,如果业务系统中同时使用了Hibernate框架,可以再加入SessionFactory等配置。
在本例中,还为processEngineConfiguration注入了deploymentResources属性,将一份流程文件交给SpringProcessEngineConfiguration,让其自动进行流程文件部署,而不需要再向前面章节那样,通过编码的方式进行流程文件部署,如果需要部署classpath下的多个流程文件,可以使用“value="classpath*:/bpmn/*.bpmn”这样的配置,如果使用了多份已知的流程文件,也可以使用以下的配置:
<property name="deploymentResources"> <list> <value>/bpmn/EngineConfigurationTest.bpmn</value> <value>/bpmn/EngineConfigurationTest2.bpmn</value> </list> </property>
对于一些流程较为固定的系统,可以使用配置deploymentResources这种方式来加载流程文件,而对于一些流程多变的系统,笔者建议还是通过编码的方式来加载,流程文件的路径或者内容均可以作为加载方法的参数,实现动态加载。
资源的部署模式
根据前面小节可知,在流程引擎配置时可加入deploymentResources配置,让Spring容器在初始化时自动帮我们部署指定的流程文件,如果希望对部署模式进行配置,可以使用deploymentMode属性。该属性可配置为以下值:
- default:默认值,在进行自动部署时,全部的资源将会被看作一次部署,只产生一条件部署数据(Depoyment)。
- single-resource:每一个资源文件都会进行单独一次部署,多个文件将会产生多条部署数据。
- resource-parent-folder:根据每个资源文件的所在目录作为一次部署,例如某个目录下有多份资源文件,那么这些资源文件将会被当作一次部署,产生一条件部署数据。
ProcessEngineFactoryBean
前面的章节中,一般情况使用ProcessEngines的getDefaultProcessEngine方法得到默认的ProcessEngine实例。如果将创建ProcessEngine的过程也交由Spring容器代为完成,可以使用ProcessEngineFactoryBean。ProcessEngineFactoryBean也是Activiti的Spring模块提供的类,主要用于维护一个ProcessEngine实例,向ProcessEngineFactoryBean设置(注入)一个流程引擎配置(ProcessEngineConfiguration)实例,它就会自动创建ProcessEngine。在得到Spring上下文后,通过getBean方法得到ProcessEngineFactoryBean的bean,实际上得到的是一个ProcessEngine实例,这是由于ProcessEngineFactoryBean类实现了FactoryBean接口,在getBean时会调用FactoryBean的getObject方法返回一个具体的bean实例,而ProcessEngineFactoryBean的getObject方法,返回的是一个ProcessEngine实例。代码清单16-8为ProcessEngineFactoryBean的配置。
代码清单16-8:codes\16\16.2\integrate-spring\resource\activiti.cfg.xml
<!-- 流程引擎的bean --> <bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean"> <property name="processEngineConfiguration" ref="processEngineConfiguration" /> </bean>
需要注意的是,processEngine的bean的class并不是ProcessEngine的实现类,通过getBean方法拿到的,才是一个ProcessEngine实例。在16.2.1小节,配置了processEngineConfiguration的bean,在本小节配置了processEngine的bean,那么启动流程引擎的话,就可以直接使用Spring的方式,如代码清单16-9所示。
代码清单16-9:
codes\16\16.2\integrate-spring\src\org\crazyit\activiti\EngineConfigurationTest.java
public static void main(String[] args) { // 创建Spring上下文 ApplicationContext ctx = new ClassPathXmlApplicationContext( new String[] { "activiti.cfg.xml" }); // 获取processEngine bean ProcessEngine engine = (ProcessEngine)ctx.getBean("processEngine"); System.out.println("流程引擎实现类:" + engine.getClass().getName()); }
代码清单16-9,像普通的Spring应用一样,创建Spring上下文,将Activiti的配置文件当作一份普通的Spring配置文件,再获取processEngine的bean,本例中输出ProcessEngine以及TaskService的实例,运行代码清单16-9,结果如下:
流程引擎实现类:org.activiti.engine.impl.ProcessEngineImpl
在bean中注入Activiti服务
通过上一小节,将ProcesseEngine交由Spring的IoC容器进行管理,那Activiti的服务同样可以交由Spring进行管理。Activiti的各个服务组件,均在ProcessEngineConfigurationImpl进行创建(使用new关键字),通过ProcesseEngine对象的getXXXService可以得到这些服务的实例。因为这些服务对象的创建由Activiti完成,所以Spring并不需要管理它们的创建过程,代码清单16-10中配置了这些服务对象的bean。
代码清单16-10:codes\16\16.2\integrate-spring\resource\activiti.cfg.xml
<!-- 服务组件的bean --> <bean id="repositoryService" factory-bean="processEngine" factory-method="getRepositoryService" /> <bean id="runtimeService" factory-bean="processEngine" factory-method="getRuntimeService" /> <bean id="taskService" factory-bean="processEngine" factory-method="getTaskService" /> <bean id="historyService" factory-bean="processEngine" factory-method="getHistoryService" /> <bean id="managementService" factory-bean="processEngine" factory-method="getManagementService" />
根据代码清单16-10可知,各个Activiti服务组件,通过实例工厂方法创建bean,此时各个bean不再设置class属性,而由factory-bean属性来指定工厂bean的id,并且使用factory-method确定产生bean实例的工厂方法。使用这种模式,bean的创建不再由Spring完成,Spring只会负责调用指定的工厂方法来创建实例,Spring会对这些实例进行管理。
在Spring中配置好这些bean后,如果要在自己的业务bean中使用Activiti的这些服务,直接使用依赖注入就可以实现,如:
<bean id="myService" class="myClass"> <property name="runtimeService" ref="runtimeService"></property> </bean>
当然,还需要在业务类中为相应的Activiti服务对象加入setter方法。
在Activiti中使用Spring的bean
在其他的业务bean中可以使用Activiti的服务,那么在流程元素中,同样可以使用这些业务bean的方法。根据前面章节可知,Activiti中的Service Task、任务监听器和流程监听器等,可以使用JUEL表达式来调用某个类的方法,同样的,也可以使用JUEL表达式来调用bean的方法,但前提是要让Activiti知道这些bean的存在。代码清单16-11是一份整合了Activiti的Spring配置文件内容。
代码清单16-11:codes\16\16.2\integrate-spring\resource\activiti.use.bean.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 配置数据源 --> <bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource"> <property name="driverClass" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/act" /> <property name="username" value="root" /> <property name="password" value="123456" /> </bean> <!-- 配置事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!-- 流程引擎的配置bean --> <bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration"> <property name="dataSource" ref="dataSource" /> <property name="databaseSchemaUpdate" value="true" /> <property name="transactionManager" ref="transactionManager" /> <!-- 向processEngineConfiguration注入bean --> <property name="beans"> <map> <entry key="myService" value-ref="myService" /> </map> </property> </bean> <!-- 流程引擎的bean --> <bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean"> <property name="processEngineConfiguration" ref="processEngineConfiguration" /> </bean> <!-- 服务组件的bean --> <bean id="repositoryService" factory-bean="processEngine" factory-method="getRepositoryService" /> <bean id="runtimeService" factory-bean="processEngine" factory-method="getRuntimeService" /> <bean id="taskService" factory-bean="processEngine" factory-method="getTaskService" /> <bean id="historyService" factory-bean="processEngine" factory-method="getHistoryService" /> <bean id="managementService" factory-bean="processEngine" factory-method="getManagementService" /> <!-- 业务组件bean --> <bean id="myService" class="org.crazyit.activiti.spring.impl.MyServiceImpl"></bean> </beans>
除了前面所介绍的几个bean外,代码清单16-11中的粗体字代码,为processEngineConfiguration注入了beans属性,beans属性是ProcessEngineConfigurationImpl类的一个属性,类型是Map<Object, Object>,在本例的配置文件中,为其注入一个叫做myService的bean,myService是一个普通的业务组件,本例中它只提供了一个业务方法,MyServiceImpl的实现如代码清单16-12所示。
代码清单16-12:
codes\16\16.2\integrate-spring\src\org\crazyit\activiti\spring\impl\MyServiceImpl.java
public class MyServiceImpl implements MyService { public void serviceMethod(String name) { System.out.println("MyService的实现类处理业务方法:" + name); } }
MyServiceImpl类中只有一个serviceMethod方法,有一个name参数,该方法只会在控制台输出一句话。新建一个简单的流程,该流程中只含有一个Service Task,流程文件内容如代码清单16-13所示。
代码清单16-13:codes\16\resource\bpmn\ActivitiUseBean.bpmn
<process id="process1" name="process1"> <startEvent id="startevent1" name="Start"></startEvent> <serviceTask id="servicetask1" name="Service Task" activiti:expression="#{myService.serviceMethod(name)}"></serviceTask> <endEvent id="endevent1" name="End"></endEvent> <sequenceFlow id="flow1" name="" sourceRef="startevent1" targetRef="servicetask1"></sequenceFlow> <sequenceFlow id="flow2" name="" sourceRef="servicetask1" targetRef="endevent1"></sequenceFlow> </process>
注意代码清单16-13中的粗体字代码,在流程中定义了一个Service Task,这个Service没有配置activiti:class属性,而使用了activiti:expresson属性,值为#{myService.serviceMethod(name)},表示使用名称为myService的bean的serviceMethod方法,参数为name,这里name参数是流程参数。代码清单16-14为运行代码。
代码清单16-14:codes\16\16.2\integrate-spring\resource\bpmn\ActivitiUseBean.bpmn
ApplicationContext ctx = new ClassPathXmlApplicationContext( new String[] { "activiti.use.bean.xml" }); // 得到Activiti的服务组件 RepositoryService repositoryService = (RepositoryService) ctx .getBean("repositoryService"); RuntimeService runtimeService = (RuntimeService) ctx .getBean("runtimeService"); // 部署流程文件 repositoryService.createDeployment() .addClasspathResource("bpmn/ActivitiUseBean.bpmn").deploy(); // 初始化流程参数 Map<String, Object> vars = new HashMap<String, Object>(); vars.put("name", "crazyit"); // 启动流程 runtimeService.startProcessInstanceByKey("process1", vars);
本例的运行代码,与前面章节的Activiti运行代码类似,只是ProcessEngine的创建由Spring完成,本例中直接使用ApplicationContext的getBean方法就可以拿到Activiti的服务组件,流程文件部署、流程参数设置和流程启动均与前面章节的案例一致。代码清单16-14中的粗体字代码,启动流程,并设置流程参数,参数名为“name”,值为“crazyit”,运行代码清单16-14,输出结果如下:
MyService的实现类处理业务方法:crazyit。
根据结果可知,Activiti流程在运行时,调用了Spring的bean方法。除了Service Task外,流程监听器和任务监听器等地方,均可以使用这种方式来调用bean的业务方法。
本文节选自《疯狂工作流讲义(第2版)》
疯狂Activiti电子书:https://my.oschina.net/JavaLaw/blog/1570397
工作流Activiti教学视频:https://my.oschina.net/JavaLaw/blog/1577577
本书代码目录:https://gitee.com/yangenxiong/CrazyActiviti

