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

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

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

                           📖MyBatis专栏📖Spring专栏📖SpringMVC专栏📖SpringBoot专栏

                           📖Docker专栏📖Reids专栏📖MQ专栏📖SpringCloud专栏     

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

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

🎫学习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>标签

🪕第三方资源配置管理

🎫德鲁伊数据库连接池

🎈导入druid坐标

    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.16</version>
    </dependency>

🎈配置数据源对象作为spring管理的bean

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://127.0.0.1:3306/spring_db"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>

🎈测试druid

🎫c3p0连接池

🎈导入c3p0坐标,mysql驱动坐标

    <dependency>
      <groupId>c3p0</groupId>
      <artifactId>c3p0</artifactId>
      <version>0.9.1.2</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.49</version>
    </dependency>

🎈配置数据源对象作为spring管理的bean

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/spring_db"/>
        <property name="user" value="root"/>
        <property name="password" value="123456"/>
    </bean>

🎈测试c3p0

🪕加载properties文件

🎈在resources下创建jdbc.properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///spring_db?useSSL=false&useServerPrepStmts=true
jdbc.username=root
jdbc.password=123456

🎈开启context命名空间

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">

🎈使用context命名空间,加载指定properties文件

<context:property-placeholder location="jdbc.properties"/>

🎈使用${}读取加载的属性值

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

🎈测试

🎫加载properties文件

🎈不加载系统属性

<context:property-placeholder location="jdbc.properties" system-properties-mode="NEVER"/>

🎈加载多个properties文件

<context:property-placeholder location="jdbc.properties,msg.properties"/>

🎈加载所有properties文件

<context:property-placeholder location="*.properties"/>

🎈加载properties文件标准格式

<context:property-placeholder location="classpath:*.properties"/>

🎈从类路径或jar包中搜索并加载properties文件

<context:property-placeholder location="classpath*:*.properties"/>

 🪕容器

🎫创建容器

🎈方式一:类路径加载配置文件(常用)

ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

🎈方式二:文件路径加载配置文件

ApplicationContext ctx = new FileSystemXmlApplicationContext("D:\\applicationContext.xml");

🎈加载多个配置文件

ApplicationContext ctx = new ClassPathXmlApplicationContext("bean1.xml", "bean2.xml");

🎫获取bean

🎈方式一:使用bean名称获取(常用)

BookDao bookDao = (BookDao) ctx.getBean("bookDao");

🎈方式二:使用bean名称获取并指定类型

BookDao bookDao = ctx.getBean("bookDao", BookDao.class);

🎈方式三:使用bean类型获取

BookDao bookDao = ctx.getBean(BookDao.class);

🎫容器类层次结构

🎫BeanFactory

🎈类路径加载配置文件

Resource resources = new ClassPathResource("applicationContext.xml");
BeanFactory bf = new XmlBeanFactory(resources);
BookDao bookDao = bf.getBean("bookDao", BookDao.class);
bookDao.save();

🎈BeanFactory创建完毕后,所有的bean均为延迟加载

🥏总结

🎫核心容器总结

  • 🎈BeanFactory是IoC容器的顶层接口,初始化BeanFactory对象时,加载的bean延迟加载
  • 🎈ApplicationContext接口是Spring容器的核心接口,初始化时bean立即加载
  • 🎈ApplicationContext接口提供基础的bean操作相关方法,通过其他接口扩展其功能
  • 🎈ApplicationContext接口常用初始化类
    • 🎐ClassPathXmlApplicationContext
    • 🎐FileSystemXmlApplicationContext

🎫bean相关

🎫依赖注入相关

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

沫洺

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值