# 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 模式 ![1677594189412](images/readme/1677594189412.png)