首页 > 其他分享 >Spring学习记录之Spring对IoC的实现

Spring学习记录之Spring对IoC的实现

时间:2023-12-26 16:00:51浏览次数:48  
标签:set userDao 记录 Spring spring UserDao IoC public 注入

Spring学习记录之Spring对IoC的实现


前言

这篇文章是我第二次学习b站老杜spring相关课程所进行的学习记录,算是对课程内容及笔记的二次整理,以自己的理解方式进行二次记录,其中理解可能存在错误,欢迎且接受各位大佬们的批评指正;

关于本笔记,只是我对于相关知识遗忘时快速查阅了解使用,至于课程中实际实验配置等,也只是记录关键,并不会记录详细步骤,若想了解可以关注我博客的项目经验模块,我会在实际项目开发过程中总结项目经验,在该模块发布!

学习视频地址:https://www.bilibili.com/video/BV1Ft4y1g7Fb/

视频配套笔记:https://www.yuque.com/dujubin/ltckqu/kipzgd?singleDoc# 《Spring6》 密码:mg9b


目录

目录


Spring对IoC的实现

一、我个人对这部分学习的一些见解

这部分学习很显然是至关重要的,是Spring的核心内容,所以一定要熟练掌握。但是其中也有一些项目中很少使用的方式方法,就比如说某某命名空间注入依赖XML方式去进行依赖方式注入等,其实在目前来看,实际开发中在依赖注入这块基本上都是使用注解的方式去进行装配注入等;但是这样是不是就代表XML方式不用认真学了呢?实际上并不是,这两种方式建议都要好好了解一下,或者说你像我一样,把XML注入方式写一份详细的笔记进行记录,后续万一用到也可以快速查阅。因为在编程中技术复活的情况比比皆是,多知多得。

以下我会引入老杜的笔记,同时会补充知识要点,最后会进行知识要点总结!

二、控制反转-依赖注入再回顾

这里的基本思想千万不要忘记了,带着基本思想去学习以下内容;如果忘记了,可以去回顾Spring学习记录之Spring启示录部分。

① IoC 控制反转

  • 控制反转是一种思想。

  • 控制反转是为了降低程序耦合度,提高程序扩展力,达到OCP原则,达到DIP原则。

  • 控制反转,反转的是什么?(后续结合XML配置来实际体会以下两点)

    • 将对象的创建权利交出去,交给第三方容器负责。
    • 将对象和对象之间关系的维护权交出去,交给第三方容器负责。
  • 控制反转这种思想如何实现呢?

    • DI(Dependency Injection):依赖注入

② 依赖注入

依赖注入实现了控制反转的思想。

Spring通过依赖注入的方式来完成Bean管理的。

Bean管理说的是:Bean对象的创建,以及Bean对象中属性的赋值(或者叫做Bean对象之间关系的维护)。

依赖注入:

  • 依赖指的是对象和对象之间的关联关系。
  • 注入指的是一种数据传递行为,通过注入行为来让对象和对象产生关系。

依赖注入常见的实现方式包括两种:

  • 第一种:set注入
  • 第二种:构造注入

三、set注入

① 什么是set注入

顾名思义,就是基于对象的set方法注入。底层会通过反射机制调用属性对应的set方法然后给属性赋值。这种方式要求属性必须对外提供set方法。

接下来我们就通过实验来分析体会set注入(具体步骤,请移步教学视频,这里只演示关键步骤。)

② 实验使用xml通过set方法实现依赖注入

  1. UserDao
package com.powernode.spring6.dao;

/**
 * @author 动力节点
 * @version 1.0
 * @className UserDao
 * @since 1.0
 **/
public class UserDao {

    public void insert(){
        System.out.println("正在保存用户数据。");
    }
}
  1. UserService
package com.powernode.spring6.service;

import com.powernode.spring6.dao.UserDao;

/**
 * @author 动力节点
 * @version 1.0
 * @className UserService
 * @since 1.0
 **/
public class UserService {

    private UserDao userDao;

    // 使用set方式注入,必须提供set方法。
    // 反射机制要调用这个方法给属性赋值的。
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void save(){
        userDao.insert();
    }
}

补充:可以看到如果这样写好两个类,两个类之间是没有产生直接关系的。这时的private UserDao userDao;只是一个空引用,如果我们使用UserService的实例直接调用save方法,一定会报空指针异常,这一点我们在Spring学习记录之Spring的入门程序中进行过相关实验。那么在不改变上述代码的情况下如何让程序运行时就创建好UserDao的实例对象,且UserService的实例对象自己就会找到UserDao的实例对象,并将该对象赋值给userDao这个过程就是所谓通过依赖注入的方式实现IoC控制反转思想。

  1. 编写spring的配置文件:spring.xml。该文件放在类的根路径下(resources),引入XMLSchema(标签书写规范)
