Spring框架 _ IoC/DI(控制反转、依赖注入)


Spring框架 _ IoC/DI(控制反转、依赖注入)

一、Spring Framework系统架构图

二、Spring Framework课程学习路线

三、IoC/DI(控制反转、依赖注入)

  • 代码书写现状 —— 耦合度偏高

3.1 解决方案

  • 使用对象时,在程序中不要主动使用new产生对象,转换为由外部提供对象。

3.2 IoC (Inversion of Control)控制反转

  • 使用对象时,由主动new产生对象转换为由外部提供对象,此过程中对象创建控制权由程序转移到外部,此思想称为控制反转。

3.3 Spring技术对IoC思想进行了实现

  • Spring提供了一个容器,称为IoC容器,用来充当IoC思想中的“外部”
  • IoC容器负责对象的创建、初始化等一系列工作,被创建或被管理的对象在IoC容器中统称为Bean

3.4 DI (Dependency Injection) 依赖注入

  • 在容器中建立bean与bean之间的依赖关系的整个过程,称为依赖注入。

3.5 目标 —— 充分解耦

  • 使用IoC容器管理bean (IoC)。
  • 在IoC容器内将有依赖关系的bean进行关系绑定(DI)。

3.6 最终效果

  • 使用对象时不仅可以直接从IoC容器中获取,并且获取到的bean已经绑定了所有的依赖关系。

3.7 IoC入门案例

1.导入Spring坐标

<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.10.RELEASE</version>
    </dependency>

2.定义Spring管理的类(接口)

package com.li.service;

public interface BookService {
    public void save();
}


public class BookServiceImpl implements BookService {
    private BookDao bookDao = new BookDaoImpl();

    @Override
    public void save() {
        bookDao.save();
    }
}

3.创建Spring配置文件,配置对应类作为Spring管理的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">

    <!--1.导入spring的坐标spring-context,对应版本是5.2.10.RELEASE-->
    <!--2.配置bean-->
    <!--bean标签标示配置bean
    id属性标示给bean起名字
    class属性表示给bean定义类型-->
    <bean id="bookDao" class="com.li.dao.impl.BookDaoImpl"/>

    <bean id="bookService" class="com.li.service.impl.BookServiceImpl"/>
    
</beans>

注意事项

  • bean定义时id属性在同一个上下文中不能重复。

4.初始化IoC容器( Spring核心容器/Spring容器),通过容器获取bean

public class APP2 {
    public static void main(String[] args) {
        //3. 获取IOC容器
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

        //4. 获取bean
        BookDao bookDao =(BookDao)  ctx.getBean("bookDao");
        bookDao.save();

        BookService bookService =(BookService)  ctx.getBean("bookService");
        bookService.save();
    }
}

3.8 DI入门案例

1.删除使用new的形式创建对象的代码

1.1.提供依赖对象对应的setter方法

public class BookServiceImpl implements BookService {
    //5.删除业务层中使用new的方式创建的dao对象
    private BookDao bookDao;

    public void save() {
        System.out.println("book service save ...");
        bookDao.save();
    }
    //6.提供对应的set方法
    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }
}

2.配置service与dao之间的关系

<?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">

    <!--1.导入spring的坐标spring-context,对应版本是5.2.10.RELEASE-->
    <!--2.配置bean-->
    <!--bean标签标示配置bean
    id属性标示给bean起名字
    class属性表示给bean定义类型-->
    <bean id="bookDao" class="com.li.dao.impl.BookDaoImpl"/>

    <bean id="bookService" class="com.li.service.impl.BookServiceImpl">
<!--    7. 配置server dao的关系-->
        <!--property标签表示配置当前bean的属性
        name属性表示配置哪一个具体的属性
        ref属性表示参照哪一个bean-->
        <property name="bookDao" ref="bookDao"/>
    </bean>

</beans>

四、IOC相关内容(Bean)

4.1 bean配置

4.1.1 bean基础配置

4.1.2 bean别名配置

注意事项

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

4.1.3 bean作用范围配置

bean作用范围说明

为什么bean默认为单例?
  • 适合交给容器进行管理的bean

    表现层对象
    业务层对象
    数据层对象
    工具对象
    
  • 不适合交给容器进行管理的bean

    封装实体的域对象
    

4.2 bean实例化

4.2.1 bean是如何创建的

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

4.2.2 实例化bean的三种方式 —— ① 构造方法(常用)

