Activiti7踩坑记录(二),依赖冲突NoSuchMethodError: javax.el.ELUtil.getExpressionFactory

本文介绍了解决Activiti与SpringBoot集成时因ELUtil方法缺失导致的NoSuchMethodError问题,通过排除特定依赖和调整版本来修复。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

项目场景:

运行环境:
SpringBoot@2.3.1,自带tomcat9
Activiti@7.1.0M4

问题描述:

引进依赖后,尝试测试一下业务流程,结果第一行debug还没开始,就已经出现错误了

测试代码:

	@Autowired
    private RepositoryService repositoryService;
    @Test
    public void initDeploymentBPMN(){
        //bpmn文件所在的文件路径
        String filename="processes/2.bpmn";
        //部署bpmn文件
        Deployment deployment=repositoryService.createDeployment()
                .addClasspathResource(filename)
                .name("流程部署测试BPMN")
                .deploy();
        System.out.println(deployment.getName());
    }

报错信息:

....
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.validation.beanvalidation.LocalValidatorFactoryBean]: Factory method 'defaultValidator' threw exception; nested exception is java.lang.NoSuchMethodError: javax.el.ELUtil.getExpressionFactory()Ljavax/el/ExpressionFactory;
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185)
	at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:650)
	... 82 more
Caused by: java.lang.NoSuchMethodError: javax.el.ELUtil.getExpressionFactory()Ljavax/el/ExpressionFactory;
	at javax.el.ELManager.getExpressionFactory(ELManager.java:38)
	at org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator.buildExpressionFactory(ResourceBundleMessageInterpolator.java:171)
	at org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator.<init>(ResourceBundleMessageInterpolator.java:94)
	at org.hibernate.validator.internal.engine.AbstractConfigurationImpl.getDefaultMessageInterpolator(AbstractConfigurationImpl.java:570)
	at org.springframework.boot.validation.MessageInterpolatorFactory.getObject(MessageInterpolatorFactory.java:53)
	at org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration.defaultValidator(ValidationAutoConfiguration.java:57)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154)
	... 83 more

相关依赖pom.xml:

		<properties>
	        <activiti.version>7.1.0.M4</activiti.version>
	    </properties>
	    
	    .....
	    
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-spring-boot-starter</artifactId>
            <version>${activiti.version}</version>
<!--            mybatis-plus引用冲突,需要排除-->
            <exclusions>
                <exclusion>
                    <artifactId>mybatis</artifactId>
                    <groupId>org.mybatis</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.activiti.dependencies</groupId>
            <artifactId>activiti-dependencies</artifactId>
            <version>${activiti.version}</version>
            <type>pom</type>
        </dependency>
        <!--自动化生成流程 -->
        <!-- https://mvnrepository.com/artifact/org.activiti/activiti-bpmn-layout -->
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-bpmn-layout</artifactId>
            <version>${activiti.version}</version>
        </dependency>

原因分析:

控制台抛出的是NoSuchMethodErrorjavax.el.ELUtil.getExpressionFactory()方法不存在,这种一般是冲突问题。

1.先排查有多少个重复类包

IDEA中双击shift查询类,这里发现有org.glassfish:jakarta.el:3.0.3javax.el:el-api:2.2两个包,都提供了相同类名
在这里插入图片描述
org.glassfish.jakarta.el.ELUtil中包含有正确方法,另外一个没有
在这里插入图片描述
2.通过idea的maven依赖图表排查各种引用的包

org.glassfish.jakarta.el由tomcat引入,说明之前错误是tomcat抛出的
在这里插入图片描述
el-api由activiti-spring引入,这就是罪魁祸首了,排除掉即可
在这里插入图片描述

解决方案:

对Activiti-Spring引入的el-api进行排除

	<exclusion>
        <artifactId>el-api</artifactId>
        <groupId>javax.el</groupId>
    </exclusion>

修改后pom.xml:

		<properties>
	        <activiti.version>7.1.0.M4</activiti.version>
	    </properties>
	    
	    .....
	    
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-spring-boot-starter</artifactId>
            <version>${activiti.version}</version>
