day01 spring
第一章 Spring的简介
第一节 Spring公司简介
该公司的创建者Rod Johnson被称之为Spring之父,他领导的Spring研发团队下有众多的优秀开发者,Spring公司旗下有非常多的优秀框架。例如:Spring FrameWork、Spring Boot、Spring Cloud、Spring Data、Spring Security等等,几乎涉及了Java开发的每一个领域。 官网地址:https://spring.io/
第二节 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五大功能模块
功能模块 | 功能介绍 |
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哪个类的对象,这样就会导致各个类之间的耦合度非常高
2. IOC方式创建对象
IOC(inversion of control)的中文解释是“控制反转”,对象的使用者不是创建者. 作用是将对象的创建反转给spring框架来创建和管理。控制反转怎么去理解呢。 其实它反转的是什么呢,是对象的创建工作。 举个例子:平常我们在servlet或者service里面创建对象,都是使用new 的方式来直接创建对象,现在有了spring之后,我们就再也不new对象了,而是把对象创建的工作交给spring容器去维护。我们只需要告诉spring容器我们需要什么对象即可
IOC的作用:削减计算机程序的耦合(解除我们代码中的依赖关系)。
第三节 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. 思路
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根路径下,配置文件名字随意
<?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 创建外部属性文件
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类型数据