diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/annotation/util/AnnoUtil.java b/liteflow-core/src/main/java/com/yomahub/liteflow/annotation/util/AnnoUtil.java index 45e1e2382..09fb5362f 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/annotation/util/AnnoUtil.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/annotation/util/AnnoUtil.java @@ -44,7 +44,7 @@ public class AnnoUtil { return annotation; } - public static Object getDefaultValue(Class annotationType, String property){ + private static Object getDefaultValue(Class annotationType, String property){ try{ return annotationType.getMethod(property).getDefaultValue(); }catch (Exception e){ diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/exception/ScriptBeanMethodInvokeException.java b/liteflow-core/src/main/java/com/yomahub/liteflow/exception/ScriptBeanMethodInvokeException.java new file mode 100644 index 000000000..c65deb183 --- /dev/null +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/exception/ScriptBeanMethodInvokeException.java @@ -0,0 +1,29 @@ +package com.yomahub.liteflow.exception; + +/** + * ScriptBean的方法无法被调用异常 + * @author Bryan.Zhang + */ +public class ScriptBeanMethodInvokeException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + /** + * 异常信息 + */ + private String message; + + public ScriptBeanMethodInvokeException(String message) { + this.message = message; + } + + @Override + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + +} diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/script/ScriptBean.java b/liteflow-core/src/main/java/com/yomahub/liteflow/script/annotation/ScriptBean.java similarity index 51% rename from liteflow-core/src/main/java/com/yomahub/liteflow/script/ScriptBean.java rename to liteflow-core/src/main/java/com/yomahub/liteflow/script/annotation/ScriptBean.java index e0dfbca0f..e99b9aa33 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/script/ScriptBean.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/script/annotation/ScriptBean.java @@ -1,4 +1,6 @@ -package com.yomahub.liteflow.script; +package com.yomahub.liteflow.script.annotation; + +import com.yomahub.liteflow.annotation.AliasFor; import java.lang.annotation.*; @@ -13,5 +15,13 @@ import java.lang.annotation.*; @Inherited public @interface ScriptBean { + @AliasFor("name") String value() default ""; + + @AliasFor("value") + String name() default ""; + + String[] includeMethodName() default {}; + + String[] excludeMethodName() default {}; } diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/script/jsr223/JSR223ScriptExecutor.java b/liteflow-core/src/main/java/com/yomahub/liteflow/script/jsr223/JSR223ScriptExecutor.java index 4bd5c6b53..fced63a9f 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/script/jsr223/JSR223ScriptExecutor.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/script/jsr223/JSR223ScriptExecutor.java @@ -98,7 +98,9 @@ public abstract class JSR223ScriptExecutor implements ScriptExecutor { }catch (Exception e){ if (ObjectUtil.isNotNull(e.getCause()) && e.getCause() instanceof LiteFlowException){ throw (LiteFlowException)e.getCause(); - }else{ + } else if (ObjectUtil.isNotNull(e.getCause()) && e.getCause() instanceof RuntimeException) { + throw (RuntimeException)e.getCause(); + } else{ throw e; } } diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/script/proxy/ScriptBeanProxy.java b/liteflow-core/src/main/java/com/yomahub/liteflow/script/proxy/ScriptBeanProxy.java new file mode 100644 index 000000000..f5a5512b7 --- /dev/null +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/script/proxy/ScriptBeanProxy.java @@ -0,0 +1,107 @@ +package com.yomahub.liteflow.script.proxy; + +import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.util.*; +import com.yomahub.liteflow.exception.LiteFlowException; +import com.yomahub.liteflow.exception.ScriptBeanMethodInvokeException; +import com.yomahub.liteflow.script.annotation.ScriptBean; +import com.yomahub.liteflow.util.LiteFlowProxyUtil; +import com.yomahub.liteflow.util.SerialsUtil; +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.implementation.InvocationHandlerAdapter; +import net.bytebuddy.matcher.ElementMatchers; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class ScriptBeanProxy { + + private final Logger LOG = LoggerFactory.getLogger(this.getClass()); + + private final Object bean; + + private final Class orignalClass; + + private final ScriptBean scriptBeanAnno; + + public ScriptBeanProxy(Object bean, Class orignalClass, ScriptBean scriptBeanAnno) { + this.bean = bean; + this.orignalClass = orignalClass; + this.scriptBeanAnno = scriptBeanAnno; + } + + public Object getProxyScriptBean(){ + //获取bean里所有的method,包括超类里的 + List methodNameList = Arrays.stream(orignalClass.getMethods()).map(Method::getName).collect(Collectors.toList()); + + //首先看@ScriptBean标注里的includeMethodName属性 + //如果没有配置,则认为是全部的method,如果有配置,就取配置的method + if (ArrayUtil.isNotEmpty(scriptBeanAnno.includeMethodName())){ + methodNameList = methodNameList.stream().filter( + methodName -> ListUtil.toList(scriptBeanAnno.includeMethodName()).contains(methodName) + ).collect(Collectors.toList()); + } + + //其次看excludeMethodName的配置 + if (ArrayUtil.isNotEmpty(scriptBeanAnno.excludeMethodName())){ + methodNameList = methodNameList.stream().filter( + methodName -> !ListUtil.toList(scriptBeanAnno.excludeMethodName()).contains(methodName) + ).collect(Collectors.toList()); + } + + try{ + return new ByteBuddy().subclass(orignalClass) + .name(StrUtil.format("{}.ByteBuddy${}", + ClassUtil.getPackage(orignalClass), + SerialsUtil.generateShortUUID())) + .method(ElementMatchers.any()) + .intercept(InvocationHandlerAdapter.of(new AopInvocationHandler(bean, methodNameList))) + .annotateType(orignalClass.getAnnotations()) + .make() + .load(ScriptBeanProxy.class.getClassLoader()) + .getLoaded() + .newInstance(); + }catch (Exception e){ + throw new LiteFlowException(e); + } + + } + + public class AopInvocationHandler implements InvocationHandler { + + private final Object bean; + + private final Class clazz; + + private final List canExecuteMethodNameList; + + public AopInvocationHandler(Object bean, List canExecuteMethodNameList) { + this.bean = bean; + this.clazz = LiteFlowProxyUtil.getUserClass(bean.getClass()); + this.canExecuteMethodNameList = canExecuteMethodNameList; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + Method invokeMethod = Arrays.stream(clazz.getMethods()).filter( + m -> m.getName().equals(method.getName()) && m.getParameterCount() == method.getParameterCount() + ).findFirst().orElse(null); + + if (invokeMethod == null){ + String errorMsg = StrUtil.format("cannot find method[{}]", clazz.getName(), method.getName()); + throw new ScriptBeanMethodInvokeException(errorMsg); + } + + if (!canExecuteMethodNameList.contains(method.getName())){ + String errorMsg = StrUtil.format("script bean method[{}.{}] cannot be executed", clazz.getName(), method.getName()); + throw new ScriptBeanMethodInvokeException(errorMsg); + } + + return invokeMethod.invoke(bean, args); + } + } +} diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/util/LiteFlowProxyUtil.java b/liteflow-core/src/main/java/com/yomahub/liteflow/util/LiteFlowProxyUtil.java index 98d246479..c65faca1a 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/util/LiteFlowProxyUtil.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/util/LiteFlowProxyUtil.java @@ -32,12 +32,7 @@ public class LiteFlowProxyUtil { public static boolean isDeclareCmp(Class clazz) { //查看bean里的method是否有方法标记了@LiteflowMethod标注 //这里的bean有可能是cglib加强过的class,所以要先进行个判断 - Class targetClass; - if (isCglibProxyClass(clazz)) { - targetClass = getUserClass(clazz); - } else { - targetClass = clazz; - } + Class targetClass = getUserClass(clazz); // 判断是否有方法标记了@LiteflowMethod标注,有则为声明式组件 return Arrays.stream(targetClass.getMethods()).anyMatch( method -> method.getAnnotation(LiteflowMethod.class) != null diff --git a/liteflow-spring/src/main/java/com/yomahub/liteflow/spring/ComponentScanner.java b/liteflow-spring/src/main/java/com/yomahub/liteflow/spring/ComponentScanner.java index 874f84b45..138a8a0e0 100644 --- a/liteflow-spring/src/main/java/com/yomahub/liteflow/spring/ComponentScanner.java +++ b/liteflow-spring/src/main/java/com/yomahub/liteflow/spring/ComponentScanner.java @@ -8,14 +8,15 @@ */ package com.yomahub.liteflow.spring; -import cn.hutool.core.annotation.AnnotationUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; +import com.yomahub.liteflow.annotation.util.AnnoUtil; import com.yomahub.liteflow.aop.ICmpAroundAspect; import com.yomahub.liteflow.core.NodeComponent; import com.yomahub.liteflow.property.LiteflowConfig; -import com.yomahub.liteflow.script.ScriptBean; +import com.yomahub.liteflow.script.annotation.ScriptBean; import com.yomahub.liteflow.script.ScriptBeanManager; +import com.yomahub.liteflow.script.proxy.ScriptBeanProxy; import com.yomahub.liteflow.util.LOGOPrinter; import com.yomahub.liteflow.util.LiteFlowProxyUtil; import org.slf4j.Logger; @@ -61,7 +62,7 @@ public class ComponentScanner implements BeanPostProcessor { @SuppressWarnings("rawtypes") @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { - Class clazz = bean.getClass(); + Class clazz = LiteFlowProxyUtil.getUserClass(bean.getClass()); //判断是不是声明式组件 //如果是,就缓存到类属性的map中 @@ -98,9 +99,10 @@ public class ComponentScanner implements BeanPostProcessor { } //扫描@ScriptBean修饰的类 - ScriptBean scriptBean = AnnotationUtil.getAnnotation(bean.getClass(), ScriptBean.class); + ScriptBean scriptBean = AnnoUtil.getAnnotation(clazz, ScriptBean.class); if (ObjectUtil.isNotNull(scriptBean)){ - ScriptBeanManager.addScriptBean(scriptBean.value(), bean); + ScriptBeanProxy proxy = new ScriptBeanProxy(bean, clazz, scriptBean); + ScriptBeanManager.addScriptBean(scriptBean.value(), proxy.getProxyScriptBean()); } return bean; diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-graaljs-springboot/src/test/java/com/yomahub/liteflow/test/script/graaljs/scriptbean/bean/DemoBean1.java b/liteflow-testcase-el/liteflow-testcase-el-script-graaljs-springboot/src/test/java/com/yomahub/liteflow/test/script/graaljs/scriptbean/bean/DemoBean1.java index 77625ac01..5b63a16b4 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-script-graaljs-springboot/src/test/java/com/yomahub/liteflow/test/script/graaljs/scriptbean/bean/DemoBean1.java +++ b/liteflow-testcase-el/liteflow-testcase-el-script-graaljs-springboot/src/test/java/com/yomahub/liteflow/test/script/graaljs/scriptbean/bean/DemoBean1.java @@ -1,6 +1,6 @@ package com.yomahub.liteflow.test.script.graaljs.scriptbean.bean; -import com.yomahub.liteflow.script.ScriptBean; +import com.yomahub.liteflow.script.annotation.ScriptBean; import org.springframework.stereotype.Component; import javax.annotation.Resource; diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/java/com/yomahub/liteflow/test/script/groovy/scriptbean/LiteFlowScriptScriptbeanGroovyELTest.java b/liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/java/com/yomahub/liteflow/test/script/groovy/scriptbean/LiteFlowScriptScriptbeanGroovyELTest.java index b6039085f..6eee3a732 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/java/com/yomahub/liteflow/test/script/groovy/scriptbean/LiteFlowScriptScriptbeanGroovyELTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/java/com/yomahub/liteflow/test/script/groovy/scriptbean/LiteFlowScriptScriptbeanGroovyELTest.java @@ -1,6 +1,7 @@ package com.yomahub.liteflow.test.script.groovy.scriptbean; import com.yomahub.liteflow.core.FlowExecutor; +import com.yomahub.liteflow.exception.ScriptBeanMethodInvokeException; import com.yomahub.liteflow.flow.LiteflowResponse; import com.yomahub.liteflow.slot.DefaultContext; import com.yomahub.liteflow.test.BaseTest; @@ -41,4 +42,37 @@ public class LiteFlowScriptScriptbeanGroovyELTest extends BaseTest { Assert.assertEquals("hello,kobe", context.getData("demo")); } + //测试scriptBean includeMethodName配置包含情况下 + @Test + public void testScriptBean3() throws Exception{ + LiteflowResponse response = flowExecutor.execute2Resp("chain3", "arg"); + Assert.assertTrue(response.isSuccess()); + DefaultContext context = response.getFirstContextBean(); + Assert.assertEquals("hello,kobe", context.getData("demo")); + } + + //测试scriptBean includeMethodName配置不包含情况下 + @Test + public void testScriptBean4() throws Exception{ + LiteflowResponse response = flowExecutor.execute2Resp("chain4", "arg"); + Assert.assertFalse(response.isSuccess()); + Assert.assertEquals(ScriptBeanMethodInvokeException.class, response.getCause().getClass()); + } + + //测试scriptBean excludeMethodName配置不包含情况下 + @Test + public void testScriptBean5() throws Exception{ + LiteflowResponse response = flowExecutor.execute2Resp("chain5", "arg"); + Assert.assertTrue(response.isSuccess()); + DefaultContext context = response.getFirstContextBean(); + Assert.assertEquals("hello,kobe", context.getData("demo")); + } + + //测试scriptBean excludeMethodName配置包含情况下 + @Test + public void testScriptBean6() throws Exception{ + LiteflowResponse response = flowExecutor.execute2Resp("chain6", "arg"); + Assert.assertFalse(response.isSuccess()); + Assert.assertEquals(ScriptBeanMethodInvokeException.class, response.getCause().getClass()); + } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/java/com/yomahub/liteflow/test/script/groovy/scriptbean/bean/DemoBean1.java b/liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/java/com/yomahub/liteflow/test/script/groovy/scriptbean/bean/DemoBean1.java index 5b04bb3c8..23fa5c252 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/java/com/yomahub/liteflow/test/script/groovy/scriptbean/bean/DemoBean1.java +++ b/liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/java/com/yomahub/liteflow/test/script/groovy/scriptbean/bean/DemoBean1.java @@ -1,6 +1,6 @@ package com.yomahub.liteflow.test.script.groovy.scriptbean.bean; -import com.yomahub.liteflow.script.ScriptBean; +import com.yomahub.liteflow.script.annotation.ScriptBean; import org.springframework.stereotype.Component; import javax.annotation.Resource; diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/java/com/yomahub/liteflow/test/script/groovy/scriptbean/bean/DemoBean3.java b/liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/java/com/yomahub/liteflow/test/script/groovy/scriptbean/bean/DemoBean3.java new file mode 100644 index 000000000..37dcbc38f --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/java/com/yomahub/liteflow/test/script/groovy/scriptbean/bean/DemoBean3.java @@ -0,0 +1,21 @@ +package com.yomahub.liteflow.test.script.groovy.scriptbean.bean; + +import com.yomahub.liteflow.script.annotation.ScriptBean; +import org.springframework.stereotype.Component; + +@Component +@ScriptBean(name = "demo3", includeMethodName = {"test1","test2"}) +public class DemoBean3 { + + public String test1(String name){ + return "hello,"+name; + } + + public String test2(String name){ + return "hello,"+name; + } + + public String test3(String name){ + return "hello,"+name; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/java/com/yomahub/liteflow/test/script/groovy/scriptbean/bean/DemoBean4.java b/liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/java/com/yomahub/liteflow/test/script/groovy/scriptbean/bean/DemoBean4.java new file mode 100644 index 000000000..34ac5075b --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/java/com/yomahub/liteflow/test/script/groovy/scriptbean/bean/DemoBean4.java @@ -0,0 +1,21 @@ +package com.yomahub.liteflow.test.script.groovy.scriptbean.bean; + +import com.yomahub.liteflow.script.annotation.ScriptBean; +import org.springframework.stereotype.Component; + +@Component +@ScriptBean(name = "demo4", excludeMethodName = {"test2","test3"}) +public class DemoBean4 { + + public String test1(String name){ + return "hello,"+name; + } + + public String test2(String name){ + return "hello,"+name; + } + + public String test3(String name){ + return "hello,"+name; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/resources/scriptbean/flow.xml b/liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/resources/scriptbean/flow.xml index c9e22fa3e..397cff2c4 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/resources/scriptbean/flow.xml +++ b/liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/resources/scriptbean/flow.xml @@ -15,6 +15,34 @@ defaultContext.setData("demo", str) ]]> + + + + + + + + + + + + + + + + @@ -24,4 +52,20 @@ THEN(a,b,c,e); + + + THEN(a,b,c,s1); + + + + THEN(a,b,c,s2); + + + + THEN(a,b,c,s3); + + + + THEN(a,b,c,s4); + \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-javascript-springboot/src/test/java/com/yomahub/liteflow/test/script/javascript/scriptbean/bean/DemoBean1.java b/liteflow-testcase-el/liteflow-testcase-el-script-javascript-springboot/src/test/java/com/yomahub/liteflow/test/script/javascript/scriptbean/bean/DemoBean1.java index 7cd387e7a..60bb7dba3 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-script-javascript-springboot/src/test/java/com/yomahub/liteflow/test/script/javascript/scriptbean/bean/DemoBean1.java +++ b/liteflow-testcase-el/liteflow-testcase-el-script-javascript-springboot/src/test/java/com/yomahub/liteflow/test/script/javascript/scriptbean/bean/DemoBean1.java @@ -1,6 +1,6 @@ package com.yomahub.liteflow.test.script.javascript.scriptbean.bean; -import com.yomahub.liteflow.script.ScriptBean; +import com.yomahub.liteflow.script.annotation.ScriptBean; import org.springframework.stereotype.Component; import javax.annotation.Resource; diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/script/qlexpress/scriptbean/bean/DemoBean1.java b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/script/qlexpress/scriptbean/bean/DemoBean1.java index cf5e59093..5b6bc7704 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/script/qlexpress/scriptbean/bean/DemoBean1.java +++ b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/script/qlexpress/scriptbean/bean/DemoBean1.java @@ -1,6 +1,6 @@ package com.yomahub.liteflow.test.script.qlexpress.scriptbean.bean; -import com.yomahub.liteflow.script.ScriptBean; +import com.yomahub.liteflow.script.annotation.ScriptBean; import org.springframework.stereotype.Component; import javax.annotation.Resource;