<!--            mybatis-plus引用冲突,需要排除-->
            <exclusions>
                <exclusion>
                    <artifactId>mybatis</artifactId>
                    <groupId>org.mybatis</groupId>
                </exclusion>
                <exclusion>
			        <artifactId>el-api</artifactId>
			        <groupId>javax.el</groupId>
			    </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.activiti.dependencies</groupId>
            <artifactId>activiti-dependencies</artifactId>
            <version>${activiti.version}</version>
            <type>pom</type>
        </dependency>
        <!--自动化生成流程 -->
        <!-- https://mvnrepository.com/artifact/org.activiti/activiti-bpmn-layout -->
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-bpmn-layout</artifactId>
            <version>${activiti.version}</version>
        </dependency>

后记

一开始发现这个问题时分析了很久没有头绪,百度找到篇可能相关的问题博文:activiti7 + springboot2 (十五) JSP页面使用EL表达式引起的错误解决
按文中方法看到大概是tomcat包冲突,但是排除Activiti的juel-api和juel-spi依赖后,问题仍然没有解决,反倒是增加了一堆排查变量,徒增排查时间…

后面在stackoverflow.查询相关问题,才窥得问题所在:问题链接
此问题引入不一样,但都有ELManager相关问题:java.lang.NoClassDefFoundError: javax/el/ELManager
按此答案中引入新依赖后可以解决问题了。下面讨论是el-api相关的东西,看到红框之后我才明白问题在哪:
tomcat8以上用的是el-api@3.0,然后activiti-spring这里引入的是el-api@2.2,升级版本后缺失方法,将el-api版本进行升级即可,后续根据这个思路理出了这篇博文
在这里插入图片描述

