[Spring] IOC控制反转/DI依赖注入详细讲解

时间:2022-09-26 11:16:50

✨✨个人主页:沫洺的主页

 ????????系列专栏: ???? JavaWeb专栏???? JavaSE专栏 ???? Java基础专栏????vue3专栏 

                           ????MyBatis专栏

????????如果文章对你有所帮助请留下三连✨✨

????????前言(很重要!!!)

????学习Spring框架(Spring Framework)之前先了解两个思想概念

  • ????IOC控制反转: 由Spring帮我们实例化指定的bean(bean就是指Java类),把创建的实例存入IOC容器(ApplicationContext)中,然后结合DI给属性赋值
  • ????DI依赖注入: (Dependency Injection)给所依赖的属性(就是声明的属性)赋值的过程

????什么意思呢?

????其实就是一种编程思想,在JavaWeb三层架构中就比如业务层去调用数据层时,要通过new对象的方式去调用数据层的方法,如下图所示

[Spring] IOC控制反转/DI依赖注入详细讲解

????这里就会出现一个问题,一旦数据层代码的实现方式发生改变,需要换一种实现方式,那么这里的业务层就需要相应的改变new的对象,就如下图所示

[Spring] IOC控制反转/DI依赖注入详细讲解

????这样会造成一种效果,对项目而言由于源代码发生了改变,那么该项目就要重新编译,重新测试,重新部署,重新发布,就企业级项目而言成本就非常的高

????总结来就是,由于在类中写了其他的一些实现,就会造成耦合度偏高,所以解决办法就是在使用对象时,在程序中不要主动使用new产生对象,而是转换为由外部提供对象,这个外部就是IOC容器,IOC容器负责对象的创建、初始化等一系列工作,被创建或被管理的对象在IoC容器中统称为Bean,这种解决办法的概念就是IOC控制反转

[Spring] IOC控制反转/DI依赖注入详细讲解

????那么实现控制反转之后,业务层和数据层并没有什么联系,也就是业务层此时还不能依赖数据层去运行,因此在IOC容器中又多了一个功能,既然IOC是创建对象的,那么业务层(service)和数据层(dao)的对象都可以在IOC容器中去创建,那既然对象都在容器中了,是否可以实现对象之间的依赖,从而建立绑定,这种绑定关系的整个过程就是DI依赖注入

[Spring] IOC控制反转/DI依赖注入详细讲解

 ????通过以上的解释可以大致这样理解

[Spring] IOC控制反转/DI依赖注入详细讲解

????以上就是学习Spring的目的

????Spring技术对IOC思想进行了实现

  • ????Spring提供了一个容器,称为IoC容器,用来充当IOC思想中的“外部”
  • ????IOC容器负责对象的创建、初始化等一系列工作,被创建或被管理的对象在IOC容器中统称为Bean
  • ????在容器中建立bean与bean之间的依赖关系的整个过程,称为依赖注入
  • ????IOC与DI思想的实现,其目的就是充分解耦

????最终效果: 使用对象时不仅可以直接从IOC容器中获取,并且获取到的bean已经绑定了所有的依赖关系

????Spring介绍

????Spring技术是JavaEE开发必备技能,企业开发技术选型命中率>90%

????为什么选型率这么高呢,有两点很关键

  • ????简化开发,降低企业级开发的复杂性
  • ????框架整合,高效整合其他技术,提高企业级应用开发与运行效率

????Spring Framework系统架构

[Spring] IOC控制反转/DI依赖注入详细讲解

????IOC与DI入门(XML格式)

????控制反转步骤

  1. ????导入Spring坐标
  2. ????定义Spring管理的类(接口)
  3. ????创建Spring配置文件,配置对应类作为Spring管理的bean对象
  4. ????初始化IOC容器(Spring核心容器/Spring容器),通过容器获取bean对象

????依赖注入步骤

  1. 删除使用new的形式创建对象的代码
  2. 提供依赖对象对应的setter方法
  3. 配置service与dao之间的关系

????导入Spring坐标

<!--导入spring的坐标spring-context,对应版本是5.2.10.RELEASE-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.10.RELEASE</version>
    </dependency>

[Spring] IOC控制反转/DI依赖注入详细讲解

????定义Spring管理的类(接口)

????com/moming/dao数据层

