day01 spring

第一章 Spring的简介

第一节 Spring公司简介

该公司的创建者Rod Johnson被称之为Spring之父,他领导的Spring研发团队下有众多的优秀开发者,Spring公司旗下有非常多的优秀框架。例如:Spring FrameWork、Spring Boot、Spring Cloud、Spring Data、Spring Security等等,几乎涉及了Java开发的每一个领域。 官网地址:https://spring.io/

Spring 大礼包 spring

第二节 Spring Framework的介绍

1. 概念

Spring Framework是Spring 基础框架,可以视为 Spring 基础设施,基本上任何其他 Spring 项目都是以 Spring Framework 为基础的。是一个分层的JavaSE/EE full-stack(一站式) 轻量级开源框架

2. 特征
  • 非侵入式:使用 Spring Framework 开发应用程序时,Spring 对应用程序本身的结构影响非常小。对领域模型(domain)可以做到零污染;对功能性组件也只需要使用几个简单的注解进行标记,完全不会破坏原有结构,反而能将组件结构进一步简化。这就使得基于 Spring Framework 开发应用程序时结构清晰、简洁优雅。
  • 控制反转:IOC——Inversion of Control,反转资源获取方向。把自己创建资源变成环境将资源准备好,我们享受资源注入。
  • 面向切面编程:AOP——Aspect Oriented Programming,在不修改源代码的基础上增强代码功能。
  • 容器:Spring IOC 是一个容器,因为它包含并且管理组件对象的生命周期。组件享受到了容器化的管理,替程序员屏蔽了组件创建过程中的大量细节,极大的降低了使用门槛,大幅度提高了开发效率。
  • 组件化:Spring 实现了使用简单的组件配置组合成一个复杂的应用。在 Spring 中可以使用 XML 和 Java 注解组合这些对象。这使得我们可以基于一个个功能明确、边界清晰的组件有条不紊的搭建超大型复杂应用系统。
  • 声明式:很多以前需要编写代码才能实现的功能,现在只需要声明需求即可由框架代为实现。
  • 一站式:在 IOC 和 AOP 的基础上可以整合各种企业应用的开源框架和优秀的第三方类库。而且 Spring 旗下的项目已经覆盖了广泛领域,很多方面的功能性需求可以在 Spring Framework 的基础上全部使用 Spring 来实现。
3. Spring Framework五大功能模块

Spring 大礼包 spring

功能模块

功能介绍

Core Container

核心容器,在 Spring 环境下使用任何功能都必须基于 IOC 容器。

AOP&Aspects

面向切面编程

Test

提供了对 junit 或 TestNG 测试框架的整合。

Data Access/Integration

提供了对数据访问/集成的功能。

Spring MVC

提供了面向Web应用程序的集成功能。

第二章 IOC容器概念

第一节 容器的概念

1. 普通容器

普通容器只是负责存储数据(对象),例如我们在JavaSE中学习的数组、List、Map等等,可以让我们使用它存储数据、获取数据,不具备其它复杂的功能

2. 复杂容器

复杂容器不仅要负责存储对象,还需要具备创建对象、调用对象方法、管理对象生命周期、并且在一定情况下负责销毁对象。例如我们之前学习的Tomcat就是一个复杂容器,它能够负责创建Servlet、Filter、Listener等等对象,并且管理他们的生命周期,在生命周期的不同阶段调用他们的不同方法。而我们后续要学习的IOC容器也是一个复杂容器

第二节 IOC的概念

1. 传统方式创建对象

传统方式创建对象的方式是: 需要哪个类的对象,就直接在项目中new哪个类的对象,这样就会导致各个类之间的耦合度非常高

Spring 大礼包 spring

2. IOC方式创建对象

IOC(inversion of control)的中文解释是“控制反转”,对象的使用者不是创建者. 作用是将对象的创建反转给spring框架来创建和管理。控制反转怎么去理解呢。 其实它反转的是什么呢,是对象的创建工作。 举个例子:平常我们在servlet或者service里面创建对象,都是使用new 的方式来直接创建对象,现在有了spring之后,我们就再也不new对象了,而是把对象创建的工作交给spring容器去维护。我们只需要告诉spring容器我们需要什么对象即可

IOC的作用:削减计算机程序的耦合(解除我们代码中的依赖关系)。

Spring 大礼包 spring

第三节 IOC容器在Spring中的实现

Spring 的 IOC 容器就是 IOC 思想的一个落地的产品实现。IOC 容器中管理的组件也叫做 bean。在创建 bean 之前,首先需要创建 IOC 容器。Spring 提供了 IOC 容器的两种实现方式:

1. BeanFactory

这是 IOC 容器的基本实现,是 Spring 内部使用的接口。面向 Spring 框架本身,供Spring框架内部功能使用,不建议开发人员使用。

2. ApplicationContext

BeanFactory 的子接口,提供了更多高级特性。面向 Spring 框架的使用者,几乎所有场合都使用 ApplicationContext 而不是底层的 BeanFactory。

