Java 8时间包的Spring批序列化问题

Java 8时间包的Spring批序列化问题,java,spring,spring-batch,xstream,java-time,Java,Spring,Spring Batch,Xstream,Java Time,我有一个Spring批处理应用程序,它在JobExecutionContext中存储了几个Java8时间对象。我正在使用JobRespository的默认序列化程序。当解析回写入BATCH_STEP_EXECUTION_上下文表的数据时,我会遇到异常。我的LocalDateTime存储为: { "@resolves-to": "java.time.Ser", "byte": [5, 8, 18, 8, 45, 50], "int":

我有一个Spring批处理应用程序,它在JobExecutionContext中存储了几个Java8时间对象。我正在使用JobRespository的默认序列化程序。当解析回写入BATCH_STEP_EXECUTION_上下文表的数据时,我会遇到异常。我的LocalDateTime存储为:

{
    "@resolves-to": "java.time.Ser",
    "byte": [5,
    8,
    18,
    8,
    45,
    50],
    "int": [2015,
    10000000]
}
当我尝试读取以前的JobExecution数据时,这会导致异常:

Caused by: java.lang.ClassCastException: java.lang.Byte cannot be cast to java.lang.Integer
at com.thoughtworks.xstream.core.util.CustomObjectInputStream.readInt(CustomObjectInputStream.java:144) ~[xstream-1.4.8.jar:1.4.8]
at java.time.LocalDate.readExternal(LocalDate.java:2070) ~[na:1.8.0_45]
at java.time.LocalDateTime.readExternal(LocalDateTime.java:2002) ~[na:1.8.0_45]
at java.time.Ser.readInternal(Ser.java:259) ~[na:1.8.0_45]
at java.time.Ser.readExternal(Ser.java:246) ~[na:1.8.0_45]
at com.thoughtworks.xstream.converters.reflection.ExternalizableConverter.unmarshal(ExternalizableConverter.java:167) ~[xstream-1.4.8.jar:1.4.8]
at com.thoughtworks.xstream.core.TreeUnmarshaller.convert(TreeUnmarshaller.java:72) ~[xstream-1.4.8.jar:na]
... 97 common frames omitted
我使用的是SpringBatch 3.0.5.0版本。我还尝试升级到最新版本的xstream(1.4.8)和抛弃(1.3.7),但我得到了同样的例外


这似乎是XStream()的一个已知问题。建议在XStream中注册一个自定义转换器。但是,SpringBatch不会为了注册转换器而公开实际的XStream对象。关于如何继续的任何建议?

Spring Batch允许您通过实现
ExecutionContext序列化程序
接口并将其注入
JobRepositoryFactoryBean
来为
ExecutionContext
配置自己的序列化程序


您是正确的,我们目前不允许您注入自己的XStream实例(尽管考虑到这个问题,它似乎是一个合理的扩展点)。现在,您必须扩展或复制
XStreamExecutionContextStringSerializer
,并使用您自己的XStream实例。

从步骤执行上下文反序列化
LocalDate
时,我遇到了同样的问题

因此,我必须制作合适的转换器:

public class DateConverter implements Converter {

    private static final String            DEFAULT_DATE_PATTERN = "yyyy-MM-dd";
    private static final DateTimeFormatter DEFAULT_DATE_FORMATTER = DateTimeFormatter.ofPattern(DEFAULT_DATE_PATTERN);

    public DateConverter() {
        super();
    }

    public boolean canConvert(Class clazz) {
        return LocalDate.class.isAssignableFrom(clazz);
    }

    /**
     * Convert LocalDate to String
     */
    public void marshal(Object value, HierarchicalStreamWriter writer, MarshallingContext context) {
        LocalDate  date = (LocalDate) value;
        String result = date.format(DEFAULT_DATE_FORMATTER);
        writer.setValue(result);
    }

    /**
     * convert Xml to LocalDate
     */
    public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
        LocalDate result = LocalDate.parse(reader.getValue(), DEFAULT_DATE_FORMATTER);
        return result;
    }
}
之后,我必须为使用转换器创建适当的
XStreamExecutionContextStringSerializer

/**
 * My XStreamExecutionContextStringSerializer
 * @since 1.0
 */
public class MyXStreamExecutionContextStringSerializer implements ExecutionContextSerializer, InitializingBean {

    private ReflectionProvider reflectionProvider = null;

    private HierarchicalStreamDriver hierarchicalStreamDriver;

    private XStream xstream;

    public void setReflectionProvider(ReflectionProvider reflectionProvider) {
        this.reflectionProvider = reflectionProvider;
    }

    public void setHierarchicalStreamDriver(HierarchicalStreamDriver hierarchicalStreamDriver) {
        this.hierarchicalStreamDriver = hierarchicalStreamDriver;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        init();
    }

