您好!
欢迎来到京东云开发者社区
登录
首页
博文
课程
大赛
工具
用户中心
开源
首页
博文
课程
大赛
工具
开源
更多
用户中心
开发者社区
>
博文
>
流程编排及可视化
分享
打开微信扫码分享
点击前往QQ分享
点击前往微博分享
点击复制链接
流程编排及可视化
马仁喜122815
2023-07-17
IP归属:北京
24280浏览
<span style='color:red;background:#fff;font-size:18px;'>写在前面这里只介绍liteflow的简单基础使用以及作者对liteflow进行可视化扩展的相关阐述</span> ## 一、背景及意义 背景:对于拥有复杂业务逻辑的系统承载着核心业务逻辑,这些核心业务逻辑涉及内部逻辑运算,缓存操作,持久化操作,外部资源调取,内部其他系统RPC调用等等。项目几经易手,维护的成本就会越来越高。各种硬代码判断,分支条件越来越多。代码的抽象,复用率也越来越低,各个模块之间的耦合度很高。一小段逻辑的变动,会影响到其他模块,需要进行完整回归测试来验证。如要灵活改变业务流程的顺序,则要进行代码大改动进行抽象,重新写方法。实时热变更业务流程,几乎很难实现 意义:逻辑解耦、提高扩展性、降低维护成本、能力充分复用、流程灵活编排 ## 二、常用流程编排框架 | | liteflow(开源) | asyncTool(开源) | JDEasyFlow(开源) | disruptor | | ---- | ---------------------------------------- | ---------------------------------------- | ---------------------------------------- | ----------------- | | 介绍 | LiteFlow是一个非常强大的现代化的规则引擎框架,融合了编排特性和规则引擎的所有特性。如果你要对复杂业务逻辑进行新写或者重构,用LiteFlow最合适不过。它是一个编排式的规则引擎框架,组件编排,帮助解耦业务代码,让每一个业务片段都是一个组件。 | 解决任意的多线程并行、串行、阻塞、依赖、回调的并发框架,可以任意组合各线程的执行顺序,带全链路回调和超时控制。 | 通用流程编排技术组件,适用于服务编排、工作流、审批流等场景 | | | 地址 | https://liteflow.yomahub.com/ | https://gitee.com/jd-platform-opensource/asyncTool | https://developer.jdcloud.com/article/2604?mid=30 | | | 优点 | 复杂业务流程编排、社区成熟活跃 | 基于jdk8 CompletableFuture、轻量级 | 简单、灵活、易扩展 | 基于生产-消费模型、无锁设计 | | 缺点 | 开源框架较重,有一定学习成本 | 新框架稳定性待验证 | | 较为底层,针对业务场景需要二次封装 | | 示例 | https://gitee.com/bryan31/liteflow-example | | | | ## 三、liteflow基础使用 ### 1.添加依赖jar包 ```xml <dependency> <groupId>com.yomahub</groupId> <artifactId>liteflow-spring</artifactId> <version>2.10.4</version> </dependency> ``` ### 2.定义组件 定义组件和实现某些组件,注册进上下文 ```java @Component("a") public class ACmp extends NodeComponent { @Override public void process() { //do your business } } ``` ### 3.配置 添加对应的配置类及配置文件 Spring xml中的配置 ```xml <context:component-scan base-package="com.yomahub.flowtest.components" /> <bean id="springAware" class="com.yomahub.liteflow.spi.spring.SpringAware"/> <bean class="com.yomahub.liteflow.spring.ComponentScanner"/> <bean id="liteflowConfig" class="com.yomahub.liteflow.property.LiteflowConfig"> <property name="ruleSource" value="config/flow.el.xml"/> </bean> <bean id="flowExecutor" class="com.yomahub.liteflow.core.FlowExecutor"> <property name="liteflowConfig" ref="liteflowConfig"/> </bean> <!-- 如果上述enableLog为false,下面这段也可以省略 --> <bean class="com.yomahub.liteflow.monitor.MonitorBus"> <property name="liteflowConfig" ref="liteflowConfig"/> </bean> ``` ### 4.规则文件的定义 --流程的定义(第3步中liteflowConfig指定了规则文件为config/flow.xml),所以需要在resources下新建文件夹config,在新建flow.xml文件,配置要定义的流程 ```xml <?xml version="1.0" encoding="UTF-8"?> <flow> <chain name="chain1"> THEN(a, b, c) </chain> </flow> ``` ### 5.执行 编排好的流程,在需要执行的地方注入FlowExecutor,执行execute2Resp ```java @Component public class YourClass{ @Resource private FlowExecutor flowExecutor; @Test public void testConfig(){ LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg"); } } ``` ## 四、liteflow在实际中的应用 这里弱化背后的实际业务只展示作者在实际中的应用案例 ### 1.添加依赖jar包 ```xml <properties> <liteflow-spring.version>2.8.0</liteflow-spring.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>com.yomahub</groupId> <artifactId>liteflow-spring</artifactId> <version>${liteflow-spring.version}</version> </dependency> </dependencies> </dependencyManagement> ``` ### 2.定义组件 定义组件和实现某些组件,注册进上下文 ```java @LiteflowComponent("checkRealNameAuthCmp") @LiteflowCmpDefine public class CheckRealNameAuthCmp { private static final Logger log = LoggerFactory.getLogger(CheckRealNameAuthCmp.class); @LiteflowMethod(LiteFlowMethodEnum.PROCESS) public void process(NodeComponent nodeComponent) throws Exception { // 获取请求参数 GeneratePolicyRightsParam generatePolicyRightsParam = nodeComponent.getSlot().getRequestData(); // 如果pin为空则结束流程 if (generatePolicyRightsParam == null || StringUtil.isEmpty(generatePolicyRightsParam.getUserPin())) { log.info("CheckRealNameAuthCmp -> process end, nodeComponent={},pin is null.", JsonUtil.toJSONString(nodeComponent)); nodeComponent.setIsEnd(true); } //封装设置流程编排上下文信息 GenerateRightsContext generateRightsContext = nodeComponent.getContextBean(GenerateRightsContext.class); generateRightsContext.setGeneratePolicyRightsParam(generatePolicyRightsParam); } } ``` LiteflowComponent:https://liteflow.yomahub.com/pages/v2.8.X/8486fb/ LiteflowCmpDefine:https://liteflow.yomahub.com/pages/v2.8.X/f33919/ ### 3.配置 添加对应的配置类及配置文件 Spring xml中的配置 spring-config.xml ```xml <import resource="classpath*:spring/spring-config-liteflow.xml"/> ``` spring-config-liteflow.xml ```xml <bean id="springAware" class="com.yomahub.liteflow.spi.spring.SpringAware"/> <bean id="springComponentScaner" class="com.yomahub.liteflow.spring.ComponentScanner"/> <!-- 注入liteflow的配置文件 --> <bean id="liteflowConfig" class="com.yomahub.liteflow.property.LiteflowConfig"> <property name="ruleSource" value="liteflow/flow.xml"/> </bean> <!-- 注入liteflow的执行引擎 --> <bean id="flowExecutor" class="com.yomahub.liteflow.core.FlowExecutor"> <property name="liteflowConfig" ref="liteflowConfig"/> </bean> <!-- 注入liteflow的MonitorBus--> <bean class="com.yomahub.liteflow.monitor.MonitorBus"> <constructor-arg ref="liteflowConfig"/> <property name="liteflowConfig" ref="liteflowConfig"/> </bean> ``` ### 4.规则文件的定义 --流程的定义(第3步中liteflowConfig指定了规则文件为liteflow/flow.xml),所以需要在resources下新建文件夹liteflow,在新建flow.xml文件,配置要定义的流程 flow.xml ```xml <?xml version="1.0" encoding="UTF-8"?> <flow> <!-- liteflow流程编排:生成(发放)保单权益--> <chain name="sendPolicyRightsChain"> <when value="checkRealNameAuthCmp"/> <then value="checkNewPolicyRightsCmp"/> <then value="getPolicyInfoCmp"/> <then value="policyMatchServiceRuleCmp"/> <then value="initPolicyRightsDataCmp"/> <then value="creatPlanGantRightsDataCmp"/> <then value="asyncFullFillCmp"/> </chain> </flow> ``` ### 5.执行 执行编排好的流程,在需要执行的地方注入FlowExecutor,执行execute2Resp ```java @Resource private FlowExecutor flowExecutor; public Boolean sendPolicyRights(GeneratePolicyRightsParam generatePolicyRightsParam) { //todo 入参和上下文不能混用,通用信息用map LiteflowResponse response = flowExecutor.execute2Resp("sendPolicyRightsChain", generatePolicyRightsParam, GenerateRightsContext.class,GenerateRightsContext.class); } ``` ## 五、liteflow能力扩展(可视化) liteflowt提供了流程编排的能力,只有研发人员能够了解这内在的流程编排含义,对于其他产品或者业务并不能直观的了解当前的业务流程,可视化并不友好。这时我们如何让当前的流程可视化呢?编写一个页面直接读取配置文件flow.xml进行显示,这是没有意义的。有意义的是我们能够对组件进行可视化、对流程可视化、对流程编排可视化。 ### 1、思想 提供新的jar包,获取到业务系统声名的组件、流程、显示流程和组件、提供编排能力。 <span style='color:red;background:#fff;font-size:18px;'> 说明: 1、小工具jar包为可视化流程编排小工具,主要提供获取业务系统声明的组件、保存的流程、进行流程可视化展示、进行流程编排可视化等,使用liteflow-util标识区别于业务系统。 2、业务系统为组件声明、流程执行、业务逻辑系统,使用liteflow-test标识 </span> ### 2、实现 #### 2.1获取特定的类或方法 如何从liteflow-util中获取liteflow-test中声明的组件 ##### 2.1.1获取上下文环境 ApplicationContextAware 当一个bean的属性初始化后会回调到setApplicationContext,从而设置应用上下文。 ``` public interface ApplicationContextAware extends Aware { /** * Set the ApplicationContext that this object runs in. * Normally this call will be used to initialize the object. * <p>Invoked after population of normal bean properties but before an init callback such * as {@link org.springframework.beans.factory.InitializingBean#afterPropertiesSet()} * or a custom init-method. Invoked after {@link ResourceLoaderAware#setResourceLoader}, * {@link ApplicationEventPublisherAware#setApplicationEventPublisher} and * {@link MessageSourceAware}, if applicable. * @param applicationContext the ApplicationContext object to be used by this object * @throws ApplicationContextException in case of context initialization errors * @throws BeansException if thrown by application context methods * @see org.springframework.beans.factory.BeanInitializationException */ void setApplicationContext(ApplicationContext applicationContext) throws BeansException; } ``` 在liteflow-util中使用一个类来实现ApplicationContextAware,从而获取到liteflow-test(依赖当前jar包的应用)的上下文环境 ```java @Configuration public class LiteFlowApplicationContext implements ApplicationContextAware { private static ApplicationContext controllerApplicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { System.out.println("applicationContext = " + applicationContext); LiteFlowApplicationContext.controllerApplicationContext=applicationContext; } public static ApplicationContext getControllerApplicationContext() { return controllerApplicationContext; } } ``` ##### 2.1.2从上下文获取类 在liteflow-util中根据上下文环境获取组件类<span style='color:red;background:#fff;font-size:18px;'>这里的重点是Map<String, Object> mvcObjects = context.getBeansWithAnnotation(Service.class);</span> ```java @Slf4j public class ReferenceManager { private static Map<Class<?>, Object> interfaceMapRef = new ConcurrentHashMap<Class<?>, Object>(); private static ReferenceManager instance; private ReferenceManager() { } public synchronized static ReferenceManager getInstance() { if (null != instance) { return instance; } instance = new ReferenceManager(); ApplicationContext controllerContext = LiteFlowApplicationContext.getControllerApplicationContext(); interfaceMapInit(controllerContext); return instance; } private static void interfaceMapInit(ApplicationContext context) { try { Map<String, Object> objects = Maps.newHashMapWithExpectedSize(64); //优化 允许 ServiceBean 被MVC容器扫描 Map<String, Object> mvcObjects = context.getBeansWithAnnotation(Service.class); objects.putAll(mvcObjects); if (objects == null || objects.size() == 0) { return; } for (Entry<String, Object> entry : objects.entrySet()) { /** * 获取代理对象的原对象 * 因为 jdk 动态代理通过接口 */ Object objectImplProxy = entry.getValue(); Object objectImpl = AopTargetUtils.getTarget(objectImplProxy); Class objectImplClass = objectImpl.getClass(); if (objectImplClass.getInterfaces().length > 0) { /** * 规定 每个interface 只对应 一个实现类 * 如果 多个类实现了该接口 接口列表中只 显示第一个实现类 */ Class interfaceClass = objectImplClass.getInterfaces()[0]; Object object = interfaceMapRef.get(interfaceClass); if (object == null) { interfaceMapRef.put(interfaceClass, objectImpl); } else { } } else { } } } catch (Exception e) { } } public Map<Class<?>, Object> getInterfaceMapRef() { return interfaceMapRef; } } ``` ```java @Component public class ServiceScanner { public Set<Class<?>> classes() { return interfaceMapRef().keySet(); } public Map<Class<?>, Object> interfaceMapRef() { return ReferenceManager.getInstance().getInterfaceMapRef(); } } ``` ```java public class AopTargetUtils { /** * 获取 目标对象 * @param proxy 代理对象 * @return * @throws Exception */ public static Object getTarget(Object proxy) throws Exception { if(!AopUtils.isAopProxy(proxy)) { return proxy;//不是代理对象 } if(AopUtils.isJdkDynamicProxy(proxy)) { return getJdkDynamicProxyTargetObject(proxy); } else { //cglib return getCglibProxyTargetObject(proxy); } } private static Object getCglibProxyTargetObject(Object proxy) { try{ Field h = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0"); h.setAccessible(true); Object dynamicAdvisedInterceptor = h.get(proxy); Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised"); advised.setAccessible(true); Object target = ((AdvisedSupport) advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget(); return target; } catch(Exception e){ e.printStackTrace(); return null; } } private static Object getJdkDynamicProxyTargetObject(Object proxy) { try{ Field h = proxy.getClass().getSuperclass().getDeclaredField("h"); h.setAccessible(true); AopProxy aopProxy = (AopProxy) h.get(proxy); Field advised = aopProxy.getClass().getDeclaredField("advised"); advised.setAccessible(true); Object target = ((AdvisedSupport) advised.get(aopProxy)).getTargetSource().getTarget(); return target; } catch(Exception e){ e.printStackTrace(); return null; } } } ``` #### 2.2访问liteflow-util页面 如何在liteflow-test里访问到liteflow-util包里的页面并展示 (1)在liteflow-util内编写一个Servlet类,直接继承HttpServlet ,重写doGet或者doPost方法 (2)在liteflow-util内将Servlet类配置到web.xml中 (3)在liteflow-util内准备前端的页面(form表单、按钮交互) (4)在liteflow-test内引入依赖并启动liteflow-test ##### 2.2.1HttpServlet ```java public class HandServlet extends HttpServlet { private static final Logger log = LoggerFactory.getLogger(HandServlet.class); private String username = null; private String password = null; private ServletContext servletContext; public HandServlet() { } @Override public void init(ServletConfig config) { log.info("HandServlet->init,start"); this.username = config.getInitParameter("loginUsername"); this.password = config.getInitParameter("loginPassword"); this.servletContext = config.getServletContext(); log.info("HandServlet->init finish"); } @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException { String contextPath = request.getContextPath(); String servletPath = request.getServletPath(); String requestURI = request.getRequestURI(); response.setCharacterEncoding("utf-8"); if (contextPath == null) { contextPath = ""; } String uri = contextPath + servletPath; String path = requestURI.substring(contextPath.length() + servletPath.length()); String usernameParam; if (!Objects.equals("/submitLogin", path)) { if (this.needLogin(request, path)) { this.redirect(request, response); } else { Result result; try { result = this.requestHandler(path, request); } catch (Throwable var11) { log.error("HandServlet->service,requestHandler error", var11); result = Result.buildFail(var11.getMessage()); } if (null != result) { response.getWriter().print(JSON.toJSONString(result)); } else { this.returnResourceFile(path, uri, response); } } } else { usernameParam = request.getParameter("loginUsername"); String passwordParam = request.getParameter("loginPassword"); System.out.println("usernameParam = " + usernameParam); System.out.println("passwordParam = " + passwordParam); // if (this.username.equals(usernameParam) && this.password.equals(passwordParam)) { HttpSession session = request.getSession(); session.setAttribute("lite-flow", this.username); session.setMaxInactiveInterval(300); response.getWriter().print(JSON.toJSONString(Result.buildSuccess("success"))); // } else { // response.getWriter().print(JSON.toJSONString(Result.buildFail("用户名或密码错误"))); // } } } private void redirect(HttpServletRequest request, HttpServletResponse response) throws IOException { if (request.getHeader("X-Requested-With") != null && "XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) { response.getWriter().print(JSON.toJSONString(Result.buildReLogin())); } else if (request.getHeader("Accept") != null && request.getHeader("Accept").contains("application/json")) { response.getWriter().print(JSON.toJSONString(Result.buildReLogin())); } else { response.sendRedirect("/lite-flow/login.html"); } } private Result requestHandler(String path, HttpServletRequest request) { System.out.println("path = " + path); System.out.println("request = " + request); String initMenu = "/initMenu"; String liteflow = "/liteflow"; if (initMenu.equals(path)) { Map<String, Object> map = new HashMap(2); List<String> classObjectMap = getClassObjectMap(); classObjectMap.forEach(item -> { int i = item.lastIndexOf("."); String substring = item.substring(i+1); System.out.println("substring = " + substring); LiteFlowNodeBuilder.createCommonNode().setId(substring).setName(substring).setClazz(item).build(); }); map.put("interfaceMapRef", classObjectMap); return Result.buildSuccess(map); } else if (liteflow.equals(path)) { try { try { String postData = this.getPostData(request); log.info("HandServlet -> requestHandler start, postData={}", postData); JSONObject jsonObject = JSONObject.parseObject(postData); JSONArray checkList = jsonObject.getJSONArray("checkList"); String chainId = (String) jsonObject.get("chainId"); log.info("HandServlet -> requestHandler start, path={},checkList={}", path, checkList); ArrayList arrayList = new ArrayList(); checkList.forEach(item -> { String itemStr = (String) item; int i = itemStr.lastIndexOf("."); String substring = itemStr.substring(i+1); arrayList.add(substring); }); String str = StringUtils.join(arrayList, ","); log.info("HandServlet -> requestHandler start, str={}", str); // String elss = "THEN(" + str + ")"; // log.info("HandServlet -> requestHandler start, elss={}", elss); Condition condition = LiteFlowConditionBuilder.createCondition(ConditionTypeEnum.TYPE_THEN).setValue(str).build(); log.info("HandServlet -> requestHandler start, condition={}", condition); LiteFlowChainBuilder.createChain().setChainName(chainId).setCondition(condition).build(); } catch (Throwable var3) { log.error("HandServlet -> requestHandler exception 未知异常, var3={}", var3); } } catch (Throwable var3) { log.info("MqUtil->haveProducer,error", var3); } return Result.buildSuccess(false); } else { return null; } } public String getPostData(HttpServletRequest request) { StringBuilder data = new StringBuilder(); String line; BufferedReader reader; try { reader = request.getReader(); while (null != (line = reader.readLine())) { data.append(line); } } catch (IOException e) { return null; } return data.toString(); } private List<String> getClassObjectMap() { List<String> result = new ArrayList<>(); WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext); Map<String, ServiceScanner> serviceScannerMap = webApplicationContext.getBeansOfType(ServiceScanner.class); ServiceScanner serviceScanner = serviceScannerMap.get("serviceScanner"); Map<Class<?>, Object> interfaceMapRef = serviceScanner.interfaceMapRef(); if (null != interfaceMapRef) { //排序 所有接口 List<Map.Entry<Class<?>, Object>> arrayList = new ArrayList<Map.Entry<Class<?>, Object>>(interfaceMapRef.entrySet()); Collections.sort(arrayList, new Comparator<Map.Entry<Class<?>, Object>>() { @Override public int compare(Map.Entry<Class<?>, Object> o1, Map.Entry<Class<?>, Object> o2) { return o1.getKey().getSimpleName().compareTo(o2.getKey().getSimpleName()); } }); //遍历 所有接口 for (Map.Entry<Class<?>, Object> entry : arrayList) { String className = entry.getValue().getClass().getName(); System.out.println("class = " + className); result.add(className); // List<Method> interfaceMethodList = Arrays.asList(entry.getKey().getDeclaredMethods()); // //方法列表排序 // Collections.sort(interfaceMethodList, new Comparator<Method>() { // @Override // public int compare(Method o1, Method o2) { // return o1.getName().compareTo(o2.getName()); // } // }); // for (Method method : interfaceMethodList) { // System.out.println("method = " + method); // System.out.println("methodName = " + method.getName()); // System.out.println("methodParameterTypes = " + method.getParameterTypes()); // System.out.println("methodReturn = " + method.getReturnType()); // } } } System.out.println("result = " + result); return result; } private boolean needLogin(HttpServletRequest request, String path) { return this.isRequireAuth() && !this.alreadyLogin(request) && !this.checkLoginParam(request) && !"/login.html".equals(path) && !path.startsWith("/css") && !path.startsWith("/js") && !path.startsWith("/img"); } private boolean checkLoginParam(HttpServletRequest request) { String usernameParam = request.getParameter("loginUsername"); String passwordParam = request.getParameter("loginPassword"); if (null != this.username && null != this.password) { return this.username.equals(usernameParam) && this.password.equals(passwordParam); } else { return false; } } private boolean isRequireAuth() { return this.username != null; } private boolean alreadyLogin(HttpServletRequest request) { HttpSession session = request.getSession(false); return session != null && session.getAttribute("lite-flow") != null; } private void returnResourceFile(String fileName, String uri, HttpServletResponse response) throws IOException { String filePath = this.getFilePath(fileName); if (filePath.endsWith(".html")) { response.setContentType("text/html; charset=utf-8"); } if (fileName.endsWith(".jpg")) { byte[] bytes = Utils.readByteArrayFromResource(filePath); if (bytes != null) { response.getOutputStream().write(bytes); } } else { String text = Utils.readFromResource(filePath); if (text == null) { response.sendRedirect(uri + "/login.html"); } else { if (fileName.endsWith(".css")) { response.setContentType("text/css;charset=utf-8"); } else if (fileName.endsWith(".js")) { response.setContentType("text/javascript;charset=utf-8"); } response.getWriter().write(text); } } } private String getFilePath(String fileName) { return "view" + fileName; } ``` ##### 2.2.2配置web.xml 在liteflow-util内web.xml配置自定义的servlet ```xml <servlet> <servlet-name>handOfLite</servlet-name> <servlet-class>com.xx.utils.liteflow.handler.HandServlet</servlet-class> <init-param> <param-name>loginUsername</param-name> <param-value>Username</param-value> </init-param> <init-param> <param-name>loginPassword</param-name> <param-value>Password</param-value> </init-param> <load-on-startup>5</load-on-startup> </servlet> <servlet-mapping> <servlet-name>handOfLite</servlet-name> <url-pattern>/hand-of-lite/*</url-pattern> </servlet-mapping> ``` ##### 2.2.3页面准备 在liteflow-util内准备显示组件的页面 ![image.png](https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2023-07-01-09-11pYyumEgPKfYAyjl.png) ##### 2.2.4访问页面 在liteflow-test内添加liteflow-util的依赖 ```xml <dependency> <groupId>com.xx.utils</groupId> <artifactId>liteflow</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> ``` 启动liteflow-test工程并访问对应的路径,看到2.2.3准备的页面 ![image.png](https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2023-06-26-16-32RsZU32S6Xn90QvKk.png) 2.3获取组件并回显 2.3.1自定义注解 在liteflow-util内自定义注解,作用装配导入需要的资源类和配置 ```java @Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME) @Target(value = { java.lang.annotation.ElementType.TYPE }) @Documented @Import({LiteFlowApplicationContext.class, FlowExecutor.class, LiteflowConfig.class, IdGeneratorHolder.class}) @ComponentScan( basePackages = {"com.xx.utils", "com.xx.utils.liteflow"} ) public @interface EnableLiteFlow { } ``` 2.3.2引入jar包依赖 在liteflow-util内引入lite-flow核心依赖 ```xml <dependency> <groupId>com.yomahub</groupId> <artifactId>liteflow-spring</artifactId> <version>2.8.3</version> </dependency> ``` 在liteflow-test内引入liteflow-util依赖 ```xml <dependency> <groupId>com.xx.utils</groupId> <artifactId>liteflow</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> ``` 2.3.3配置liteflow-util 在liteflow-test中使用自定义注解导入需要的配置 ```java @Configuration @EnableLiteFlow public class LiteFlowConfig { } ``` 2.3.4显示组件类 重启liteflow-test,访问页面显示已获取到的组件集合 ![image.png](https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2023-06-26-16-3488vmpQAXQU0OVlW.png) 2.4创建新的组件 liteflow-util提供对于的RequestMapping创建和保存node ```java @RestController @RequestMapping("/node") public class NodeController { private static final Logger log = LoggerFactory.getLogger(NodeController.class); /** * 构建一个普通组件 */ @RequestMapping("/createCommonNode") @ResponseBody public Boolean createCommonNode(@RequestBody NodeParam nodeParam) { log.info("NodeController -> createCommonNode start, nodeParam={}", nodeParam.toString()); try { //构建一个普通组件 LiteFlowNodeBuilder.createCommonNode().setId(nodeParam.getId()).setName(nodeParam.getName()).setClazz(nodeParam.getClazz()).build(); return Boolean.TRUE; } catch (Exception e) { return Boolean.FALSE; } } /** * 构建一个普通条件组件 */ @RequestMapping("/createSwitchNode") @ResponseBody public Boolean createSwitchNode(@RequestBody NodeParam nodeParam) { try { LiteFlowNodeBuilder.createSwitchNode().setId(nodeParam.getId()).setName(nodeParam.getName()).setClazz(nodeParam.getClazz()).build(); return Boolean.TRUE; } catch (Exception e) { return Boolean.FALSE; } } } ``` 2.5创建新的流程 https://liteflow.yomahub.com/pages/v2.8.X/9aa85a/ ```java LiteFlowChainELBuilder.createChain().setChainName("chain2").setEL( "THEN(a, b, WHEN(c, d))" ).build(); ``` 3、整体的总结 其实整体的思想就是提供一个jar包,从这个jar包里可以获取到被依赖工程里的类创建对应的组件、创建流程、保存流程、回显流程、执行流程等,这里涉及springbean的生命周期、上下文环境、httpservlet、自定义注解、反射、前端页面等相关知识的融合应用。 ## 六、流程编排相关文章传送门: [流程编排&异步并发调度探索分享](http://xingyun.jd.com/shendeng/article/detail/5275) [Matrix流程编排使用指南](http://xingyun.jd.com/shendeng/article/detail/4287) [CompletableFuture与流程编排](http://xingyun.jd.com/shendeng/article/detail/13294) [【实践篇】流程编排助力BPass](http://xingyun.jd.com/shendeng/article/detail/14478) [流程编排、如此简单-通用流程编排组件JDEasyFlow介绍](http://xingyun.jd.com/shendeng/article/detail/2968)[【B-PaaS 2.0 指导手册】_第32章 Fiber框架-流程模块(Flow)](http://xingyun.jd.com/shendeng/article/detail/14549) ## 七、技术沉淀相关文章 [Intellij IDEA 插件开发](http://xingyun.jd.com/shendeng/article/detail/14773) [从0到1搭建自己的脚手架(java后端)](http://xingyun.jd.com/shendeng/article/detail/14752) [流程编排及可视化](http://xingyun.jd.com/shendeng/article/detail/16103)
上一篇:聊聊Spring注解@Transactional失效的那些事
下一篇:Ui2Code+ChatGPT助力低代码搭建
马仁喜122815
文章数
7
阅读量
2630
作者其他文章
01
Intellij IDEA 插件开发
如有疏漏欢迎指正,如想深入了解欢迎联系探讨写在前面:很多idea插件文档更多的是介绍如何创建一个简单的idea插件文章的意义:开发环境、demo、生态组件、添加依赖包、源码解读、网络请求、渲染数据、页面交互等介绍,是一篇能够满足基本的插件开发工程要求的文章。一、简介IntelliJ IDEA 与 IntelliJ PlatformIntelliJ IDEA 简称 IDEA,是 Jetbrains
01
流程编排及可视化
写在前面这里只介绍liteflow的简单基础使用以及作者对liteflow进行可视化扩展的相关阐述一、背景及意义背景:对于拥有复杂业务逻辑的系统承载着核心业务逻辑,这些核心业务逻辑涉及内部逻辑运算,缓存操作,持久化操作,外部资源调取,内部其他系统RPC调用等等。项目几经易手,维护的成本就会越来越高。各种硬代码判断,分支条件越来越多。代码的抽象,复用率也越来越低,各个模块之间的耦合度很高。一小段逻辑
01
从0到1搭建自己的脚手架(java后端)
一、脚手架是什么脚手架:快速创建一个基础项目的模版二、脚手架的意义主流的微服务架构体系下很多公司会将原有的单体架构或者繁重的微服务进行拆分。这个时候按照模块(商品、订单、用户、库存、优惠、搜索、管理后台..)、按照领域驱动设计(投保、理赔、支付、保单管理)等进行拆分就需要很多的微服务项目,如果一个一个的去创建微服务其实是在做重复的工作容易出错而且没有意义,浪费人力物力。这个时候基础性的持久层、日志
01
ASM字节码操作类库(打开java语言世界通往字节码世界的大门)
前言:授人以鱼不如授人以渔,应用asm的文章有很多,简单demo的也很多,那么ASM都具备哪些能力呢?如何去学习编写ASM代码呢?什么样的情景需要用到ASM呢?让我们带着这些问题阅读这篇文章吧。这里由于篇幅限制做了删减(第六部分TreeApi和CoreApi的比较、核心API类的介绍等),如果有兴趣可以联系作者进行交流,个人认为核心在于第五部分如何查看一个想写的类的ASM代码如何写,以及全面了解A
马仁喜122815
文章数
7
阅读量
2630
作者其他文章
01
Intellij IDEA 插件开发
01
从0到1搭建自己的脚手架(java后端)
01
ASM字节码操作类库(打开java语言世界通往字节码世界的大门)
添加企业微信
获取1V1专业服务
扫码关注
京东云开发者公众号