【Java.Spring.Core】【IoC】Bean依赖注入- Dependencies

本文深入探讨了Spring框架中的依赖注入机制,包括基于构造函数和setter方法的依赖注入方式,详细解释了依赖解析过程及配置细节。同时,介绍了如何在实际应用中使用依赖注入来提高代码的整洁性和解耦能力。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

依赖注入 - Dependency Injection

当容器创建bean的时候会注入这些依赖。

当为对象提供依赖对象时,代码会比较整洁同时会解耦。对象不需要查找它们的依赖,也不需要知道这些依赖所对应的类或位置。

(1)基于构造函数的依赖注入

基于构造函数的依赖通过容器调用构造函数来实现,构造函数可能包含若干个参数,每一个代表一个依赖。


例如:

package x.y;

public class Foo {

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

}

当使用参数类型匹配时,如果不存在歧义,默认的,bean定义中构造函数参数的顺序就是实际传入参数时的顺序。

假设Bar 和 Baz是不相关的类型,则不存在类型歧义。因此,下列配置是合法的,不需要在<constructor-arg>元素中显式的指定构造函数参数索引或者类型,

<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>


当使用简单类型时,例如<value>true</value>,Spring不能确定该值的类型,此时需要指定其他的属性,例如:

package examples;

public class ExampleBean {

    // Number of years to calculate the Ultimate Answer
    private int years;

    // The Answer to Life, the Universe, and Everything
    private String ultimateAnswer;

    public ExampleBean(int years, String ultimateAnswer) {
        this.years = years;
        this.ultimateAnswer = ultimateAnswer;
    }

}


在这个例子中,可以使用 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是从 0 开始计数的。


同样,也可以使用构造函数参数名来避免歧义,例如:

<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg name="years" value="7500000"/>
    <constructor-arg name="ultimateAnswer" value="42"/>
</bean>


注意,当使用参数名来指定依赖时,需要开启debug编译选项,否则Spring无法找到该构造函数的参数名。如果没有开启 debug 编译选项,可以使用 @ConstructProperties 注解显式的指定构造函数的参数名,例如:

package examples;

public class ExampleBean {

    // Fields omitted

    @ConstructorProperties({"years", "ultimateAnswer"})
    public ExampleBean(int years, String ultimateAnswer) {
        this.years = years;
        this.ultimateAnswer = ultimateAnswer;
    }

}


(2)基于Setter方法的依赖注入

该方法在使用无参数的构造函数或者无参数的厂方法实例化bean之后,通过容器调用该bean的 setter 方法来完成依赖注入

例如:

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 setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // business logic that actually uses the injected MovieFinder is omitted...

}


ApplicationContext处理支持基于构造函数的注入和基于 setter 方法的注入外,也支持在使用构造函数注入部分依赖后,再使用基于 setter 方法来注入其余的依赖


依赖解析过程

容器如下完成bean的依赖解析:

  • ApplicationContext被创建,并使用配置元数据来初始化
  • 对于每一个bean, 它的依赖可能是属性,构造函数的参数,静态厂方法的参数等形式;当bean实际创建的时候,这些依赖被提供给bean
  • 每一个属性或者构造函数的参数都是一个实际的值,或者是存在于容器中的另一个bean
  • 每一个属性或者构造函数的参数,如果其是值类型,那么该值被转换为属性或参数所需的实际的类型

当容器被创建的时候,Spring容器会验证每一个 bean 的配置;但是,对于bean的属性,直到bean被创建的时候才会被设置。

单例范围的bean 和提前初始化的bean在容器被创建的时候就会被创建。否则,直到该bean被请求的时候才会被创建

当创建 bean 的时候,其依赖即依赖的依赖都会被创建并赋值。


依赖注入示例

  • 基于 setter 方法的依赖注入:

<bean id="exampleBean" class="examples.ExampleBean">
    <!-- setter injection using the nested ref element -->
    <property name="beanOne">
        <ref bean="anotherExampleBean"/>
    </property>

    <!-- setter injection using the neater ref attribute -->
    <property name="beanTwo" ref="yetAnotherBean"/>
    <property name="integerProperty" value="1"/>
</bean>

<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>

public class ExampleBean {

    private AnotherBean beanOne;
    private YetAnotherBean beanTwo;
    private int i;

    public void setBeanOne(AnotherBean beanOne) {
        this.beanOne = beanOne;
    }

    public void setBeanTwo(YetAnotherBean beanTwo) {
        this.beanTwo = beanTwo;
    }

    public void setIntegerProperty(int i) {
        this.i = i;
    }

}

  • 基于构造函数的依赖注入:

<bean id="exampleBean" class="examples.ExampleBean">
    <!-- constructor injection using the nested ref element -->
    <constructor-arg>
        <ref bean="anotherExampleBean"/>
    </constructor-arg>

    <!-- constructor injection using the neater ref attribute -->
    <constructor-arg ref="yetAnotherBean"/>

    <constructor-arg type="int" value="1"/>
</bean>

<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>

public class ExampleBean {

    private AnotherBean beanOne;
    private YetAnotherBean beanTwo;
    private int i;

    public ExampleBean(
        AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {
        this.beanOne = anotherBean;
        this.beanTwo = yetAnotherBean;
        this.i = i;
    }

}

  • 使用厂方法的依赖注入:

<bean id="exampleBean" class="examples.ExampleBean" factory-method="createInstance">
    <constructor-arg ref="anotherExampleBean"/>
    <constructor-arg ref="yetAnotherBean"/>
    <constructor-arg value="1"/>
</bean>

<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>

public class ExampleBean {

    // a private constructor
    private ExampleBean(...) {
        ...
    }

