说明
本工程为工作流样例工程,用于熟悉和学习使用flowable组件
1. Flowable简介
Flowable是一个使用Java编写的轻量级业务流程引擎。Flowable流程引擎可用于部署BPMN 2.0流程定义(用于定义流程的行业XML标准), 创建这些流程定义的流程实例,进行查询,访问运行中或历史的流程实例与相关数据。
BPMN(Business Process Modeling Notation),是指业务流程建模与标注,包括这些图元如何组合成一个业务流程图(Business Process Diagram)
flowable / flowəb(ə)l /
-
a compact and highly efficient workflow and Business Process Management (BPM) platform for developers, system admins and business users.
-
a lightning fast, tried and tested BPMN 2 process engine written in Java. It is Apache 2.0 licensed open source, with a committed community.
-
can run embedded in a Java application, or as a service on a server, a cluster, and in the cloud. It integrates perfectly with Spring. With a rich Java and REST API, it is the ideal engine for orchestrating human or system activities.
2. Flowable与Activiti
Activiti和Flowable都是来自于一个叫JBPM的开源工作流
Flowable 基于activiti开发
3. 依赖包
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-boot-starter-process</artifactId>
<version>${flowable.version}</version>
</dependency>
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-ui-modeler-rest</artifactId>
<version>${flowable.version}</version>
</dependency>
考虑到有可能使用二次编译的源码依赖,本项目使用使用以下依赖
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-engine</artifactId>
<version>${flowable.version}</version>
</dependency>
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring</artifactId>
<version>${flowable.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-ui-modeler-rest</artifactId>
<version>${flowable.version}</version>
</dependency>
其它依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>
4. 工程结构
4.1 总体结构
. ├── doc # 工程文档 │ ├── img │ ├── pdm # PDM图 │ └── sql # 初始化SQL(备用,实际由项目首次启动初始化) ├── fintell-workflow-frontend # 前端服务 │ └── src │ ├── main │ │ ├── java │ │ │ └── com │ │ │ └── fintell │ │ └── resources │ │ ├── db │ │ │ └── changelog │ │ ├── static # 静态资源,前端 │ │ │ │ │ └── templates │ └── test └── fintell-workflow-service # 后端服务 └── src ├── main └── test
4.2后端工程结构
. └── src ├── main │ ├── java │ │ └── com │ │ └── fintell │ │ └── flowable │ │ ├── config │ │ ├── constants │ │ ├── controller │ │ └── dto │ │ ├── data # 数据类DTO │ │ ├── operate # 操作类DTO │ │ │ └── flowable │ │ └── query # 查询类DTO │ │ └── flowable │ └── resources │ ├── db │ │ └── changelog │ ├── properties │ ├── static │ │ ├── display │ │ ├── display-cmmn │ │ ├── display-dmn │ │ ├── editor-app │ │ ├── fonts │ │ ├── i18n │ │ ├── images │ │ ├── libs │ │ ├── scripts │ │ ├── styles │ │ └── views │ └── templates └── test └── java └── com └── fintell
5. 配置Bean
/**
* 核心配置
*/
@Bean
public SpringProcessEngineConfiguration springProcessEngineConfiguration(){
SpringProcessEngineConfiguration springProcessEngineConfiguration
= new SpringProcessEngineConfiguration();
springProcessEngineConfiguration.setDataSource(dataSource); springProcessEngineConfiguration.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE); springProcessEngineConfiguration.setTransactionManager(dataSourceTransactionManager(dataSource));
return springProcessEngineConfiguration;
}
@Bean
public DataSourceTransactionManager dataSourceTransactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
// ********************** 数据库配置 start **********************
@Bean(destroyMethod = "clearCache") // destroyMethod: see https://github.com/mybatis/old-google-code-issues/issues/778
public SqlSessionTemplate SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
@Bean(destroyMethod = "clearCache") // destroyMethod: see https://github.com/mybatis/old-google-code-issues/issues/778
@Qualifier("flowableModeler")
public SqlSessionTemplate modelerSqlSessionTemplate(@Qualifier("flowableModeler") SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
@Bean
@Qualifier("flowableModeler")
public SqlSessionFactory modelerSqlSessionFactory(DataSource dataSource) {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
String databaseType = initDatabaseType(dataSource);
if (databaseType == null) {
throw new FlowableException("couldn't deduct database type");
}
try {
Properties properties = new Properties();
properties.put("prefix","");
properties.put("blobType", "BLOB");
properties.put("boolValue", "TRUE");
properties.load(this.getClass().getClassLoader().getResourceAsStream("properties/" + databaseType + ".properties"));
sqlSessionFactoryBean.setConfigurationProperties(properties);
sqlSessionFactoryBean
.setMapperLocations(ResourcePatternUtils.getResourcePatternResolver(resourceLoader).getResources("classpath:/META-INF/modeler-mybatis-mappings/*.xml"));
sqlSessionFactoryBean.afterPropertiesSet();
return sqlSessionFactoryBean.getObject();
} catch (Exception e) {
throw new FlowableException("Could not create sqlSessionFactory", e);
}
}
protected String initDatabaseType(DataSource dataSource) {
String databaseType = null;
Connection connection = null;
try {
connection = dataSource.getConnection();
DatabaseMetaData databaseMetaData = connection.getMetaData();
String databaseProductName = databaseMetaData.getDatabaseProductName();
LOGGER.info("database product name: '{}'", databaseProductName);
databaseType = databaseTypeMappings.getProperty(databaseProductName);
if (databaseType == null) {
throw new FlowableException("couldn't deduct database type from database product name '" + databaseProductName + "'");
}
LOGGER.info("using database type: {}", databaseType);
} catch (SQLException e) {
LOGGER.error("Exception while initializing Database connection", e);
} finally {
try {
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
LOGGER.error("Exception while closing the Database connection", e);
}
}
return databaseType;
}
protected static Properties databaseTypeMappings = getDefaultDatabaseTypeMappings();
public static final String DATABASE_TYPE_H2 = "h2";
public static final String DATABASE_TYPE_HSQL = "hsql";
public static final String DATABASE_TYPE_MYSQL = "mysql";
public static final String DATABASE_TYPE_ORACLE = "oracle";
public static final String DATABASE_TYPE_POSTGRES = "postgres";
public static final String DATABASE_TYPE_MSSQL = "mssql";
public static final String DATABASE_TYPE_DB2 = "db2";
public static Properties getDefaultDatabaseTypeMappings() {
Properties databaseTypeMappings = new Properties();
databaseTypeMappings.setProperty("H2", DATABASE_TYPE_H2);
databaseTypeMappings.setProperty("HSQL Database Engine", DATABASE_TYPE_HSQL);
databaseTypeMappings.setProperty("MySQL", DATABASE_TYPE_MYSQL);
databaseTypeMappings.setProperty("MariaDB", DATABASE_TYPE_MYSQL);
databaseTypeMappings.setProperty("Oracle", DATABASE_TYPE_ORACLE);
databaseTypeMappings.setProperty("PostgreSQL", DATABASE_TYPE_POSTGRES);
databaseTypeMappings.setProperty("Microsoft SQL Server", DATABASE_TYPE_MSSQL);
databaseTypeMappings.setProperty(DATABASE_TYPE_DB2, DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/NT", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/NT64", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2 UDP", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/LINUX", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/LINUX390", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/LINUXX8664", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/LINUXZ64", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/LINUXPPC64", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/400 SQL", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/6000", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2 UDB iSeries", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/AIX64", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/HPUX", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/HP64", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/SUN", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/SUN64", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/PTX", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/2", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2 UDB AS400", DATABASE_TYPE_DB2);
return databaseTypeMappings;
}
@Bean
@Qualifier("flowableModeler")
public Liquibase modelerLiquibase(DataSource dataSource) {
LOGGER.info("Configuring Liquibase");
try {
return LiquibaseUtil.runInFlowableScope(() -> createAndUpdateLiquibase(dataSource));
} catch (Exception e) {
throw new InternalServerErrorException("Error creating liquibase database", e);
}
}
public Liquibase createAndUpdateLiquibase(DataSource dataSource) {
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);
}
}
}
}
// ********************** 数据库配置 end **********************
// ********************** 其它依赖配置 **********************
@Bean
public FlowableModelerAppProperties flowableModelerAppProperties(){
FlowableModelerAppProperties flowableModelerAppProperties = new FlowableModelerAppProperties();
return flowableModelerAppProperties;
}
@Bean
public ModelService modelerModelService() {
return new ModelServiceImpl();
}
@Bean
public ModelImageService modelerModelImageService() {
return new ModelImageService();
}
@Bean
public FlowableModelQueryService modelerModelQueryService() {
return new FlowableModelQueryService();
}
后续将更新如何集成设计器到springboot
附:资源
# 官网 https://www.flowable.com/open-source # 源码 https://github.com/flowable # 中文手册(源码中的说明文档更为详细) https://tkjohn.github.io/flowable-userguide/#_introduction