This commit is contained in:
everywhere.z
2022-09-15 13:03:29 +08:00
254 changed files with 7178 additions and 85 deletions

View File

@@ -1,5 +1,6 @@
package com.yomahub.liteflow.annotation;
import com.yomahub.liteflow.enums.AnnotationNodeTypeEnum;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
import java.lang.annotation.*;
@@ -11,4 +12,14 @@ import java.lang.annotation.*;
public @interface LiteflowMethod {
LiteFlowMethodEnum value();
// 节点ID用于区分节点
// 默认为空 则按照Spring模式下BeanName为准。
String nodeId() default "";
/**
* cmp定义
*
*/
AnnotationNodeTypeEnum nodeType() default AnnotationNodeTypeEnum.COMMON;
}

View File

@@ -8,7 +8,7 @@ import java.lang.annotation.*;
* @author Bryan.Zhang
* @since 2.6.0
*/
@Target({ElementType.TYPE})
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited

View File

@@ -1,10 +1,18 @@
package com.yomahub.liteflow.core.proxy;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.*;
import cn.hutool.core.lang.Tuple;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.annotation.LiteflowRetry;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.AnnotationNodeTypeEnum;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
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;
@@ -12,13 +20,16 @@ import net.bytebuddy.implementation.InvocationHandlerAdapter;
import net.bytebuddy.matcher.ElementMatchers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.annotation.Annotation;
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.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
/**
@@ -42,7 +53,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())){
@@ -50,31 +61,104 @@ public class ComponentProxy {
}else{
beanClazz = bean.getClass();
}
//得到当前bean里所覆盖的组件方法(一定是被@LiteFlowMethod修饰的),自己定义的不算
List<String> methodStrList = Arrays.stream(beanClazz.getDeclaredMethods()).filter(
//得到当前bean里所覆盖的LiteflowMethod(一定是被@LiteFlowMethod修饰的),自己定义的不算
Map<String, List<Method>> methodListMap = Arrays.stream(beanClazz.getDeclaredMethods()).filter(
m -> m.getAnnotation(LiteflowMethod.class) != null
).map(m -> {
LiteflowMethod liteflowMethod = m.getAnnotation(LiteflowMethod.class);
return liteflowMethod.value().getMethodName();
}).collect(Collectors.toList());
).collect(Collectors.groupingBy(
m -> m.getAnnotation(LiteflowMethod.class).nodeId()
));
//创建对象
//这里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();
return methodListMap.entrySet().stream().map(entry -> {
// 获取当前节点的原有注解LiteFlowRetry 之类的规则注解
Annotation[] beanClassAnnotation = bean.getClass().getAnnotations();
// 如果entry的key为空字符串则是为了兼容老版本的写法没有指定nodeId的情况
// 判断是否是方法级创造节点
boolean isMethodCreate = !StrUtil.isEmpty(entry.getKey());
// 获取当前bean 真实的nodeId
String activeNodeId = isMethodCreate ? entry.getKey() : nodeId;
// 获取当前节点所有的@LiteflowRetry @LiteflowMethod注解对
List<Tuple> tupleList = entry.getValue().stream().map(m ->
new Tuple(m.getAnnotation(LiteflowRetry.class), m.getAnnotation(LiteflowMethod.class))
).collect(Collectors.toList());
// 获取当前节点的所有LiteFlowMethod注解
List<LiteflowMethod> methodList = tupleList.stream().map(tuple -> ((LiteflowMethod)tuple.get(1)))
.filter(Objects::nonNull)
.collect(Collectors.toList());
// nodeType去重
List<? extends Class<? extends NodeComponent>> classes = methodList.stream()
.map(LiteflowMethod::nodeType)
.map(AnnotationNodeTypeEnum::getCmpClass)
.distinct()
.collect(Collectors.toList());
// 相同nodeId里只能定义同一种的类型的NodeComponent
boolean legal = classes.size() == 1;
if (!legal){
throw new LiteFlowException("The cmpClass of the same nodeId must be the same,you declared nodeId:" + activeNodeId + ",cmpClass:" + classes);
}
// 当前节点实际LiteflowRetry注解
AtomicReference<LiteflowRetry> liteflowRetryAtomicReference = new AtomicReference<>(null);
// 相同nodeId只能有一个LiteflowRetry定义方法,且必须再Process方法上
boolean illegal = tupleList.stream().anyMatch(
tuple -> {
LiteflowRetry liteflowRetry = tuple.get(0);
LiteflowMethod liteflowMethod = tuple.get(1);
boolean existRetry = liteflowRetry != null;
boolean isProcess = liteflowMethod.value().equals(LiteFlowMethodEnum.PROCESS)
|| liteflowMethod.value().equals(LiteFlowMethodEnum.PROCESS_IF)
|| liteflowMethod.value().equals(LiteFlowMethodEnum.PROCESS_SWITCH);
// 如果是再Process方法上的liteflowRetry注解则默认为真实节点。
if (isProcess && existRetry) {
liteflowRetryAtomicReference.set(liteflowRetry);
}
// 如果存在existRetry注解但是不是在Process方法上则为非法
return existRetry && !isProcess;
}
);
if (illegal){
throw new LiteFlowException("the retry annotation (@LiteflowRetry) must be declared on the PROCESS method");
}
// 生成nodeCmp的类型默认为全局定义的clazz
Class<?> cmpClazz;
cmpClazz = clazz;
// 判断是否是方法声明的组件
if (isMethodCreate){
cmpClazz = methodList.iterator().next().nodeType().getCmpClass();
LiteflowRetry liteflowRetry;
if ((liteflowRetry = liteflowRetryAtomicReference.get()) != null){
// 增加LiteFlowRetry注解到注解数组里
List<Annotation> annotations = Arrays.stream(beanClassAnnotation)
.filter(a -> !a.annotationType().equals(LiteflowRetry.class))
.collect(Collectors.toList());
annotations.add(liteflowRetry);
beanClassAnnotation = new Annotation[annotations.size()];
annotations.toArray(beanClassAnnotation);
}
}
try {
//创建对象
//这里package进行了重设放到了被代理对象的所在目录
//生成的对象也加了上被代理对象拥有的注解
//被拦截的对象也根据被代理对象根据@LiteFlowMethod所标注的进行了动态判断
Object instance = new ByteBuddy().subclass(cmpClazz)
.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(beanClassAnnotation)
.make()
.load(ComponentProxy.class.getClassLoader())
.getLoaded()
.newInstance();
NodeComponent nodeComponent = (NodeComponent) instance;
// 重设nodeId
nodeComponent.setNodeId(activeNodeId);
return nodeComponent;
} catch (Exception e) {
throw new LiteFlowException(e);
}
}).collect(Collectors.toList());
}
public class AopInvocationHandler implements InvocationHandler {
@@ -93,6 +177,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);

View File

@@ -0,0 +1,48 @@
package com.yomahub.liteflow.enums;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.core.NodeIfComponent;
import com.yomahub.liteflow.core.NodeSwitchComponent;
/**
* 注解节点类型枚举
*
* @author Sorghum
* @since 2.9.0
*/
public enum AnnotationNodeTypeEnum {
/**
* 普通节点
*/
COMMON("普通", NodeComponent.class),
/**
* 选择节点
*/
SWITCH("选择", NodeSwitchComponent.class),
/**
* 条件节点
*/
IF("条件", NodeIfComponent.class),;
/**
* 描述
*/
final String desc;
/**
* cmp类
*/
final Class<? extends NodeComponent> cmpClass;
AnnotationNodeTypeEnum(String desc, Class<? extends NodeComponent> cmpClass) {
this.desc = desc;
this.cmpClass = cmpClass;
}
/**
* 得到Node定义类
*
* @return {@link Class}<{@link ?} {@link extends} {@link NodeComponent}>
*/
public Class<? extends NodeComponent> getCmpClass() {
return cmpClass;
}
}

