跳至主要內容

Spring IOC 与 DI

晨光-向大约 27 分钟JavaSpringJavaSpring

Spring IOC 与 DI

1. IOC 控制反转 [Inversion Of Control]

1. 控制反转解决色问题

原来: 我们在获取对象时,都是采用 new 的方式。是主动的

现在:我们获取对象时,同时跟工厂要,有工厂为我们查找或者创建对象。是被动的

这种被动接收的方式获取对象的思想就是控制反转,它是 spring 框架的核心之一。

我的理解:

1.之前有自己new创建对象 [主动]

2.先在由媒人帮我找对象 [被动]

2. IOC的概述 解耦合

  1. 控制反转: 把创建的对象权利交给框架(工厂),是框架的重要特性,并非面向对象编程的专用术语,它包含**依赖注入(DI)**和依赖查询

  2. 明确 ioc 的作用:削减计算机程序的耦合(解除我们代码中的依赖关系)。降低程序间的耦合(依赖关系)

2. IOC入门应用

1. 依赖

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

2. 配置文件

  • bean.xml
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--1. 把对象的创建交给Spring来管理-->
    <!--accountService-->
    <bean id="accountService" class="com.chggx.service.impl.AccountServiceImpl"/>
    <!--accountDao-->
    <bean id="accountDao" class="com.chggx.dao.impl.AccountDaoImpl"/>

</beans>

3. ApplicationContext操作

ApplicationContext接口

  1. ClassPathXmlApplicationContext

    • 可以加载类路径下的配置文件,要求配置文件必须在类路径下,不在的话,加载不了.[推荐]
  2. FileSystemXmlApplicationContext

    • 它可以加载磁盘任意路径下的配置文件(必须有访问权限)
  3. AnnotationConfigApplicationContext

    • 它是用于读取注解创建容器的.
   /**
     * 获取spring的Ioc核心容器,并根据id获取对象
     * ApplicationContext:
     *  1. ClassPathXmlApplicationContext: 可以加载类路径下的配置文件,要求配置文件必须在类路径下,不在的话,加载不了.[推荐]
     *  2. FileSystemXmlApplicationContext: 它可以加载磁盘任意路径下的配置文件(必须有访问权限)
     *  3. AnnotationConfigApplicationContext: 它是用于读取注解创建容器的.
     */
    public static void main(String[] args) {
        // 1. 获取核心容器兑现
//        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        ApplicationContext ac = new FileSystemXmlApplicationContext("D:\\java-code\\kuangjia\\spring\\spring\\spring_03_ioc\\src\\main\\resources\\bean.xml");
        // 2. 根据id获取bean对象
        AccountService accountService = (AccountService) ac.getBean("accountService");
        AccountDao accountDao = ac.getBean("accountDao", AccountDao.class);

        System.out.println(accountService);
        System.out.println(accountDao);
    }

3. IOC核心容器的两个接口

1. ApplicationContext 单例对象使用

ApplicationContext: [单例对象使用 如: service,dao]
    它在构建核心容器时,创建对象策略是采用立即加载的方式,也就是说,只要一读取完配置文件(bean.xml)马上就创建配置文件中配置的对象(通过反射实现)
: 以下代码 [立即加载]
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml"); 读取配置文件时候,通过反射就创建对象了

2. BeanFactory 多例对象使用

BeanFactory: [多例对象使用]
    它在构建核心容器时,创建对象的策略采用延迟加载的方式,也就是说,什么时候根据id(bean容器对象的唯一标识)获取对象了,什么时候才真正的创建对象.
: 以下代码 [延迟加载]
        BeanFactory factory = new XmlBeanFactory(resource); 读取配置文件
        AccountService accountService = (AccountService) factory.getBean("accountService"); 根据ID调用对象时候,才创建对象

3. 使用

