这里选择的版本为 6.4.1
Flowable 6.4.1 release
中文版用户手册:Flowable BPMN 用户手册
如果需要集成 Flowable Modeler 的请下载源码
PS:不要选择 6.4.2 版本,这个版本有发版问题
由于是 spring-boot 集成,因此直接选择 flowable-spring-boot-starter
,里面提供了齐全的 REST API
<dependency>
<groupId>org.flowablegroupId>
<artifactId>flowable-spring-boot-starterartifactId>
<version>6.4.1version>
dependency>
其他的也可以直接选择 flowable-engine
<dependency>
<groupId>org.flowablegroupId>
<artifactId>flowable-engineartifactId>
<version>6.4.1version>
dependency>
# flowable 配置
flowable:
# 关闭异步,不关闭历史数据的插入就是异步的,会在同一个事物里面,无法回滚
# 开发可开启会提高些效率,上线需要关闭
async-executor-activate: false
Flowable 使用 SLF4J
作为内部日志框架。在这个例子中,我们使用 log4j
作为 SLF4J
的实现。
加依赖
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-apiartifactId>
<version>1.7.21version>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-log4j12artifactId>
<version>1.7.21version>
dependency>
resource
目录下新建文件 log4j.properties
log4j.rootLogger=DEBUG, CA
log4j.appender.CA=org.apache.log4j.ConsoleAppender
log4j.appender.CA.layout=org.apache.log4j.PatternLayout
log4j.appender.CA.layout.ConversionPattern= %d{hh:mm:ss,SSS} [%t] %-5p %c %x - %m%n
// 流程引擎配置
ProcessEngineConfiguration cfg = new StandaloneProcessEngineConfiguration()
.setJdbcUrl(url)
.setJdbcUsername(username)
.setJdbcPassword(password)
.setJdbcDriver(driverClassName)
// 初始化基础表,不需要的可以改为 DB_SCHEMA_UPDATE_FALSE
.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
// 初始化流程引擎对象
ProcessEngine processEngine = cfg.buildProcessEngine();
代码部分
// 流程引擎配置
ProcessEngineConfiguration cfg = ProcessEngineConfiguration
// 根据文件名获取配置文件
//.createProcessEngineConfigurationFromResource("activiti.cfg.xml");
// 获取默认配置文件,默认的就是 activiti.cfg.xml
.createProcessEngineConfigurationFromResourceDefault()
// 初始化基础表,不需要的可以改为 DB_SCHEMA_UPDATE_FALSE
.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
// 初始化流程引擎对象
ProcessEngine processEngine = cfg.buildProcessEngine();
新建 flowable.cfg.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="processEngineConfiguration" class="org.flowable.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/test"/>
<property name="jdbcDriver" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUsername" value="root"/>
<property name="jdbcPassword" value="123456"/>
<property name="databaseSchemaUpdate" value="true"/>
bean>
beans>
我的配置文件 ProcessEngineConfig.java
依赖
spring-boot-configuration-processor
加载配置文件lomok
简化 java 代码
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-configuration-processorartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.0version>
<scope>providedscope>
dependency>
/**
* 流程引擎配置文件
* @author: linjinp
* @create: 2019-10-21 16:49
**/
@Configuration
@ConfigurationProperties(prefix = "spring.datasource")
@Data
public class ProcessEngineConfig {
private Logger logger = LoggerFactory.getLogger(ProcessEngineConfig.class);
@Value("${spring.datasource.url}")
private String url;
@Value("${spring.datasource.driver-class-name}")
private String driverClassName;
@Value("${spring.datasource.username}")
private String username;
@Value("${spring.datasource.password}")
private String password;
@Value("${spring.datasource.publicKey}")
private String publicKey;
/**
* 初始化流程引擎
* @return
*/
@Primary
@Bean(name = "processEngine")
public ProcessEngine initProcessEngine() {
logger.info("=============================ProcessEngineBegin=============================");
// 流程引擎配置
ProcessEngineConfiguration cfg = null;
try {
cfg = new StandaloneProcessEngineConfiguration()
.setJdbcUrl(url)
.setJdbcUsername(username)
.setJdbcPassword(ConfigTools.decrypt(publicKey, password))
.setJdbcDriver(driverClassName)
// 初始化基础表,不需要的可以改为 DB_SCHEMA_UPDATE_FALSE
.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE)
// 默认邮箱配置
// 发邮件的主机地址,先用 QQ 邮箱
.setMailServerHost("smtp.qq.com")
// POP3/SMTP服务的授权码
.setMailServerPassword("xxxxxxx")
// 默认发件人
.setMailServerDefaultFrom("[email protected]")
// 设置发件人用户名
.setMailServerUsername("管理员")
// 解决流程图乱码
.setActivityFontName("宋体")
.setLabelFontName("宋体")
.setAnnotationFontName("宋体");
} catch (Exception e) {
e.printStackTrace();
}
// 初始化流程引擎对象
ProcessEngine processEngine = cfg.buildProcessEngine();
logger.info("=============================ProcessEngineEnd=============================");
return processEngine;
}
}
PS:这里没有单独对流程引擎中的 8 个核心服务做初始化,是因为使用 flowable-spring-boot-starter
依赖,会自动帮忙注册好,不需要自己再注册,直接使用即可
如果你使用的依赖是 flowable-engine
,你可能还需要
//八大接口
// 业务流程的定义相关服务
@Bean
public RepositoryService repositoryService(ProcessEngine processEngine){
return processEngine.getRepositoryService();
}
// 流程对象实例相关服务
@Bean
public RuntimeService runtimeService(ProcessEngine processEngine){
return processEngine.getRuntimeService();
}
// 流程任务节点相关服务
@Bean
public TaskService taskService(ProcessEngine processEngine){
return processEngine.getTaskService();
}
// 流程历史信息相关服务
@Bean
public HistoryService historyService(ProcessEngine processEngine){
return processEngine.getHistoryService();
}
// 表单引擎相关服务
@Bean
public FormService formService(ProcessEngine processEngine){
return processEngine.getFormService();
}
// 用户以及组管理相关服务
@Bean
public IdentityService identityService(ProcessEngine processEngine){
return processEngine.getIdentityService();
}
// 管理和维护相关服务
@Bean
public ManagementService managementService(ProcessEngine processEngine){
return processEngine.getManagementService();
}
// 动态流程服务
@Bean
public DynamicBpmnService dynamicBpmnService(ProcessEngine processEngine){
return processEngine.getDynamicBpmnService();
}
//八大接口 end
版本为 6.4.1
,不多说了,看文章开头下载源码
打开文件夹 flowable-ui-modeler
路径:flowable-engine-flowable-6.4.1\modules\flowable-ui-modeler
resource/static
下这些都是需要用到的
使用 rest,logic,conf 的依赖
<dependency>
<groupId>org.flowablegroupId>
<artifactId>flowable-ui-modeler-restartifactId>
<version>6.4.1version>
dependency>
<dependency>
<groupId>org.flowablegroupId>
<artifactId>flowable-ui-modeler-logicartifactId>
<version>6.4.1version>
dependency>
<dependency>
<groupId>org.flowablegroupId>
<artifactId>flowable-ui-modeler-confartifactId>
<version>6.4.1version>
dependency>
在项目中的 resource
文件夹下新建一个 static
文件夹
SpringBoot 能自动读取 static
目录下的静态文件,因此文件夹名称不可随意更改
复制 flowable-ui-modeler-app
包中 resources\static
下所有文件,复制到新建的 static
下
路径:flowable-engine-flowable-6.4.1\modules\flowable-ui-modeler\flowable-ui-modeler-app\src\main\resources\static
复制以下文件到自己的项目中
ApplicationConfiguration.java
路径:flowable-engine-flowable-6.4.1\modules\flowable-ui-modeler\flowable-ui-modeler-conf\src\main\java\org\flowable\ui\modeler\conf
原因:这个文件是启动中必要的配置文件,需要做修改,详细的可以看下 app 中启动类,文件路径随意
AppDispatcherServletConfiguration.java
路径:flowable-engine-flowable-6.4.1\modules\flowable-ui-modeler\flowable-ui-modeler-conf\src\main\java\org\flowable\ui\modeler\servlet
原因:这个文件是启动中必要的配置文件,需要做修改,详细的可以看下 app 中启动类,文件路径随意
StencilSetResource.java
路径:flowable-engine-flowable-6.4.1\modules\flowable-ui-modeler\flowable-ui-modeler-rest\src\main\java\org\flowable\ui\modeler\rest\app
同时在 resource 下新建一个 stencilset 文件夹用来放汉化文件,可以直接下载我上传的
原因:国际化配置加载,为了使用我们自己的汉化文件因此把文件拿出来并修改,文件路径随意
PS:复制出来后要对这个文件进行重命名,否则会与 Jar 包里的文件产生 Bean 存在的冲突
我这重命名后叫 FlowableStencilSetResource.java
SecurityUtils
路径:flowable-engine-flowable-6.4.1\modules\flowable-ui-common\src\main\java\org\flowable\ui\common\security
原因:流程模型加载需要调用的工具类,文件路径需要与原路径保持一致
也就是包路径必须是 org.flowable.ui.common.security
这样在 Jar 中的方法在调用时会覆盖原 Jar 里的工具类
此文件不需要过多说明,主要移除 IDM 方面的配置
注意 conf 目录不要引入,里面也包含和 IDM 相关的配置
@Configuration
@EnableConfigurationProperties(FlowableModelerAppProperties.class)
@ComponentScan(basePackages = {
// "org.flowable.ui.modeler.conf", // 不引入 conf
"org.flowable.ui.modeler.repository",
"org.flowable.ui.modeler.service",
// "org.flowable.ui.modeler.security", //授权方面的都不需要
// "org.flowable.ui.common.conf", // flowable 开发环境内置的数据库连接
// "org.flowable.ui.common.filter", // IDM 方面的过滤器
"org.flowable.ui.common.service",
"org.flowable.ui.common.repository",
//
// "org.flowable.ui.common.security",//授权方面的都不需要
"org.flowable.ui.common.tenant" },excludeFilters = {
// 移除 RemoteIdmService
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = RemoteIdmService.class)
}
)
public class ApplicationConfiguration {
@Bean
public ServletRegistrationBean modelerApiServlet(ApplicationContext applicationContext) {
AnnotationConfigWebApplicationContext dispatcherServletConfiguration = new AnnotationConfigWebApplicationContext();
dispatcherServletConfiguration.setParent(applicationContext);
dispatcherServletConfiguration.register(ApiDispatcherServletConfiguration.class);
DispatcherServlet servlet = new DispatcherServlet(dispatcherServletConfiguration);
ServletRegistrationBean registrationBean = new ServletRegistrationBean(servlet, "/api/*");
registrationBean.setName("Flowable Modeler App API Servlet");
registrationBean.setLoadOnStartup(1);
registrationBean.setAsyncSupported(true);
return registrationBean;
}
}
同理,为了不引入 IDM 的配置
@Configuration
@ComponentScan(value = { "org.flowable.ui.modeler.rest.app",
// 不加载 rest,因为 getAccount 接口需要我们自己实现
// "org.flowable.ui.common.rest"
},excludeFilters = {
// 移除 EditorUsersResource 与 EditorGroupsResource,因为不使用 IDM 部分
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = EditorUsersResource.class),
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = EditorGroupsResource.class),
// 配置文件用自己的
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = StencilSetResource.class),
}
)
@EnableAsync
public class AppDispatcherServletConfiguration implements WebMvcRegistrations {
private static final Logger LOGGER = LoggerFactory.getLogger(AppDispatcherServletConfiguration.class);
@Bean
public SessionLocaleResolver localeResolver() {
return new SessionLocaleResolver();
}
@Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
LOGGER.debug("Configuring localeChangeInterceptor");
LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
localeChangeInterceptor.setParamName("language");
return localeChangeInterceptor;
}
@Override
public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
LOGGER.debug("Creating requestMappingHandlerMapping");
RequestMappingHandlerMapping requestMappingHandlerMapping = new RequestMappingHandlerMapping();
requestMappingHandlerMapping.setUseSuffixPatternMatch(false);
requestMappingHandlerMapping.setRemoveSemicolonContent(false);
Object[] interceptors = { localeChangeInterceptor() };
requestMappingHandlerMapping.setInterceptors(interceptors);
return requestMappingHandlerMapping;
}
}
这个主要保存时候会调这里的接口
将 getCurrentUserObject
方法进行修改,让他获取默认的 admin
/**
* @return the {@link User} object associated with the current logged in user.
*/
public static User getCurrentUserObject() {
if (assumeUser != null) {
return assumeUser;
}
RemoteUser user = new RemoteUser();
user.setId("admin");
user.setDisplayName("Administrator");
user.setFirstName("Administrator");
user.setLastName("Administrator");
user.setEmail("[email protected]");
user.setPassword("123456");
List<String> pris = new ArrayList<>();
pris.add(DefaultPrivileges.ACCESS_MODELER);
pris.add(DefaultPrivileges.ACCESS_IDM);
pris.add(DefaultPrivileges.ACCESS_ADMIN);
pris.add(DefaultPrivileges.ACCESS_TASK);
pris.add(DefaultPrivileges.ACCESS_REST_API);
user.setPrivileges(pris);
return user;
}
新建文件 FlowableController
,自己随意
在加载页面时候会调用这个接口获取用户信息,由于我们绕过了登陆,因此给个默认的用户 admin
为了不和原文件冲突,所以 @RequestMapping("/login")
/**
* Flowable 相关接口
* @author linjinp
* @date 2019/10/31 10:55
*/
@RestController
@RequestMapping("/login")
public class FlowableController {
/**
* 获取默认的管理员信息
* @return
*/
@RequestMapping(value = "/rest/account", method = RequestMethod.GET, produces = "application/json")
public UserRepresentation getAccount() {
UserRepresentation userRepresentation = new UserRepresentation();
userRepresentation.setId("admin");
userRepresentation.setEmail("[email protected]");
userRepresentation.setFullName("Administrator");
// userRepresentation.setLastName("Administrator");
userRepresentation.setFirstName("Administrator");
List<String> privileges = new ArrayList<>();
privileges.add(DefaultPrivileges.ACCESS_MODELER);
privileges.add(DefaultPrivileges.ACCESS_IDM);
privileges.add(DefaultPrivileges.ACCESS_ADMIN);
privileges.add(DefaultPrivileges.ACCESS_TASK);
privileges.add(DefaultPrivileges.ACCESS_REST_API);
userRepresentation.setPrivileges(privileges);
return userRepresentation;
}
}
路径:resource\static\scripts\configuration\url-conf.js
将 getAccountUrl 的路径改为上面自己的 getAccount 接口的路径
记得重命名,我这重命名后叫 FlowableStencilSetResource
把配置文件路径改为我们自己目录下的路径
stencilset/stencilset_bpmn.json
与 stencilset/stencilset_cmmn.json
主要修改三个
自己目录
下的 ApplicationConfiguration 与 AppDispatcherServletConfiguration,可参考 app 的启动器Jar 包
里的 DatabaseConfiguration,这个文件是对表进行更新的,由于 conf
目录不引入,因此我们只能单独引入,具体内容可以自己看下这个文件exclude={SecurityAutoConfiguration.class}
//启用全局异常拦截器
@Import(value={
// 引入修改的配置
ApplicationConfiguration.class,
AppDispatcherServletConfiguration.class,
// 引入 DatabaseConfiguration 表更新转换
DatabaseConfiguration.class})
// Eureka 客户端
@EnableDiscoveryClient
@ComponentScan(basePackages = {"com.springcloud.*"})
@MapperScan("com.springcloud.*.dao")
// 移除 Security 自动配置
@SpringBootApplication(exclude={SecurityAutoConfiguration.class})
public class FlowableApplication {
public static void main(String[] args) {
SpringApplication.run(FlowableApplication.class, args);
}
}
https://localhost:8087/
自动跳转
首页不建议将业务代码和流程引擎混在一个项目中
如果一定要这样,遇到自己的 XML 总扫描不到,转下面的文章
SpringBoot 集成 Flowable + Flowable Modeler 导致自身 XML 扫描不到解决方案
文章如果存在什么问题,请及时留言反馈
集成后的代码:https://gitee.com/linjinp-spring-cloud/linjinp-spring-cloud
代码在 flowable-demo
包,IDEA Active profiles
配置为 sit
测试分支,单独启动即可