Spring之IOC和DI(三)

文章目录

(一)IOC的概念和作用
(二)前期准备
(三)基于XML的IOC环境搭建和入门
(四)ApplicationContext的三个实现类
(五)BeanFactory和ApplicationContext的区别
(六)Bean的细节:三种创建Bean对象的方式
(七)Bean的细节:作用范围
(八)Bean的细节:生命周期
(九)DI的概念和作用
(十)构造函数注入
(十一)set方法注入
(十二)注入复杂类型数据
(十三)业务层实现类调用持久层的方法

(一)IOC的概念和作用

IOC:Inversion of Control,是控制反转的意思
我们之前想new谁就new谁,控制权在自己手上
现在我们把控制权交给了工厂,由工厂去创建对象,所以说控制反转
Spring之IOC和DI(三)_第1张图片
IOC的作用:削减计算机程序的耦合(只能降低耦合,无法完全消除)

(二)前期准备

下载与目录结构简述:

官网:http://spring.io/
下载地址:http://repo.springsource.org/libs-release-local/org/springframework/spring
解压:(Spring 目录结构:)
* docs :API 和开发规范
* libs :jar 包和源码
* schema :约束

我们其实只需要一个dist文件夹即可,里面包含了jar包、文档和约束
Spring之IOC和DI(三)_第2张图片
Spring之IOC和DI(三)_第3张图片
特别说明:

我们上课使用的版本是 spring5.0.2
spring5 版本是用 jdk8 编写的,所以要求我们的 jdk 版本是 8 及以上
同时 tomcat 的版本要求 8.5 及以上

如何使用:

  1. jar包
    Spring之IOC和DI(三)_第4张图片
  2. 源码
    Spring之IOC和DI(三)_第5张图片
    Spring之IOC和DI(三)_第6张图片

(三)基于XML的IOC环境搭建和入门

我们重新新建一个普通的maven项目
Spring之IOC和DI(三)_第7张图片
然后把上一个项目的代码文件复制过来,并且把工厂类相关的代码去掉
Spring之IOC和DI(三)_第8张图片
可以看到,这个时候项目又变回原来的样子,可以正常运行,但是耦合又回来了
我们接下来开始真正的使用Spring框架
我们先导入maven坐标,如下:
Spring之IOC和DI(三)_第9张图片
jar包的对应关系如下:
(其中的apache的日志jar包已经被spring集成到自己的jar包里面去了)
Spring之IOC和DI(三)_第10张图片
依赖关系如下:
Spring之IOC和DI(三)_第11张图片
我们接下来在resources目录下创建一个xml文件
在这里插入图片描述
我们接下来去Spring的官网拷贝xml的约束
Spring之IOC和DI(三)_第12张图片
使用CTRL+F进行全局搜索
Spring之IOC和DI(三)_第13张图片
约束的部分拷贝到xml里面
Spring之IOC和DI(三)_第14张图片
然后向map容器添加数据
Spring之IOC和DI(三)_第15张图片
最后尝试使用Spring的ioc容器获取对象
Spring之IOC和DI(三)_第16张图片
说明

  1. 获取核心容器对象的方法有很多,以下是常用的
    Spring之IOC和DI(三)_第17张图片
  2. 根据id获取对象的方法有两种
    在这里插入图片描述

(四)ApplicationContext的三个实现类

