Spring Framework---IOC/DI

2023-10-29

目录

1.Spring框架的主要内容

1.1Spring的发展版本

1.2Spring系统架构

(1)核心层

(2)AOP层

(3)数据层

(4)Web层

(5)Test层

1.3Spring核心概念

1.3.1IOC(Inversion of control)控制反转

1.3.2DI(Dependency Injection)依赖注入

1.3.3核心概念小结

1.4入门案例代码实现

步骤1:创建Maven项目

步骤2:添加Spring的依赖jar包

步骤3:添加案例中需要的类

步骤4:添加spring配置文件(先导入Spring的jar包)

步骤5:在配置文件中完成bean的配置

步骤6:获取IOC容器

步骤7:从容器中获取对象进行方法调用

步骤8:运行程序

总结

1.5DI入门案例

步骤1: 去除代码中的new

步骤2:为属性提供setter方法

步骤3:修改配置完成注入

步骤4:运行程序

1.6bean基础配置

1.6.1bean的name属性

*1.6.2bean作用范围scope配置

 1.6.3scope思考

1.6.4小结

1.7bean实例化

1.7.1构造方法实例化bean

1.7.2静态工程实例化

1.7.3实例工厂与FactoryBean

 1.7.4FactoryBean的使用

1.8bean的生命周期

1.8.2注册钩子关闭容器

注意(关于afterPropertiesSet与setBookDao的执行顺序):

1.8.3bean生命周期小结

1.9setter注入和构造器注入

1.9.1setter注入引用类型

1.9.2setter注入简单数据类型

1.9.3构造器注入引用数据类型

1.9.4构造器注入多个简单数据类型(解耦的两种方式)

1.9.5参数注入方式的选择

1.10自动配置

1.10.1依赖自动装配

1.10.2自动装配的方式(按类型byType和按名称byName)

1.10.3集合注入


1.Spring框架的主要内容

简化开发 : Spring 框架中提供了两个大的核心技术,分别是 : IOCAOP
事务处理
1.Spring 的简化操作都是基于这两块内容 , 所以这也是 Spring 学习中最为重要的两个知识点。
2. 事务处理属于 Spring AOP 的具体应用,可以简化项目中的事务管理,也是 Spring 技术中的一
大亮点。

框架整合 : Spring 在框架整合这块已经做到了极致,它可以整合市面上几乎所有主流框架,比
:
MyBatis
MyBatis-plus
Struts
Struts2
Hibernate
综上所述,对于 Spring 的学习,主要学习四块内容 :
(1)IOC,(2) 整合 Mybatis(IOC 的具体应用 ) (3)AOP,(4) 声明式事务 (AOP 的具体应用 )

1.1Spring的发展版本

Spring1.0 是纯配置文件开发
Spring2.0 为了简化开发引入了注解开发,此时是配置文件加注解的开发方式
Spring3.0 已经可以进行纯注解开发,使开发效率大幅提升,我们的课程会以注解开发为主
Spring4.0 根据 JDK 的版本升级对个别 API 进行了调整
Spring5.0 已经全面支持 JDK8 ,现在 Spring 最新的是 5 系列所以建议大家把 JDK 安装成 1.8

1.2Spring系统架构

Spring Framework 5 版本目前没有最新的架构图,而最新的是 4 版本,所以接下来主要研究的
4 的架构图:

(1)核心层

Core Container: 核心容器,这个模块是 Spring 最核心的模块,其他的都需要依赖该模块

(2)AOP

AOP: 面向切面编程,它依赖核心层容器,目的是 在不改变原有代码的前提下对其进行功能增强
Aspects:AOP 是思想 ,Aspects 是对 AOP 思想的具体实现

(3)数据层

Data Access: 数据访问, Spring 全家桶中有对数据访问的具体实现技术 Data Integration: 数据集
成, Spring 支持整合其他的数据层解决方案,比如 Mybatis
Transactions: 事务, Spring 中事务管理是 Spring AOP 的一个具体实现,也是后期学习的 重点内容

