LitePal icon indicating copy to clipboard operation
LitePal copied to clipboard

有参构造器存在风险

Open leobert-lan opened this issue 5 years ago • 2 comments

当生成对象时,利用 org.litepal.crud.DataHandler#findBestSuitConstructor 找到了一个最合适的构造器, 如果这个构造器是一个有参构造器,且有空指针风险时,例如:

public class Bar extends LitePalSupport {
    public int i;
    public String s;


    public Bar(BarArg arg) {
        i = arg.i;
        s = arg.s;
    }

    public Bar(@NonNull BarArg2 arg) {
        i = arg.i;
        s = arg.bean.s;
    }

    @Override
    public String toString() {
        return "Bar{" +
                "i=" + i +
                ", s='" + s + '\'' +
                '}';
    }
}
public class BarArg2 {
    public static class Bean {
        public String s;
    }
    public int i;
    public Bean bean;

}

会出现空指针,实际使用的构造函数为: Bar(BarArg2 arg), arg.bean.s;出现空指针。

虽然这是业务方的疏漏,但是有点不太友好,这要求业务方对类似BarArg2的类均需要提供合适的构造器,例如:

public class BarArg2 {
    public static class Bean {
        @NonNull
        public String s;

        public Bean(@NonNull String s) {
            this.s = s;
        }
    }
    public int i;
    @NonNull
    public Bean bean;

    public BarArg2(int i, @NonNull Bean bean) {
        this.i = i;
        this.bean = bean;
    }
}

或者

public class BarArg2 {
    public static class Bean {
        @NonNull
        public String s;

        public Bean(@NonNull String s) {
            this.s = s;
        }
    }
    public int i;
    @NonNull
    public Bean bean;

    public BarArg2(@NonNull Bean bean) {
        this.bean = bean;
    }
}

这两种情况下均不会出现空指针

或者要求Bar提供无参构造(这会被认为是最合适的构造器选择出来

建议直接使用Unsafe类生成实例

public class UnsafeHelper {
    private UnsafeHelper() {
    }

    private static final Object unsafe;

    private static final Method allocateInstance;

    static {

        final Class<?> unsafeClass;

        final Field theUnsafeField;

        try {
            unsafeClass = Class.forName("sun.misc.Unsafe");
        } catch (ClassNotFoundException e) {
            throw new UnsupportedOperationException("can't find sun.misc.Unsafe. " + e.getMessage(), e);
        }

        try {
            theUnsafeField = unsafeClass.getDeclaredField("theUnsafe");
        } catch (NoSuchFieldException e) {
            throw new UnsupportedOperationException("can't find the field theUnsafe in sun.misc.Unsafe." + e.getMessage(), e);
        }
        theUnsafeField.setAccessible(true);

        try {
            unsafe = theUnsafeField.get(null);
        } catch (IllegalAccessException e) {
            throw new UnsupportedOperationException("get Unsafe instance failed: " + e.getMessage(), e);
        }

        try {
            allocateInstance = unsafeClass.getMethod("allocateInstance", Class.class);
        } catch (NoSuchMethodException e) {
            throw new UnsupportedOperationException("can't find the method allocateInstance in sun.misc.Unsafe : " + e.getMessage(),
                    e);
        }

    }

    public static <T> T newInstance(Class<?> clazz) {
        try {
            return (T) allocateInstance.invoke(unsafe, clazz);
        } catch (Exception e) {
            throw new UnsupportedOperationException("create instance for " + clazz + " failed. " + e.getMessage(), e);
        }
    }
}

这样可以避免稀奇古怪的业务问题。就不提Pr了

leobert-lan avatar Nov 18 '20 07:11 leobert-lan

你的提议是有道理的,我也确实有计划在下个版本中使用Unsafe API来构建对象的实例,谢谢建议。

guolindev avatar Dec 13 '20 06:12 guolindev

刚才又反思了一下,上文体积的UnSafe助手类,还不适合直接放入litepal中使用,还不够全面完善,对于plain object类而言没多大问题,对于ArrayList等jdk中的类而言反而会出现问题。

感谢采纳。

过段时间不太忙的时候准备把项目里的litepal升级一波了。

leobert-lan avatar Dec 13 '20 06:12 leobert-lan