Spring 学习笔记 - 核心容器

知识分子没文化
2021-12-18 / 0 评论 / 249 阅读 / 正在检测是否收录...

目录:

学习之前

1、Spring 要学习哪些技术:

  • IOC
  • AOP
  • Spring 的事务处理
  • 整合 Mybatis 框架

2、怎么学

  • 学习 Spring 框架设计思想
  • 学习基础操作,思考操作与思想建的联系
  • 学习案例,熟练应用操作的同时,体会思想

3、初识 Spring

Spring 家族:

官网:https://spring.io

基础框架:Spring Framework ——是其他项目的根基

简化、加速开发:Spring Boot

分布式开发:Spring Cloud

Spring发展史:

01

学习路线:

02

4、Spring的核心Maven依赖

<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.9</version>
</dependency>


<!-- JDBC -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.3.9</version>
</dependency>

参考:spring maven依赖 - 烟火里的尘埃 - 博客园 (cnblogs.com)


一、核心概念

代码书写现状:耦合度偏高

解决方案:使用对象时,在程序中不要主动使用 new 产生对象,而是转换为由外部提供对象。

1.1、IoC(Inversion of Control)控制反转

使用对象时,由程序主动 new 产生对象转换为由外部提供对象,此过程中对象的创建控制权也由程序转移到外部,这种思想称为控制反转。

  • 实质上是对象的创建控制权发生了变化

  • Spring 提供了一个容器,称为 IOC 容器,用l来充当 IOC 思想中的“外部”

  • IOC 容器负责对象的创建、初始化等一系列工作,被创建或管理的对象在 IoC 容器中统称为 Bean

目标:

  • 使用 IoC 容器管理 bean —— IOC
  • 在 IoC 容器中将有依赖关系的 bean 进行关系绑定 —— DI

最终效果:使用对象是不仅可以从 IOC 容器中获取,并且获取到的 bean 中已经绑定了所有的依赖关系。

