已经发布了几个关于依赖注入的具体问题,例如何时使用它以及它有什么框架,
什么是依赖注入,何时/为什么应该或不应该使用它?
已经发布了几个关于依赖注入的具体问题,例如何时使用它以及它有什么框架,
什么是依赖注入,何时/为什么应该或不应该使用它?
当前回答
依赖注入(DI)是设计模式中的一种,它使用了OOP的基本特性——一个对象与另一个对象之间的关系。虽然继承继承一个对象以实现更复杂和更具体的另一个对象,但关系或关联只需使用属性从一个对象创建指向另一对象的指针。DI的功能与OOP的其他特性相结合,如接口和隐藏代码。假设图书馆里有一个客户(订阅者),为了简单起见,他只能借一本书。
书本界面:
package com.deepam.hidden;
public interface BookInterface {
public BookInterface setHeight(int height);
public BookInterface setPages(int pages);
public int getHeight();
public int getPages();
public String toString();
}
接下来我们可以有很多种书;其中一种类型是虚构:
package com.deepam.hidden;
public class FictionBook implements BookInterface {
int height = 0; // height in cm
int pages = 0; // number of pages
/** constructor */
public FictionBook() {
// TODO Auto-generated constructor stub
}
@Override
public FictionBook setHeight(int height) {
this.height = height;
return this;
}
@Override
public FictionBook setPages(int pages) {
this.pages = pages;
return this;
}
@Override
public int getHeight() {
// TODO Auto-generated method stub
return height;
}
@Override
public int getPages() {
// TODO Auto-generated method stub
return pages;
}
@Override
public String toString(){
return ("height: " + height + ", " + "pages: " + pages);
}
}
现在,用户可以与图书建立关联:
package com.deepam.hidden;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Subscriber {
BookInterface book;
/** constructor*/
public Subscriber() {
// TODO Auto-generated constructor stub
}
// injection I
public void setBook(BookInterface book) {
this.book = book;
}
// injection II
public BookInterface setBook(String bookName) {
try {
Class<?> cl = Class.forName(bookName);
Constructor<?> constructor = cl.getConstructor(); // use it for parameters in constructor
BookInterface book = (BookInterface) constructor.newInstance();
//book = (BookInterface) Class.forName(bookName).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return book;
}
public BookInterface getBook() {
return book;
}
public static void main(String[] args) {
}
}
这三个类都可以隐藏起来,以便实现自己的功能。现在我们可以将此代码用于DI:
package com.deepam.implement;
import com.deepam.hidden.Subscriber;
import com.deepam.hidden.FictionBook;
public class CallHiddenImplBook {
public CallHiddenImplBook() {
// TODO Auto-generated constructor stub
}
public void doIt() {
Subscriber ab = new Subscriber();
// injection I
FictionBook bookI = new FictionBook();
bookI.setHeight(30); // cm
bookI.setPages(250);
ab.setBook(bookI); // inject
System.out.println("injection I " + ab.getBook().toString());
// injection II
FictionBook bookII = ((FictionBook) ab.setBook("com.deepam.hidden.FictionBook")).setHeight(5).setPages(108); // inject and set
System.out.println("injection II " + ab.getBook().toString());
}
public static void main(String[] args) {
CallHiddenImplBook kh = new CallHiddenImplBook();
kh.doIt();
}
}
如何使用依赖注入有许多不同的方法。可以将它与Singleton等结合起来,但基本上它只是通过在另一个对象内创建对象类型的属性来实现的关联。它的有用性是唯一的,也是唯一的特点,我们应该反复编写的代码总是为我们准备好并做好准备。这就是为什么DI如此紧密地与控制反转(IoC)绑定,这意味着我们的程序将控制传递给另一个正在运行的模块,该模块将bean注入到我们的代码中。(可以被注入的每个对象都可以被签名或被认为是一个Bean。)例如,在Spring中,它是通过创建和初始化ApplicationContext容器来完成的,这对我们来说很有用。我们只需在代码中创建Context并调用初始化Bean。此时注射已自动完成。
其他回答
公认的答案是一个好答案——但我想补充一点,DI非常像代码中避免硬编码常量的经典做法。
当您使用诸如数据库名称之类的常量时,您可以将其从代码内部快速移动到某个配置文件,并将包含该值的变量传递到需要它的位置。这样做的原因是,这些常量通常比代码的其他部分更频繁地更改。例如,如果您想在测试数据库中测试代码。
在面向对象编程的世界中,DI与此类似。那里的值而不是常量文字是整个对象-但是将创建它们的代码从类代码中移出的原因是相似的-对象的更改比使用它们的代码更频繁。一个重要的情况是需要进行这样的改变,那就是测试。
DI是真实对象之间实际交互的方式,而不需要一个对象负责另一个对象的存在。应平等对待对象。它们都是对象。任何人都不应该表现得像一个创造者。这就是你如何公正对待你的目标。
简单示例:
如果你需要医生,你只需去找一位(现有的)医生。你不会考虑从头开始创建一个医生来帮助你。他已经存在,他可能为你或其他对象服务。无论你(一个物体)是否需要他,他都有权存在,因为他的目的是为一个或多个物体服务。决定他的存在的是全能的上帝,而不是自然选择。因此,DI的一个优点是避免在整个宇宙(即应用程序)的生命周期中创建无用的冗余对象。
摘自《扎实的Java开发人员:Java 7和多语言编程的关键技术》一书
DI是IoC的一种特殊形式,因此查找依赖项的过程是不受当前执行代码的直接控制。
依赖注入(DI)是设计模式中的一种,它使用了OOP的基本特性——一个对象与另一个对象之间的关系。虽然继承继承一个对象以实现更复杂和更具体的另一个对象,但关系或关联只需使用属性从一个对象创建指向另一对象的指针。DI的功能与OOP的其他特性相结合,如接口和隐藏代码。假设图书馆里有一个客户(订阅者),为了简单起见,他只能借一本书。
书本界面:
package com.deepam.hidden;
public interface BookInterface {
public BookInterface setHeight(int height);
public BookInterface setPages(int pages);
public int getHeight();
public int getPages();
public String toString();
}
接下来我们可以有很多种书;其中一种类型是虚构:
package com.deepam.hidden;
public class FictionBook implements BookInterface {
int height = 0; // height in cm
int pages = 0; // number of pages
/** constructor */
public FictionBook() {
// TODO Auto-generated constructor stub
}
@Override
public FictionBook setHeight(int height) {
this.height = height;
return this;
}
@Override
public FictionBook setPages(int pages) {
this.pages = pages;
return this;
}
@Override
public int getHeight() {
// TODO Auto-generated method stub
return height;
}
@Override
public int getPages() {
// TODO Auto-generated method stub
return pages;
}
@Override
public String toString(){
return ("height: " + height + ", " + "pages: " + pages);
}
}
现在,用户可以与图书建立关联:
package com.deepam.hidden;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Subscriber {
BookInterface book;
/** constructor*/
public Subscriber() {
// TODO Auto-generated constructor stub
}
// injection I
public void setBook(BookInterface book) {
this.book = book;
}
// injection II
public BookInterface setBook(String bookName) {
try {
Class<?> cl = Class.forName(bookName);
Constructor<?> constructor = cl.getConstructor(); // use it for parameters in constructor
BookInterface book = (BookInterface) constructor.newInstance();
//book = (BookInterface) Class.forName(bookName).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return book;
}
public BookInterface getBook() {
return book;
}
public static void main(String[] args) {
}
}
这三个类都可以隐藏起来,以便实现自己的功能。现在我们可以将此代码用于DI:
package com.deepam.implement;
import com.deepam.hidden.Subscriber;
import com.deepam.hidden.FictionBook;
public class CallHiddenImplBook {
public CallHiddenImplBook() {
// TODO Auto-generated constructor stub
}
public void doIt() {
Subscriber ab = new Subscriber();
// injection I
FictionBook bookI = new FictionBook();
bookI.setHeight(30); // cm
bookI.setPages(250);
ab.setBook(bookI); // inject
System.out.println("injection I " + ab.getBook().toString());
// injection II
FictionBook bookII = ((FictionBook) ab.setBook("com.deepam.hidden.FictionBook")).setHeight(5).setPages(108); // inject and set
System.out.println("injection II " + ab.getBook().toString());
}
public static void main(String[] args) {
CallHiddenImplBook kh = new CallHiddenImplBook();
kh.doIt();
}
}
如何使用依赖注入有许多不同的方法。可以将它与Singleton等结合起来,但基本上它只是通过在另一个对象内创建对象类型的属性来实现的关联。它的有用性是唯一的,也是唯一的特点,我们应该反复编写的代码总是为我们准备好并做好准备。这就是为什么DI如此紧密地与控制反转(IoC)绑定,这意味着我们的程序将控制传递给另一个正在运行的模块,该模块将bean注入到我们的代码中。(可以被注入的每个对象都可以被签名或被认为是一个Bean。)例如,在Spring中,它是通过创建和初始化ApplicationContext容器来完成的,这对我们来说很有用。我们只需在代码中创建Context并调用初始化Bean。此时注射已自动完成。
到目前为止,我找到的最好的定义是詹姆斯·肖尔的定义:
“依赖注入”是25美元5美分概念的术语。[...]依赖注入意味着对象的实例变量。[...].
马丁·福勒的一篇文章可能也很有用。
依赖注入基本上是提供对象所需的对象(其依赖项),而不是让它自己构造它们。这是一种非常有用的测试技术,因为它允许对依赖项进行嘲笑或清除。
依赖关系可以通过多种方式注入到对象中(例如构造函数注入或setter注入)。甚至可以使用专门的依赖注入框架(例如Spring)来实现这一点,但它们肯定不是必需的。您不需要这些框架进行依赖注入。显式实例化和传递对象(依赖项)与框架注入一样好。