4.2.2.1 提供可访问的构造方法

public class BookDaoImpl implements BookDao {
    public void save() {
        System.out.println("book dao save ...");
    }
    
    public  void save(){
        System.out.println("book save");
    }
}

4.2.2.2 配置

<bean
     id="bookDao"
     class="com.li.dao.impl.BookDaoImpl"
/>

4.2.2.2 无参构造方法如果不存在,将抛出异常BeanCreationException

4.2.3 实例化bean的三种方式 —— ② 静态工厂(了解即可)

4.2.3.1 静态工厂

//静态工厂创建对象
    public class OrderDaoFactory {
        public static OrderDao getOrderDao(){
            System.out.println("factory setup....");
            return new OrderDaoImpl();
        }
    }

4.2.3.2 配置

<!--方式二:使用静态工厂实例化bean-->
    <bean 
         id="orderDao" 
         class="com.li.factory.OrderDaoFactory" 
         factory-method="getOrderDao"/>

4.2.4 实例化bean的三种方式 —— ③ 实例工厂(了解即可)

4.2.4.1 实例工厂

public class UserDaoFactory {
    public UserDao getUserDao(){
        return new UserDaoImpl();
    }

}

4.2.4.2 配置

<!--方式三:使用实例工厂实例化bean-->
     <bean id="userFactory" class="com.li.factory.UserDaoFactory"/>

    <bean id="userDao" factory-method="getUserDao" factory-bean="userFactory"/>

4.2.4.3 缺点

  • 配合使用实际无意义
  • 方法名不固定每次需要配置

4.2.5 实例化bean的三种方式 —— ④ FactoryBean(实用)—— 实例工厂的升级

4.2.1.1 FactoryBean

//FactoryBean创建对象
public class UserDaoFactoryBean implements FactoryBean<UserDao> {
    //代替原始实例工厂中创建对象的方法
    public UserDao getObject() throws Exception {
        return new UserDaoImpl();
    }

    public Class<?> getObjectType() {
        return UserDao.class;
    }

}

4.2.1.2 配置

<!--方式四:使用FactoryBean实例化bean-->
<bean id="userDao" class="com.li.factory.UserDaoFactoryBean"/>

4.3 bean生命周期

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

4.3.1 bean初始化经历阶段

4.3.1.1 初始化容器

1.创建对象(内存分配)
2.执行构造方法
3.执行属性注入(set操作)
4.执行bean初始化方法

4.3.2 bean销毁时机

  • 容器关闭前触发bean的销毁

4.3.2.1 关闭容器方式

  • 手工关闭容器

    ConfigurableApplicationcontext接口close()操作
    
  • 注册关闭钩子,在虚拟机退出前先关闭容器再退出虚拟机

    ConfigurableApplicationContext接口registerShutdownHook()操作
    
4.3.2.1.1 实例
public class AppForLifeCycle {
    public static void main( String[] args ) {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

        //关闭容器
        ctx.close();
    }
}

4.3.2.2 使用bean

1.执行业务操作

4.3.2.3 关闭销毁容器

1.执行bean销毁方法

(1)提供生命周期控制方法

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

}

(2)配置生命周期控制方法

<!--init-method:设置bean初始化生命周期回调函数-->
    <!--destroy-method:设置bean销毁生命周期回调函数,仅适用于单例对象-->
    <bean id="bookDao" class="com.li.dao.impl.BookDaoImpl" init-method="init" destroy-method="destory"/>

(3)实现InitializingBean, DisposableBean接口(了解即可)

public class BookServiceImpl implements BookService, InitializingBean, DisposableBean {
    private BookDao bookDao;

    public void setBookDao(BookDao bookDao) {
        System.out.println("set .....");
        this.bookDao = bookDao;
    }

    public void save() {
        System.out.println("book service save ...");
        bookDao.save();
    }

    public void destroy() throws Exception {
        System.out.println("service destroy");
    }

    public void afterPropertiesSet() throws Exception {
        System.out.println("service init");
    }
}

五、依赖注入方式(DI)

5.1 思考 —— 向一个类中传递数据的方式有几种?

  • 普通方法(set方法)
  • 构造方法

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

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

5.3 依赖注入方式

setter注入

  • 简单类型
  • 引用类型

构造器注入

  • 简单类型
  • 引用类型

5.4 setter注入 —— 引用类型

