面试中经常被问到的一个问题就是,说下什么是ioc和di?
然后我想很多小伙伴们都讲不清楚吧,百度一下这个问题,总结起来的答案就是:
IOC是将对象的创建权交给Spring 容器
DI依赖于IOC容器,负责实现对象依赖关系和创建,不需要通过new来实现
其实百度的这类答案并不是很对,实际上更确切的说法是:
IOC(Inversion of Control 控制反转)是将对象的创建权交给Spring 容器,是一种设计原则,可以用来减低计算机代码之间的耦合度。
DI(Dependency Injection 依赖注入)则是IOC的一种实现方式。
其实IOC的实现方式并不只有DI一种,还有一种方式叫“依赖查找”(Dependency Lookup),小伙伴们可以自行百度,包括还有其他方式。
所以我们为什么要使用spring ioc呢?
在日常程序开发过程当中,我们推荐面向抽象编程,面向抽象编程会产生类的依赖,当然如果你够强大可以自己写一个管理的容器,但是既然spring以及实现了,并且spring如此优秀,我们仅仅需要学习spring框架便可。
当我们有了一个管理对象的容器之后,类的产生过程也交给了容器,至于我们自己的app则可以不需要去关系这些对象的产生了。
可能现在的开发中大部分都是基于spring boot开发,我们在一个类中引入其他类的实例的时候基本都是通过@Autowired
等注解或者在config
类中添加@Bean
注解注入对象,我们并不需要关心这些对象之间的依赖关系,现在让我们通过最原始的spring mvc来了解一下spring是怎么进行依赖注入的吧。
首先我们新建一个项目,项目结构如下:
项目结构很简单,注意,使用ioc需要引入spring-context依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.1.1.RELEASE</version>
</dependency>
类代码如下:
public class IndexDaoImpl implements IndexDao {
public void test() {
System.out.println("daoImpl");
}
}
public class IndexService {
private IndexDao indexDao;
public void service(){
indexDao.test();
}
}
spring.xml
我们先不做任何配置,使用Test
类来测试下代码:
public class Test {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");
IndexService service = (IndexService) applicationContext.getBean("service");
service.service();
}
}
这里我们通过ClassPathXmlApplicationContext
读取spring.xml
配置文件来获取相应的bean对象,因为这里没有写任何配置,所以这里是获取不到IndexService
对象的。
同时我们需要清楚的是,现在项目中没有创建任何对象,包括IndexDaoImpl
和IndexService
,我们再了解一下这两个类之间的依赖关系,这里的IndexService
中引入了IndexDao
,所以我们称,IndexService
依赖IndexDao
,这点小伙伴们应该清楚吧,不清楚的话现在清楚了,所以在spring中,我们需要体现这两者的依赖关系,用传统的xml方式,我们在spirng.xml
中添加如下配置:
<?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="dao" class="IndexDaoImpl"></bean>
<bean id="service" class="IndexService">
<property name="indexDao" ref="dao"></property>
</bean>
</beans>
这里我们首先在xml中通过配置两个bean,然后IndexService
需要依赖IndexDao
,所以我们在service的bean中添加配置注入indexDao属性,我们知道属于set注入方式,所以这里大家会发现service
中indexDao
属性这时候是报红的,因为在IndexService
中缺少indexDao的set方法,我们修改IndexService
代码如下 :
public class IndexService {
private IndexDao indexDao;
public void service(){
indexDao.test();
}
public void setIndexDao(IndexDao indexDao) {
this.indexDao = indexDao;
}
}
这里我们添加了set方法实例化indexDao,让我们再来运行下Test代码,结果如下:
可以看到,我们通过xml配置文件的方式注入了IndexService
和IndexDaoImpl
对象并添加了两者的依赖关系。如果我们在xml配置文件中去掉两者的依赖关系,配置文件如下:
<?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="dao" class="IndexDaoImpl"></bean>
<bean id="service" class="IndexService">
</bean>
</beans>
这时候我们再运行Test代码,结果如下:
可以看到,这时候IndexService
类中的IndexDao
对象为空。
上面我们是用的set方式注入对象属性(这里指IndexService
中的IndexDaoImpl
对象),我们还可以通过构造方式注入对象属性,修改xml配置文件如下:
<?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="dao" class="IndexDaoImpl"></bean>
<bean id="service" class="IndexService">
<constructor-arg ref="dao"></constructor-arg>
</bean>
</beans>
这里我们同样需要在IndexDaoImpl
添加相应的构造方法:
public IndexService(IndexDaoImpl indexDao) {
this.indexDao = indexDao;
}
运行结果如楼上所示。
所以如果下次面试官问你,spring的注入方式有哪几种,你会怎么说?你可能会说,有两种,一种是set注入,一种构造器注入。
你要真这么回答,那你真得自杀了。要知道,上面说的只是对象属性的注入方式而已,事实上,对象属性可不仅仅只有对象,其它像所有基本类型,集合等,好像只要是属性都可以在xml中配置吧,但是一般情况下,像一个普通的字符串、集合什么的,我们是不会在xml事先定义好值的吧,大家仔细想想是不是这样。
如上所示,我们是通过xml配置文件中注入对象同时维护了对象间的依赖关系,spring一共给我们提供了三种依赖注入的方式:
- schemal-based-------xml
- annotation-based-----annotation
- java-based----java Configuration
第一种是通过xml配置文件方式,第二种就是注解的方式,第三种就是配置类的方式。
了解spring的小伙伴对上面三个概念应该不会陌生吧。
所以我们再给你一次机会,spring的注入方式有哪几种?你可以这么回答:
在spring中,提供了三种注入对象的方式,我们可以通过xml配置文件,注解或者配置类的方式注入对象,在xml配置文件中,我们可以通过set或者构造器方式注入对象属性,在spring boot中,我们只需要通过注解或者配置类的方式就可以完成对象的注入及依赖关系。
同时,以上三种注入方式可以同时使用,这里有些复杂,后面有机会再说吧。
可能有些小伙伴们会说,属性的注入方式并不只有两种,还有什么工厂方式等各种奇奇怪怪姿势的,其实像这些旁门左类的说法完全可以不管,就两种就完事了。面试官要说你就直接怼回去,自信点。
拓展
@Autowired和@Resource区别
对象的注入,@Autowired默认是按类型注入的,如果按类型没有找到,则会根据名称注入,如果都没有找到就会报错。
@Resource是根据名称注入的,也可以通过type属性指定注入类型