<?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">

</beans>
  1. 让spring管理UserServiceUserDao
<?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="userDaoBean" class="com.powernode.spring6.dao.UserDao"/>

    <bean id="userServiceBean" class="com.powernode.spring6.service.UserService"/>
    
</beans>

像这样,我们就将UserServiceUserDao交给了spring管理。其中id代表该对象在spring容器中的唯一标识,class指向的是类的全路径,用于spring找到该类并创建其对象实例。

但是如果只是这样,我们仅仅只是将两个类的对象创建权交给了spring,并没有让两个类产生关系,即UserService的对象实例调用save方法时依旧会产生空指针异常。

  1. 让spring管理UserServiceUserDao之间的关系
<?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="userDaoBean" class="com.powernode.spring6.dao.UserDao"/>

    <bean id="userServiceBean" class="com.powernode.spring6.service.UserService">
        <property name="userDao" ref="userDaoBean"/>
    </bean>

</beans>

像这样,在userServiceBean写入<property name="userDao" ref="userDaoBean"/>,同时必须在UserService中提供userDao属性的set方法,这样spring底层通过反射机制要调用这个方法给userDao属性赋值的。至于赋值哪一个对象,由ref指定。其中name指定的是userDao属性的set方法名称去掉set之后首字母缩写的名称,并不是属性名称,只不过大多数标准代码书写情况下恰好是属性名称。通过这种方法就完成了对象之间的关系维护,即所谓的依赖注入。

  1. 测试程序
package com.powernode.spring6.test;

import com.powernode.spring6.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author 动力节点
 * @version 1.0
 * @className DITest
 * @since 1.0
 **/
public class DITest {

    @Test
    public void testSetDI(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        UserService userService = applicationContext.getBean("userServiceBean", UserService.class);
        userService.save();
    }
}

运行结果:

img

③ 重点总结

spring管理的对象通过property标签获取到name的指定值userDao

通过userDao推断出要执行的set方法名为:setUserDao

通过反射机制调用setUserDao()方法给相应属性赋值

property标签的ref指定的是要注入的bean对象的id。(通过ref属性来完成bean的装配,这是bean最简单的一种装配方式。装配指的是:创建系统组件之间关联的动作)

我们可以把UserService类中的set方法注释掉,再测试一下:

img

通过测试得知,底层实际上调用了setUserDao()方法。所以需要确保这个方法的存在。

我们现在把属性名修改一下,但方法名还是setUserDao(),我们来测试一下:

package com.powernode.spring6.service;

import com.powernode.spring6.dao.UserDao;

/**
 * @author 动力节点
 * @version 1.0
 * @className UserService
 * @since 1.0
 **/
public class UserService {

    private UserDao aaa;

    // 使用set方式注入,必须提供set方法。
    // 反射机制要调用这个方法给属性赋值的。
    public void setUserDao(UserDao userDao) {
        this.aaa = userDao;
    }

    public void save(){
        aaa.insert();
    }
}

运行测试程序:

img

通过测试看到程序仍然可以正常执行,说明property标签的name是:setUserDao()方法名演变得到的。(而不是属性名)演变的规律是:

  • setUsername() 演变为 username
  • setPassword() 演变为 password
  • setUserDao() 演变为 userDao
  • setUserService() 演变为 userService

另外,对于property标签来说,ref属性也可以采用标签的方式,但使用ref属性是多数的:(了解)

<bean id="userServiceBean" class="com.powernode.spring6.service.UserService">
  <property name="userDao">
    <ref bean="userDaoBean"/>
  </property>
</bean>

④ set注入的核心实现原理

通过反射机制调用set方法来给属性赋值,让两个对象之间产生关系。

补充:先创建对象,再执行set方法注入;

四、构造注入

① 什么是构造注入

顾名思义,就是基于对象的构造方法注入。底层会通过调用对象的构造方法来给属性赋值。

② set方法注入和构造方法注入时机对比

set方法注入时机:先创建对象,再执行set方法注入;

构造方法注入时机:在创建对象时注入;

③ 构造注入的三种方式

方式1:根据构造方法的参数索引位置注入
  1. OrderDao
package com.powernode.spring6.dao;

/**
 * @author 动力节点
 * @version 1.0
 * @className OrderDao
 * @since 1.0
 **/