????创建BookDao接口,创建impl/BookDaoImpl实现类

????BookDao接口

package com.moming.dao;

public interface BookDao {
    void save();
}

????BookDaoImpl实现类

package com.moming.dao.impl;

import com.moming.dao.BookDao;

public class BookDaoImpl implements BookDao {
    @Override
    public void save() {
        System.out.println("数据层执行");
    }
}

????com/moming/service业务层

????创建BookService接口,创建impl/BookServiceImpl实现类

????BookService接口

package com.moming.service;

public interface BookService {
    void save();
}

????BookServiceImpl实现类

package com.moming.service.impl;

import com.moming.dao.BookDao;
import com.moming.service.BookService;

public class BookServiceImpl implements BookService {
    //删除业务层中使用new的方式创建的dao对象
    private BookDao bookDao;
    //提供set方法
    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }

    public void save(){
        System.out.println("业务层执行");
        bookDao.save();
    }
}

????resources下创建Spring配置文件(applicationContext.xml ),配置对应类作为Spring管理的bean对象, 配置service与dao之间的关系

????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标签:表示配置bean
    	id属性:表示给bean起名字
    	class属性:表示给bean定义类型
	-->
    <bean id="bookDao" class="com.moming.dao.impl.BookDaoImpl"/>
    <bean id="bookService" class="com.moming.service.impl.BookServiceImpl">
        <!--配置service与dao的依赖关系
			property标签:表示配置当前bean的属性
        	name属性:表示配置哪一个具体的属性
        	ref属性:表示参照哪一个bean
		-->
        <property name="bookDao" ref="bookDao"/>
    </bean>

</beans>

????创建测试类App,获取IOC容器,获取bean,调用方法

????App

package com.moming;

import com.moming.service.BookService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {
    public static void main(String[] args) {
        //获取IOC容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        //获取bean
        BookService bookService = (BookService) ac.getBean("bookService");
        //调用
        bookService.save();
    }
}

????运行结果

[Spring] IOC控制反转/DI依赖注入详细讲解

????bean配置

????bean基础配置

[Spring] IOC控制反转/DI依赖注入详细讲解

????bean别名配置

[Spring] IOC控制反转/DI依赖注入详细讲解

  • ????获取bean无论是通过id还是name获取,如果无法获取到,将抛出异常NoSuchBeanDefinitionException: No bean named 'bookServiceImpl' available

????bean作用范围配置(一个bean造出来的是否是同一个对象)

[Spring] IOC控制反转/DI依赖注入详细讲解

????适合交给容器进行管理的bean

  • ????表现层对象 业务层对象 数据层对象 工具对象

????不适合交给容器进行管理的bean

  • ????封装实体的域对象

????bean初始化

????bean本质上就是对象,创建bean使用构造方法完成

????实例化bean的三种方式

  • ????构造方法(常用)
  • ????静态工厂(了解)
  • ????FactoryBean(实用)

????构造方法(常用)

  • ????提供可访问的构造方法
public class BookDaoImpl implements BookDao {
    public void save() {
        System.out.println("无参构造,默认存在 ...");
    }
}
  • ????配置
<bean id="bookDao" class="com.moming.dao.impl.BookDaoImpl"/>
  • ????无参构造方法如果不存在,将抛出异常BeanCreationException

????静态工厂(了解)

  • ????静态工厂
public class BookDaoFactory{
    public static BookDao getBookDao(){
        return new BookDaoImpl;
    }
}
  • ????配置
<bean id="bookDao" factory-method="getBookDao" class="com.moming.factory.BookDaoFactory"/>

????FactoryBean(实用)

  • ????FactoryBean
public class BookDaoFactoryBean implements FactoryBean<BookDao>{
    public BookDao getObject() throws Exception{
        return new BookDaoImpl();
    }
    public Class<?> getObjectType(){
        return BookDao.class;
    }
}
  • ????配置
<bean id="bookDao" class="com.moming.factory.BookDaoFactoryBean"/>

????bean生命周期

  • ????生命周期:从创建到消亡的完整过程
  • ????bean生命周期:bean从创建到销毁的整体过程
  • ????bean生命周期控制:在bean创建后到销毁前做一些事情

????提供生命周期控制方法

