Why learn source code?
benefits
- Through the source code can understand the structure of the code relationship experience the details of code design, reading the source code can quickly improve the level of development technology
- Reading source code is a required course for junior engineers to move to senior engineers
Activiti6.0 source Overview
Summary of the source code
The way GitHub works
-
GitHub source: github.com/Activiti/Ac…
-
Git checkout -b study6 activiti-6.0.0
- Compile using MAVNE: MVN clean test-compile
The Activiti module is introduced
- Project engineering structure
- Engineering submodules (compile design from top to bottom)
- Each package under the project is explained accordingly
The core module
- Module /activiti-engine Core engine
- Module/Activiti-Spring Spring integration module
- Module /activiti-spring-boot SpringBoot integration module
- Module/ativiti-REST Provides restful API modules externally
- Module /activiti-form-engine Form engine module
- Module/Activiti-LDAP Integrated LDAP user module (integrated with corporate/enterprise users)
Acitviti-engine relies directly on modules
- Bpmn-converter is mainly for model conversion
- Process-validation validation of the mileage model
- Dmp-api new decision standard
- Image-generate XML file how to draw flow chart in stream file
- Form-api corresponds to the form
- Form-model Corresponds to the form
Run activiti-app based on the source code
Start the 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 integrates published WAR packages
- Activiti-app-conf UI is independent of configuration outside of the business
- Activiti-logic UI business logic
- Activiti-app-rest provides the rest API for the interface
web.xml
-
Activiti-app is an aggregation of the entire Activiti-UI project, so start with web.xml.
-
Web.xml position.. /activiti-ui/activiti-app/src/main/webapp/WEB-INF/web.xml
-
View the Listener configuration in web. XML
- WebConfigurer members
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(); // Rebuild the container if the top and bottom ask you if the container is empty
rootContext.register(ApplicationConfiguration.class); // Define a root container to construct the bean inside the container
if (rootContext.getServletContext() == null) {
rootContext.setServletContext(servletContext);
}
rootContext.refresh();
context = rootContext;
} else {
rootContext = context;
if (rootContext.getServletContext() == null) {
rootContext.setServletContext(servletContext); // Put the servletContext container in the Spring container}}// Make a bidirectional binding between the Spring and Tomcat containers. First, put the Sping container in the Servlet container as a property
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, rootContext);
EnumSet<DispatcherType> disps = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.ASYNC);
initSpring(servletContext, rootContext); // Initialize the spring-related class capacity
initSpringSecurity(servletContext, disps); // Initialize the springSecurity-related class capacity
log.debug("Web application fully configured");
}
Copy the code
initSpring
private void initSpring(ServletContext servletContext, AnnotationConfigWebApplicationContext rootContext) {
log.debug("Configuring Spring Web application context");
AnnotationConfigWebApplicationContext appDispatcherServletConfiguration = new AnnotationConfigWebApplicationContext();
appDispatcherServletConfiguration.setParent(rootContext); // The context passed above
After initialization, register two child containers (one is appDispatcher, the other is 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);
After initialization, register two child containers (one is appDispatcher, the other is apiDispatcher)
apiDispatcherServletConfiguration.register(ApiDispatcherServletConfiguration.class);
ServletRegistration.Dynamic apiDispatcherServlet = servletContext.addServlet("apiDispatcher".new DispatcherServlet(apiDispatcherServletConfiguration));
apiDispatcherServlet.addMapping("/api/*");
apiDispatcherServlet.setLoadOnStartup(1);
apiDispatcherServlet.setAsyncSupported(true);
}
Copy the code
WebConfigurer
The WebConfigurer Listtener refers to the LoadContextListener corresponding to SpringMVC. The corresponding container is ApplicationConfiguration
AppContextDispatcherServlet is corresponding to the entire business (internal), corresponding container for AppDispatcherServletConfiguration
ApiContextDispatcherServlet is available API, corresponding container for ApiDispatcherServletConfiguration
ApplicationConfiguration (Root container)
The root container is primarily an annotation configured on a class
@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), })
// Some configurations related to postnatal processing are assumed to be unique
@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(a) {
return newPropertySourcesPlaceholderConfigurer(); }}Copy the code
AppDispatcherServletConfiguration
@Configuration
@ComponentScan(value = {"org.activiti.app.rest"})
@EnableAsync
public class AppDispatcherServletConfiguration extends WebMvcConfigurationSupport {
// A lot of the configuration in the class is related to front-end configuration
private final Logger log = LoggerFactory.getLogger(AppDispatcherServletConfiguration.class);
@Inject
private ObjectMapper objectMapper; // To handle JSON serialization
@Inject
private Environment environment; // Environment information
@Bean // Check for local information or persistent information
public SessionLocaleResolver localeResolver(a) {
return new SessionLocaleResolver();
}
@Bean // Local information gets language information
public LocaleChangeInterceptor localeChangeInterceptor(a) {
log.debug("Configuring localeChangeInterceptor");
LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
localeChangeInterceptor.setParamName("language");
return localeChangeInterceptor;
}
@Bean // Maximum number of file uploads
public MultipartResolver multipartResolver(a) {
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
multipartResolver.setMaxUploadSize(environment.getProperty("file.upload.max.size", Long.class));
return multipartResolver;
}
@Bean // The most important thing in SpringMVC is HandlerMapping, which maps each URL and method
public RequestMappingHandlerMapping requestMappingHandlerMapping(a) {
log.debug("Creating requestMappingHandlerMapping");
RequestMappingHandlerMapping requestMappingHandlerMapping = new RequestMappingHandlerMapping();
requestMappingHandlerMapping.setUseSuffixPatternMatch(false); Struts (.do or.xml suffix)
requestMappingHandlerMapping.setRemoveSemicolonContent(false);// Whether to keep the original URL separated by semicolons
Object[] interceptors = {localeChangeInterceptor()};
requestMappingHandlerMapping.setInterceptors(interceptors);
return requestMappingHandlerMapping;
}
@Override // Configure some methods for JSON conversion
public void configureMessageConverters(List
> converters)
> {
addDefaultHttpMessageConverters(converters);
for(HttpMessageConverter<? > converter: converters) {if (converter instanceof MappingJackson2HttpMessageConverter) {
MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = (MappingJackson2HttpMessageConverter) converter;
jackson2HttpMessageConverter.setObjectMapper(objectMapper);
break; }}}}Copy the code
The HelloWorld Activiti 6.0
Secondary approval process
Eclipse (Design Flowchart)
Eclipse installs the Activiti6.0 plug-in
-
The Eclipse blog.csdn.net/jenyzhang/a Activiti6.0 plug-in installation address and their operating document…
-
The working flow chart based on Eclipse is drawn as follows
IDEA Creates the Maven project
Maven Engineering Basics
Pom dependency description
Package based on SpringBoot
Add dependencies to POM files
<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>
Copy the code
Maven package command
mvn package
Copy the code
Workflow applets based on command line interaction
public static void main(String[] args) throws ParseException {
LOGGER.info("Start our program.");
/* Create a process engine based on in-memory database storage */
ProcessEngine processEngine = getProcessEngine();
// Deploy the process definition file
ProcessDefinition processDefinition = getProcessDefinition(processEngine);
// Start the run stream
ProcessInstance processInstance = getProcessInstance(processEngine, processDefinition);
// Process process tasks
processTask(processEngine, processInstance);
LOGGER.info("End our program.");
}
Copy the code
Create a process engine
private static ProcessEngine getProcessEngine(a) {
ProcessEngineConfiguration cfg = ProcessEngineConfiguration.createStandaloneInMemProcessEngineConfiguration();
ProcessEngine processEngine = cfg.buildProcessEngine(); // Create the process
String name = processEngine.getName();
String version = ProcessEngine.VERSION;
LOGGER.info("Name of process engine [{}], version of process engine [{}]", name, version);
return processEngine;
}
Copy the code
Deploy the process definition file
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("Process definition file [{}], process ID[{}]", processDefinition.getName(), processDefinition.getId());
return processDefinition;
}
Copy the code
Start running flow
private static ProcessInstance getProcessInstance(ProcessEngine processEngine, ProcessDefinition processDefinition) {
RuntimeService runtimeService = processEngine.getRuntimeService();
ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinition.getId());
LOGGER.info("Start process [{}]", processInstance.getProcessDefinitionKey());
return processInstance;
}
Copy the code
Process tasks
private static void processTask(ProcessEngine processEngine, ProcessInstance processInstance) throws ParseException {
Scanner scanner = new Scanner(System.in);
while(processInstance ! =null && !processInstance.isEnded()) {
// Process process tasks
TaskService taskService = processEngine.getTaskService();
List<Task> list = taskService.createTaskQuery().list();
LOGGER.info("Number of pending tasks [{}]", list.size());
for (Task task : list) {
LOGGER.info("Pending tasks [{}]", 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("Please enter [{}]?", formProperty.getName());
line = scanner.nextLine();
variables.put(formProperty.getId(), line);
} else if (DateFormType.class.isInstance(formProperty.getType())) {
LOGGER.info("Please enter [{}]? Format (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("This content is not currently supported [{}]", formProperty.getType());
}
LOGGER.info("The class capacity you entered is [{}]", line);
}
taskService.complete(task.getId(), variables);
processInstance = processEngine.getRuntimeService().createProcessInstanceQuery()
.processInstanceId(processInstance.getId()).singleResult();
}
}
scanner.close();
}
Copy the code