public class OrderDao {
    public void deleteById(){
        System.out.println("正在删除订单。。。");
    }
}
  1. OrderService
package com.powernode.spring6.service;

import com.powernode.spring6.dao.OrderDao;

/**
 * @author 动力节点
 * @version 1.0
 * @className OrderService
 * @since 1.0
 **/
public class OrderService {
    private OrderDao orderDao;

    // 通过反射机制调用构造方法给属性赋值
    public OrderService(OrderDao orderDao) {
        this.orderDao = orderDao;
    }

    public void delete(){
        orderDao.deleteById();
    }
}
  1. spring.xml
<bean id="orderDaoBean" class="com.powernode.spring6.dao.OrderDao"/>
<bean id="orderServiceBean" class="com.powernode.spring6.service.OrderService">
  <!--index="0"表示构造方法的第一个参数,将orderDaoBean对象传递给构造方法的第一个参数。-->
  <constructor-arg index="0" ref="orderDaoBean"/>
</bean>
  1. 测试程序
@Test
public void testConstructorDI(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
    OrderService orderServiceBean = applicationContext.getBean("orderServiceBean", OrderService.class);
    orderServiceBean.delete();
}

运行结果如下:

img

补充:如果构造方法有两个参数

  1. OrderService
package com.powernode.spring6.service;

import com.powernode.spring6.dao.OrderDao;
import com.powernode.spring6.dao.UserDao;

/**
 * @author 动力节点
 * @version 1.0
 * @className OrderService
 * @since 1.0
 **/
public class OrderService {
    private OrderDao orderDao;
    private UserDao userDao;

    // 通过反射机制调用构造方法给属性赋值
    public OrderService(OrderDao orderDao, UserDao userDao) {
        this.orderDao = orderDao;
        this.userDao = userDao;
    }

    public void delete(){
        orderDao.deleteById();
        userDao.insert();
    }
}
  1. spring.xml
<bean id="orderDaoBean" class="com.powernode.spring6.dao.OrderDao"/>

<bean id="orderServiceBean" class="com.powernode.spring6.service.OrderService">
  <!--第一个参数下标是0-->
  <constructor-arg index="0" ref="orderDaoBean"/>
  <!--第二个参数下标是1-->
  <constructor-arg index="1" ref="userDaoBean"/>
</bean>

<bean id="userDaoBean" class="com.powernode.spring6.dao.UserDao"/>

执行测试程序:

img

方式2:根据构造方法的参数名称注入
  1. spring.xml
<bean id="orderDaoBean" class="com.powernode.spring6.dao.OrderDao"/>

<bean id="orderServiceBean" class="com.powernode.spring6.service.OrderService">
  <!--这里使用了构造方法上参数的名字-->
  <constructor-arg name="orderDao" ref="orderDaoBean"/>
  <constructor-arg name="userDao" ref="userDaoBean"/>
</bean>

<bean id="userDaoBean" class="com.powernode.spring6.dao.UserDao"/>

执行测试程序:

img

方式3:根据构造方法参数的类型匹配注入
  1. spring.xml
<bean id="orderDaoBean" class="com.powernode.spring6.dao.OrderDao"/>
<bean id="orderServiceBean" class="com.powernode.spring6.service.OrderService">
  <!--没有指定下标,也没有指定参数名字-->
  <constructor-arg ref="orderDaoBean"/>
  <constructor-arg ref="userDaoBean"/>
</bean>

<bean id="userDaoBean" class="com.powernode.spring6.dao.UserDao"/>

执行测试程序:

img

补充提问:配置文件中构造方法参数的类型(索引、名称)顺序和构造方法参数的类型顺序不一致呢?

测试:

<bean id="orderDaoBean" class="com.powernode.spring6.dao.OrderDao"/>

<bean id="orderServiceBean" class="com.powernode.spring6.service.OrderService">
  <!--顺序已经和构造方法的参数顺序不同了-->
  <constructor-arg ref="userDaoBean"/>
  <constructor-arg ref="orderDaoBean"/>
</bean>

<bean id="userDaoBean" class="com.powernode.spring6.dao.UserDao"/>

执行测试程序:

img

可以看出和配置顺序没有关系

④ 测试总结

通过测试得知,通过构造方法注入的时候:

  • 可以通过下标
  • 可以通过参数名
  • 也可以不指定下标和参数名,可以类型自动推断。(补充:这种方式spring容器中同类型的对象只能管理一个,否则在进行类型匹配对象的时候程序不能确定匹配哪一个对象实例而报错)

Spring在装配方面做的还是比较健壮的。

五、总结

