# spring6
**Repository Path**: learner_xu/spring6
## Basic Information
- **Project Name**: spring6
- **Description**: 【动力节点】老杜Spring
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 1
- **Created**: 2023-02-27
- **Last Updated**: 2023-08-09
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# Spring6(前言)
## 开闭原则(OCP)
- 什么是OCP?
- OCP是软件七大开发原则当中最基本的一个原则,开闭原则。
对扩展开放
对修改关闭
- OCP原则是最核心的,最基本的,其他六个原则都是为这个原则服务的。
## 依赖倒置原则(DIP)
- 什么是依赖倒置原则?
- 面向接口编程,面向抽象编程,不要面向具体编程。
- 依赖倒置原则的目的?
- 降低程序的耦合度,提高扩展力。
- 什么叫做符合依赖倒置原则?
- “上” 不依赖 “下”,就是符合。
- 什么叫做违背依赖倒置原则?
- “上” 依赖 “下”,就是违背。
- 只要 “下” 一改动,“上” 就受到影响。
## 控制反转(IoC)
- 控制反转:IoC(Inversion of Control)
是一种编程思想,或者说叫做一种新型的设计模式,没有被纳入GoF23种设计模式中。
- 反转的是什么?
1. 不在程序中使用硬编码的方式new对象了。
2. 不在程序中使用硬编码的方式维护对象间的关系了。
## Spring框架
- Spring框架实现了IoC这种思想
Spring框架可以帮我们new对象
Spring框架可以帮我们维护对象间的关系。
- Spring是一个实现IoC思想的容器。
- 控制反转的实现方式有多种,其中重要的叫做:DI(Dependency Injection, 依赖注入)
- 控制反转是思想,依赖注入是这种思想的具体实现。
- 依赖注入DI又包括常见的两种方式:
1. set注入(执行set方法给属性赋值)
2. 构造方法注入(执行构造方法给属性赋值)
- 依赖注入中 “依赖” 是什么意思?“注入” 是什么意思?
- 依赖:对象A和对象B的关系。
- 注入:是一种手段,通过这种手段,让对象A和对象B产生关系。
- 依赖注入:对象A和对象B的关系,靠注入的手段来维护。
注入的方式又分为:set注入、构造方法注入。
## 术语
- OCP(开闭原则):开发思想
- DIP(依赖倒置原则):开发思想
- IoC(控制反转):一种思想,一种新型的设计模式
- DI(依赖注入):IoC的具体实现方式
---
## bean标签中id和class属性
- id:代表对象的唯一标识,不可重复。
- class:用来指定要创建的Java对象的类名(全限定类名)
## Spring是怎么实例化对象的?
- 默认情况是Spring通过反射机制,调用类的==无参构造方法==来实例化对象的。
## Spring底层的IoC是怎么实现的?
- XML解析 + 工厂模式 + 反射机制
- ApplicationContext接口的超级父接口是:BeanFactory(Bean工厂)。
BeanFactory是IoC的顶级接口
Spring的IoC容器底层使用了:工厂模式
---
# Spring6
## Spring对IoC的实现
### IoC 控制反转
- 控制反转是一种思想。
- 控制反转是为了降低程序耦合度,提高程序扩展力,达到OCP原则,达到DIP原则。
- 控制反转,“反转”的是什么?
- 将程序中对象的创建权交出去,交给第三方容器负责。
- 将程序中对象间关系的维护权交出去,交给第三方容器负责。
- 控制反转这种思想是怎么实现的?
- DI 依赖注入
### 依赖注入
- 依赖注入(DI)实现了控制反转的思想。
- Spring是通过依赖注入的方式来完成Bean管理的。
Bean管理:bean对象的创建,以及bean对象的属性的赋值。(或者说是,bean对象之间关系的维护)
- 依赖注入:
- 依赖:对象之间的关联关系。
- 注入:是一种数据传递行为,通过注入的行为来使对象间产生关系。
- 依赖注入常见的两种注入方式:
- set注入
- 构造方法注入
#### set注入
通过反射机制调用==set方法==来给属性赋值,让两个对象间产生关系。
~~~Xml
~~~
#### 构造方法注入
1. 通过参数下标完成构造方法注入
~~~Xml
~~~
2. 通过参数名称完成构造方法注入
~~~Xml
~~~
### 基于XML的自动装配
> Spring的基于XML的自动装配是依赖set方法的。(no、byTye、byName)
#### 根据名称自动装配
- byName(属性值与set方法名一致【方法名 - (set+首字母小写)】)
~~~Java
public class CarService {
private CarDao carDao;
public void setCarDao(CarDao carDao) {
this.carDao = carDao;
}
public void saveCar() {
carDao.insertCar();
}
}
~~~
~~~Xml
~~~
#### 根据类型自动装配
- byType(在可用配置文件中,属性值的类型的实例只能存在一个)
~~~Java
public class CustomerService {
private UserDao userDao;
private VipDao vipDao;
public CustomerService() {
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void setVipDao(VipDao vipDao) {
this.vipDao = vipDao;
}
public void save() {
userDao.insertUser();
vipDao.insertVip();
}
}
~~~
~~~Xml
~~~
### 引入外部属性配置文件
- context:property-placeholder 标签
~~~Xml
~~~
## Bean的作用域
### 单例和多例
- 单例(singleton)
- 默认情况下,Spring的IoC容器创建的bean对象是单例的。
- 默认情况下,bean对象在获取Spring容器的时候就创建完成了。
~~~Java
@Test
public void testScope() {
// 默认情况下,此时bean对象就创建完成了
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring-scope.xml");
}
~~~
- 多例(prototype)
- 每一次执行 getBean() 方法的时候,创建一个新的bean对象。(调用几次,创建几个对象)
~~~Java
@Test
public void testScope() {
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring-scope.xml");
MyBean myBean1 = classPathXmlApplicationContext.getBean("myBean", MyBean.class);
// // edu.hrbu.spring.bean.MyBean@11758f2a
System.out.println(myBean1);
MyBean myBean2 = classPathXmlApplicationContext.getBean("myBean", MyBean.class);
// edu.hrbu.spring.bean.MyBean@e720b7
System.out.println(myBean2);
}
~~~
*注意*:scope属性共有8个选项(不止 singleton、prototype)
## GoF之工厂模式
> GoF 23种设计模式可分为3类:
>
> - 创建型(5):解决对象创建问题
> - 工厂模式属于创建型。
> - 结构型(7):一些类或对象组合在一起的经典结构
> - 行为型(11):解决类或对象之间的交互问题
工厂模式有三种形态:
- 简单工厂模式:不属于GoF,是工厂方法模式的一种特殊实现,又叫做静态工厂方法模式。
- 工厂方法模式:属于GoF
- 抽象工厂模式:属于GoF
### 简单工厂模式
> Spring中的BeanFactory就使用了简单工厂模式。
简单工厂模式中的角色:
- 抽象产品角色
- 具体产品角色
- 工厂类角色
优点:
- 客户端不需要关心对象的创建细节,初步实现了责任的分离。客户端负责“消费”,工厂负责“生产”,实现生产和消费分离。
缺点:
1. 不符合OCP开闭原则,在进行系统扩展时,需要修改工厂类。
2. 工厂类集中了所有产品的创造逻辑,一旦出问题,整个系统瘫痪。
### 工厂方法模式
工厂方法模式中的角色:
- 抽象产品角色
- 具体产品角色
- 抽象工厂角色
- 具体工厂角色
优点:
- 对比简单工厂,符合了OCP开闭原则。
缺点:
- 每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。
### 抽象工厂模式
## Bean的实例化的方式
### 1. 通过无参数构造方法实例化
~~~Xml
~~~
### 2. 通过简单工厂模式实例化
~~~Xml
~~~
~~~Java
public class Star {
}
public class StarFactory {
public static Star get() {
return new Star();
}
}
@Test
public void testStarFactory() {
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring.xml");
Star star = classPathXmlApplicationContext.getBean("starFactory", Star.class);
System.out.println(star);
}
~~~
### 3. 通过factory-bean实例化
~~~Xml
~~~
~~~Java
public class Gun {
}
public class GunFactory {
/**
* 工厂方法模式的具体工厂角色
* 与简单工厂模式的工厂类角色的区别是:
* 简单工厂模式:通过工厂类角色的静态方法来获取bean
* 工厂方法模式:通过具体工厂角色的实例方法来获取bean
*/
public Gun get() {
return new Gun();
}
}
@Test
public void testGunFactory() {
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring.xml");
Gun gun = classPathXmlApplicationContext.getBean("gun", Gun.class);
System.out.println(gun);
}
~~~
### 4. 通过FactoryBean接口实例化
实际上就是对第三种方式的简化。
~~~Xml
~~~
~~~Java
public class Human {
public Human() {
System.out.println("Human的无参构造方法被执行了");
}
}
public class HumanFactoryBean implements FactoryBean {
@Override
public Human getObject() throws Exception {
// 依然是我们自己对bean进行实例化
return new Human();
}
@Override
public Class> getObjectType() {
return null;
}
@Override
public boolean isSingleton() {
return FactoryBean.super.isSingleton();
}
}
@Test
public void testHumanFactoryBean() {
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring.xml");
Human human = classPathXmlApplicationContext.getBean("human", Human.class);
System.out.println(human);
}
~~~
### ==BeanFactory 和 FactoryBean 有什么区别==
https://blog.csdn.net/weixin_56311692/article/details/125782919
### 通过FactoryBean来注入Date
> 通过工厂bean来注入普通bean
~~~Xml
~~~
~~~Java
public class DateFactoryBean implements FactoryBean {
private String dateStr;
public void setDateStr(String dateStr) {
this.dateStr = dateStr;
}
/**
* 通过工厂bean来加工普通bean
* @return
* @throws Exception
*/
@Override
public Date getObject() throws Exception {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
return sdf.parse(dateStr);
}
@Override
public Class> getObjectType() {
return null;
}
}
public class Student {
private Date birth;
public void setBirth(Date birth) {
this.birth = birth;
}
@Override
public String toString() {
return "Student{" +
"birth=" + birth +
'}';
}
}
@Test
public void testDate() {
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring.xml");
Student student = classPathXmlApplicationContext.getBean("student", Student.class);
System.out.println(student);
}
~~~
## Bean的生命周期
> 将bean的生命周期划分为5步的话:
1. 实例化bean(调用无参数构造方法)
2. 给bean属性赋值(调用set方法)
3. 初始化bean(这个方法需要我们自己写、自己配)
4. 使用bean
5. 销毁bean(这个方法需要我们自己写、自己配)
~~~Xml
~~~
~~~Java
public class User {
private String name;
public User() {
System.out.println("生命周期第一步:实例化bean");
}
public void setName(String name) {
System.out.println("生命周期第二步:给bean属性赋值");
this.name = name;
}
public void initUser() {
System.out.println("生命周期第三步:初始化bean");
}
public void destroyUser() {
System.out.println("生命周期第五步:销毁bean");
}
}
@Test
public void testFivePart() {
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring.xml");
User user = classPathXmlApplicationContext.getBean("user", User.class);
System.out.println("生命周期第四步:使用bean " + user);
// 只有正常关闭Spring容器才会执行销毁方法
classPathXmlApplicationContext.close();
}
~~~
> 将bean的生命周期划分为7步的话:
>
> - 比五步新添加的两步在哪里?
> 1. “初始化Bean” 之前
> 2. “初始化Bean” 之后
1. 实例化Bean
2. 给Bean属性赋值
3. **“Bean后置处理器” 的before方法执行**
4. 初始化bean
5. **“Bean后置处理器” 的after方法执行**
6. 使用bean
7. 销毁bean
~~~Xml
~~~
~~~Java
public class UserBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("Bean后置处理器的before方法执行,即将进行初始化");
return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("Bean后置处理器的after方法执行,初始化结束完毕");
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
}
}
~~~
***注意***:在该配置文件中配置Bean后置处理器,会对该配置文件中所有的bean起作用。
> 将bean的生命周期划分为10步的话:
>
> - 比七步新添加的三步在哪里?
> 1. “Bean后置处理器” 的before方法之前
> 2. “Bean后置处理器“ 的before方法之后
> 3. 销毁Bean之前
1. 实例化bean(执行bean的无参构造方法)
2. 给bean属性赋值
3. **检查该bean是否实现了 ==Aware相关的接口==**(BeanClassLoaderAware、BeanFactoryAware、BeanNameAware),如果实现了,则执行这些接口中的方法
执行这些方法时,Spring会给这些方法传递一些参数(类加载器、创造这个bean的BeanFactory、这个bean的name)
4. 执行 ”Bean后置处理器“ 的before方法
5. **检查该bean是否实现了 ==InitializingBean接口==,如果实现了,则执行 afterPropertiesSet方法**
6. 初始化bean
7. 执行 ”Bean后置处理器“ 的after方法
8. 使用bean
9. **检查该bean是否实现了 ==DisposableBean接口==,如果实现了,则执行 destory方法**
10. 销毁bean
~~~Java
public class User implements BeanNameAware,
BeanClassLoaderAware,
BeanFactoryAware,
InitializingBean,
DisposableBean {
private String name;
public User() {
System.out.println("生命周期第一步:实例化bean");
}
public void setName(String name) {
System.out.println("生命周期第二步:给bean属性赋值");
this.name = name;
}
public void initUser() {
System.out.println("生命周期第四步:初始化bean");
}
public void destroyUser() {
System.out.println("生命周期第七步:销毁bean");
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
System.out.println("[User] 类加载器:" + classLoader);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("[User] bean工厂:" + beanFactory);
}
@Override
public void setBeanName(String s) {
System.out.println("[User] bean的name:" + s);
}
@Override
public void destroy() throws Exception {
System.out.println("[User] DisposableBean's destory()");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("[User] InitializingBean's afterPropertiesSet()");
}
}
public class UserBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("生命周期第三步:Bean后置处理器的before方法执行,即将进行初始化");
return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("生命周期第五步:Bean后置处理器的after方法执行,初始化结束完毕");
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
}
}
@Test
public void testFivePart() {
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring.xml");
User user = classPathXmlApplicationContext.getBean("user", User.class);
System.out.println("生命周期第六步:使用bean " + user);
// 只有正常关闭Spring容器才会执行销毁方法
classPathXmlApplicationContext.close();
}
~~~
**注意**:Bean的作用域不同,管理方式不同
- 当Bean的作用域为 singleton时,Spring会完整的管理该bean的生命周期。
- 当Bean的作用域为 prototype时,Spring只会管理该Bean到初始化完毕,一旦客户端获取到Bean,Spring容器就不再对该Bean的生命周期进行管理。
> 将自己创建的对象,交给Spring容器进行管理。
~~~Java
@Test
public void testRegisterBean() {
Human human = new Human();
// edu.hrbu.spring.bean.Human@484b61fc
System.out.println(human);
DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
// 将我们自己创建的human对象,交给Spring容器管理。
defaultListableBeanFactory.registerSingleton("human", human);
// 通过Spring容器获取Human类的实例对象
Human human1 = defaultListableBeanFactory.getBean("human", Human.class);
// edu.hrbu.spring.bean.Human@484b61fc
System.out.println(human1);
}
~~~
## Bean的循环依赖问题
### 1. singleton + setter 模式
singleton + setter 模式下的循环依赖问题,Spring是可以解决的。
Spring在这种模式下是怎么解决循环依赖问题的?
- 解决方案:*实例化对象* 和 *对对象的属性赋值* 分为两个阶段来进行。
- 第一阶段:加载Spring容器时,对Bean进行实例化,只要其中一个Bean实例化之后,直接 “曝光”。(不进行属性赋值,就曝光)
- 第二阶段:Bean曝光后,再对其属性进行赋值。(通过set方法)
### 2. prototype + setter 模式