以后在 Spring 环境下看到一个类或接口的名称中包含 ApplicationContext,那基本就可以断定,这个类或接口与 IOC 容器有关。

3. ApplicationContext的主要实现类

类型名

简介

ClassPathXmlApplicationContext

通过读取类路径下的 XML 格式的配置文件创建 IOC 容器对象

FileSystemXmlApplicationContext

通过文件系统路径读取 XML 格式的配置文件创建 IOC 容器对象

ConfigurableApplicationContext

ApplicationContext 的子接口,包含一些扩展方法 refresh() 和 close() ,让 ApplicationContext 具有启动、关闭和刷新上下文的能力。

AnnotationConfigApplicationContext

可以实现基于Java的配置类加载Spring的应用上下文,创建IOC容器对象

WebApplicationContext

专门为 Web 应用准备,基于 Web 环境创建 IOC 容器对象,并将对象引用存入 ServletContext 域中。

第三章 Spring IOC

第一节 快速入门

1. 目标

1.1 让Spring IOC容器创建类的对象

1.2 从Spring IOC容器中获取对象

2. 思路

Spring 大礼包 spring

3. 具体实现
3.1 Maven依赖
<dependencies>
    <!-- 基于Maven依赖传递性,导入spring-context依赖即可导入当前所需所有jar包 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.1</version>
    </dependency>
    <!-- junit测试 -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
</dependencies>
3.2 创建类
package com.atguigu.controller;

public class UserController {

    public String getName(){
        return "张三";
    }

}
3.3 创建Spring配置文件并且配置组件

配置文件的存放路径建议放在resources根路径下,配置文件名字随意

Spring 大礼包 spring

<?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属性: 对象的唯一标识,根据这个唯一标识,就可以从核心容器中获取对象
            class属性: 对象所属的实现类的全限定名
    -->
    <bean id="userController" class="com.atguigu.controller.UserController"/>
</beans>
  • bean标签:通过配置bean标签告诉IOC容器需要创建对象的组件是什么
  • id属性:bean的唯一标识
  • class属性:组件类的全类名

3.4 从核心容器中获取对象
package com.atguigu;

import com.atguigu.controller.UserController;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * 包名:com.atguigu
 *
 * @author Leevi
 * 日期2022-11-09  10:48
 */
public class IocTest {
    @Test
    public void testStart(){
        //1. 创建IOC容器,加载配置文件:类路径中的spring.xml
        ApplicationContext act = new ClassPathXmlApplicationContext("spring.xml");

        //2. 从IOC容器中获取对象
        //2.1 方式一:根据类型获取,前提是IOC容器中只有一个该类型的对象
        //UserController userController = act.getBean(UserController.class);

        //2.2 方式二:根据名称/id获取
        UserController userController = (UserController) act.getBean("userController");
        System.out.println(userController.getName());
    }


}

如果该类型在核心容器中有多个对象:那么根据类型获取时会抛出异常,具体异常信息如下

org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type ‘com.atguigu.ioc.component.HappyComponent’ available: expected single matching bean but found 2: happyComponent,happyComponent2

思考

如果组件类实现了接口,根据接口类型可以获取 bean对象 吗?

可以,前提是bean对象唯一

如果一个接口有多个实现类,这些实现类都配置了 bean,根据接口类型可以获取 bean 吗?

不行,因为bean对象不唯一

结论

根据类型来获取bean时,在满足bean唯一性的前提下,其实只是看:『对象 instanceof 指定的类型』的返回结果,只要返回的是true就可以认定为和类型匹配,能够获取到。

第二节 依赖注入

依赖注入全称是 dependency Injection 翻译过来是依赖注入.其实就是如果spring核心容器管理的某一个类中存在属性,需要spring核心容器在创建该类实例的时候,顺便给这个对象里面的属性进行赋值。

1. setter方法注入

如果某个Bean对象的属性有对应的setter方法,那我们可以在配置文件中使用setter方法对属性进行依赖注入

1.1 注入简单类型数据
1.1.1 给组件类添加一个简单类型属性
package com.atguigu.controller;
/**
 * 给一个对象的成员变量赋值的方式:
 * 1. 调用set方法
 * 2. 通过构造器
 * 3. 通过暴力反射
 */
public class UserController {
    private String name;
    
    public void setName(String name) {
        this.name = name;
    }

    public String getName(){
        return name;
    }

}
1.1.2 在配置时给属性指定值

通过property标签配置的属性值会通过setXxx()方法注入

<!--
        依赖注入:给核心容器中的Bean对象的成员变量赋值
        setter方法进行依赖注入:
            在要进行依赖注入的bean标签中添加<property>子标签,该子标签的name属性就是要赋值的成员变量名
            前提是这个属性一定要有set方法,name属性的值应该是"setXXX"后面的"XXX"首字母改小写
            1. 注入简单类型数据: 那么我们使用property标签的value属性给简单类型的成员变量赋值


            2. 注入Bean类型数据: 那么我们使用property标签的ref属性给Bean类型的成员变量赋值,
                               ref属性的值就是要赋值的Bean类型的对象在核心容器中的id
    -->
