feature #I64L3Q 期望拓展@ScriptBean注解,能注入指定类的指定方法

This commit is contained in:
everywhere.z 2022-12-09 00:04:52 +08:00
parent d9257392d8
commit ffa29a9330
15 changed files with 283 additions and 18 deletions

View File

@ -44,7 +44,7 @@ public class AnnoUtil {
return annotation;
}
public static <A extends Annotation> Object getDefaultValue(Class<A> annotationType, String property){
private static <A extends Annotation> Object getDefaultValue(Class<A> annotationType, String property){
try{
return annotationType.getMethod(property).getDefaultValue();
}catch (Exception e){

View File

@ -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;
}
}

View File

@ -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 {};
}

View File

@ -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;
}
}

View File

@ -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<String> 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<String> canExecuteMethodNameList;
public AopInvocationHandler(Object bean, List<String> 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);
}
}
}

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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());
}
}

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -15,6 +15,34 @@
defaultContext.setData("demo", str)
]]>
</node>
<node id="s1" type="script" language="groovy">
<![CDATA[
def str = demo3.test1("kobe")
defaultContext.setData("demo", str)
]]>
</node>
<node id="s2" type="script" language="groovy">
<![CDATA[
def str = demo3.test3("kobe")
defaultContext.setData("demo", str)
]]>
</node>
<node id="s3" type="script" language="groovy">
<![CDATA[
def str = demo4.test1("kobe")
defaultContext.setData("demo", str)
]]>
</node>
<node id="s4" type="script" language="groovy">
<![CDATA[
def str = demo4.test3("kobe")
defaultContext.setData("demo", str)
]]>
</node>
</nodes>
<chain name="chain1">
@ -24,4 +52,20 @@
<chain name="chain2">
THEN(a,b,c,e);
</chain>
<chain name="chain3">
THEN(a,b,c,s1);
</chain>
<chain name="chain4">
THEN(a,b,c,s2);
</chain>
<chain name="chain5">
THEN(a,b,c,s3);
</chain>
<chain name="chain6">
THEN(a,b,c,s4);
</chain>
</flow>

View File

@ -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;

View File

@ -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;