/**
     * 1. 获取spring的Ioc核心容器,并根据id获取对象
     *  ApplicationContext:
     *      1). ClassPathXmlApplicationContext: 可以加载类路径下的配置文件,要求配置文件必须在类路径下,不在的话,加载不了.[推荐]
     *      2). FileSystemXmlApplicationContext: 它可以加载磁盘任意路径下的配置文件(必须有访问权限)
     *      3). AnnotationConfigApplicationContext: 它是用于读取注解创建容器的.
     *  2. 核心容器的两个接口引发出的问题:
     *  ApplicationContext: [单例对象使用 如: service,dao]
     *      它在构建核心容器时,创建对象策略是采用立即加载的方式,也就是说,只要一读取完配置文件(bean.xml)马上就创建配置文件中配置的对象(通过反射实现)
     *      如: 以下代码 [立即加载]
     *          ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml"); 读取配置文件时候,通过反射就创建对象了
     *  BeanFactory: [多例对象使用]
     *      它在构建核心容器时,创建对象的策略采用延迟加载的方式,也就是说,什么时候根据id(bean容器对象的唯一标识)获取对象了,什么时候才真正的创建对象.
     *      如: 以下代码 [延迟加载]
     *          BeanFactory factory = new XmlBeanFactory(resource); 读取配置文件
     *          AccountService accountService = (AccountService) factory.getBean("accountService"); 根据ID调用对象时候,才创建对象
     */
    public static void main(String[] args) {

        // ============= ApplicationContext [立即加载] =============

//        // 1. 获取核心容器对象 [推荐 ClassPathXmlApplicationContext方式创建]
//        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
////        ApplicationContext ac = new FileSystemXmlApplicationContext("D:\\java-code\\kuangjia\\spring\\spring\\spring_03_ioc\\src\\main\\resources\\bean.xml");
//        // 2. 根据id获取bean对象 [service,dao都只创建一次,单例模式]
//        AccountService accountService = (AccountService) ac.getBean("accountService");
//        AccountDao accountDao = ac.getBean("accountDao", AccountDao.class);
//
//        System.out.println(accountService);
//        System.out.println(accountDao);

        // ============= BeanFactory [延迟加载] =============
        // 1. 加载配置文件"bean.xml"
        ClassPathResource resource = new ClassPathResource("bean.xml");
        // 2. 读取配置文件
        BeanFactory factory = new XmlBeanFactory(resource);
        // 3. 根据id创建Bean对象
        AccountService accountService = (AccountService) factory.getBean("accountService");
        System.out.println(accountService);

    }

4. spring对bean的管理细节

1. 创建Bean的三种方式

1. 构造函数创建对象
第一种方式: 在spring的配置文件中使用bean标签,配以id和class属性之后,且没有其他属性和标签时,采用的就是默认构造函数创建bean对象,此时如果类中没有默认的构造函数,则对象无法创建
  • 配置文件
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--
         spring对bean的管理细节:
             1. 创建Bean的三种方式
             2. bean对象的作用范围
             3. bean对象的生命周期
    -->
    <!--创建Bean的三种方式-->
    <!--
        第一种方式: 使用默认的构造函数创建
                    在spring的配置文件中使用bean标签,配以id和class属性之后,且没有其他属性和标签时,采用的就是默认构造函数创建bean对象,此时如果类中没有默认的构造函数,则对象无法创建
     -->
    <bean id="accountService" class="com.chggx.service.impl.AccountServiceImpl"/>


</beans>
  • 类 [构造函数]
public class AccountServiceImpl implements AccountService {

    /**
     * 创建bean的方式: 构造函数
     */
    public AccountServiceImpl() {
        System.out.println("AccountServiceImpl 对象创建了...");
    }

    /**
     * 模拟保存账户
     */
    @Override
    public void saveAccount(){
        System.out.println("service saveAccount() ......");
    }
}
2. 普通工厂的方法创建对象
第二种方式: 使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器)
  • 工厂类
package com.chggx.factory;

import com.chggx.service.AccountService;
import com.chggx.service.impl.AccountServiceImpl;

/**
 * @Description: <h1> 模拟一个工厂类 </h1>
 * 该类可能存在于jar包中的,我们无法通过修改源码的方式来提供默认构造函数
 */
public class InstanceFactory {

    /**
     * 构造函数
     * @return AccountService
     */
    public AccountService getAccountService(){
        return new AccountServiceImpl();
    }

}
  • 配置文件
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--1. 把对象的创建交给Spring来管理-->
    <!--
         spring对bean的管理细节:
             1. 创建Bean的三种方式
             2. bean对象的作用范围
             3. bean对象的生命周期
    -->
    <!--创建Bean的三种方式-->
    <!--
        第一种方式: 使用默认的构造函数创建
                    在spring的配置文件中使用bean标签,配以id和class属性之后,且没有其他属性和标签时,
                    采用的就是默认构造函数创建bean对象,此时如果类中没有默认的构造函数,则对象无法创建
    <bean id="accountService" class="com.chggx.service.impl.AccountServiceImpl"/>
     -->
    <!--第二种方式: 使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器)-->
    <bean id="instanceFactory" class="com.chggx.factory.InstanceFactory"/>
    <bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"/>


</beans>
3. 工厂中的静态方法创建对象
第三种方式: 使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入spring容器)
  • 工厂类
package com.chggx.factory;

import com.chggx.service.AccountService;
import com.chggx.service.impl.AccountServiceImpl;

