一、IoC
-
IOC(Inverse of Control):控制反转,它不是什么技术,而是一种设计思想。在Java开发中,IoC意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。
-
下面从哪些方面被反转了来理解IoC?
1、谁控制谁?为什么叫反转? ------ IoC容器控制,而以前是应用程序控制,所以叫反转
2、控制什么? ------ 控制应用程序所需要的资源(对象、文件……)
3、为什么控制? ------ 解耦组件之间的关系
4、控制的哪些方面被反转了? ------ 程序的控制权发生了反转:从应用程序转移到了IoC容器。 -
IoC容器特点
无需主动new对象;由IoC容器创建。
不需要主动装配对象之间的依赖关系,而是描述需要哪个服务(组件),IoC容器会帮你装配(即负责将它们关联在一起),被动接受装配;
主动变被动,好莱坞法则:别打电话给我们,我们会打给你;
迪米特法则(最少知识原则):不知道依赖的具体实现,只知道需要提供某类服务的对象(面向抽象编程),松散耦合,一个对象应当对其他对象有尽可能少的了解,不和陌生人(实现)说话
IoC是一种让服务消费者不直接依赖于服务提供者的组件设计方式,是一种减少类与类之间依赖的设计原则。
二、DI
-
DI(Dependency Injection):依赖注入,用一个单独的对象(装配器)来装配对象之间的依赖关系 。
-
理解DI问题关键
谁依赖于谁? ------- 应用程序依赖于IoC容器
为什么需要依赖? ------- 应用程序依赖于IoC容器装配类之间的关系
依赖什么东西? ------- 依赖了IoC容器的装配功能
谁注入于谁? ------- IoC容器注入应用程序
注入什么东西? ------- 注入应用程序需要的资源(类之间的关系)。 -
DI优点
帮你看清组件之间的依赖关系,只需要观察依赖注入的机制(setter/构造器),就可以掌握整个依赖(类与类之间的关系)。
组件之间的依赖关系由容器在运行期决定,形象的来说,即由容器动态的将某种依赖关系注入到组件之中。
依赖注入的目标并非为软件系统带来更多的功能,而是为了提升组件重用的概率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不用关心具体的资源来自何处、由谁实现。
4.DI注入方式
- 构造器注入
首先,定义一个Person类
public class Person {
String name;
int age;
Person(String name,int age){
this.name=name;
this.age=age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
配置文件,Beans标签下加入如下内容
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="xiaojun" class="main.Person">
<constructor-arg index="0" value="xiaojun"></constructor-arg>
<constructor-arg index="1" value="12"></constructor-arg>
</bean>
<bean id="xiaohong" class="main.Person">
<constructor-arg index="0" value="xiaohong"></constructor-arg>
<constructor-arg index="1" value="11"></constructor-arg>
</bean>
</beans>
测试代码
import main.Person;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class PersonTest {
public static void main(String[] args){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-config.xml");
Person person = (Person) applicationContext.getBean("xiaojun");
System.out.println(person.toString());
}
}
结果如下
Person{name=‘xiaojun’, age=12}
属性注入
在刚才Person类基础上添加set方法(这个是必须的)
public class Person {
String name;
int age;
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public void setName(String name) {
this.name=name;
}
public void setAge(int age) {
this.age = age;
}
}
配置文件如下
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="xiaojun" class="main.Person">
<property name="name" value="小山"></property>
<property name="age" value="22"></property>
</bean>
</beans>
使用上面的测试方法可得如下结果
Person{name=‘小山’, age=22}
接口注入
定义一个Clothes的枚举类
public enum Clothes {
coat,
T_shirt,
skirt,
jeans,
}
不同的人穿不同的衣服,定义一个穿衣服的接口
public interface WearClothes {
public void wear(Clothes clothes);
}
穿衣服接口的实现类
public class WearClothesImlp implements WearClothes{
Clothes clothes;
@Override
public void wear(Clothes clothes) {
this.clothes=clothes;
System.out.println("People wear "+ clothes);
}
}
通过示例发现,这个接口注入挺像set注入的,只不过把set改名了。
因为WearClothesImlp类依赖于WearClothes接口,增加了耦合性。所以Spring不支持接口注入。
三、IoC和DI由什么关系呢?
其实它们是同一个概念的不同角度描述,由于控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以2004年大师级人物Martin Fowler又给出了一个新的名字:“依赖注入”,相对IoC 而言,“依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”。
四、为什么会出现IoC和DI ?
通常我们在一个类的方法内使用另一个类方法,这时候就需要在类方法内创建对象,然后使用该方法。这样会增加该类对另一类的耦合性。在编程中我们一般建议高内聚,低耦合。所以,当我们使用IoC和DI后,就会发现问题迎刃而解。
Spring框架正式采用这种方法,我们把创建对象的任务交给IoC容器,由IoC容器代替我们进行创建对象的工作。我们无需关系对象设么时候创建,只需要创建配置文件的容器对象,我们在使用时用getBean方法获取到该对象就可以了。