动态生成enum(dynamic enum)

由于历史代码中存在不能轻易修改的enum类,为避免影响,文章介绍了如何在Java中动态添加枚举属性。参考了网上资源,提供了一个名为DynamicEnumUtil的工具类,用于动态创建枚举实例。枚举类Carrier包括三个属性,文章提供了相关链接供深入学习。

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

因为以前的老代码定义了enum类,而这部分代码是作为公共的jar,修改时会影响很大,所以希望动态添加枚举属性

首先是一个工具类DynamicEnumUtil ,参考网上的:

原文链接:

http://bbs.youkuaiyun.com/topics/391037532

https://www.niceideas.ch/roller2/badtrash/entry/java_create_enum_instances_dynamically



import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import com.**.enums.Carrier;

import sun.reflect.ConstructorAccessor;
import sun.reflect.FieldAccessor;
import sun.reflect.ReflectionFactory;

public class DynamicEnumUtil {

   private static ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory();

   private static void setFailsafeFieldValue(Field field, Object target, Object value) throws NoSuchFieldException,
           IllegalAccessException {

       // let's make the field accessible
       field.setAccessible(true);

       // next we change the modifier in the Field instance to
       // not be final anymore, thus tricking reflection into
       // letting us modify the static final field
       Field modifiersField = Field.class.getDeclaredField("modifiers");
       modifiersField.setAccessible(true);
       int modifiers = modifiersField.getInt(field);

       // blank out the final bit in the modifiers int
       modifiers &= ~Modifier.FINAL;
       modifiersField.setInt(field, modifiers);

       FieldAccessor fa = reflectionFactory.newFieldAccessor(field, false);
       fa.set(target, value);
<think>我们正在处理Activiti工作流引擎中的自定义表单字段类型问题。用户希望实现动态的enum类型枚举值,即在流程定义中枚举值不是固定的,而是可以动态变化的。 在Activiti中,默认的表单字段类型包括enum,但它的枚举值是在流程定义时固定的。为了实现动态枚举值,我们需要自定义一个表单类型,并在运行时动态提供枚举选项。 参考引用[1]中提到,Activiti允许自定义表单字段类型。我们可以通过实现`org.activiti.engine.form.AbstractFormType`类来创建自定义类型。 具体步骤: 1. 创建一个自定义表单类型类,继承`AbstractFormType`,并实现必要的方法。 2. 在流程定义文件中使用自定义类型,如`<activiti:formProperty id="status" name="状态" type="dynamicEnum" />`。 3. 在运行时,通过重写`getInformation()`方法返回动态枚举值。 然而,用户要求的是动态枚举值,即枚举值可能来自数据库、配置文件或其他外部源。因此,我们需要在自定义表单类型中能够根据上下文动态获取枚举值。 注意:在Activiti中,表单的渲染通常是在流程启动时或任务完成时。因此,我们可以在这些时刻动态获取枚举值。 实现方案: 1. 创建自定义表单类型类,例如`DynamicEnumFormType`。 2. 在该类中,我们需要覆盖`getName()`(返回类型名称,如"dynamicEnum")和`convertFormValueToModelValue(String propertyValue)`(将表单值转换为模型值)以及`convertModelValueToFormValue(Object modelValue)`(将模型值转换为表单值)方法。 3. 关键是如何提供动态的枚举选项?在Activiti中,表单的选项通常是在流程定义中固定的,但我们可以通过扩展Activiti的表单渲染机制来实现动态选项。 另一种思路:在自定义表单类型中,我们并不直接存储枚举值,而是存储一个键(key),然后通过这个键在运行时去获取实际的枚举选项列表。例如,我们可以将类型定义为`<activiti:formProperty id="user" name="用户" type="dynamicEnum:userList" />`,其中`userList`是一个标识符,表示我们要从某个服务获取用户列表。 但是,Activiti的表单类型定义并不直接支持传递参数。因此,我们可以这样设计: - 在自定义表单类型中,我们通过解析类型字符串来获取动态源标识。例如,类型字符串可以是`dynamicEnum:${enumProviderBean.getEnumOptions('type')}`,但这样并不灵活。 更常见的做法是:在表单渲染时,通过服务任务或者监听器来设置表单属性。但是,这超出了自定义表单类型的范围。 实际上,Activiti的表单属性可以在流程实例启动前或者任务完成前通过Java代码动态设置。我们可以利用这一点: 1. 在流程定义中,定义一个类型为`string`(或自定义类型)的表单属性。 2. 在启动流程实例或完成任务时,通过`FormService`获取启动表单(或任务表单),然后动态添加枚举选项。 但是,这种方式需要我们在每次获取表单时都动态设置,而且Activiti的API并不直接支持在表单属性中动态添加选项。 因此,我们采用自定义表单类型,并在该类型中获取动态枚举值。我们需要在自定义表单类型中注入一个服务(例如通过Spring),然后通过该服务获取枚举值。 由于Activiti的表单类型是在流程引擎配置中注册的,我们可以将自定义表单类型配置为Spring Bean,从而注入所需的服务。 步骤: 1. 创建自定义表单类型类: ```java public class DynamicEnumFormType extends AbstractFormType { private EnumService enumService; // 用于获取动态枚举值的服务 // 通过setter注入或使用Spring自动注入 public void setEnumService(EnumService enumService) { this.enumService = enumService; } @Override public String getName() { return "dynamicEnum"; } // 将表单值(字符串)转换为模型值(存储到变量中的值) @Override public Object convertFormValueToModelValue(String propertyValue) { // 这里我们假设存储的是枚举的键(字符串)或者枚举值的编码(如Integer),根据实际情况转换 // 因为我们动态枚举,所以直接返回字符串即可,或者根据业务需求转换 return propertyValue; } // 将模型值转换为表单值(字符串) @Override public String convertModelValueToFormValue(Object modelValue) { if (modelValue == null) { return null; } return modelValue.toString(); } // 关键:如何获取枚举选项?Activiti的表单引擎会调用getInformation()来获取选项 // 但默认的AbstractFormType并没有定义获取选项的方法,所以我们需要扩展。 // 实际上,在Activiti中,对于枚举类型,通常是在流程定义中通过activiti:value节点定义固定选项。 // 因此,为了支持动态,我们需要重写渲染过程。 // 注意:在Activiti中,表单的渲染是由FormData提供的,而FormData中的表单属性包含了FormProperty对象,每个FormProperty有一个Map<String, String>的typeInfo,其中可以包含选项。 // 所以,我们可以在自定义表单类型中提供一个方法,用于设置动态选项,但Activiti并没有提供这样的扩展点。 // 替代方案:在获取表单数据之前,通过服务设置动态选项。 } ``` 由于Activiti的FormProperty的typeInfo是在流程定义时确定的,我们不能在运行时修改。因此,我们需要在流程定义中避免使用enum类型,而是使用自定义类型,并在获取表单数据时动态设置选项。 参考引用[5]中提到,我们可以通过代码在提交表单前设置一些信息。同样,在获取表单数据时,我们也可以动态设置。 因此,我们可以这样做: 1. 定义一个普通的字符串类型的表单属性,不要使用enum类型。 2. 在获取启动表单(或任务表单)时,动态添加一个包含枚举选项的变量,然后在前端根据这个变量生成下拉框。 但是,这样就不是通过自定义表单类型实现了,而是通过业务逻辑代码。 如果我们坚持使用自定义表单类型,我们可以参考Activiti的EnumFormType,它也是AbstractFormType的子类,并且它有一个`getValues()`方法返回枚举值。但是,这个类在Activiti中并没有暴露这个方法,而且它的值是在解析流程定义时固定的。 因此,我们需要扩展Activiti的表单引擎,这比较复杂。 另一种推荐的做法: 在流程定义中使用`string`类型,然后通过外部表单(如Freemarker、Vaadin等)来动态生成下拉框。Activiti的REST API返回的表单数据中,我们可以附加额外的信息(如枚举选项)。 具体实现: 1. 在流程定义中,定义表单属性为`string`类型。 2. 当用户请求启动表单时,我们不仅返回表单属性,还返回一个动态获取的枚举选项列表(作为额外的数据)。 3. 前端根据枚举选项列表渲染下拉框。 这样,我们就实现了动态枚举值。 但是,用户要求的是在Activiti中实现,即使用Activiti自带的表单引擎。那么,我们可能需要修改Activiti的源代码,这并不推荐。 因此,我建议采用自定义表单类型并结合服务注入的方式,但需要重写表单数据获取的逻辑。 具体步骤: 1. 创建自定义表单类型(如`DynamicEnumFormType`),并实现`getInformation`方法(虽然AbstractFormType没有这个方法,但我们可以模仿EnumFormType,但EnumFormType是内部类,不可直接使用)。 2. 在流程引擎配置中注册这个自定义表单类型。 3. 在流程定义中使用这个类型,例如:`type="dynamicEnum"`。 4. 在获取表单数据时,Activiti会为每个表单属性创建FormProperty对象,但我们的自定义类型需要提供枚举选项。因此,我们需要在创建FormProperty时动态获取选项。 但是,Activiti并没有提供这样的扩展点。所以,我们需要一个更高级的解决方案。 参考Activiti的UserTaskActivityBehavior,它负责创建任务表单。我们可以通过重写这个行为来动态设置表单属性的选项。 但这需要深入Activiti内部,比较复杂。 鉴于以上分析,一个可行的折中方案是:在流程定义中,我们将枚举类型定义为普通字符串,然后通过流程变量来设置枚举选项。具体做法如下: 1. 在流程启动前,先设置流程变量(如`statusEnumValues`),该变量是一个Map或者List,包含枚举选项。 2. 在表单渲染时,前端从流程变量中读取这个变量,然后生成下拉框。 这样,我们就不需要自定义表单类型了,而是通过流程变量和前端配合实现。 但是,如果用户使用的是Activiti Explorer(官方提供的管理界面),那么我们需要修改Activiti Explorer的代码来支持这种动态枚举。 所以,如果用户使用的是自己的业务系统,那么可以在自己的业务系统中处理表单,动态获取枚举值,然后通过Activiti的表单服务提交。 总结: 在Activiti中实现完全动态的枚举值在自带的表单引擎中比较困难。推荐的做法是: 1. 使用外部表单,在渲染表单时动态获取枚举选项(通过调用业务服务)。 2. 在流程定义中,对应的表单属性使用`string`类型。 3. 提交表单时,将选中的值作为字符串提交。 这样,我们就不需要自定义表单类型,而是将动态枚举的逻辑放在外部系统中处理。 如果必须使用Activiti自带的表单引擎,那么我们需要自定义表单类型,并且需要扩展Activiti的表单渲染机制(例如,通过重写部分类),但这需要深入了解Activiti的内部机制。 因此,根据引用[1]中提到的自定义表单类型,我们可以实现一个基础的自定义类型,但动态枚举值的获取需要我们在获取表单数据时通过其他方式注入。这可能需要结合流程变量或者调用外部服务。 由于时间限制,这里给出一个简化的示例,展示如何创建自定义表单类型,并尝试在类型中获取动态枚举值(通过静态方法,实际应用中可以通过Spring注入服务): ```java public class DynamicEnumFormType extends AbstractFormType { public static final String TYPE_NAME = "dynamicEnum"; @Override public String getName() { return TYPE_NAME; } @Override public Object convertFormValueToModelValue(String propertyValue) { return propertyValue; } @Override public String convertModelValueToFormValue(Object modelValue) { if (modelValue == null) { return null; } return modelValue.toString(); } // 自定义方法,用于获取枚举选项,但Activiti的表单引擎不会调用这个方法 public Map<String, String> getValues() { // 这里模拟动态获取枚举值 Map<String, String> values = new HashMap<>(); values.put("value1", "Label1"); values.put("value2", "Label2"); return values; } } ``` 在流程引擎配置中注册: ```xml <bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration"> ... <property name="customFormTypes"> <list> <bean class="com.example.DynamicEnumFormType" /> </list> </property> </bean> ``` 在流程定义中使用: ```xml <activiti:formProperty id="status" name="状态" type="dynamicEnum" /> ``` 但是,如何让表单引擎在渲染时调用`getValues()`方法获取选项呢?Activiti默认不会调用。因此,我们需要在获取表单数据后,手动设置每个表单属性的选项。这可以通过拦截FormService的调用实现,但同样复杂。 因此,对于大多数应用,建议使用外部表单系统来处理动态枚举。 如果用户坚持要在Activiti内部实现,那么可能需要扩展FormService和相关的类,这超出了简单自定义的范围。 综上所述,回答用户的问题:在Activiti中实现动态枚举值,最常用的方法是通过外部表单系统动态生成枚举选项。如果必须使用Activiti自带表单,则需要通过自定义表单类型并结合流程变量或外部服务调用,但需要扩展Activiti的表单渲染机制,这较为复杂。 相关问题: 1. Activiti中自定义表单类型还有哪些常见用途? 2. 如何在Activiti中动态设置流程表单的选项? 3. Activiti表单引擎和外部表单系统有何优缺点?</think>### 在Activiti中实现动态枚举值的方法 在Activiti中实现动态枚举值(即枚举选项从数据库或外部服务动态获取)可通过**自定义表单字段类型**实现。以下是具体步骤和代码示例: --- ### 1. 创建自定义表单类型类 继承`AbstractFormType`并实现核心方法: ```java import org.activiti.engine.form.AbstractFormType; import java.util.Map; public class DynamicEnumFormType extends AbstractFormType { private final EnumService enumService; // 注入枚举服务 public DynamicEnumFormType(EnumService enumService) { this.enumService = enumService; } @Override public String getName() { return "dynamicEnum"; // 类型标识符 } // 表单值 → 流程变量值 @Override public Object convertFormValueToModelValue(String propertyValue) { return enumService.getEnumByCode(propertyValue); // 转换为枚举对象 } // 流程变量值 → 表单值 @Override public String convertModelValueToFormValue(Object modelValue) { return ((BusinessStatusEnum) modelValue).getCode().toString(); // 提取枚举编码 } // 关键:获取动态枚举选项 public Map<String, String> getValues() { return enumService.loadDynamicOptions(); // 从服务获取动态选项 } } ``` --- ### 2. 实现枚举服务 定义获取动态枚举的服务接口: ```java public interface EnumService { Map<String, String> loadDynamicOptions(); // 返回选项键值对 BusinessStatusEnum getEnumByCode(String code); // 编码转枚举 } ``` 实现示例(从数据库获取): ```java @Service public class DbEnumService implements EnumService { @Autowired private StatusRepository statusRepo; // 数据库仓库 @Override public Map<String, String> loadDynamicOptions() { return statusRepo.findAllActiveStatuses() .stream() .collect(Collectors.toMap( Status::getCode, Status::getName )); } @Override public BusinessStatusEnum getEnumByCode(String code) { return BusinessStatusEnum.getEnumByCode(Integer.parseInt(code)); } } ``` --- ### 3. 注册自定义类型 在流程引擎配置中注册类型: ```xml <bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration"> <property name="customFormTypes"> <list> <bean class="com.example.DynamicEnumFormType"> <constructor-arg ref="dbEnumService"/> <!-- 注入服务 --> </bean> </list> </property> </bean> ``` --- ### 4. 流程定义中使用 在BPMN文件中声明动态枚举字段: ```xml <process id="dynamic_enum_process"> <startEvent> <extensionElements> <activiti:formProperty id="businessStatus" name="业务状态" type="dynamicEnum" <!-- 使用自定义类型 --> required="true"/> </extensionElements> </startEvent> </process> ``` --- ### 5. 前端渲染动态选项 获取表单时动态生成选项: ```java StartFormData formData = formService.getStartFormData(processDefId); for (FormProperty prop : formData.getFormProperties()) { if (prop.getType() instanceof DynamicEnumFormType) { DynamicEnumFormType enumType = (DynamicEnumFormType) prop.getType(); Map<String, String> options = enumType.getValues(); // 动态选项 // 传递给前端渲染下拉框 } } ``` --- ### 关键点说明 1. **动态数据源** 通过`EnumService`实现从数据库、API或配置文件中实时获取枚举值[^1][^4]。 2. **类型转换** `convertModelValueToFormValue()`和`convertFormValueToModelValue()`方法实现枚举对象与表单值的双向转换[^1]。 3. **流程变量存储** 实际存储的是枚举编码(如Integer),而非显示文本,保证数据一致性[^4]。 4. **扩展性** 可结合Spring EL表达式实现更复杂的动态逻辑,如根据用户角色过滤选项。 > **提示**:对于需要发起人信息的场景,需在提交前设置`Authentication.setAuthenticatedUserId(userId)`[^5]。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值