IOC入门思路分析:

  1. 管理什么?(Service 与 Dao)

  2. 如何将被管理的对象告知 IoC 容器?(配置 applicationContext.xml 文件)

  3. 被管理的对象交给 IoC 容器,使用时如何获取到 IoC 容器?(接口)

  4. IoC 容器得到之后,如何从容器中获取 bean?(接口方法 getBean() )

  5. 使用 Spring 导入哪些坐标?(pom.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">
    <!--    1.导入spring坐标spring-context,对应版本是5.2.10.RELEASE-->

    <!--    2.配置bean -->
    <!--    bean 标签表示配置 bean
            id 属性表示给 bean 起名字,在同一个上下文中不能重复
            class 属性表示给 bean 定义类型 -->
    <bean id="bookDao" class="com.wlplove.dao.impl.BookDaoImpl"/>

</beans>

1.2、DI(Dependency Inject)依赖注入

在容器中建立 bean 与 bean 之间的依赖关系的整个过程称为依赖注入

DI 入门案例思路分析:

  1. 基于 IoC 管理 bean
  2. Service 中使用 new 形式创建的 Dao对象是否保留(否)
  3. Service 中需要的 Dao 对象如何进入到 Service 中(提供方法)
  4. Service 与 Dao 间的关系如何描述(配置)

二、IOC

2.1、bean 的配置

2.1.1、bean 的基础配置(id与class)
<!-- bean 标签表示配置 bean
     id 属性表示给 bean 起名字,在同一个上下文中不能重复
     class 属性表示给 bean 定义类型,后面接的是全路径类名 -->
<bean id="bookDao" class="com.wlplove.dao.impl.BookDaoImpl"/>


<bean id="bookService" class="com.wlplove.service.impl.BookServiceImpl">
    <!--   配置service与dao的关系     -->
    <!--  property 标签表示配置当前 bean 的属性,需要其设置相应的set方法
          name 属性表示配置哪一个具体的属性,是当前属性的名称
          ref 属性表示参照哪一个 bean,是当前容器中存在的 bean -->
    <property name="bookDao" ref="bookDao"/>
</bean>
类别 描述
名称 bean
类型 属性
所属 beans 标签
功能 定义 Spring 核心容器管理的对象
格式

属性列表 id:bean 的 id,使用容器可以通过 id 值获取对应的 bean,在一个 IoC 容器中 id 值唯一
class:bean 的类型,即配置的 bean 的全路径类名
范例
2.1.2、bean的别名配置(name)
<!-- name 属性表示别名 -->
    <bean id="bookService" name="service service2 bookEbi" class="com.wlplove.service.impl.BookServiceImpl"/>
类别 描述
名称 name
类型 属性
所属 bean 标签
功能 定义 bean 的别名,可定义多个,使用逗号、分号、空格分开
范例
2.1.3、bean的作用范围(scope)

控制创建的 bean 实体的数量(是否单例)

<!-- "scope"是用来控制 bean 的作用域,默认是单例模式 singleton,也可以指定成 prototype(非单例) -->
<bean id="bookDao" class="com.wlplove.dao.impl.BookDaoImpl" scope="singleton"/>
类别 描述
名称 scope
类型 属性
所属 bean 标签
功能 定义 bean 的作用范围,可选:singleton(单例,默认)、prototype(非单例)
范例
  • 为什么 bean 默认为单例?
  • 适合交给容器管理的 bean
    • 表现层对象
    • 业务层对象
    • 数据层对象
    • 工具对象
  • 不适合交给容器管理的 bean
    • 封装实体的域对象

2.2、实例化bean

bean 的实例化过程 主要解决两部分内容,分别是

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

在讲解这三种创建方式之前,我们需要先确认一件事:

bean 本质上就是对象,对象在 new 的时候会使用构造方法完成,那创建bean也是使用构造方法完成的。

2.2.1、构造方法(常用)

提供可访问的构造方法:

public interface BookDao {
    public void save();
}

public class BookDaoImpl implements BookDao {
    private BookDaoImpl() {
        System.out.println("book dao constructor is running ....");
    }

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

配置:

<bean id="bookDao" class="com.wlplove.dao.impl.BookDaoImpl"/>
  • 无参构造方法如果不存在,将抛出异常 BeanCreationException
2.2.2、静态工厂(了解)
//静态工厂创建对象
public class OrderDaoFactory {
    // 工厂方法
    public static OrderDao getOrderDao(){
        return new OrderDaoImpl();
    }
}

配置:

<bean id="orderDao" class="com.wlplove.factory.OrderDaoFactory" 
    factory-method="getOrderDao"/>
2.2.3、实例工厂(了解)

FactoryBean(实用,较重要)

public class UserDaoFactoryBean implements FactoryBean<UserDao> {
    //代替原始实例工厂中创建对象的方法
    public UserDao getObject() throws Exception {
        return new UserDaoImpl();
    }

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

配置:

<bean id="userDao" class="com.wlplove.factory.UserDaoFactoryBean" />

2.3、bean的生命周期

  • bean 生命周期:bean 从创建到销毁的整个过程
<!-- “init-method”方法指定 bean 初始化方法,“destory-method”方法指定 bean 销毁方法 -->
<bean id="bookDao" class="com.wlplove.dao.impl.BookDaoImpl" init-method="init" destroy-method="destory"></bean>

小结:

(1)关于Spring中对bean生命周期控制提供了两种方式:

  • 在配置文件中的bean标签中添加init-methoddestroy-method属性
  • 类实现InitializingBeanDisposableBean接口,这种方式了解下即可。

(2)整个生命周期:

  • 初始化容器

    • 创建对象(内存分配)

    • 执行构造方法

    • 执行属性注入(set 注入)

    • 执行 bean 初始化方法

  • 使用 bean

    • 执行业务操作
  • 关闭/销毁容器

    • 执行 bean 销毁方法

(3)关闭容器的两种方式:

  • ConfigurableApplicationContext是ApplicationContext的子类
    • close()方法
    • registerShutdownHook()方法

三、DI

  1. 向一个类中传递数据的方式有几种:

    • 普通方法(set 方法)
    • 构造方法
  2. 依赖注入描述了在容器中简历 bean 与 bean 之间依赖关系的过程,如果 bean 运行需要的是数字或者字符串呢?

    • 简单类型(基本数据类型与 String)
    • 引用类型
  3. 依赖注入方式

    • setter 注入
      • 简单类型
      • 引用类型
    • 构造器注入
      • 简单类型
      • 引用类型

3.1、setter 注入

分为 简单类型引用类型 的注入

  • 类中需要提供可访问的 set 方法

    public void setConnectionNum(int connectionNum) {
        this.connectionNum = connectionNum;
    }
    public void setDatebaseName(String datebaseName) {
        this.datebaseName = datebaseName;
    }
    
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
    public void setBookDao(BookDaoImpl bookDao) {
        this.bookDao = bookDao;
    }
  • xml文件配置:注入标签需要用

    <!-- 简单类型 (通过 value 属性注入普通类型值) -->
    <bean id="bookDao" class="com.wlplove.dao.impl.BookDaoImpl">
      <property name="datebaseName" value="mysql"></property>
        <property name="connectionNum" value="100"></property>
    </bean>
    
    <!-- 引用类型 (通过 ref 属性注入引用类型值) -->
    <bean id="bookService" class="com.wlplove.service.impl.BookServiceImpl">
      <property name="bookDao" ref="bookDao"></property>
        <property name="userDao" ref="userDao"></property>
    </bean>

3.2、构造方法注入

同样地,分为 简单类型引用类型 的注入

  • 类似于 setter 注入需要提供 set 方法,构造方法注入需要在类中提供构造方法:

    public BookDaoImpl(int connectionNum, String datebaseName) {
        this.connectionNum = connectionNum;
        this.datebaseName = datebaseName;
    }
    
    public BookServiceImpl(BookDao bookDao, UserDaoImpl userDao) {
        this.bookDao = bookDao;
        this.userDao = userDao;
    }
  • xml 文件中的配置:注入标签需要用

    <!-- 普通类型 (通过 value 属性注入引用类型值,name属性的值是构造方法形参的值)-->
    <bean id="bookDao" class="com.wlplove.dao.impl.BookDaoImpl">
        <constructor-arg name="connectionNum" value="666"></constructor-arg>
        <constructor-arg name="datebaseName" value="mysql"></constructor-arg>
    </bean>
    
    <bean id="userDao" class="com.wlplove.dao.impl.UserDaoImpl"></bean>
    
    <!-- 引用类型 (通过 ref 属性注入引用类型值,name 属性的值是构造方法形参的值)-->
    <bean id="bookService" class="com.wlplove.service.impl.BookServiceImpl">
        <constructor-arg name="bookDao" ref="bookDao"/>
        <constructor-arg name="userDao" ref="userDao"/>
    </bean>

3.3、构造器注入的其他xml配置写法:

3.3.1、type 方式

配置中使用 constructor-arg 标签、type 属性,设置按构造方法的形参类型注入:

<!--type方式,解决 name 属性绑定了形参名称的问题-->
<bean id="bookDao" class="com.wlplove.dao.impl.BookDaoImpl">
    <constructor-arg type="int" value="666"></constructor-arg>
    <constructor-arg type="java.lang.String" name="datebaseName" value="mysql"></constructor-arg>
</bean>
3.3.2、index 索引方式

配置中使用 constructor-arg 标签 index 属性设置按形参类型注入

<!-- index索引方式,解决参数类型重复问题,使用索引进行参数匹配对应 -->
<bean id="bookDao" class="com.wlplove.dao.impl.BookDaoImpl">
    <constructor-arg index="0" value="6"></constructor-arg>
    <constructor-arg index="1" name="datebaseName" value="mysql"></constructor-arg>
</bean>

3.4、依赖注入方式的选择

  • 尽量选择 setter 注入:自己开发的模块推荐使用 setter 注入
  • 强制依赖使用构造器进行注入,但是使用 setter 注入时不进行注入的话会导致 null 现象出现
  • 可选依赖使用 setter 注入进行,灵活性强
  • Spring 框架倡导使用构造器,第三方框架内部大多数采用构造器注入的形式进行数据初始化,相对严谨
  • 如果有必要可以两者也可同时使用,使用构造器注入完成强制依赖的注入,使用 setter 注入完成可选依赖的注入
  • 实际开发过程中还要根据实际情况分析,如果受控对象没有提供 setter 方法就必须使用构造器注入

四、自动装配

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

自动装配有这么几种方式:

  • 按类型(常用)
  • 按名称
  • 按构造方法

自动装配时需要注意:

  • 需要注入属性的类中对应属性的 setter 方法不能省略

  • 被注入的对象必须要被 Spring 的 IoC 容器管理

自动装配只需要修改applicationContext.xml配置文件即可:

  1. <property> 标签删除
  2. <bean> 标签中添加 autowire 属性

4.1、按类型 byType(推荐使用)

如果容器中只存在一个属性类型的 bean,则让属性自动装配。 如果存在多个,则会引发异常,这表明不能为该 bean 使用 byType 自动装配。 如果没有匹配的 bean,则不会发生任何事情(未设置属性)。

会自动在容器上下文中查找,和对象 set() 方法后面的值对应的 bean 的 id

需要保证所有 bean 的类型唯一,并且这个 bean 需要和自动注入的属性的 set() 方法的值一致。

按照类型自动装配时,如果在 Spring 的 IoC 容器中如果找到多个对象,会报 NoUniqueBeanDefinitionException 异常

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

<!-- autowire="byType" 属性按类型自动装配,甚至都可以不指定 id 属性 -->
<bean id="bookService" class="com.wlplove.service.impl.BookServiceImpl" autowire="byType"/>

4.2、按名称 byName(不建议使用)

  • 按照名称注入中的名称指的是什么?

    03

    • bookDao 是 private 修饰的,外部类无法直接访问,只能通过属性的 set 方法进行访问
    • 对外部类来说,setBookDao 是方法名,去掉 set 后首字母小写是其属性名
      • 为什么是去掉 set 首字母小写?
      • 这个规则是 set 方法生成的默认规则,set方法的生成是把属性名首字母大写前面加set形成的方法名
    • 所以按照名称注入,其实是和对应的 set 方法有关,但是如果按照标准起名称,属性名和 set 对应的名是一致的
  • 如果按照名称去找对应的 bean 对象,找不到则注入 Null

  • 当某一个类型在 IoC 容器中有多个对象,按照名称注入只找其指定名称对应的 bean 对象,不会报错

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

<!-- autowire="byName" 按名称自动装配 -->
<bean id="bookService" class="com.wlplove.service.impl.BookServiceImpl" autowire="byName"/>

最后对于依赖注入,需要注意一些其他的配置特征:

  1. 自动装配只能用于引用类型依赖注入,不能对简单类型进行操作
  2. 使用按类型装配时(byType)必须保障容器中相同类型的bean唯一,推荐使用
  3. 使用按名称装配时(byName)必须保障容器中具有指定名称的 bean,因变量名与配置耦合,不推荐使用
  4. 自动装配优先级低于 setter 注入与构造器注入,同时出现时自动装配配置失效

五、集合的注入

接下来的例子以 setter 注入为例进行说明,构造器注入也是类似的,将 标签修改为 即可。

5.1、注入数组类型数据

注入数组对象:

<!-- name 属性设置将数据注入哪一个数组 -->
<property name="test_array">
    <!-- 此处 array 与 list 可以混用 -->
    <array>
        <value>123</value>
        <value>456</value>
        <value>789</value>
        <!--  引用类型可以这样写:-->
        <!--  <ref bean="beanId"></ref>-->
    </array>
</property>

5.2、注入 Set 类型数据

注入 Set 对象:

<property name="test_set">
    <set>
        <value>set_qwe1</value>
        <value>set_asd2</value>
        <value>set_zxc3</value>
        <!-- 重了会自动过滤 -->
        <value>set_zxc3</value>
    </set>
</property>

5.3、注入 List 类型数据

注入 List 对象:

<property name="test_list">
    <!-- 此处 array 与 list 可以混用 -->
    <list>
        <value>list_qwe1</value>
        <value>list_asd2</value>
        <value>list_zxc3</value>
    </list>
</property>

5.4、注入 Map 类型数据

注入 Map 对象:

<property name="test_map">
    <map>
        <entry key="contry" value="China"></entry>
        <entry key="province" value="Gansu"></entry>
        <entry key="city" value="Lanzhou"></entry>
    </map>
</property>

5.5、注入 property 类型数据

<property name="test_properties">
    <props>
        <prop key="contry">China</prop>
        <prop key="province">Gansu</prop>
        s<prop key="city">Lanzhou</prop>
    </props>
</property>

说明:

  • property 标签表示 setter 方式注入,构造方式注入 constructor-arg 标签内部也可以写<array><list><set><map><props>标签
  • List 的底层也是通过数组实现的,所以 <list><array> 标签是可以混用
  • 集合中要添加引用类型,只需要把 <value> 标签改成 <ref> 标签,但这种方式用的比较少

六、加载properties文件

第一步:xml文件中添加 context 命名空间:

原文件:

<?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">

</beans>

通过复制修改过的新文件:

<?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
+       http://www.springframework.org/schema/context/spring-context.xsd
        ">
</beans>

第二步:在 xml 文件中添加,使用 context 空间加载指定 properties 文件

<context:property-placeholder location="jdbc.properties"/>

<!-- 可以加载多个文件 -->
<context:property-placeholder location="jdbc.properties,jdbc2.properties"/>
<!-- 或者用通配符加载所有文件 -->
<context:property-placeholder location="*.properties"/>

<!-- 用 system-properties-mode="NEVER" 禁用系统属性 -->
<context:property-placeholder location="classpath:*.properties" system-properties-mode="NEVER"/>

<!-- 从类路径或 jar 包中搜索并加载所有 properties 文件 -->
<context:property-placeholder location="classpath*:*.properties"/>

最规范的写法可以这样:

<!-- 最规范的写法,标准格式 -->
<context:property-placeholder location="classpath:*.properties"/>

第三步:使用 ${} 读取加载的属性值

properties 的文件内容为:

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/spring_db
jdbc.username=root
jdbc.password=root

则这样写:

<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>

七、容器的核心操作

7.1、创建容器

  • 方式一:通过类路径来加载配置文件的方式创建容器

    ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
  • 方式二:通过文件路径来加载配置文件的方式创建容器

    ApplicationContext ctx = new FileSystemXmlApplicationContext("E:\\applicationContext.xml");
  • 以上两种方式也都支持加载多个配置文件:

    ApplicationContext ctx = new ClassPathXmlApplicationContext("bean1.xml","bean2.xml");
    
    ApplicationContext ctx = new FileSystemXmlApplicationContext("E:\\bean1.xml","E:\\bean2.xml");

7.2、获取 bean

  • 方式一:使用 bean 名称获取

    // 需要设置类型转换
    BookDao bookDao = (BookDao) ctx.getBean("bookDao");
  • 方式二:使用 bean 名称获取并指定类型

    BookDao bookDao = ctx.getBean("bookDao",BookDao.class);
  • 方式三:使用 bean 类型获取

    // 注意:参数是 类.class
    BookDao bookDao = ctx.getBean(BookDao.class);

7.3、容器类层次结构

7.4、BeanFactory

  • BeanFactory是延迟加载,只有在获取 bean 对象的时候才会去创建

  • ApplicationContext 是立即加载,容器加载的时候就会创建 bean 对象

  • ApplicationContext 要想成为延迟加载,只需要按照如下方式进行配置,设置 “ lazy-init="true" ”

    <?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"  lazy-init="true"/>
    </beans>

7.5、核心容器内容总结

7.5.1、容器相关
  • BeanFactory 是 IoC 容器的顶层接口,初始化 BeanFactory 对象时,加载的 bean 延迟加载
  • ApplicationContext 接口是Spring容器的核心接口,初始化时 bean 立即加载
  • ApplicationContext 接口提供基础的 bean 操作相关方法,通过其他接口扩展其功能
  • ApplicationContext 接口常用初始化类
    • ClassPathXmlApplicationContext(常用)
    • FileSystemXmlApplicationContext
7.5.2、bean 相关

04

7.5.3、依赖注入相关

05

0

评论 (0)

取消