深入解析Java的Spring框架中bean的依赖注入

每一个基于java的应用程序都有一个共同工作来展示给用户看到的内容作为工作的应用几个对象。当编写一个复杂的Java应用程序,应用程序类应该尽可能独立其他Java类来增加重复使用这些类,并独立于其他类别的测试它们,而这样做单元测试的可能性。依赖注入(或有时称为布线)有助于粘合这些类在一起,同时保持他们的独立。

考虑有其中有一个文本编辑器组件的应用程序,要提供拼写检查。标准的代码将看起来像这样:

public class TextEditor {
  private SpellChecker spellChecker;
  public TextEditor() {
   spellChecker = new SpellChecker();
  }
}

我们在这里所做的就是创建文本编辑和拼写检查之间的依赖性。在控制方案中的反转,我们反而会做这样的事情:

public class TextEditor {
  private SpellChecker spellChecker;
  public TextEditor(SpellChecker spellChecker) {
   this.spellChecker = spellChecker;
  }
}

在这里,文本编辑不应该担心拼写检查落实。拼写检查器将独立实施,将提供给文本编辑在文本编辑实例化的时候,这整个过程是由Spring框架的控制。

在这里,我们已经删除从文本编辑的全面控制,并保持它在其他地方(即XML配置文件)和依赖性(即类拼写检查)被注入到类文本编辑通过类构造函数。因此,流程控制已经“倒”通过依赖注入(DI),因为已经有效地委派依赖一些外部系统。

依赖注入的第二种方法是通过文本编辑类,我们将创建拼写检查实例的setter方法​​,该实例将被用来调用setter方法​​来初始化文本编辑的属性。

因此,DI主要有两种变体和下面的两个子章将涵盖两者结合实例:

基于构造函数的依赖注入
当容器调用类的构造函数有多个参数,每个代表在其他类中的构造函数依赖关系为基础的DI来完成。

例子:
下面的例子显示了一个类文本编辑TextEditor 只能是依赖注入与构造函数注入。

我们使用Eclipse IDE,然后按照下面的步骤来创建一个Spring应用程序:

这里是TextEditor.java文件的内容:

package com.yiibai;

public class TextEditor {
  private SpellChecker spellChecker;

  public TextEditor(SpellChecker spellChecker) {
   System.out.println("Inside TextEditor constructor." );
   this.spellChecker = spellChecker;
  }
  public void spellCheck() {
   spellChecker.checkSpelling();
  }
}

下面是另外一个相关的类文件SpellChecker.java内容:

package com.yiibai;

public class SpellChecker {
  public SpellChecker(){
   System.out.println("Inside SpellChecker constructor." );
  }

  public void checkSpelling() {
   System.out.println("Inside checkSpelling." );
  }

}

以下是MainApp.java文件的内容:

package com.yiibai;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainApp {
  public static void main(String[] args) {
   ApplicationContext context =
       new ClassPathXmlApplicationContext("Beans.xml");

   TextEditor te = (TextEditor) context.getBean("textEditor");

   te.spellCheck();
  }
}

以下是配置文件beans.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-3.0.xsd">

  <!-- Definition for textEditor bean -->
  <bean id="textEditor" class="com.yiibai.TextEditor">
   <constructor-arg ref="spellChecker"/>
  </bean>

  <!-- Definition for spellChecker bean -->
  <bean id="spellChecker" class="com.yiibai.SpellChecker">
  </bean>

</beans>

创建源代码和bean配置文件完成后,让我们运行应用程序。如果一切顺利将打印以下信息:

Inside SpellChecker constructor.
Inside TextEditor constructor.
Inside checkSpelling.

构造函数的参数解析:
可能有歧义存在,而将参数传递给构造函数的情况下有一个以上的参数。要解决这种不确定性,其中的构造器参数在一个bean定义中定义的顺序就是这些参数提供给适当的构造函数的顺序。请考虑下面的类:

package x.y;

public class Foo {
  public Foo(Bar bar, Baz baz) {
   // ...
  }
}

下面的配置工作正常:

<beans>
  <bean id="foo" class="x.y.Foo">
   <constructor-arg ref="bar"/>
   <constructor-arg ref="baz"/>
  </bean>

  <bean id="bar" class="x.y.Bar"/>
  <bean id="baz" class="x.y.Baz"/>
</beans>

让我们检查一个更多情况下我们通过不同类型的构造函数。请考虑下面的类:

package x.y;

public class Foo {
  public Foo(int year, String name) {
   // ...
  }
}

容器也可以使用类型匹配与简单类型,如果你明确地指定使用type属性的构造函数的参数类型。例如:

<beans>

  <bean id="exampleBean" class="examples.ExampleBean">
   <constructor-arg type="int" value="2001"/>
   <constructor-arg type="java.lang.String" value="Zara"/>
  </bean>

</beans>

最后,并通过构造函数参数的最佳方法,使用索引属性来显式地指定一个构造器参数的索引。这里的索引是从0开始。例如:

<beans>

  <bean id="exampleBean" class="examples.ExampleBean">
   <constructor-arg index="0" value="2001"/>
   <constructor-arg index="1" value="Zara"/>
  </bean>

</beans>

最后需要说明的,如果你传递一个引用到一个对象,需要使用<constructor-arg>标签的ref属性,如果是直接传递一个值,那么应该使用value属性。

基于setter方法的依赖注入
基于setter DI由容器调用setter方法​​对bean调用无参构造器或无参static工厂方法实例化bean之后完成。
这里是TextEditor.java文件的内容:

package com.yiibai;

public class TextEditor {
  private SpellChecker spellChecker;

  // a setter method to inject the dependency.
  public void setSpellChecker(SpellChecker spellChecker) {
   System.out.println("Inside setSpellChecker." );
   this.spellChecker = spellChecker;
  }
  // a getter method to return spellChecker
  public SpellChecker getSpellChecker() {
   return spellChecker;
  }

  public void spellCheck() {
   spellChecker.checkSpelling();
  }
}

在这里,需要检查setter方法​​的命名约定。设置我们使用setSpellChecker()方法,这是非常类似于Java POJO类的变量的拼写检查器。让我们创造另一个相关的类文件SpellChecker.java,内容如下:

package com.yiibai;

public class SpellChecker {
  public SpellChecker(){
   System.out.println("Inside SpellChecker constructor." );
  }

  public void checkSpelling() {
   System.out.println("Inside checkSpelling." );
  }

}

以下是MainApp.java文件的内容:

package com.yiibai;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainApp {
  public static void main(String[] args) {
   ApplicationContext context =
       new ClassPathXmlApplicationContext("Beans.xml");

   TextEditor te = (TextEditor) context.getBean("textEditor");

   te.spellCheck();
  }
}

以下是配置文件beans.xml文件里面有配置为基于setter方法​​注入:

<?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-3.0.xsd">

  <!-- Definition for textEditor bean -->
  <bean id="textEditor" class="com.yiibai.TextEditor">
   <property name="spellChecker" ref="spellChecker"/>
  </bean>

  <!-- Definition for spellChecker bean -->
  <bean id="spellChecker" class="com.yiibai.SpellChecker">
  </bean>

</beans>

应该注意在基于构造函数注入和setter注入定义beans.xml文件的差异。唯一的区别是,我们已经使用<constructor-arg>标签为基于构造函数的注入和的<property>标签为基于setter注入的<bean>元素内。

需要注意的第二个重要的一点是,如果传递一个引用到一个对象,需要使用<property>标签的ref属性,如果是直接传递一个值,那么应该使用value属性。

创建源代码和bean配置文件完成后,让我们运行应用程序。如果一切顺利,这将打印以下信息:

Inside SpellChecker constructor.
Inside setSpellChecker.
Inside checkSpelling.

采用p名称空间的XML配置:
如果你有很多的setter方法​​则可以很方便地使用p名称空间的XML配置文件中。让我们查看他们的区别:

让我们来用的<property>标签标准的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-3.0.xsd">

  <bean id="john-classic" class="com.example.Person">
   <property name="name" value="John Doe"/>
   <property name="spouse" ref="jane"/>
  </bean>

  <bean name="jane" class="com.example.Person">
   <property name="name" value="John Doe"/>
  </bean>

</beans>

上面的XML配置可重写使用 p-namespace如下一个简洁的方法:

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:p="http://www.springframework.org/schema/p"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

  <bean id="john-classic" class="com.example.Person"
   p:name="John Doe"
   p:spouse-ref="jane"/>
  </bean>

  <bean name="jane" class="com.example.Person"
   p:name="John Doe"/>
  </bean>

</beans>

在这里,不应该在指定原始值和对空间对象引用的区别。-ref部分表示,这不是直链的值,而是一个引用到另一个bean中。

(0)

