IOC与DI理解
IOC(Inversion Of Control):我的理解是在某个类(A)聚合另外一个类(B)的时候,由原本在当前类中去直接实例化这个类(类似privat B b = new C())变成由容器提供,按需分配。这样不必在当前类中强耦合C类,这样就可以在不改变原有代码的基础上增加实现类(D),大大提高了项目的扩展性。
DI(Dependency Injection):我的理解是IOC的一种实现方式,容器提供是由setter或者constructor来实现,通过这两种方式注入你依赖的实现类,降低了项目的耦合度,并提高了扩展性。
代码演示理解
A类:PersonService
public class PersonService {
private PersonDao personDao = new PersonDaoOracleImpl();
public Person getPerson() {
return personDao.getPerson();
}
}
B类:PersonDao
public interface PersonDao {
Person getPerson();
}
C类:PersonDaoOracleImpl
public class PersonDaoOracleImpl implements PersonDao {
@Override
public Person getPerson() {
return new Person(0, "Oracle", true);
}
}
测试类
public class IocTest {
public static void main(String[] args) {
PersonService personService = new PersonService();
System.out.println(personService.getPerson());
}
}
输出结果:Person{id=0, name='Oracle', male=true}
在以上的例子中假设使用的是Oracle数据库,那么我现在想更换为Mysql数据库的话,是不是应当去修改PersonService中new PersonDaoOracleImpl()
这部分,这就意味着你需要去改动已有的代码。如果你PersonService这段代码是在Jar文件里,那你岂不是还还没法改成Mysql数据库了呢,这是不是代表扩展性很差了呢。
设计原则中有特别重要的一条就是“开闭原则”:“对修改关闭,对扩展开放”,那我们这个设计的时候就违 背了这个重要的原则。
如果我将PersonService做一些调整(添加构造方法和set方法二选一即可):
public class PersonService {
private PersonDao personDao;
public PersonService(){}
// 第一种方式,construct注入personDao
// public PersonService(PersonDao personDao){
// this.personDao = personDao;
// }
// 第二种方式,set注入personDao
public void setPersonDao(PersonDao personDao){
this.personDao = personDao;
}
public Person getPerson() {
return personDao.getPerson();
}
}
我们在修改一下测试类(添加构造方法和set方法二选一即可,保持与上边一致):
public class IocTest {
public static void main(String[] args) {
// 第一种方式,construct注入personDao
// PersonService personService = new PersonService();
// PersonDao personDao = new PersonDaoOracleImpl(personService);
// 第二种方式,set注入personDao
PersonService personService = new PersonService();
PersonDao personDao = new PersonDaoOracleImpl();
personService.setPersonDao(personDao);
System.out.println(personService.getPerson());
}
}
结果:Person{id=0, name='Oracle', male=true}
这时我们更换为Mysql数据库应该怎么操作呢?是不是只需要将测试类中的PersonDaoOracleImpl
实现改为PersonDaoMysqlImpl
实现就可以了呢,这样就不用去动原有的代码,也不用担心它是Jar文件的内容,直接去新定义实现类来进行数据库切换,是不是代表了扩展性变好了呢。
D类:PersonDaoMysqlImpl
public class PersonDaoMysqlImpl implements PersonDao {
@Override
public Person getPerson() {
return new Person(1, "Mysql", false);
}
}
结果:Person{id=1, name='Mysql', male=false}
概念个人总结
以上就解释了容器提供的实现方式,“提供”也就是依赖注入,你依赖什么样的实现我就给你注入什么样的实现,那容器是从哪来的呢?以上的示例代码中并没有容器,提供的实现类也是我们手动new在测试类里的,但是Spring就不一样了,它实现了容器的机制,也就是IOC容器:
容器,顾名思义就是装东西的,并对外部提供东西的。那Spring这个IOC容器装什么?提供什么呢?通过上述的描述,我们已经知道了容器提供的实现方式,但是我们缺少容器提供的提供方啊,由此推断IOC容器是用来装各个实现类的,也称为装B(bean 可以理解为需要由Spring管理的类)的。
IOC
与IOC容器
是两个概念,前者代表的是一种设计思想,后者代表的是一个容器,不要混淆。
最后我个人是这样总结的:IOC这种设计思想,是由IOC容器使用DI的方式提供来实现的