feature #I64L3Q 期望拓展@ScriptBean注解,能注入指定类的指定方法
This commit is contained in:
parent
d9257392d8
commit
ffa29a9330
|
@ -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){
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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 {};
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue