前文《SpringBoot2.0整合activiti6示例》已经介绍了SpringBoot2.0与activiti6的集成,虽然现在activiti6的使用量还是很大,但是activiti7将来毕竟是趋势,所以本文将介绍如何整合SpringBoot2.0与activiti7,并且通过这两次代码整合,最后会分析activiti7相比activiti6有哪些改进。
maven的pom.xml文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.0.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>activiti7</artifactId> <version>0.0.1-SNAPSHOT</version> <name>activiti7</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.activiti</groupId> <artifactId>activiti-spring-boot-starter</artifactId> <version>7.1.0.M1</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.16</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.45</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> |
application.properties
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
server.port=9090 spring.datasource.username=root spring.datasource.password=123456 spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/activiti7?useUnicode=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true # 使用阿里的Druid spring.datasource.type=com.alibaba.druid.pool.DruidDataSource spring.activiti.database-schema-update=true spring.activiti.history-level=full spring.activiti.db-history-used=true # activiti默认是在processes里找资源文件,所以如果想更换目录,就在这里更改 spring.activiti.processDefinitionLocationPrefix=classpath:/processes/ logging.level.com.ascendant=debug logging.pattern.console="%d{yyyy-MM-dd HH:mm:ss.SSS} %clr(-%5p) %clr(${PID:- }){magenta} --- %clr([%15.15t]){faint}\ %highlight(%-80.80logger{300}){cyan} %clr(:) %m %n%wEx" |
RankMovie.bpmn20.xml
和activiti6一样,也是在src/main/resources
下新建processes目录,然后把流程文件放进来。
这边activiti7和activiti6不一样的是,除了支持原来以.bpmn
和.bpmn20.xml
为后缀的文件,还新增了支持json格式的文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
<?xml version="1.0" encoding="UTF-8"?> <bpmn2:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:activiti="http://activiti.org/bpmn" id="sample-diagram" targetNamespace="http://bpmn.io/schema/bpmn" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd"> <bpmn2:process id="RankMovieId" name="RankMovie" isExecutable="true"> <bpmn2:documentation /> <bpmn2:startEvent id="StartEvent_1"> <bpmn2:outgoing>SequenceFlow_02b718w</bpmn2:outgoing> </bpmn2:startEvent> <bpmn2:sequenceFlow id="SequenceFlow_02b718w" sourceRef="StartEvent_1" targetRef="Task_1spvopd" /> <bpmn2:serviceTask id="Task_1spvopd" name="Get Movie Description" implementation="Movies.getMovieDesc"> <bpmn2:incoming>SequenceFlow_02b718w</bpmn2:incoming> <bpmn2:outgoing>SequenceFlow_19i3kha</bpmn2:outgoing> </bpmn2:serviceTask> <bpmn2:exclusiveGateway id="ExclusiveGateway_00cvmz6"> <bpmn2:incoming>SequenceFlow_19i3kha</bpmn2:incoming> <bpmn2:outgoing>SequenceFlow_1maw9gg</bpmn2:outgoing> <bpmn2:outgoing>SequenceFlow_053d6ej</bpmn2:outgoing> </bpmn2:exclusiveGateway> <bpmn2:sequenceFlow id="SequenceFlow_19i3kha" sourceRef="Task_1spvopd" targetRef="ExclusiveGateway_00cvmz6" /> <bpmn2:sequenceFlow id="SequenceFlow_1maw9gg" sourceRef="ExclusiveGateway_00cvmz6" targetRef="Task_01uy47y"> <bpmn2:conditionExpression xsi:type="bpmn2:tFormalExpression">${movieDesc != null}</bpmn2:conditionExpression> </bpmn2:sequenceFlow> <bpmn2:sequenceFlow id="SequenceFlow_053d6ej" sourceRef="ExclusiveGateway_00cvmz6" targetRef="EndEvent_0r2untf"> <bpmn2:conditionExpression xsi:type="bpmn2:tFormalExpression">${movieDesc == null}</bpmn2:conditionExpression> </bpmn2:sequenceFlow> <bpmn2:endEvent id="EndEvent_0r2untf"> <bpmn2:incoming>SequenceFlow_053d6ej</bpmn2:incoming> </bpmn2:endEvent> <bpmn2:endEvent id="EndEvent_0211pj5"> <bpmn2:incoming>SequenceFlow_0hczb50</bpmn2:incoming> </bpmn2:endEvent> <bpmn2:userTask id="Task_01uy47y" name="Add Rating" activiti:assignee="reviewer"> <bpmn2:incoming>SequenceFlow_1maw9gg</bpmn2:incoming> <bpmn2:outgoing>SequenceFlow_0hczb50</bpmn2:outgoing> </bpmn2:userTask> <bpmn2:sequenceFlow id="SequenceFlow_0hczb50" sourceRef="Task_01uy47y" targetRef="EndEvent_0211pj5" /> </bpmn2:process> <bpmndi:BPMNDiagram id="BPMNDiagram_1"> <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="RankMovieId"> <bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1"> <dc:Bounds x="206" y="143" width="36" height="36" /> </bpmndi:BPMNShape> <bpmndi:BPMNEdge id="SequenceFlow_02b718w_di" bpmnElement="SequenceFlow_02b718w"> <di:waypoint x="242" y="161" /> <di:waypoint x="292" y="161" /> </bpmndi:BPMNEdge> <bpmndi:BPMNShape id="ServiceTask_0u8jifg_di" bpmnElement="Task_1spvopd"> <dc:Bounds x="292" y="121" width="100" height="80" /> </bpmndi:BPMNShape> <bpmndi:BPMNShape id="ExclusiveGateway_00cvmz6_di" bpmnElement="ExclusiveGateway_00cvmz6" isMarkerVisible="true"> <dc:Bounds x="442" y="136" width="50" height="50" /> </bpmndi:BPMNShape> <bpmndi:BPMNEdge id="SequenceFlow_19i3kha_di" bpmnElement="SequenceFlow_19i3kha"> <di:waypoint x="392" y="161" /> <di:waypoint x="442" y="161" /> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge id="SequenceFlow_1maw9gg_di" bpmnElement="SequenceFlow_1maw9gg"> <di:waypoint x="492" y="161" /> <di:waypoint x="542" y="161" /> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge id="SequenceFlow_053d6ej_di" bpmnElement="SequenceFlow_053d6ej"> <di:waypoint x="467" y="186" /> <di:waypoint x="467" y="271" /> <di:waypoint x="574" y="271" /> </bpmndi:BPMNEdge> <bpmndi:BPMNShape id="EndEvent_0r2untf_di" bpmnElement="EndEvent_0r2untf"> <dc:Bounds x="574" y="253" width="36" height="36" /> </bpmndi:BPMNShape> <bpmndi:BPMNShape id="EndEvent_0211pj5_di" bpmnElement="EndEvent_0211pj5"> <dc:Bounds x="696" y="143" width="36" height="36" /> </bpmndi:BPMNShape> <bpmndi:BPMNShape id="UserTask_1a6ia1l_di" bpmnElement="Task_01uy47y"> <dc:Bounds x="542" y="121" width="100" height="80" /> </bpmndi:BPMNShape> <bpmndi:BPMNEdge id="SequenceFlow_0hczb50_di" bpmnElement="SequenceFlow_0hczb50"> <di:waypoint x="642" y="161" /> <di:waypoint x="696" y="161" /> </bpmndi:BPMNEdge> </bpmndi:BPMNPlane> </bpmndi:BPMNDiagram> </bpmn2:definitions> |