/**
 * @Description: <h1> 模拟一个工厂类 </h1>
 * 该类可能存在于jar包中的,我们无法通过修改源码的方式来提供默认构造函数
 */
public class StaticFactory {

    /**
     * 构造函数
     * @return AccountService
     */
    public static AccountService getAccountService(){
        return new AccountServiceImpl();
    }

}
  • 配置文件
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--1. 把对象的创建交给Spring来管理-->
    <!--
         spring对bean的管理细节:
             1. 创建Bean的三种方式
             2. bean对象的作用范围
             3. bean对象的生命周期
    -->
    <!--创建Bean的三种方式-->
    <!--
        第一种方式: 使用默认的构造函数创建
                    在spring的配置文件中使用bean标签,配以id和class属性之后,且没有其他属性和标签时,
                    采用的就是默认构造函数创建bean对象,此时如果类中没有默认的构造函数,则对象无法创建
    <bean id="accountService" class="com.chggx.service.impl.AccountServiceImpl"/>
     -->

    <!--第二种方式: 使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器)-->
   <!--
    <bean id="instanceFactory" class="com.chggx.factory.InstanceFactory"/>
    <bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"/>
    -->

    <!--第三种方式: 使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入spring容器)-->
    <bean id="accountService" class="com.chggx.factory.StaticFactory" factory-method="getAccountService"/>

</beans>

2. bean对象的作用范围 scope

bean的scope属性:

作用: 用于指定bean的作用范围
取值: [常用单例的和多例的]
类型描述
singleton单例的(默认值)
prototype多例的
request作用于web应用的请求范围
session作用于web应用的会话范围
global-session作用于集群环境的会话范围(全局会话范围),当不是集群环境时,它就是session
  • 应用
<bean id="accountService" class="com.chggx.service.impl.AccountServiceImpl" scope="prototype"/>

3. bean对象的生命周期

  • 单例对象的生命周期和容器相同
1. 单例 立即加载
  • 配置文件
<!--bean对象生命周期-->
<!--
    单例对象: [立即加载]
        出生: 当容器创建时对象创建
        活着: 只要容器还在,对象一直存在
        死亡: 容器销毁,对象消亡
       总结: 单例对象的生命周期和容器相同
 -->
<!--init-method="init": 初始化 destroy-method="destroy": 销毁-->
<bean id="accountService" class="com.chggx.service.impl.AccountServiceImpl"
      scope="singleton" init-method="init" destroy-method="destroy" />
  • 业务方法
public class AccountServiceImpl implements AccountService {

    /**
     * 创建bean的方式: 构造函数
     */
    public AccountServiceImpl() {
        System.out.println("AccountServiceImpl 对象创建了...");
    }

    /**
     * 模拟保存账户
     */
    @Override
    public void saveAccount(){
        System.out.println("service saveAccount() ......");
    }

    /**
     * 初始化
     */
    public void init(){
        System.out.println("object init...");
    }

    /**
     * 结束销毁
     */
    public void destroy(){
        System.out.println("object destroy...");
    }
}
  • 调用方法
public class AccountController {
    
    public static void main(String[] args) {

        // ============= ApplicationContext [立即加载] =============

        // 1. 获取核心容器对象 [推荐 ClassPathXmlApplicationContext方式创建]
        // 多态
//        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        // 2. 根据id获取bean对象 [service,dao都只创建一次,单例模式]
        AccountService accountService = (AccountService) ac.getBean("accountService");
        // 3. 调用方法
        accountService.saveAccount();

        // 手动关闭容器destroy
        ac.close();
    }

}
  • 执行结果
AccountServiceImpl 对象创建了...
object init...
service saveAccount() ......
object destroy...
2. 多例 懒加载
  • 配置文件
<!--bean对象生命周期-->
<!--
    单例对象: [立即加载]
        出生: 当容器创建时对象创建
        活着: 只要容器还在,对象一直存在
        死亡: 容器销毁,对象消亡
       总结: 单例对象的生命周期和容器相同
    多例对象: [懒加载]
        出生: 当我们使用对象时spring框架为我们创建
        活着: 对象只要是在使用过程中就一直存在
        死亡: 当对象长时间不用,且没有别的对象引用时,有Java的垃圾回收器回收
 -->
<!--init-method="init": 初始化 destroy-method="destroy": 销毁-->
<!--单例: 立即加载-->
<!--    <bean id="accountService" class="com.chggx.service.impl.AccountServiceImpl"
        scope="singleton" init-method="init" destroy-method="destroy" />-->
<!--多例: 懒加载-->
<bean id="accountService" class="com.chggx.service.impl.AccountServiceImpl"
      scope="prototype" init-method="init" destroy-method="destroy"/>
  • 结果
