IOC控制反转和DI依赖注入

以前一直听说控制反转和依赖注入,一直不知道是什么,不理解。现在懂了

举个例子,我一个人想要穿衣服,用代码实现怎么做呢?

衣服类 衣服=new 衣服类();
人类 人=new 人类();
人.set衣服=衣服();

大概就是这样子,衣服有一个类,人有一个类,人这个类里面有一个穿衣服的方法set衣服,所以,为了实现一个人穿衣服的功能,我写了3个东西

  1. 创建衣服类
  2. 创建人类
  3. 人类依赖的衣服对象,我手动的设置赋值了(赋值就是注入)

这个不难理解吧,那么控制反转IOC到底是干嘛的呢?就是不需要我们手动的去创建类了,也不需要我们手动的给类的依赖进行赋值了也就是注入,用了IOC,代码就会变成这样

人类 人=IOC容器.get人类();

没了...完事!就是这么简单,你可能会问,衣服类不需要创建?不需要,IOC容器知道人类依赖衣服类,自己创建完成了,你可能会问,人类里面还得set衣服呢,不需要去赋值注入?不需要,IOC容器自动帮你注入了

IOC容器这么好用?我要用!!

这就是控制反转IOC,其本质就是帮你创建一些对象,帮你去注入依赖的对象。

那么依赖注入DI是啥呢?其实就是IOC,这俩是一样的。只不过IOC这个名字听起来侧重于控制反转,就是容器帮你创建对象,DI这个名字听起来侧重于依赖注入,容器帮你注入依赖的对象。

IOC和DI就仿佛于许嵩和Vae一样,不同的名字,一样的人。

IOC实现Hello World

上面搞懂了概念,接下来用代码去实现一下IOC

使用maven导入包

有3个jar包是需要的,如下

  <!--https://mvnrepository.com/artifact/org.springframework/spring-beans-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.1.4.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.1.4.RELEASE</version>
</dependency>
<!--https://mvnrepository.com/artifact/commons-logging/commons-logging-->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>

新建IOC配置文件

在resources目录下新建一个IOC的配置文件applicationContext.xml,等下需要通过这个配置文件去创建IOC容器

<?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="helloWorld" class="com.vae.springboot.bean.HelloWorld">
<property name="userName" value="许嵩"></property>
</bean> </beans>
注意:property标签里面给属性赋值的时候,name一定是字段名,这个字段一定要有set属性,否则无效

新建一个类HelloWorld

内容就写一个输出方法

package com.vae.springboot.bean;

public class HelloWorld {

    private String userName;
public void setUserName(String userName){
this.userName=userName;
} public void sayHello(){
System.out.println("Hello World "+userName);
} }

新建HelloWorld的测试类

这个测试类里面,我们会使用两种方式来输出sayHello方法,一种是普通的方式,一种是IOC方式

package com.vae.springboot.bean;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class)
@SpringBootTest
public class HelloWorldTest { //正常的,我们怎么去调用sayHello方法
@Test
public void sayHelloTest(){
HelloWorld helloWorld=new HelloWorld();
helloWorld.setUserName("许嵩");
helloWorld.sayHello();
//这就是最普遍的做法吧,自己创建对象,自己注入依赖值,啥都是自己干
} //来一个IOC的例子
@Test
public void sayHelloIOC(){
HelloWorld helloWorld=null;
//--------------------IOC开始了-------------------
//1.从classpath路径去寻找配置文件,加载我们的配置
Resource resources= new ClassPathResource("applicationContext.xml");
//2.加载配置文件之后,创建IOC容器
BeanFactory factory=new XmlBeanFactory(resources);
//3.从Spring IOC容器中获取指定名称的对象
helloWorld= (HelloWorld) factory.getBean("helloWorld");
//--------------------IOC结束了---------------------
helloWorld.sayHello();
} }

直接执行sayHelloTest方法,sayHelloIOC方法,结果是一样的,一个是普通的自己去创建对象,去注入依赖,一种是IOC容器帮你创建和注入。可能有人会说,为什么IOC的代码还多......看着IOC也没有简单啊,代码还多......别慌,往下看,到时候绝对惊喜。(现在就想知道的看下面的@Autowired)

先讲解一下上面的代码

BeanFactory:表示Spring IOC容器,专门生产bean对象的工厂,负责配置,创建和管理bean

bean:被Spring IOC容器管理的对象都是bean,可以理解为Spring下皆为bean

Spring IOC容器怎么知道哪些是管理的对象?

  1. xml配置文件
  2. 注解
  3. Java代码

其中xml配置文件的方式我们已经讲了,这也是最简单的方式了,后面的两种方式暂留到时候补在这里

暂留地...

IOC容器是怎么去管理对象的呢?到底是怎么帮助我们去创建对象的,到底是怎么帮助我们去自动注入的?我们在测试类里面去模拟一下IOC的工作原理,使用到的技术有两个,反射和内省

