bug #ICO23A 修复设置AbstractBeanFactory的cacheMetadata为false时LF无法正确获取rawClass的问题

This commit is contained in:
gqh
2025-07-22 17:35:34 +08:00
parent 851c3f908b
commit f5da5aaa5f
6 changed files with 174 additions and 38 deletions

View File

@@ -2,11 +2,13 @@ package com.yomahub.liteflow.spring;
import cn.hutool.core.annotation.AnnotationUtil; import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.annotation.LiteflowMethod; import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.proxy.DeclWarpBean; import com.yomahub.liteflow.core.proxy.DeclWarpBean;
import com.yomahub.liteflow.log.LFLog; import com.yomahub.liteflow.log.LFLog;
import com.yomahub.liteflow.log.LFLoggerManager; import com.yomahub.liteflow.log.LFLoggerManager;
import com.yomahub.liteflow.spi.holder.DeclComponentParserHolder; import com.yomahub.liteflow.spi.holder.DeclComponentParserHolder;
import org.apache.commons.lang3.tuple.Triple;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinition;
@@ -17,7 +19,7 @@ import org.springframework.beans.factory.support.*;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; 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(); String[] beanDefinitionNames = defaultListableBeanFactory.getBeanDefinitionNames();
Arrays.stream(beanDefinitionNames).filter(beanName -> { Arrays.stream(beanDefinitionNames)
BeanDefinition beanDefinition = defaultListableBeanFactory.getMergedBeanDefinition(beanName); .map(beanName -> combineAsTriple(beanName, defaultListableBeanFactory))
Class<?> rawClass = getRawClassFromBeanDefinition(beanDefinition); .filter(this::hasLiteflowMethodAnnotation)
if (rawClass == null){ .forEach(triple -> splitAndRegisterNewBeanDefinition(triple, defaultListableBeanFactory));
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<DeclWarpBean> 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);
});
});
} }
@Override @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<String, BeanDefinition, Class<?>> 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<String, BeanDefinition, Class<?>> 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<String, BeanDefinition, Class<?>> 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"); Method method = ReflectUtil.getMethodByName(DeclBeanDefinition.class, "getResolvableType");
if (method != null){ if (method != null) {
Object resolvableType = ReflectUtil.invoke(beanDefinition, method); Object resolvableType = ReflectUtil.invoke(beanDefinition, method);
return ReflectUtil.invoke(resolvableType, "getRawClass"); res = ReflectUtil.invoke(resolvableType, "getRawClass");
}else{
return ReflectUtil.invoke(beanDefinition, "getTargetType");
} }
}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; return null;
} }
} }

View File

@@ -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<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
applicationContext.getBeanFactory().setCacheBeanMetadata(false);
}
}

View File

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

View File

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

View File

@@ -0,0 +1 @@
liteflow.rule-source=cacheBeanMetadata/flow.xml

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE flow PUBLIC "liteflow" "https://liteflow.cc/liteflow.dtd">
<flow>
<chain name="chain1">
THEN(demo);
</chain>
</flow>