public class BookDaoImpl implements BookDao {
    public void save() {
        System.out.println("数据层执行 ...");
    }
    //表示bean初始化对应的操作
    public void init(){
        System.out.println("初始化执行 ...");
    }
    //表示bean销毁前对应的操作
    public void destory(){
        System.out.println("销毁前执行 ...");
    }
}

????配置生命周期控制方法

<bean id="bookDao" class="com.moming.dao.impl.BookDaoImpl" init-method="init" destroy-method="destory"/>

????要想看到destory()执行,必须要关闭容器,因为程序结束后Java虚拟机就关闭了,destory()没机会执行,所以就要设置钩子在虚拟机退出前先关闭容器再退出虚拟机

[Spring] IOC控制反转/DI依赖注入详细讲解

 ????在service上按照spring接口的方式控制生命周期

[Spring] IOC控制反转/DI依赖注入详细讲解

???? BookServiceImpl

package com.moming.service.impl;

import com.moming.dao.BookDao;
import com.moming.service.BookService;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;

public class BookServiceImpl implements BookService , InitializingBean, DisposableBean {
    //删除业务层中使用new的方式创建的dao对象
    private BookDao bookDao;
    //提供set方法
    public void setBookDao(BookDao bookDao) {
        System.out.println("属性设置");
        this.bookDao = bookDao;
    }
    public void save(){
        System.out.println("业务层执行");
        bookDao.save();
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("service 销毁前执行");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("service 属性设置完以后执行");
    }
}

???? App

package com.moming;

import com.moming.service.BookService;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {
    public static void main(String[] args) {
        //获取IOC容器
        //ApplicationContext没有close方法,所以去下一级找
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        //获取bean
        BookService bookService = (BookService) ctx.getBean("bookService");
        //调用
        bookService.save();
        //直接手动暴力关闭(不推荐)
        //ctx.close();
        //获取钩子关闭
        ctx.registerShutdownHook();
    }
}

????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标签:表示配置bean
    	id属性:表示给bean起名字
    	class属性:表示给bean定义类型
	-->
    <!--<bean id="bookDao" class="com.moming.dao.impl.BookDaoImpl"/>-->
    <bean id="bookDao" class="com.moming.dao.impl.BookDaoImpl" init-method="init" destroy-method="destory"/>
    <bean id="bookService" class="com.moming.service.impl.BookServiceImpl">
        <!--配置service与dao的依赖关系
			property标签:表示配置当前bean的属性
        	name属性:表示配置哪一个具体的属性
        	ref属性:表示参照哪一个bean
		-->
        <property name="bookDao" ref="bookDao"/>
    </bean>

</beans>

????bean整个生命周期所经历的阶段

????初始化容器

  • ????创建对象(内存分配)
  • ????执行构造方法
  • ????执行属性注入(set操作)
  • ????执行bean初始化方法

????使用bean

  • ????执行业务操作

????关闭/销毁容器

  • ????执行bean销毁方法

????依赖注入方式

????向一个类中传递数据的方式有两种

  1. ????普通方法(set方法)
  2. ????构造方法

????依赖注入描述了在容器中建立bean与bean之间依赖关系的过程,如果bean运行需要的是数字或字符串呢

  • ????引用类型
  • ????简单类型(基本数据类型与String)

????依赖注入方式

????setter注入(推荐使用)

  • ????简单类型
  • ????引用类型

????构造器注入

  • ????简单类型
  • ????引用类型

????setter注入引用类型

  • ????在bean中定义引用类型属性并提供可访问的set方法
public class BookServiceImpl implements BookService{    
        private BookDao bookDao;    
        public void setBookDao(BookDao bookDao) {        
                this.bookDao = bookDao;    
        }
}
  • ????配置中使用property标签ref属性注入引用类型对象
<bean id="bookService" class="com.moming.service.impl.BookServiceImpl">    
        <property name="bookDao" ref="bookDao"/>
</bean>
<bean id="bookDao" class="com.moming.dao.impl.BookDaoImpl"/>

????setter注入简单类型

  • ????在bean中定义引用类型属性并提供可访问的set方法
public class BookDaoImpl implements BookDao {    
        private int connectionNumber;    
        public void setConnectionNumber(int connectionNumber) {                         
            this.connectionNumber = connectionNumber;    
         }
}
  • ????配置中使用property标签value属性注入简单类型数据
