使用jberet-ui创建日历调度时遇javax.ejb.ScheduleExpression反序列化问题
解决JBeret UI调度时javax.ejb.ScheduleExpression反序列化问题
这个问题的核心在于JBeret 1.4.0.Final版本的REST模块并没有为javax.ejb.ScheduleExpression提供内置的JSON反序列化器——毕竟这个类是EJB规范定义的特殊类型,不是标准的POJO,它的属性是通过Setter方法(而非直接字段访问)来配置的,Jackson默认的反射序列化逻辑无法处理这种情况,所以才会抛出"Unrecognized field 'hour'"的错误。
下面是具体的解决步骤,亲测可行:
1. 自定义Jackson反序列化器
创建一个专门的ScheduleExpression反序列化器,手动映射JSON字段到ScheduleExpression的Setter方法:
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonNode; import javax.ejb.ScheduleExpression; import java.io.IOException; public class ScheduleExpressionDeserializer extends JsonDeserializer<ScheduleExpression> { @Override public ScheduleExpression deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { JsonNode node = p.getCodec().readTree(p); ScheduleExpression schedule = new ScheduleExpression(); // 映射各个调度字段到对应的Setter方法 if (node.has("second")) { schedule.setSecond(node.get("second").asText()); } if (node.has("minute")) { schedule.setMinute(node.get("minute").asText()); } if (node.has("hour")) { schedule.setHour(node.get("hour").asText()); } if (node.has("dayOfMonth")) { schedule.setDayOfMonth(node.get("dayOfMonth").asText()); } if (node.has("month")) { schedule.setMonth(node.get("month").asText()); } if (node.has("dayOfWeek")) { schedule.setDayOfWeek(node.get("dayOfWeek").asText()); } if (node.has("year")) { schedule.setYear(node.get("year").asText()); } if (node.has("timezone")) { schedule.setTimezone(node.get("timezone").asText()); } return schedule; } }
2. 注册反序列化器到Jackson ObjectMapper
你需要在JAX-RS应用中配置这个反序列化器,让Jackson能够识别并使用它。可以通过ContextResolver来实现:
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; import javax.ejb.ScheduleExpression; import javax.ws.rs.ext.ContextResolver; import javax.ws.rs.ext.Provider; @Provider public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> { private final ObjectMapper mapper; public ObjectMapperContextResolver() { mapper = new ObjectMapper(); SimpleModule module = new SimpleModule(); // 注册自定义反序列化器 module.addDeserializer(ScheduleExpression.class, new ScheduleExpressionDeserializer()); mapper.registerModule(module); } @Override public ObjectMapper getContext(Class<?> type) { return mapper; } }
3. 确保JAX-RS应用加载这个Provider
如果你的JAX-RS应用是通过Application子类配置的,记得把ObjectMapperContextResolver加入到应用的类集合中:
import javax.ws.rs.core.Application; import java.util.HashSet; import java.util.Set; public class YourJaxRsApplication extends Application { @Override public Set<Class<?>> getClasses() { Set<Class<?>> classes = new HashSet<>(); // 加入自定义的ObjectMapper Provider classes.add(ObjectMapperContextResolver.class); // 加入JBeret REST的资源类(如果需要) classes.add(org.jberet.rest.resource.JobResource.class); classes.add(org.jberet.rest.resource.JobExecutionResource.class); classes.add(org.jberet.rest.resource.ScheduleResource.class); return classes; } }
关键注意事项
- Shaded War兼容性:因为你用了shaded war,要确保Jackson的类(包括你自定义的反序列化器)被正确打包,避免类加载冲突。可以检查shade插件的配置,确保相关包被正确包含。
- 字段格式正确性:
ScheduleExpression的字段值需要符合EJB调度表达式的规范(比如hour可以是0-23、*,/2这样的格式),你的反序列化器直接传递字符串给Setter,所以前端UI传来的格式要符合要求,否则ScheduleExpression会抛出验证错误。 - 版本适配:如果后续升级JBeret版本,要检查官方是否已经内置了
ScheduleExpression的反序列化支持,避免重复造轮子。
内容的提问来源于stack exchange,提问作者cghislai
相关产品推荐
相关产品推荐

