接上文beanDefinition的内容,接下来会介绍spring中的依赖注入部分。
DI
当我们用bean定义来描述一个bean的时候,这个bean往往需要依赖于其他对象,IOC容器在实例化这个bean的时候,会将依赖的对象进行注入,这个过程就叫依赖注入。依赖注入主要有两种方式:
- Constructor-based dependency injection
- 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();
}