SpringIOC容器 详解SpringIOC容器相关知识
IPostYellow 人气:0一、前言
IOC控制反转,不是一种技术,而是一种设计思想,就是将原本在程序中手动创建对象的控制权,交给Spring框架来管理。
区别:
- 没有IOC的思路:若要使用某个对象,就必须自己负责去写对象的创建
- IOC的思路:若要使用某个对象,只需要从Spring容器中获取需要使用的对象,不关心对象的创建过程,也就是把创建对象的控制权交给了Spring框架。
- 好莱坞法则:Don't call me, I 'll call you
举例说明:
做菜,做蒜薹炒猪肉
你有两种做法:
第一种,自己养猪,然后种蒜薹。等到猪长大了,你就可以杀猪,蒜薹成熟了,就收割。然后开始炒,做成了蒜薹炒猪肉。
第二种,从农贸市场获取猪和蒜薹,拿回来直接炒,做成了蒜薹炒猪肉。
此时的IOC就相当于这个农贸市场,我要做菜,我去农贸市场拿过来就可以了,而不需要自己去弄。为什么要Java对象放到容器里?因为我们要做到拿来即用,便于管理。那你能管理农贸市场吗?你不能,那谁来管农贸市场?Spring!这就是控制反转IOC,我们把控制权交给了Spring框架,他来帮我们管这个农贸市场,他来养猪,他来种菜。我们只需在要菜的时候,去市场买就好了。
再举一个例子
过年了,想要给家里打扫个卫生,你想请几个钟点工来打扫。也有两种做法。
第一种:自己主动找,找身边人看看谁认识钟点工,你自己打电话邀约,谈价格
第二种:直接找家政公司,直接提出需求即可。
第一种方式就是我们自己创建对象的方式,自己主动new几个钟点工。而第二种就是spring给我们提供的IOC方式,家政公司就是一个容器,能给我提供很多的服务,钟点工对象是spring帮我们创建的。
又过了几天,我又想给厨房的油烟机清理一下,也能直接打电话给家政公司,提出需求。
那上述例子中的农贸市场和家政公司哪里来啊?
我们可以自己构建,就像自己成立一个公司一样。具体在程序中表现为:
1.使用配置文件或者注解的方式定义一下我们自己容器里存放的东西。
或者去别人的公司里找。具体在程序中表现为:
2.一定有很多人创建了自己的公司,这些服务都可以集成在我们自己的容器里,为我们提供强大的功能,比如spring自带很多的template模板类。
二、IOC原理实战
首先在pom.xml文件中加入spring的相关jar包。
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.0.RELEASE</version> </dependency> </dependencies>
我们定义我们的接口和实现类
// UserDao接口 public interface UserDao { void getUser(); } // UserDao实现类1,mysql实现 public class UserDaoImpl implements UserDao { public void getUser() { System.out.println("mysql实现"); } } // UserDao实现类2,oracle实现 public class UserDaoImpl implements UserDao { public void getUser() { System.out.println("oracle实现"); } }
然后我们的业务实现类,在不使用set注入的情况下,是这样的:
//业务接口 public interface UserService { void getUser(); } //业务实现类 public class UserServiceImpl implements UserService { //传统的方法中,如果这边要改变,那就必须将这里的语句改变才可以 private UserDao userDao = new UserDaoImpl(); public void getUser() { userDao.getUser(); } }
对应的测试类:
public class MyTest { public static void main(String[] args) { //用户实际调用的是业务层,不需要接触dao层 UserServiceImpl userService =new UserServiceImpl(); userService.getUser(); } }
但是你会发现使用这种方法如果我在测试这里想用oracle实现,那就必须新增一个业务实现类或者修改我原本的业务实现类,违反了开闭原则。
所以我们的业务实现类要使用set方法动态注入我们的UserDao实现类。
public class UserServiceImpl implements UserService { private UserDao userDao; // 利用set进行动态实现值的注入 public void setUserDao(UserDao userDao) { this.userDao = userDao; } public void getUser() { userDao.getUser(); } }
如此一来只需要在测试类中通过set方法,传入对应的实现类对象,就可以实现调用不同的实现对象的getUser方法。
public class MyTest { public static void main(String[] args) { // 利用set注入的方法,我们可以不需要修改service中的代码,从而实现多个不同对象的getUser方法 UserServiceImpl userService = new UserServiceImpl(); userService.setUserDao(new UserDaoImpl()); userService.getUser();//mysql实现 userService.setUserDao(new UserDaoOracleImpl()); userService.getUser();//oracle实现 } }
这两种模式的区别可以发现。之前,控制UserDao实现类的控制权,在程序员手上,程序员写在UserServiceImpl里,写死了对应的是实现类,如果要修改的话,程序员就必须去修改对应的代码。而后面这种方法,控制UserDao实现类的控制权,就已经不在程序员手上了。现在程序是被动接收对象,然后动态set注入实现了可以随意使用不同的实现类的getUser方法。
这其实就是一种控制反转IOC的原型。这种思想从本质上解决了问题,程序员不用再去管理对象的创建了。系统的耦合性大大降低。可以更加专注的在业务的实现上。spring的底层全部都是基于这种思想去实现的。
三、IOC本质
像上图所示,IOC本质上就是把左边变成了右边。本来是业务层里程序员写来主动决定调用的下面的Mysql还是Oracle,但是现在通过IOC,可以把主动权交给用户,让用户想用Mysql用Mysql,想用Oracle就用Oracle。
DI(依赖注入)是实现IOC的一种方法,在没有IOC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码再程序中,对象的创建由程序自己控制(也就是程序员自己写),控制反转(IOC)后将对象的创建移交给第三方了,控制反转的这个反转说的就是获得依赖对象的方式反转了。
采用XML配置方式配置Bean的时候,Bean的定义信息和实现是分离的,而采用注解的方式的时候两者是合为一体的,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目睹。
控制反转是一种通过描述(XML或者注解)并通过第三方去生产或获得特定对象的方式。在Spring中实现控制反转的是IOC容器,其实现方式是依赖注入(Dependency Injection,DI)
四、spring helloworld
找到1.2.2实例化容器部分,发现了其配置文件格式:
首先创建我们的实体类Hello:
package com.hj.pojo; public class Hello { private String str; public String getStr() { return str; } public void setStr(String str) { this.str = str; } @Override public String toString() { return "Hello{" + "str='" + str + '\'' + '}'; } }
然后根据文档中所述,在resources文件下创建beans.xml文件来使用spring创建对象。beans.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"> <!--使用spring来创建对象,在spring中这些都称为bean bean = 对象 相当于 new Hello(); 正常是 类型 变量名 = new 类型(); Hello hello = new Hello(); 利用bean来实现,id就是变量名,class就是我们对象的类型 里面的property相当于给对象中的属性设置一个值。 --> <bean id="hello" class="com.hj.pojo.Hello"> <!-- ref:引用spring容器中创建好的对象 value:具体的值,基本数据类型 --> <property name="str" value="Spring"/> </bean> </beans>
再次查看官方文档,查询如何使用容器。
可以看到需要借助一个工厂来读取bean的定义并进行访问,然后创建对象。
import com.hj.pojo.Hello; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest { public static void main(String[] args) { //获取spring的上下文对象 ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); //我们的对象现在都在spring中管理了,我们要使用,直接去取出来就可以了 Hello hello = (Hello) context.getBean("hello"); System.out.println(hello.toString());//Hello{str='Spring'} //思考? //Hello对象是谁创建的?是由Spring创建的 //Hello对象的属性是怎么设置的?是由Spring容器设置的 } }
这个Hello对象由spring创建并且由spring容器设置属性的过程就是控制反转。
五、小结
控制:谁来控制对象的创建,传统的应用程序的对象是由程序本身控制创建的,使用spring后,对象是由spring来创建的。
反转:程序本身不创建对象,而变成被动的接收对象
依赖注入:就是利用set方法来进行注入
IOC是一种编程思想,由主动的编程去变成被动的接收。
我们回头看Hello类里左边有个豆子的标志了,这说明这个类已经被Spring托管了。
所谓的IoC,一句话来概括:对象由spring来创建,管理和装配。
加载全部内容