1 Activiti和Spring整合开发
1.1 Activiti和Spring整合的配置
<properties> <activiti.version>7.0.0.GA</activiti.version></properties><dependencies> <dependency> <groupId>org.activiti</groupId> <artifactId>activiti-engine</artifactId> <version>7.0.0.GA</version> </dependency> <dependency> <groupId>org.activiti</groupId> <artifactId>activiti-spring</artifactId> <version>${activiti.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.1.4.RELEASE</version> </dependency> <dependency> <groupId>org.activiti</groupId> <artifactId>activiti-bpmn-model</artifactId> <version>${activiti.version}</version> </dependency> <dependency> <groupId>org.activiti</groupId> <artifactId>activiti-bpmn-converter</artifactId> <version>${activiti.version}</version> </dependency> <dependency> <groupId>org.activiti</groupId> <artifactId>activiti-json-converter</artifactId> <version>${activiti.version}</version> </dependency> <dependency> <groupId>org.activiti</groupId> <artifactId>activiti-bpmn-layout</artifactId> <version>${activiti.version}</version> </dependency> <dependency> <groupId>org.activiti.cloud</groupId> <artifactId>activiti-cloud-services-api</artifactId> <version>7-201802-EA</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.19</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <!-- log start --> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.13.3</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.13.3</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.30</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j-impl</artifactId> <version>2.13.3</version> </dependency> <!-- log end --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.5</version> </dependency> <dependency> <groupId>commons-dbcp</groupId> <artifactId>commons-dbcp</artifactId> <version>1.4</version> </dependency></dependencies><dependencyManagement> <dependencies> <dependency> <groupId>org.activiti.cloud.dependencies</groupId> <artifactId>activiti-cloud-dependencies</artifactId> <version>7.0.0.GA</version> <scope>import</scope> <type>pom</type> </dependency> </dependencies></dependencyManagement>
- 创建Spring和Activiti的整合配置文件activiti-spring.xml
<?xml version="1.0" encoding="UTF-8"?><beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns="http://www.springframework.org/schema/beans" 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.xsd"> <!-- 配置数据源 --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/> <property name="username" value="root"/> <property name="url" value="jdbc:mysql://192.168.1.146:3306/activiti?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true"/> <property name="password" value="123456"/> <property name="maxActive" value="3"/> <property name="maxIdle" value="1"/> </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"/> <!-- 使用Spring的事务管理器 --> <property name="transactionManager" ref="transactionManager"/> <!-- 数据库的策略 --> <property name="databaseSchemaUpdate" value="true"/> </bean> <!-- 流程引擎 --> <bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean"> <property name="processEngineConfiguration" ref="processEngineConfiguration"/> </bean> <!-- 资源服务service --> <bean id="repositoryService" factory-bean="processEngine" factory-method="getRepositoryService"/> <!-- 流程运行service --> <bean id="runtimeService" factory-bean="processEngine" factory-method="getRuntimeService"/> <!-- 任务管理service --> <bean id="taskService" factory-bean="processEngine" factory-method="getTaskService"/> <!-- 历史管理service --> <bean id="historyService" factory-bean="processEngine" factory-method="getHistoryService"/></beans>
<?xml version="1.0" encoding="UTF-8"?><!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL --><!--Configuration后面的status用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,可以看到log4j2内部各种详细输出--><configuration status="INFO"> <!--先定义所有的appender--> <appenders> <!--输出日志信息到控制台--> <console name="Console" target="SYSTEM_OUT"> <!--控制日志输出的格式--> <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> </console> </appenders> <!--然后定义logger,只有定义了logger并引入的appender,appender才会生效--> <!--root:用于指定项目的根日志,如果没有单独指定Logger,则会使用root作为默认的日志输出--> <loggers> <root level="INFO"> <appender-ref ref="Console"/> </root> </loggers></configuration>
1.2 测试Activiti和Spring的整合
package com.sunxiaping.test;import org.activiti.engine.RepositoryService;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;/** * @author <a href="mailto:1900919313@qq.com">weiwei.xu</a> * @version 1.0 * 2020-08-12 09:23 */@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(locations={"classpath:activiti-spring.xml"})public class Activiti7Test { @Autowired private RepositoryService repositoryService; @Test public void test(){ System.out.println("repositoryService = " + repositoryService); }}
2 Activiti7和SpringBoot的整合开发
2.1 概述
- Activiti7发布正式版以后,它可以和SpringBoot2.x完全整合。我们可以将Activiti7和SpringBoot整合开发的坐标引入到工程中,从而达到SpringBoot支持Activiti7。
- SpringBoot整合Activiti7的具体步骤如下:
添加SpringBoot整合Activiti7的jar包的坐标。
添加SpringSecurity安全框架的整合配置信息。
使用Activiti7新支持的接口(ProcessRuntime接口和TaskRuntime接口)来实现工作流开发。
使用新的API实现工作流的开发,主要包括:流程定义查询,启动流程实例,任务的查询,任务的完成。
2.2 Activiti和SpringBoot整合的配置
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.9.RELEASE</version> <relativePath/></parent><dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.activiti</groupId> <artifactId>activiti-spring-boot-starter</artifactId> <version>7.0.0.GA</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.3</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency></dependencies><build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>2.2.9.RELEASE</version> </plugin> </plugins></build>
spring: datasource: url: jdbc:mysql://192.168.134.100:3306/activiti?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true username : root password : 123456 driver-class-name: com.mysql.cj.jdbc.Driver activiti: database-schema-update: true db-history-used: true history-level: full check-process-definitions: false async-executor-activate: true

/* * Copyright 2018 Alfresco, Inc. and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package com.sunxiaping.activiti7.config;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;import org.springframework.security.config.annotation.web.builders.HttpSecurity;import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;import org.springframework.security.core.authority.SimpleGrantedAuthority;import org.springframework.security.core.userdetails.User;import org.springframework.security.core.userdetails.UserDetailsService;import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;import org.springframework.security.crypto.password.PasswordEncoder;import org.springframework.security.provisioning.InMemoryUserDetailsManager;import java.util.Arrays;import java.util.List;import java.util.stream.Collectors;/** * SpringSecurity的配置类 */@Configuration@EnableWebSecuritypublic class SpringSecurityConfig extends WebSecurityConfigurerAdapter { private Logger logger = LoggerFactory.getLogger(SpringSecurityConfig.class); @Override public void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder()); } @Bean public UserDetailsService userDetailsService() { InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager(); String[][] usersGroupsAndRoles = { {"salaboy", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"}, {"ryandawsonuk", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"}, {"erdemedeiros", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"}, {"other", "password", "ROLE_ACTIVITI_USER", "GROUP_otherTeam"}, {"admin", "password", "ROLE_ACTIVITI_ADMIN"}, }; for (String[] user : usersGroupsAndRoles) { List<String> authoritiesStrings = Arrays.asList(Arrays.copyOfRange(user, 2, user.length)); logger.info("> Registering new user: " + user[0] + " with the following Authorities[" + authoritiesStrings + "]"); inMemoryUserDetailsManager.createUser(new User(user[0], passwordEncoder().encode(user[1]), authoritiesStrings.stream().map(s -> new SimpleGrantedAuthority(s)).collect(Collectors.toList()))); } return inMemoryUserDetailsManager; } @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .anyRequest() .authenticated() .and() .httpBasic(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); }}
package com.sunxiaping.activiti7.service;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.security.core.Authentication;import org.springframework.security.core.GrantedAuthority;import org.springframework.security.core.context.SecurityContextHolder;import org.springframework.security.core.context.SecurityContextImpl;import org.springframework.security.core.userdetails.UserDetails;import org.springframework.security.core.userdetails.UserDetailsService;import org.springframework.stereotype.Component;import java.util.Collection;@Componentpublic class UserService { @Qualifier("userDetailsService") @Autowired private UserDetailsService userDetailsService; public void logInAs(String username) { UserDetails user = userDetailsService.loadUserByUsername(username); if (user == null) { throw new IllegalStateException("User " + username + " doesn't exist, please provide a valid user"); } SecurityContextHolder.setContext(new SecurityContextImpl(new Authentication() { @Override public Collection<? extends GrantedAuthority> getAuthorities() { return user.getAuthorities(); } @Override public Object getCredentials() { return user.getPassword(); } @Override public Object getDetails() { return user; } @Override public Object getPrincipal() { return user; } @Override public boolean isAuthenticated() { return true; } @Override public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException { } @Override public String getName() { return user.getUsername(); } })); org.activiti.engine.impl.identity.Authentication.setAuthenticatedUserId(username); }}
package com.sunxiaping.activiti7;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class Activiti7SpringBootApplication { public static void main(String[] args) { SpringApplication.run(Activiti7SpringBootApplication.class, args); }}
2.3 测试Activiti和SpringBoot的整合
package com.sunxiaping.activiti7;import com.sunxiaping.activiti7.service.UserService;import org.activiti.api.process.model.ProcessDefinition;import org.activiti.api.process.model.ProcessInstance;import org.activiti.api.process.model.builders.ProcessPayloadBuilder;import org.activiti.api.process.runtime.ProcessRuntime;import org.activiti.api.runtime.shared.query.Page;import org.activiti.api.runtime.shared.query.Pageable;import org.activiti.api.task.model.Task;import org.activiti.api.task.model.builders.TaskPayloadBuilder;import org.activiti.api.task.runtime.TaskRuntime;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;import java.util.List;@RunWith(SpringRunner.class)@SpringBootTest(classes = Activiti7SpringBootApplication.class)public class Activiti7Test { /** * 流程定义相关操作 */ @Autowired private ProcessRuntime processRuntime; /** * 任务相关操作 */ @Autowired private TaskRuntime taskRuntime; @Autowired private UserService userService; /** * 查看流程定义 */ @Test public void testQueryProcessDefinition() { userService.logInAs("salaboy"); //分页查询流程定义信息 Page<ProcessDefinition> page = processRuntime.processDefinitions(Pageable.of(0, 10)); int totalItems = page.getTotalItems(); System.out.println("查看部署流程的个数 = " + totalItems); List<ProcessDefinition> content = page.getContent(); for (ProcessDefinition processDefinition : content) { String id = processDefinition.getId(); System.out.println("当前部署的流程定义的id = " + id); } } /** * 启动流程实例 */ @Test public void testStartProcessInstance() { userService.logInAs("salaboy"); ProcessInstance processInstance = processRuntime.start(ProcessPayloadBuilder.start().withProcessDefinitionKey("team01").build()); System.out.println("流程实例的id = " + processInstance.getId()); } /** * 任务分页查询 */ @Test public void testQueryTask() { userService.logInAs("salaboy"); Page<Task> page = taskRuntime.tasks(Pageable.of(0, 10)); int totalItems = page.getTotalItems(); System.out.println("任务的总数 = " + totalItems); for (Task task : page.getContent()) { String id = task.getId(); System.out.println("任务的id = " + id); String name = task.getName(); System.out.println("任务名称 = " + name); } } /** * 测试查询任务并完成任务 */ @Test public void testQueryTaskAndCompleteTask(){ userService.logInAs("salaboy"); Page<Task> page = taskRuntime.tasks(Pageable.of(0, 10)); int totalItems = page.getTotalItems(); System.out.println("任务的总数 = " + totalItems); for (Task task : page.getContent()) { String id = task.getId(); System.out.println("任务的id = " + id); String name = task.getName(); System.out.println("任务名称 = " + name); //拾取任务 taskRuntime.claim(TaskPayloadBuilder.claim().withTaskId(id).build()); //完成任务 taskRuntime.complete(TaskPayloadBuilder.complete().withTaskId(id).build()); } }}