相关推荐

  • 因Spring AOP导致@Autowired依赖注入失败的解决方法

    发现问题: 之前用springAOP做了个操作日志记录,这次在往其他类上使用的时候,service一直注入失败,找了网上好多内容,发现大家都有类似的情况出现,但是又和自己的情况不太符合.后来总结自己的情况发现:方法为private修饰的,在AOP适配的时候会导致service注入失败,并且同一个service在其他的public方法中就没有这种情况,十分诡异. 解决过程: 结合查阅的资料进行了分析:在org.springframework.aop.support.AopUtils中: publi

  • 详解SpringBoot中实现依赖注入功能

    今天给大家介绍一下SpringBoot中是如何实现依赖注入的功能. 在以往spring使用中,依赖注入一般都是通过在Spring的配置文件中添加bean方法实现的,相对于这个方式SpringBoot的实现方式就显得非常便捷了.SpringBoot的实现方式基本都是通过注解实现的. 下面来看一下具体案例,这里我编写了三个测试类用于测试依赖注入到底是否可以正确实现. TestBiz接口: package example.biz; public interface TestBiz { public S

  • 详解Spring的核心机制依赖注入

    详解Spring的核心机制依赖注入 对于一般的Java项目,他们都或多或少有一种依赖型的关系,也就是由一些互相协作的对象构成的.Spring把这种互相协作的关系称为依赖关系.如A组件调用B组件的方法,可称A组件依赖于B组件,依赖注入让Spring的Bean以配置文件组织在一起,而不是以硬编码的方式耦合在一起 一.理解依赖注入 依赖注入(Dependency Injection) = 控制反转(Inversion ofControl,IoC):当某个Java实例(调用者)需另一个Java实例(被调

  • 详析Spring中依赖注入的三种方式

    前言 平常的java开发中,程序员在某个类中需要依赖其它类的方法,则通常是new一个依赖类再调用类实例的方法,这种开发存在的问题是new的类实例不好统一管理,spring提出了依赖注入的思想,即依赖类不由程序员实例化,而是通过spring容器帮我们new指定实例并且将实例注入到需要该对象的类中.依赖注入的另一种说法是"控制反转",通俗的理解是:平常我们new一个实例,这个实例的控制权是我们程序员,而控制反转是指new实例工作不由我们程序员来做而是交给spring容器来做. 在Sprin

  • Spring 依赖注入的几种方式详解

    IoC 简介 平常的Java开发中,程序员在某个类中需要依赖其它类的方法. 通常是new一个依赖类再调用类实例的方法,这种开发存在的问题是new的类实例不好统一管理. Spring提出了依赖注入的思想,即依赖类不由程序员实例化,而是通过Spring容器帮我们new指定实例并且将实例注入到需要该对象的类中. 依赖注入的另一种说法是"控制反转".通俗的理解是:平常我们new一个实例,这个实例的控制权是我们程序员. 而控制反转是指new实例工作不由我们程序员来做而是交给Spring容器来做.

  • 实例讲解Java的Spring框架中的控制反转和依赖注入

    近来总是接触到 IoC(Inversion of Control,控制反转).DI(Dependency Injection,依赖注入)等编程原则或者模式,而这些是著名 Java 框架 Spring.Struts 等的核心所在.针对此查了 Wikipedia 中各个条目,并从图书馆借来相关书籍,阅读后有些理解,现结合书中的讲解以及自己的加工整理如下: eg1 问题描述: 开发一个能够按照不同要求生成Excel或 PDF 格式的报表的系统,例如日报表.月报表等等.   解决方案: 根据"面向接口编

  • 详解Java Spring各种依赖注入注解的区别

    注解注入顾名思义就是通过注解来实现注入,Spring和注入相关的常见注解有Autowired.Resource.Qualifier.Service.Controller.Repository.Component. Autowired是自动注入,自动从spring的上下文找到合适的bean来注入 Resource用来指定名称注入 Qualifier和Autowired配合使用,指定bean的名称 Service,Controller,Repository分别标记类是Service层类,Contro

  • 深入解析Java的Spring框架中bean的依赖注入

    每一个基于java的应用程序都有一个共同工作来展示给用户看到的内容作为工作的应用几个对象.当编写一个复杂的Java应用程序,应用程序类应该尽可能独立其他Java类来增加重复使用这些类,并独立于其他类别的测试它们,而这样做单元测试的可能性.依赖注入(或有时称为布线)有助于粘合这些类在一起,同时保持他们的独立. 考虑有其中有一个文本编辑器组件的应用程序,要提供拼写检查.标准的代码将看起来像这样: public class TextEditor { private SpellChecker spell

  • 深入解析Java的Spring框架中的混合事务与bean的区分

    混合事务 在ORM框架的事务管理器的事务内,使用JdbcTemplate执行SQL是不会纳入事务管理的. 下面进行源码分析,看为什么必须要在DataSourceTransactionManager的事务内使用JdbcTemplate. 1.开启事务 DataSourceTransactionManager protected void doBegin(Object transaction,TransactionDefinition definition) { DataSourceTransact

  • 详解Java的Spring框架中bean的定义以及生命周期

    bean的定义 形成应用程序的骨干是由Spring IoC容器所管理的对象称为bean.bean被实例化,组装,并通过Spring IoC容器所管理的对象.这些bean由容器提供,例如,在XML的<bean/>定义,已经看到了前几章的形式配置元数据创建. bean定义包含所需要的容器要知道以下称为配置元数据的信息: 如何创建一个bean Bean 生命周期的详细信息 Bean 依赖关系 上述所有配置元数据转换成一组的下列属性构成每个bean的定义. Spring配置元数据 Spring IoC

  • 详解Java的Spring框架中bean的注入集合

    使用value属性和使用<property>标签的ref属性在你的bean配置文件中的对象引用,这两种情况下可以处理单值到一个bean,如果你想通过多元值,如Java Collection类型List, Set, Map 及 Properties.要处理这种情况,Spring提供了四种类型的如下集合的配置元素: 可以使用<list> 或<set> 来连接任何实现java.util.Collection或数组. 会遇到两种情况(a)将收集的直接的值及(b)传递一个bean

  • Java的Spring框架中bean的继承与内部bean的注入

    bean的定义继承 bean定义可以包含很多的配置信息,包括构造函数的参数,属性值,比如初始化方法,静态工厂方法名等容器的具体信息. 子bean定义从父定义继承配置数据.子的定义可以覆盖一些值,或者根据需要添加其他. Spring bean定义继承无关,与Java类的继承,但继承的概念是一样的.你可以定义一个父bean定义为模板和其他孩子bean可以从父bean继承所需的配置. 当使用基于XML的配置元数据,指明一个子bean定义使用所在的当前属性指定的父bean作为这个属性的值. 例如: 让我

  • spring IOC中三种依赖注入方式

    一.Spring IOC(依赖注入的三种方式): 1.Setter方法注入. 2.构造方法注入. 使用构造方法,注入bean值. 关键代码: public UserServiceImpl(UserDao dao) { this.dao=dao; } <bean id="service" class="service.impl.UserServiceImpl"> <constructor-arg><ref bean="dao&q

  • 深入理解Java的Spring框架中的IOC容器

    Spring IOC的原型 spring框架的基础核心和起点毫无疑问就是IOC,IOC作为spring容器提供的核心技术,成功完成了依赖的反转:从主类的对依赖的主动管理反转为了spring容器对依赖的全局控制. 这样做的好处是什么呢? 当然就是所谓的"解耦"了,可以使得程序的各模块之间的关系更为独立,只需要spring控制这些模块之间的依赖关系并在容器启动和初始化的过程中将依据这些依赖关系创建.管理和维护这些模块就好,如果需要改变模块间的依赖关系的话,甚至都不需要改变程序代码,只需要将

  • Java的Spring框架中AOP项目的一般配置和部署教程

    0.关于AOP 面向切面编程(也叫面向方面编程):Aspect Oriented Programming(AOP),是软件开发中的一个热点,也是Spring框架中的一个重要内容.利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率. AOP是OOP的延续. 主要的功能是:日志记录,性能统计,安全控制,事务处理,异常处理等等. 主要的意图是:将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过

  • 详解Java的Spring框架下bean的自动装载方式

    Spring容器可以自动装配相互协作bean之间的关系,这有助于减少对XML配置,而无需编写一个大的基于Spring应用程序的较多的<constructor-arg>和<property>元素. 自动装配模式: 有下列自动装配模式,可用于指示Spring容器使用自动装配依赖注入.使用<bean/>元素的autowire属性为一个bean定义中指定自动装配模式. byName模式 这种模式规定由自动装配属性名称.Spring容器在外观上自动线属性设置为byName的XML

  • 浅析Java的Spring框架中IOC容器容器的应用

    Spring容器是Spring框架的核心.容器将创建对象,它们连接在一起,配置它们,并从创建到销毁管理他们的整个生命周期.在Spring容器使用依赖注入(DI)来管理组成应用程序的组件.这些对象被称为Spring Beans. 容器获得其上的哪些对象进行实例化,配置和组装通过阅读提供的配置元数据的说明.配置元数据可以通过XML,Java注释或Java代码来表示.下面的图是Spring如何工作的高层次图. Spring IoC容器是利用Java的POJO类和配置元数据的产生完全配置和可执行的系统或

随机推荐