(4)Web

这一层的内容将在 SpringMVC 框架具体学习

(5)Test

Spring 主要整合了 Junit 来完成单元测试和集成测试

1.3Spring核心概念

Spring 核心概念这部分内容中主要包含 IOC/DIIOC容器 Bean , 那么问题就来了,这些都是什
么呢 ?
为了解决 现在代码在编写的过程中存在的问题是: 耦合度偏高
使用对象时,在程序中不要主动使用 new 产生对象,转换为由 外部 提供对象,这种实现思想
就是 Spring 的一个核心概念 。

1.3.1IOC(Inversion of control)控制反转

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

(2)SpringIOC之间的关系是什么呢?
Spring 技术对 IOC 思想进行了实现
Spring 提供了一个容器,称为 IOC 容器 ,用来充当 IOC 思想中的 " 外部 "
IOC 思想中的 别人 [ 外部 ] 指的就是 Spring IOC 容器
(3)IOC容器的作用以及内部存放的是什么?
IOC 容器负责对象的创建、初始化等一系列工作,其中包含了数据层和业务层的类对象
被创建或被管理的对象在 IOC 容器中统称为 Bean
IOC 容器中放的就是一个个的 Bean 对象
(4)IOC容器中创建好servicedao对象后,程序能正确执行么?
不行,因为 service 运行需要依赖 dao 对象
IOC 容器中虽然有 service dao 对象
但是 service 对象和 dao 对象没有任何关系
需要把 dao 对象交给 service, 也就是说要绑定 service dao 对象之间的关系
像这种在容器中建立对象与对象之间的绑定关系就要用到 DI(依赖注入) :

1.3.2DI(Dependency Injection)依赖注入

 

(1)什么是依赖注入呢?
在容器中建立 bean bean 之间的依赖关系的整个过程,称为依赖注入
业务层要用数据层的类对象,以前是自己 new 的, 现在自己不 new 了,靠 别人 [ 外部其实指的就是
IOC 容器 ] 来给注入进来, 这种思想就是依赖注入
(2)IOC容器中哪些bean之间要建立依赖关系呢?
这个需要程序员根据业务需求提前建立好关系,如 业务层需要依赖数据层, service 就要和 dao 立依赖关系
Spring IOC 和DI两个概念的最终目标就是 : 充分解耦
使用 IOC 容器管理 bean IOC)
IOC 容器内将有依赖关系的 bean 进行关系绑定( DI
最终结果为 : 使用对象时不仅可以直接从 IOC 容器中获取,并且获取到的 bean 已经绑定了所有的依
赖关系

1.3.3核心概念小结

(1)什么IOC/DI思想?
IOC: 控制反转,控制反转的是对象的创建权
DI: 依赖注入,绑定对象与对象之间的依赖关系
(2)什么是IOC容器?
Spring 创建了一个容器用来存放所创建的对象,这个容器就叫 IOC 容器
(3)什么是Bean?
容器中所存放的一个个对象就叫 Bean Bean 对象

1.4入门案例代码实现

需求分析 : BookServiceImpl BookDaoImpl 交给 Spring 管理,并从容器中获取对应的 bean
对象进行方法调用。
1. 创建 Maven java 项目
2.pom.xml 添加 Spring 的依赖 jar
3. 创建 BookService,BookServiceImpl BookDao BookDaoImpl 四个类
4.resources 下添加 spring 配置文件,并完成 bean 的配置
5. 使用 Spring 提供的接口完成 IOC 容器的创建
6. 从容器中获取对象进行方法调用

步骤1:创建Maven项目

步骤2:添加Spring的依赖jar

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.itheima</groupId>
    <artifactId>spring_01_quickstart</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.10.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

步骤3:添加案例中需要的类

创建BookService,BookServiceImplBookDaoBookDaoImpl四个类

public interface BookDao {
    public void save(); 
}

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

public interface BookService {
    public void save();
}

public class BookServiceImpl implements BookService { 

private BookDao bookDao = new BookDaoImpl(); 
    public void save() {
    System.out.println("book service save ...");
    bookDao.save(); 
    } 
}

步骤4:添加spring配置文件(先导入Spring的jar包)

resources 下添加 spring 配置文件 applicationContext.xml ,并完成 bean 的配置

步骤5:在配置文件中完成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"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
    <bean id="bookService" name="service service2 bookEbi" class="com.itheima.service.impl.BookServiceImpl"/>
</beans>
注意事项: bean 定义时 id 属性在同一个上下文中 ( 配置文件 ) 不能重复

步骤6:获取IOC容器

package com.itheima.service;

import com.itheima.dao.BookDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App2 {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
    }
}