在这部分我们通过编写xml的方式实验了依赖注入的两种方式:set注入、构造注入。同时通过实验的方式体会了什么是IoC思想以及实现IoC思想的手段依赖注入。

这里需要去了解老杜这节相关讲解,可以直接点击下面链接跳转到对应课程学习了解!

016-控制反转和依赖注入的关系_哔哩哔哩_bilibili

标签:set,userDao,记录,Spring,spring,UserDao,IoC,public,注入
From: https://www.cnblogs.com/zhao-XH/p/17928319.html

相关文章

  • 浅聊SpringCloud的网关
    为什么要设计网关?上网搜罗了一下,觉得别人说的挺好,就引用了一下,在使用微服务的时候,不同的功能业务会集成一个服务群,而网关是基于服务群上的一个服务层,也是单独暴露给客户端的APIs。客户端对微服务的依赖直接使重构服务变得困难。一种直观的方法是将这些服务隐藏在一个新的服务层后面......
  • 微信小程序自定义ui库开发记录
    首先去翻官网,看有没有提供,发现微信官方提供了命令行工具,用于快速初始化一个项目npminstall-g@wechat-miniprogram/miniprogram-cli自定义组件/开发第三方自定义组件(qq.com)使用体验打包后的组件,每个js顶部都会带上一大段代码,增大了文件体积。没有相关的组件文档生......
  • dremio hive jdbc arp date 类型问题记录
    简单记录下碰到的一些问题分析arthasstack查看调用stackcom.mysql.cj.jdbc.result.ResultSetImplgetDate效果ffect(classcount:2,methodcount:4)costin329ms,listenerId:11ts=2023-12-2606:18:17;thread_name=e3-1a758f......
  • 微信小程序自动化测试踩坑记录
    微信开发工具版本:1.06.2310080win32-x64手动录制录制回放时使用真实接口会存在接口响应时间不一致,或者数据变化导致回放失败问题,以及有些业务流程可能只能走一遍,比如买同一时间的机票,所以建议使用mockrequest请求录制时不建议开启touch事件合并,会导致scroll-view滚动记录......
  • Spring Event 与 AOP
    在构建现代化的应用中,日志记录是不可或缺的一环。Spring框架为我们提供了强大的事件机制(SpringEvent)和切面编程(AOP),结合使用可以实现优雅的日志记录,使得代码更加模块化和可维护。本文将介绍如何结合SpringEvent和AOP,以及如何在不同场景下应用这两个强大的特性。1.SpringEvent......
  • NetSuite 开发日记 —— 库存详细信息记录更改数量问题
    详细报错:"type":"error.SuiteScriptError","name":"USER_ERROR","message":"在更改数量之后,您仍需要重新配置库存详细信息记录。""type":"error.SuiteScriptError","name":"USER_ERROR","......
  • LiveGBS流媒体平台GB/T28181常见问题-配置国标流媒体服务日志文件个数及记录时长配置l
    LiveGBS流媒体平台GB/T28181常见问题-如何配置国标流媒体服务日志文件个数及记录时长1、日志文件2、配置日志文件个数及记录时间3、配置日志文件路径4、相关问题4.1、如何关闭信令日志?5、搭建GB28181视频直播平台1、日志文件部署LiveGBS后,LiveCMS和LiveSMS的解压目录下都个l......
  • 2023最新高级难度Objective-C面试题,包含答案。刷题必备!记录一下。
    好记性不如烂笔头内容来自面试宝典-高级难度Objective-C面试题合集问:请解释一下Objective-C中的“MethodSwizzling”(方法混淆)是什么?它的原理和使用场景是什么?MethodSwizzling是Objective-C中的一个特性,它允许你在一个运行时环境中交换两个方法的实现。这意味着你可以替......
  • 2023最新初级难度R语言面试题,包含答案。刷题必备!记录一下。
    好记性不如烂笔头内容来自面试宝典-初级难度R语言面试题合集问:请解释一下R语言是什么?它的主要特点和用途是什么?R语言是一种用于统计分析、绘图和数据挖掘的编程语言和环境。它是开源的,可以免费下载和使用,并且具有强大的社区支持。R语言由新西兰奥克兰大学的RossIhaka和R......
  • SpringBoot项目导入的时候没有显示父工程
    一、出现错误导入选中要导入的项目的文件夹,然后点击OK,接下来一直next即可。但是导入完了之后没有出现父模块,只有子模块。二、修正错误CTRL+SHIFT+A打开下面的菜单输入maven,选择AddMavenProjects.选择你要导入的工程的pom文件,然后点击OK,父模块就导入了。三、较好的导入方法从......