<bean id="userController" class="com.atguigu.controller.UserControlle">
    <property name="username" value="奥巴马"></property>
</bean>
1.1.3测试代码
package com.atguigu;

import com.atguigu.controller.UserController;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * 包名:com.atguigu
 *
 * @author Leevi
 * 日期2022-11-09  10:48
 */
public class IocTest {
    @Test
    public void testStart(){
        //1. 创建IOC容器,加载配置文件:类路径中的spring.xml
        ApplicationContext act = new ClassPathXmlApplicationContext("spring.xml");

        //2. 从IOC容器中获取对象
        //2.1 方式一:根据类型获取,前提是IOC容器中只有一个该类型的对象
        //UserController userController = act.getBean(UserController.class);

        //2.2 方式二:根据名称/id获取
        UserController userController = (UserController) act.getBean("userController");
        System.out.println(userController.getName());
    }
}
1.2 注入Bean类型数据
1.2.1 创建接口UserService
package com.atguigu.service;

public interface UserService {
    String getName();
}
1.2.2 创建UserService实现类
package com.atguigu.service.impl;

import com.atguigu.dao.UserDao;
import com.atguigu.service.UserService;

public class UserServiceImpl implements UserService {
    private UserDao userDao;


    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public String getName() {
        return userDao.getName();
    }
}
1.2.3创建接口UserDao
package com.atguigu.dao;

public interface UserDao {

    String getName();
}
1.2.4 创建UserDao实现类
package com.atguigu.dao.impl;

import com.atguigu.dao.UserDao;

public class UserDaoImpl implements UserDao {
    private String username;

    public void setUsername(String username) {
        this.username = username;
    }

    @Override
    public String getName() {
        // 模拟数据库查询出了username
        return username;
    }
}
1.2.5 UserController代码
package com.atguigu.controller;

import com.atguigu.service.UserService;

public class UserController {
    private UserService userService;
    private String name;

    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName(){
        return userService.getName();
    }
}
1.2.6 在UserController注入userService和userDao
<?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="userController" class="com.atguigu.controller.UserController">
        <property name="userService" ref="userService"></property>
    </bean>

    <bean id="userService" class="com.atguigu.service.impl.UserServiceImpl">
        <property name="userDao" ref="userDao"></property>
    </bean>

    <bean id="userDao" class="com.atguigu.dao.impl.UserDaoImpl">
        <property name="username" value="李四"></property>
    </bean>
</beans>
1.2.6测试
package com.atguigu;


import com.atguigu.controller.UserController;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class IOCTest {

    @Test
    public void testStart(){
        // 1.创建IOC容器,加载配置文件:类路径中的spring.xml
        ApplicationContext act = new ClassPathXmlApplicationContext("spring.xml");

        // 2.从IOC容器中获取对象
        // 2.1 方式一:根据类型获取 前提是IOC容器只有一个该类型的对象
//        UserController userController = act.getBean(UserController.class);
//        System.out.println(userController.getName());

        // 2.2 方式二:根据名称/id获取
        UserController userController = (UserController) act.getBean("userController");
        System.out.println(userController.getName());
    }

}
1.2.6 易错点

如果错把ref属性写成了value属性,会抛出异常: Caused by: java.lang.IllegalStateException: Cannot convert value of type ‘java.lang.String’ to required type ‘com.atguigu.ioc.component.HappyMachine’ for property ‘happyMachine’: no matching editors or conversion strategy found 意思是不能把String类型转换成我们要的HappyMachine类型 说明我们使用value属性时,Spring只把这个属性看做一个普通的字符串,不会认为这是一个bean的id,更不会根据它去找到bean来赋值

1.3 注入内部Bean类型数据(了解)
1.3.1 重新配置原组件

在bean里面配置的bean就是内部bean,内部bean只能在当前bean内部使用,在其他地方不能使用。

public class UserDaoImpl implements UserDao {
    private String name;
    private Date date;

    @Override
    public String getName() {
        System.out.println("当前的时间是:" + date);
        //模拟从数据库查询出了name
        return name;
    }

    public void setDate(Date date) {
        this.date = date;
    }

    public void setName(String name) {
        this.name = name;
    }
}
<bean id="userDao" class="com.atguigu.dao.impl.UserDaoImpl" p:dataSource-ref="dataSource">
        <constructor-arg name="name">
            <null></null>
        </constructor-arg>

        <property name="date">
            <bean class="java.util.Date"></bean>
        </property>
    </bean>