AccountServiceImpl 对象创建了...
object init...
service saveAccount() ......

5. 依赖注入 DI

1. 概述

  • 依赖注入: Dependency Injection

  • IOC作用: 降低程序间的耦合(依赖关系)

  • 依赖关系管理: 以后都交给spring维护

 * 在当前类需要用到其类的对象,有spring为我们提供,我们只需要在配置文件中说明
  • 依赖关系的维护: 称之为 依赖注入.

2. 注入方式

1. 构造方法
  1. 使用标签: constructor-arg
  2. 标签出现位置: bean标签的内部
  3. 标签中的属性
属性描述
type用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数类型
index用于指定要注入的数据给构造函数中指定指定索引位置的参数赋值,所以得位置从0开始
name用于指定给构造函数中名称的参数赋值 [推荐]
value用于基本类型和String类型的数据
ref用于指定其他的bean类型数据.它指的就是spring的Ioc核心容器中出现过的bean对象
  1. 优势
    • 在获取bean对象时,注入数据是必须操作.否则对象无法创建成功
  2. 弊端
    • 改变了bean对象的实例化方式,使我们创建对象时,如果用不到这些数据,也必须提供
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--1. 把对象的创建交给Spring来管理-->
    <!--
     spring中的依赖注入 DI:
        1. 依赖注入: Dependency Injection
        2. IOC作用: 降低程序间的耦合(依赖关系)
        3. 依赖关系管理: 以后都交给spring维护
         * 在当前类需要用到其类的对象,有spring为我们提供,我们只需要在配置文件中说明
        4. 依赖关系的维护: 称之为 依赖注入.
        5. 依赖注入:
            5.1 能注入的数据: [3类]
                基本类型
                其他bean类型(在配置文件中或者注解配置过得bean)
                复杂类型/集合类型
            5.2 注入的方式: [3种]
                第一种: 使用构造方法
                第二种: 使用set方法提供
                第三种: 使用注解提供
    -->

    <!--
    构造函数注入
        使用标签: constructor-arg
        标签出现位置: bean标签的内部
        标签中的属性
            type: 用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数类型
            index: 用于指定要注入的数据给构造函数中指定指定索引位置的参数赋值,所以得位置从0开始
            name: 用于指定给构造函数中名称的参数赋值 [推荐]
            ============= 以上三个用于指定给构造函数中哪个参数赋值 ===========
            value: 用于基本类型和String类型的数据
            ref: 用于指定其他的bean类型数据.它指的就是spring的Ioc核心容器中出现过的bean对象
        优势:
            在获取bean对象时,注入数据是必须操作.否则对象无法创建成功
        弊端:
            改变了bean对象的实例化方式,使我们创建对象时,如果用不到这些数据,也必须提供
    -->
    <bean id="accountService" class="com.chggx.service.impl.AccountServiceImpl">
        <constructor-arg name="name" value="构造函数注入"/>
        <!--String可自动转换为Integer-->
        <constructor-arg name="age" value="18"/>
        <!--Date: 需要配置日期对象-->
        <constructor-arg name="birthday" ref="now"/>
    </bean>

    <!--配置一个日期对象-->
    <bean id="now"  class="java.util.Date"/>


</beans>
  • 构造方法类
public class AccountServiceImpl implements AccountService {

    /**
     * 如果是经常变化的数据,并不适用与注入方式
     */
    private String name;
    private Integer age;
    private Date birthday;

    /**
     * 依赖注入: 使用构造方法
     */
    public AccountServiceImpl(String name, Integer age, Date birthday) {
        this.name = name;
        this.age = age;
        this.birthday = birthday;
    }

    /**
     * 模拟保存账户
     */
    @Override
    public void saveAccount() {
        System.out.println("service saveAccount() ......" + name + "," + age + "," + birthday);
    }
}
  • 应用
public class AccountController {

    public static void main(String[] args) {

        // ============= ApplicationContext [立即加载] =============

        // 1. 获取核心容器对象 [推荐 ClassPathXmlApplicationContext方式创建]
        // 多态
//        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");

        // ============= 依赖注入方式: 构造方法 ==============
        // 2. 根据id获取bean对象 [service,dao都只创建一次,单例模式]
        AccountService accountService = (AccountService) ac.getBean("accountService");
        // 3. 调用方法
        accountService.saveAccount();

}
2. set方法 [推荐: 可以使用默认的构造方法]
  1. 使用标签: property
  2. 标签出现位置: bean标签的内部
  3. 标签中的属性
属性描述
name用于指定给构造函数中名称的参数赋值
value用于基本类型和String类型的数据
ref用于指定其他的bean类型数据.它指的就是spring的Ioc核心容器中出现过的bean对象
  1. 优势

