暂无图片
暂无图片
5
暂无图片
暂无图片
暂无图片

SpringBoot 集成Flowable设计器

ITSK 2021-03-14
9781
多动脑,勤思考!




上篇文章介绍了SpringBoot集成Flowable实现流程部署,可能小伙伴疑问bpmn xml文件怎么生成呢?可以安装eclipse插件、Flowable也提供了流程设计器可以绘制bpmn xml。公司项目需求是:前后端项目分离,前端使用bpmn插件生成bpmn xml文件,后端解析处理数据。今天主要介绍后端集成flowable设计器的过程中遇到的问题,笔者一步步做了总结。
强调!强调!强调!笔者使用的Flowable版本是6.5.0!

为什么需要自己集成Flowable设计器?因为SpringBoot提供的依赖只集成Flowable引擎模块,没有集成modeler模块。我们上篇文章提到SpringBoot集成Flowable需要导入如下依赖:
 <dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-boot-starter-process</artifactId>
<version>${flowable.version}</version>
</dependency>
可以看到没有集成和modeler相关的依赖,如下是flowable源码中提供的流程设计器模块:
因为只需要集成flowable操作model 接口相关组件,所以只需要集成logic模块即可,添加依赖如下:
     <dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-ui-modeler-logic</artifactId>
<version>${flowable.version}</version>
</dependency>
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-ui-common</artifactId>
<version>${flowable.version}</version>
</dependency>
</dependencies>
以下是总结的遇到的问题及解决方案:
问题1:集成flowable-ui-modeler-logic后,注入ModelRepository ModelService 启动服务报错如下:ModelRepository 、ModelService 没有注入到Spring容器中,所以需要我们手动注入:
@Service
public class SyFlowServiceImpl implements SyFlowService {
...
@Autowired
private ModelRepository modelRepository;


@Autowired
    private ModelService modelService;
    ...  }
Field modelRepository in com.flow.test.flowabledemo.service.impl.SyFlowServiceImpl required a bean of type 'org.flowable.ui.modeler.repository.ModelRepository' that could not be found.
解决方法:参考flowable flowable-ui-modeler-conf组件中的源码中的配置类扫描我们需要的注入的类所在的包即可:
添加ApplicationConfiguration.java、ApiDispatcherServletConfiguration.java配置类到项目任意位置:
@Configuration(proxyBeanMethods = false)
@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;
}
}


@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 ApiDispatcherServletConfiguration extends WebMvcConfigurationSupport {


@Autowired
protected ObjectMapper objectMapper;


@Bean
public SessionLocaleResolver localeResolver() {
return new SessionLocaleResolver();
}


/**
* RequestMappingHandlerMapping和RequestMappingHandlerAdapter是为Spring MVC分发请求所必须的
*RequestMappingHandlerMapping:主要是将Contoller的带RequestMapping方法,添加到处理方法映射器和路径方法解析器中
*RequestMappingHandlerAdapter:主要是解决请求的,会话,请求头部处理,数据的绑定等,然后从容器中,获取handlerMethod,处理业务逻辑,获取数据,并渲染视图,返回。
*注入RequestMappingHandlerMapping和RequestMappingHandlerAdapter Bean就相当于配置<context:annotation-config/>启用注解
*
* @return
*/
@Override
protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
RequestMappingHandlerMapping requestMappingHandlerMapping = new RequestMappingHandlerMapping();
requestMappingHandlerMapping.setUseSuffixPatternMatch(false);
requestMappingHandlerMapping.setRemoveSemicolonContent(false);
return requestMappingHandlerMapping;
}