1.3.2 测试
@Test
    public void testStart(){
        // 1.创建IOC容器,加载配置文件:类路径中的spring.xml
        ApplicationContext act = new ClassPathXmlApplicationContext("spring.xml");

        // 2.从IOC容器中获取对象
        // 2.1 方式一:根据类型获取 前提是IOC容器只有一个该类型的对象
//        UserController userController = act.getBean(UserController.class);
//        System.out.println(userController.getName());

        // 2.2 方式二:根据名称/id获取
        UserController userController = (UserController) act.getBean("userController");
        System.out.println(userController.getName());
    }
1.4 引入外部属性文件用于给Bean注入属性(重要)
1.4.1 添加Maven依赖

这个依赖只是为了使用Druid连接池,而不是引入外部属性文件所必须的

<!-- MySQL驱动 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.19</version>
</dependency>
<!-- 数据源 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.0.31</version>
</dependency>
1.4.2 创建外部属性文件

Spring 大礼包 spring

jdbc.user=root
jdbc.password=123456
jdbc.url=jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf8&serverTimezone=UTC
jdbc.driver=com.mysql.cj.jdbc.Driver
1.4.3 在spring的配置文件中引入jdbc.properties文件
<!-- 引入外部属性文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
1.4.4 在spring的配置文件中使用引入的jdbc.properties文件中的数据
<!--[重要]给bean的属性赋值:引入外部属性文件 -->
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="url" value="${jdbc.url}"/>
    <property name="driverClassName" value="${jdbc.driver}"/>
    <property name="username" value="${jdbc.user}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>
1.4.5 测试
@Test
    public void testStart(){
        // 1.创建IOC容器,加载配置文件:类路径中的spring.xml
        ApplicationContext act = new ClassPathXmlApplicationContext("spring.xml");

        // 2.从IOC容器中获取对象
        // 2.1 方式一:根据类型获取 前提是IOC容器只有一个该类型的对象
//        UserController userController = act.getBean(UserController.class);
//        System.out.println(userController.getName());

        // 2.2 方式二:根据名称/id获取
        UserController userController = (UserController) act.getBean("userController");
        System.out.println(userController.getName());
    }
1.4.6 结论

标签的value属性: 注入简单类型数据

标签的ref属性:用于引入IOC容器中的Bean对象的id,注入Bean对象类型的数据

1.5 注入集合类型属性(了解)
1.5.1 给组件类添加集合类型属性
package com.atguigu.dao.impl;

import com.atguigu.dao.UserDao;

import javax.sql.DataSource;
import java.util.Date;
import java.util.List;

public class UserDaoImpl implements UserDao {
    private String username;
    private Date date;
    private DataSource dataSource;
    private List<String> stringList;


    public UserDaoImpl() {
    }

    public UserDaoImpl(String username) {
        this.username = username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setDate(Date date) {
        this.date = date;
    }

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public void setStringList(List<String> stringList) {
        this.stringList = stringList;
    }


    @Override
    public String getName() {
        System.out.println("当前的时间是:" + date);
        System.out.println("数据源对象:" + dataSource);
        System.out.println("stringList集合对象:" + stringList);
        // 模拟从数据库查询出了name
        return username;
    }
}
1.5.2 配置
<bean id="userDao" class="com.atguigu.dao.impl.UserDaoImpl" p:dataSource-ref="druidDataSource">
<!--        <constructor-arg name="username" value="李四">-->
<!--        </constructor-arg>-->


        <property name="date">
            <bean class="java.util.Date"></bean>
        </property>
<!--        <property name="username" value="王五"></property>-->
        <property name="stringList" ref="stringList"></property>
<!--        <property name="dataSource" ref="druidDataSource"></property>-->
    </bean>


    <util:list id="stringList">
<!--        实现这种效果AA<BB<CC<-->
<!--        方式一 使用转义符-->
        <value>AA<BB<CC</value>
<!--        方式二 使用![CDATA]-->
        <value><![CDATA[AA<BB<CC]]></value>
        <value>李四</value>
        <value>王五</value>
    </util:list>
1.6 注入Map类型属性(了解)
1.6.1 给组件类添加Map类型属性
package com.atguigu.dao.impl;

import com.atguigu.dao.UserDao;

import javax.sql.DataSource;
import java.util.Date;
import java.util.List;
import java.util.Map;

public class UserDaoImpl implements UserDao {
    private String username;
    private Date date;
    private DataSource dataSource;
    private List<String> stringList;
    private Map<String,String> stringMap;

    public UserDaoImpl() {
    }

    public UserDaoImpl(String username) {
        this.username = username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setDate(Date date) {
        this.date = date;
    }

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public void setStringList(List<String> stringList) {
        this.stringList = stringList;
    }

    public void setStringMap(Map<String, String> stringMap) {
        this.stringMap = stringMap;
    }

    @Override
    public String getName() {
        System.out.println("当前的时间是:" + date);
        System.out.println("数据源对象:" + dataSource);
        System.out.println("stringList集合对象:" + stringList);
        System.out.println("stringMap对象:" + stringMap);
        // 模拟从数据库查询出了name
        return username;
    }
}
1.6.2 配置
<bean id="userDao" class="com.atguigu.dao.impl.UserDaoImpl" p:dataSource-ref="druidDataSource">
<!--        <constructor-arg name="username" value="李四">-->
<!--        </constructor-arg>-->

