为啥要学习源码?
好处
- 通过源码可以了解代码关系结构体会代码设计的细节,阅读源码可以快速提升开发技术水平
- 阅读源代码是初级工程师走向中高级工程师的必修课
Activiti6.0源码概述
源码概述
GitHub的工作方式
-
gitHub上获取源码:github.com/Activiti/Ac…
-
创建分支 git checkout -b study6 activiti-6.0.0
- 使用mavne进行编译 : mvn clean test-compile
Activiti模块介绍
- 项目工程结构
- 工程子模块(编译设计是从上到下)
- 工程下每一个包对应解释
核心模块
- module/activiti-engine 核心引擎
- module/activiti-spring Spring集成模块
- module/activiti-spring-boot SpringBoot 集成模块
- module/ativiti-rest 对外提供rest-api模块
- module/activiti-form-engine 表单引擎模块
- module/activiti-ldap 集成ldap用户模块(和公司/企业用户提做集成)
Acitviti-engine 直接依赖模块
- bpmn-converter 主要是对模型转化
- process-validation 里程模型校验,是否合法
- dmp-api 新的决策标准
- image-generate xml文件在流文件如何把流程图绘制出来
- form-api 对应的表单
- form-model 对应的表单
基于源码运行activiti-app
启动activiti-app
1 $ cd modules/activiti-ui/activiti-app
2 $ cd mvn clean tomcat7:run
3 $ open http://locahot:9999/activiti-app
activiti-ui
- activiti-app 集成发布的war包
- activiti-app-conf UI独立于业务之外的配置
- activiti-logic UI的业务逻辑
- activiti-app-rest 提供接口的rest api
web.xml
-
activiti -app 是activiti-ui整个工程的聚合,所以从web.xml入手。
-
web.xml 位置 ../activiti-ui/activiti-app/src/main/webapp/WEB-INF/web.xml
-
查看web.xml 里面Listener配置
- WebConfigurer 成员
WebConfigurer
contextInitialized
@Override
public void contextInitialized(ServletContextEvent sce) {
log.debug("Configuring Spring root application context");
ServletContext servletContext = sce.getServletContext();
AnnotationConfigWebApplicationContext rootContext = null;
if (context == null) {
rootContext = new AnnotationConfigWebApplicationContext(); // 如果上下问你容器为空重新构建容器
rootContext.register(ApplicationConfiguration.class); // 定义一个根容器,构造容器里面的bean
if (rootContext.getServletContext() == null) {
rootContext.setServletContext(servletContext);
}
rootContext.refresh();
context = rootContext;
} else {
rootContext = context;
if (rootContext.getServletContext() == null) {
rootContext.setServletContext(servletContext); // 把servletContext容器放在Spring容器中
}
}
// 把Spring容器和Tomcat容器做了一个双向绑定,首先把Sping容器放在Servlet容器里面,作为一个属性放进去
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, rootContext);
EnumSet<DispatcherType> disps = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.ASYNC);
initSpring(servletContext, rootContext); // 初始化Spring相关类容
initSpringSecurity(servletContext, disps); // 初始化SpringSecurity相关类容
log.debug("Web application fully configured");
}
initSpring
private void initSpring(ServletContext servletContext, AnnotationConfigWebApplicationContext rootContext) {
log.debug("Configuring Spring Web application context");
AnnotationConfigWebApplicationContext appDispatcherServletConfiguration = new AnnotationConfigWebApplicationContext();
appDispatcherServletConfiguration.setParent(rootContext); // 上文传递的context
// 先注册一个根容器初始化完成后在注册两个子容器 (一个是appDispatcher,另一个是 apiDispatcher)
appDispatcherServletConfiguration.register(AppDispatcherServletConfiguration.class);
log.debug("Registering Spring MVC Servlet");
ServletRegistration.Dynamic appDispatcherServlet = servletContext.addServlet("appDispatcher",
new DispatcherServlet(appDispatcherServletConfiguration));
appDispatcherServlet.addMapping("/app/*");
appDispatcherServlet.setLoadOnStartup(1);
appDispatcherServlet.setAsyncSupported(true);
log.debug("Registering Activiti public REST API");
AnnotationConfigWebApplicationContext apiDispatcherServletConfiguration = new AnnotationConfigWebApplicationContext();
apiDispatcherServletConfiguration.setParent(rootContext);
// 先注册一个根容器初始化完成后在注册两个子容器 (一个是appDispatcher,另一个是 apiDispatcher)
apiDispatcherServletConfiguration.register(ApiDispatcherServletConfiguration.class);
ServletRegistration.Dynamic apiDispatcherServlet = servletContext.addServlet("apiDispatcher",
new DispatcherServlet(apiDispatcherServletConfiguration));
apiDispatcherServlet.addMapping("/api/*");
apiDispatcherServlet.setLoadOnStartup(1);
apiDispatcherServlet.setAsyncSupported(true);
}
WebConfigurer
WebConfigurer 这个Listtener 参考了SpringMVC对应的LoadContextListener,对应容器为ApplicationConfiguration
AppContextDispatcherServlet是对应整个业务(内部),对应容器为AppDispatcherServletConfiguration
ApiContextDispatcherServlet 是对外提供的API , 对应容器为ApiDispatcherServletConfiguration
ApplicationConfiguration(根容器)
根容器主要是配置在类上的注解
@Configuration
@PropertySources({
@PropertySource("classpath:/META-INF/activiti-app/activiti-app.properties"),
@PropertySource(value = "classpath:activiti-app.properties", ignoreResourceNotFound = true),
// ignoreResourceNotFound = true 是支持包扫描找不到的
@PropertySource(value = "file:activiti-app.properties", ignoreResourceNotFound = true),
})
//和后天处理相关的一些配置,想这些配置都是唯一性的
@ComponentScan(basePackages = {
"org.activiti.app.conf",
"org.activiti.app.repository",
"org.activiti.app.service",
"org.activiti.app.security",
"org.activiti.app.model.component"})
@EnableJpaRepositories({ "org.activiti.app.repository" })
@EntityScan({"org.activiti.app.domain"})
@EnableTransactionManagement
public class ApplicationConfiguration {
/**
* This is needed to make property resolving work on annotations ...
* (see http://stackoverflow.com/questions/11925952/custom-spring-property-source-does-not-resolve-placeholders-in-value)
*
* @Scheduled(cron="${someProperty}")
*/
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
AppDispatcherServletConfiguration
@Configuration
@ComponentScan(value = {"org.activiti.app.rest"})
@EnableAsync
public class AppDispatcherServletConfiguration extends WebMvcConfigurationSupport {
// 类里面的很多配置都是和前端配置相关的内容
private final Logger log = LoggerFactory.getLogger(AppDispatcherServletConfiguration.class);
@Inject
private ObjectMapper objectMapper; // 用来处理JSON序列化
@Inject
private Environment environment; // 环境的信息
@Bean // 用来检查本地信息或者持续的信息
public SessionLocaleResolver localeResolver() {
return new SessionLocaleResolver();
}
@Bean // 本地信息获取语言信息
public LocaleChangeInterceptor localeChangeInterceptor() {
log.debug("Configuring localeChangeInterceptor");
LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
localeChangeInterceptor.setParamName("language");
return localeChangeInterceptor;
}
@Bean // 文件上传最大值
public MultipartResolver multipartResolver() {
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
multipartResolver.setMaxUploadSize(environment.getProperty("file.upload.max.size", Long.class));
return multipartResolver;
}
@Bean // SpringMVC最重要的是HandlerMapping,把每一个URL和方法映射起来
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
log.debug("Creating requestMappingHandlerMapping");
RequestMappingHandlerMapping requestMappingHandlerMapping = new RequestMappingHandlerMapping();
requestMappingHandlerMapping.setUseSuffixPatternMatch(false); // 不考虑后滤匹配,后滤匹配就是Struts(.do或者.xml作为后缀的)
requestMappingHandlerMapping.setRemoveSemicolonContent(false);// 是否以分号为区分,保留原始的url
Object[] interceptors = {localeChangeInterceptor()};
requestMappingHandlerMapping.setInterceptors(interceptors);
return requestMappingHandlerMapping;
}
@Override // 配置JSON转化的一些方法
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
addDefaultHttpMessageConverters(converters);
for (HttpMessageConverter<?> converter: converters) {
if (converter instanceof MappingJackson2HttpMessageConverter) {
MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = (MappingJackson2HttpMessageConverter) converter;
jackson2HttpMessageConverter.setObjectMapper(objectMapper);
break;
}
}
}
}
HelloWorld之Activiti 6.0
二级审批流程
Eclipse (设计流程图)
Eclipse安装Activiti6.0插件
-
Eclipse安装Activiti6.0插件 地址及其操作文档 blog.csdn.net/jenyzhang/a…
-
基于Eclipse画出工作流程图如下
IDEA 创建Maven工程
Maven工程基础
Pom依赖说明
基于SpringBoot 方式打包
pom文件中加入依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
</parent>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
maven 打包命令
mvn package
基于命令行交互的工作流程小程序
public static void main(String[] args) throws ParseException {
LOGGER.info("启动我们的程序");
/*
创建流程引擎
基于内存数据库存储
*/
ProcessEngine processEngine = getProcessEngine();
// 部署流程定义文件
ProcessDefinition processDefinition = getProcessDefinition(processEngine);
// 启动运行流
ProcessInstance processInstance = getProcessInstance(processEngine, processDefinition);
// 处理流程任务
processTask(processEngine, processInstance);
LOGGER.info("结束我们的程序");
}
创建流程引擎
private static ProcessEngine getProcessEngine() {
ProcessEngineConfiguration cfg = ProcessEngineConfiguration.createStandaloneInMemProcessEngineConfiguration();
ProcessEngine processEngine = cfg.buildProcessEngine(); // 创建流程
String name = processEngine.getName();
String version = ProcessEngine.VERSION;
LOGGER.info("流程引擎的名称[{}],流程引擎的版本[{}]", name, version);
return processEngine;
}
部署流程定义文件
private static ProcessDefinition getProcessDefinition(ProcessEngine processEngine) {
RepositoryService repositoryService = processEngine.getRepositoryService();
DeploymentBuilder deploymentBuilder = repositoryService.createDeployment();
deploymentBuilder.addClasspathResource("second_approve.bpmn20.xml");
Deployment deployment = deploymentBuilder.deploy();
String deploymentId = deployment.getId();
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId)
.singleResult();
LOGGER.info("流程定义文件[{}],流程ID[{}]", processDefinition.getName(), processDefinition.getId());
return processDefinition;
}
启动运行流
private static ProcessInstance getProcessInstance(ProcessEngine processEngine, ProcessDefinition processDefinition) {
RuntimeService runtimeService = processEngine.getRuntimeService();
ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinition.getId());
LOGGER.info("启动流程[{}]", processInstance.getProcessDefinitionKey());
return processInstance;
}
处理流程任务
private static void processTask(ProcessEngine processEngine, ProcessInstance processInstance) throws ParseException {
Scanner scanner = new Scanner(System.in);
while (processInstance != null && !processInstance.isEnded()) {
// 处理流程任务
TaskService taskService = processEngine.getTaskService();
List<Task> list = taskService.createTaskQuery().list();
LOGGER.info("待处理任务量[{}]", list.size());
for (Task task : list) {
LOGGER.info("待处理的任务[{}]", task.getName());
FormService formService = processEngine.getFormService();
TaskFormData taskFormData = formService.getTaskFormData(task.getId());
List<FormProperty> formProperties = taskFormData.getFormProperties();
HashMap<String, Object> variables = Maps.newHashMap();
String line = null;
for (FormProperty formProperty : formProperties) {
if (StringFormType.class.isInstance(formProperty.getType())) {
LOGGER.info("请输入[{}]?", formProperty.getName());
line = scanner.nextLine();
variables.put(formProperty.getId(), line);
} else if (DateFormType.class.isInstance(formProperty.getType())) {
LOGGER.info("请输入[{}]?格式(yyyy-MM-dd)", formProperty.getName());
line = scanner.nextLine();
SimpleDateFormat dateFormType = new SimpleDateFormat("yyyy-MM-dd");
Date date = dateFormType.parse(line);
variables.put(formProperty.getId(), date);
} else {
LOGGER.info("暂时不支持该内容[{}]", formProperty.getType());
}
LOGGER.info("您输入的类容是[{}]", line);
}
taskService.complete(task.getId(), variables);
processInstance = processEngine.getRuntimeService().createProcessInstanceQuery()
.processInstanceId(processInstance.getId()).singleResult();
}
}
scanner.close();
}