天天看点

Spring Framework---IOC/DI1.Spring框架的主要内容

目录

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 框架中提供了两个大的核心技术,分别是 : IOC和 AOP 事务处理 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 的架构图:

Spring Framework---IOC/DI1.Spring框架的主要内容

(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 来完成单元测试和集成测试
Spring Framework---IOC/DI1.Spring框架的主要内容

1.3Spring核心概念

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

1.3.1IOC(Inversion of control)控制反转

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

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

1.3.2DI(Dependency Injection)依赖注入

Spring Framework---IOC/DI1.Spring框架的主要内容
(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项目

Spring Framework---IOC/DI1.Spring框架的主要内容

步骤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,BookServiceImpl,BookDao和BookDaoImpl四个类

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 的配置

Spring Framework---IOC/DI1.Spring框架的主要内容

步骤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:运行程序

测试结果为:

Spring Framework---IOC/DI1.Spring框架的主要内容
但是在 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 进行注入

综上所述,对应关系如下 :

Spring Framework---IOC/DI1.Spring框架的主要内容

步骤4:运行程序

运行,测试结果为:

Spring Framework---IOC/DI1.Spring框架的主要内容

1.6bean基础配置

Spring Framework---IOC/DI1.Spring框架的主要内容

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: 运行程序 测试结果为:

Spring Framework---IOC/DI1.Spring框架的主要内容

注意事项 : bean 依赖注入的 ref 属性指定 bean ,必须在容器中存在

Spring Framework---IOC/DI1.Spring框架的主要内容

如果不存在 , 则会报错,如下 :

Spring Framework---IOC/DI1.Spring框架的主要内容

这个错误大家需要特别关注下 :

Spring Framework---IOC/DI1.Spring框架的主要内容

  获取 bean 无论是通过 id 还是 name 获取,如果无法获取到,将抛出异常 NoSuchBeanDefinitionException

*1.6.2bean作用范围scope配置

Spring Framework---IOC/DI1.Spring框架的主要内容

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小结

Spring Framework---IOC/DI1.Spring框架的主要内容

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: 具体工厂类中创建对象的方法名
Spring Framework---IOC/DI1.Spring框架的主要内容
通过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: 工厂对象中的具体创建对象的方法名 , 对应关系如下 :
Spring Framework---IOC/DI1.Spring框架的主要内容
首先我们想要从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运行类不用做任何修改,直接运行

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

1.7.5bean小结

(1)bean是如何创建的呢? 构造方法 (2)Spring的IOC实例化对象的三种方式分别是: 构造方法 ( 常用 ) 静态工厂 ( 了解 ) 实例工厂 ( 了解 ) 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 打印结果为:

Spring Framework---IOC/DI1.Spring框架的主要内容
从结果中可以看出, 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");
           

调用ctx的close()方法

ctx.close();
           

运行程序,就能执行 destroy 方法的内容

Spring Framework---IOC/DI1.Spring框架的主要内容

1.8.2注册钩子关闭容器

在容器未关闭之前,提前设置好回调函数,让 JVM 在退出之前回调此函数来关闭容器 调用 ctx 的 registerShutdownHook() 方法

ctx.registerShutdownHook();
           

注意 : registerShutdownHook 在 ApplicationContext 中也没有,在其子类 运行后,查询打印结果

Spring Framework---IOC/DI1.Spring框架的主要内容
两种方式介绍完后,close和registerShutdownHook选哪个? 相同点 : 这两种都能用来关闭容器 不同点 :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 容器为其注入属性的方法 思考:afterPropertiesSet和setBookDao谁先执行? 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>