    • 创建对象时没有明确的限制,可以直接使用默认的构造函数
  2. 弊端

    • 如果有某个成员那必须有值,则获取对象是有可能set方法没有执行
  • 配置文件
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--1. 把对象的创建交给Spring来管理-->
    <!--
     spring中的依赖注入 DI:
        1. 依赖注入: Dependency Injection
        2. IOC作用: 降低程序间的耦合(依赖关系)
        3. 依赖关系管理: 以后都交给spring维护
         * 在当前类需要用到其类的对象,有spring为我们提供,我们只需要在配置文件中说明
        4. 依赖关系的维护: 称之为 依赖注入.
        5. 依赖注入:
            5.1 能注入的数据: [3类]
                基本类型
                其他bean类型(在配置文件中或者注解配置过得bean)
                复杂类型/集合类型
            5.2 注入的方式: [3种]
                第一种: 使用构造方法
                第二种: 使用set方法提供
                第三种: 使用注解提供
    -->
    <!--配置一个日期对象-->
    <bean id="now"  class="java.util.Date"/>

    <!--
    set方法注入 [推荐]
        使用标签: property
        标签出现位置: bean标签的内部
        标签中的属性
            name: 用于指定注入是所调用的set方法名称
                set方法名称: setName ==== name
            value: 用于基本类型和String类型的数据
            ref: 用于指定其他的bean类型数据.它指的就是spring的Ioc核心容器中出现过的bean对象
        优势:
            创建对象时没有明确的限制,可以直接使用默认的构造函数
        弊端:
            如果有某个成员那必须有值,则获取对象是有可能set方法没有执行
    -->
    <bean id="accountService2" class="com.chggx.service.impl.AccountService2Impl">
        <property name="name" value="set方法注入"/>
        <property name="age" value="21"/>
        <property name="birthday" ref="now"/>
    </bean>

</beans>
  • set方法类
public class AccountService2Impl implements AccountService {

    /**
     * 如果是经常变化的数据,并不适用与注入方式
     */
    private String name;
    private Integer age;
    private Date birthday;

    /**
     * 依赖注入: 使用set方法
     */
    public void setName(String name) {
        this.name = name;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    /**
     * 模拟保存账户
     */
    @Override
    public void saveAccount() {
        System.out.println("service saveAccount() ......" + name + "," + age + "," + birthday);
    }
}
  • 应用
public class AccountController {

    public static void main(String[] args) {

        // ============= ApplicationContext [立即加载] =============

        // 1. 获取核心容器对象 [推荐 ClassPathXmlApplicationContext方式创建]
        // 多态
//        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");

        // ============= 依赖注入方式: set方法 ==============
        // 2. 根据id获取bean对象 [service,dao都只创建一次,单例模式]
        AccountService accountService2 = (AccountService) ac.getBean("accountService2");
        // 3. 调用方法
        accountService2.saveAccount();
    }

}
3. 注解方式
  • 详细参考 ioc 注解

3. 能注入的数据

1. 基本类型
  • 参考 注入方式 中的使用
2. 其他bean类型(在配置文件中或者注解配置过得bean)
  • 参考 注入方式 中的使用
3. 复杂类型/集合类型
  • 结论: 结构相同,标签可以互换
  1. 配置文件
<!--复杂类型的注入/集合类型的注入-->
<!--
    用于list结构集合注入的标签: list,array,set
    用于Map结构集合注入的标签: map,prop
    * 结论: 结构相同,标签可以互换
-->
<bean name="accountService3" class="com.chggx.service.impl.AccountService3Impl">
    <!--1. 数组-->
    <property name="myStr">
        <array>
            <value>array1</value>
            <value>array2</value>
            <value>array3</value>
        </array>
    </property>
    <!--2. list集合-->
    <property name="myList">
        <list>
            <value>list1</value>
            <value>list2</value>
            <value>list3</value>
        </list>
    </property>
    <!--3. set集合-->
    <property name="mySet">
        <set>
            <value>set1</value>
            <value>set2</value>
            <value>set3</value>
        </set>
    </property>
    <!--4. map集合-->
    <property name="myMap">
        <map>
            <entry key="map1" value="aaa"/>
            <entry key="map2" value="bbb"/>
        </map>
    </property>
    <!--5. map集合-->
    <property name="myProps">
        <props>
            <prop key="prop1">111</prop>
            <prop key="prop2">222</prop>
        </props>
    </property>
</bean>
public class AccountService3Impl implements AccountService {

    /**
     * 能注入的数据类型:  复杂类型/集合类型
     */
    private String[] myStr;
    private List<String> myList;
    private Set<String> mySet;
    private Map<String,String> myMap;
    private Properties myProps;

