Activiti 6.0源码初探

1,766 阅读3分钟

为啥要学习源码?

好处

  • 通过源码可以了解代码关系结构体会代码设计的细节,阅读源码可以快速提升开发技术水平
  • 阅读源代码是初级工程师走向中高级工程师的必修课

Activiti6.0源码概述

源码概述

GitHub的工作方式

image-20210207133700066.png

image-20210207134741689.png

image-20210207134635997.png

image-20210207142026122.png

  • 使用mavne进行编译 : mvn clean test-compile

Activiti模块介绍

  • 项目工程结构

image-20210208112316326.png

  • 工程子模块(编译设计是从上到下)

image-20210208112420755.png

  • 工程下每一个包对应解释

image-20210208173151526.png

image-20210208173939519.png

image-20210208180502625.png

image-20210208182505351.png

核心模块
  • 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 对应的表单

image-20210208204424350.png

基于源码运行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配置

image-20210209103157442.png

  • WebConfigurer 成员

image-20210209105000244.png

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

image-20210209134955439.png

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插件

image-20210209215947656.png

IDEA 创建Maven工程

Maven工程基础

image-20210209220039701.png

Pom依赖说明

image-20210209220115369.png

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