我们接下来看一下ApplicationContext的实现类
在这里插入图片描述
Spring之IOC和DI(三)_第18张图片
我们常用的实现类有三个,如下:
(其中第一个是用于注解开发的,我们后面再讲;我们先看后面两个)
Spring之IOC和DI(三)_第19张图片

  • ClassPathXmlApplicationContext
    它可以加载类路径下的配置文件,要求配置文件必须在类路径下,否则加载不了(这种比较常用)
    之前演示的就是ClassPathXmlApplicationContext,这里不再重复演示了
  • FileSystemXmlApplicationContext
    它可以加载磁盘任意路径下的配置文件(必须有访问权限
//1.获取核心容器对象
ApplicationContext ac = new FileSystemXmlApplicationContext("G:\\JavaEE\\Spring_xml\\src\\main\\resources\\bean.xml");
  • AnnotationConfigApplicationContext
    它是用于读取注解创建容器的,后面再介绍

(五)BeanFactory和ApplicationContext的区别

  • ApplicationContext(适用于单例模式;这种比较常用
    它在构建核心容器时,创建对象采取的策略是采用立即加载的方式
    也就是说,只要一读取完配置文件马上就创建配置文件中配置的对象
    我们来证明一下,先给两个实现类写上空参的构造方法,如下:
    Spring之IOC和DI(三)_第20张图片
    然后我们设置断点测试一下
    F8之前
    Spring之IOC和DI(三)_第21张图片
    F8之后
    Spring之IOC和DI(三)_第22张图片
  • BeanFactory(适用于多例模式
    它在构建核心容器时,创建对象采取的策略是采用延迟加载的方式
    也就是说,什么时候根据id获取对象了,什么时候才真正的创建对象
    演示代码如下:
//----------BeanFactory--------------
Resource resource = new ClassPathResource("bean.xml");
BeanFactory factory = new XmlBeanFactory(resource);
IAccountService as = (IAccountService) factory.getBean("accountService");
System.out.println(as);

断点测试如下:
Spring之IOC和DI(三)_第23张图片
Spring之IOC和DI(三)_第24张图片
Spring之IOC和DI(三)_第25张图片
可以看到只有根据id创建对象时,才是真正的创建对象
Spring之IOC和DI(三)_第26张图片
那我们怎么知道springioc容器为我们创建对象是单例的还是多例的呢?下面会详细介绍

(六)Bean的细节:三种创建Bean对象的方式

其实我们现在还是无法调用业务层实现类的方法
Spring之IOC和DI(三)_第27张图片
现在看起来好像可以调用,其实是因为在业务层实现类中依然存在耦合
我们是直接new了一个持久层对象,我们应该把它去掉的,如下:
Spring之IOC和DI(三)_第28张图片
我们后面再详细介绍怎么调用业务层实现类的方法,现在我们先不要调用
(本篇博客最末尾会介绍,点击跳转 ----- 业务层实现类调用持久层的方法)
我们继续创建一个新的普通的maven项目
Spring之IOC和DI(三)_第29张图片
为了方便展示创建对象,删掉持久层,只留下业务层
Spring之IOC和DI(三)_第30张图片
我们接下来分别看看三种创建Bean对象的方式:

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

注意:如果我们不重写构造函数,其实是会有默认构造函数的;如果我们重写了带参构造函数,并且不重写一个空参构造函数,这种情况就属于没有默认构造函数,如下:
Spring之IOC和DI(三)_第31张图片
这种情况是会报错的
Spring之IOC和DI(三)_第32张图片

  • 第二种:使用普通工厂中的方法创建对象
    (又或者是使用某个类中的方法创建对象,并存入spring容器)
    我们设想一个场景:我们要使用别人写好的代码,别人写好的代码通常是封装在一个jar包中的,我们要怎么要怎么创建jar包字节码文件的对象呢?
    (我们无法通过修改源码的方式来提供默认构造方法)

我们接下来模拟一个工厂类(它可能是存在于jar包中的),如下:
(通常jar包都会暴露一个工厂类,给调用者创建对象)
Spring之IOC和DI(三)_第33张图片
Spring之IOC和DI(三)_第34张图片
我们来运行一下
Spring之IOC和DI(三)_第35张图片

  • 第三种:使用工厂中的静态方法创建对象
    (又或者是使用某个类中的静态方法创建对象,并存入spring容器)
    Spring之IOC和DI(三)_第36张图片
    Spring之IOC和DI(三)_第37张图片
    Spring之IOC和DI(三)_第38张图片

(七)Bean的细节:作用范围

bean对象默认情况下是单例的,我们验证一下:
Spring之IOC和DI(三)_第39张图片
那我们要怎么修改bean对象的作用范围呢?

bean标签的scope属性:
    作用:用于指定bean的作用范围
    取值: 常用的就是单例的和多例的
        singleton:单例的(默认值)
        prototype:多例的
        request:作用于web应用的请求范围
        session:作用于web应用的会话范围
        global-session:作用于集群环境的会话范围(全局会话范围),当不是集群环境时,它就是session
  • singleton:单例模式(默认值)
    什么都不写就等价于singleton,不再演示,效果就是单例模式
    Spring之IOC和DI(三)_第40张图片
  • prototype:多例模式
    Spring之IOC和DI(三)_第41张图片
  • request:作用于web应用的请求范围
  • session:作用于web应用的会话范围
  • global-session:作用于集群环境会话范围(全局会话范围)
    (当不是集群环境时,它就是session)
    Spring之IOC和DI(三)_第42张图片
    说明:
    当我们访问登录页面的时候,向192.168.0.1服务器发起请求,这个时候验证码session的形式存在于192.168.0.1服务器,并且以缓存的形式存在于客户端浏览器
    当我们输入验证码访问登录接口的时候,假设这个时候192.168.0.1服务器满负载了,但是198.168.0.6服务器是空闲的,我们通过负载均衡自然就会访问198.168.0.6服务器
    但是该服务器并没有session,所以这个时候就需要用到全局session,让所有服务器共享session

(八)Bean的细节:生命周期

  单例对象
       出生:当容器创建时对象出生
       活着:只要容器还在,对象一直活着
       死亡:容器销毁,对象消亡
       总结:单例对象的生命周期和容器相同

我们来测试一下
我们先在业务层的实现类中添加初始化和销毁的方法
Spring之IOC和DI(三)_第43张图片
然后编写bean.xml

    <bean id="accountService" class="com.zzq.service.impl.AccountServiceImpl"
          scope="singleton" init-method="init" destroy-method="destroy">bean>

运行效果如下:
Spring之IOC和DI(三)_第44张图片
为什么没有调用销毁方法呢?因为main函数执行完的话,所有的数据都被销毁
换而言之,还没来得及调用销毁方法就已经被销毁了
如果我们想手动关闭,却发现没有这个方法
Spring之IOC和DI(三)_第45张图片
原因是:我们使用了多态,接口是不具备close()方法的
我们不要把它成接口,我们把它看成一个类(也就是它本身)就可以了
Spring之IOC和DI(三)_第46张图片

多例对象
    出生:当我们使用对象时spring框架为我们创建
    活着:对象只要是在使用过程中就一直活着
    死亡:当对象长时间不用,且没有别的对象引用时,由Java的垃圾回收器回收

我们修改为多例模式

    <bean id="accountService" class="com.zzq.service.impl.AccountServiceImpl"
          scope="prototype" init-method="init" destroy-method="destroy">bean>

我们其他的保持不变,直接运行试试
Spring之IOC和DI(三)_第47张图片
我们发现销毁方法没有被执行,这是为什么呢?
因为spring交给了Java的垃圾回收器进行回收,回收与否不能干涉

(九)DI的概念和作用

再创建一个普通的maven项目,并且把不需要的代码删掉
Spring之IOC和DI(三)_第48张图片

依赖注入:
    Dependency Injection
IOC的作用:
    降低程序间的耦合(依赖关系)
DI的作用:依赖关系的管理,依赖都交给spring来维护
	在当前类需要用到其他类的对象,由spring为我们提供,我们只需要在配置文件中说明
依赖关系的维护:
    就称之为依赖注入
依赖注入:
    能注入的数据:有三类
        基本类型和String
        其他bean类型(在配置文件中或者注解配置过的bean)
        复杂类型/集合类型
    注入的方式:有三种
        第一种:使用构造函数提供
        第二种:使用set方法提供
        第三种:使用注解提供(后面再介绍)

(十)构造函数注入

(在讲注入之前明确一点:经常变化的数据,不适合注入
这里举例的变量为:name、age和birthday是为了迎合Account场景,其实我们知道这三个变量几乎每个用户都不一样,那每一个用户请求该接口的时候这三个变量都要发生改变,是不合适注入的

首先我们要在类中定义一些可注入的变量,如下:
Spring之IOC和DI(三)_第49张图片
接下来写一个构造方法
Spring之IOC和DI(三)_第50张图片
我们回来bean.xml已经发现报错了,因为已经没有默认构造函数
Spring之IOC和DI(三)_第51张图片

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

尝试用构造函数注入试试

    
    <bean id="accountService" class="com.zzq.service.impl.AccountServiceImpl">
        <constructor-arg name="name" value="test">constructor-arg>
        <constructor-arg name="age" value="18">constructor-arg>
        <constructor-arg name="birthday" value="1970-01-01">constructor-arg>
    bean>

由于xml只能输入字符串,所以spring会自动把String类型的"18"转成Integer类型的18
但是对于Date类型,spring就无能为力了,直接运行会报错,如下:
Spring之IOC和DI(三)_第52张图片
对于像Date这样的其他bean类型,需要在bean.xml里面声明,如下:
(同时需要ref标签来做引用)
Spring之IOC和DI(三)_第53张图片
此时运行的结果如下:
Spring之IOC和DI(三)_第54张图片
总结:

  优势:
      在获取bean对象时,注入数据是必须的操作,否则对象无法创建成功(因为没有默认空参构造)
  弊端:
      改变了bean对象的实例化方式,使我们在创建对象时,如果用不到这些数据,也必须提供

由于其弊端太明显,我们一般不用这种方式,更多的是用set方法注入

(十一)set方法注入

  涉及的标签:property
  出现的位置:bean标签的内部
  标签的属性:
       name:用于指定注入时所调用的set方法名称(如:setName --> name)
       value:用于提供基本类型和String类型的数据
       ref:用于指定其他的bean类型数据。它指的就是在spring的ioc核心容器中出现过的bean对象

复制刚才的实现类,并且加以修改,去掉构造方法,增加set方法,如下:
Spring之IOC和DI(三)_第55张图片
接下来编写bean.xml,如下:

    
    <bean id="now" class="java.util.Date">bean>

    <bean id="accountService" class="com.zzq.service.impl.AccountServiceImpl2">
        <property name="name" value="test">property>
        <property name="age" value="21">property>
        <property name="birthday" ref="now">property>
    bean>

运行结果:
Spring之IOC和DI(三)_第56张图片
总结:

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

(十二)注入复杂类型数据

我们来看看复杂类型(集合类型)的注入
AccountServiceImpl3.java

/**
 * 账户的业务层实现类
 */
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("myStrs:" + Arrays.toString(myStrs));
        System.out.println("myList:" + myList);
        System.out.println("mySet:" + mySet);
        System.out.println("myMap:" + myMap);
        System.out.println("myProps:" + myProps);
    }
}

在尝试编写bean.xml的时候发现了很多子标签,如下:
Spring之IOC和DI(三)_第57张图片
我们先写一个数组

    <bean id="accountService" class="com.zzq.service.impl.AccountServiceImpl3">
        <property name="myStrs">
            <array>
                <value>AAAvalue>
                <value>BBBvalue>
                <value>CCCvalue>
            array>
        property>
    bean>

运行试试:
Spring之IOC和DI(三)_第58张图片
同理把其他的复杂类型数据也赋值,如下:

    <bean id="accountService" class="com.zzq.service.impl.AccountServiceImpl3">
        <property name="myStrs">
            <array>
                <value>AAAvalue>
                <value>BBBvalue>
                <value>CCCvalue>
            array>
        property>

        <property name="myList">
            <list>
                <value>AAAvalue>
                <value>BBBvalue>
                <value>CCCvalue>
            list>
        property>

        <property name="mySet">
            <set>
                <value>AAAvalue>
                <value>BBBvalue>
                <value>CCCvalue>
            set>
        property>
        
        <property name="myMap">
            <map>
                <entry key="testA" value="AAA">entry>
                <entry key="testB">
                    <value>BBBvalue>
                entry>
            map>
        property>

        <property name="myProps">
            <props>
                <prop key="testC">cccprop>
                <prop key="testD">dddprop>
            props>
        property>
    bean>

运行结果:
Spring之IOC和DI(三)_第59张图片
注意:

   用于给List结构集合注入的标签:
       list array set
   用于个Map结构集合注入的标签:
       map  props
   结构相同,标签可以互换

如下:

    <bean id="accountService" class="com.zzq.service.impl.AccountServiceImpl3">
        <property name="myStrs">
            <set>
                <value>AAAvalue>
                <value>BBBvalue>
                <value>CCCvalue>
            set>
        property>

        <property name="myList">
            <array>
                <value>AAAvalue>
                <value>BBBvalue>
                <value>CCCvalue>
            array>
        property>

        <property name="mySet">
            <list>
                <value>AAAvalue>
                <value>BBBvalue>
                <value>CCCvalue>
            list>
        property>

        <property name="myMap">
            <props>
                <prop key="testC">cccprop>
                <prop key="testD">dddprop>
            props>
        property>

        <property name="myProps">
            <map>
                <entry key="testA" value="aaa">entry>
                <entry key="testB">
                    <value>BBBvalue>
                entry>
            map>
        property>
    bean>

运行效果:
Spring之IOC和DI(三)_第60张图片
所以我们实际开发中,只需要使用list结构map结构即可

(十三)业务层实现类调用持久层的方法

我们把持久层补全,如下:
Spring之IOC和DI(三)_第61张图片
然后用set方法注入数据

    <bean id="dao" class="com.zzq.dao.impl.AccountDaoImpl">bean>

    <bean id="accountService" class="com.zzq.service.impl.AccountServiceImpl4">
        <property name="accountDao" ref="dao">property>
    bean>

运行效果:
Spring之IOC和DI(三)_第62张图片

你可能感兴趣的:(Spring)