步骤7:从容器中获取对象进行方法调用

package com.itheima.service;

import com.itheima.dao.BookDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App2 {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

//        BookDao bookDao = (BookDao) ctx.getBean("bookDao");
//
//        bookDao.save();
        BookService bookService = (BookService) ctx.getBean("service");

        bookService.save();
    }
}

步骤8:运行程序

测试结果为:
但是在 BookServiceImpl 的类中依然存在 BookDaoImpl 对象的new 操作,它们之间的耦合度还是比
较高(在BookServiceImpl实体类中还是存在new BookDaoImpl,故耦合度还是较高),这块该如何
解决,就需要用到下面的 DI: 依赖注入

总结

首先获取IOC容器,使用ClassPathXmlApplicationContext()方法对配置文件applicationContext.xml
进行加载,new出一个IOC容器对象调用getBean()方法通过id(bookService)对bean标签进行加
载,即可得到bookService对象,在配置文件中,id="bookService"的标签通过全类名定位该实体
类,最后调用其方法。

1.5DI入门案例

思路分析:
(1) 要想实现依赖注入,必须要基于 IOC 管理 Bean
DI 的入门案例要依赖于前面 IOC 的入门案例
(2)Service 中使用 new 形式创建的 Dao 对象是否保留 ?
需要删除掉,最终要使用 IOC 容器中的 bean 对象
(3)Service 中需要的 Dao 对象如何进入到 Service ?
Service 中提供方法,让 Spring IOC 容器可以通过该方法传入 bean 对象
(4)Service Dao 间的关系如何描述 ?
使用配置文件
需求 : 基于 IOC 入门案例,在 BookServiceImpl 类中删除 new 对象的方式,使用 Spring DI 完成
Dao 层的注入
1. 删除业务层中使用 new 的方式创建的 dao 对象
2. 在业务层提供 BookDao setter 方法
3. 在配置文件中添加依赖注入的配置
4. 运行程序调用方法

步骤1: 去除代码中的new

BookServiceImpl类中,删除业务层中使用new的方式创建的dao对象

package com.itheima.service.impl;

import com.itheima.dao.BookDao;
import com.itheima.service.BookService;

public class BookServiceImpl implements BookService {
    private BookDao bookDao;

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

步骤2:为属性提供setter方法

BookServiceImpl 类中 , BookDao 提供 setter 方法
package com.itheima.service.impl;

import com.itheima.dao.BookDao;
import com.itheima.service.BookService;

public class BookServiceImpl implements BookService {
    private BookDao bookDao;

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

步骤3:修改配置完成注入

在配置文件中添加依赖注入的配置
<?xml version="1.0" encoding="UTF-8"?>
<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 http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <!--bean标签标示配置bean id属性标示给bean起名字 class属性表示给bean定义类型 -->
    <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
    <bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
    <!--配置server与dao的关系--> 
    <!--property标签表示配置当前bean的属性 name属性表示配置哪一个具体的属性 ref属性表示参照哪一个bean -->
        <property name="bookDao" ref="bookDao"/>
    </bean>
</beans>
注意 : 配置中的两个 bookDao 的含义是不一样的
name="bookDao" bookDao 的作用是让 Spring IOC 容器在获取到名称后,将首字母大写,前
面加 set 找对应的 setBookDao() 方法进行对象注入
ref="bookDao" bookDao 的作用是让 Spring 能在 IOC 容器中找到 id bookDao Bean 对象给
bookService 进行注入
综上所述,对应关系如下 :

 

步骤4:运行程序

运行,测试结果为:

 

1.6bean基础配置

class属性能不能写接口如BookDao的类全名呢?

答案肯定是不行,因为接口是没办法创建对象的。

1.6.1bean的name属性

步骤 1 :配置别名
打开 spring 的配置文件 applicationContext.xml
<?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">

