Activiti(十)之Activiti整合SpringBoot案例

1、概述

       Activiti7 发布正式版之后,它与 SpringBoot2.x 已经完全支持整合开发。我们可以将 Activiti7 与SpringBoot 整合开发的坐标引入到工程中,从而达到 SpringBoot 支持 Activti7 整合。

整合步骤:

1、引入Activiti7和SpringBoot相关的依赖,以及其他一些相关的坐标:比如MySql驱动,Mybatis等;

2、因为Activiti7与SpringSecurity是高耦合的,所以我们需要引入SpringSecurity安全框架的相关配置;

3、编写测试类,使用Activiti7的新增的两个主要接口:ProcessRuntime 接口和TaskRuntime 接口来进行流程的相关操作;

4、整合SpringMvc,通过浏览器访问触发流程。

2、搭建工程,代码实现

2.1 搭建maven工程,添加依赖

使用IDEA搭建maven工程,工程整体的结构如下:

pom.xml文件的内容如下:

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.zdw.activiti</groupId>
    <artifactId>activiti7-springboot</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.0.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</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.Beta2</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.5</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.27</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

2.2 application.yml

在resources目录下创建application.yml配置文件,里面的数据库配置根据自己的情况来配置:

#datasource
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/activiti?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT
    username : root
    password : 123
    driver-class-name: com.mysql.jdbc.Driver

2.3 添加SpringSecurity配置

      因为 Activiti7 与 SpringBoot 整合后,默认情况下,集成了 SpringSecurity 安全框架,这样我们就要去准备 SpringSecurity 整合进来的相关用户权限配置信息。
      可以查看一下整合 SpringBoot 的依赖包,发现同时也将 SpringSecurity 的依赖包也添加进项目中了,如下:

2.3.1 添加SecurityUtil 类

创建包:com.zdw.activiti.security,然后创建SecurityUtil:这个类可以从我们下载的 Activiti7 官方提供的 Example 中找到

package com.zdw.activiti.security;

import org.springframework.beans.factory.annotation.Autowired;
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 {

    @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);
    }
}

2.3.2  添加 DemoApplicationConfig类

      在 Activiti7 官方下载的 Example 中找到 DemoApplicationConfig 类,它的作用是为了实现SpringSecurity 框架的用户权限的配置,这样我们就可以在系统中使用用户权限信息。本次项目中基本是在文件中定义出来的用户信息,当然也可以是数据库中查询的用户权限信息。

       在包com.zdw.activiti.security下面创建DemoApplicationConfig:

/*
 * 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.zdw.activiti.security;

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.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;

@Configuration
@EnableWebSecurity
public class DemoApplicationConfiguration extends WebSecurityConfigurerAdapter {

    private Logger logger = LoggerFactory.getLogger(DemoApplicationConfiguration.class);

    @Override
    @Autowired
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(myUserDetailsService());
    }

    @Bean
    public UserDetailsService myUserDetailsService() {

        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();
    }
}

3、SpringBoot 整合 Junit 测试

在test目录下创建包com.zdw.activiti.test,然后创建测试类:Actviti7DemoApplicationTests

package com.zdw.activiti.test;

import com.zdw.activiti.security.SecurityUtil;
import org.activiti.api.process.model.ProcessDefinition;
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.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;

@RunWith(SpringRunner.class)
@SpringBootTest
public class Actviti7DemoApplicationTests {
    @Autowired
    private ProcessRuntime processRuntime;
    @Autowired
    private TaskRuntime taskRuntime;
    @Autowired
    private SecurityUtil securityUtil;

    //查看流程定义
    @Test
    public void testDefinition(){
        securityUtil.logInAs("salaboy");//用户验证
        Page<ProcessDefinition> processDefinitionPage = processRuntime.processDefinitions(Pageable.of(0, 10));
        System.out.println(" 可 用 的 流 程 定 义 数 量 : "  + processDefinitionPage.getTotalItems());
        for  (org.activiti.api.process.model.ProcessDefinition  pd  : processDefinitionPage.getContent()) {
            System.out.println("流程定义:" + pd);
        }
    }
}

执行测试方法之前,先要创建数据库activiti。

执行测试方法,可以看出控制台打印了:可用的流程定义数量 : 0 ,这是因为我们没有部署任何的流程定义,所以是0,但是我们去查看数据库,发现数据库中创建出了17张表,如下:

之前博客中,我们的数据库都是会创建25张表的,而这里只有17张表,以act_hi开头的历史表都没有创建出来。怎么解决这个问题呢?其实这是因为我们在application.yml中缺少了activiti相关的配置,没有开启创建历史表的开关,所以,我们接下来就是要添加相关配置,如下:

#datasource
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/activiti?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT
    username : root
    password : 123
    driver-class-name: com.mysql.jdbc.Driver

  #参考配置https://www.cnblogs.com/liaojie970/p/8857710.html
  activiti:
    # 自动建表
    database-schema: ACTIVITI
    database-schema-update: true
    history-level: full
    db-history-used: true

       database-schema-update表示启动时检查数据库表,不存在则创建
    history-level表示哪种情况下使用历史表,这里配置为full表示全部记录历史,方便绘制流程图
    db-history-used为true表示使用历史表,如果不配置,则工程启动后可以检查数据库,只建立了17张表,历史表没有建立,则流程图及运行节点无法展示(暂未找到可行方式)
 

当再次运行上面的测试方法,成功之后,查看数据库,会发现以act_hi开头的7张表也为我们创建好了,如下:

4、创建流程图并部署

在resources下面创建processes文件夹,然后创建test.bpmn文件:

注意:在activiti7的官网中,解释了在与SpringBoot整合的时候,把流程图放在resources/processes目录下,那么就会自动部署流程定义。还需要了解的是,我们这里为每个流程都指定了Candidate Groups,他的值是类:DemoApplicationConfiguration中:activitiTeam,所以我们之后处理流程需要用相关的用户:salaboy、ryandawsonuk、erdemedeiros、other

 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"},
        };

上面的工作就绪之后,我们再次执行测试类中的testDefinition方法,会看到控制台打印:

可用的流程定义数量 : 1
流程定义:ProcessDefinition{id='test:1:cb20897d-0b39-11ea-b002-005056c00001', name='null', key='test', description='null', version=1}

这说明流程自动部署成功啦。

5、启动流程实例并完成任务

上面的这些准备工作,已经证明我们Activiti7整合SpringBoot是成功的,所以接下来要使用Activiti7提供的新接口来启动流程实例,查询任务并完成任务。

5.1 启动流程实例

//启动流程实例
    @Test
    public void testStartInstance(){
        securityUtil.logInAs("salaboy");//用户验证
        //test是我们定义流程的时候设置的key
        ProcessInstance processInstance = processRuntime.start(ProcessPayloadBuilder.start().withProcessDefinitionKey("test").build());
        System.out.println("流程实例的ID是:"+processInstance.getId());
    }

5.2 查询并完成任务

//查询并完成任务
    @Test
    public void testQueryAndCompleteTask(){
        securityUtil.logInAs("ryandawsonuk");//用户验证
        Page<Task> taskPage = taskRuntime.tasks(Pageable.of(0, 10));
        if(taskPage.getTotalItems()>0){
            for (Task task : taskPage.getContent()) {
                //拾取任务,由当前通过验证的用户ryandawsonuk来拾取任务
                Task task_ = taskRuntime.claim(TaskPayloadBuilder.claim().withTaskId(task.getId()).build());
                System.out.println("任务id:"+task_.getId()+",任务名称:"+task_.getName());
                Task task_new = taskRuntime.complete(TaskPayloadBuilder.complete().withTaskId(task.getId()).build());
            }
        }
        //然后再次查询任务
        Page<Task> taskPage2 = taskRuntime.tasks(Pageable.of(0, 10));
        if(taskPage2.getTotalItems()>0){
            for (Task task : taskPage2.getContent()) {
                System.out.println("剩下的任务名称:"+task.getName());
            }
        }
    }

使用TaskRuntime 接口的tasks()方法实现任务的查询。
使用TaskRuntime 接口的claim()方法实现任务拾取。
使用TaskRuntime 接口的complete()方法实现任务的完成

 

6、加入SpringMvc

上面我们都是SpringBoot整合Junit做的测试,现在把SpringMvc加入进来,通过浏览器来触发部署流程定义,以及查询启动流程实例和完成任务的操作。

6.1 创建启动类

package com.zdw.activiti;

import org.activiti.api.process.runtime.connector.Connector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.context.annotation.Bean;

@SpringBootApplication(exclude = SecurityAutoConfiguration.class)
public class Actviti7DemoApplication {
	private Logger logger = LoggerFactory.getLogger(Actviti7DemoApplication.class);

	public static void main(String[] args) {
		SpringApplication.run(Actviti7DemoApplication.class, args);
	}

	@Bean
	public Connector testConnector() {
		return integrationContext -> {
			logger.info("以前叫代理,现在叫连接器被调用啦~~");
			return integrationContext;
		};
	}
}

6.2 创建控制器类

package com.zdw.activiti.controller;

import com.zdw.activiti.security.SecurityUtil;
import org.activiti.api.process.model.ProcessDefinition;
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.runtime.TaskRuntime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * Create By zdw on 2019/11/20
 */
@RestController
@RequestMapping("activiti")
public class ActivitiController {

    @Autowired
    private ProcessRuntime processRuntime;
    @Autowired
    private TaskRuntime taskRuntime;
    @Autowired
    private SecurityUtil securityUtil;

    @RequestMapping("repository")
    public String repository(){
        Page<ProcessDefinition> processDefinitionPage = processRuntime.processDefinitions(Pageable.of(0, 10));
        System.out.println("可用的流程定义数量 : "  + processDefinitionPage.getTotalItems());
        for  (org.activiti.api.process.model.ProcessDefinition  pd  : processDefinitionPage.getContent()) {
            System.out.println("流程定义:" + pd);
        }
        return "流程部署成功";
    }
}

6.3 测试部署流程定义

在测试之前,先删除activiti数据库,然后再创建一个全新的activiti数据库。

执行启动程序,浏览器访问:http://localhost:8080/activiti/repository,会弹出一个登录窗口:

此时我们就需要用:DemoApplicationConfiguration类中的存在的用户去登录,比如:salaboy,密码是:password。

登录成功,就会访问到我们的repository方法,然后自动部署位于resources/processes目录下流程定义,控制台会打印:

可用的流程定义数量 : 1
流程定义:ProcessDefinition{id='test:1:f6aae9e0-0b40-11ea-a6d6-005056c00001', name='null', key='test', description='null', version=1}

浏览器会显示方法的返回值:流程部署成功。

其他的流程实例以及任务相关的操作就不做开发了,基本上和之前的测试类的方法是一样的。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值