spring学习笔记(2):IOC和DI

一、IOC的概念和作用

1基本概念

这里我们先简单总结下前文中创建对象的两种方式

  • 前文中两种不同的方式来创建对象就是一个典型的对比
    在这里插入图片描述- 前一种获取对象的方式是主动的,直接new,将应用和资源直接绑定在一起
    在这里插入图片描述- 后一种方式,把创建对象交给工厂,我们只需要和工厂打交道即可
    在这里插入图片描述- 由工厂来控制资源和提供资源,减少程序间的耦合性

二、使用spring框架来实现前文的解耦案例

1.项目环境搭建

  • 创建maven项目
  • 复制上文的主要代码
  • 设置pom.xml文件,导入spring的jar包坐标
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>

    </dependencies>

  • 导入后,可以发现相关jar包的信息
    在这里插入图片描述
  • 新建资源文件bean.xml
  • 导入约束
    在这里插入图片描述
  • 接下来把对象的创建交给spring来管理,在配置文件中进行配置
  <!--
     id:唯一标志
     class:bean类的全限定类名
     -->
    <bean id="accountService" class="com.xpt.service.impl.AccountServiceImpl"></bean>

    <bean id="accountDao" class="com.xpt.dao.impl.AccountDaoImpl"></bean>
  • 创建对象,分为如下两步
    • 获取core对象
    • 根据配置文件中的id获取对象
public class Client {

    public static void main(String[] args) {
        //1.获取核心容器对象
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.根据id获取对象
        IAccountService as = (IAccountService) ac.getBean("accountService");
        IAccountDao dao = (IAccountDao) ac.getBean("accountDao");
        System.out.println(as);
        System.out.println(dao);
    }
}

在这里插入图片描述- 可以看到上述的过程代码非常的少,从配置文件解析到对象创建全都由spring做了

2.ApplicationContext的3个常用实现类

  • ClassPathXmlApplicationContext:加载类路径下的配置文件,要求配置文件必须在类路径下,不在的话,加载不了。(相较于下一种更推荐使用)
  • FileSystemXmlApplicationContext:可以加载磁盘任意路径下的配置文件,但必须有访问权限
  • AnnotationConfigApplicationContext:它是用于读取注解创建容器的。

3.核心容器的两个接口引发的问题

3.1ApplicationContext

它在创建核心容器时,对象创建的策略是采用立即加载的方式,也就是说,只要一读取完配置文件就能马上创建配置文件种配置好的对象。

  • 验证:在bean类中定义一个空参的构造函数
public class AccountServiceImpl implements IAccountService {

    IAccountDao accountDao = new AccountDaoImpl();
    public AccountServiceImpl(){
        System.out.println("对象已经被创建了");
    }


    public void saveAccount() {
        accountDao.saveAccount();
    }

}
  • Client类中注释掉后面的对象获取
public class Client {

    public static void main(String[] args) {
        //1.获取核心容器对象
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//        //2.根据id获取对象
//        IAccountService as = (IAccountService) ac.getBean("accountService");
//        IAccountDao dao = (IAccountDao) ac.getBean("accountDao");
//        System.out.println(as);
//        System.out.println(dao);
    }
}
  • 运行效果:
    在这里插入图片描述

3.2BeanFactory

它在构建核心容器的时候,创建对象的策略是采用延迟加载的方式,也就是说,什么时候根据id获取对象了,什么时候才真正的创建对象。

  • 演示:修改代码
  Resource resource = new ClassPathResource("bean.xml");
        BeanFactory factory = new XmlBeanFactory(resource);
        IAccountService as = (IAccountService) factory.getBean("accountService");
        System.out.println(as);
        
  • 断点调试
    在这里插入图片描述- 继续执行
    在这里插入图片描述

3.3使用选择

上面演示了两种容器接口创建对象的不同时机,那在实际开发中如何选择呢?单例对象的情况下,对象只被创建一次,然后多次使用,这种时候肯定希望立即加载,所以ApplicationContext更适用于单例对象,那么显然BeanFactory更适用于多例对象使用

三、spring对bean的管理细节

1.创建bean的三种方式

1.1方式一:使用默认构造函数

在spring的配置文件中使用bean标签,配以idclass属性,且没有其他的属性和标签时。采用的就算默认构造函数创建bean对象,如果此时类中没有默认构造函数,则对象无法创建

  • 演示效果
  • 配置文件
