Dagger - 嵌套注入,有必要调用inject()吗?
Dagger - nested injections, is it necessary to call inject()?
我是 Dagger 的新手,一开始我遇到了一些问题。到目前为止,我的项目结构很简单。我的注入模块:
@Module(
injects = {GameBoardFragment.class, GameManager.class},
complete = false,
library = true
)
public class GameObjectsProviderModule {
private final Application mApplication;
public GameObjectsProviderModule(Application application){
this.mApplication = application;
}
@Provides
@Singleton
public GameManager provideGameManager(){
return new GameManager();
}
@Provides
public Board getBoard(){
return new Board();
}
@Provides @Singleton @ForApplication Context provideAppContext() {
return mApplication;
}
我的简化自定义应用 class 看起来像这样:
public class MyApp extends Application {
private static ObjectGraph mApplicationGraph;
@Override public void onCreate() {
super.onCreate();
mApplicationGraph = ObjectGraph.create(new GameObjectsProviderModule(this));
}
public static ObjectGraph getObjectGraph(){
return mApplicationGraph;
}
}
现在,我的片段看起来像这样:
public class GameBoardFragment extends Fragment {
@Inject
GameManager mGameManager;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
MyApp.getObjectGraph().inject(this);
View root = inflater.inflate(R.layout.fragment_game_board, container, false);
findViews(root);
confViews();
return root;
}
}
最后是我的 GameManager class
public class GameManager {
@Inject Board mBoard;
public GameManager(){
MyApp.getObjectGraph().inject(this);
}
}
安迪嘿,它有效!伟大的。但我的问题是,如果我注释掉这一行,为什么它不起作用:
MyApp.getObjectGraph().inject(this);
我们是否总是明确地调用 inject() 函数来使所有必要的注入发生在嵌套对象中?
看起来不是,如咖啡机示例所示:
https://github.com/square/dagger/tree/master/examples/simple/src/main/java/coffee
为什么我必须在 GameManager class 中调用 inject() 才能让它工作?
编辑:
构造函数注入方法工作得很好。
但是为了将来的使用我尝试了字段注入运行,但到目前为止我还没有成功。
我注释掉了模块中的两个@Provide 方法,并使我的 GameManager 看起来像这样:
@Singleton
public class GameManager {
@Inject Board mBoard;
@Inject
public GameManager(){
}
}
和董事会:
public class Board {
@Inject
public Board() {
}
}
但是mBoard没有被实例化。我会尝试更多,我想我会找到合适的解决方案。
您应该使用构造函数注入(例如 Thermosiphon),除非必要,否则应避免字段注入。例如,让您的 GameManager
将 Board
作为构造函数参数:
@Singleton
public class GameManager {
private final Board mBoard;
@Inject
public GameManager(final Board board){
mBoard = board;
}
}
Dagger 将使用此构造函数创建 GameManager
的实例(因此有 @Inject
注释),并注意到它需要一个 Board
实例。使用 ObjectGraph
,它将首先创建一个 Board
,然后使用该实例创建 GameManager
。如果这样做,您可以删除 @Provides GameManager
方法。
在您的例子中,您的模块中有一个 @Provides Board
方法。如果您将 @Inject
注释添加到 Board
构造函数,则可以从模块中删除此提供方法:
public class Board {
@Inject
public Board() {
}
}
如果你不想使用构造函数注入,问题是你告诉 Dagger 你想自己创建你的 GameManager
实例(因为你有 @Provides GameManager
方法)。如果你删除这个方法,让 Dagger 像上面那样为你创建它,但在构造函数中没有 Board
参数,Dagger 也会注意到 @Inject Board
字段并注入它。
最后的评论。删除 library = true
和 complete = false
语句!在这个例子中这些都不是必需的。只有当你真的知道自己在做什么时才添加它们。如果没有它们,Dagger 将创建编译时错误以通知您出现问题。如果您确实包含了它们,那么您就是在告诉 Dagger "Hey, I know what I'm doing, don't worry, it's all correct",而实际上并非如此。
编辑
引自 Dagger1 网站:
If your class has @Inject-annotated fields but no @Inject-annotated
constructor, Dagger will use a no-argument constructor if it exists.
Classes that lack @Inject annotations cannot be constructed by Dagger.
我不经常使用这种方法,所以我可能是错的。我认为这意味着您应该从构造函数中删除 @Inject
注释,如下所示:
@Singleton
public class GameManager {
@Inject Board mBoard;
public GameManager(){ // Or remove the constructor entirely since it's empty
}
}
由于 Board
字段上有一个 @Inject
注释,Dagger 将知道使用无参数构造函数。
我一直在努力解决同样的问题,因为大多数 dagger 示例都使用带有 Provides 的模块,我很难找到一个不使用 Provides 的完整示例。
我创建了这个。它使用字段注入(不是构造函数注入)并且在层次结构中工作得很好,不需要任何注入调用。我正在使用 Dagger 1.2.2.
Main.java
import javax.inject.*;
import dagger.*;
import dagger.ObjectGraph;
public class Main {
public static void main(String[] args) {
ObjectGraph objectGraph = ObjectGraph.create(new CarModule());
Car car = objectGraph.get(Car.class);
car.start();
}
}
CarModule.Java
import dagger.Module;
@Module(injects = Car.class)
public class CarModule {
}
Car.Java
import javax.inject.*;
public class Car {
@Inject public Engine engine;
@Inject Car() {
System.out.println("Car constructor");
}
public void start() {
engine.start();
}
}
Engine.Java
import javax.inject.*;
public class Engine {
@Inject WaterPump waterPump;
Engine() {
System.out.println("Engine Constructor");
}
void start() {
waterPump.run();
System.out.println("starting engine.");
}
}
WaterPump.Java
import javax.inject.*;
public class WaterPump {
@Inject WaterPump() {
System.out.println("WaterPump Constructor.");
}
public void run() {
System.out.println("WaterPump running.");
}
}
输出为:
Car constructor
Engine Constructor
WaterPump Constructor.
WaterPump running.
starting engine.
没有声明它的 CarModule
注入 Car.Class 这是行不通的。你得到:
Exception in thread "main" java.lang.IllegalArgumentException: No
inject registered for members/Car. You must explicitly add it to the
'injects' option in one of your modules.
但是请注意 CarModule
没有 @Provides
任何东西。使用对象图自动创建所有依赖项的是 dagger。
另请注意,如果 class 中有 @Inject
字段,则不必在默认构造函数上放置 @Inject
注释。对于 Car
class 我在构造函数和字段上都使用了它,而在 Engine
class 中我只将它用于字段而不是构造函数并且它工作正常记录在案。
我是 Dagger 的新手,一开始我遇到了一些问题。到目前为止,我的项目结构很简单。我的注入模块:
@Module(
injects = {GameBoardFragment.class, GameManager.class},
complete = false,
library = true
)
public class GameObjectsProviderModule {
private final Application mApplication;
public GameObjectsProviderModule(Application application){
this.mApplication = application;
}
@Provides
@Singleton
public GameManager provideGameManager(){
return new GameManager();
}
@Provides
public Board getBoard(){
return new Board();
}
@Provides @Singleton @ForApplication Context provideAppContext() {
return mApplication;
}
我的简化自定义应用 class 看起来像这样:
public class MyApp extends Application {
private static ObjectGraph mApplicationGraph;
@Override public void onCreate() {
super.onCreate();
mApplicationGraph = ObjectGraph.create(new GameObjectsProviderModule(this));
}
public static ObjectGraph getObjectGraph(){
return mApplicationGraph;
}
}
现在,我的片段看起来像这样:
public class GameBoardFragment extends Fragment {
@Inject
GameManager mGameManager;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
MyApp.getObjectGraph().inject(this);
View root = inflater.inflate(R.layout.fragment_game_board, container, false);
findViews(root);
confViews();
return root;
}
}
最后是我的 GameManager class
public class GameManager {
@Inject Board mBoard;
public GameManager(){
MyApp.getObjectGraph().inject(this);
}
}
安迪嘿,它有效!伟大的。但我的问题是,如果我注释掉这一行,为什么它不起作用:
MyApp.getObjectGraph().inject(this);
我们是否总是明确地调用 inject() 函数来使所有必要的注入发生在嵌套对象中? 看起来不是,如咖啡机示例所示:
https://github.com/square/dagger/tree/master/examples/simple/src/main/java/coffee
为什么我必须在 GameManager class 中调用 inject() 才能让它工作?
编辑:
构造函数注入方法工作得很好。
但是为了将来的使用我尝试了字段注入运行,但到目前为止我还没有成功。
我注释掉了模块中的两个@Provide 方法,并使我的 GameManager 看起来像这样:
@Singleton
public class GameManager {
@Inject Board mBoard;
@Inject
public GameManager(){
}
}
和董事会:
public class Board {
@Inject
public Board() {
}
}
但是mBoard没有被实例化。我会尝试更多,我想我会找到合适的解决方案。
您应该使用构造函数注入(例如 Thermosiphon),除非必要,否则应避免字段注入。例如,让您的 GameManager
将 Board
作为构造函数参数:
@Singleton
public class GameManager {
private final Board mBoard;
@Inject
public GameManager(final Board board){
mBoard = board;
}
}
Dagger 将使用此构造函数创建 GameManager
的实例(因此有 @Inject
注释),并注意到它需要一个 Board
实例。使用 ObjectGraph
,它将首先创建一个 Board
,然后使用该实例创建 GameManager
。如果这样做,您可以删除 @Provides GameManager
方法。
在您的例子中,您的模块中有一个 @Provides Board
方法。如果您将 @Inject
注释添加到 Board
构造函数,则可以从模块中删除此提供方法:
public class Board {
@Inject
public Board() {
}
}
如果你不想使用构造函数注入,问题是你告诉 Dagger 你想自己创建你的 GameManager
实例(因为你有 @Provides GameManager
方法)。如果你删除这个方法,让 Dagger 像上面那样为你创建它,但在构造函数中没有 Board
参数,Dagger 也会注意到 @Inject Board
字段并注入它。
最后的评论。删除 library = true
和 complete = false
语句!在这个例子中这些都不是必需的。只有当你真的知道自己在做什么时才添加它们。如果没有它们,Dagger 将创建编译时错误以通知您出现问题。如果您确实包含了它们,那么您就是在告诉 Dagger "Hey, I know what I'm doing, don't worry, it's all correct",而实际上并非如此。
编辑
引自 Dagger1 网站:
If your class has @Inject-annotated fields but no @Inject-annotated constructor, Dagger will use a no-argument constructor if it exists. Classes that lack @Inject annotations cannot be constructed by Dagger.
我不经常使用这种方法,所以我可能是错的。我认为这意味着您应该从构造函数中删除 @Inject
注释,如下所示:
@Singleton
public class GameManager {
@Inject Board mBoard;
public GameManager(){ // Or remove the constructor entirely since it's empty
}
}
由于 Board
字段上有一个 @Inject
注释,Dagger 将知道使用无参数构造函数。
我一直在努力解决同样的问题,因为大多数 dagger 示例都使用带有 Provides 的模块,我很难找到一个不使用 Provides 的完整示例。
我创建了这个。它使用字段注入(不是构造函数注入)并且在层次结构中工作得很好,不需要任何注入调用。我正在使用 Dagger 1.2.2.
Main.java
import javax.inject.*;
import dagger.*;
import dagger.ObjectGraph;
public class Main {
public static void main(String[] args) {
ObjectGraph objectGraph = ObjectGraph.create(new CarModule());
Car car = objectGraph.get(Car.class);
car.start();
}
}
CarModule.Java
import dagger.Module;
@Module(injects = Car.class)
public class CarModule {
}
Car.Java
import javax.inject.*;
public class Car {
@Inject public Engine engine;
@Inject Car() {
System.out.println("Car constructor");
}
public void start() {
engine.start();
}
}
Engine.Java
import javax.inject.*;
public class Engine {
@Inject WaterPump waterPump;
Engine() {
System.out.println("Engine Constructor");
}
void start() {
waterPump.run();
System.out.println("starting engine.");
}
}
WaterPump.Java
import javax.inject.*;
public class WaterPump {
@Inject WaterPump() {
System.out.println("WaterPump Constructor.");
}
public void run() {
System.out.println("WaterPump running.");
}
}
输出为:
Car constructor
Engine Constructor
WaterPump Constructor.
WaterPump running.
starting engine.
没有声明它的 CarModule
注入 Car.Class 这是行不通的。你得到:
Exception in thread "main" java.lang.IllegalArgumentException: No inject registered for members/Car. You must explicitly add it to the 'injects' option in one of your modules.
但是请注意 CarModule
没有 @Provides
任何东西。使用对象图自动创建所有依赖项的是 dagger。
另请注意,如果 class 中有 @Inject
字段,则不必在默认构造函数上放置 @Inject
注释。对于 Car
class 我在构造函数和字段上都使用了它,而在 Engine
class 中我只将它用于字段而不是构造函数并且它工作正常记录在案。