Spring的核心是IOC(Inverse of Control 控制反转)和AOP(Aspect Oriented Programming 面向切面编程),那么如何来理解IOC呢,这里做一个最最通俗易懂的例子来说明为什么要用IOC,以及IOC到底是怎么实现的。 比如我们新创建了一个动物园,这个动物园里有猴子,按照我们原先创建类的方式,这个动物园的创建的方式是这样的:
public class Monkey { public void say(){ System.out.println("Monkey say ..."); } } public class Zoo { public void AnimalSay(){ //直接创建猴子对象 Monkey monkey = new Monkey(); monkey.say(); } }可以看到创建动物园时,直接创建了猴子对象,这样就造成类与类之间产生了一定的耦合性。在真实的项目中这种类与类之间紧密的耦合性会造成代码的难以理解,复用性不高。只要猴子类编写出现了错误,这段代码就会出现问题。但是完全解耦的代码又是不现实的,很多功能需要贴合在一起才能产生作用。那么如何来做呢?我们引入接口,做一些修改:
//定义一个动物的接口 public interface Animal { public void say(); } //猴子实现这个接口 public class Monkey implements Animal { public void say(){ System.out.println("Monkey say ..."); } } public class Zoo { public void AnimalSay(){ //引入动物接口创建对象 Animal animal=new Monkey(); //接口来实现方法 animal.say(); } }于是当动物园创建时,不用考虑是哪一种具体的动物,Animal接口可以接受任何动物对象,比如也会有猫、狗,只要实现接口即可。 事情好像顺了一点,原来复杂的关系解开了一些,但是原来动物园创建时只与猴子类有关,现在多出一个动物接口,似乎变得更加复杂。如何能让动物园创建时与具体的猴子、狮子、老虎脱离关系呢?于是对动物园的创建方式进行修改:
public class Zoo { //注入动物,以接口为接收类型 private Animal animal; //将具体的对象传入群 public Zoo(Animal animal){ this.animal=animal; } //以接口实现方法 public void AnimalSay(){ animal.say(); } }可以看到,动物园里已经完全找不到猴子对象了,当动物园创建时会根据构造函数传入的动物类型进行响应。于是动物园与猴子成功解耦。但是谁来传入猴子或者狮子或者老虎这些具体的对象呢?当然是管理员了。在创建动物园时,管理员做的工作实际上是这样的:
//创建猴子对象 Monkey monkey=new Monkey(); //将猴子、狮子、老虎对象注入到动物园 Zoo zoo = new Zoo(monkey); zoo.AnimalSay();有了管理员,动物园与具体动物可以完全解耦,这些类可以单独进行复用,实现了最大的自由度,这实际上是一种对象创建权利移交的过程,一开始创建对象的权利属于动物园,后来属于管理员,按照这个逻辑,管理员上头是经理,经理上面是总经理…,最大的那个上级是董事长,那么在Spring框架内这个董事长是谁呢?就是Spring容器。IOC(控制反转)的意思就是将具体对象创建的控制权转交给Spring容器。在spring框架内创建动物园实际是这样做的,我用springboot框架的注解方式实现:
//接口 public interface Animal { public void say(); } //实现接口,并将该Monkey类放入spring容器中,并取名为monkey @Service("monkey") public class Monkey implements Animal { public void say(){ System.out.println("Monkey say ..."); } } //表明将Zoo类注入到Spring容器中 @Component public class Zoo { //将Monkey类从容器中取出,注入到Zoo类中 @Autowired //有多个接口实现类时,注入名为monkey的那个 @Qualifier("monkey") private Animal animal; public void AnimalSay(){ animal.say(); } } //测试 //@RunWith与@SpringBootTest表示引入Spring环境 @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) class SpringIocDemoApplicationTests { //从Spring容器中取出Zoo,注入到此测试类中 @Autowired private Zoo zoo; //测试zoo中的AnimalSay()方法 @Test void contextLoads() { zoo.AnimalSay(); } }可以看到,全程没有new 出一个对象,全部用注入的方式。这表示在容器启动时,Spring根据配置文件的描述信息,自动实例化Bean(也就是放入容器中的类)并完成依赖关系的装配,从容器中即可返回准备就绪的Bean实例,后续可直接使用。上面的代码可以看到,Zoo以及Zoo中的Animal都没有进行实例化(也就是创建对象),而是在测试时自动到Spring容器中去取相应的对象,这说明Spring容器控制了创建对象的这个权利,实现了IOC(控制反转)。