<bean id="accountService" class="com.xpt.service.impl.AccountServiceImpl"></bean>
  • 设置bean类无默认构造函数
public class AccountServiceImpl implements IAccountService {


    //增加参数 使当前类没有默认的构造函数
    public AccountServiceImpl(String name){
        System.out.println("对象已经被创建了");
    }

    public void saveAccount() {
        System.out.println("service中的saveAccount方法执行了");
    }

}
  • 测试类
public class Client {

    public static void main(String[] args) {
        //1.获取核心容器对象
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.根据id获取对象
        IAccountService as = (IAccountService) ac.getBean("accountService");
        System.out.println(as);
    }
}
  • 运行结果报错
    在这里插入图片描述

1.2方式二:使用实例工厂的方法创建对象

使用普通工厂中的方法创建对象
或者使用某个类中的方法创建对象,并存入spring容器

  • 演示
  • 配置
  <bean id="instanceFactory" class="com.xpt.factory.InstanceFactory"></bean>
    <bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean>


  • 模拟实例工厂类
public class InstanceFactory {

    public IAccountService getAccountService(){
        return new AccountServiceImpl();
    }

}
  • 其余部分代码不变,之间运行测试类,结果成功创建对象
    在这里插入图片描述

1.3方式三:使用静态工厂的静态方法来创建对象

使用静态工厂的静态方法来创建对象
或者使用某个类的静态方法创建对象,并存入spring容器

  • 演示
  • 配置
 <bean id="accountService" class="com.xpt.factory.StaticFactory"
          factory-method="getAccountService" ></bean>
  • 静态工厂和静态方法
public class StaticFactory {

    public static IAccountService getAccountService(){
        return new AccountServiceImpl();
    }

}
  • 其余代码不变,直接运行测试类,成功创建对象
    在这里插入图片描述

2.bean的作用范围

2.1默认作用范围( singleton:单例的)

默认作用范围是单例的,即创建的bean都是一个对象

  • 演示
    在这里插入图片描述

2.2设置多例的作用范围( prototype)

  • xml配置
    在这里插入图片描述- 运行测试
    在这里插入图片描述

2.3小结

bean标签的scope属性:
作用:用于指定bean的作用范围
取值: 常用的就是单例的和多例的

  • singleton:单例的(默认值)
  • prototype:多例的
  • request:作用于web应用的请求范围
  • session:作用于web应用的会话范围
  • global-session:作用于集群环境的会话范围(全局会话范围),当不是集群环境时,它就是session

2.4global-session简单了解

在这里插入图片描述

3.bean对象的生命周期

3.1单例对象的生命周期

  • 出生:当容器创建时对象出生

  • 活着:只要容器还在,对象一直活着

  • 死亡:容器销毁,对象消亡

  • 总结:单例对象的生命周期和容器相同

  • 演示

  • xml配置

在这里插入图片描述

  • bean类中添加initdestroy方法
  public void saveAccount() {
        System.out.println("service中的saveAccount方法执行了");
    }

    public void init() {
        System.out.println("对象创建");
    }
    public void destroy() {
        System.out.println("对象销毁");
    }
  • 测试类改造:
    在这里插入图片描述- 结果:
    在这里插入图片描述

3.2多例对象的生命周期

  • 出生:当我们使用对象时spring框架为我们创建(延迟加载,spring会智能的判断对象是单例还是多例的,然后智能选择加载方式,这里回想前面核心容器的两个不同的加载方式)
  • 活着:对象只要是在使用过程中就一直活着。
  • 死亡:当对象长时间不用,且没有别的对象引用时,由Java的垃圾回收器回收
  • 验证
  • xml配置
    在这里插入图片描述- 其余部分不变,测试代码结果
    在这里插入图片描述

四、spring的依赖注入

1.基本概念

  • 依赖注入:Dependency Injection
  • IOC的作用: 降低程序间的耦合(依赖关系)
  • 依赖关系的管理:以后都交给spring来维护
  • 在当前类需要用到其他类的对象,由spring为我们提供,我们只需要在配置文件中说明,依赖关系的维护:就称之为依赖注入。

2注入数据类型和方式

2.1三类数据

  • 基本类型和String
  • 其他bean类型(在配置文件中或者注解配置过的bean)
  • 复杂类型/集合类型