    <!--name:为bean指定别名,别名可以有多个,使用逗号,分号,空格进行分隔-->
    <bean id="bookService" name="service service4 bookEbi" class="com.itheima.service.impl.BookServiceImpl">
        <property name="bookDao" ref="bookDao"/>
    </bean>

    <!--scope:为bean设置作用范围,可选值为单例singloton,非单例prototype-->
    <bean id="bookDao" name="dao" class="com.itheima.dao.impl.BookDaoImpl" scope="prototype"/>
</beans>
说明 :Ebi 全称 Enterprise Business Interface ,翻译为企业业务接口
步骤 2: 根据名称容器中获取 bean 对象
package com.itheima;

import com.itheima.dao.BookDao;
import com.itheima.service.BookService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AppForName {
    public static void main(String[] args) {

        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

        BookService bookService = (BookService) ctx.getBean("service4");

        bookService.save();
    }
}
步骤 3: 运行程序
测试结果为:

 

注意事项 :
bean 依赖注入的 ref 属性指定 bean ,必须在容器中存在
如果不存在 , 则会报错,如下 :
这个错误大家需要特别关注下 :
 
获取 bean 无论是通过 id 还是 name 获取,如果无法获取到,将抛出异常
NoSuchBeanDefinitionException

*1.6.2bean作用范围scope配置

singleton(单例):

只有一个共享的实例存在,所有对这个bean的请求都会返回这个唯一的实例。

prototype(多例):

对这个bean的每次请求都会创建一个新的bean实例,类似于new。

 1.6.3scope思考

为什么bean默认为单例?
1.bean 为单例的意思是在 Spring IOC 容器中只会有该类的一个对象
2.bean 对象只有一个就避免了对象的频繁创建与销毁,达到了 bean 对象的复用,性能高
bean在容器中是单例的,会不会产生线程安全问题?
1.如果对象是有状态对象,即该对象有成员变量可以用来存储数据的, 所有请求线程共用一个
bean 对象,所以会存在线程安全问题。
2.如果对象是无状态对象,即该对象没有成员变量没有进行数据存储的, 方法中的局部变量在方法
调用完成后会被销毁,所以不会存在线程安全问题。
哪些bean对象适合交给容器进行管理?
1.表现层对象
2.业务层对象
3.数据层对象
4.工具对象
哪些bean对象不适合交给容器进行管理?
封装实例的域对象,因为会引发线程安全问题,所以不适合。

1.6.4小结

1.7bean实例化

bean 是如何创建的
实例化 bean 的三种方式, 构造方法,静态工厂实例工厂

1.7.1构造方法实例化bean

Spring内部走的依然是构造函数 , 能访问到类中的私有构造方法 , 显而易见 Spring 底层用的是反射,
Spring 底层使用的是类的无参构造方法。

1.7.2静态工程实例化

配置文件:
<bean id="orderDao" class="com.itheima.factory.OrderDaoFactory" 
factory- method="getOrderDao"/>
class: 工厂类的类全名
factory-mehod: 具体工厂类中创建对象的方法名

通过IOC容器获取配置文件id,通过全类名获取工厂类,通过factory-method属性获取工厂类中的方法将对象返回到IOC容器中。

1.7.3实例工厂与FactoryBean

配置文件:
<bean id="userFactory" class="com.itheima.factory.UserDaoFactory"/>
<bean id="userDao" factory-method="getUserDao" factory-bean="userFactory"/>
实例化工厂运行的顺序是 :
1.创建实例化工厂对象 , 对应的是第一行配置
2.调用对象中的方法来创建 bean ,对应的是第二行配置
3.factory-bean: 工厂的实例对象
factory-method: 工厂对象中的具体创建对象的方法名 , 对应关系如下 :

首先我们想要从IOC容器中调用到UserDao对象,并且是通过实例工厂的方法,在配置文件中,第一行配置IOC容器已经得到UserDaoFactory对象,我们通过factory-bean="userFactory"得到该对象,再通过属性factory-method="getUserDao"得到该对象的方法,从而返回UserDaoImpl实体类对象。

