You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 18:22:34