Activiti7Config.java
这是个配置类,因为 activiti7相比activiti6新增了与SpringSecurity的集成,进行用户权限的管理,所以这边的WebSecurityConfigurerAdapter
是 SpringSecurity WEB环境下需要用到的配置(这边不了解 SpringSecurity 的小伙伴建议先了解下它)。
该配置类还存储了一些初始的用户数据到内存,虽然这边使用的是InMemoryUserDetailsManager,实际项目中大家肯定要改为JdbcUserDetailsManager,已达到使用数据库来存储用户、用户组、权限数据的目的。当然也可以自己实现UserDetailsManager来自定义存储。
另外该配置类还配置了一些activiti里面的实时任务监听器,还有参数创建监听器,流程完成监听器等等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
import org.activiti.api.model.shared.event.VariableCreatedEvent; import org.activiti.api.process.runtime.events.ProcessCompletedEvent; import org.activiti.api.process.runtime.events.listener.ProcessRuntimeEventListener; import org.activiti.api.runtime.shared.events.VariableEventListener; import org.activiti.api.task.runtime.events.TaskAssignedEvent; import org.activiti.api.task.runtime.events.TaskCompletedEvent; import org.activiti.api.task.runtime.events.listener.TaskRuntimeEventListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; 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.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; @Configuration public class Activiti7Config extends WebSecurityConfigurerAdapter { private Logger logger = LoggerFactory.getLogger(Activiti7Config.class); @Override @Autowired public void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(myUserDetailsService()); } @Override protected void configure(HttpSecurity http) throws Exception { http .csrf().disable() .authorizeRequests() // 任何请求都要验证 .anyRequest() .authenticated() .and() .httpBasic(); } @Bean public VariableEventListener<VariableCreatedEvent> variableCreatedEventListener() { return variableCreatedEvent -> logger.info("监听到新的参数:{}", variableCreatedEvent); } @Bean public ProcessRuntimeEventListener<ProcessCompletedEvent> processCompletedEventListener() { return processCompletedEvent -> logger.info("监听到新的流程完成:{}", processCompletedEvent); } @Bean public TaskRuntimeEventListener<TaskAssignedEvent> taskAssignedListener() { return taskAssigned -> logger.info("任务分配:" + taskAssigned.getEntity().getName() + "分配给" + taskAssigned.getEntity().getAssignee()); } @Bean public TaskRuntimeEventListener<TaskCompletedEvent> taskCompletedListener() { return taskCompleted -> logger.info("任务完成:" + taskCompleted.getEntity().getName() + "任务所有者是" + taskCompleted.getEntity().getOwner()); } /** * 熟悉springsecurity的应该知道UserDetailsService,它里面定义了登录的接口 * 这边就是在按springsecurity的规则在内存里维护一组用户和对应的权限以备使用 * @return */ @Bean public UserDetailsService myUserDetailsService() { InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager(); // 准备了三个用户以及它们所拥有的权限,这些用户在bpmn20.xml文件中有使用到 String[][] usersGroupsAndRoles = { {"system", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"}, {"reviewer", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"}, {"admin", "password", "ROLE_ACTIVITI_ADMIN", "GROUP_activitiTeam"}, {"other", "password", "ROLE_ACTIVITI_USER", "GROUP_otherTeam"} }; for (String[] user : usersGroupsAndRoles) { List<String> authoritiesStrings = Arrays.asList(Arrays.copyOfRange(user, 2, user.length)); logger.info("注册一个新用户: " + user[0] + ", 其权限有:[" + authoritiesStrings + "]"); inMemoryUserDetailsManager.createUser(new User(user[0], passwordEncoder().encode(user[1]), authoritiesStrings.stream().map(s -> new SimpleGrantedAuthority(s)).collect(Collectors.toList()))); } return inMemoryUserDetailsManager; } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } } |
SecurityUtil.java
这是一个工具类,主要是为了方便模拟SpringSecurity的用户登录。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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; @Component public class SecurityUtil { private Logger logger = LoggerFactory.getLogger(SecurityUtil.class); @Qualifier("myUserDetailsService") @Autowired private UserDetailsService userDetailsService; /** * 一个简易的登录 * @param username */ public void logInAs(String username) { UserDetails user = userDetailsService.loadUserByUsername(username); if (user == null) { throw new IllegalStateException("用户 " + username + " 不存在, 请核查!"); } logger.info("用户{}登录成功", username); 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); } } |
MoviesConnector.java
这个类是为名叫Get Movie Description
这个serviceTask流程节点服务的,自动设置需要的参数。
如果不理解的,可以先查看下如何使用 serviceTask

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
import org.activiti.api.process.model.IntegrationContext; import org.activiti.api.process.runtime.connector.Connector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import java.util.Map; @Component("Movies.getMovieDesc") public class MoviesConnector implements Connector { private Logger logger = LoggerFactory.getLogger(MoviesConnector.class); @Override public IntegrationContext apply(IntegrationContext integrationContext) { Map<String, Object> inBoundVariables = integrationContext.getInBoundVariables(); logger.info("已有的参数: " + inBoundVariables); logger.info("设置新的参数 movieDesc"); integrationContext.addOutBoundVariable("movieDesc", "这是李小龙的电影描述"); return integrationContext; } } |
DemoController.java
这就是测试controller类了,它里面介绍了如何使用ProcessRuntime、TaskRuntime等api,这都是activiti的核心api。
写到这里,不禁要提一笔,网上很多整合SpringBoot和activiti7的文章,里面使用的activiti的api还都是用的 activiti 6的旧api,虽然官方也说暂时没有丢弃旧的api,但是新的api更简洁更高效,并且删除了原来的identityService和formService,所以activiti7首选还是要使用新的api。
具体细节就不说了,代码里的注释也很详细了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
import com.example.activiti7.bean.Content; import com.example.activiti7.util.SecurityUtil; import org.activiti.api.model.shared.model.VariableInstance; 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.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import java.util.Random; /** * 这是一个测试controller */ @RestController public class DemoController { private Logger logger = LoggerFactory.getLogger(DemoController.class); @Autowired private ProcessRuntime processRuntime; @Autowired private TaskRuntime taskRuntime; @Autowired private SecurityUtil securityUtil; /** * 一个简单的测试接口 */ @GetMapping("/process") public void process() { // 模拟用户reviewer登录 securityUtil.logInAs("reviewer"); // 可使用流程 listAvailableProcesses(); // 开始一个流程 ProcessInstance processInstance = startProcess(); // 流程里的参数 listProcessVariables(processInstance); // 完成正在流转的任务 completeAvailableTasks(); } /** * 该接口用来测试分组任务,用户信息都已在config类里配置好了 * 另外也可以通过该接口的实现认识activiti7的task api如何使用 */ @GetMapping("/taskRelevant") public void taskRelevant(){ // 模拟GROUP_activitiTeam组下的用户reviewer登录 securityUtil.logInAs("reviewer"); logger.info("reviewer创建了一个任务"); taskRuntime.create(TaskPayloadBuilder.create() .withName("activitiTeam组下的第一个任务") .withDescription("写点描述") .withCandidateGroup("activitiTeam") .withPriority(10) .build()); // 模拟GROUP_otherTeam组下的用户other登录 securityUtil.logInAs("other"); logger.info("看看用户other能不能看到用户reviewer创建的任务"); Page<Task> tasks = taskRuntime.tasks(Pageable.of(0, 10)); logger.info("other能看到的任务数: " + tasks.getTotalItems()); // 模拟GROUP_activitiTeam组下的用户system登录 securityUtil.logInAs("system"); logger.info("看看用户system能不能看到用户reviewer创建的任务"); tasks = taskRuntime.tasks(Pageable.of(0, 10)); logger.info("system能看到的任务数: " + tasks.getTotalItems()); // 让system来领取这个任务,领取完后其他人就都不能看到这个任务了,并且该任务的assignee变为system String availableTaskId = tasks.getContent().get(0).getId(); taskRuntime.claim(TaskPayloadBuilder.claim().withTaskId(availableTaskId).build()); logger.info("用户system完成任务"); taskRuntime.complete(TaskPayloadBuilder.complete().withTaskId(availableTaskId).build()); } /** * 这个接口是用来测试传参接收参数使用bean的情况 */ @GetMapping("/variableWithBean") public void variableWithBean(){ securityUtil.logInAs("reviewer"); Content content = pickRandomString(); SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yy HH:mm:ss"); logger.info("启动一个带bean的参数的流程: " + content + " ,time: " + formatter.format(new Date())); ProcessInstance processInstance = processRuntime.start(ProcessPayloadBuilder .start() .withProcessDefinitionKey("RankMovieId") .withName("Processing Content: " + content) .withVariable("content", content) .build()); logger.info("开始一个流程实例: " + processInstance); Page<Task> tasks = taskRuntime.tasks(Pageable.of(0, 10)); if (tasks.getTotalItems() > 0) { for (Task t : tasks.getContent()) { logger.info("领取任务: " + t.getId()); taskRuntime.claim(TaskPayloadBuilder.claim().withTaskId(t.getId()).build()); List<VariableInstance> variables = taskRuntime.variables(TaskPayloadBuilder.variables().withTaskId(t.getId()).build()); VariableInstance variableInstance = variables.get(0); if (variableInstance.getName().equals("content")) { Content contentToProcess = variableInstance.getValue(); logger.info("获取到的内容信息: " + contentToProcess); if (contentToProcess.getBody().contains("activiti")) { logger.info("同意内容"); contentToProcess.setApproved(true); } else { logger.info("不同意内容"); contentToProcess.setApproved(false); } taskRuntime.complete(TaskPayloadBuilder.complete() .withTaskId(t.getId()).withVariable("content", contentToProcess).build()); } } } else { logger.info("没有任务"); } } private void completeAvailableTasks() { Page<Task> tasks = taskRuntime.tasks(Pageable.of(0, 20)); tasks.getContent().forEach(task -> { logger.info("正在流转的任务,{} ", task); listTaskVariables(task); taskRuntime.complete(TaskPayloadBuilder .complete() .withTaskId(task.getId()) .withVariable("rating", 5) .build()); }); } private void listTaskVariables(Task task) { logger.info("可使用任务:"); taskRuntime.variables(TaskPayloadBuilder .variables() .withTaskId(task.getId()) .build()) .forEach(variableInstance -> logger.info("\t> " + variableInstance.getName() + " -> " + variableInstance.getValue())); } private void listProcessVariables(ProcessInstance processInstance) { logger.info("流程参数:"); List<VariableInstance> variables = processRuntime.variables( ProcessPayloadBuilder .variables() .withProcessInstance(processInstance) .build()); variables.forEach(variableInstance -> logger.info("\t> " + variableInstance.getName() + " -> " + variableInstance.getValue())); } private ProcessInstance startProcess() { ProcessInstance processInstance = processRuntime.start(ProcessPayloadBuilder .start() .withProcessDefinitionKey("RankMovieId") .withName("myProcess") .withVariable("movieToRank", "Lord of the rings") .build()); logger.info("开始一个流程实例: " + processInstance); return processInstance; } private void listAvailableProcesses() { Page<ProcessDefinition> processDefinitionPage = processRuntime.processDefinitions(Pageable.of(0, 10)); logger.info("可使用流程定义数:{}", processDefinitionPage.getTotalItems()); for (ProcessDefinition pd : processDefinitionPage.getContent()) { logger.info("\t 流程: " + pd); } } private Content pickRandomString() { String[] texts = {"和平", "富强", "民主", "文明" + "法治", "乐观", "友善"}; return new Content(texts[new Random().nextInt(texts.length)],false,null); } } |
Content.java
这是测试设置和获取activiti里的变量时需要用到的bean
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonTypeInfo; import java.util.ArrayList; import java.util.List; @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY,property = "@class") public class Content { private String body; private boolean approved; private List<String> tags; @JsonCreator public Content(@JsonProperty("body")String body, @JsonProperty("approved")boolean approved, @JsonProperty("tags")List<String> tags){ this.body = body; this.approved = approved; this.tags = tags; if(this.tags == null){ this.tags = new ArrayList<>(); } } public String getBody() { return body; } public void setBody(String body) { this.body = body; } public boolean isApproved() { return approved; } public void setApproved(boolean approved) { this.approved = approved; } public List<String> getTags() { return tags; } public void setTags(List<String> tags) { this.tags = tags; } @Override public String toString() { return "Content{" + "body='" + body + '\'' + ", approved=" + approved + ", tags=" + tags + '}'; } } |
Activiti7Application.java
启动类,因为集成了SpringSecurity,所以web环境下加上@EnableWebSecurity
,另外又由于集成了SpringSecurity ,所以默认会拦截我们所有的访问路径到登录页面,所以有的人想通过在@SpringBootApplication
里设置(exclude = {org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class})
来规避掉这个问题,但是我个人不要建议这样做,除非你的activiti的生产环境不需要用户权限控制,或者你自己实现一套权限控制?
如果真的要过滤一些路径不让 SpringSecurity 来拦截,应该是在上面的config类里的configure(HttpSecurity http)
来进行配置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; //SecurityAutoConfiguration.class, // 如果项目里需要使用到springsecurity,就不要绕过它 (exclude = {org.springframework.boot.autoconfigure.security.servlet // .SecurityAutoConfiguration.class}) @SpringBootApplication @EnableWebSecurity public class Activiti7Application { public static void main(String[] args) { SpringApplication.run(Activiti7Application.class, args); } } |
工程结构图

测试
到了这边,代码就都解释完了,接下来就启动工程,进行测试

这就算启动成功了,然后访问 http://localhost:9090/process

可以看到一切OK。
总结activiti7相比activiti6的一些改变
1.数据库表对比,可以看到删除了原来与用户相关的一些表

2.资源文件增加了对json格式文件的支持
3.增加了与SpringSecurity的集成
4.核心api进行了更新和删减,现在的api更高效更简洁
代码下载
https://download.csdn.net/download/u013081610/12273591
最后
本文系本人原创,首发在 http://791202.com
,如要转载,请注明出处。
你把代码放在CSDN就过分了
😄
说的就是,为啥要放在CSDN!github,百度网盘啥的他不香么?
以后会改进😀
csdn牛逼