View File

@@ -9,16 +9,17 @@
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;
import com.yomahub.liteflow.core.*;
import com.yomahub.liteflow.exception.NullNodeTypeException;
import com.yomahub.liteflow.flow.element.Chain;
import com.yomahub.liteflow.flow.element.Node;
import com.yomahub.liteflow.enums.FlowParserTypeEnum;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import com.yomahub.liteflow.exception.ComponentCannotRegisterException;
import com.yomahub.liteflow.exception.NullNodeTypeException;
import com.yomahub.liteflow.flow.element.Chain;
import com.yomahub.liteflow.flow.element.Node;
import com.yomahub.liteflow.parser.LocalJsonFlowParser;
import com.yomahub.liteflow.parser.LocalXmlFlowParser;
import com.yomahub.liteflow.parser.LocalYmlFlowParser;
@@ -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,42 +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, cmpInstance.getNodeId() == null ? nodeId : cmpInstance.getNodeId())).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) {
String error = StrUtil.format("component[{}] register error", StrUtil.isEmpty(name)?nodeId:StrUtil.format("{}({})",nodeId,name));
LOG.error(error);

View File

@@ -1,21 +1,22 @@
package com.yomahub.liteflow.util;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.annotation.LiteflowCmpDefine;
import com.yomahub.liteflow.annotation.LiteflowIfCmpDefine;
import com.yomahub.liteflow.annotation.LiteflowSwitchCmpDefine;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.annotation.LiteflowSwitchCmpDefine;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.core.NodeIfComponent;
import com.yomahub.liteflow.core.NodeSwitchComponent;
import com.yomahub.liteflow.core.proxy.ComponentProxy;
import com.yomahub.liteflow.exception.ComponentProxyErrorException;
import com.yomahub.liteflow.exception.LiteFlowException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Arrays;
import java.util.List;
/**
* 组件代理类通用方法
@@ -29,23 +30,6 @@ public class LiteFlowProxyUtil {
//判断一个bean是否是声明式组件
public static boolean isDeclareCmp(Class<?> clazz){
//判断bean是否标记了@LiteflowCmpDefine,@LiteflowCondCmpDefine,LiteflowIfCmpDefine这3个标注之一
boolean flag1 = clazz.getAnnotation(LiteflowCmpDefine.class) != null
|| clazz.getAnnotation(LiteflowSwitchCmpDefine.class) != null
|| clazz.getAnnotation(LiteflowIfCmpDefine.class) != null;
if (!flag1){
return false;
}
//看超类是否是NodeComponent,NodeCondComponent,NodeIfComponent中的一个如果不是则说明满足条件。是的话也不满足
boolean flag2 = !ListUtil.toList(NodeComponent.class, NodeSwitchComponent.class, NodeIfComponent.class)
.contains(clazz.getSuperclass());
if (!flag2){
return false;
}
//查看bean里的method是否有方法标记了@LiteflowMethod标注
//这里的bean有可能是cglib加强过的class所以要先进行个判断
Class<?> targetClass;
@@ -54,15 +38,14 @@ public class LiteFlowProxyUtil {
}else{
targetClass = clazz;
}
boolean flag3 = Arrays.stream(targetClass.getMethods()).anyMatch(
// 判断是否有方法标记了@LiteflowMethod标注有则为声明式组件
return Arrays.stream(targetClass.getMethods()).anyMatch(
method -> method.getAnnotation(LiteflowMethod.class) != null
);
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,21 +54,23 @@ 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();
}catch (Exception e){
return new ComponentProxy(nodeId, bean, NodeIfComponent.class).getProxyList();
}catch (LiteFlowException liteFlowException){
throw liteFlowException;
}
catch (Exception e){
String errMsg = StrUtil.format("Error while proxying bean[{}]",bean.getClass().getName());
LOG.error(errMsg);
throw new ComponentProxyErrorException(errMsg);