mirror of
https://gitee.com/dromara/liteFlow.git
synced 2026-05-14 04:02:09 +08:00
#feature #I581A1 @LiteflowMethod注解支持将方法声明成组件
This commit is contained in:
@@ -11,4 +11,8 @@ import java.lang.annotation.*;
|
||||
public @interface LiteflowMethod {
|
||||
|
||||
LiteFlowMethodEnum value();
|
||||
|
||||
// 节点ID,用于区分节点
|
||||
// 默认为空 则按照Spring模式下BeanName为准。
|
||||
String nodeId() default "";
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import cn.hutool.core.util.*;
|
||||
import com.yomahub.liteflow.annotation.LiteflowMethod;
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import com.yomahub.liteflow.exception.ComponentMethodDefineErrorException;
|
||||
import com.yomahub.liteflow.exception.LiteFlowException;
|
||||
import com.yomahub.liteflow.util.LiteFlowProxyUtil;
|
||||
import com.yomahub.liteflow.util.SerialsUtil;
|
||||
import net.bytebuddy.ByteBuddy;
|
||||
@@ -15,10 +16,7 @@ import org.slf4j.LoggerFactory;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@@ -42,7 +40,7 @@ public class ComponentProxy {
|
||||
this.clazz = clazz;
|
||||
}
|
||||
|
||||
public Object getProxy() throws Exception{
|
||||
public List<NodeComponent> getProxyList() throws Exception{
|
||||
//这里要判断bean是否是spring代理过的bean,如果是代理过的bean需要取到原class对象
|
||||
Class<?> beanClazz;
|
||||
if (LiteFlowProxyUtil.isCglibProxyClass(bean.getClass())){
|
||||
@@ -51,30 +49,37 @@ public class ComponentProxy {
|
||||
beanClazz = bean.getClass();
|
||||
}
|
||||
|
||||
//得到当前bean里所覆盖的组件方法(一定是被@LiteFlowMethod修饰的),自己定义的不算
|
||||
List<String> methodStrList = Arrays.stream(beanClazz.getDeclaredMethods()).filter(
|
||||
//得到当前bean里所覆盖的LiteflowMethod(一定是被@LiteFlowMethod修饰的),自己定义的不算
|
||||
Map<String, List<LiteflowMethod>> methodListMap = Arrays.stream(beanClazz.getDeclaredMethods()).filter(
|
||||
m -> m.getAnnotation(LiteflowMethod.class) != null
|
||||
).map(m -> {
|
||||
LiteflowMethod liteflowMethod = m.getAnnotation(LiteflowMethod.class);
|
||||
return liteflowMethod.value().getMethodName();
|
||||
).map(m -> m.getAnnotation(LiteflowMethod.class)).collect(Collectors.groupingBy(LiteflowMethod::nodeId));
|
||||
return methodListMap.entrySet().stream().map(entry -> {
|
||||
String activeNodeId = StrUtil.isEmpty(entry.getKey()) ? nodeId : entry.getKey();
|
||||
List<LiteflowMethod> methodList = entry.getValue();
|
||||
try {
|
||||
//创建对象
|
||||
//这里package进行了重设,放到了被代理对象的所在目录
|
||||
//生成的对象也加了上被代理对象拥有的注解
|
||||
//被拦截的对象也根据被代理对象根据@LiteFlowMethod所标注的进行了动态判断
|
||||
Object instance = new ByteBuddy().subclass(clazz)
|
||||
.name(StrUtil.format("{}.ByteBuddy${}${}",
|
||||
ClassUtil.getPackage(bean.getClass()),
|
||||
activeNodeId,
|
||||
SerialsUtil.generateShortUUID()))
|
||||
.method(ElementMatchers.namedOneOf(methodList.stream().map(m -> m.value().getMethodName()).toArray(String[]::new)))
|
||||
.intercept(InvocationHandlerAdapter.of(new AopInvocationHandler(bean)))
|
||||
.annotateType(bean.getClass().getAnnotations())
|
||||
.make()
|
||||
.load(ComponentProxy.class.getClassLoader())
|
||||
.getLoaded()
|
||||
.newInstance();
|
||||
NodeComponent nodeComponent = (NodeComponent) instance;
|
||||
nodeComponent.setNodeId(activeNodeId);
|
||||
return nodeComponent;
|
||||
} catch (Exception e) {
|
||||
throw new LiteFlowException(e);
|
||||
}
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
//创建对象
|
||||
//这里package进行了重设,放到了被代理对象的所在目录
|
||||
//生成的对象也加了上被代理对象拥有的注解
|
||||
//被拦截的对象也根据被代理对象根据@LiteFlowMethod所标注的进行了动态判断
|
||||
return new ByteBuddy().subclass(clazz)
|
||||
.name(StrUtil.format("{}.ByteBuddy${}${}",
|
||||
ClassUtil.getPackage(bean.getClass()),
|
||||
nodeId,
|
||||
SerialsUtil.generateShortUUID()))
|
||||
.method(ElementMatchers.namedOneOf(methodStrList.toArray(new String[]{})))
|
||||
.intercept(InvocationHandlerAdapter.of(new AopInvocationHandler(bean)))
|
||||
.annotateType(bean.getClass().getAnnotations())
|
||||
.make()
|
||||
.load(ComponentProxy.class.getClassLoader())
|
||||
.getLoaded()
|
||||
.newInstance();
|
||||
}
|
||||
|
||||
public class AopInvocationHandler implements InvocationHandler {
|
||||
@@ -93,6 +98,10 @@ public class ComponentProxy {
|
||||
List<LiteFlowMethodBean> liteFlowMethodBeanList = Arrays.stream(ReflectUtil.getMethods(bean.getClass())).filter(m -> {
|
||||
LiteflowMethod liteFlowMethod = m.getAnnotation(LiteflowMethod.class);
|
||||
return ObjectUtil.isNotNull(liteFlowMethod);
|
||||
}).filter(m -> {
|
||||
// 过滤不属于当前NodeComponent的方法
|
||||
LiteflowMethod liteFlowMethod = m.getAnnotation(LiteflowMethod.class);
|
||||
return StrUtil.isEmpty(liteFlowMethod.nodeId())|| Objects.equals(liteFlowMethod.nodeId(),((NodeComponent) proxy).getNodeId());
|
||||
}).map(m -> {
|
||||
LiteflowMethod liteFlowMethod = m.getAnnotation(LiteflowMethod.class);
|
||||
return new LiteFlowMethodBean(liteFlowMethod.value().getMethodName(), m);
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
package com.yomahub.liteflow.flow;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
@@ -36,7 +37,10 @@ import com.yomahub.liteflow.util.LiteFlowProxyUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 流程元数据类
|
||||
@@ -156,7 +160,7 @@ public class FlowBus {
|
||||
try {
|
||||
//判断此类是否是声明式的组件,如果是声明式的组件,就用动态代理生成实例
|
||||
//如果不是声明式的,就用传统的方式进行判断
|
||||
NodeComponent cmpInstance = null;
|
||||
List<NodeComponent> cmpInstances = new ArrayList<>();
|
||||
if (LiteFlowProxyUtil.isDeclareCmp(cmpClazz)){
|
||||
//这里的逻辑要仔细看下
|
||||
//如果是spring体系,把原始的类往spring上下文中进行注册,那么会走到ComponentScanner中
|
||||
@@ -167,43 +171,50 @@ public class FlowBus {
|
||||
ContextAware contextAware = ContextAwareHolder.loadContextAware();
|
||||
Object bean = ContextAwareHolder.loadContextAware().registerBean(nodeId, cmpClazz);
|
||||
if (LocalContextAware.class.isAssignableFrom(contextAware.getClass())){
|
||||
cmpInstance = LiteFlowProxyUtil.proxy2NodeComponent(bean, nodeId);
|
||||
cmpInstances = LiteFlowProxyUtil.proxy2NodeComponent(bean, nodeId);
|
||||
}else {
|
||||
cmpInstance = (NodeComponent) bean;
|
||||
cmpInstances = ListUtil.toList((NodeComponent) bean);
|
||||
}
|
||||
}else{
|
||||
//以node方式配置,本质上是为了适配无spring的环境,如果有spring环境,其实不用这么配置
|
||||
//这里的逻辑是判断是否能从spring上下文中取到,如果没有spring,则就是new instance了
|
||||
//如果是script类型的节点,因为class只有一个,所以也不能注册进spring上下文,注册的时候需要new Instance
|
||||
if (!CollectionUtil.newArrayList(NodeTypeEnum.SCRIPT, NodeTypeEnum.SWITCH_SCRIPT, NodeTypeEnum.IF_SCRIPT).contains(type)){
|
||||
cmpInstance = (NodeComponent) ContextAwareHolder.loadContextAware().registerOrGet(nodeId, cmpClazz);
|
||||
cmpInstances = ListUtil.toList((NodeComponent) ContextAwareHolder.loadContextAware().registerOrGet(nodeId, cmpClazz));
|
||||
}
|
||||
|
||||
if (ObjectUtil.isNull(cmpInstance)) {
|
||||
cmpInstance = (NodeComponent) cmpClazz.newInstance();
|
||||
// 去除null元素
|
||||
cmpInstances.remove(null);
|
||||
// 如果为空
|
||||
if (cmpInstances.isEmpty()) {
|
||||
NodeComponent cmpInstance = (NodeComponent) cmpClazz.newInstance();
|
||||
cmpInstances.add(cmpInstance);
|
||||
}
|
||||
}
|
||||
|
||||
//进行初始化
|
||||
cmpInstance = ComponentInitializer.loadInstance().initComponent(cmpInstance, type, name, nodeId);
|
||||
cmpInstances = cmpInstances.stream().map(cmpInstance -> ComponentInitializer.loadInstance().initComponent(cmpInstance, type, name, nodeId)).collect(Collectors.toList());
|
||||
|
||||
//初始化Node
|
||||
Node node = new Node(cmpInstance);
|
||||
List<Node> nodes = cmpInstances.stream().map(Node::new).collect(Collectors.toList());
|
||||
|
||||
//如果是脚本节点(普通脚本节点/条件脚本节点),则还要加载script脚本
|
||||
if (StrUtil.isNotBlank(script)){
|
||||
node.setScript(script);
|
||||
if (type.equals(NodeTypeEnum.SCRIPT)){
|
||||
((ScriptComponent)cmpInstance).loadScript(script);
|
||||
}else if(type.equals(NodeTypeEnum.SWITCH_SCRIPT)){
|
||||
((ScriptSwitchComponent)cmpInstance).loadScript(script);
|
||||
}else if(type.equals(NodeTypeEnum.IF_SCRIPT)){
|
||||
((ScriptIfComponent)cmpInstance).loadScript(script);
|
||||
for (int i = 0; i < nodes.size(); i++) {
|
||||
Node node = nodes.get(i);
|
||||
NodeComponent cmpInstance = cmpInstances.get(i);
|
||||
if (StrUtil.isNotBlank(script)){
|
||||
node.setScript(script);
|
||||
if (type.equals(NodeTypeEnum.SCRIPT)){
|
||||
((ScriptComponent)cmpInstance).loadScript(script);
|
||||
}else if(type.equals(NodeTypeEnum.SWITCH_SCRIPT)){
|
||||
((ScriptSwitchComponent)cmpInstance).loadScript(script);
|
||||
}else if(type.equals(NodeTypeEnum.IF_SCRIPT)){
|
||||
((ScriptIfComponent)cmpInstance).loadScript(script);
|
||||
}
|
||||
}
|
||||
String activeNodeId = StrUtil.isEmpty(cmpInstance.getNodeId()) ? nodeId : cmpInstance.getNodeId();
|
||||
//如果是脚本节点(普通脚本节点/条件脚本节点),则还要加载script脚本
|
||||
nodeMap.put(activeNodeId, node);
|
||||
}
|
||||
|
||||
nodeMap.put(nodeId, node);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
String error = StrUtil.format("component[{}] register error", StrUtil.isEmpty(name)?nodeId:StrUtil.format("{}({})",nodeId,name));
|
||||
LOG.error(error);
|
||||
throw new ComponentCannotRegisterException(error);
|
||||
|
||||
@@ -16,6 +16,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 组件代理类通用方法
|
||||
@@ -61,8 +62,8 @@ public class LiteFlowProxyUtil {
|
||||
return flag3;
|
||||
}
|
||||
|
||||
//对一个满足声明式的bean进行代理
|
||||
public static NodeComponent proxy2NodeComponent(Object bean, String nodeId){
|
||||
//对一个满足声明式的bean进行代理,生成代理类数组
|
||||
public static List<NodeComponent> proxy2NodeComponent(Object bean, String nodeId){
|
||||
try{
|
||||
LiteflowCmpDefine liteflowCmpDefine = bean.getClass().getAnnotation(LiteflowCmpDefine.class);
|
||||
LiteflowSwitchCmpDefine liteflowSwitchCmpDefine = bean.getClass().getAnnotation(LiteflowSwitchCmpDefine.class);
|
||||
@@ -71,17 +72,17 @@ public class LiteFlowProxyUtil {
|
||||
ComponentProxy proxy;
|
||||
if (ObjectUtil.isNotNull(liteflowCmpDefine)){
|
||||
proxy = new ComponentProxy(nodeId, bean, NodeComponent.class);
|
||||
return (NodeComponent) proxy.getProxy();
|
||||
return proxy.getProxyList();
|
||||
}
|
||||
|
||||
if (ObjectUtil.isNotNull(liteflowSwitchCmpDefine)){
|
||||
proxy = new ComponentProxy(nodeId, bean, NodeSwitchComponent.class);
|
||||
return (NodeSwitchComponent) proxy.getProxy();
|
||||
return proxy.getProxyList();
|
||||
}
|
||||
|
||||
if (ObjectUtil.isNotNull(liteflowIfCmpDefine)){
|
||||
proxy = new ComponentProxy(nodeId, bean, NodeIfComponent.class);
|
||||
return (NodeIfComponent) proxy.getProxy();
|
||||
return proxy.getProxyList();
|
||||
}
|
||||
|
||||
throw new RuntimeException();
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
*/
|
||||
package com.yomahub.liteflow.spring;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.yomahub.liteflow.aop.ICmpAroundAspect;
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import com.yomahub.liteflow.flow.FlowBus;
|
||||
import com.yomahub.liteflow.property.LiteflowConfig;
|
||||
import com.yomahub.liteflow.util.LOGOPrinter;
|
||||
import com.yomahub.liteflow.util.LiteFlowProxyUtil;
|
||||
@@ -20,6 +20,7 @@ import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -62,9 +63,19 @@ public class ComponentScanner implements BeanPostProcessor {
|
||||
//如果是,就缓存到类属性的map中
|
||||
if (LiteFlowProxyUtil.isDeclareCmp(bean.getClass())){
|
||||
LOG.info("proxy component[{}] has been found", beanName);
|
||||
NodeComponent nodeComponent = LiteFlowProxyUtil.proxy2NodeComponent(bean, beanName);
|
||||
nodeComponentMap.put(beanName, nodeComponent);
|
||||
return nodeComponent;
|
||||
List<NodeComponent> nodeComponents = LiteFlowProxyUtil.proxy2NodeComponent(bean, beanName);
|
||||
nodeComponents.forEach(
|
||||
nodeComponent -> {
|
||||
String nodeId = nodeComponent.getNodeId();
|
||||
nodeId = StrUtil.isEmpty(nodeId) ? beanName : nodeId;
|
||||
nodeComponentMap.put(nodeId, nodeComponent);
|
||||
}
|
||||
);
|
||||
// 只有注解支持单bean多Node,所以一个直接返回
|
||||
if (nodeComponents.size() == 1){
|
||||
return nodeComponents.get(0);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
// 组件的扫描发现,扫到之后缓存到类属性map中
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.yomahub.liteflow.test.cmpMultiNode;
|
||||
|
||||
import com.yomahub.liteflow.core.FlowExecutor;
|
||||
import com.yomahub.liteflow.flow.LiteflowResponse;
|
||||
import com.yomahub.liteflow.test.BaseTest;
|
||||
import com.yomahub.liteflow.test.cmpRetry.LiteflowRetryELDeclSpringbootTest;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
|
||||
/**
|
||||
* 测试springboot下的节点执行器
|
||||
* @author sorghum
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@TestPropertySource(value = "classpath:/cmpMulti/application.properties")
|
||||
@SpringBootTest(classes = LiteflowMultiELDeclSpringbootTest.class)
|
||||
@EnableAutoConfiguration
|
||||
@ComponentScan({"com.yomahub.liteflow.test.cmpMultiNode.cmp"})
|
||||
public class LiteflowMultiELDeclSpringbootTest extends BaseTest {
|
||||
|
||||
@Resource
|
||||
private FlowExecutor flowExecutor;
|
||||
|
||||
//全局重试配置测试
|
||||
@Test
|
||||
public void testBase() throws Exception{
|
||||
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
|
||||
Assert.assertTrue(response.isSuccess());
|
||||
String executeStepStr = response.getExecuteStepStr();
|
||||
Assert.assertEquals("a==>b==>c==>d==>e",executeStepStr);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.yomahub.liteflow.test.cmpMultiNode.cmp;
|
||||
|
||||
import com.yomahub.liteflow.annotation.LiteflowCmpDefine;
|
||||
import com.yomahub.liteflow.annotation.LiteflowMethod;
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* 多cmp配置
|
||||
*
|
||||
* @author sorghum
|
||||
*/
|
||||
@Configuration
|
||||
@LiteflowCmpDefine
|
||||
public class MultiCmpConfiguration {
|
||||
|
||||
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "a")
|
||||
public void processA(NodeComponent bindCmp) {
|
||||
System.out.println("ACmp executed!");
|
||||
}
|
||||
|
||||
@LiteflowMethod(value = LiteFlowMethodEnum.IS_ACCESS,nodeId = "a")
|
||||
public boolean isAccessA(NodeComponent bindCmp) {
|
||||
System.out.println("ACmp isAccessA!");
|
||||
return true;
|
||||
}
|
||||
|
||||
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "b")
|
||||
public void processB(NodeComponent bindCmp) {
|
||||
System.out.println("BCmp executed!");
|
||||
}
|
||||
|
||||
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "c")
|
||||
public void processC(NodeComponent bindCmp) {
|
||||
System.out.println("CCmp executed!");
|
||||
}
|
||||
|
||||
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "d")
|
||||
public void processD(NodeComponent bindCmp) {
|
||||
System.out.println("DCmp executed!");
|
||||
}
|
||||
|
||||
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "e")
|
||||
public void processE(NodeComponent bindCmp) {
|
||||
System.out.println("ECmp executed!");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<flow>
|
||||
<chain name="chain1">
|
||||
THEN(a,b,c,d,e);
|
||||
</chain>
|
||||
</flow>
|
||||
Reference in New Issue
Block a user