一、IOC和DI
1、简介
IOC是Inversion Of Control(控制反转)的缩写,它是一种设计思想,是指将对象的控制权由程序代码反转给外部容器。
public class AccountServiceImpl implements IAccountService {
private IAccountDao accountDao;
public AccoutServiceImpl( {
this.accountDao = new AccountDaolmpl(); //在Spring中看不到这条语句[new AccountDaolmpl()]
}
}
在Spring 中,控制反转是实现Spring容器的指导思想。有了Spring容器,开发人员无需编写管理对象生命周期和依赖关系的代码,此项工作将由Spring容器根据配置自动完成,如此一来,对象的控制权由程序代码反转给Spring容器。
DI是Dependency Injection(依赖注入)的缩写,它是控制反转的另一种说法,同时也为控制反转提供了实现方法。依赖注入是指调用类对其他类的依赖关系由容器注入,这就避免了调用类对其他类的过度依赖,降低了类与类之间的耦合。注意,降低类与类之间的耦合度并不是让他们不再依赖,而是使得调用类不依赖具体的某个类的对象。
传统方式中,如果一个类依赖另一个类,则必须在该类中new一个具体依赖类的对象,比如:
public class AccountServiceImpl implements IAccountService {
private AccountDaoImpl accountDao;
public AccoutServiceImpl() {
accountDao = new AccountDaoImpl();
}
}
以上代码中,AccoutDaoImpl是 IAccountDao接口的具体实现类,当需要换接口的另一种实现时,代码需要进行相应改变:
private AccountDaoImpl2 accountDao;
accountDao = new AccountDaoImpl2();
实际开发中类与类之间依赖应该做到:编译期不依赖,运行时才依赖。
编译期依赖如下:
public class AccountServiceImpl implements IAccountService {
private AccountDaoImpl accountDao;
public AccoutServiceImpl(){
this.accountDao = new AccountDaoImpl();
}
}
运行时依赖如下:
AccoutServiceImpl类定义里,不再依赖具体的IAccountDao接口的实现类。
如果代码中存在多处类对象的创建,当具体实现类改变时相应代码都需要修改。此外,类的对象可以有不同的作用域,传统的方式很难去实现不同的作用域,比如实现单例模式。
如果使用Spring,则依赖关系由IOC容器进行注入。
public class AccountServiceImpl implements IAccountService {
private IAccountDao accountDao;
public void setAccountDao(IAccountDao accountDao){
this.accountDao = accountDao;
}
}
在AccountServiceImpl中,不再依赖具体的IAccountDao接口的实现类,只需要知道接口所定义的方法,调用该方法即可,这就降低AccountServiceImpl类与IAccountDao接口实现类的耦合度。
XML定义如下:
<bean id="accountServiceImpl" class="cn.edu.hnit.example.AccountServiceImpl">
<property name ="accountDao" ref="accountDaoImpl"/>
</bean>
<bean id="accountDaoImpl" class="cn.edu.hnit.example.AccountDaoImpl">
</bean>
对于依赖IAccountDao接口的类,只需要分别在XML中具体指定依赖即可,比如:
<bean id="accountServiceImpl" class="cn.edu.hnit.example.AccountServiceImpl">
<property name ="accountDao" ref="accountDaoImpl"/>
</bean>
<bean id="accountDaoImpl" class="cn.edu.hnit.example.AccountDaoImpl2">
</bean>
AccountServiceImpl类依赖IAccountDao接口的实现类,而此时接口实现类已从AccountDaoImpl改成AccountDaoImpl2。
2、依赖注入的方式
依赖注入常用的两种方式为:
- 构造器注入:将依赖对象以构造器参数的形式注入。
- 属性注入:将依赖对象通过Setter方法注入。
基于构造器函数的依赖注入需要在XML中指明依赖关系,比如TextEditor 需要依赖SpellChecker类时,XML的定义如下:
在TextEditor类中定义带参构造器。
public class TextEditor{
private SpellChecker spellChecker;
public TextEditor(SpellChecker spellChecker){
this.spellChecker = spellChecker;
}
}
基于setter 函数的依赖注入实际类似于给Bean设置值,因此需要有对应的setter方法,然后在XML中定义好Bean的值,比如:
public class TextEditor {
private SpellChecker spellChecker;
public void setSpellChecker(SpellChecker spellChecker){
System.out.println("Inside setSpellChecker." );
this.spellChecker = spellChecker;
}
}
<bean id="textEditor" class="com.tutorialspoint.TextEditor">
<property name="spellChecker" ref="spell"/>
</bean>
<bean id="spell" class="com.tutorialspoint.SpellChecker">
</bean>
注意:首先将构造方法设置为无参的构造方法,然后才能利用setter注入为其设置新的值。
基本数据类型的传递使用value,对象的传递使用ref。
public class TextEditor {
private SpellChecker spellChecker;
private String msg;
}
<bean id="textEditor" class="com.tutorialspoint.TextEditor">
<property name="spellChecker" ref="spell"/>
<property name = "language" value="english"/>
</bean>
<bean id="spell" class="com.tutorialspoint.SpellChecker">
</bean>
二、Bean的配置
1、Bean的定义
Bean是一个被实例化、组装、并通过Spring loC容器所管理的对象。Bean由容器提供的配置元数据创建。在配置元数据中必须提供以下信息:
- 如何创建一个bean
- Bean的生命周期的详细信息
- Bean的依赖关系
通常使用XML文件来实现Bean的配置。XML配置文件的根元素是<beans>,<beans>元素中有很多<bean>子元素,每个<bean>子元素都定义了一个Bean,并且描述了该Bean应该被如何配置到Spring容器中。
<bean>元素提供了一系列属性用于描述Bean的信息:
- class:指定用于创建bean的类的全限定名。
- id:指定唯一的bean标识符。
- scope:指定由特定bean定义创建的对象的作用域。
- lazy-init:是否延时加载(需要使用时(getBean())才加载),默认为false。
- init-method:指定对象初始化方法
- destroy-method:指定对象销毁方法
<bean>元素包含配置依赖的子元素,子元素中基本数据类型的传递使用属性value,对象的传递使用属性ref。
<property>:用于通过属性注入的方式设置值。
public class TextEditor {
private SpellChecker spellChecker;
public void setSpellChecker(SpellChecker spellChecker){
this.spellChecker = spellChecker;
}
}
<bean id="textEditor" class="com.tutorialspoint.TextEditor">
<property name="spellChecker" ref="spellChecker"/> <!-- name为唯一标识,必不可少 -->
</bean>
<contrustor-arg>:用于自动寻找Bean的构造函数,并在初始化时将设置的参数注入。
<bean id="textEditor" class="cpm.tutorialspoint.TextEditor">
<constructor-ag rer="spenChecker"/>
</bean>
<bean id="textEditor" class="com.tutorialspoint.TextEditor">
<constructor-arg name = "spellChecher" ref="spellChecker"/>
</bean>
注意:<contrustor-arg>可以省略name的指定,但参数声明的顺序必须严格与构造器参数的顺序保持一致。而当指明name时,则不需遵照构造器参数的顺序。
Spring提供Bean定义继承机制,子Bean定义可以继承父定义的配置数据,也可以重写一些值或者添加其他值。
使用父属性(parent)来指定父Bean作为该属性的值,从而表示子Bean的定义。
class HelloWorld {
private String messagel;
private String message2;
}
class HelloIndia extends HelloWorld {
private String message3;
}
<bean id="helloWorld" class="com.tutorialspoint.HelloWorld">
<property name="message1" value="Hello World!"/>
<property name="message2" value="Hello Second World!"/>
</bean>
<bean id="helloIndia" class="com.tutorialspoint.HelloIndia" parent="helloWorld">
<property name="message1" value="Hello India!"/> <!-- 重写 -->
<property name="message3" value="Namaste India!"/> <!-- 添加 -->
</bean>
2、注入集合
Spring 可以传递Java Collection类型List,Set,Array,Map和Properties。集合类型的传递需要使用下面Spring提供的五个标签:
- <list>:它可以注入一列值,允许重复。
<property name="addressList"> <list> <ref bean="address1"/> <ref bean="address2"/> <value>Pakistan</value> </list> </property>
- <set>:它可以注入一组值,但不能重复。
<property name="addressSet"> <set> <ref bean="address1"/> <ref bean="address2"/> <value>Pakistan</value> </set> </property>
- <array>:它可以注入一列值,允许重复。
<property name="addressArray"> <array> <ref bean="address1"> <ref bean="address2"> <value>Pakistan</value> </array> </property>
- <map>:它可以用来注入名称-值对的集合,其中名称和值可以是任何类型。
<property name="addressMap"> <map> <entry key="one" value="INDIA"> <entry key ="two" value-ref="address1"/> <entry key ="three" value-ref="address2"/> </map> </property>
- <props>:它可以用来注入名称-值对的集合,其中名称和值都是字符串类型。
<property name="addressProp"> <props> <prop key="one">INDIA</prop> <prop key="two">Pakistan</prop> <prop key="three">USA</prop> <prop key="four">USA</prop> </props> </property>
对于基本数据类型,使用<value>元素或value属性,对于对象,使用<ref>元素或者ref。