        <constructor-arg name="username">
            <null></null>
        </constructor-arg>

        <property name="date">
            <bean class="java.util.Date"></bean>
        </property>
<!--        <property name="username" value="王五"></property>-->
        <property name="stringList" ref="stringList"></property>
        <property name="stringMap" ref="stringMap"></property>
<!--        <property name="dataSource" ref="druidDataSource"></property>-->
    </bean>

    <util:map id="stringMap">
        <entry key="k1" value="v1"></entry>
        <entry key="k2" value="v2"></entry>
        <entry key="k3" value="v3"></entry>
    </util:map>
2. 构造器注入(了解)

在前面我们通过<bean>标签配置Bean对象,其实是执行Bean类的无参构造函数创建的对象,当Bean类包含有参构造函数的时候,我们在配置文件中可以通过有参构造函数进行配置注入

2.1 声明组件类
package com.atguigu.dao.impl;

import com.atguigu.dao.UserDao;

import javax.sql.DataSource;
import java.util.Date;
import java.util.List;
import java.util.Map;

public class UserDaoImpl implements UserDao {
    private String username = "德莱文";
    private Date date;
    private DataSource dataSource;
    private List<String> stringList;
    private Map<String,String> stringMap;

    public UserDaoImpl() {
    }

    public UserDaoImpl(String username) {
        this.username = username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setDate(Date date) {
        this.date = date;
    }

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public void setStringList(List<String> stringList) {
        this.stringList = stringList;
    }

    public void setStringMap(Map<String, String> stringMap) {
        this.stringMap = stringMap;
    }

    @Override
    public String getName() {
        System.out.println("当前的时间是:" + date);
        System.out.println("数据源对象:" + dataSource);
        System.out.println("stringList集合对象:" + stringList);
        System.out.println("stringMap对象:" + stringMap);
        // 模拟从数据库查询出了name
        return username;
    }
}
2.2 配置构造器注入
<bean id="userController" class="com.atguigu.controller.UserController">
        <property name="userService" ref="userService"></property>
    </bean>
<!--    构造器注入-->
    <bean id="userService" class="com.atguigu.service.impl.UserServiceImpl">
        <property name="userDao" ref="userDao"></property>
    </bean>

    <bean id="userDao" class="com.atguigu.dao.impl.UserDaoImpl">
        <constructor-arg name="username" value="张三"></constructor-arg>
<!--            set方式注入-->
<!--        <property name="username" value="李四"></property>-->
        <property name="date">
            <bean class="java.util.Date"></bean>
        </property>
        <property name="stringList" ref="stringList"></property>
        <property name="stringMap" ref="stringMap"></property>
    </bean>
2.3 测试
@Test
    public void testStart(){
        // 1.创建IOC容器,加载配置文件:类路径中的spring.xml
        ApplicationContext act = new ClassPathXmlApplicationContext("spring.xml");

        // 2.从IOC容器中获取对象
        // 2.1 方式一:根据类型获取 前提是IOC容器只有一个该类型的对象
//        UserController userController = act.getBean(UserController.class);
//        System.out.println(userController.getName());

        // 2.2 方式二:根据名称/id获取
        UserController userController = (UserController) act.getBean("userController");
        System.out.println(userController.getName());
    }
2.4 补充

constructor-arg标签还有两个属性可以进一步描述构造器参数:

  • index属性:指定参数所在位置的索引(从0开始)
  • name属性:指定参数名
3. 特殊值处理(了解)
3.1 声明一个类用于测试
package com.atguigu.dao.impl;

import com.atguigu.dao.UserDao;

import javax.sql.DataSource;
import java.util.Date;
import java.util.List;
import java.util.Map;

public class UserDaoImpl implements UserDao {
    private String username = "德莱文";
    private Date date;
    private DataSource dataSource;
    private List<String> stringList;
    private Map<String,String> stringMap;

    public UserDaoImpl() {
    }