 1.7.4FactoryBean的使用

具体的使用步骤为 :
(1)创建一个UserDaoFactoryBean的类,实现FactoryBean接口,重写接口的方法
package com.itheima.factory;

import com.itheima.dao.UserDao;
import com.itheima.dao.impl.UserDaoImpl;
import org.springframework.beans.factory.FactoryBean;
//FactoryBean创建对象
public class UserDaoFactoryBean implements FactoryBean<UserDao> {
    //代替原始实例工厂中创建对象的方法
    public UserDao getObject() throws Exception {
        return new UserDaoImpl();
    }

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

}
(2)Spring的配置文件中进行配置
 <bean id="userDao" class="com.itheima.factory.UserDaoFactoryBean"/>
(3)AppForInstanceUser运行类不用做任何修改,直接运行

T getObject() throws Exception;
Class<?> getObjectType();
default boolean isSingleton() {
    return true;
}
查看源码会发现, FactoryBean 接口其实会有三个方法,分别是 :
方法一 :getObject() ,被重写后,在方法中进行对象的创建并返回
方法二 :getObjectType(), 被重写后,主要返回的是被创建类的 Class 对象
方法三 : 没有被重写,因为它已经给了默认值,从方法名中可以看出其作用是设置对象是否为单 例,默 true

1.7.5bean小结

(1)bean是如何创建的呢?
构造方法
(2)SpringIOC实例化对象的三种方式分别是:
构造方法 ( 常用 )
静态工厂 ( 了解 )
实例工厂 ( 了解 )
FactoryBean( 实用 )
这些方式中,重点掌握 构造方法 FactoryBean 即可。
需要注意的一点是,构造方法在类中默认会提供,但是如果重写了构造方法,默认的就会消失,在
使 用的过程中需要注意,如果需要重写构造方法,最好把默认的构造方法也重写下。

1.8bean的生命周期

关于 bean 的相关知识还有最后一个是 bean 的生命周期 , 对于生命周期,我们主要围绕着 bean 生命周
期控 来讲解 :
首先理解下什么是生命周期?
从创建到消亡的完整过程 , 例如人从出生到死亡的整个过程就是一个生命周期。
bean生命周期是什么?
bean 对象从创建到销毁的整体过程。
构造方法bean生命周期控制是什么?
bean 创建后到销毁前做一些事情。

 步骤1:添加初始化和销毁方法

针对这两个阶段,我们在 BooDaoImpl 类中分别添加两个方法, 方法名任意
package com.itheima.dao.impl;

import com.itheima.dao.BookDao;

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: 配置生命周期
    <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" init-method="init" destroy-method="destory"/>
步骤 3: 运行程序
运行 AppForLifeCycle 打印结果为:

 

从结果中可以看出, init 方法执行了,但是 destroy 方法却未执行,这是为什么呢 ?
Spring IOC 容器是运行在 JVM
运行 main 方法后 ,JVM 启动 ,Spring 加载配置文件生成 IOC 容器 , 从容器获取 bean 对象,然后调方
法执行 main 方法执行完后, JVM 退出,这个时候 IOC 容器中的 bean 还没有来得及销毁就已经结束了
所以没有调用对应的 destroy 方法

