Liferay7 BPM门户开发之5: Activiti和Spring集成
参考文档:
https://github.com/jbarrez/spring-boot-with-activiti-example
https://github.com/sxyx2008/spring-activiti-webapp
http://www.cnblogs.com/hongwz/p/5548473.html
Spring典型配置
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
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
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"> <bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
<property name="driverClass" value="org.h2.Driver" />
<property name="url" value="jdbc:h2:mem:activiti;DB_CLOSE_DELAY=1000" />
<property name="username" value="sa" />
<property name="password" value="" />
</bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean> <bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
<property name="dataSource" ref="dataSource" />
<property name="transactionManager" ref="transactionManager" />
<property name="databaseSchemaUpdate" value="true" />
<property name="jobExecutorActivate" value="false" />
</bean> <bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
<property name="processEngineConfiguration" ref="processEngineConfiguration" />
</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" /> ...
自定义的实体类可以这么设置:
<beans>
...
<tx:annotation-driven transaction-manager="transactionManager"/> <bean id="userBean" class="org.activiti.spring.test.UserBean">
<property name="runtimeService" ref="runtimeService" />
</bean> <bean id="printer" class="org.activiti.spring.test.Printer" /> </beans>
写注解:
@ContextConfiguration("classpath:org/activiti/spring/test/transaction/SpringTransactionIntegrationTest-context.xml")
然后就可以访问Activiti的工厂类和自定义的实体类,
比如RepositoryService的实例这样获得,同时这也是Spring集成环境下的bpmn文件的部署方式:
RepositoryService repositoryService =
(RepositoryService) applicationContext.getBean("repositoryService");
String deploymentId = repositoryService
.createDeployment()
.addClasspathResource("org/activiti/spring/test/hello.bpmn20.xml")
.deploy()
.getId();
new 一个自定义的实体类实例:
UserBean userBean = (UserBean) applicationContext.getBean("userBean");
userBean.hello();
自定义的实体类:
public class UserBean { /** Spring 注入 */
private RuntimeService runtimeService; @Transactional
public void hello() {
runtimeService.startProcessInstanceByKey("helloProcess");
} public void setRuntimeService(RuntimeService runtimeService) {
this.runtimeService = runtimeService;
}
}
表达式
在流程定义文件bpmn20.xml使用表达式
表达式可以灵活动态的使用类的方法,他可以实现流程计算逻辑和实体类的解耦,说具体些就是流程定义文件可以不用Hardcode了,直接通过接口调用的形式访问具体的类,
举例,定义一个输出打印的类,
public class Printer { public void printMessage() {
System.out.println("hello world");
}
}
在Spring里注册:
<beans>
... <bean id="printer" class="org.activiti.examples.spring.Printer" /> </beans>
建立关联配置:
<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
...
<property name="beans">
<map>
<entry key="printer" value-ref="printer" />
</map>
</property>
</bean>
最后,就可以在流程定义文件bpmn20.xml使用表达式,定义了一个serviceTask:
这种调用方法非常灵活,也是集成Spring高效和方便的地方
<definitions id="definitions"> <process id="helloProcess"> <startEvent id="start" />
<sequenceFlow id="flow1" sourceRef="start" targetRef="print" /> <serviceTask id="print" activiti:expression="#{printer.printMessage()}" />
<sequenceFlow id="flow2" sourceRef="print" targetRef="end" /> <endEvent id="end" /> </process> </definitions>
使用Spring自动部署bpmn20.xml
仅当文件变化的时候,才会被重新部署,特别方便用于系统调试,程序员必备
<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
...
<property name="deploymentResources"
value="classpath*:/org/activiti/spring/test/autodeployment/autodeploy.*.bpmn20.xml" />
</bean> <bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
<property name="processEngineConfiguration" ref="processEngineConfiguration" />
</bean>
可以使用通配符
<property name="deploymentResources" value="classpath*:/activiti/*.bpmn" />
<property name="deploymentMode" value="single-resource" />
Hibernate 4.2.x集成
POM:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${org.springframework.version}</version>
</dependency> <dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.183</version>
</dependency>
配置是不是很简单?
然后就可以开发了
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration; @Configuration
@ComponentScan
@EnableAutoConfiguration
public class MyApplication { public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
} @Bean
public CommandLineRunner init(final RepositoryService repositoryService,
final RuntimeService runtimeService,
final TaskService taskService) { return new CommandLineRunner() {
@Override
public void run(String... strings) throws Exception {
System.out.println("Number of process definitions : "
+ repositoryService.createProcessDefinitionQuery().count());
System.out.println("Number of tasks : " + taskService.createTaskQuery().count());
runtimeService.startProcessInstanceByKey("oneTaskProcess");
System.out.println("Number of tasks after process start: " + taskService.createTaskQuery().count());
}
}; }
}
使用MySQL数据库
POM:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.34</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jdbc</artifactId>
<version>8.0.15</version>
</dependency>
数据源
@Bean
public DataSource database() {
return DataSourceBuilder.create()
.url("jdbc:mysql://127.0.0.1:3306/activiti-spring-boot?characterEncoding=UTF-8")
.username("alfresco")
.password("alfresco")
.driverClassName("com.mysql.jdbc.Driver")
.build();
}
REST开发扩展
Spring Boot可以快速开发web api接口,REST对集成第三方应用非常重要
POM:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring.boot.version}</version>
</dependency>
开发服务类,服务类是用于控制器的重复调用,即帮助类
@Service
public class MyService { @Autowired
private RuntimeService runtimeService; @Autowired
private TaskService taskService; @Transactional
public void startProcess() {
runtimeService.startProcessInstanceByKey("oneTaskProcess");
} @Transactional
public List<Task> getTasks(String assignee) {
return taskService.createTaskQuery().taskAssignee(assignee).list();
} }
控制器:
注意:启动流程使用POST,获取任务使用了GET,在输出时自动把实体类(TaskRepresentation)转换为JSON格式
@RestController
public class MyRestController { @Autowired
private MyService myService; @RequestMapping(value="/process", method= RequestMethod.POST)
public void startProcessInstance() {
myService.startProcess();
} @RequestMapping(value="/tasks", method= RequestMethod.GET, produces=MediaType.APPLICATION_JSON_VALUE)
public List<TaskRepresentation> getTasks(@RequestParam String assignee) {
List<Task> tasks = myService.getTasks(assignee);
List<TaskRepresentation> dtos = new ArrayList<TaskRepresentation>();
for (Task task : tasks) {
dtos.add(new TaskRepresentation(task.getId(), task.getName()));
}
return dtos;
} static class TaskRepresentation { private String id;
private String name; public TaskRepresentation(String id, String name) {
this.id = id;
this.name = name;
} public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
} } }
需要注意@ComponentScan一定在加在application类的注解
测试:
curl -X POST http://localhost:8080/process
curl http://localhost:8080/tasks?assignee=kermit
[{"id":"10004","name":"my task"}]
JPA支持
默认JPA是Hibernate
POM:
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter-jpa</artifactId>
<version>${activiti.version}</version>
</dependency>
实体类:
@Entity
class Person { @Id
@GeneratedValue
private Long id; private String username; private String firstName; private String lastName; private Date birthDate; public Person() {
} public Person(String username, String firstName, String lastName, Date birthDate) {
this.username = username;
this.firstName = firstName;
this.lastName = lastName;
this.birthDate = birthDate;
} public Long getId() {
return id;
} public void setId(Long id) {
this.id = id;
} public String getUsername() {
return username;
} public void setUsername(String username) {
this.username = username;
} public String getFirstName() {
return firstName;
} public void setFirstName(String firstName) {
this.firstName = firstName;
} public String getLastName() {
return lastName;
} public void setLastName(String lastName) {
this.lastName = lastName;
} public Date getBirthDate() {
return birthDate;
} public void setBirthDate(Date birthDate) {
this.birthDate = birthDate;
}
}
如果不使用in-memory数据库,数据表不会自动建立,需要 application.properties 增加配置:
spring.jpa.hibernate.ddl-auto=update
增加接口:
public interface PersonRepository extends JpaRepository<Person, Long> { Person findByUsername(String username); }
服务类:
@Service
@Transactional
public class MyService { @Autowired
private RuntimeService runtimeService; @Autowired
private TaskService taskService; @Autowired
private PersonRepository personRepository; public void startProcess(String assignee) { Person person = personRepository.findByUsername(assignee); Map<String, Object> variables = new HashMap<String, Object>();
variables.put("person", person);
runtimeService.startProcessInstanceByKey("oneTaskProcess", variables);
} public List<Task> getTasks(String assignee) {
return taskService.createTaskQuery().taskAssignee(assignee).list();
} public void createDemoUsers() {
if (personRepository.findAll().size() == 0) {
personRepository.save(new Person("jbarrez", "Joram", "Barrez", new Date()));
personRepository.save(new Person("trademakers", "Tijs", "Rademakers", new Date()));
}
} }
在CommandLineRunner 初始化的时候,添加用户持久化数据(createDemoUsers())
@Bean
public CommandLineRunner init(final MyService myService) { return new CommandLineRunner() {
public void run(String... strings) throws Exception {
myService.createDemoUsers();
}
}; }
控制器:
@RestController
public class MyRestController { @Autowired
private MyService myService; @RequestMapping(value="/process", method= RequestMethod.POST)
public void startProcessInstance(@RequestBody StartProcessRepresentation startProcessRepresentation) {
myService.startProcess(startProcessRepresentation.getAssignee());
} ... static class StartProcessRepresentation { private String assignee; public String getAssignee() {
return assignee;
} public void setAssignee(String assignee) {
this.assignee = assignee;
}
}
流程定义再一次使用了表达式:
<userTask id="theTask" name="my task" activiti:assignee="${person.id}"/>
测试:POST一个流程启动者的username
curl -H "Content-Type: application/json" -d '{"assignee" : "jbarrez"}' http://localhost:8080/process
curl http://localhost:8080/tasks?assignee=1
[{"id":"12505","name":"my task"}]
更高级的内容可以自行研究:
- Actuator support
- Spring Integration support
- Rest API integration
- Spring Security support
Liferay7 BPM门户开发之5: Activiti和Spring集成的更多相关文章
- Liferay7 BPM门户开发之11: Activiti工作流程开发的一些统一规则和实现原理(完整版)
注意:以下规则是我为了规范流程的处理过程,不是Activiti公司的官方规定. 1.流程启动需要设置启动者,在Demo程序中,“启动者变量”名统一设置为initUserId 启动时要做的: ident ...
- Liferay7 BPM门户开发之8: Activiti实用问题集合
1.如何实现审核的上级获取(任务逐级审批) 这个是必备功能,通过Spring的注入+Activiti表达式可以很容易解决. 可参考: http://blog.csdn.net/sunxing007/a ...
- Liferay7 BPM门户开发之7: Activiti中的重要概念和主要数据库结构
流程的人员参与角色: Assignee :签收者(即待办人) Candidate:候选人 Owner:拥有者 Starter:启动者 participant:参与者,包含查阅 流程变量的类型: Str ...
- Liferay7 BPM门户开发之6: Activiti数据库换为mysql
第一步: 在mysql中创建数据库名字叫 'activiti' 执行D:\activiti-5.21.0\database\create下的脚本 第二步: 打开=> apache-tomcat/ ...
- Liferay7 BPM门户开发之4: Activiti事件处理和监听Event handlers
事件机制从Activiti 5.15开始引入,这非常棒,他可以让你实现委托. 可以通过配置添加事件监听器,也可以通过Runtime API加入注册事件. 所有的事件参数子类型都来自org.activi ...
- Liferay7 BPM门户开发之3: Activiti开发环境搭建
下载地址: http://activiti.org/download.html 源码: https://github.com/Activiti/Activiti 环境准备(检查项): JDK 1.7 ...
- Liferay7 BPM门户开发之10: 通用流程实现从Servlet到Portlet(Part1)
开发目的: 实现通用流程自动化处理(即实现不需要hardcode代码的bpm统一处理后台,仅需要写少量前端html form代码和拖拽设计BPM定义) 既可独立运行或可依托于Liferay或依托其它门 ...
- Liferay7 BPM门户开发之37: Liferay7下的OSGi Hook集成开发
hook开发是Liferay客制扩展的一种方式,比插件灵活,即可以扩展liferay门户,也能对原有特性进行更改,Liferay有许多内置的服务,比如用hook甚至可以覆盖Liferay服务. 可作为 ...
- Liferay7 BPM门户开发之17: Portlet 生命周期
Portlet 生命周期 init() =〉 render() =〉 processAction() =〉 processEvent() =〉 serveResource() =〉destroy() ...
随机推荐
- HDU2665 求区间第K大 主席树
题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=2665 代码: //#include<bits/stdc++.h> #include< ...
- Oracle 基本语法、触发器、视图
参考文章:https://www.cnblogs.com/linjiqin/category/349944.html 数据库分类 1.小型数据库:access.foxbase 2.中型数据库:inor ...
- 搭建React项目(一):在网页中使用
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- 新建一个项目,如何使用abp用户登录系统
1.首先参考Framework.Web里的packages.config,把相关的包都安装好. 2.App_Start文件夹下的xxxModule.cs和Startup.cs拷过来,修改下命名空间. ...
- cookie和session 以及Django中应用
cookie和session 以及Django中应用 cookie和session机制 cookie和session机制 cookie机制采用的是在客户端保持状态的方案.作用就是为了解决HTTP协 ...
- docker 镜像存放路径的修改
可以通过在启动时使用--graph参数来指定存储路径. 或者使用systemd来管理服务, 就在/lib/systemd/system/docker.service中修改这一行: 1.ExecStar ...
- 6J - 盐水的故事
挂盐水的时候,如果滴起来有规律,先是滴一滴,停一下:然后滴二滴,停一下:再滴三滴,停一下...,现在有一个问题:这瓶盐水一共有VUL毫升,每一滴是D毫升,每一滴的速度是一秒(假设最后一滴不到D毫升,则 ...
- sql中with as测试实例
一.使用场景 1.多处使用才有必要2.一方面减少代码数量便于理解维护3.一方面跟代码一样一次计算到处用 二.实例(本处示例仅为测试,实际用join比较好) 1.不使用with as 2.使用with ...
- osg探究补充:DatabasePager类简介
简介 DatabasePager类,也就是常说的数据库分页技术,简单来说,就是在进行数据库查找时,有可能满足条件的数据很多,为了提高相应速度我们进行数据查找时进行分页查找与显示,当点击下一页时才会进行 ...
- Spring Boot 整合mybatis 使用多数据源
本人想要实现一个项目里面多个数据库源连接,所以就尝试写一个demo,不多说,先贴结构,再贴代码,可以根据以下的顺序,直接copy解决问题. 首先,dao和resource下的mappers可以用myb ...