什么是IOC
IOC(Inversion Of Control)的意思是控制反转,它的思想是从容器的角度出发,容器控制应用程序,由容器反向的向应用程序注入应用程序所需的外部资源,说白了就是把对象的控制权交给容器来管理。实现方式就是将new对象这个步骤交给容器来做,对象里面的属性的赋值也是交给容器来注入。这里大家肯定都想到了,IOC就是通过反射来实现的。
这里我想强调一下,IOC和DI(Dependence Injection)是什么关系。本质上说他们描述的是一个事情,只是IOC是从程序的角度出发,将new对象这个事交给了容器,所以叫依赖注入。而DI则是从容器出发,将各种依赖通过容器注入,以达到解耦和的目的。
IOC的优缺点
优点:
1、把对象的创建和依赖关系定义在了XML文件中,我们改变子类的实现变得异常简单。
2、控制反转减轻了对象之间的耦合度,减轻了对象之间的依赖关系,增加了系统的灵活性,可维护性,以及可移植性等等。
缺点:
1、生成对象的方式变复杂了(事实上操作还是挺简单的),对于不习惯这种方式的人,会觉得有些别扭和不直观。
2、创建对象因为使用了反射技术,在效率上有些损耗。但相对于IOC提高的维护性和灵活性来说,这点损耗是微不足道的,除非某对象的生成对效率要求特别高。
Spring中IOC的实现
Spring中实现IOC有五种注入方式
Setter注入
这种方式需要被注入的JavaBean的属性需要有set方法。
1、被注入类:
1 | package com.aowin.testspring.di; |
这个JavaBean中有四个属性,数据类型分别是Integer、String、Date(ps:为什么用Integer不直接用int呢?因为如果用int的话,jvm迟早会将int自动装箱,那我们为什么不一开始就用Integer来提高程序执行的效率呢。)和接口interfaceA
2、在配置文件中:
1 | <!-- Setter注入 --> |
在配置文件中,我们一一将ClassB对象所需的资源通过property标签注入进去。
3、测试方法:
1 | @Test |
ClassPathXmlApplicationContextl类是Spring提供的类,它有一个getBean()方法,我们只需要传入前面配置的id值,就可以得到对象。我们是将配置文件放在了src/main/resources下,所以直接用ClassPathXmlApplicationContextl来读取配置文件,如果你的配置文件放在别的路径下,可以使用FileSystemXmlApplicationContext类,然后通过绝对路径来读取配置文件。
4、执行结果:
可以看到程序并没有报空指针异常,并且对象的属性值也成功的被注入。
interface注入
1、我们先写一个interfaceA接口的实现类ClassA:
1 | package com.aowin.testspring.ioc; |
ClassB类不做改动。
2、在配置文件中:
1 | <!-- interface注入 --> |
先配置一个interfaceA实现类,在配置ClassB的时候,将配置的ClassA通过ref属性注入进去。
3、测试方法:
1 | @Test |
4、运行结果:
可以看到我们成功的将ClassA注入到了ClassB中。但是遗憾的是,接口注入和属性注入的功能差不多,但接口使得项目的类数量变多,现如今不推崇使用接口注入。
constructor注入
构造器注入。构造器注入需要JavaBean有对应的构造方法才能使用这种方式。
ClassB有一个带全部参数的构造方法,在这里我们不做改动。
1、在配置文件中:
1 | <!-- constructor注入 --> |
可以看到,在配置文件中,我们不再是像Setter注入一样,用property标签,而是使用constructor-arg标签来配置。在constructor-arg标签中,index表示的是构造方法参数的索引下标(从0开始)。
2、测试方法:
1 | @Test |
3、执行结果:
可以看到,我们通过构造方法配置方式将所有的属性值都成功注入。
AutoWire注解
AutoWired注入方式可以不用写set方法。我们写三个类来模拟Autowired注入。
ClassE:
1 | package com.aowin.testspring.di; |
ClassD:
1 | package com.aowin.testspring.di; |
ClassE:
1 | package com.aowin.testspring.di; |
ClassE中有两个属性分别是ClassC和ClassD。属性上都加了注解@Autowired这个注解是Spring提供的。
1、在配置文件中:
1 | <context:annotation-config /> |
首先我们需要先开启Autowired模式,用context:annotation-config标签。然后直接配置bean,但是id值需要和属性名相同。autowire默认是安卓byType方式匹配。autowire的值可以是byName、byType、constructor、autodetect、no他们的功能如下:
2、测试方法:
1 | @Test |
3、运行结果:
可以看到程序并未报空指针异常。这种配置方式使我们不需要再写set方法了,节省了我们的时间。
Resource注解
@Resource并不是spring提供的,由J2EE提供,@Resource默认按照ByName自动注入,需要导入包javax.annotation.Resource。
@Resource有两个重要的属性:name和type,而Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以,如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不制定name也不制定type属性,这时将通过反射机制使用byName自动注入策略。
1、我们写一个新类ClassF:
1 | package com.aowin.testspring.di; |
@Resource注解设置了name属性,是为了后面匹配用。这里的InterfaceA类型是延用的前面的interfaceA接口。之前我们还写了它的实现类ClasssA。
2、配置文件中:
1 | <bean id="classa" class="com.aowin.testspring.ioc.ClassA"></bean> |
首先我们要开启注解,同样通过context:annotation-config标签来开启。id的值classa和classA需要和类中的name属性值对应。
3、测试方法:
1 | @Test |