diff --git a/liteflow-spring/src/main/java/com/yomahub/liteflow/spring/DeclBeanDefinition.java b/liteflow-spring/src/main/java/com/yomahub/liteflow/spring/DeclBeanDefinition.java index 7d4d396b6..c92616b1e 100644 --- a/liteflow-spring/src/main/java/com/yomahub/liteflow/spring/DeclBeanDefinition.java +++ b/liteflow-spring/src/main/java/com/yomahub/liteflow/spring/DeclBeanDefinition.java @@ -2,11 +2,13 @@ package com.yomahub.liteflow.spring; import cn.hutool.core.annotation.AnnotationUtil; import cn.hutool.core.util.ReflectUtil; +import cn.hutool.core.util.StrUtil; import com.yomahub.liteflow.annotation.LiteflowMethod; import com.yomahub.liteflow.core.proxy.DeclWarpBean; import com.yomahub.liteflow.log.LFLog; import com.yomahub.liteflow.log.LFLoggerManager; import com.yomahub.liteflow.spi.holder.DeclComponentParserHolder; +import org.apache.commons.lang3.tuple.Triple; import org.springframework.beans.BeansException; import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.factory.config.BeanDefinition; @@ -17,7 +19,7 @@ import org.springframework.beans.factory.support.*; import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; -import java.util.Map; +import java.util.function.Function; /** * 声明式类的元信息注册器 @@ -34,35 +36,10 @@ public class DeclBeanDefinition implements BeanDefinitionRegistryPostProcessor { String[] beanDefinitionNames = defaultListableBeanFactory.getBeanDefinitionNames(); - Arrays.stream(beanDefinitionNames).filter(beanName -> { - BeanDefinition beanDefinition = defaultListableBeanFactory.getMergedBeanDefinition(beanName); - Class rawClass = getRawClassFromBeanDefinition(beanDefinition); - if (rawClass == null){ - return false; - }else{ - return Arrays.stream(rawClass.getMethods()).anyMatch(method -> AnnotationUtil.getAnnotation(method, LiteflowMethod.class) != null); - } - }).forEach(beanName -> { - BeanDefinition beanDefinition = defaultListableBeanFactory.getMergedBeanDefinition(beanName); - Class rawClass = getRawClassFromBeanDefinition(beanDefinition); - List declWarpBeanList = DeclComponentParserHolder.loadDeclComponentParser().parseDeclBean(rawClass); - - declWarpBeanList.forEach(declWarpBean -> { - GenericBeanDefinition newBeanDefinition = new GenericBeanDefinition(); - newBeanDefinition.setBeanClass(DeclWarpBean.class); - newBeanDefinition.setScope(ConfigurableBeanFactory.SCOPE_SINGLETON); - MutablePropertyValues mutablePropertyValues = new MutablePropertyValues(); - mutablePropertyValues.add("nodeId", declWarpBean.getNodeId()); - mutablePropertyValues.add("nodeName", declWarpBean.getNodeName()); - mutablePropertyValues.add("nodeType", declWarpBean.getNodeType()); - mutablePropertyValues.add("rawClazz", declWarpBean.getRawClazz()); - mutablePropertyValues.add("methodWrapBeanList", declWarpBean.getMethodWrapBeanList()); - mutablePropertyValues.add("rawBean", beanDefinition); - newBeanDefinition.setPropertyValues(mutablePropertyValues); - defaultListableBeanFactory.setAllowBeanDefinitionOverriding(true); - defaultListableBeanFactory.registerBeanDefinition(declWarpBean.getNodeId(), newBeanDefinition); - }); - }); + Arrays.stream(beanDefinitionNames) + .map(beanName -> combineAsTriple(beanName, defaultListableBeanFactory)) + .filter(this::hasLiteflowMethodAnnotation) + .forEach(triple -> splitAndRegisterNewBeanDefinition(triple, defaultListableBeanFactory)); } @Override @@ -70,17 +47,91 @@ public class DeclBeanDefinition implements BeanDefinitionRegistryPostProcessor { } - private Class getRawClassFromBeanDefinition(BeanDefinition beanDefinition){ - try{ + /** + * 根据 BeanName 封装一个 triple + * + * @param beanName bean 名称 + * @param defaultListableBeanFactory bean 工厂 + * @return Triple, left 是 beanName, middle 是 beanName 在工厂中对应的 beanDefinition, right 是 rawClass + */ + private Triple> combineAsTriple(String beanName, DefaultListableBeanFactory defaultListableBeanFactory) { + BeanDefinition beanDefinition = defaultListableBeanFactory.getMergedBeanDefinition(beanName); + Class rawClass = getRawClassFromBeanDefinition(beanDefinition); + return Triple.of(beanName, beanDefinition, rawClass); + } + + /** + * 判断 triple 中的 rawClass 是否有 LiteflowMethod 注解修饰的方法 + * + * @param triple beanName、beanDefinition、rawClass 的封装对象 + * @return true 表明 triple 中的 rawClass 有 LiteflowMethod 注解修饰的方法; false 没有 + */ + private boolean hasLiteflowMethodAnnotation(Triple> triple) { + Class rawClass = triple.getRight(); + return rawClass != null && + Arrays.stream(rawClass.getMethods()) + .anyMatch(method -> AnnotationUtil.hasAnnotation(method, LiteflowMethod.class)); + } + + /** + * 根据 triple 中的信息, 对声明式组件的 beanDefinition 进行拆分 + * 拆分成新的 beanDefinition 并注册进 Spring 工厂 + * + * @param triple 声明式组件原始 bean 的封装 + * @param defaultListableBeanFactory bean 工厂 + */ + private void splitAndRegisterNewBeanDefinition(Triple> triple, DefaultListableBeanFactory defaultListableBeanFactory) { + BeanDefinition beanDefinition = triple.getMiddle(); + Class rawClass = triple.getRight(); + DeclComponentParserHolder.loadDeclComponentParser() + .parseDeclBean(rawClass) + .forEach(declWarpBean -> registerNewBeanDefinition(declWarpBean, beanDefinition, defaultListableBeanFactory)); + } + + private void registerNewBeanDefinition(DeclWarpBean declWarpBean, BeanDefinition beanDefinition, DefaultListableBeanFactory defaultListableBeanFactory) { + GenericBeanDefinition newBeanDefinition = new GenericBeanDefinition(); + newBeanDefinition.setBeanClass(DeclWarpBean.class); + newBeanDefinition.setScope(ConfigurableBeanFactory.SCOPE_SINGLETON); + MutablePropertyValues mutablePropertyValues = new MutablePropertyValues(); + mutablePropertyValues.add("nodeId", declWarpBean.getNodeId()); + mutablePropertyValues.add("nodeName", declWarpBean.getNodeName()); + mutablePropertyValues.add("nodeType", declWarpBean.getNodeType()); + mutablePropertyValues.add("rawClazz", declWarpBean.getRawClazz()); + mutablePropertyValues.add("methodWrapBeanList", declWarpBean.getMethodWrapBeanList()); + mutablePropertyValues.add("rawBean", beanDefinition); + newBeanDefinition.setPropertyValues(mutablePropertyValues); + defaultListableBeanFactory.setAllowBeanDefinitionOverriding(true); + defaultListableBeanFactory.registerBeanDefinition(declWarpBean.getNodeId(), newBeanDefinition); + } + + private Class getRawClassFromBeanDefinition(BeanDefinition beanDefinition) { + try { + Class res = null; Method method = ReflectUtil.getMethodByName(DeclBeanDefinition.class, "getResolvableType"); - if (method != null){ + if (method != null) { Object resolvableType = ReflectUtil.invoke(beanDefinition, method); - return ReflectUtil.invoke(resolvableType, "getRawClass"); - }else{ - return ReflectUtil.invoke(beanDefinition, "getTargetType"); + res = ReflectUtil.invoke(resolvableType, "getRawClass"); } - }catch (Exception e){ - LOG.error("An error occurred while obtaining the rowClass.",e); + + if (res != null) + return res; + + res = ReflectUtil.invoke(beanDefinition, "getTargetType"); + if (res != null) + return res; + + String beanClassName = beanDefinition.getBeanClassName(); + if (StrUtil.isNotBlank(beanClassName)) { + try { + res = Class.forName(beanClassName); + } + catch (ClassNotFoundException ignored) {} + } + + return res; + } + catch (Exception e) { + LOG.error("An error occurred while obtaining the rawClass.", e); return null; } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/cacheBeanMetadata/CustomSpringApplicationInitializer.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/cacheBeanMetadata/CustomSpringApplicationInitializer.java new file mode 100644 index 000000000..fc1c2d491 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/cacheBeanMetadata/CustomSpringApplicationInitializer.java @@ -0,0 +1,11 @@ +package com.yomahub.liteflow.test.cacheBeanMetadata; + +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.ConfigurableApplicationContext; + +public class CustomSpringApplicationInitializer implements ApplicationContextInitializer { + @Override + public void initialize(ConfigurableApplicationContext applicationContext) { + applicationContext.getBeanFactory().setCacheBeanMetadata(false); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/cacheBeanMetadata/SpringbootCacheBeanMetadataConfigTest.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/cacheBeanMetadata/SpringbootCacheBeanMetadataConfigTest.java new file mode 100644 index 000000000..f6fd5f143 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/cacheBeanMetadata/SpringbootCacheBeanMetadataConfigTest.java @@ -0,0 +1,51 @@ +package com.yomahub.liteflow.test.cacheBeanMetadata; + +import cn.hutool.core.util.ReflectUtil; +import com.yomahub.liteflow.core.FlowExecutor; +import com.yomahub.liteflow.spring.DeclBeanDefinition; +import com.yomahub.liteflow.test.cacheBeanMetadata.demoComponents.DemoComponent; +import com.yomahub.liteflow.test.component.FlowExecutorELSpringbootTest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.*; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; + +import javax.annotation.Resource; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +@TestPropertySource(value = "classpath:/cacheBeanMetadata/application.properties") +@SpringBootTest(classes = FlowExecutorELSpringbootTest.class) +@EnableAutoConfiguration +@ComponentScan({"com.yomahub.liteflow.test.cacheBeanMetadata.demoComponents"}) +@ContextConfiguration(initializers = CustomSpringApplicationInitializer.class) +public class SpringbootCacheBeanMetadataConfigTest { + @Resource + private FlowExecutor flowExecutor; + @Resource + private ApplicationContext applicationContext; + @Resource + private DeclBeanDefinition declBeanDefinition; + + @Test + public void test() throws InvocationTargetException, IllegalAccessException { + DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory(); + BeanDefinition demoComponent = beanFactory.getMergedBeanDefinition("demoComponent"); + Method method = ReflectUtil.getMethodByName(DeclBeanDefinition.class, "getRawClassFromBeanDefinition"); + method.setAccessible(true); + Object clz = method.invoke(declBeanDefinition, demoComponent); + Assertions.assertNotNull(clz); + Assertions.assertEquals(DemoComponent.class, clz); + } + + @Test + public void test2() { + flowExecutor.execute2Resp("chain1"); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/cacheBeanMetadata/demoComponents/DemoComponent.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/cacheBeanMetadata/demoComponents/DemoComponent.java new file mode 100644 index 000000000..17bc02a35 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/cacheBeanMetadata/demoComponents/DemoComponent.java @@ -0,0 +1,15 @@ +package com.yomahub.liteflow.test.cacheBeanMetadata.demoComponents; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.annotation.LiteflowMethod; +import com.yomahub.liteflow.core.NodeComponent; +import com.yomahub.liteflow.enums.LiteFlowMethodEnum; +import com.yomahub.liteflow.enums.NodeTypeEnum; + +@LiteflowComponent +public class DemoComponent { + @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeId = "demo", nodeName = "demo组件", nodeType = NodeTypeEnum.COMMON) + public void process(NodeComponent demoNode) throws Exception { + System.out.println("当前执行 processA 方法, demoNode.getClass is " + demoNode.getClass() + ", demoNode.getSuperClass is " + demoNode.getClass().getSuperclass()); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/cacheBeanMetadata/application.properties b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/cacheBeanMetadata/application.properties new file mode 100644 index 000000000..0351ddd13 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/cacheBeanMetadata/application.properties @@ -0,0 +1 @@ +liteflow.rule-source=cacheBeanMetadata/flow.xml \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/cacheBeanMetadata/flow.xml b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/cacheBeanMetadata/flow.xml new file mode 100644 index 000000000..63624ca8d --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/cacheBeanMetadata/flow.xml @@ -0,0 +1,7 @@ + + + + + THEN(demo); + + \ No newline at end of file