<think>我们正在解决Activiti 6中,使用formService.submitTaskFormData提交任务表单时,由于表达式使用了函数但未提供FunctionMapper导致的ELException异常。 异常信息:Expression uses functions no function mapper provided 分析原因: 在Activiti 6中,当我们在表单属性(form property)中使用表达式(如${myFunction()})时,需要配置FunctionMapper来解析这些函数。 然而,在默认情况下,Activiti并没有提供FunctionMapper的实现,因此当表达式中包含函数调用时,就会抛出上述异常。 解决方案: 1. 避免在表单表达式中使用函数调用(如果可能的话)。 2. 自定义FunctionMapper并注册到Activiti的配置中。 由于我们无法避免使用函数调用(比如需要动态计算某些值),因此我们选择第种方案。 具体步骤: 步骤1:创建自定义FunctionMapper 我们需要实现javax.el.FunctionMapper接口,将表达式中的函数名映射到具体的Java方法。 步骤2:将自定义FunctionMapper注册到Activiti配置中 在流程引擎配置中设置自定义的FunctionMapper。 步骤3:在表达式中使用函数 下面详细说明: 步骤1:创建自定义FunctionMapper 例如,我们有一个工具类StringUtils,其中有一个静态方法join,我们想在表达式中使用它。 首先,我们需要在自定义FunctionMapper中注册这个函数。 示例代码: ```java import javax.el.FunctionMapper; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; public class ActivitiFunctionMapper extends FunctionMapper { private static final Map<String, Method> FUNCTION_MAP = new HashMap<>(); static { try { // 注册函数:将表达式中的函数名映射到具体的Java方法 // 例如:在表达式中使用 ${str:join(array)} 对应 StringUtils.join(array) FUNCTION_MAP.put("str:join", StringUtils.class.getMethod("join", Object[].class)); // 可以继续注册其他函数 } catch (NoSuchMethodException e) { throw new RuntimeException("初始化函数映射失败", e); } } @Override public Method resolveFunction(String prefix, String localName) { // 组合成 key: prefix:localName String key = prefix + ":" + localName; return FUNCTION_MAP.get(key); } } ``` 步骤2:在流程引擎配置中设置FunctionMapper 在创建流程引擎配置时,设置自定义的FunctionMapper。 如果你使用Spring配置: ```xml <bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration"> <!-- 其他配置 --> <property name="functionMappers"> <list> <bean class="com.example.ActivitiFunctionMapper"/> </list> </property> </bean> ``` 如果是非Spring环境,在代码中配置: ```java ProcessEngineConfiguration config = ProcessEngineConfiguration.createStandaloneProcessEngineConfiguration(); config.setFunctionMappers(Collections.singletonList(new ActivitiFunctionMapper())); // 其他配置... ProcessEngine engine = config.buildProcessEngine(); ``` 步骤3:在表单表达式中使用函数 在表单定义中,我们可以这样使用: ```xml <startEvent id="start" name="Start"> <extensionElements> <activiti:formProperty id="deptList" name="部门列表" expression="${str:join(handleDepts)}" required="true"/> </extensionElements> </startEvent> ``` 注意:这里我们假设handleDepts是一个数组或集合,而str:join函数将调用StringUtils.join方法将其连接成字符串。 但是,请注意:在表单属性中使用表达式时,表达式中包含函数调用,Activiti在解析表达式时就需要FunctionMapper。 另外,我们还需要注意,在提交表单时,我们仍然需要按照之前讨论的方法处理集合变量(因为表单提交只接受字符串)。所以,我们可能需要在表达式中进行转换,或者使用监听器进行转换。 如果我们在表达式中使用函数,那么必须确保FunctionMapper已经配置,否则就会抛出异常。 补充:如果不想使用自定义FunctionMapper,还有另一种方法,即使用委托表达式(delegate expression)或执行监听器(execution listener)在流程中设置变量,避免在表单表达式中使用函数。 例如,我们可以将转换集合为字符串的操作放在监听器中: ```java public class JoinDeptsListener implements ExecutionListener { @Override public void notify(DelegateExecution execution) { List<String> handleDepts = (List<String>) execution.getVariable("handleDepts"); String deptsStr = StringUtils.join(handleDepts.toArray(), ","); execution.setVariable("deptList", deptsStr); } } ``` 然后在流程定义中: ```xml <startEvent id="start"> <extensionElements> <activiti:executionListener event="start" class="com.example.JoinDeptsListener" /> </extensionElements> </startEvent> ``` 这样,在流程启动时,监听器会自动将集合转换为字符串,并设置到变量中。然后表单属性可以直接使用${deptList},而无需调用函数。 总结: 要解决ELException: Expression uses functions no function mapper provided,有两种主要方法: 1. 配置自定义FunctionMapper(适用于需要在表达式中使用函数的场景)。 2. 避免在表达式中使用函数,改用监听器或委托表达式来设置变量(推荐,更简单且避免配置FunctionMapper)。 根据实际情况选择合适的方法。 相关问题: 1. 如何在Activiti中配置自定义EL函数? 2. 如何避免在Activiti表达式中使用函数? 3.Activiti中,执行监听器和任务监听器有什么区别?</think>### 解决 Activiti 6 中 `formService.submitTaskFormData` 的 ELException 异常 在 Activiti 6 中使用 `formService.submitTaskFormData` 提交任务表单时,若表达式包含函数调用但未配置 `FunctionMapper`,会抛出 `ELException: Expression uses functions no function mapper provided` 异常。以下是完整解决方案: --- #### 问题根本原因 1. **EL 表达式限制**: - Activiti 使用 JUEL 实现表达式解析 - 当表单表达式包含函数调用(如 `${myFunction()}`)时,需要显式提供 `FunctionMapper` - 未配置时抛出异常:`error.method.literal.void = Expected type ''void'' is not allowed for literal method expression ''{0}''` [^1] 2. **典型错误场景**: ```xml <!-- BPMN 文件中包含函数调用 --> <userTask id="task1" activiti:assignee="${myUtility.getAssignee()}"/> ``` --- #### 解决方案 ##### 方案1:配置全局 FunctionMapper(推荐) **步骤:** 1. 创建自定义 `FunctionMapper` 实现: ```java import javax.el.FunctionMapper; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; public class ActivitiFunctionMapper extends FunctionMapper { private static final Map<String, Method> FUNCTION_MAP = new HashMap<>(); static { try { // 注册表达式函数映射 FUNCTION_MAP.put("myUtility:getAssignee", MyUtility.class.getMethod("getAssignee")); } catch (NoSuchMethodException e) { throw new RuntimeException("函数映射初始化失败", e); } } @Override public Method resolveFunction(String prefix, String localName) { return FUNCTION_MAP.get(prefix + ":" + localName); } } ``` 2. 在流程引擎配置中注册: ```java ProcessEngineConfiguration config = ProcessEngineConfiguration .createStandaloneProcessEngineConfiguration(); // 注册自定义 FunctionMapper config.setFunctionMappers(Collections.singletonList(new ActivitiFunctionMapper())); ProcessEngine engine = config.buildProcessEngine(); ``` 3. 在表达式中使用: ```xml <!-- 使用 prefix:functionName 格式 --> <userTask id="task1" activiti:assignee="${myUtility:getAssignee()}"/> ``` ##### 方案2:避免函数表达式 将函数调用移至监听器: ```java public class AssigneeListener implements TaskListener { @Override public void notify(DelegateTask task) { // 直接调用Java方法 String assignee = MyUtility.getAssignee(); task.setAssignee(assignee); } } ``` BPMN 配置: ```xml <userTask id="task1"> <extensionElements> <activiti:taskListener event="create" class="com.example.AssigneeListener"/> </extensionElements> </userTask> ``` ##### 方案3:表达式预处理 在提交前解析表达式: ```java // 获取原始表单数据 TaskFormData formData = formService.getTaskFormData(taskId); List<FormProperty> formProperties = formData.getFormProperties(); // 预处理表达式 Map<String, String> processedValues = new HashMap<>(); for (FormProperty prop : formProperties) { String value = prop.getValue(); if (value != null && value.contains("${")) { // 自定义解析逻辑 value = ExpressionParser.parse(value); } processedValues.put(prop.getId(), value); } // 提交预处理后的值 formService.submitTaskFormData(taskId, processedValues); ``` --- #### 关键配置点 1. **JUEL 配置增强**: ```java // 启用完整EL功能 config.setExpressionManager(new JuelExpressionManager( null, new BeanElResolver(), new ArrayElResolver(), new ListElResolver(), new MapElResolver() )); ``` 2. **Spring 集成配置**: ```xml <bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration"> <property name="functionMappers"> <list> <bean class="com.example.ActivitiFunctionMapper"/> </list> </property> </bean> ``` --- #### 错误处理建议 1. **表达式验证工具**: ```java try { Expression expr = expressionManager.createExpression("${myFunction()}"); expr.getValue(execution); } catch (ActivitiException e) { logger.error("表达式解析失败: {}", e.getMessage()); } ``` 2. **安全函数白名单**: ```java // 在FunctionMapper中限制可用函数 public Method resolveFunction(String prefix, String localName) { if (!"safe".equals(prefix)) return null; // 只允许safe前缀函数 return SAFE_FUNCTIONS.get(prefix + ":" + localName); } ``` --- #### 最佳实践 1. **表达式规范**: - 避免在表达式中直接调用业务方法 - 优先使用变量引用:`${assignee}` 而非 `${getAssignee()}` 2. **混合方案示例**: ```xml <!-- 安全表达式 + 监听器 --> <sequenceFlow id="flow1" sourceRef="start" targetRef="task1"> <conditionExpression xsi:type="tFormalExpression"> ${safe:isValid(approvalResult)} <!-- 白名单函数 --> </conditionExpression> </sequenceFlow> <userTask id="task1"> <extensionElements> <activiti:taskListener event="create" delegateExpression="${taskAssignmentDelegate}"/> <!-- Spring Bean --> </extensionElements> </userTask> ``` 通过上述方案,可彻底解决 `Expression uses functions no function mapper provided` 异常,同时保证表达式功能的灵活使用[^1]。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值