@Test
public void testIOC() throws Exception {
String className="com.vae.springboot.study.bean.HelloWorld";
HelloWorld helloWorld=null;
//--------------------模拟IOC开始了-------------------
//1.使用反射创建对象
Class clzz=Class.forName(className);
Constructor con=clzz.getConstructor();
con.setAccessible(true);//设置构造器可访问性为true
Object obj=con.newInstance(); //2.使用内省机制获取所有的属性名称
BeanInfo beanInfo=Introspector.getBeanInfo(clzz,Object.class);
PropertyDescriptor[] pds=beanInfo.getPropertyDescriptors(); for (PropertyDescriptor pd : pds) {
String propertyName=pd.getName();
if ("userName".equals(propertyName)) {
pd.getWriteMethod().invoke(obj,"蜀云泉");
}
}
helloWorld=(HelloWorld)obj;
//--------------------模拟IOC结束了---------------------
helloWorld.sayHello();
}

执行测试方法,输出结果正是

Hello World 蜀云泉

这不就是和IOC的效果一样嘛,所以我们可以说,IOC的本质就是反射+内省机制

IOC容器getBean方法的三种签名

我们上面的IOC容器有一行是加载Bean的,以便于获取指定的对象,如下:

helloWorld= (HelloWorld) factory.getBean("helloWorld");

这里的helloWorld正是我们xml文件里面的bean的id,这个就叫做签名,有三种方式:

  1. 根据Bean对象在容器中的id来获取

    这个其实就是我们使用的方式,我们可以尝试着把xml文件中bean的id复制一个出来,当有两个id为helloWorld时,就会报错。所以我们Bean的id一定要是唯一的

    <?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="helloWorld" class="com.vae.springboot.bean.HelloWorld">
    <property name="userName" value="许嵩"></property>
    </bean>
    <bean id="helloWorld" class="com.vae.springboot.bean.HelloWorld">
    <property name="userName" value="许嵩"></property>
    </bean> </beans>
  2. 根据类型获取Bean

    这种方式的代码是这样的

     helloWorld= factory.getBean(HelloWord.class);

    根据类的类型获取Bean,连强转都不需要了,当然,这种方式也是有问题的,xml里面再赋值一下,两个bean的id不一样,class一样的时候,还是会报错,报类找到的Bean不是唯一的

    <?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="helloWorld" class="com.vae.springboot.bean.HelloWorld">
    <property name="userName" value="许嵩"></property>
    </bean>
    <bean id="helloWorld2" class="com.vae.springboot.bean.HelloWorld">
    <property name="userName" value="许嵩"></property>
    </bean> </beans>
  3. 根据id+类型来获取Bean

    这个就牛逼了,上面的1和2的综合体啊,妈妈再也不怕我获取Bean对象的时候报错了

    helloWorld= factory.getBean("helloWorld",HelloWord.class);

getBean的三种签名,我们以后就使用第三种。

xml配置文件的import导入

我们的applicationContext.xml里面写的是Bean,一个项目里面那么多需要控制反转的,难道我们写上千个Bean?这样都写在一个xml文件里,太大,也太乱,所以我们可以分开写每个包里面写个自己的xml,然后applicationContext.xml直接import导入就可以了

    <!--导入其他的配置文件-->
<import resource="classpath:com.vae.springboot.bean.HelloWorld.xml"></import>

大概就是这样,直接在拆分的xml文件上单击右键,Copy Reference就可以了,import默认是从classpath下面找的,所以我们加上一个classpath:,加不加都一样,默认就是这个,所以还是加上吧

@Autowired

牛逼的地方来了啊,我们上面写过了好几行的IOC代码,抱怨了IOC这么牛逼还要写好几行代码,现在@Autowired,来了

@Autowired:表示自动按照类型去Spring容器中找到对应的Bean对象,然后自动注入

再来贴一下我们上面写的IOC代码吧

 @Test
public void sayHelloIOC(){
HelloWorld helloWorld=null;
//--------------------IOC开始了-------------------
//1.从classpath路径去寻找配置文件,加载我们的配置
Resource resources= new ClassPathResource("applicationContext.xml");
//2.加载配置文件之后,创建IOC容器
BeanFactory factory=new XmlBeanFactory(resources);
//3.从Spring IOC容器中获取指定名称的对象
helloWorld= (HelloWorld) factory.getBean("helloWorld");
//--------------------IOC结束了---------------------
helloWorld.sayHello();
}

啧啧,惨不忍睹,这样写,好麻烦....来看看注解的方式

@ContextConfiguration("classpath:applicationContext.xml")

先在测试类头上加个这个,意思是找到我们的Spring容器,classpath就是resource目录

然后写测试方法

    @Autowired