在bean中定义引用类型属性并提供可访问的set方法

public class BookServiceImpl implements BookService{
    private BookDao bookDao;
    private UserDao userDao;

    //setter注入需要提供要注入对象的set方法
    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }

配置中使用property标签ref属性注入引用类型对象

<!--注入引用类型-->
    <bean id="bookService" class="com.li.service.impl.BookServiceImpl">
<!--property标签:设置注入属性-->
        <!--name属性:设置注入的属性名,实际是set方法对应的名称-->
        <!--ref属性:设置注入引用类型bean的id或name-->
        <property name="bookDao" ref="bookDao"/>
    </bean>
 <bean id="bookDao" class="com.li.dao.impl.BookDaoImpl">

5.5 setter注入 —— 简单类型

在bean中定义引用类型属性并提供可访问的set方法

public class BookDaoImpl implements BookDao {
    private int connectionNum;
    //setter注入需要提供要注入对象的set方法
    public void setConnectionNum(int connectionNum) {
        this.connectionNum = connectionNum;
    }

配置中使用property标签value属性注入简单类型数据

<!--注入简单类型-->
    <bean id="bookDao" class="com.li.dao.impl.BookDaoImpl">
        <!--property标签:设置注入属性-->
        <!--name属性:设置注入的属性名,实际是set方法对应的名称-->
        <!--value属性:设置注入简单类型数据值-->
        <property name="connectionNum" value="100"/>
    </bean>

5.6 构造器注入 —— 引用类型(了解即可)

在bean中定义引用类型属性并提供可访问的构造方法

public class BookServiceImpl implements BookService{
    private BookDao bookDao;

    public BookServiceImpl(BookDao bookDao) {
        this.bookDao = bookDao;
    }

}

配置中使用constructor-arg标签ref属性注入引用类型对象

<bean id="bookService" class="com.li.service.impl.BookServiceImpl">
        <constructor-arg name="userDao" ref="userDao"/>
        <constructor-arg name="bookDao" ref="bookDao"/>
    </bean>
 //解决形参名称的问题,与形参名不耦合
    <bean id="bookDao" class="com.li.dao.impl.BookDaoImpl">

5.7 构造器注入 —— 简单类型(了解即可)

在bean中定义引用类型属性并提供可访问的set方法

public class BookDaoImpl implements BookDao {
    private int connectionNum;

    public BookDaoImpl(String databaseName, int connectionNum) {
        this.connectionNum = connectionNum;
    }

}

配置中使用constructor-arg标签value属性注入简单类型数据

<bean id="bookDao" class="com.li.dao.impl.BookDaoImpl">
 <constructor-arg name="connectionNum" value="10"/>
        
    </bean>

5.8 依赖注入方式选择

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

六、依赖自动装配

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

6.1 自动装配方式

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

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

<bean class="com.li.dao.impl.BookDaoImpl"/>
    <!--autowire属性:开启自动装配,通常使用按类型装配-->
    <bean id="bookService" class="com.li.service.impl.BookServiceImpl" autowire="byType"/>

6.3 依赖自动装配特征

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

七、集合注入

7.1 数组、List、Set、Map、Properties

 <!--数组注入-->
        <property name="array">
            <array>
                <value>100</value>
                <value>200</value>
                <value>300</value>
            </array>
        </property>
        <!--list集合注入-->
        <property name="list">
            <list>
                <value>itcast</value>
                <value>itheima</value>
                <value>boxuegu</value>
                <value>chuanzhihui</value>
            </list>
        </property>
        <!--set集合注入-->
        <property name="set">
            <set>
                <value>itcast</value>
                <value>itheima</value>
                <value>boxuegu</value>
                <value>boxuegu</value>
            </set>
        </property>
        <!--map集合注入-->
        <property name="map">
            <map>
                <entry key="country" value="china"/>
                <entry key="province" value="henan"/>
                <entry key="city" value="kaifeng"/>
            </map>
        </property>
        <!--Properties注入-->
        <property name="properties">
            <props>
                <prop key="country">china</prop>
                <prop key="province">henan</prop>
                <prop key="city">kaifeng</prop>
            </props>
        </property>

声明:三二一的一的二|版权所有,违者必究|如未注明,均为原创|本网站采用BY-NC-SA协议进行授权

转载:转载请注明原文链接 - Spring框架 _ IoC/DI(控制反转、依赖注入)


三二一的一的二