最近工作问题总结——反射&volatile等

根据业务需求,需要每天从服务器拿数据,数据格式以“|”分割,类似zhang san|18,这样的。然后需要读取文件,将其转成Object。因为数据格式不是json或者xml等,所以无法使用fastjson等直接转换成Object。但是数据是有序的,因此可以使用反射或者setter方法进行构建Object。

一、反射

@Slf4j
public class EBBSDataMapper {
    private static volatile Field[] fields;

    public static CustomerInfo convertToCustomer(String line) throws Exception {
        String[] strings = line.split("\\|", -1);
        return arrayToJson(strings);
    }

    public static CustomerInfo arrayToJson(String[] array) throws Exception {
        String[] trimArray = Arrays.stream(array).map(s -> StringUtils.trimToNull(s)).toArray(String[]::new);
        CustomerInfo customerInfo = CustomerInfo.class.newInstance();
        fields = CustomerInfo.class.getDeclaredFields();

        for (int i = 0; i < fields.length; i++) {
            Field field = fields[i];
            // bypass $jacocoData
            // Maven integrates jacoco to count the code coverage of unit tests
            // Jacoco weaves an additional field named $jacocoData into the compiled classes
            // So using java reflect will find this boolean[] field
            if (field.isSynthetic()) {
                continue;
            }

            String fieldName = field.getName();
            field.setAccessible(true);
            String fieldClassName = field.getType().getSimpleName();
            log.debug(fieldName + " [" + fieldClassName + "," + trimArray[i] + "]");

            TargetExecutorFactory.getTargetExecutor(fieldClassName).setData(field, trimArray[i], customerInfo);
        }

        return customerInfo;
    }
}

这里,首先通过String的split将字段分割出来,然后将空字符串“”转成null,接着通过反射将数据存入对象中。

有关反射的知识:Java高级特性——反射

值得注意的是,这里利用多态和简单工厂模式将反射的set重构了,这样以后有利于扩展,而且代码没有if、else if,看起来干净整洁。

public interface TargetExecutor {
    void setData(Field field, String data, CustomerInfo output) throws Exception;
}
@Slf4j
public class LocalDateTargetExecutor implements TargetExecutor {

    @Override
    public void setData(Field field, String data, CustomerInfo output) throws Exception {
        try {
            LocalDate localDate = StringUtils.isEmpty(data) ? null :
                    LocalDate.parse(data, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
            field.set(output, localDate);
        } catch (IllegalAccessException | DateTimeParseException e) {
            log.error(e.getMessage());
            throw e;
        }
    }
}
public class TargetExecutorFactory {
    public static TargetExecutor getTargetExecutor(String fieldClassName) {
        TargetExecutor targetExecutor;
        switch (fieldClassName) {
            case "LocalDate":
                targetExecutor = new LocalDateTargetExecutor();
                break;
            case "String":
                targetExecutor = new StringTargetExecutor();
                break;
            case "BigDecimal":
                targetExecutor = new BigDecimalTargetExecutor();
                break;
            case "EBBSRelStatusEnum":
                targetExecutor = new EBBSRelStatusEnumTargetExecutor();
                break;
            case "GenderEnum":
                targetExecutor = new GenderEnumTargetExecutor();
                break;
            case "RiskLevelEnum":
                targetExecutor = new RiskLevelEnumTargetExecutor();
                break;
            default:
                throw new BatchProcessingException("No TargetExecutor for [" + fieldClassName + "]");
        }
        return targetExecutor;
    }
}

二、Jacoco导致的反射错误

问题描述:当时在本地直接跑测试,是一切正常的。然后上传到Jenkins上面,发现Unit Test Step build失败了。经过打断点,查log,最后才发现是Maven test有问题。Maven test集成了Jacoco做代码覆盖率,而Jacoco在编译时会动态织入一个boolean[] $jacocoData字段,因此在DataMapper中无法找到该boolean数组如何set数据,导致错误。

解决办法:只要把该字段跳过就好。

if (field.isSynthetic()) {
    continue;
}

三、多线程下反射资源冲突

问题描述:因为数据量比较多,因此采用Java的线程池去多线程处理数据转换。但是,实际运行时发现,有些数据无法set进去。

解决办法:采用volatile

private static volatile Field[] fields;

volatile学习:Java中Volatile关键字详解

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值