    public UserDaoImpl(String username) {
        this.username = username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setDate(Date date) {
        this.date = date;
    }

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public void setStringList(List<String> stringList) {
        this.stringList = stringList;
    }

    public void setStringMap(Map<String, String> stringMap) {
        this.stringMap = stringMap;
    }

    @Override
    public String getName() {
        System.out.println("当前的时间是:" + date);
        System.out.println("数据源对象:" + dataSource);
        System.out.println("stringList集合对象:" + stringList);
        System.out.println("stringMap对象:" + stringMap);
        // 模拟从数据库查询出了name
        return username;
    }
}
3.2 null值
<constructor-arg name="username">
    <!--            注入属性为null-->
    <null></null>
</constructor-arg>
3.3 当value值中有特殊字符时
<util:list id="stringList">
<!--        实现这种效果AA<BB<CC<-->
<!--        方式一 使用转义符-->
        <value>AA<BB<CC</value>
<!--        方式二 使用![CDATA]-->
        <value><![CDATA[AA<BB<CC]]></value>
        <value>李四</value>
        <value>王五</value>
    </util:list>
4. p命名空间方式注入(了解)
4.1 引入p命名空间的约束

使用 p 名称空间需要导入相关的 XML 约束,在 IDEA 的协助下导入即可:

<?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"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:p="http://www.springframework.org/schema/p"
       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 http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd">
4.2 使用p命名空间注入
<bean id="userDao" class="com.atguigu.dao.impl.UserDaoImpl" p:dataSource-ref="druidDataSource">
4.3 测试
@Test
    public void testStart(){
        // 1.创建IOC容器,加载配置文件:类路径中的spring.xml
        ApplicationContext act = new ClassPathXmlApplicationContext("spring.xml");

        // 2.从IOC容器中获取对象
        // 2.1 方式一:根据类型获取 前提是IOC容器只有一个该类型的对象
//        UserController userController = act.getBean(UserController.class);
//        System.out.println(userController.getName());

        // 2.2 方式二:根据名称/id获取
        UserController userController = (UserController) act.getBean("userController");
        System.out.println(userController.getName());
    }
5. 自动装配(理解)

所谓自动装配就是一个组件需要其他组件时,由 IOC 容器负责找到那个需要的组件,并装配进去。

5.1 配置
<!--
        你想让ioc容器创建什么对象,就将那个类配置到bean标签中

        使用依赖注入给UserService属性赋值

        自动装配:autowire属性表示自动装配,就是不需要你去管依赖注入,IOC容器会自动进行依赖注入。它的取值有如下两个
            1. byName:根据要注入的属性名和Bean对象的id的对应关系去注入
            2. byType:表示核心容器会自动在自身容器中查找一个该类型的对象,给成员变量赋值


    -->
	<!--    自动装配-->
    <bean id="userController"
          class="com.atguigu.controller.UserController"
          autowire="byName"
          >

    </bean>

<!--     手动装配-->
<!--    <bean id="userController" class="com.atguigu.controller.UserController">-->
<!--        <property name="userService" ref="userService"></property>-->
<!--    </bean>-->
5.2 测试
@Test
    public void testStart(){
        // 1.创建IOC容器,加载配置文件:类路径中的spring.xml
        ApplicationContext act = new ClassPathXmlApplicationContext("spring.xml");

        // 2.从IOC容器中获取对象
        // 2.1 方式一:根据类型获取 前提是IOC容器只有一个该类型的对象
//        UserController userController = act.getBean(UserController.class);
//        System.out.println(userController.getName());

        // 2.2 方式二:根据名称/id获取
        UserController userController = (UserController) act.getBean("userController");
        System.out.println(userController.getName());
    }

第三节 Bean的作用域和生命周期

1. Bean的作用域
1.1 概念

在Spring中可以通过配置bean标签的scope属性来指定bean的作用域范围,各取值含义参加下表:

取值

含义

创建对象的时机

singleton

在IOC容器中,这个bean的对象始终为单实例

IOC容器初始化时

prototype

这个bean在IOC容器中有多个实例

获取bean时

如果是在WebApplicationContext环境下还会有另外两个作用域(但几乎不用):

取值

含义

request

在一个请求范围内有效

session

在一个会话范围内有效

1.2 配置
<!-- scope属性:取值singleton(默认值),bean在IOC容器中只有一个实例,IOC容器初始化时创建对象 -->
<!-- scope属性:取值prototype,bean在IOC容器中可以有多个实例,getBean()时创建对象 -->
    <bean id="userController"
          class="com.atguigu.controller.UserController"
          autowire="byName"
          scope="singleton"
    >
1.3 测试
@Test
    public void testScope(){
        // 1. 创建IOC容器 加载配置文件:类路径中的spring.xml
        ApplicationContext act = new ClassPathXmlApplicationContext("spring.xml");
        // 2.从IOC容器中获取对象
        UserController userController1 = (UserController) act.getBean("userController");
        UserController userController2 = (UserController) act.getBean("userController");
        // 判断是否是单例
        System.out.println(userController1 == userController2);

    }
2. Bean的生命周期(了解)
2.1 bean的生命周期清单
  • bean对象创建(调用无参构造器/有参构造器)
  • 给bean对象设置属性(依赖注入)
  • bean对象初始化之前操作(由bean的后置处理器前置方法负责)
  • bean对象初始化(需在配置bean时指定初始化方法)
  • bean对象初始化之后操作(由bean的后置处理器后置方法负责)
  • bean对象就绪可以使用
  • bean对象销毁(需在配置bean时指定销毁方法)
  • IOC容器关闭
2.2 指定bean的初始化方法和销毁方法
2.2.1 创建两个方法作为初始化和销毁方法

用com.atguigu.controller.UserController类测试,在类中加俩方法:

package com.atguigu.controller;

import com.atguigu.service.UserService;

public class UserController {
    private UserService userService;