    /**
     * 依赖注入: 使用set方法
     */
    public void setMyStr(String[] myStr) {
        this.myStr = myStr;
    }

    public void setMyList(List<String> myList) {
        this.myList = myList;
    }

    public void setMySet(Set<String> mySet) {
        this.mySet = mySet;
    }

    public void setMyMap(Map<String, String> myMap) {
        this.myMap = myMap;
    }

    public void setMyProps(Properties myProps) {
        this.myProps = myProps;
    }

    /**
     * 模拟保存账户
     */
    @Override
    public void saveAccount() {
        System.out.println(Arrays.toString(myStr));
        System.out.println(myList);
        System.out.println(mySet);
        System.out.println(myMap);
        System.out.println(myProps);
    }
}
  1. 应用
public class AccountController {

    public static void main(String[] args) {

        // ============= ApplicationContext [立即加载] =============

        // 1. 获取核心容器对象 [推荐 ClassPathXmlApplicationContext方式创建]
        // 多态
//        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");

        // ============= 能注入的数据类型: 复杂类型/集合类型 set方法注入 ==============
        // 2. 根据id获取bean对象 [service,dao都只创建一次,单例模式]
        AccountService accountService3 = (AccountService) ac.getBean("accountService3");
        // 3. 调用方法
        accountService3.saveAccount();

    }

}

6. ioc注解

1. 用于创建对象的注解

  • 它们的作用就和XML配置文件中编写一个标签实现的功能是一样的
注解作用属性
@Component用于把当前类作为对象,存入spring容器中value:用于指定bean的id.当我们不写时,它默认是当前类名,且首字母改小写
@Controller一般用于表现层属性/作用同上
@Service一般用于业务层属性/作用同上
@Repository一般用于持久层属性/作用同上
1. @Component
/**
 * @Description: <h1> 账户业务层实现类 </h1>
 * xml配置
 *     <bean id="accountService" class="com.chggx.service.impl.AccountServiceImpl"/>
 *     属性: id,class,scope,init-method,destroy-method,property,name,value|ref
 *  注解
 *      1. 用于创建对象的注解:
 *          它们的作用就和XML配置文件中编写一个<bean></bean>标签实现的功能是一样的
 *          @Component:
 *              作用: 用于把当前类作为对象,存入spring容器中
 *              属性:
 *                  value: 用于指定bean的id.当我们不写时,它默认是当前类名,且首字母改小写
 				衍生注解: 下面三个 与 @Component 的作用和属性一模一样,是spring框架为我们提供明确的三层使用的注解,使用我们的三层对象更加清晰
 *                  @Controller: 一般用于表现层
 *                  @Service: 一般用于业务层
 *                  @Repository: 一般用于持久层
 
 *      2. 用于注入数据的
 *          它们的作用就和XML配置文件中编写一个bean标签写一个<property></property>的标签是一样的
 *      3. 用于改变作用范围的
 *          它们的作用就和XML配置文件中编写一个bean标签使用scope属性实现的功能是一样的
 *      4. 和生命周期有关的
 *          它们的作用就和XML配置文件中编写一个bean标签使用init-method,destroy-method的作用是一样的
 */
@Component(value="accountServiceImpl")
public class AccountServiceImpl implements AccountService {

    /**
     * 普通方式 (耦合度高)
     */
    private AccountDao accountDao;

    public AccountServiceImpl() {
        System.out.println("AccountServiceImpl 对象创建了...");
    }

    /**
     * 模拟保存账户
     */
    @Override
    public void saveAccount() {
        accountDao.saveAccount();
    }
}
  • 配置文件 (开启包扫描,使用注解)
<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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <!--把对象的创建交给Spring来管理-->
    <!--1. xml方式实现-->
    <!--accountService-->
    <!--<bean id="accountService" class="com.chggx.service.impl.AccountServiceImpl"/>-->
    <!--accountDao-->
    <!--<bean id="accountDao" class="com.chggx.dao.impl.AccountDaoImpl"/>-->

    <!--2. 注解方式实现-->
    <!--
    告知spring在创建容器时要扫描的包,配置所需要的标签不是在beans的约束中,而是一个名称为content名称空间和约束中
    -->
    <!--包扫描-->
    <context:component-scan base-package="com.chggx"/>

</beans>
  • 应用
public class AccountController {

    public static void main(String[] args) {
        // ============= ApplicationContext [立即加载] =============

        // 1. 获取核心容器对象 [推荐 ClassPathXmlApplicationContext方式创建]
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        // 2. 根据id获取bean对象 [service,dao都只创建一次,单例模式]
        AccountService accountService = (AccountService) ac.getBean("accountServiceImpl");

        System.out.println(accountService);
    }

}
2. 衍生注解使用
  • dao
@Repository(value = "accountDao")
public class AccountDaoImpl implements AccountDao {