2.2三种注入方式

  • 第一种:使用构造函数提供
  • 第二种:使用set方法提供
  • 第三种:使用注解提供

3第一种注入方式:使用构造函数提供

下面演示基于这种方式的配置过程

3.1bean类提供带参的构造函数

public class AccountServiceImpl implements IAccountService {

    //如果是经常变化的数据,并不适用于注入的方式
    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;
    }

    public void saveAccount() {
        System.out.println("service中的saveAccount方法执行了:"+name+","+age+","+birthday);
    }
    
}
  • 注意
    在这里插入图片描述

  • 现在上述的bean类中没有默认的构造函数,所以在配置的时候肯定无法使用默认的构造函数来创建bean对象
    在这里插入图片描述- 所以为了解决上面的问题就需要进行构造函数的依赖注入的配置

3.2配置方法

  • 使用的标签:constructor-arg
  • 标签出现的位置:bean标签的内部
  • 标签中的属性
    • type:用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型
    • index:用于指定要注入的数据给构造函数中指定索引位置的参数赋值。索引的位置是从0开始
    • name:用于指定给构造函数中指定名称的参数赋值(推荐)
  • 以上三个用于指定给构造函数中哪个参数赋值
    • value:用于提供基本类型和String类型的数据
    • ref:用于指定其他的bean类型数据。它指的就是在spring的Ioc核心容器中出现过的bean对象
3.2.1配置实例

在这里插入图片描述

3.3运行效果

  • 代码

public class Client {

    public static void main(String[] args) {
        //1.获取核心容器对象
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.根据id获取对象
        IAccountService as = (IAccountService) ac.getBean("accountService");
        System.out.println(as);
        as.saveAccount();
    }
}

  • 结果
    在这里插入图片描述- 顺利执行

3.4优势和弊端

  • 优势: 在获取bean对象时,注入数据是必须的操作,否则对象无法创建成功。
  • 弊端:改变了bean对象的实例化方式,使我们在创建对象时,如果用不到这些数据,也必须提供。

4第二种注入方式:使用set方法提供(更常用)

4.1bean类提供属性的set方法

  • 只提供set方法即可

在这里插入图片描述

4.2配置方法

  • 涉及的标签:property
  • 出现的位置:bean标签的内部
  • 标签的属性
    • name:用于指定注入变量的名称
    • value:用于提供基本类型和String类型的数据
    • ref:用于指定其他的bean类型数据。它指的就是在spring的Ioc核心容器中出现过的bean对象
4.2.1配置实例

在这里插入图片描述

4.3运行效果

  • 代码同上
    在这里插入图片描述

4.4优势和弊端

  • 优势:创建对象时没有明确的限制,可以直接使用默认构造函数
  • 弊端: 如果有某个成员必须有值,则获取对象是有可能set方法没有执行。

5.复杂数据类型(集合类型)的注入案例

使用set方式注入复杂数据类型

5.1含有复杂数据类型的bean类

public class AccountServiceImpl3 implements IAccountService {

    private String[] myStrs;
    private List<String> myList;
    private Set<String> mySet;
    private Map<String,String> myMap;
    private Properties myProps;

    public void setMyStrs(String[] myStrs) {
        this.myStrs = myStrs;
    }

    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;
    }

    public void  saveAccount(){
        System.out.println(Arrays.toString(myStrs));
        System.out.println(myList);
        System.out.println(mySet);
        System.out.println(myMap);
        System.out.println(myProps);
    }

5.2配置方法

  • 配置实例
   <bean id="accountService03" class="com.xpt.service.impl.AccountServiceImpl3">
        <property name="myStrs">
            <array>
                <value>array</value>
            </array>
        </property>

        <property name="myList">
            <list>
                <value>list</value>
            </list>
        </property>

        <property name="mySet">
            <set>
                <value>set</value>
            </set>
        </property>

        <property name="myMap">
            <map>
                <entry key="mapA" value="valueA"></entry>
                <entry key="mapB" value="valueB"></entry>
            </map>
        </property>

        <property name="myProps">
            <props>
                <prop key="propA">valueA</prop>
            </props>
        </property>
    </bean>
5.2.1标签小结
  • 用于给List结构集合注入的标签: list array set
  • 用于个Map结构集合注入的标签: map props
  • 结构相同,标签可以互换

5.3运行效果

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值