private HelloWorld helloWorld;
@Test
public void sayHelloIOCNB(){
helloWorld.sayHello();
}

没了.....就这么简单...@Autowired牛逼(破音)

IOC容器

虽然我们讲过了牛逼的@Autowired,但是弱鸡的还是需要讲一下,ICO容器有俩

  1. BeanFactory:懒,懒得很
  2. ApplicationContext:用这个

我们在上面已经用过BeanFactory了,ApplicationContext这个其实是BeanFactory的一个子接口,我们再看看这俩的方式有啥不同

    @Test
public void sayHelloIOC(){
HelloWorld helloWorld=null;
//--------------------IOC开始了-------------------
//1.从classpath路径去寻找配置文件,加载我们的配置
Resource resources= new ClassPathResource("applicationContext.xml");
//2.加载配置文件之后,创建IOC容器
BeanFactory factory=new XmlBeanFactory(resources);
//3.从Spring IOC容器中获取指定名称的对象
System.out.println("上面的代码没有创建Bean,下面的代码获取Bean的时候才会创建Bean");
helloWorld= (HelloWorld) factory.getBean("helloWorld");
//--------------------IOC结束了---------------------
helloWorld.sayHello();
} @Test
public void sayHelloIOCctx(){
HelloWorld helloWorld=null;
//--------------------IOC开始了-------------------
ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
System.out.println("上面的代码已经创建Bean对象了,下面的获取Bean,获取已有的Bean");
helloWorld= ctx.getBean("helloWorld",HelloWorld.class);
//--------------------IOC结束了---------------------
helloWorld.sayHello();
}

我在打印里面写了,这俩其实是一样的结果,但是BeanFactory懒,你不获取Bean,我就不创建Bean,ApplicationContext比较好,所以IOC容器,我选择ApplicationContext

Bean的作用域

  1. singltton:单例,在IOC容器中的Bean实例,都是唯一的
  2. prototype:多例,在IOC容器中的Bean,每次都返回一个新的对象
  3. ......

作用域有好几个,我这里只介绍两个,一个单例,一个多例的,xml配置如下

    <bean id="helloWorld1" class="com.vae.springboot.bean.HelloWorld" scope="singleton"></bean>
<bean id="helloWorld2" class="com.vae.springboot.bean.HelloWorld" scope="prototype"></bean>

Bean的初始化和销毁

我新建一个类,就叫MyDataSource

package com.vae.springboot.bean;

public class MyDataSource {

    public MyDataSource(){

    }

    public void open(){
System.out.println("初始化");
} public void dowork(){
System.out.println("工作");
} public void close(){
System.out.println("销毁");
}
}

然后新建一个测试类

package com.vae.springboot.bean;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration("classpath:applicationContext.xml")
public class MyDataSourceTest { //最普通的方式
@Test
public void test1(){
MyDataSource myDataSource=new MyDataSource();
myDataSource.open();
myDataSource.dowork();
myDataSource.close();
} @Autowired
private MyDataSource myDataSource;
//IOC容器的方式
@Test
public void test2(){
myDataSource.dowork();
} }

普通方式和IOC容器的方式,都是有初始化和销毁的,原因是我们的xml写了这一行

  <bean id="myDataSource" class="com.vae.springboot.bean.MyDataSource"  init-method="open" destroy-method="close"></bean>

Bean的初始化和销毁,以后就用xml配置吧,就让IOC来管理吧,省事

人衣看DI

我们开头讲了一个人和衣服的例子,这次,我们使用DI(IOC)再来看看,还是这3种方式

  1. xml配置方式
  2. 注解方式
  3. Java代码方式

我们来一个一个的用代码实现一下

xml配置方式(不推荐使用,但是还是看会)

新建一个类,Person

package com.vae.bean;

public class Person {
private Clothes clothes; public void setClothes(Clothes clothes) {
this.clothes = clothes;
} @Override
public String toString() {
return "Person{" +
"clothes=" + clothes +
'}';
}
}

新建一个类,Clothes

package com.vae.bean;

public class Clothes {
public Clothes() {
System.out.println("我是一件衣服,我很方");
}
}

我们的applicationContext.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="clothes" class="com.vae.bean.Clothes"></bean>
<bean id="person" class="com.vae.bean.Person" autowire="byName"></bean> </beans>

bean里的autowire有好几个值,byName和byType都可以用,construct不能用,因为衣服类默认构造器没了

最后,我们的测试类

package com.vae.bean;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration("classpath:applicationContext.xml")
public class PersonTest { @Autowired
private Person person; @Test
public void test(){
System.out.println(person);
}
}

执行一下,效果还是很棒的

如果Clothes类中还有一个类类型的变量,比如叫款式style,可以这样

    <bean id="style" class="com.vae.springboot.study.bean.style"/>
