IoC和DI要结合使用;

1,什么是IoC:(Inversion of Control)

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

    创建对象的控制权由内部转移到外部就称为 : 控制反转

  2. Spring技术对IoC思想进行了实现:

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

      bean 就是对象; IOC容器 : 也可以称为Spring容器;

IoC的容器结构:

  1. - Service层依赖Dao层对象运行;简单来说,就是在IoC容器中:ServiceDao之间构建依赖关系;**(IoC容器用于管理依赖对象)**![image.png](https://cdn.nlark.com/yuque/0/2022/png/25975946/1647997211603-980c958d-4732-4c51-96b7-28360db2b85d.png#clientId=u010c2aa6-1b79-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=131&id=uded02d03&margin=%5Bobject%20Object%5D&name=image.png&originHeight=197&originWidth=817&originalType=binary&ratio=1&rotation=0&showTitle=false&size=13206&status=done&style=none&taskId=u62854f47-f58f-4b1d-ae3f-08a09f5b2e2&title=&width=544.6666666666666)

A,什么是DI:(Dependency Injection)

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

IoC的核心概念:(好处)

  - **充分解决耦合度高的问题:(解耦)**
     - **使用IoC容器管理bean(IoC)**;    或者可以这样说:将Bean交给Spring容器进行管理;
     - 在IoC容器内将有依赖关系的bean进行关系绑定(DI)
  - 最终的效果:
     - 使用对象时不需要在程序**内部**中主动创建对象,需要使用对象的时候,直接到**外部**获取即可**;**
     - 获取到的bean已经绑定了所有的依赖关系

IoC的使用思路:

  - 管理什么?(Service与Dao)
  - 如何将被管理的对象告知IoC容器?(配置)
  - 被管理的对象交给IoC容器,如何获取到IoC容器(接口与实现类)
  - IoC容器得到后,如何从容器中获取bean?(接口方法)
  - 使用Spring导入哪些坐标?(pom.xml)

2,* IoC的创建(使用)步骤:

文件结构: image.png

  1. **导入依赖坐标:(pom.xml):**
<dependency> 
 <groupId>org.springframework</groupId> 
 <artifactId>spring-context</artifactId> 
 <version>5.2.10.RELEASE</version>
</dependency>
  2. **定义Spring管理的类和接口:**
//业务层的实现类
public class BookServiceImpl implements BookService {

    @Override
    public void sava () {

    }
}
//业务层的接口
public interface BookService {

    void sava();

}
  3. **创建Spring配置文件,配置对应类作为Spring管理的bean:**![image.png](https://cdn.nlark.com/yuque/0/2022/png/25975946/1648000421344-a232891d-c9a5-4f1b-9100-29cead035cfb.png#clientId=u911002d7-c729-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=57&id=uf2ac4232&margin=%5Bobject%20Object%5D&name=image.png&originHeight=86&originWidth=383&originalType=binary&ratio=1&rotation=0&showTitle=false&size=4569&status=done&style=none&taskId=u21fb4a5c-1086-4a28-b06a-ce510d0736e&title=&width=255.33333333333334)
<?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="bookService" class="top.jztice5.service.impl.BookServiceImpl"></bean>
</beans>
  4. **初始化IoC容器(Spring核心容器/Spring容器),通过容器获取bean:(这里用测试类进行举例):**

使用类路径创建IoC容器连接的方法:ClassPathXmlApplicationContext(“配置文件类路径”);

    @Test
    public void testBookService(){
        //创建IoC容器对象,加载Spring核心配置文件
        ClassPathXmlApplicationContext cxt = new ClassPathXmlApplicationContext("applicationContext.xml");
        //从IoC容器中获取bean对象
        BookService bookService = (BookService) cxt.getBean("bookService");
        //调用bean对象实现save方法
        bookService.save();
        //关闭容器
        cxt.close();
    }

这里创建对象的时机:启动Spring容器的时候就已经把所有的bean创建;因为没有设置创建时机;(详见下方)

简单来说,bean 就是根据注入的配置 准备 去 new 的一个对象;(bean = 一个对象) 获取bean:就是实现Spring框架将这个bean”生产”出来到这个bean被获取的这个过程/直接将已经生产好的bean获取;


3,什么是DI:(Dependency Injection)

  1. 在IoC容器中建立bean与bean之间的依赖关系的整个过程,称为依赖注入;
  2. 简单来说,就是设置Spring框架创建什么(类型)对象,这个对象与哪个对象产生依赖,创建哪个对象及怎么获取对象,放到哪里;

    A,实现依赖注入的步骤:

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

      调用getBean就会由Spring框架创建;再通过set方法获取这个对象回来放到BookServiceImpl(实现类)中;

/**
    业务层(service)的实现类
*/
public class BookServiceImpl implements BookService {
    //定义数据层(dao)对象;
    //到时候,    调用getBean就会由Spring框架创建;再通过set方法获取这个对象回来放到BookServiceImpl(实现类)中;
    private BookDao bookDao;

    //设置从容器中获取对象的set方法
    public void setBookDao (BookDao bookDao) {
        this.bookDao = bookDao;
    }

    @Override
    public void save() {

    }
}
  2. **在Spring核心配置文件中配置service和dao之间的关系**:
<!--  创建业务对象bean:将对象放到IoC容器中。使用实现类的全名,记住:不是接口  -->
<bean id = "bookService" class = "top.jztice5.service.impl.BookServiceImpl">
  <!--    调用set方法: name:指定set方法中被注入的参数属性名字 ;  ref:是注入的bean对应id(引用类型)    value:简单类型 -->
  <property name = "bookDao" ref = "bookDao"></property>
  <!-- 
      //参数属性名
     public void setBookDao (BookDao bookDao) {
        this.bookDao = bookDao;
    }
 -->
</bean>
<!--  DAO对象也由Spring管理  -->
<bean class = "top.jztice5.dao.impl.BookDaoImpl" id = "bookDao"/>

注入类型的参数: ref:用于注入“引用类型”; value:用于注入“简单类型”(简单类型=基本数据类型+String类型);

B,* 依赖注入的方式及注入的主要类型:(其他的类型在下面)

  - **setter注入*:        <property>**
     - **引用类型:**`<property name = "bookDao" ref = "bookDao"></property>`
<bean id = "bookService" class = "top.jztice5.service.impl.BookServiceImpl">
  <!--    调用set方法: name:指定set方法中被注入的属性名字 ;  ref:是注入的bean对应id(引用类型)    value:简单类型 -->
  <property name = "bookDao" ref = "bookDao"></property>
</bean>
<!--  DAO对象也由Spring管理  -->
<!--  id:bean的id(引用类型) -->
<bean class = "top.jztice5.dao.impl.BookDaoImpl" id = "bookDao"/>
     - **简单类型:**`<property name="connectionNumber" **value**="10"/>`
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
  <property name="connectionNumber" value="10"/>
</bean>
              - 和引用类型不同的是,不用绑定引用类型的id;
  - **构造器注入:        <constructor-arg>**
     - **引用类型:**
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
  <constructor-arg name="bookDao" ref="bookDao"/>
</bean>
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
public class BookDaoImpl implements BookDao {
    private int connectionNumber;
    //有参构造器
    public BookDaoImpl(int connectionNumber) {
        this.connectionNumber = connectionNumber;
    }
}
     - **简单类型:**
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
  <constructor-arg name="connectionNumber" value="10"/>
</bean>
public class BookDaoImpl implements BookDao {
    private int connectionNumber;
    //有参构造器
    public BookDaoImpl(int connectionNumber) {
        this.connectionNumber = connectionNumber;
    }
}
  - **构造器注入——参数适配:(要有对应的有参构造方法)了解**
     1. 按**形参类型**注入:
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
    <constructor-arg type="int" value="10"/>
    <constructor-arg type="java.lang.String" value="mysql"/>
</bean>
     2. 按**形参位置**注入:
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
    <constructor-arg index="0" value="10"/>
    <constructor-arg index="1" value="mysql"/>
</bean>

C,注入方式的选择:

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

除了 setter注入 和 构造器注入 还有 注解注入 ;一个三种注入方式;

· IoC的配置属性(xml方式):

image.png


4,* 整合的IoC代码:

//建议从dao层开始写起:


//dao层:**********
/**
 * dao层接口
 */
public interface BookDao {

}

/**
 * dao层实现类
 */
public class BookDaoImpl implements BookDao {


}



//业务层(service):**********
/**
 * 业务层接口(存放实现方法)
 */
public interface BookService {

    void save();

}

/**
 * 业务层实现类
 */
public class BookServiceImpl implements BookService {
    //private BookService bookService;
    private BookDao bookDao;
    //设置从容器中获取对象的set方法
    public void setBookDao (BookDao bookDao) {
        this.bookDao = bookDao;
    }

    @Override
    public void save () {
        System.out.println("Spring IoC");
    }
}



//Spring的核心配置:**********
<?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:将对象放到IoC容器中。使用实现类的全名,记住:不是接口  -->
    <bean id = "bookService" class = "top.jztice5.service.impl.BookServiceImpl">
        <!--    调用set方法: name:指定set方法中被注入的属性名字 ;  ref:是注入的bean对应id -->
        <property name = "bookDao" ref = "bookDao"/>
    </bean>
    <!--  DAO对象也由Spring管理  -->
    <bean class = "top.jztice5.dao.impl.BookDaoImpl" id = "bookDao"/>

</beans>


//初始化IoC容器,通过容器获取bean对象:
/**
 * 测试类
 */
public class test {

    @Test
    public void testBookService(){
        //创建IoC容器对象,加载Spring核心配置文件
        ClassPathXmlApplicationContext cxt = new ClassPathXmlApplicationContext("applicationContext.xml");
        //从IoC容器中获取bean对象
        BookService bookService = (BookService) cxt.getBean("bookService");
        //用接口对象(bean)调用save方法
        bookService.save();
        //关闭容器
        cxt.close();
    }

}

5,Bean的配置:

· 什么是bean:简单来说,bean 就是一个根据注入的配置 new 好的对象;

A,bean的基础配置: beanimage.png

简单来说,bean 就是一个根据注入的配置 new 好的对象; 获取bean:就是实现Spring框架将这个bean”生产”出来到这个bean被获取的这个过程/直接将已生产的bean获取; IoC是装bean的一个容器(仓库),其实理解为Spring工厂的生产线更为合理;

B,bean的别名配置: nameimage.png

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

C,bean的作用范围配置: scope

image.png

在Spring中,默认每个类只会创建一个对象(单例模式),目的是有利于节省资源;

  • 因此,哪怕是有创建多个对象的代码,也只会存在一个对象,所以,每个对象都是相等的;
  - **设置单例与非单例模式:**
     - `<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"scope="prototype" />`
  - *** 单例与非单例创建对象的时机:**
     - **单例**:当Spring核心文件被加载的时候,就会创建对应配置的对象;
     - **非单例**:只有调用**getBean()**方法调用对应的bean才会创建对应的对象;
  - **bean作用范围的说明**:
     - **适合交给容器进行管理的bean**:
        - 控制器对象 (以前用的Servlet做为控制器);
        - 业务层对象 (如:BookService);
        - 数据层对象 (如:BookDao或BookMapper);
        - 工具对象 (如:CartUtils对象);
     - **不适合交给容器进行管理的bean:**
        - 封装实体的域对象(实体类对象 Book 包名:pojo,entity, domain);

bean的生命周期:

第一种:默认情况下,启动 spring 容器便创建对象(遇到bean便创建对象)

*:设置创建bean对象的时机:(单例模式下的懒加载机制:)

第二种:在中添加属性lazy-init 设置为懒加载,默认值为false。

在spring的配置文件bean中有一个属性(懒加载) lazy-init=“default/true/false”

<bean lazy-init="false/ture">

A,true:在context.getBean的时候才要创建对象

  - **优点**:
     -  如果该bean中有大数据存在,则什么时候context.getBean,什么时候创建对象可以防止数据过早的停留在内存中,做到了懒加载;
  - **缺点**:
     -  如果spring配置文件中,该bean的配置有错误,那么在tomcat容器启动的时候,发现不了

B,false:在启动spring容器的时候创建对象

  - **优点**:    
     - 如果在启动tomcat时要启动spring容器,那么如果spring容器会错误,这个时候tomcat容器不会正常启动;
  - **缺点**:  如果存在大量的数据,会过早的停留在内存中;

注意:该配置只适用于单例模式,而非单例模式:默认调用getBean才创建对象; 懒加载机制只对单例bean有作用,对于多例bean设置懒加载没有意义。 懒加载只是延后了对象创建的时机,对象仍然是单例的。


  - **生命周期:从创建到消亡的完整过程;**
  - **bean生命周期:bean从创建到销毁的整体过程:**
     1. 初始化容器:
        1. 创建对象(内存分配);
        1. 执行构造方法;
        1. 执行属性注入(set操作);
        1. 执行bean初始化方法;
     2. 使用bean:
        1. 执行业务方法操作;
     3. 关闭/销毁容器:
        1. 执行bean销毁方法;
  - **bean生命周期:在bean创建后到销毁前做的一些事情;**

① bean的销毁时机:

  - **关闭容器的方式:**
     - 手动关闭:`close();`
     - 注册关闭钩子,在虚拟机退出前关闭容器再退出虚拟机:`registerShutdownHook();操作`

②,bean的生命周期的控制:

init() bean的初始化方法;
destory() bean的销毁方法;
  - **生命周期控制方法及配置:**
     - `**init-method="init"**`**:初始化方法指向;**
     - `**destroy-method="destory"**`**:销毁方法指向;**
//配置文件代码:
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" init-method="init" destroy-method="destory"/>

//定义类中的控制方法:
    public class BookDaoImpl implements BookDao {
        public void init () {
            System.out.println("book init ...");
        }

        public void destory () {
            System.out.println("book destory ...");
        }
    }

第一种:默认情况下,启动 spring 容器便创建对象(遇到bean便创建对象) 第二种:在spring的配置文件bean中有一个属性(懒加载) lazy-init=“default/true/false”     ①、如果lazy-init为”default/false”在启动spring容器时创建对象(默认情况)      ②、如果lazy-init为”true”,在context.getBean时才要创建对象


6,创建对象的三种方式:

A,使用无参构造方法实例化bean/创建对象(一种):

Spring默认使用无参的构造方法,因此,在出现有参的构造方法时,必须创建一个无参的构造方法(参考JavaSE中的标准类格式);

<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
public class BookDaoImpl implements BookDao {
    //无参构造器
    public BookDaoImpl() {

    }
    //定义类的方法;
}

无参构造器在Spring的用途:仅能创建无属性数据对象及调用无属性数据的方法; 大部分情况下创建对象,都需要用到有参构造器实现对象属性依赖注入;

B,使用静态和实例工厂模式创建对象(两种):(不推荐)

  - **静态工厂:**![image.png](https://cdn.nlark.com/yuque/0/2022/png/25975946/1648009610657-f726bb27-ac10-4439-b6f1-3f7aa9690d7d.png#clientId=u911002d7-c729-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=485&id=u4d98a02f&margin=%5Bobject%20Object%5D&name=image.png&originHeight=728&originWidth=1764&originalType=binary&ratio=1&rotation=0&showTitle=false&size=430377&status=done&style=none&taskId=u7cb8e14c-7ed9-42cd-a8fd-caea7ec65d7&title=&width=1176)
  - **实例工厂:**![image.png](https://cdn.nlark.com/yuque/0/2022/png/25975946/1648009623248-e979616a-b056-4299-92c6-835c05ec0913.png#clientId=u911002d7-c729-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=424&id=u29ae3c57&margin=%5Bobject%20Object%5D&name=image.png&originHeight=636&originWidth=1746&originalType=binary&ratio=1&rotation=0&showTitle=false&size=466805&status=done&style=none&taskId=uc18a7f01-75f1-4551-82e4-5067283d50c&title=&width=1164)
  - 两者区别:是否静态;

7,依赖的自动装配:

A,自动装配的概念:

  - `**IoC容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称为自动装配;**`
     - **自动装配的配置目录所在的文件:spring.factories ;**

B,自动装配的方式:

  - 按类型(常用)byType
  - 按名称 byName
  - 按构造方法 constructor
  - 不启用自动装配 no

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

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

D,依赖自动装配的特征:

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

8,* 集合属性对象的注入:

A,注入数组对象:

//配置文件:
      <bean id="bookService" class="top.jztice5.service.impl.BookServiceImpl">
        <property name = "array">
            //数组
           <array>
                <value>100</value>
                <value>200</value>
                <value>300</value>
           </array>
        </property>
       </bean>

//定义类:
public class BookServiceImpl implements BookService {
    private int[] array;    
    public void setArray (int[] array) {
        this.array = array;
    }
}

B,* 注入list对象(重点):

//配置文件:
      <bean id="bookService" class="top.jztice5.service.impl.BookServiceImpl">
        <property name = "array">
            //数组
           <list>
                <value>100</value>
                <value>200</value>
                <value>300</value>
           </list>
        </property>
       </bean>

//定义类:
public class BookServiceImpl implements BookService {
    private List<Integer> list;

    public void setList (List <Integer> list) {
        this.list = list;
    }
}

C,注入set对象:和List同理,略

这里的List,set对象和array(数组)对象的区别只是在语义上有不同,实际上用哪一个都是一样的效果的;

D,* 注入Map对象(重点):

//配置文件:
    <bean id="bookService" class="top.jztice5.service.impl.BookServiceImpl">
        <property name="map">
            <map>
                <entry key="country" value="china"/>
                <entry key="province" value="henan"/>
                <entry key="city" value="kaifeng"/>
            </map>
        </property>
    </bean>

//定义类:
public class BookServiceImpl implements BookService {
    private Map<String, String> map;
    public void setMap (Map <String, String> map) {
        this.map = map;

    }
}

注意:这里注入的是简单类型;如果想更换为引用类型的话,将key和value改为key-ref和value-ref即可;

E,注入Properties对象:

(注意:Properties对象的结构是String类型的键值对)

    //配置文件
    <bean id="bookService" class="top.jztice5.service.impl.BookServiceImpl">
        <property name="properties">
            <props>
                <prop key="country">china</prop>
                <prop key="province">henan</prop>
                <prop key="city">kaieng</prop>
            </props>
        </property>
    </bean>


//定义类:
public class BookServiceImpl implements BookService {
    Properties properties = new Properties();

    public void setProperties (Properties properties) {
        this.properties = properties;
    }
}