 1.8.1close关闭容器

ApplicationContext 中没有 close 方法
需要将 ApplicationContext 更换成 ClassPathXmlApplicationContext
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

调用ctxclose()方法

ctx.close();
运行程序,就能执行 destroy 方法的内容

1.8.2注册钩子关闭容器

在容器未关闭之前,提前设置好回调函数,让 JVM 在退出之前回调此函数来关闭容器
调用 ctx registerShutdownHook() 方法
ctx.registerShutdownHook();
注意 : registerShutdownHook ApplicationContext 中也没有,在其子类
运行后,查询打印结果

 

两种方式介绍完后,closeregisterShutdownHook选哪个?
相同点 : 这两种都能用来关闭容器
不同点 :close() 是在调用的时候关闭, registerShutdownHook() 是在 JVM 退出前调用关闭。
分析上面的实现过程,会发现添加初始化和销毁方法,即需要编码也需要配置,实现起来步骤比较
多同时 也比较乱。
简化操作bean的生命周期操作:
Spring 提供了两个接口来完成生命周期的控制,好处是可以不用再进行配置 init - method
destroy - method
接下来在 BookServiceImpl 完成这两个接口的使用 :
修改 BookServiceImpl 类,添加两个接口 InitializingBean DisposableBean 并实现接口中的
两个方法 afterPropertiesSet destroy
package com.itheima.service.impl;

import com.itheima.dao.BookDao;
import com.itheima.service.BookService;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;

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

注意(关于afterPropertiesSet与setBookDao的执行顺序):

对于 InitializingBean 接口中的 afterPropertiesSet 方法,翻译过来为 属性设置之后
对于 BookServiceImpl 来说, bookDao 是它的一个属性
setBookDao 方法是 Spring IOC 容器为其注入属性的方法
思考:afterPropertiesSetsetBookDao谁先执行?
setBookDao 方法先执行,初始化方法会在类中属性设置之后执行,因为bookDao是一个属性,而
afterPropertiesSet方法是在属性设置之后执行

1.8.3bean生命周期小结

(1)关于Spring中对bean生命周期控制提供了两种方式:
在配置文件中的 bean 标签中添加 init-method destroy-method 属性
类实现 InitializingBean DisposableBean 接口,这种方式了解下即可。
(2)对于bean的生命周期控制在bean的整个生命周期中所处的位置如下:
初始化容器
1. 创建对象 ( 内存分配 )
2. 执行构造方法
3. 执行属性注入 (set 操作 )
4. 执行 bean 初始化方法
使用 bean
1. 执行业务操作
关闭 / 销毁容器
1. 执行 bean 销毁方法
(3)关闭容器的两种方式:
ConfigurableApplicationContext ApplicationContext 的子类
close() 方法
registerShutdownHook()方法

1.9setter注入和构造器注入

1.9.1setter注入引用类型

1.在实体类 中定义引用类型属性,并提供可访问的 set 方法
2. 配置中使用 property 标签 ref 属性注入引用类型对象

例如:

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

    <bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>

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

1.9.2setter注入简单数据类型

1.在实体类 中定义引用类型属性,并提供可访问的 set
2. 配置中使用 property 标签 value 属性注入简单数据类型的值
例如:
    <!--注入简单类型-->
    <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
        <!--property标签:设置注入属性-->
        <!--name属性:设置注入的属性名,实际是set方法对应的名称-->
        <!--value属性:设置注入简单类型数据值-->
        <property name="connectionNum" value="100"/>
        <property name="databaseName" value="mysql"/>
    </bean>

说明:value:后面跟的是简单数据类型,对于参数类型,Spring在注入的时候会自动转换,但无法

将字母转换为数字

1.9.3构造器注入引用数据类型

1.删除 setter方法并且提供 构造方法
2.配置文件中进行配置 构造方式注入
例如:
    <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>

