依赖注入DI指的是Spring IoC容器对对象的依赖对象的处理过程,对象的依赖对象,说起来比拟拗口,其实指的就是:如果一个对象A的某一属性为对象B,则对象B就是对象A的依赖对象,对象A创立的过程中也要创建对象B并注入到对象A,之后对象A能力失常工作。

Spring IoC可通过如下三种形式注入依赖对象:

  1. 结构器参数
  2. 工厂办法参数
  3. Setter办法

Spring DI因而也分为两种:基于结构器的DI和基于Setter的DI。

咱们须要用代码来阐明Spring两种形式DI的区别。

代码筹备

创立一个Spring我的项目,Spring的任何版本都能够,在pom中退出依赖即可:

<dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-context</artifactId>            <version>5.2.8.RELEASE</version>        </dependency>        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-beans</artifactId>            <version>5.2.8.RELEASE</version>        </dependency>

创立两个根底的接口,以及他们的实现类,不须要蕴含任何业务逻辑,咱们只是为了阐明依赖注入的概念。

public interface IDependencyA {    public void test();}public interface IDependencyB {    public void testB();}@Servicepublic class DependencyA implements IDependencyA {    @Override    public void test(){        System.out.println("I am DependencyA test...");    }}@Servicepublic class DependencyB implements IDependencyB{    private IDependencyA dependencyA;    public static void main(String[] args) {        System.out.println("I am a new comer...hello world ");    }    @Override    public void testB(){        System.out.print("This is DependencyB's test and ...");        dependencyA.test();    }    public DependencyB(){        //this.dependencyA =springTest;        System.out.println("create DependencyB...");    }}

其中类DependencyB蕴含了一个类型为IDependencyA的属性dependencyA,因而,DependencyB依赖于对象dependencyA。

在增加一个配置类,配置类啥也不干,仅指定包扫描门路,包扫描门路就是为了通知Spring从什么中央加载bean:

@Configuration@ComponentScan(value={"SpringTest"})public class MyConfiguration {}

还要简略约定一下,目前来看Spring我的项目应用注解的状况应该会多于应用xml配置的状况,因而咱们的文章次要基于注解而非基于xml配置文件。除非某些非凡案例只能应用xml、无奈应用注解代替的状况下,才给出xml文件的配置。

最初削减加一个启动类:

public class App {    public static void main(String[] args) {        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfiguration.class);        System.out.println("Now start to getbean...");        DependencyB dependencyB = applicationContext.getBean(DependencyB.class);        dependencyB.testB();    }}

上面咱们就用这个简略的例子来阐明Spring不同的DI形式。

基于结构器的DI

第一步,咱们啥也不干,间接运行启动类:

create DependencyB...Now start to getbean...This is DependencyB's test and ...Exception in thread "main" java.lang.NullPointerException    at SpringTest.DependencyB.testB(DependencyB.java:14)    at SpringTest.App.main(App.java:11)

DependencyB的依赖对象dependencyA没有创立,所以报了空指针异样。

咱们并没有给Spring IoC任何DI的批示,所以DI没有工作,dependencyA没有注入到dependencyB中。

基于结构器的DI蕴含两种形式:

  1. 结构器参数
  2. 工厂办法参数

先来看第一种,通过构造方法参数注入,批改DependencyB的构造方法,减少一个IDependencyA的参数:

    public DependencyB(IDependencyA dependencyA){        this.dependencyA =dependencyA;        System.out.println("create DependencyB...");    }

再次运行启动类:

create DependencyB...Now start to getbean...This is DependencyB's test and ...I am DependencyA test...

发现dependencyA曾经通过结构器参数胜利注入到dependencyB中。

另外一种基于结构器参数的DI是通过工厂办法,这种状况下应用xml配置会更加不便:

<!--工厂办法注入-->    <bean id="dependencyB" class="springTest.DependencyB" factory-method="factoryMethod"></bean>

指定factory-method,Spring IoC会调用该办法创立bean,能够通过结构器参数constructor-arg指定依赖对象实现注入。

DependencyB须要减少动态工厂办法factoryMethod:

   public static DependencyB factoryMethod(IDependencyA dependencyA){        DependencyB dependencyB = new DependencyB();        dependencyB.dependencyA =dependencyA;        System.out.println("create DependencyB...");        return dependencyB;    }

基于Setter的DI

Spring IoC通过调用对象的setter办法注入依赖对象的形式。

咱们去掉DepedencyB的构造方法的参数,增加setDepencyB办法:

@Componentpublic class DependencyB implements IDependencyB{    private IDependencyA dependencyA;    public static void main(String[] args) {        System.out.println("I am a new comer...hello world ");    }    @Override    public void testB(){        System.out.print("This is DependencyB's test and ...");        dependencyA.test();    }    public void setDependencyA(IDependencyA dependencyA){        System.out.println("here you call set method...");        this.dependencyA=dependencyA;    }    public DependencyB(){    }}

而后间接运行启动类:

Now start to getbean...This is DependencyB's test and ...Exception in thread "main" java.lang.NullPointerException    at springTest.DependencyB.testB(DependencyB.java:14)    at springTest.App.main(App.java:11)

显然是不行的,因为咱们并没有指定DependencyB的属性dependencyA须要注入,此时Spring IoC并不知道须要为DependencyB注入什么属性,所以咱们须要显示指定一下(前面咱们会晓得Spring其实有主动拆卸的概念,不指定也能够)。

一种形式是通过注解指定,setDependencyA办法增加@Autowired注解:

    @Autowired    public void setDependencyA(IDependencyA dependencyA){        System.out.println("here you call set method...");        this.dependencyA=dependencyA;    }

再次运行启动类,就OK了。@Autowired注解咱们暂不剖析,前面学习主动拆卸的时候会具体说。

另外一种,当然能够通过xml指定,所以咱们须要在我的项目的resource目录下创立一个xml文件mySpring.xml:

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">    <bean id="dependencyB" class="springTest.DependencyB">        <property name="dependencyA" ref="dependencyA"/>    </bean>    <bean id="dependencyA" class="springTest.DependencyA">    </bean></beans>

在xml文件中通过property标签,以及ref指定了类之间的依赖关系。

咱们还须要从新创立一个基于xml的启动类:

public class AppByXML {    public static void main(String[] args) {        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("mySpring.xml");        System.out.println("Now start to getbean...");        DependencyB dependencyB = applicationContext.getBean(DependencyB.class);        dependencyB.testB();    }}

运行启动类:

here you call set method...Now start to getbean...This is DependencyB's test and ...I am DependencyA test...

dependencyA被胜利注入了dependencyB中了,从log中能够清晰的看到Spring是通过调用setter办法实现注入的。

这种状况下,setter办法必须是public的,否则Spring执行注入的时候会因为找不到setter办法而抛异样。

基于结构器的 or 基于Setter的 DI?

既然有基于结构器的和基于Setter的DI,那咱们在我的项目中应该怎么选?

Spring官网说,Spring Team个别举荐应用基于结构器的DI,因为基于结构器的DI有个益处是当你创建对象后,依赖对象也一起筹备好了,能够防止后续业务解决的谬误。

然而对于一个绝对简单的类来说,依赖对象一大堆,结构器就会很俊俏,所以,其实我的项目中还是基于Setter、或者主动拆卸更好用。

另外,基于结构器和基于Setter的DI是能够联合应用的,对于强制要求的依赖对象能够放在结构器中,能够在编译阶段就发现依赖对象没有筹备好的问题,防止运行期谬误。

另外一种依赖 depend on

除了咱们下面所说的,一个对象作为另外一个对象的属性这种类型的依赖之外,还有另外一种依赖:一个对象的运行是依赖于另外一个对象的然而另外一个对象并不是以后对象的属性。

这种状况能够应用@DependsOn注解或xml的 depend on标签:

<bean id="beanOne" class="ExampleBean" depends-on="manager"/><bean id="manager" class="ManagerBean" />

配置很麻烦

你不感觉DI的配置、尤其是xml形式的配置,很麻烦吗?一个一个的类、其中的一个一个依赖都须要通过xml进行配置,尤其是当你须要批改一个类、引入新的依赖对象或者去掉一个依赖对象的时候,你不得不同时关上xml配置文件做相应的批改。

的确十分麻烦,然而Spring为你想到了一个很好的解决方法:主动拆卸。

下一篇文章学习。

上一篇 Spring FrameWork从入门到NB - Ioc