在 Gson 反序列化期间使用 Dagger 进行依赖注入
Dependency injection with Dagger during Gson deserialization
在我的应用程序中,我的对象不是我创建的,而是由 Gson 反序列化器创建的。这些对象需要引用我可以使用构造函数注入在其他任何地方提供的单例实例。
但是,在Gson调用的默认构造函数中访问相关组件是这样的
DaggerExampleComponent.builder().build().inject(this)
不会重用在其他地方注入的单例 - 据我了解,这是因为构建器实际上会创建一个 ExampleComponent
的新实例,它对现有实例一无所知。
我的解决方法是在 ExampleComponent
中保留静态 instance
字段以及 getter,但我想知道是否有最佳实践来实现另一种方法也是如此。
EDIT 正在使用 Android Room Persistence library 对从数据库检索的数据进行反序列化。将数据转换为自定义对象是通过在静态方法上使用 @TypeConverter
注释来实现的,当从数据库中检索元素时会隐式调用这些方法。这阻止了我在那里注入创建的对象 - 转换器是静态 class 中的静态方法,未实例化,因此我无法将 DaggerComponent 对象传递给它以用于注入创建的实例,如建议的那样下面是索尔本。
免责声明
我已经很久没有使用 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 class,您将确保每个创建的对象都具有相同的 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();
更新
由于新信息,我建议增强现有的 class,它用于 Android Room Persistence 库(class 包含注释方法)像这样:
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 class 看到正确的 DaggerAppComponent。
这可能仍然存在问题。这是一个race condition,关于静态变量的空状态。如果很快调用数据库,结果将是 NullpointerException,因为尚未设置静态字段。为了抵消这种情况,您可以使用信号量(或任何类似的东西)来创建某种屏障。对于信号量,这将包括一个具有 0 个许可的简单信号量。在使用变量之前调用 acquire 和 release 就可以了。设置变量后(在此 class 之外),对其调用 release 一次。
这不是一个好的解决方案(就软件设计而言),但它应该可以解决问题。
在我的应用程序中,我的对象不是我创建的,而是由 Gson 反序列化器创建的。这些对象需要引用我可以使用构造函数注入在其他任何地方提供的单例实例。
但是,在Gson调用的默认构造函数中访问相关组件是这样的
DaggerExampleComponent.builder().build().inject(this)
不会重用在其他地方注入的单例 - 据我了解,这是因为构建器实际上会创建一个 ExampleComponent
的新实例,它对现有实例一无所知。
我的解决方法是在 ExampleComponent
中保留静态 instance
字段以及 getter,但我想知道是否有最佳实践来实现另一种方法也是如此。
EDIT 正在使用 Android Room Persistence library 对从数据库检索的数据进行反序列化。将数据转换为自定义对象是通过在静态方法上使用 @TypeConverter
注释来实现的,当从数据库中检索元素时会隐式调用这些方法。这阻止了我在那里注入创建的对象 - 转换器是静态 class 中的静态方法,未实例化,因此我无法将 DaggerComponent 对象传递给它以用于注入创建的实例,如建议的那样下面是索尔本。
免责声明
我已经很久没有使用 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 class,您将确保每个创建的对象都具有相同的 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();
更新
由于新信息,我建议增强现有的 class,它用于 Android Room Persistence 库(class 包含注释方法)像这样:
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 class 看到正确的 DaggerAppComponent。
这可能仍然存在问题。这是一个race condition,关于静态变量的空状态。如果很快调用数据库,结果将是 NullpointerException,因为尚未设置静态字段。为了抵消这种情况,您可以使用信号量(或任何类似的东西)来创建某种屏障。对于信号量,这将包括一个具有 0 个许可的简单信号量。在使用变量之前调用 acquire 和 release 就可以了。设置变量后(在此 class 之外),对其调用 release 一次。
这不是一个好的解决方案(就软件设计而言),但它应该可以解决问题。