    /**
     * 模拟保存账户
     */
    @Override
    public void saveAccount() {
        System.out.println("save success...");
    }
}
  • service
@Service(value="accountService")
public class AccountServiceImpl implements AccountService {

    private AccountDao accountDao;

//    public AccountServiceImpl() {
//        System.out.println("AccountServiceImpl 对象创建了...");
//    }

    /**
     * 模拟保存账户
     */
    @Override
    public void saveAccount() {
        accountDao.saveAccount();
    }
}
  • 应用
public class AccountController {

    public static void main(String[] args) {
        // ============= ApplicationContext [立即加载] =============

        // 1. 获取核心容器对象 [推荐 ClassPathXmlApplicationContext方式创建]
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        // 2. 根据id获取bean对象 [service,dao都只创建一次,单例模式]
        AccountService accountService = (AccountService) ac.getBean("accountService");
        System.out.println(accountService);

        AccountDao accountDao = ac.getBean("accountDao", AccountDao.class);
        System.out.println(accountDao);
    }

}

2. 用于注入数据的

  • 它们的作用就和XML配置文件中编写一个bean标签写一个的标签是一样的
注解作用类型
@Autowired注入数据(按照类型)注入其他bean类型
@Qualifier注入数据(配置和@Autowired解决多类型注入问题注入其他bean类型
@Resource注入数据(取代上面两个注解,根据bean的id注入)注入其他bean类型
@Value注入基本数据类型和String类型基本类型和String类型
1. @Autowired 自动按照类型注入
作用: 自动按照类型注入.
	  1. 容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功
      2. 如果ioc容器中没有任何bean的类型和要注入变量类型匹配,则报错(不能注入)
      3. 如果ioc容器中有多个类型匹配时,报错,引出新的注解: @Qualifier
出现位置: 可以是变量上,也可以是方法上
细节: 使用注解注入时,set方法不是必须的
  • 问题

如果ioc容器中有多个类型匹配时 使用@Autowired怎样操作?

2. @Qualifier 解决@Autowired多类型匹配
作用: 在自动按照类中注入的基础之上,在按照名称注入.
      它在给类成员注入时不能单独使用.但是在给方法参数注入时可以
属性:
    value: 用于指定注入bean的id
  • dao
@Repository(value = "accountDao2")
public class AccountDao2Impl implements AccountDao {

    /**
     * 模拟保存账户
     */
    @Override
    public void saveAccount() {
        System.out.println("save success 222...");
    }
}
@Repository(value = "accountDao1")
public class AccountDaoImpl implements AccountDao {

    /**
     * 模拟保存账户
     */
    @Override
    public void saveAccount() {
        System.out.println("save success 111...");
    }
}
  • service调用
@Service(value="accountService")
public class AccountServiceImpl implements AccountService {

    @Autowired
    @Qualifier(value = "accountDao1")
    private AccountDao accountDao;

    /**
     * 模拟保存账户
     */
    @Override
    public void saveAccount() {
        accountDao.saveAccount();
    }
}
3. @Resource 替换上边两个
 作用: 直接按照bean的id注入.可以单独使用
 属性:
    name: 用于指定bean的id
@Service(value="accountService")
public class AccountServiceImpl implements AccountService {

//    @Autowired
//    @Qualifier(value = "accountDao1")
    @Resource(name = "accountDao1")
    private AccountDao accountDao;

//    public AccountServiceImpl() {
//        System.out.println("AccountServiceImpl 对象创建了...");
//    }

    /**
     * 模拟保存账户
     */
    @Override
    public void saveAccount() {
        accountDao.saveAccount();
    }
}
4. @Value 注入基本类型和String类型
作用: 用于注入基本类型和String类型的数据
属性:
   value: 用于指定数据的值,可以使用spring中SpEL(也就是spring的el表达式)
          SpEL写法: ${表达式}
5. 总结
  1. @Autowired,@Qualifier,@Resource: 只能注入其他bean类型,而基本类型和String类型无法使用上述注解实现,另外,集合类型的注入只能通过xml来实现

  2. @Value 解决了 基本类型和String类型 的注入

3. 用于改变作用范围的

  • 它们的作用就和XML配置文件中编写一个bean标签使用scope属性实现的功能是一样的

@Scope

作用: 用于指定bean的作用范围
属性:
    value: 指定范围的取值,常用取值: singleton(默认),property
@Scope(value = "singleton") // 方法时

4. 和生命周期有关的

  • 它们的作用就和XML配置文件中编写一个bean标签使用init-method,destroy-method的作用是一样的
1. @PreDestroy 销毁
  • 作用: 用于指定销毁方法
2. @PostConstruct 初始胡
  • 用于指定初始化方法
3. 应用
  • 方法
/**
 * @Description: <h1> 账户业务层实现类 </h1>
 * xml配置
 *     <bean id="accountService" class="com.chggx.service.impl.AccountServiceImpl"/>
 *     属性: id,class,scope,init-method,destroy-method,property,name,value|ref
 *  注解
 *      1. 用于创建对象的注解:
 *          它们的作用就和XML配置文件中编写一个<bean></bean>标签实现的功能是一样的
 *          @Component:
 *              作用: 用于把当前类作为对象,存入spring容器中
 *              属性:
 *                  value: 用于指定bean的id.当我们不写时,它默认是当前类名,且首字母改小写
 *              衍生注解: 下面三个 与 @Component 的作用和属性一模一样,是spring框架为我们提供明确的三层使用的注解,使用我们的三层对象更加清晰
 *                  @Controller: 一般用于表现层
 *                  @Service: 一般用于业务层
 *                  @Repository: 一般用于持久层
 *
 *      2. 用于注入数据的
 *          它们的作用就和XML配置文件中编写一个bean标签写一个<property></property>的标签是一样的
 *          @Autowired
 *              作用: 自动按照类型注入.
 *                    1. 容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功
 *                    2. 如果ioc容器中没有任何bean的类型和要注入变量类型匹配,则报错(不能注入)
 *                    3. 如果ioc容器中有多个类型匹配时,报错,引出新的注解 @Qualifier
 *              出现位置: 可以是变量上,也可以是方法上
 *              细节: 使用注解注入时,set方法不是必须的
 *          @Qualifier:
 *              作用: 在自动按照类中注入的基础之上,在按照名称注入.
 *                    它在给类成员注入时不能单独使用.但是在给方法参数注入时可以
 *              属性:
 *                  value: 用于指定注入bean的id
 *          @Resource:
 *              作用: 直接按照bean的id注入.可以单独使用
 *              属性:
 *                 name: 用于指定bean的id
 *          总结: 以上三个注解只能注入其他bean类型,而基本类型和String类型无法使用上述注解实现.
 *                另外,集合类型的注入只能通过xml来实现
 *          @Value:
 *              作用: 用于注入基本类型和String类型的数据
 *              属性:
 *                 value: 用于指定数据的值,可以使用spring中SpEL(也就是spring的el表达式)
 *                        SpEL写法: ${表达式}
 *
 *      3. 用于改变作用范围的
 *          它们的作用就和XML配置文件中编写一个bean标签使用scope属性实现的功能是一样的
 *          @Scope:
 *              作用: 用于指定bean的作用范围
 *              属性:
 *                  value: 指定范围的取值,常用取值: singleton(默认),property
 *
 *      4. 和生命周期有关的 [了解]
 *          它们的作用就和XML配置文件中编写一个bean标签使用init-method,destroy-method的作用是一样的
 *          @PreDestroy:
 *              作用: 用于指定销毁方法
 *          @PostConstruct:
 *              作用: 用于指定初始化方法
 */
@Service(value="accountService")
@Scope(value = "singleton")
public class AccountServiceImpl implements AccountService {

//    @Autowired
//    @Qualifier(value = "accountDao1")
    @Resource(name = "accountDao1")
    private AccountDao accountDao;

//    public AccountServiceImpl() {
//        System.out.println("AccountServiceImpl 对象创建了...");
//    }

    /**
     * 初始化方法
     */
    @PostConstruct
    public void init() {
        System.out.println("init...");
    }

    /**
     * 销毁方法
     */
    @PreDestroy
    public void destroy() {
        System.out.println("destroy...");
    }

    /**
     * 模拟保存账户
     */
    @Override
    public void saveAccount() {
        accountDao.saveAccount();
    }
}
  • 调用
public class AccountController {

    public static void main(String[] args) {
        // ============= ApplicationContext [立即加载] =============

        // 1. 获取核心容器对象 [推荐 ClassPathXmlApplicationContext方式创建]
//        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        // 2. 根据id获取bean对象 [service,dao都只创建一次,单例模式]
        AccountService accountService = (AccountService) ac.getBean("accountService");
//        System.out.println(accountService);
//        AccountDao accountDao = ac.getBean("accountDao", AccountDao.class);
//        System.out.println(accountDao);
        // 3. 调用方法
        accountService.saveAccount();
        // 4. 销毁
        ac.close();
    }

}
init...
save success 111...
destroy...