    public String getName(){
        return userService.getName();
    }

    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    public void init(){
        System.out.println("UserController对象进行一些初始化操作....");
    }

    public void destroy(){
        System.out.println("UserController对象销毁之前做一些数据备份工作,日期记录工作...");
    }
}
2.2.2 配置bean时指定初始化和销毁方法
<!--
        bean标签的scope属性表示这个Bean对象的范围:
            1. singleton(默认取值): 单例
            2. prototype: 多例
        bean标签的init-method属性是用于配置这个Bean对象的初始化方法,
        bean标签的destroy-method属性是用于配置这个Bean对象的销毁方法
    -->
    <bean id="userController"
          class="com.atguigu.controller.UserController"
          autowire="byName"
          scope="singleton"
          init-method="init"
          destroy-method="destroy"
    >
2.3 bean的后置处理器
2.3.1 创建后置处理器类
package com.atguigu.processor;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

// 声明一个自定义的bean后置处理器
// 注意:bean后置处理器不是单独针对某一个bean生效,而是针对IOC容器中所有bean都会执行
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(beanName + "初始化之前,做统一处理");
        return null;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(beanName + "初始化之后,做统一处理");
        return null;
    }
}
2.3.2 把bean的后置处理器放入IOC容器
<!--    配置MyBeanPostProcessor-->
    <bean class="com.atguigu.processor.MyBeanPostProcessor"></bean>
2.3.3 执行效果示例

java.util.Date#3abbfa04初始化之前,做统一处理
java.util.Date#3abbfa04初始化之后,做统一处理
stringList初始化之前,做统一处理
stringList初始化之后,做统一处理
stringList初始化之后,做统一处理
stringMap初始化之前,做统一处理
stringMap初始化之后,做统一处理
stringMap初始化之后,做统一处理
druidDataSource初始化之前,做统一处理
druidDataSource初始化之后,做统一处理
userDao初始化之前,做统一处理
userDao初始化之后,做统一处理
userService初始化之前,做统一处理
userService初始化之后,做统一处理
userController初始化之前,做统一处理
UserController对象进行一些初始化操作…
userController初始化之后,做统一处理
UserController对象销毁之前做一些数据备份工作,日期记录工作…

第四节 FactoryBean机制(了解)

1. 简介

FactoryBean是Spring提供的一种整合第三方框架的常用机制。和普通的bean不同,配置一个FactoryBean类型的bean,在获取bean的时候得到的并不是class属性中配置的这个类的对象,而是getObject()方法的返回值。通过这种机制,Spring可以帮我们把复杂组件创建的详细过程和繁琐细节都屏蔽起来,只把最简洁的使用界面展示给我们。

将来我们整合Mybatis时,Spring就是通过FactoryBean机制来帮我们创建SqlSessionFactory对象的。

源码:

/*
 * Copyright 2002-2020 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.beans.factory;
import org.springframework.lang.Nullable;

/**
 * Interface to be implemented by objects used within a {@link BeanFactory} which
 * are themselves factories for individual objects. If a bean implements this
 * interface, it is used as a factory for an object to expose, not directly as a
 * bean instance that will be exposed itself.
 *
 * <p><b>NB: A bean that implements this interface cannot be used as a normal bean.</b>
 * A FactoryBean is defined in a bean style, but the object exposed for bean
 * references ({@link #getObject()}) is always the object that it creates.
 *
 * <p>FactoryBeans can support singletons and prototypes, and can either create
 * objects lazily on demand or eagerly on startup. The {@link SmartFactoryBean}
 * interface allows for exposing more fine-grained behavioral metadata.
 *
 * <p>This interface is heavily used within the framework itself, for example for
 * the AOP {@link org.springframework.aop.framework.ProxyFactoryBean} or the
 * {@link org.springframework.jndi.JndiObjectFactoryBean}. It can be used for
 * custom components as well; however, this is only common for infrastructure code.
 *
 * <p><b>{@code FactoryBean} is a programmatic contract. Implementations are not
 * supposed to rely on annotation-driven injection or other reflective facilities.</b>
 * {@link #getObjectType()} {@link #getObject()} invocations may arrive early in the
 * bootstrap process, even ahead of any post-processor setup. If you need access to
 * other beans, implement {@link BeanFactoryAware} and obtain them programmatically.
 *
 * <p><b>The container is only responsible for managing the lifecycle of the FactoryBean
 * instance, not the lifecycle of the objects created by the FactoryBean.</b> Therefore,
 * a destroy method on an exposed bean object (such as {@link java.io.Closeable#close()}
 * will <i>not</i> be called automatically. Instead, a FactoryBean should implement
 * {@link DisposableBean} and delegate any such close call to the underlying object.
 *
 * <p>Finally, FactoryBean objects participate in the containing BeanFactory's
 * synchronization of bean creation. There is usually no need for internal
 * synchronization other than for purposes of lazy initialization within the
 * FactoryBean itself (or the like).
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @since 08.03.2003
 * @param <T> the bean type
 * @see org.springframework.beans.factory.BeanFactory
 * @see org.springframework.aop.framework.ProxyFactoryBean
 * @see org.springframework.jndi.JndiObjectFactoryBean
 */
public interface FactoryBean<T> {