    <bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
        <constructor-arg name="bookDao" ref="bookDao"/>
    </bean>
说明 :
在标签中
name 属性对应的值为构造函数中方法形参的参数名,必须要保持一致。
ref 属性指向的是 spring IOC 容器中其他 bean 对象。
此外,构造器注入多个引用数据类型,只需在构造器中添加形参,而在配置文件中添加
<constructor-arg name=? ref=?/>标签,该标签在多个的情况下,无先后顺序。

1.9.4构造器注入多个简单数据类型(解耦的两种方式)

1.添加多个 简单属性并提供 构造方法
2.配置完成 多个属性构造器注入
例如:
    <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
        根据构造方法参数名称注入
        <constructor-arg name="connectionNum" value="10"/>
        <constructor-arg name="databaseName" value="mysql"/>
    </bean>

由于根据属性name来进行数据注入存在较高耦合,我们使用以下两种方式来降低耦合

方式一(将name属性替换为type属性,添加相应的类型):

    <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
        根据构造方法参数类型注入
        <constructor-arg type="int" value="10"/>
        <constructor-arg type="java.lang.String" value="mysql"/>
    </bean>

方式二(将name属性替换为index属性,添加对应的参数位置(0开始)):

    <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
        <!--根据构造方法参数位置注入-->
        <constructor-arg index="0" value="mysql"/>
        <constructor-arg index="1" value="100"/>
    </bean>

1.9.5参数注入方式的选择

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

1.10自动配置

1.10.1依赖自动装配

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

1.10.2自动装配的方式(按类型byType和按名称byName)

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

 在配置文件中,<property>标签删除,在<bean>标签中添加autowire属性

例如:

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

</beans>
注意事项 :
需要注入属性的类中对应属性的 setter 方法不能省略
被注入的对象必须要被 Spring IOC 容器管理
按照类型在 Spring IOC 容器中如果找到多个对象,会报 NoUniqueBeanDefinitionException
一个类型在 IOC 中有多个对象,还想要注入成功,这个时候就需要按照名称注入,配置方式为 :
<?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 class="com.itheima.dao.impl.BookDaoImpl"/>
    <!--autowire属性:开启自动装配,通常使用按类型装配-->
    <bean id="bookService" class="com.itheima.service.impl.BookServiceImpl" autowire="byName"/>

</beans>
注意事项 :
按照名称注入中的名称指的是什么?
0bookDao private 修饰的,外部类无法直接方法, 部类只能通过属性的 set 方法进行访问
对外部类来说, setBookDao 方法名,去掉 set 后首字母小写是其属性名
为什么是去掉set首字母小写?
这个规则是 set 方法生成的默认规则, set 方法的生成是把属性名首字母大写前面加 set 形成
的方法名, 所以按照名称注入,其实是和对应的 set 方法有关,但是如果按照标准起名称,属性名
set 应的名是一致的, 如果按照名称去找对应的 bean 对象,找不到则注入 Null, 当某一个类型
IOC 容器中有多个对象,按照名称注入只找其指定名称对应的 bean 对象,不会报错
两种方式介绍完后,以后用的更多的是按照类型注入。
最后对于依赖注入,需要注意一些其他的配置特征 :
1. 自动装配用于引用类型依赖注入,不能对简单类型进行操作
2. 使用按类型装配时( byType )必须保障容器中相同类型的 bean 唯一,推荐使用
3. 使用按名称装配时( byName )必须保障容器中具有指定名称的 bean ,因变量名与配置耦
合,不推 荐使用
4. 自动装配优先级低于setter注入与构造器注入,同时出现时自动装配配置失效

1.10.3集合注入

常见的集合类型有哪些 ?
数组
List
Set
Map
Properties

集合注入的格式:

<?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="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
        <!--数组注入-->
        <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>
    </bean>
</beans>

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spring Framework---IOC/DI 的相关文章

随机推荐