    public synchronized void init() throws Exception {
        if (hierarchicalStreamDriver == null) {
            this.hierarchicalStreamDriver = new JettisonMappedXmlDriver();
        }
        if (reflectionProvider == null) {
            xstream =  new XStream(hierarchicalStreamDriver);
        }
        else {
            xstream = new XStream(reflectionProvider, hierarchicalStreamDriver);
        }

        // Convert LocalDate
        xstream.registerConverter(new DateConverter());
    }

    /**
     * Serializes the passed execution context to the supplied OutputStream.
     *
     * @param context
     * @param out
     * @see Serializer#serialize(Object, OutputStream)
     */
    @Override
    public void serialize(Map<String, Object> context, OutputStream out) throws IOException {
        Assert.notNull(context);
        Assert.notNull(out);

        out.write(xstream.toXML(context).getBytes());
    }

    /**
     * Deserializes the supplied input stream into a new execution context.
     *
     * @param in
     * @return a reconstructed execution context
     * @see Deserializer#deserialize(InputStream)
     */
    @SuppressWarnings("unchecked")
    @Override
    public Map<String, Object> deserialize(InputStream in) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(in));

        StringBuilder sb = new StringBuilder();

        String line;
        while ((line = br.readLine()) != null) {
            sb.append(line);
        }

        return (Map<String, Object>) xstream.fromXML(sb.toString());
    }
}
/**
*我的XStreamExecutionContextStringSerializer
*@自1.0以来
*/
公共类MyXStreamExecutionContextStringSerializer实现ExecutionContextSerializer,InitializingBean{
私有ReflectionProvider ReflectionProvider=null;
私有HierarchicalStreamDriver HierarchicalStreamDriver;
私有XStream XStream;
public void setReflectionProvider(ReflectionProvider ReflectionProvider){
this.reflectionProvider=reflectionProvider;
}
public void setHierarchicalStreamDriver(HierarchicalStreamDriver HierarchicalStreamDriver){
this.hierarchicalStreamDriver=hierarchicalStreamDriver;
}
@凌驾
public void afterPropertieSet()引发异常{
init();
}
public synchronized void init()引发异常{
如果(HierarchycalStreamDriver==null){
this.hierarchycalstreamdriver=新的丢弃nmappedxmldriver();
}
if(reflectionProvider==null){
xstream=新的xstream(HierarchycalStreamDriver);
}
否则{
xstream=新的xstream(reflectionProvider、HierarchycalStreamDriver);
}
//转换本地日期
registerConverter(新的日期转换器());
}
/**
*将传递的执行上下文序列化到提供的OutputStream。
*
*@param上下文
*@param out
*@请参阅序列化程序#序列化(对象、输出流)
*/
@凌驾
公共void序列化(映射上下文,OutputStream out)引发IOException{
Assert.notNull(上下文);
Assert.notNull(out);
write(xstream.toXML(context.getBytes());
}
/**
*将提供的输入流反序列化到新的执行上下文中。
*
*@param-in
*@返回重建的执行上下文
*@请参阅反序列化程序#反序列化(InputStream)
*/
@抑制警告(“未选中”)
@凌驾
公共映射反序列化(InputStream in)引发IOException{
BufferedReader br=新的BufferedReader(新的InputStreamReader(in));
StringBuilder sb=新的StringBuilder();
弦线;
而((line=br.readLine())!=null){
某人附加(行);
}
返回(映射)xstream.fromXML(sb.toString());
}
}
最后一步是在注册bean jobRepository的文件execution-context.xml中注册MyXStreamExecutionContextStringSerializer

<bean id="jobRepository"
    class="org.springframework.batch.core.repository.support.JobRepositoryFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="transactionManager" ref="transactionManager" />
    <property name="tablePrefix" value="${batch.table.prefix:BATCH.BATCH_}" />
    <property name="serializer"> <bean class="com.batch.config.MyXStreamExecutionContextStringSerializer"/> </property>
</bean>


您是否在配置类上添加了@EnableBatchProcessing注释?我添加了自己的客户序列化程序。但是,
JobRepositoryFactoryBean
仍在使用默认的
XStreamExecutionContextStringSerializer
,当我尝试从
JobExplorer
读取作业数据时,这会导致以下异常:com.thoughtworks.xstream.converters.ConversionException:无法使用新的readObject()反序列化对象/writeObject()方法
----调试信息--
类:java.time.LocalDate
所需类型:java.time.LocalDate
转换器类型:com.thoughtworks.xstream.converters.reflection.SerializableConverter
单击Spring Batch Admin上的重新启动按钮时UI触发JobExecutionController。重新启动,它正在尝试使用默认的XStreamExecutionContextStringSerializer进行序列化。我在该场景中遇到了相同的异常(我的堆栈跟踪包括XStreamExecutionContextStringSerializer,而不是我的自定义序列化程序)。请在问题中发布JobRepositoryFactoryBean的配置好吗?