首页 > 解决方案 > 在 Gson 反序列化期间使用 Dagger 进行依赖注入

问题描述

在我的应用程序中,我的对象不是由我创建的,而是由 Gson 反序列化器创建的。这些对象需要引用我可以使用构造函数注入提供的其他任何地方的单例实例。

但是,像这样在 Gson 调用的默认构造函数中访问相关组件

DaggerExampleComponent.builder().build().inject(this)

不会重用在其他地方注入的单例 - 据我了解,这是因为构建器实际上会创建一个新实例,ExampleComponent该实例对现有实例一无所知。

我的解决方法是在内部保留一个静态instance字段ExampleComponent和一个吸气剂,但我想知道是否存在如何使用另一种方法实现相同目标的最佳实践。

编辑使用Android Room Persistence library对从数据库中检索到的数据进行反序列化。将数据转换为自定义对象是通过使用@TypeConverter静态方法上的注释来实现的,这些方法在从数据库中检索元素时被隐式调用。这阻止了我在那里注入创建的对象 - 转换器是未实例化的静态类中的静态方法,因此我不能将 DaggerComponent 对象传递给它以用于注入创建的实例,如下面的 Thorben 建议的那样。

标签: javadependency-injectiongsondagger

解决方案


免责声明

我已经很久没有和 Dagger 合作了。采取以下解决方案与一粒盐!该解决方案在本地对我有用。

没有 DaggerExampleComponent

您的问题的一个答案可能是,使用自定义JsonDeserializer接口实现,它将您要注入的对象的实例作为构造函数参数。

您可以编写自己的反序列化器,将单例实例注入反序列化对象,如下所示:

class MyJsonDeserializer implements JsonDeserializer<MyObject> {

    private final MyComponent singleton;

    public MyJsonDeserializer(MyComponent component) {
        this.singleton = component;
    }

    public MyObject deserialize(JsonElement json, Type tye, JsonDeserializationContext context) throws JsonParseException {
        // you could here parse some arguments
        return new MyObject(singleton);
    }
}

你会像这样注册它:

MyComponent component = ...
Gson gson = new GsonBuilder().registerTypeAdapter(MyObject.class, new MyJsonDeserializer(component)).create();

如果您要注入 MyComponent 类,您将确保每个创建的对象都具有相同的 MyComponent 对象实例。

我个人更喜欢这个解决方案,不要混淆 Dagger 和 Gson。

使用匕首

您也可以更改代码以在所述 JsonDeserializer 中使用 DaggerAppComponent,如下所示:

class MyJsonDeserializer implements JsonDeserializer<MyObject> {

    private final DaggerAppComponent singletonProvider;

    public MyJsonDeserializer(DaggerAppComponent componentProvdider) {
        this.singletonProvider = componentProvider;
    }

    public MyObject deserialize(JsonElement json, Type tye, JsonDeserializationContext context) throws JsonParseException {
        // you could here parse some arguments
        MyObject object =  ...
        singletonProvider.inject(object);
        return object;
    }
}

并像这样更改注册:

DaggerAppComponent componentBuilder = DaggerExampleComponent.builder().build();
Gson gson = new GsonBuilder().registerTypeAdapter(MyObject.class, new MyJsonDeserializer(componentBuilder)).create();

更新

由于新信息,我建议增强现有类,该类用于 Android Room Persistence 库(包含注释方法的类),如下所示:

class Convert {
    static DaggerAppComponent singletonProvider;
    @TypeConverter
    public static MyObject convert(String arg) {
        Gson gson = new GsonBuilder().registerTypeAdapter(MyObject.class, new MyJsonDeserializer(componentBuilder)).create();
        return gson.fromJson(arg, MyObject.class);
    }

    @TypeConverter
    public static String fromArrayLisr(MyObject object) {
        Gson gson = new Gson();
        String json = gson.toJson(v);
        return json;
    }
}

我从thetechguru 那里得到了一些灵​​感。此外,这假定与上述相同的 JsonDeserializer。

由于我不知道实际参数,因此我假设 String 作为此类型转换器的参数。在那里插入你对应的类型。

为了有效地使用它,在代码中的某个地方(在完成任何与数据库相关的东西之前),应该调用它:

Convert.singletonProvider = DaggerExampleComponent.builder().build();

这将允许 Convert 类看到正确的 DaggerAppComponent。

这可能仍然存在问题。这是一个竞争条件,涉及静态变量的空状态。如果很快调用数据库,结果将是 NullpointerException,因为尚未设置静态字段。为了解决这个问题,您可以使用信号量(或任何类似的东西)来创建某种障碍。使用信号量,这将包括一个具有 0 个许可的简单信号量。在使用变量之前调用acquire和release就可以了。设置变量后(在此类之外),对其调用 release 一次。

这不是一个好的解决方案(就软件设计而言),但它应该可以解决问题。


推荐阅读