<bean id="bookDao" class="com.moming.dao.impl.BookDaoImpl">
    <property name="connectionNumber" value="10"/>
</bean>

????构造器注入引用类型(了解)

  • ????在bean中定义引用类型属性并提供可访问的构造方法
public class BookServiceImpl implements BookService{    
        private BookDao bookDao;    
        public BookServiceImpl(BookDao bookDao) {        
                this.bookDao = bookDao;    
         }
}
  • ????配置中使用constructor-arg标签ref属性注入引用类型对象
<bean id="bookService" class="com.moming.service.impl.BookServiceImpl">          
    <constructor-arg name="bookDao" ref="bookDao"/>
</bean>
<bean id="bookDao" class="com.moming.dao.impl.BookDaoImpl"/>

????构造器注入简单类型(了解)

  • ????在bean中定义引用类型属性并提供可访问的set方法
public class BookDaoImpl implements BookDao {    
        private int connectionNumber;    
        public void setConnectionNumber(int connectionNumber) {                         
            this.connectionNumber = connectionNumber;    
        }
}
  • ????配置中使用constructor-arg标签value属性注入简单类型数据
<bean id="bookDao" class="com.moming.dao.impl.BookDaoImpl">
    <constructor-arg name="connectionNumber" value="10"/>
</bean>

????构造器注入参数适配(了解)

  • ????配置中使用constructor-arg标签type属性设置按形参类型注入
<bean id="bookDao" class="com.moming.dao.impl.BookDaoImpl">
    <constructor-arg type="int" value="10"/>
    <constructor-arg type="java.lang.String" value="mysql"/>
</bean>
  • ????配置中使用constructor-arg标签index属性设置按形参位置注入
<bean id="bookDao" class="com.moming.dao.impl.BookDaoImpl">
    <constructor-arg index="0" value="10"/>
    <constructor-arg index="1" value="mysql"/>
</bean>

????依赖注入方式选择

  • ????强制依赖使用构造器进行,使用setter注入有概率不进行注入导致null对象出现
  • ????可选依赖使用setter注入进行,灵活性强
  • ????Spring框架倡导使用构造器,第三方框架内部大多数采用构造器注入的形式进行数据初始化,相对严谨
  • ????如果有必要可以两者同时使用,使用构造器注入完成强制依赖的注入,使用setter注入完成可选依赖的注入
  • ????实际开发过程中还要根据实际情况分析,如果受控对象没有提供setter方法就必须使用构造器注入
  • ????自己开发的模块推荐使用setter注入

????依赖自动装配

????IOC容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称为自动装配

????自动装配方式

  • ????按类型(常用)
  • ????按名称
  • ????按构造方法
  • ????不启用自动装配

????配置中使用bean标签autowire属性设置自动装配的类型

<bean id="bookDao" class="com.moming.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.moming.service.impl.BookServiceImpl" autowire="byType"/>

????依赖自动装配特征

  • ????自动装配用于引用类型依赖注入,不能对简单类型进行操作
  • ????使用按类型装配时(byType)必须保障容器中相同类型的bean唯一,推荐使用
  • ????使用按名称装配时(byName)必须保障容器中具有指定名称的bean,因变量名与配置耦合,不推荐使用
  • ????自动装配优先级低于setter注入与构造器注入,同时出现时自动装配配置失效

????集合注入 

????注入数组对象

<property name="array">
    <array>
        <value>100</value>
        <value>200</value>
        <value>300</value>
    </array>
</property>

????注入List对象(重点)

<property name="list">
    <list>
        <value>java</value>
        <value>spring</value>
        <value>DI</value>
    </list>
</property>

????注入Set对象

<property name="set">
    <set>
        <value>java</value>
        <value>spring</value>
        <value>DI</value>
    </set>
</property>

????注入Map对象(重点)

<property name="map">
    <map>
        <entry key="张三" value="23"/>
        <entry key="李四" value="24"/>
        <entry key="王五" value="25"/>
    </map>
</property>

????注入Properties对象

<property name="properties">
    <props>
        <prop key="zhangsan">23</prop>
        <prop key="lisi">24</prop>
        <prop key="wangwu">25</prop>
    </props>
</property>

????说明:property标签表示setter方式注入,构造方式注入constructor-arg标签内部也可以写<array>、<list>、<set>、<map>、<props>标签