疯狂Activiti6.0连载(19)Activiti整合Spring


声明:本文转载自https://my.oschina.net/JavaLaw/blog/1580574,转载目的在于传递更多信息,仅供学习交流之用。如有侵权行为,请联系我,我会及时删除。

本文节选自《疯狂工作流讲义(第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

本文发表于2017年11月27日 20:33
(c)注:本文转载自https://my.oschina.net/JavaLaw/blog/1580574,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如有侵权行为,请联系我们,我们会及时删除.

阅读 2042 讨论 0 喜欢 0

抢先体验

扫码体验
趣味小程序
文字表情生成器

闪念胶囊

你要过得好哇,这样我才能恨你啊,你要是过得不好,我都不知道该恨你还是拥抱你啊。

直抵黄龙府,与诸君痛饮尔。

那时陪伴我的人啊,你们如今在何方。

不出意外的话,我们再也不会见了,祝你前程似锦。

这世界真好,吃野东西也要留出这条命来看看

快捷链接
网站地图
提交友链
Copyright © 2016 - 2021 Cion.
All Rights Reserved.
京ICP备2021004668号-1