    // a static factory method; the arguments to this method can be
    // considered the dependencies of the bean that is returned,
    // regardless of how those arguments are actually used.
    public static ExampleBean createInstance (
        AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {

        ExampleBean eb = new ExampleBean (...);
        // some other operations...
        return eb;
    }

}

Bean依赖的配置细节

Spring可以定义bean的属性和构造函数的参数为其他beans的引用; Spring的基于XML格式的配置元数据支持在 <property/> 和 <construct-arg/>元素中添加子元素:

字面量(primitive, Strings等)

<property/>元素包含一个属性 value,用来以可读的字符串形式指定bean的属性或者构造函数的参数。

Spring的conversion service用来将这些值由String类型转换为属性或参数的实际类型。

例如:

<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <!-- results in a setDriverClassName(String) call -->
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
    <property name="username" value="root"/>
    <property name="password" value="masterkaoli"/>
</bean>

idref 元素

dref元素用来将容器内其它bean的id传给<constructor-arg/>或<property/>元素,同时提供错误验证功能.


引用其他的beans

ref 元素位于 <construct=arg/> 或者 <property/> 元素定义的内部。用来设置一个bean的指定属性为引用容器中的其他bean。

通过使用<ref/>元素的 bean 属性指定目标bean是最常见的情形,该属性可以用来创建一个位于同一个或父容器(不同XML文件)中的bean的引用。bean属性的值可以是引用bean的id属性,或者是引用bean的name的值。

<ref bean="someBean"/>

通过parent属性来指定引用的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" <!-- bean name is the same as 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 here -->
</bean>

内部beans

位于<property/>或者<constructor-arg/>元素内部的<bean/>元素称作内部bean:

<bean id="outer" class="...">
    <!-- instead of using a reference to a target bean, simply define the target bean inline -->
    <property name="target">
        <bean class="com.example.Person"> <!-- this is the inner bean -->
            <property name="name" value="Fiona Apple"/>
            <property name="age" value="25"/>
        </bean>
    </property>
</bean>

内部不需要id 或者 name, 容器会忽略这些值。也会忽略 scope 属性。 内部bean通常是匿名bean,通常被外部bean所创建。

集合Collections

使用<list>, <set>, <map>, <props>元素,设置Java集合类型的属性或构造函数参数(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@example.org</prop>
            <prop key="support">support@example.org</prop>
            <prop key="development">development@example.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="an entry" value="just some string"/>
            <entry key ="a ref" value-ref="myDataSource"/>
        </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>


强类型集合

public class Foo {

    private Map<String, Float> accounts;

    public void setAccounts(Map<String, Float> accounts) {
        this.accounts = accounts;
    }
}

<beans>
    <bean id="foo" class="x.y.Foo">
        <property name="accounts">
            <map>
                <entry key="one" value="9.99"/>
                <entry key="two" value="2.75"/>
                <entry key="six" value="3.99"/>
            </map>
        </property>
    </bean>
</beans>

当bean foo的属性 accounts准备注入的时候,字符串值 9.99, 2.75, 3.99 被转换成实际的Float类型。

Null 和空字符串值

Spring将为空值的属性看作是空的字符串,例如:

<bean class="ExampleBean">
    <property name="email" value=""/>
</bean>

等价与下列的代码:

exampleBean.setEmail("")

<null/>元素表示 null, 例如:

<bean class="ExampleBean">
    <property name="email">
        <null/>
    </property>
</bean>

其等价于:

exampleBean.setEmail(null)

depends-on

如果一个bean是另一个bean的依赖,意味着这个bean是另一个bean的属性。通常,使用<ref/>元素进行设置。但,有时bean之间的依赖不是非常直接,例如,一个类内部的静态初始化器,depends-on属性可以显式的另这些bean在该bean初始化之前被初始化。

例如:

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

或:

<bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao">
    <property name="manager" ref="manager" />
</bean>

<bean id="manager" class="ManagerBean" />
<bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />

惰性初始化beans

默认,ApplicationContext的实现类会在初始化的过程中创建并配置全部的单件singleton beans。

我们可以使用惰性初始化来禁止单件bean的提前初始化过程。

惰性初始化通知容器在请求这个bean的时候再进行初始化。

<bean id="lazy" class="com.foo.ExpensiveToCreateBean" lazy-init="true"/>
<bean name="not.lazy" class="com.foo.AnotherBean"/>

但是,当一个惰性初始化bean是一个非惰性初始化bean的依赖时, ApplicationContext仍然会在启动的时候创建这个惰性初始化bean,应为需要满足单件bean的依赖注入。


Autowiring collaborators自动装配

Spring容器可以在bean之间进行自动装配。Spring可以通过检测ApplicationContext的内容来自动解析相互合作的beans。

自动装配的优点:

  • 减少属性或构造函数参数的显式指定
  • 当对象的配置发生改变时,可以自动识别

当使用基于XML的配置元数据时,通过指定<bean/>元素的 autowire属性可以指定bean的自动装配模式。

自动装配功能有5中模式,可以为每个bean的定义指定单独的自动装配属性:

  • no - 不自动装配,默认为 no
  • byName - 根据属性名称自动装配;Spring查找相同属性名称的bean;例如,一个bean定义设置自动装配模式为by name,其包含一个 master 属性,Spring会查找一个名为 master 的bean定义,并使用它作为属性
  • byType - 使用容器中存在的相同类型的bean作为属性进行自动装配。如果存在多个,将抛出异常;如果没有匹配的bean,该属性将不会被设置
  • constructor - 

自动装配的缺点:

  • 显式的依赖常常会覆盖自动装配
  • 自动装配的准确性差

方法注入









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值