@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
addDefaultHttpMessageConverters(converters);
for (HttpMessageConverter<?> converter : converters) {
if (converter instanceof MappingJackson2HttpMessageConverter) {
MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = (MappingJackson2HttpMessageConverter) converter;
jackson2HttpMessageConverter.setObjectMapper(objectMapper);
break;
}
}
}
}
问题2:启动后访问接口直接重定向到登录页面,
然后看控制台输出密码如下:看提示我们知道是因为导入spring security的原因,但是自己并没有手动注入,那就是flowable自己依赖了spring security服务,果不其然看flowable源码确实有依赖。
解决方法,将spring security依赖排除:不需要排除spring-security-config,看源码指定:FlowableRemoteIdmAutoConfiguration类中使用了该包下的GrantedAuthorityDefaults类:
<!--flowable依赖-->
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-boot-starter-process</artifactId>
<version>${flowable.version}</version>
<exclusions>
<exclusion>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-security</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-ui-modeler-logic</artifactId>
<version>${flowable.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
</exclusion>
<!-- <exclusion>-->
<!-- <groupId>org.springframework.security</groupId>-->
<!-- <artifactId>spring-security-config</artifactId>-->
<!-- </exclusion>-->
<exclusion>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-crypto</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-ui-common</artifactId>
<version>${flowable.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
</exclusion>
<!-- <exclusion>-->
<!-- <groupId>org.springframework.security</groupId>-->
<!-- <artifactId>spring-security-config</artifactId>-->
<!-- </exclusion>-->
<exclusion>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-crypto</artifactId>
</exclusion>
</exclusions>
</dependency>
问题3:报错如下:在Spring容器中没有找到SqlSessionTemplate
Description:Field sqlSessionTemplate in org.flowable.ui.modeler.repository.ModelRepositoryImpl required a bean of type 'org.mybatis.spring.SqlSessionTemplate' that could not be found.The injection point has the following annotations:  - @org.springframework.beans.factory.annotation.Autowired(required=true)Action:Consider defining a bean of type 'org.mybatis.spring.SqlSessionTemplate' in your configuration.
解决方法:引入SpringBoot集成mybatis的依赖:
 <dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>
问题4:调用ModelService的保存模板方法报错:找不到flowable的mapper配置文件:
Cause: java.lang.IllegalArgumentException: Mapped Statements collection does not contain value for org.flowable.ui.modeler.domain.Model.insertModel
解决方法:修改application.yml配置:
mybatis:
mapper-locations: classpath:/META-INF/modeler-mybatis-mappings/*.xml
问题5:报错如下:
Cause: org.apache.ibatis.builder.BuilderException: Error resolving JdbcType. Cause: java.lang.IllegalArgumentException: No enum constant org.apache.ibatis.type.JdbcType.${blobType}
解决方法:application.yml配置:
mybatis:  configuration-properties:    blobType: BLOB    boolValue: TRUE
问题6:报错如下:
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'prefix' in 'class org.flowable.ui.modeler.domain.Model'
解决方法:修改application.yml配置:
mybatis:
configuration-properties:
prefix:
问题7:报错如下,找不到act_de_model表,因为flowable将act_re_model改成了act_de_model表了,所以需要手动添加配置类生产act_de_model表:
org.springframework.jdbc.BadSqlGrammarException:
### Error updating database. Cause: java.sql.SQLSyntaxErrorException: Table 'flowable-study.act_de_model' doesn't exist
解决方法:直接参考flowable-ui-modeler-conf源码中DatabaseConfiguration.java配置类配置即可,如下:使用Liquibase自动生成act_de_开头数据表
@Configuration
public class DatabaseConfiguration {


private static final Logger LOGGER = LoggerFactory.getLogger(DatabaseConfiguration.class);


protected static final String LIQUIBASE_CHANGELOG_PREFIX = "ACT_DE_";


@Bean
public Liquibase liquibase(DataSource dataSource) {
LOGGER.info("Configuring Liquibase");


Liquibase liquibase = null;
try {
DatabaseConnection connection = new JdbcConnection(dataSource.getConnection());
Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(connection);
database.setDatabaseChangeLogTableName(LIQUIBASE_CHANGELOG_PREFIX + database.getDatabaseChangeLogTableName());
database.setDatabaseChangeLogLockTableName(LIQUIBASE_CHANGELOG_PREFIX + database.getDatabaseChangeLogLockTableName());


liquibase = new Liquibase("META-INF/liquibase/flowable-modeler-app-db-changelog.xml", new ClassLoaderResourceAccessor(), database);
liquibase.update("flowable");
return liquibase;
} catch (Exception e) {
throw new InternalServerErrorException("Error creating liquibase database", e);
} finally {
closeDatabase(liquibase);
}
}


private void closeDatabase(Liquibase liquibase) {
if (liquibase != null) {
Database database = liquibase.getDatabase();
if (database != null) {
try {
database.close();
} catch (DatabaseException e) {
LOGGER.warn("Error closing database", e);
}
}
}
}


}
问题8:调用ModelService保存model方法时,设置数据隔离的问题(何为数据隔离 ,简单理解就是区分不同服务的数据,因为我们项目是ToB的,即为区分不同企业间的数据),我们看flowable源码如下:
ModelRepositoryImpl.java:
@Autowired
protected TenantProvider tenantProvider;
@Override
public void save(Model model) {
model.setTenantId(tenantProvider.getTenantId());
if (model.getId() == null) {
model.setId(idGenerator.generateId());
sqlSessionTemplate.insert(NAMESPACE + "insertModel", model);
} else {
sqlSessionTemplate.update(NAMESPACE + "updateModel", model);
}
}
debug一下发现tenantProvider.getTenantId()获取的是:DefaultTenantProvider中的getTenantId()方法,获取的配置文件FlowableCommonAppProperties中的:只要我们在application.yml文件中配置flowable.common.app.tenantId值即可,但是我们在实际开发中,tenantId值不是写死在配置文件中,而是动态获取的,那怎么办呢?
解决办法:举例就像我们项目中是拦截header请求中的token,从中解析获取的,所以我的解决办法是:注入自己的DefaultTenantProvider类,重写获取getTenantId方法,然后再写一个请求的监听器,只要监听到请求然后就将解析出的tenantId设置即可。由于隐私问题,这里只提供思路,有需要小伙伴可以自行实现。
解决完这里几个问题就可以完美调用flowable操作流程模板相关的接口啦~~

长按二维码关注我们

ITSK

博客|yajing8


我知道你在看

文章转载自ITSK,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论