当前位置: 首页 > news >正文

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

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

 📚📚系列专栏: 📖 JavaWeb专栏📖 JavaSE专栏 📖 Java基础专栏📖vue3专栏 

                           📖MyBatis专栏

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

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

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

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

🎫什么意思呢?

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

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

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

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

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

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

🎫以上就是学习Spring的目的

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

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

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

🥏Spring介绍

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

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

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

🎫Spring Framework系统架构

🥏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管理的类(接口)

🎈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();
    }
}

🎈运行结果

🪕bean配置

🎫bean基础配置

🎫bean别名配置

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

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

🎫适合交给容器进行管理的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()没机会执行,所以就要设置钩子在虚拟机退出前先关闭容器再退出虚拟机

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

🎐 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>标签

相关文章:

  • 代码中使用ffmpeg命令行给图片添加水印
  • JDBC内容整理
  • SPD5详解
  • Python 教程之控制流(3)Python 中的循环和控制语句(继续、中断和通过)
  • 神仙级别Kafka架构笔记,阿里架构师看到都感慨怎么没早看到
  • Django 使用VScode 创建工程
  • mysql进阶:canal实现跨机房数据同步|主从数据同步
  • 交换机与路由技术-31-扩展ACL
  • 【JAVA数据结构】二叉树的常用方法(你想要的这里都有)
  • vue实战-轮播图的最佳方案/swiper的使用
  • spring-cloud-netflix 组件概述
  • 【MICCAI 2022】PHTrans:并行聚合全局和局部表示以进行医学图像分割
  • 渗透学习-靶场篇-XSS-labs(持续更新中)
  • 【SpringCloud】三、 分布式系统的延迟和容错
  • Ultra Fast Deep Lane Detection with HybridAnchor Driven Ordinal Classification
  • hexo+github搭建个人博客
  • 《Java8实战》-第四章读书笔记(引入流Stream)
  • 【347天】每日项目总结系列085(2018.01.18)
  • 【译】React性能工程(下) -- 深入研究React性能调试
  • exif信息对照
  • JS进阶 - JS 、JS-Web-API与DOM、BOM
  • python docx文档转html页面
  • React系列之 Redux 架构模式
  • VirtualBox 安装过程中出现 Running VMs found 错误的解决过程
  • 闭包--闭包之tab栏切换(四)
  • 从重复到重用
  • 分布式任务队列Celery
  • 系统认识JavaScript正则表达式
  • 小李飞刀:SQL题目刷起来!
  • [Shell 脚本] 备份网站文件至OSS服务(纯shell脚本无sdk) ...
  • 摩拜创始人胡玮炜也彻底离开了,共享单车行业还有未来吗? ...
  • 如何通过报表单元格右键控制报表跳转到不同链接地址 ...
  • 如何用纯 CSS 创作一个菱形 loader 动画
  • ​软考-高级-系统架构设计师教程(清华第2版)【第20章 系统架构设计师论文写作要点(P717~728)-思维导图】​
  • #调用传感器数据_Flink使用函数之监控传感器温度上升提醒
  • (1)(1.11) SiK Radio v2(一)
  • (4)事件处理——(7)简单事件(Simple events)
  • (附源码)计算机毕业设计SSM疫情居家隔离服务系统
  • (没学懂,待填坑)【动态规划】数位动态规划
  • (十)DDRC架构组成、效率Efficiency及功能实现
  • (十二)springboot实战——SSE服务推送事件案例实现
  • (一)Thymeleaf用法——Thymeleaf简介
  • (转)IIS6 ASP 0251超过响应缓冲区限制错误的解决方法
  • .NET Compact Framework 多线程环境下的UI异步刷新
  • .NET MVC 验证码
  • .NET 中的轻量级线程安全
  • .NET处理HTTP请求
  • .Net下使用 Geb.Video.FFMPEG 操作视频文件
  • @Transaction注解失效的几种场景(附有示例代码)
  • @我的前任是个极品 微博分析
  • [Android Studio] 开发Java 程序
  • [ARM]ldr 和 adr 伪指令的区别
  • [Asp.net MVC]Asp.net MVC5系列——Razor语法
  • [BUAA软工]第一次博客作业---阅读《构建之法》
  • [BZOJ] 2006: [NOI2010]超级钢琴