`
kuru
  • 浏览: 140159 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

注入依赖

    博客分类:
  • java
阅读更多

依赖注入(DI)背后的基本原理是对象之间的依赖关系(即一起工作的其它对象)只会通过以下几种方式来实现:构造器的参数、工厂方法的参数,或给由构造函数或者工厂方法创建的对象设置属性。因此,容器的工作就是创建bean时注入那些依赖关系。

Setter注入

通过调用无参构造器或无参static工厂方法实例化bean之后,调用该bean的setter方法,即可实现基于setter的DI。

下面的例子将展示使用setter注入依赖。注意,这个类并没有什么特别之处,它就是普通的Java类。
public class SimpleMovieLister {
      // the SimpleMovieLister has a dependency on the MovieFinder
    private MovieFinder movieFinder;
      // a setter method so that the Spring container can 'inject' a MovieFinder
    public void setMoveFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    } 
    // business logic that actually 'uses' the injected MovieFinder is omitted...
}

构造器注入
基于构造器的DI通过调用带参数的构造器来实现,每个参数代表着一个协作者。此外,还可通过给静态工厂方法传参数来构造bean。

下面的展示了只能使用构造器参数来注入依赖关系的例子。再次提醒,这个类并没有什么特别之处。
public class SimpleMovieLister {
      // the SimpleMovieLister has a dependency on the MovieFinder
    private MovieFinder movieFinder;
      // a constructor so that the Spring container can 'inject' a  MovieFinder
    public SimpleMovieLister(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
    // business logic that actually 'uses' the injected MovieFinder is omitted...
}

处理bean依赖关系通常按以下步骤进行:
1.根据定义bean的配置(文件)创建并初始化BeanFactory实例
2.每个bean的依赖将以属性、构造器参数、或静态工厂方法参数的形式出现。
3.每个属性或构造器参数既可以是一个实际的值,也可以是对该容器中另一个bean的引用。
4.每个指定的属性或构造器参数值必须能够被转换成属性或构造参数所需的类型。

需要强调的一点就是,Spring会在容器被创建时验证容器中每个bean的配置,包括验证那些bean所引用的属性是否指向一个有效的bean(即被引用的bean也在容器中被定义)。

通常情况下,你可以信赖Spring,它会在容器加载时发现配置错误。Spring会在bean创建的时才去设置属性和依赖关系(只在需要时创建所依赖的其他对象)。

在默认情况下,ApplicationContext实现中的bean采用提前实例化的singleton模式。在实际需要之前创建这些bean将带来时间与内存的开销。而这样做的好处就是ApplicationContext被加载的时候可以尽早的发现一些配置的问题。不过用户也可以根据需要采用延迟实例化来替代默认的singleton模式。

构造器参数的解析
构造器参数将根据类型来进行匹配。如果bean定义中的构造器参数类型明确,那么bean定义中的参数顺序就是对应构造器参数的顺序。考虑以下的类...
package x.y;
  public class Foo {
      public Foo(Bar bar, Baz baz) {
        // ...
    }
}
下面的配置将会很好地工作,且无须显式地指定构造器参数索引及其类型。
<beans>
    <bean name="foo" class="x.y.Foo">
        <constructor-arg>
            <bean class="x.y.Bar"/>
        </constructor-arg>
        <constructor-arg>
            <bean class="x.y.Baz"/>
        </constructor-arg>
    </bean>
</beans>

构造器参数类型匹配
但是当使用象<value>true<value>这样的简单类型时,Spring将无法决定该值的类型,因而仅仅根据类型是无法进行匹配的。针对这种情况,我们可以在构造器参数定义中使用type属性来显式的指定参数所对应的简单类型。例如:
<bean id="exampleBean" class="examples.ExampleBean">
  <constructor-arg type="int" value="7500000"/>
  <constructor-arg type="java.lang.String" value="42"/>
</bean>

构造器参数的索引
通过使用index属性可以显式的指定构造器参数出现顺序。例如:
<bean id="exampleBean" class="examples.ExampleBean">
  <constructor-arg index="0" value="7500000"/>
  <constructor-arg index="1" value="42"/>
</bean>
使用index属性除了可以解决多个简单类型构造参数造成的模棱两可的问题之外,还可以用来解决两个构造参数类型相同造成的麻烦。注意:index属性值从0开始。
提示
指定构造器参数索引是使用构造器IoC首选的方式。

bean属性及构造器参数详解
bean的属性及构造器参数既可以引用容器中的其他bean,也可以是内联(inline,在spring的XML配置中使用<property/>和<constructor-arg/>元素定义)bean。

直接量(基本类型、Strings类型等。)
<value/>元素通过字符串来指定属性或构造器参数的值。

idref元素
idref元素用来将容器内其它bean的id传给<constructor-arg/> 或 <property/>元素,同时提供错误验证功能。
<bean id="theTargetBean" class="..."/>
  <bean id="theClientBean" class="...">
    <property name="targetName">
        <idref bean="theTargetBean" />
    </property>
</bean>

上述bean定义片段完全地等同于(在运行时)以下的片段:
<bean id="theTargetBean" class="..."/>
  <bean id="client" class="...">
    <property name="targetName">
        <value>theTargetBean</value>
    </property>
</bean>

第一种形式比第二种更可取的主要原因是,使用idref标记允许容器在部署时 验证所被引用的bean是否存在。而第二种方式中,传给client bean的targetName属性值并没有被验证。

此外,如果被引用的bean在同一XML文件内,且bean名字就是bean id,那么可以使用local属性,此属性允许XML解析器在解析XML文件时来对引用的bean进行验证。
<property name="targetName">
   <!-- a bean with an id of 'theTargetBean' must exist, else an XML exception will be thrown -->
   <idref local="theTargetBean"/>
</property>

引用其它的bean(协作者)
在<constructor-arg/>或<property/>元素内部还可以使用ref元素。该元素用来将bean中指定属性的值设置为对容器中的另外一个bean的引用。如前所述,该引用bean将被作为依赖注入,而且在注入之前会被初始化(如果是singleton bean则已被容器初始化)。尽管都是对另外一个对象的引用,但是通过id/name指向另外一个对象却有三种不同的形式,不同的形式将决定如何处理作用域及验证。

第一种形式也是最常见的形式是通过使用<ref/>标记指定bean属性的目标bean,通过该标签可以引用同一容器或父容器内的任何bean(无论是否在同一XML文件中)。XML 'bean'元素的值既可以是指定bean的id值也可以是其name值。
<ref bean="someBean"/>

第二种形式是使用ref的local属性指定目标bean,它可以利用XML解析器来验证所引用的bean是否存在同一文件中。local属性值必须是目标bean的id属性值。如果在同一配置文件中没有找到引用的bean,XML解析器将抛出一个例外。如果目标bean是在同一文件内,使用local方式就是最好的选择(为了尽早地发现错误)。
<ref local="someBean"/>

第三种方式是通过使用ref的parent属性来引用当前容器的父容器中的bean。parent属性值既可以是目标bean的id值,也可以是name属性值。而且目标bean必须在当前容器的父容器中。使用parent属性的主要用途是为了用某个与父容器中的bean同名的代理来包装父容器中的一个bean(例如,子上下文中的一个bean定义覆盖了他的父bean)。
<!-- in the parent context -->
<bean id="accountService" class="com.foo.SimpleAccountService">
    <!-- insert dependencies as required as here -->
</bean>
<!-- in the child (descendant) context -->
<bean id="accountService"  <-- notice that the name of this bean is the same as the name of the 'parent' bean
      class="org.springframework.aop.framework.ProxyFactoryBean">
      <property name="target">
          <ref parent="accountService"/>  <-- notice how we refer to the parent bean
      </property>
    <!-- insert other configuration and dependencies as required as here -->
</bean>

内部bean
所谓的内部bean(inner bean)是指在一个bean的<property/>或 <constructor-arg/>元素中使用<bean/>元素定义的bean。内部bean定义不需要有id或name属性,即使指定id 或 name属性值也将会被容器忽略。
<bean id="outer" class="...">
  <!-- instead of using a reference to a target bean, simply define the target inline -->
  <property name="target">
    <bean class="com.mycompany.Person"> <!-- this is the inner bean -->
      <property name="name" value="Fiona Apple"/>
      <property name="age" value="25"/>
    </bean>
  </property>
</bean>
注意:内部bean中的singleton标记及id或name属性将被忽略。内部bean总是匿名的且它们总是prototype模式的。同时将内部bean注入到包含该内部bean之外的bean是不可能的。

集合
通过<list/>、<set/>、<map/>及<props/>元素可以定义和设置与Java Collection类型对应List、Set、Map及Properties的值。
<bean id="moreComplexObject" class="example.ComplexObject">
  <!-- results in a setAdminEmails(java.util.Properties) call -->
  <property name="adminEmails">
    <props>
        <prop key="administrator">administrator@somecompany.org</prop>
        <prop key="support">support@somecompany.org</prop>
        <prop key="development">development@somecompany.org</prop>
    </props>
  </property>
  <!-- results in a setSomeList(java.util.List) call -->
  <property name="someList">
    <list>
        <value>a list element followed by a reference</value>
        <ref bean="myDataSource" />
    </list>
  </property>
  <!-- results in a setSomeMap(java.util.Map) call -->
  <property name="someMap">
    <map>
        <entry>
            <key>
                <value>yup an entry</value>
            </key>
            <value>just some string</value>
        </entry>
        <entry>
            <key>
                <value>yup a ref</value>
            </key>
            <ref bean="myDataSource" />
        </entry>
    </map>
  </property>
  <!-- results in a setSomeSet(java.util.Set) call -->
  <property name="someSet">
    <set>
        <value>just some string</value>
        <ref bean="myDataSource" />
    </set>
  </property>
</bean>
注意:map的key或value值,或set的value值不能是以下元素:
bean | ref | idref | list | set | map | props | value | null

集合合并
这样我们可以定义parent-style和child-style的<list/>、<map/>、<set/>或<props/>元素,子集合的值从其父集合继承和覆盖而来;也就是说,父子集合元素合并后的值就是子集合中的最终结果,而且子集合中的元素值将覆盖父集全中对应的值。
<beans>
<bean id="parent" abstract="true" class="example.ComplexObject">
    <property name="adminEmails">
        <props>
            <prop key="administrator">administrator@somecompany.com</prop>
            <prop key="support">support@somecompany.com</prop>
        </props>
    </property>
</bean>
<bean id="child" parent="parent">
    <property name="adminEmails">
        <!-- the merge is specified on the *child* collection definition -->
        <props merge="true">
            <prop key="sales">sales@somecompany.com</prop>
            <prop key="support">support@somecompany.co.uk</prop>
        </props>
    </property>
</bean>
<beans>
在上面的例子中,childbean的adminEmails属性的<props/>元素上使用了merge=true属性。当child bean被容器实际解析及实例化时,其 adminEmails将与父集合的adminEmails属性进行合并。
administrator=administrator@somecompany.com
sales=sales@somecompany.com
support=support@somecompany.co.uk

XML-based configuration metadata shortcuts
<property name="myProperty">
  <value>hello</value>
</property>
等同于:
<property name="myProperty" value="hello"/>

<property name="myProperty">
  <ref bean="myBean">
</property>
等同于:
<property name="myProperty" ref="myBean"/>

map中entry元素的简写形式为key/key-ref 和 value /value-ref属性,因而,以下的代码:
<entry>
  <key>
    <ref bean="myKeyBean" />
  </key>
  <ref bean="myValueBean" />
</entry>
等同于:
<entry key-ref="myKeyBean" value-ref="myValueBean"/>
强调,只有<ref bean="xxx">元素的简写形式,没有<ref local="xxx">的简写形式。

组合属性名称
当设置bean的组合属性时,除了最后一个属性外,只要其他属性值不为null,组合或嵌套属性名是完全合法的。例如,下面bean的定义:
<bean id="foo" class="foo.Bar">
  <property name="fred.bob.sammy" value="123" />
</bean>
foo bean有个fred属性,此属性有个 bob属性,而bob属性又有个sammy属性,最后把sammy属性设置为123。为了让此定义能工作, foo的fred属性及fred 的bob属性在bean被构造后都必须非空,否则将抛出NullPointerException异常。

使用depends-on
有时候bean之间的依赖关系并不是那么的直接, depends-on属性可以用于当前bean初始化之前显式地强制一个或多个bean被初始化。
<bean id="beanOne" class="ExampleBean" depends-on="manager"/>
<bean id="manager" class="ManagerBean" />
若需要表达对多个bean的依赖,可以在'depends-on'中将指定的多个bean名字用分隔符进行分隔,分隔符可以是逗号、空格及分号等。

迟初始化bean
在XML配置文件中,延迟初始化将通过<bean/>元素中的lazy-init属性来进行控制。
分享到:
评论

相关推荐

    spring依赖注入

    接口注入则相对较少使用,它通过实现特定接口来注入依赖。 首先,我们来看看构造器注入。在类中定义构造器,并将依赖作为参数传入。Spring容器在创建对象时,会根据构造器的参数类型自动匹配并注入相应的bean。这种...

    Spring-注入依赖

    3. **接口注入**:Spring通过实现特定的接口,如`InitializingBean`或`DisposableBean`,来自动调用接口方法注入依赖。这种方式较少使用,因为通常构造器和setter注入已经足够。 在Spring项目中,配置依赖注入的...

    Dagger2通过AndroidInjector统一注入依赖.zip

    《Dagger2通过AndroidInjector统一注入依赖:精解与实践》 Dagger2,作为一款强大的依赖注入框架,被广泛应用于Android开发中,它能够帮助我们管理应用中的对象依赖关系,减少代码耦合,提高可测试性和可维护性。...

    Spring使用@Autowired为抽象父类注入依赖代码实例

    Spring使用@Autowired为抽象父类注入依赖代码实例 Spring框架中,使用@Autowired注解可以将依赖项注入到Bean中,但是当我们需要将依赖项注入到抽象父类时,会遇到一些问题。如果我们直接使用@Autowired注解在抽象...

    Core5 WebApi JWT验证登录+注入依赖Demo

    【标题】"Core5 WebApi JWT验证登录+注入依赖Demo" 涉及到的关键技术主要集中在.NET Core 5框架下的WebAPI开发、JSON Web Token(JWT)验证以及依赖注入。这里将详细介绍这些核心概念。 1. **WebAPI**: WebAPI是...

    依赖注入那些事儿

    构造函数注入是在对象创建时通过构造函数参数来注入依赖。这种方式可以确保对象在创建时就拥有所有必需的依赖项,并且对象的状态不会在创建后发生改变。 ##### 3.1.3 依赖获取 依赖获取是指对象主动请求依赖项。...

    c# 依赖注入 控制反转

    - **Constructor Injection(构造函数注入)**:通过对象的构造函数来注入依赖项,这种方式可以确保对象在创建时就已经具备所有必需的依赖项。 - **Setter Injection(设值注入)**:通过对象的setter方法来注入依赖...

    C++ 依赖注入

    3. Set函数注入:通过对象的setter方法注入依赖对象,提供了一定程度上的灵活性,但可能引入空状态。 4. 函数参数注入:将依赖对象作为方法参数传递,适用于依赖关系不需要长期保留的场景。 依赖注入容器是管理依赖...

    php依赖注入demo

    2. 属性注入:这种方式允许在类实例化后通过直接设置属性来注入依赖,但应谨慎使用,因为它可能会破坏类的封装性。 ```php class MyClass { public $dependency; // 没有构造函数,依赖可以通过直接设置属性注入...

    Spring Ioc 注解 依赖注入

    1. **构造器注入**:通过类的构造方法来注入依赖项。 2. **设值注入**:通过setter方法来注入依赖项。 #### 四、使用注解进行依赖注入 随着Spring框架的发展,除了XML配置之外,Spring还引入了注解的方式来简化...

    Spring-注入依赖,AOP,自动注入Bean

    在Spring框架中,依赖注入(Dependency Injection,DI)和面向切面编程(Aspect-Oriented Programming,AOP)是两大核心特性,同时自动注入Bean也是Spring管理对象的一种常见方式。让我们深入探讨这些概念。 首先,...

    依赖注入概念

    依赖注入允许我们将依赖关系从类的内部移除,由外部负责管理和注入依赖项,从而降低了类之间的耦合度,提高了代码的可复用性和可测试性。 #### 三、依赖注入的实现方式 依赖注入可以通过以下几种方式实现: 1. **...

    spring依赖注入的实现原理

    随着Java的发展,Spring引入了注解驱动的配置方式,如`@Component`,`@Service`,`@Repository`和`@Controller`用于标记Bean,`@Autowired`用于自动注入依赖。 4. **反射(Reflection)** Spring使用反射机制来...

    spring学习:依赖注入的几种方式讨论

    在类或方法上使用`@Autowired`注解可以自动匹配并注入依赖。例如: ```java @Service public class Service { @Autowired private Repository repository; } ``` `@Service`是Spring的组件注解,标记此类为一个...

    第三章 Spring4 依赖注入

    Spring会自动调用setter方法来注入依赖。例如: ```java public class UserService { private UserRepository userRepository; @Autowired public void setUserRepository(UserRepository userRepository) { ...

    Spring的依赖注入,与前置通知的实例

    1. **构造器注入**:通过在类的构造函数中传递依赖对象的实例,Spring容器会在创建目标对象时调用合适的构造函数并注入依赖。 2. **setter注入**:在类中定义setter方法,Spring容器可以通过调用这些setter方法来...

    Unity MVC实现依赖注入实例

    `DependencyResolver.SetResolver`则将Unity容器设置为MVC的默认依赖解析器,这样MVC控制器就可以通过构造函数注入依赖了。 在控制器中,我们可以像下面这样注入依赖: ```csharp public class HomeController : ...

    spring依赖注入三种方式 测试源码

    接口注入在Spring中较少使用,主要是通过实现特定的接口,由Spring提供实现该接口的方法来注入依赖。这种方式对类的侵入性较大,但可以避免使用setter方法。在实际应用中,更多地会采用构造器注入和设值注入。 ...

    两种依赖注入的类型

    - **setter注入** 更灵活,可以在对象创建后任何时候注入依赖,但可能会导致对象的状态不一致,因为依赖可能没有在所有setter方法都调用后完成。 - **构造方法注入** 确保对象在构造时就处于完整状态,提高了对象的...

    Spring 控制反转 依赖注入

    3. **接口注入**:较少使用,通过实现特定接口来注入依赖,但Spring并不直接支持,通常需要自定义实现。 **Spring的配置方式** Spring提供了多种配置方式: 1. **XML配置**:传统的Spring配置方式,通过`&lt;bean&gt;`...

Global site tag (gtag.js) - Google Analytics