源码学习之DI-基础篇

         接上文beanDefinition的内容,接下来会介绍spring中的依赖注入部分。

DI

         当我们用bean定义来描述一个bean的时候,这个bean往往需要依赖于其他对象,IOC容器在实例化这个bean的时候,会将依赖的对象进行注入,这个过程就叫依赖注入。依赖注入主要有两种方式:

  1. Constructor-based dependency injection
  2. Setter-based dependency injection

Constructor-based dependency injection构造函数注入

         当对象存在有参构造函数时(或者存在静态工厂方法),通过<constructor-arg>来指定构造函数的参数。

参数是对象时:

 
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>

参数是简单数据类型(基本数据类型+String)

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

根据类型来进行配置:

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

如果两个类型相同,可能会引起歧义的情况,则可以使用下标来指定构造函数参数:

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

 

Setter-based dependency injection属性注入

         当对象不存在有参构造函数或者静态工厂方法时,则可以使用<property>标签调用成员变量的setter方法进行注入:

 
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">
    <!-- 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"/>

 

<property>中数据类型的支持:

Straight values (primitives, Strings, and so on)

         Property中对于简单数据的配置:

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

上面这种配置,还可以使用p-namespace来表示,这两个是等价的:

 
    <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close"
        p:driverClassName="com.mysql.jdbc.Driver"
        p:url="jdbc:mysql://localhost:3306/mydb"
        p:username="root"
        p:password="masterkaoli"/>

spring中还支持对properties形式的解析:

 
<bean id="mappings"
    class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">

    <!-- typed as a java.util.Properties -->
    <property name="properties">
        <value>
            jdbc.driver.className=com.mysql.jdbc.Driver
            jdbc.url=jdbc:mysql://localhost:3306/mydb
        </value>
    </property>
</bean>

 

 

Collections

 除了简单的数据类型外,property还支持集合的处理:<list/><set/><map/>,  <props/>

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

 

ref 和 depends-on

         ref:当bean A依赖于bean B时,可以使用ref来表示AB之间的依赖关系。

         depends-on:当bean A和bean B没有明显的依赖关系,但是有部分功能需要bean B初始化完成以后才能正确执行,这种情况bean A也对bean B有依赖关系,这个时候就可以使用depends-on来进行表示。

        

Method injection

         在我们的使用中,还会有这种场景,bean A依赖于bean B,当A的scope是singleton,B的scope是prototype时,此时每次请求A都需要返回一个新的B的实例。正常的实现的话会是这样:

// a class that uses a stateful Command-style class to perform some processing
package fiona.apple;

// Spring-API imports
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class CommandManager implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    public Object process(Map commandState) {
        // grab a new instance of the appropriate Command
        Command command = createCommand();
        // set the state on the (hopefully brand new) Command instance
        command.setState(commandState);
        return command.execute();
    }

    protected Command createCommand() {
        // notice the Spring API dependency!
        return this.applicationContext.getBean("command", Command.class);
    }

    public void setApplicationContext(
            ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

 

通过this.applicationContext.getBean("command", Command.class)来实现每次返回B实例。

这样做的确实现了我们的要求,但同时也导致了业务代码和spring框架代码的耦合,要知道spring一直在做的都是各种手段解耦,怎么会允许这种事发生呢。所以spring提供了<lookup-method>通过方法注入这种途径来解决这类问题,使得代码可以更加整洁。

         它的使用方式如下(还是用上面的例子):

 

 
package fiona.apple;

// no more Spring imports!

public abstract class CommandManager {

    public Object process(Object commandState) {
        // grab a new instance of the appropriate Command interface
        Command command = createCommand();
        // set the state on the (hopefully brand new) Command instance
        command.setState(commandState);
        return command.execute();
    }

    // okay... but where is the implementation of this method?
    protected abstract Command createCommand();
}

 

把原来返B实例的方法改成抽象方法,这里需要注意,这个格式是有固定结构的:

<public|protected> [abstract] <return-type> theMethodName(no-arguments);

 

然后在配置文件中:

<!-- a stateful bean deployed as a prototype (non-singleton) -->
<bean id="myCommand" class="fiona.apple.AsyncCommand" scope="prototype">
    <!-- inject dependencies here as required -->
</bean>

<!-- commandProcessor uses statefulCommandHelper -->
<bean id="commandManager" class="fiona.apple.CommandManager">
    <lookup-method name="createCommand" bean="myCommand"/>
</bean>

这里定义的抽象方法createCommand()方法并不需要我们来实现,我们只需要在配置文件中通过<lookup-method>来指定这个方法需要返回的对象即可。

 

除了在bean定义中使用<lookup-method>外,spring同样提供了注解的形式:

第一种:

public abstract class CommandManager {

    public Object process(Object commandState) {
        Command command = createCommand();
        command.setState(commandState);
        return command.execute();
    }

    @Lookup("myCommand")
    protected abstract Command createCommand();
}

第二种:

public abstract class CommandManager {

    public Object process(Object commandState) {
        MyCommand command = createCommand();
        command.setState(commandState);
        return command.execute();
    }

    @Lookup
    protected abstract MyCommand createCommand();
}
 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值