<bean id="helloWorld" class="com.vae.springboot.study.bean.HelloWord">
<property name="userName" ref="style"></property>
</bean>

使用ref可以注入类类型

Spring Boot笔记十:IOC控制反转的更多相关文章

  1. Spring专题2: DI,IOC 控制反转和依赖注入

    合集目录 Spring专题2: DI,IOC 控制反转和依赖注入 https://docs.spring.io/spring/docs/2.5.x/reference/aop.html https:/ ...

  2. spring(一) IOC 控制反转 、DI 依赖注入

    IOC 控制反转:创建对象的方式  变成了由Spring来主导 IOC底层原理:对象工厂 1.导入jar包:4个核心jar和1个依赖jar spring-beans-4.3.9.RELEASE.jar ...

  3. spring最核心思想--ioc控制反转

    一核心概念 控制反转:将bean的生成交给容器,程序可以从容器中获取指定的bean. 个人理解:此优势也是spring能够流行并成为java主流框架的主要原因,java是帮助java程序员以对象的方式 ...

  4. Spring框架系列(四)--IOC控制反转和DI依赖注入

    背景: 如果对象的引用或者依赖关系的管理由具体对象完成,代码的耦合性就会很高,代码测试也变得困难.而IOC可以很好的解决这个问题,把这 些依赖关系交给框架或者IOC容器进行管理,简化了开发. IOC是 ...

  5. Spring IOC(控制反转)思想笔记

    Spring IOC(控制反转)思想笔记 IOC控制反转基本理念就是将程序控制权从程序员手中交给用户自定义,从而避免了因为用户一个小需求的变化使得程序员需要改动大量代码. 案例 如果按照之前javaw ...

  6. Spring学习之Ioc控制反转(1)

    开始之前: 1. 本博文为原创,转载请注明出处 2. 作者非计算机科班出身,如有错误,请多指正 ---------------------------------------------------- ...

  7. Spring学习之Ioc控制反转(2)

    开始之前: 1. 本博文为原创,转载请注明出处 2. 作者非计算机科班出身,如有错误,请多指正 ---------------------------------------------------- ...

  8. Spring框架之IOC(控制反转)

    [TOC] 第一章Spring框架简介 IOC(控制反转)和AOP(面向方面编程)作为Spring框架的两个核心,很好地实现了解耦合.所以,简单来说,Spring是一个轻量级的控制反转(IoC)和面向 ...

  9. Spring详解(二)------IOC控制反转

    我相信提到 Spring,很多人会脱口而出IOC(控制反转).DI(依赖注入).AOP等等概念,这些概念也是面试官经常问到的知识点.那么这篇博客我们就来详细的讲解 IOC控制反转. ps:本篇博客源码 ...

随机推荐

  1. BZOJ3223文艺平衡树——非旋转treap

    此为平衡树系列第二道:文艺平衡树您需要写一种数据结构,来维护一个有序数列,其中需要提供以下操作: 翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1 ...

  2. 微信小程序——使用vue构建小程序【外传】

    文档 http://mpvue.com/mpvue/ 根据文档构建完成的页面如下 更多的,还要继续看下文档~

  3. CH2601 电路维修(算竞进阶习题)

    01边bfs 这题很容易想到的就是根据符号的情况建图,把每个点方格的对角线看成图的节点,有线相连就是边权就是0,没有就是1 然后跑最短路,但是最短路用的优先队列维护是有logn的代价的 这题还有一个更 ...

  4. 脚本监控web服务器,工作不正常发邮件提醒

    背景介绍公司有多个web网站,没有配置监控服务,每天都需要定时检查服务器是否工作正常.低效耗时. 代码片段 #!/bin/bash # Author Jerry.huang (Email:Jerry. ...

  5. 【UOJ#236】[IOI2016]railroad(欧拉回路,最小生成树)

    [UOJ#236][IOI2016]railroad(欧拉回路,最小生成树) 题面 UOJ 题解 把速度看成点,给定的路段看成边,那么现在就有了若干边,然后现在要补上若干边,以及一条\([inf,\) ...

  6. poj 3666 Making the Grade(离散化+dp)

    Description A straight dirt road connects two fields on FJ's farm, but it changes elevation more tha ...

  7. htmlunit 校验验证码

    htmlUnit 校验验证码 直接上代码 String url = "http://www.zycg.gov.cn/"; WebclientUtil webClientUtils ...

  8. Go 语言 HTTP Server 源码分析

    http://www.codeceo.com/go-http-server-code.html

  9. kafka 流式计算

    http://www.infoq.com/cn/articles/kafka-analysis-part-7/ Kafka设计解析(七)- 流式计算的新贵 Kafka Stream

  10. macOS在virtualenv中使用wxPython报错

    在虚拟的Python环境中运行GUI的软件报错 This program needs access to the screen. Please run with a Framework build o ...