    /**
     * The name of an attribute that can be
     * {@link org.springframework.core.AttributeAccessor#setAttribute set} on a
     * {@link org.springframework.beans.factory.config.BeanDefinition} so that
     * factory beans can signal their object type when it can't be deduced from
     * the factory bean class.
     * @since 5.2
     */
    String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
    
    
    /**
     * Return an instance (possibly shared or independent) of the object
     * managed by this factory.
     * <p>As with a {@link BeanFactory}, this allows support for both the
     * Singleton and Prototype design pattern.
     * <p>If this FactoryBean is not fully initialized yet at the time of
     * the call (for example because it is involved in a circular reference),
     * throw a corresponding {@link FactoryBeanNotInitializedException}.
     * <p>As of Spring 2.0, FactoryBeans are allowed to return {@code null}
     * objects. The factory will consider this as normal value to be used; it
     * will not throw a FactoryBeanNotInitializedException in this case anymore.
     * FactoryBean implementations are encouraged to throw
     * FactoryBeanNotInitializedException themselves now, as appropriate.
     * @return an instance of the bean (can be {@code null})
     * @throws Exception in case of creation errors
     * @see FactoryBeanNotInitializedException
     */
    @Nullable
    T getObject() throws Exception;
    
    /**
     * Return the type of object that this FactoryBean creates,
     * or {@code null} if not known in advance.
     * <p>This allows one to check for specific types of beans without
     * instantiating objects, for example on autowiring.
     * <p>In the case of implementations that are creating a singleton object,
     * this method should try to avoid singleton creation as far as possible;
     * it should rather estimate the type in advance.
     * For prototypes, returning a meaningful type here is advisable too.
     * <p>This method can be called <i>before</i> this FactoryBean has
     * been fully initialized. It must not rely on state created during
     * initialization; of course, it can still use such state if available.
     * <p><b>NOTE:</b> Autowiring will simply ignore FactoryBeans that return
     * {@code null} here. Therefore it is highly recommended to implement
     * this method properly, using the current state of the FactoryBean.
     * @return the type of object that this FactoryBean creates,
     * or {@code null} if not known at the time of the call
     * @see ListableBeanFactory#getBeansOfType
     */
    @Nullable
    Class<?> getObjectType();
    
    /**
     * Is the object managed by this factory a singleton? That is,
     * will {@link #getObject()} always return the same object
     * (a reference that can be cached)?
     * <p><b>NOTE:</b> If a FactoryBean indicates to hold a singleton object,
     * the object returned from {@code getObject()} might get cached
     * by the owning BeanFactory. Hence, do not return {@code true}
     * unless the FactoryBean always exposes the same reference.
     * <p>The singleton status of the FactoryBean itself will generally
     * be provided by the owning BeanFactory; usually, it has to be
     * defined as singleton there.
     * <p><b>NOTE:</b> This method returning {@code false} does not
     * necessarily indicate that returned objects are independent instances.
     * An implementation of the extended {@link SmartFactoryBean} interface
     * may explicitly indicate independent instances through its
     * {@link SmartFactoryBean#isPrototype()} method. Plain {@link FactoryBean}
     * implementations which do not implement this extended interface are
     * simply assumed to always return independent instances if the
     * {@code isSingleton()} implementation returns {@code false}.
     * <p>The default implementation returns {@code true}, since a
     * {@code FactoryBean} typically manages a singleton instance.
     * @return whether the exposed object is a singleton
     * @see #getObject()
     * @see SmartFactoryBean#isPrototype()
     */
    default boolean isSingleton() {
        return true;
    }
}
2. 实现FactoryBean接口
package com.atguigu.component;

import org.springframework.beans.factory.FactoryBean;

import java.util.Calendar;
import java.util.Date;


// 返回的是Date类型的对象
public class DateFactoryBean implements FactoryBean<Date> {


    @Override
    public Date getObject() throws Exception {
        Calendar calendar = Calendar.getInstance();
        calendar.set(2022, 11, 23);
        return calendar.getTime();
    }

    @Override
    public Class<?> getObjectType() {
        return Date.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}
3. 配置bean
<!--    配置DateFactoryBean-->
    <bean id="date" class="com.atguigu.component.DateFactoryBean"></bean>
4. 测试获取bean
@Test
    public void testFactorBean(){
        // 1.创建IOC容器 加载配置文件:类路径中的spring.xml
        ApplicationContext act = new ClassPathXmlApplicationContext("spring.xml");
        // 2.从IOC容器中获取对象
        Object bean = act.getBean("date");
        System.out.println(bean);
    }

返回的是Date类型数据

Spring 大礼包 spring