小黑子—spring:第一章
spring入门1.0
- 一 小黑子对spring基础进行概述
- 1.1 spring导论
- 1.2 传统Javaweb开发困惑及解决方法
- 1.3 三大的思想提出
- 1.3.1 IOC入门案例
- 1.3.2 DI入门案例
- 1.4 框架概念
- 1.5 初识spring
- 1.5.1 Spring Framework
- 1.6 BeanFactory快速入门
- 1.7 ApplicationContext快速入门
- 1.8 BeanFactory与ApplicationContext的关系
- 1.9 BeanFactory的继承体系
- 1.10 ApplicationContext的继承体系
- 二 小黑子基于xml的SpringBean配置详解
- 2.1 Bean的配置概述
- 2.2 beanName和别名配置
- 2.3 Bean的作用范围scope配置
- 2.4 Bean的延迟加载
- 2.5 Bean的初始化方法和销毁方法
- 2.6 InitializingBean方式
- 2.7 实例化Bean的方式-构造方法方式
- 2.7.1 spring bean异常的看法
- 2.7.2 静态工厂方法该方式去实例化Bean
- 2.7.3 实例工厂方法该方式去实例化Bean
- 2.7.4 有参数的静态工厂和实例工厂方法
- 2.7.5 实现FactoryBean规范延迟实例化Bean
- 2.8 Bean的注入
- 2.8.1 Bean的注入方式
- 2.8.2 Bean的注入数据类型
- 2.8.3 自动装配
- 2.9 命名空间的种类
- 2.9.1 beans的profile属性切换环境
- 2.9.2 import标签
- 2.9.3 alias标签
- 2.9.4 自定义命名空间的使用步骤
- 三 小黑子Spring的get方法
- 3.1 spring常用的三种getBean的API
- 四 小黑子配置Spring非自定义的Bean
- 4.1 DruidDatasource
- 4.2 Connection
- 4.3 Date
- 4.4 SqlSessionFactory
- 五 小黑子Bean实例化的基本流程
- 5.1 BeanDefinition
- 5.2 单例池和流程总结
- 六 小黑子Spring的Bean工厂后处理器
- 6.1 入门
- 6.2 注册BeanDefinition
- 6.3 BeanDefinitionRegistryPostProcessor
- 6.4 完善实例化流程图
- 6.5 自定义@Component
- 七 Spring的Bean后处理器
- 7.1 BeanPostProcessor
- 7.2 before和after方法的执行时机
- 7.3 案例-对Bean方法执行日志功能增强
- 7.4 再次完善实例化基本流程图
- 八 小黑子Spring Bean的生命周期
- 8.1 概述
- 8.2 初始化阶段执行步骤
- 8.3 初始化阶段注入属性信息封装
- 8.4 属性注入的三种情况
- 8.5 注入单向对象的代码验证
- 8.6 循环依赖概念及解决方案
- 8.7 三级缓存的设计原理
- 8.8 循环依赖源码流程剖析
- 8.9 Aware接口
- 九 小黑子对Spring IoC整体流程总结
- 十 小黑子进行Spring xml方式整合第三方框架
- 10.1 Mybatis整合Spring实现
- 10.2 Mybatis整合Spring源码解析
- 10.3 加载外部properties文件
- 10.4 自定义空间步骤
- 10.5 自定义命名空间案例
一 小黑子对spring基础进行概述
1.1 spring导论

1.2 传统Javaweb开发困惑及解决方法
问题一:层与层之间紧密耦合在了一起,接口与具体实现紧密耦合在了一起

- 解决思路:程序代码中不要手动new对象,第三方根据要求为程序提供需要的Bean对象

问题二:通用的事务功能耦合在业务代码中,通用的日志功能耦合在业务代码中

- 解决思路:程序代码中不要手动new对象,第三方根据要求为程序提供需要的Bean对象的代理对象

1.3 三大的思想提出
现今代码书写现状:
- 耦合度偏高:在项目上线部署之后,项目要更新代码就要更新,假如说数据层要换个实现,那么业务层也要跟着换个实现对象,然后重新测试、部署、发布,成本高。

- 解决方案:使用对象时,在程序中不要主动使用new产生对象,转换为由外部提供对象,这种对象创建控制器由程序转移到外部——称为IoC

-
IoC思想
-
lnversion of Control,控制反转,强调的是原来在程序中创建Bean的权利反转给第三方。使用对象时,由主动new产生对象转换为由
外部提供对象,此过程对象创建控制权由程序转移到外部,该思想叫做控制反转。 -
spring提供了一个容器——IoC容器(spring容器),用来充当思想中的
外部

-
IoC容器负责对象的创建、初始化等一系列工作,呗创建或被管理的对象在IoC容器中统称为
Bean
-
-
DI思想
- Dependency Injection,
依赖注入,强调的Bean之间关系,这种关系第三方负责去设置。 - 在容器中建立
bean与bean之间的依赖关系的整个过程,被称为依赖注入

- Dependency Injection,
-
AOP思想
- Aspect Oriented Programming,面向切面编程,功能的横向抽取,主要的实现方式就是Proxy


1.3.1 IOC入门案例
IoC入门案例思路分析
- 管理什么?( Service与Dao )
- 如何将被管理的对象告知IoC容器?(配置)
- 被管理的对象交给IoC容器,如何获取到IoC容器?(接口)
- IoC容器得到后,如何从容器中获取bean ?(接口方法)
- 使用Spring导入哪些坐标?( pom.xml )
- 在IDEA下创建maven项目,在resources文件下:导入Spring坐标
- 发现没有spring这种结构选,这就需要导入包

- 发现没有spring这种结构选,这就需要导入包
- 在pom文件里面的导包
之后就出现spring的配置文件

目录:

-
定义spring管理的类(接口)

-
创建spring配置文件,配置对应类作为spring管理的bean

-
初始化IoC容器(spring核心容器/spring容器),通过容器获取bean

1.3.2 DI入门案例
DI入门案例思路分析
- 基于IoC管理bean
- Service中使用new形式创建的Dao对象是否保留?(否)
- Service中需要的Dao对象如何进入到service中?(提供方法)
- Service与Dao间的关系如何描述?(配置)
-
删除使用new的形式创建对象的代码

-
提供依赖对象对应的setter方法

-
配置service与dao之间的关系

1.4 框架概念
- 框架(Framework),是基于基础技术之上,从众多业务中抽取出的通用解决方案;
- 框架是一个半成品,使用框架规定的语法开发可以提高开发效率可以用简单的代码就能完成复杂的基础业务;
- 框架内部使用
在这里插入代码片大量的设计模式、算法、底层代码操作技术,如反射、内省、xml解析、注解解析等; - 框架一般都具备扩展性;
- 有了框架,我们可以将精力尽可能的投入在纯业务开发上而不用去费心技术实现以及一些辅助业务。
Java中常用的框架:
不同语言,不同领域都有属于自己的框架,使用框架开发是作为程序员的最基础的底线。Java语言中的框架,可以分为基础框架和服务框架:
- 基础框架:完成基本业务操作的框架,如MyBatis、Spring、SpringMVC、Struts2、Hibernate等
- 服务框架:特定领域的框架,一般还可以对外提供服务框架,如MQ、ES、Nacos等
1.5 初识spring
spring是一个开源的轻量级Java开发应用框架,可以简化企业级应用开发。Spring解决了开发者在JavaEE开发中遇到的许多常见的问题,提供了功能强大IOC、AOP及Web MVC等功能。是当前企业中Java开发几乎不能缺少的框架之一。Spring的生态及其完善,不管是Spring哪个领域的解决方案都是依附于在Spring Framework基础框架的。
spring官网
Spring发展到今天已经形成了一种开发的生态圈,spring提供了若干给项目,每个项目用于完成特定的功能

Spring框架的历史
- Jsp 默默扛下所有;
- MVC+三层架构分工明确,但开发成本及其高;
- EJB重量级框架出现,走出一个困境,有进入另一个困境;
- Spring春天来到,随之,
- SSH风生水起、称霸武林;
- Spring 稳住江湖大哥位置,SSM开始上位;、
- Spring本着“拿来主义”的思维快速发展,生态不断健全;
- SpringBoot 又一里程碑崛起,把“约定大于配置“思想玩儿的炉火纯青;
- SpringCloud打包了微服务众多解决方案,应对互联网项目更加easy!
-
Spring Boot
使用spring boot技术可以在简化开发的基础上加速开发,它是用来提高开发速度的,会让原先的spring开发变得更加简单,代码写得更少 -
Spring cloud
分布式开发的相关技术
1.5.1 Spring Framework

这是spring里面的第一门技术,也是最早出现的,在整个全家桶的地位即其他的所有技术都是依赖它执行的。它是Spring生态圈中最基础的项目,是其他项目的根基
Spring Framework技术栈图示

- 第一:要学习的内容是Core Container核心容器部分,它是管理对象的。所有的一切都是基于对象的,一定要先学这spring核心部分的内容
- 第二:学数据访问已集成的部分,可以整合mybatis。还有一个东西需要重点学习——Transactions事务,开发效率非常高的事务控制方案
- 第三:学习spring中的第二个核心东西,AOP技术——其也是一种编程的思想,其具体是可以在不惊动原始程序的基础上,给它增强功能。而Aspects也是对AOP思想进行了实现

1.6 BeanFactory快速入门
根据下图,分析一下Spring的BeanFactory的开发步骤:

- 在pom文件中导入Spring的jar包或Maven坐标;
<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.7</version>
</dependency>
- 定义UserService接口及其UserServiceImpl实现类;
public interface UserService {
}
public class UserServiceImpl implements UserService {
}
- 创建beans.xml配置文件,将UserServiceImpl的信息配置到该xml中;
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><!-- 配置UserServiceImpl--><bean id="userService" class="com.itheima.service.impl.UserServiceImpl"></bean></beans>
- 编写测试代码,创建BeanFactory,加载配置文件,获取UserService实例对象
//创建工厂对象
DefaultListableBeanFactory BeanFactory = new DefaultListableBeanFactory();
//创建一个读取器(XML文件)
XmlBeanDefinitionReader Reader = new XmlBeanDefinitionReader(BeanFactory);
//读取配置文件给工厂
Reader.loadBeanDefinitions("beans.xml");
//根据id获取Bean实例对象
UserService userService = (UserService)BeanFactory.getBean("UserService");
能够打印出其对象

实现DI依赖注入
-
在
dao包下定义UserDao接口及其UserDaolmpl实现类; -
修改UserServicelmpl代码,添加一个setUserDao(UserDao userDao)用于接收注入的对象;
public class UserServiceImpl implements UserService {private UserDao userDao;//BeanFactory去调用该反复 从容器中获的userDao设置到此处public void setUserDao(UserDao userDao){System.out.println("BeanFactory去调用该方法获得userDao设置到此处:"+userDao);this.userDao = userDao;}
}
- 修改beans.xml配置文件,在UserDaolmpl的
<bean>中嵌入<property>,其name和UserServicelmpl里面的创建变量的userDao,该属性名是相互对应的,其ref同引入的哪个bean的id相互对应,配置注入;
<bean id="UserService" class="com.Smulll.service.Impl.UserServiceImpl"><property name="userDao" ref="UserDao"/>
</bean>
<bean id="UserDao" class="com.Smulll.Dao.Impl.UserDaoImpl"></bean>
- 修改测试代码,获得UserService时,setUserService方法执行了注入操作
//创建工厂对象
DefaultListableBeanFactory BeanFactory = new DefaultListableBeanFactory();
//创建一个读取器(XML文件)
XmlBeanDefinitionReader Reader = new XmlBeanDefinitionReader(BeanFactory);
//读取配置文件给工厂
Reader.loadBeanDefinitions("beans.xml");
//根据id获取Bean实例对象
UserService userService = (UserService)BeanFactory.getBean("UserService");
1.7 ApplicationContext快速入门
ApplicationContext 称为Spring容器,内部封装了BeanFactory,比BeanFactory功能更丰富更强大,使用ApplicationContext进行开发时,xml配置文件的名称习惯写成applicationContext.xml
//创建ApplicationContext,加载配置文件,实例化容器ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");//根据beanName获得容器中的Bean实例UserService userService = (UserService) applicationContext.getBean("userService");System.out.println(userService);

1.8 BeanFactory与ApplicationContext的关系
- BeanFactory是Spring的早期接口,称为Spring的Bean工厂,
ApplicationContext是后期更高级接口,称之为Spring容器; - ApplicationContext在BeanFactory基础上对功能进行了扩展,例如:监听功能、国际化功能等。
BeanFactory的API更偏向底层,ApplicationContext的API大多数是对这些底层API的封装; - Bean创建的主要逻辑和功能都被封装在BeanFactory中,ApplicationContext不仅继承了BeanFactory,而且ApplicationContext内部还维护着BeanFactory的引用,所以,ApplicationContext与BeanFactory既有继承关系,又有融合关系。
- Bean的初始化时机不同,原始BeanFactory是在首次调用
getBean时才进行Bean的创建,
ApplicationContext则是配置文件加载,容器一创建就将Bean都实例化并初始化好。
ApplicationContext除了继承了BeanFactory外,还继承了ApplicationEventPublisher(事件发布器)、ResouresPatternResolver(资源解析器)、MessageSource(消息资源)等。但是ApplicationContext的核心功能还是BeanFactory。


1.9 BeanFactory的继承体系
BeanFactory是核心接口,项目运行过程中肯定有具体实现参与,这个具体实现就是DefaultListableBeanFactory,而ApplicationContext内部维护的Beanfactory的实现类也是它

1.10 ApplicationContext的继承体系
只在Spring基础环境下,即只导入spring-context坐标时,此时ApplicationContext的继承体系

只在Spring基础环境下,常用的三个ApplicationContext作用如下:
| 实现类 | 功能描述 |
|---|---|
| ClassPathXmlApplicationContext | 加载类路径下的xml配置的ApplicationContext |
| FileSystemXmlApplicationContext | 加载磁盘路径下的xml配置的ApplicationContext |
| AnnotationConfigApplicationContext | 加载注解配置类的ApplicationContext |
如果Spring基础环境中加入了其他组件解决方案,如web层解决方案,即导入spring-web坐标,此时ApplicationContext的继承体系
<dependency><groupId>org.springframework</groupId><artifactId>spring-web</artifactId><version>5.3.7</version>
</dependency>
只在Spring的Web环境下,常用的两个ApplicationContext作用如下:
| 实现类 | 功能描述 |
|---|---|
| XmlWebApplicationContext | web环境下,加载类路径下的xml配置的ApplicationContext |
| AnnotationConfigWebApplicationContext | web环境下,加载磁盘路径下的xml配置的ApplicationContext |
二 小黑子基于xml的SpringBean配置详解
2.1 Bean的配置概述
Spring开发中主要是对Bean的配置,Bean的常用配置:
| Xml配置方式 | 功能描述 |
|---|---|
<bean id="" class=""> | Bean的id和全限定名配置 |
<bean name=""> | 通过name设置Bean的别名,通过别名也能直接获取到Bean实例 |
<bean scope=""> | Bean的作用范围, BeanFactory作为容器时取值singleton和prototype |
<bean lazy-init=""> | Bean的实例化时机,是否延迟加载。BeanFactory作为容器时无效 |
<bean init-method=""> | Bean实例化后自动执行的初始化方法,method指定方法名 |
<bean destroy-method=""> | Bean实例销毁前的方法,method指定方法名 |
<bean autowire="byType"> | 设置自动注入模式,常用的有按照类型byType,按照名字byName |
<bean factory-bean="" factory-method=""/> | 指定哪个工厂Bean的哪个方法完成Bean的创建 |
2.2 beanName和别名配置
一、Bean的基础配置
例如:配置UserDaolmpl由Spring容器负责管理
<bean id="userDao" class="com.itheima.dao .impl.UserDaoImp! " />
此时存储到Spring容器(singleObjects单例池)中的Bean的beanName是userDao,值是UserDaolmpl对象,可以根据beanName获取Bean实例
applicationContext.getBean("userDao");
如果不配置id,则Spring会把当前Bean实例的全限定名作为beanName
applicationContext.getBean("com.itheima.dao.impl.UserDaoImpl");
二、Bean的别名配置
| 类别 | 描述 |
|---|---|
| 名称 | name |
| 类型 | 属性 |
| 所属 | bean标签 |
| 功能 | 定义bean的别名,可定义多个,使用逗号,分号;空格 分隔 |
| 范例 | ![]() |
可以为当前Bean指定多个别名,根据别名也可以获得Bean对象
<bean id="userDao" name="aaa,bbb" class="com.itheima.dao.impl.UserDaoImp1" />
此时多个名称都可以获得UserDaolmpl实例对象
applicationContext.getBean("userDao");
applicationContext.getBean("aaa");
applicationContext.getBean("bbb");
注意:
获取bean无论是通过id还是name获取,如果无法获取到,将抛出异常
NoSuchBeanDefinitionException
NoSuchBeanDefinitionException: No bean named ‘bookServiceImpl’ available
所以抛出这个异常,检查id或者name有没有对上即可
2.3 Bean的作用范围scope配置
默认情况下,单纯的Spring环境Bean的作用范围有两个: Singleton和Prototype
- singleton:单例,默认值,Spring容器创建的时候,就会进行Bean的实例化,并存储到容器内部的单例池中,每次getBean时都是从单例池中获取相同的Bean实例;
- prototype:非单例,Spring容器初始化时不会创建Bean实例当调用getBean时才会实例化Bean,每次getBean都会创建一个新的Bean实例。
为什么bean默认为单例?
- 为适合交给spring容器进行管理的bean
- 表现层对象
- 业务层对象serivce
- 数据层对象dao
- 工具对象utils
- 不适合交给容器进行管理的bean
- 封装实体的域对象
2.4 Bean的延迟加载
当lazy-init设置为true时为延迟加载,也就是当Spring容器创建的时候,不会立即创建Bean实例,等待用到时在创建Bean实例并存储到单例池中去,后续在使用该Bean直接从单例池获取即可,本质上该Bean还是单例的
<bean id="userDao" class=" com.itheima.dao.impl.UserDaoImpl" lazy-init="true"/>
2.5 Bean的初始化方法和销毁方法
- 格式一:

- 格式二:

Bean在被实例化后,可以执行指定的初始化方法完成一些初始化的操作,Bean在销毁之前也可以执行指定的销毁方法完成一些操作,初始化方法名称和销毁方法名称通过
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl" init-method="init" destroy-method="destroy"><property name="userDao" ref="userDao"></property></bean>
package com.itheima.service.impl;import com.itheima.dao.UserDao;
import com.itheima.service.UserService;public class UserServiceImpl implements UserService {public void init(){ System.out.println("初始化方法...");}public void destroy(){ System.out.println("销毁方法...");}private UserDao userDao;//BeanFactory去调用该反复 从容器中获的userDao设置到此处public void setUserDao(UserDao userDao){System.out.println("BeanFactory去调用该方法获得userDao设置到此处:"+userDao);this.userDao = userDao;}
}
- 测试
@Testpublic void BeanTest3(){ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");UserService userService = (UserService) applicationContext.getBean("userService");System.out.println(userService);applicationContext.close();}

bean的消耗和bean销毁的方法的调用是两回事,有的时候容器已经没有了,对应内部维护的哪些bean对象也没有了,这时方法没有被调用的画——是因为spring没有执行到调用销毁方法那个步骤,这个容器就挂掉了
步骤:

- bean销毁时机

2.6 InitializingBean方式
扩展:除此之外,我们还可以通过实现InitializingBean接口,完成一些Bean的初始化操作,如下:
package com.itheima.service.impl;import com.itheima.dao.UserDao;
import com.itheima.service.UserService;
import org.springframework.beans.factory.InitializingBean;public class UserServiceImpl implements UserService, InitializingBean {public void init(){ System.out.println("初始化方法...");}public void destroy(){ System.out.println("销毁方法...");}private UserDao userDao;//BeanFactory去调用该反复 从容器中获的userDao设置到此处public void setUserDao(UserDao userDao){System.out.println("BeanFactory去调用该方法获得userDao设置到此处:"+userDao);this.userDao = userDao;}//执行时机早于init-method配置的方法@Overridepublic void afterPropertiesSet() throws Exception {System.out.println(" afterPropertiesSet方法执行...");}
}

2.7 实例化Bean的方式-构造方法方式
-
提供可访问的构造方法

-
配置

Bean的实例化配置
Spring的实例化方式主要如下两种:
- 构造方式实例化: 底层通过构造方法对Bean进行实例化
- 工厂方式实例化: 底层通过调用自定义的工厂方法对Bean进行实例化
- 测试
@Testpublic void BeanTest3(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");UserService userService = (UserService) applicationContext.getBean("userService");System.out.println(userService);}
构造方式实例化Bean又分为无参构造方法实例化和有参构造方法实例化,Spring中配置的<bean>几乎都是无参构造该方式,此处不在赘述。下面讲解有参构造方法实例化Bean
public class UserServiceImpl implements UserService {public UserServiceImpl(){System.out.println("UserServiceImpl无参构造方法执行");}public UserServiceImpl(String name,int age){System.out.println("UserServiceImpl有参构造方法执行");}private UserDao userDao;//BeanFactory去调用该反复 从容器中获的userDao设置到此处public void setUserDao(UserDao userDao){this.userDao = userDao;}}

有参构造在实例化Bean时,需要参数的注入,通过<constructor-arg>标签,其name为有参构造里面的形参名字,value值随便写。该嵌入在<bean>标签内部提供构造参数,如下:
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl"><constructor-arg name="name" value="haohao"></constructor-arg><constructor-arg name="age" value="18"></constructor-arg><property name="userDao" ref="userDao"></property></bean>

大部分的情况下用的都是无参构造
- 无参构造方法如果不存在,将会抛出异常
BeanCreationException
2.7.1 spring bean异常的看法

报错异常建议从最下面开始看起解决,看不懂再到上一层,逐层递增
大部分的情况下解决最后一个异常就解决问题了
2.7.2 静态工厂方法该方式去实例化Bean
工厂方式实例化Bean,又分为如下三种:
- 静态工厂方法实例化Bean
- 实例工厂方法实例化Bean
- 实现FactoryBean规范延迟实例化Bean
使用该静态工厂方法的优点:
- 可以执行一些创建对象之前其他的逻辑操作
- 可以生成一些其他工具类或jar包使用静态方法创造的对象
public class MyBeanFactory1 {public static UserDao userDao(){//Bean创建之前可以进行一些其他业务逻辑操作return new UserDaoImpl();}
}
- bean标签中有了factory-method,就不是把MyBeanFactory1当做一个对象,而是找它内部的这个userDao方法,之后把该方法的返回值当做对象。再以你指定的id作为nam存储到spring容器当中
<bean id="userDao1" class="com.itheima.factory.MyBeanFactory1" factory-method="userDao"></bean>
- 测试
@Testpublic void BeanTest3(){ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");Object userDao1 = applicationContext.getBean("userDao1");System.out.println(userDao1);}

2.7.3 实例工厂方法该方式去实例化Bean
区别静态工厂就是不加static
使用实例工厂方法的优点:
- 可以执行一些创建对象之前其他的逻辑操作
- 可以生成一些其他工具类或jar包通过方法来创造的对象

public class MyBeanFactory2 {public UserDao userDao(){//Bean创建之前可以进行一些其他业务逻辑操作return new UserDaoImpl();}
}
实例工厂就要调用对象
<!--配置实例工厂方法-->
<!--先设置工厂对象--><bean id="myBeanFactory2" class="com.itheima.factory.MyBeanFactory2"></bean>
<!--在设置工厂方法--><bean id="userDao2" factory-bean="myBeanFactory2" factory-method="userDao"></bean>
如果是静态工厂就不用传递对象,直接调用方法即可
- 测试
@Testpublic void BeanTest3(){ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");Object userDao2 = applicationContext.getBean("userDao2");System.out.println(userDao2);}

2.7.4 有参数的静态工厂和实例工厂方法
<bean id="factoryUser" factory-bean="factoryDemo2" factory-method="CreateUserService"><constructor-arg name="name" value="zhangsan"/><constructor-arg name="age" value="18"/>
</bean>
constructor-arg该参数是工厂方法构造Bean方法的参数
2.7.5 实现FactoryBean规范延迟实例化Bean

使工厂类继承FactoryBean<UserDao>
通过getObject()方法返回对象
public class MyBeanFactory3 implements FactoryBean<UserDao> {@Overridepublic UserDao getObject() throws Exception {return new UserDaoImpl();}@Overridepublic Class<?> getObjectType() {return UserDao.class;}
}
该bean获取的不是MyBeanFactory3,而是MyBeanFactory3内部的getObject()返回的这个对象
<bean id="userDao3" class="com.itheima.factory.MyBeanFactory3"></bean>
该方法有延迟功能,只有调用类时,才会将getObject()方法返回的类储存到FactoryBean缓存当中
- 测试
@Testpublic void BeanTest3(){ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");Object userDao3 = applicationContext.getBean("userDao3");System.out.println(userDao3);}

2.8 Bean的注入
2.8.1 Bean的注入方式
Bean的依赖注入又两种方式:
| 注入方式 | 配置方式 |
|---|---|
| 通过Bean的set方法(又叫setter)注入 | <property name="userDao" ref="userDao" /> <property name="userDao" value="haohao"/> |
| 通过构造Bean的方法进行注入 | <constructor-arg name="name" ref="userDao"/> <constructor-arg name="name" value="haohao"/> |
其中,ref是reference的缩写形式,翻译为:涉及,参考的意思,用于引用其他Bean的id。value用于注入普通属性值。

2.8.2 Bean的注入数据类型
依赖注入的数据类型有如下三种:
-
普通数据类型,例如: String、int、 boolean等,通过value属性指定

-
引用数据类型,例如: UserDaolmpl、DataSource等,通过ref属性指定。
-
集合数据类型,例如: List、Map、Properties等。
注:property和constructor-arg的name属性和参数的名字必须要一样
public class UserServiceImpl implements UserService {//注入Listprivate List<String> stringList;public void setStringList(List<String> stringList) {this.stringList = stringList;}private List<UserDao> userDaoList;public void setUserDaoList(List<UserDao> userDaoList) {this.userDaoList = userDaoList;}private Set<String> strSet;public void setStrSet(Set<String> strSet){this.strSet = strSet;}private Set<UserDao> userDaoSet;public void setUserDaoSet(Set<UserDao> userDaoSet){this.userDaoSet = userDaoSet;}private Map<String,UserDao> map;public void setMap(Map<String, UserDao> map) {this.map = map;}private Properties properties;public void setProperties(Properties properties) {this.properties = properties;}public void show(){System.out.println(stringList);System.out.println(userDaoList);System.out.println(userDaoSet);System.out.println(strSet);System.out.println(map);System.out.println(properties);}private UserDao userDao;//BeanFactory去调用该反复 从容器中获的userDao设置到此处public void setUserDao(UserDao userDao){this.userDao = userDao;}}
- bean容器
<property name="stringList"><list><value>aaa</value><value>bbb</value><value>ccc</value></list></property><property name="userDaoList"><list><ref bean="userDao1"></ref><ref bean="userDao2"></ref><ref bean="userDao3"></ref></list></property><property name="strSet"><set><value>xxxx</value><value>yyyy</value></set></property><property name="userDaoSet"><set><ref bean="userDao1"></ref><ref bean="userDao2"></ref><ref bean="userDao3"></ref></set></property><property name="map"><map><entry key="d1" value-ref="userDao1"></entry><entry key="d2" value-ref="userDao2"></entry></map></property><property name="properties"><props><prop key="p1">pp1</prop><prop key="p2">pp2</prop></props></property>
</bean><bean id="userDao1" class="com.itheima.dao.impl.UserDaoImpl"></bean><bean id="userDao2" class="com.itheima.dao.impl.UserDaoImpl"></bean><bean id="userDao3" class="com.itheima.dao.impl.UserDaoImpl"></bean>
- 接口的方法创建
public interface UserService {public void show();
}
- 测试
@Testpublic void BeanTest3(){ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");UserService userService = (UserService) applicationContext.getBean("userService");userService.show();}

2.8.3 自动装配
扩展:自动装配方式
如果被注入的属性类型是Bean引用的话,那么可以在<bean>标签中使用autowire属性去配置自动注入方式,属
性值有两个:
- byName:通过属性名自动装配,即去匹配
setXxx与id="xxx" (name="xxx")是否一致;
public void show(){System.out.println(userDao);}private UserDao userDao;public void setUserDao(UserDao userDao){this.userDao = userDao;}
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl" autowire="byName"></bean><bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"></bean>
- byType:通过Bean的类型从容器中匹配,匹配出多个相同Bean类型时,报错。

2.9 命名空间的种类
Spring的xml标签大体上分为两类,一种是默认标签,一种是自定义标签
- 默认标签:就是不用额外导入其他命名空间约束的标签,例如
<bean>标签 - 自定义标签:就是需要额外引入其他命名空间约束,并通过前缀引用的标签,例如
<context:property-placeholder/>标签
Spring的默认标签用到的是Spring的默认命名空间
<?xml version="1.0"encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http: //www.w3.org/2001/xMschema-instance"xsi:schemalocation="http: //www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
该命名空间约束下的默认标签如下:
| 标签 | 作用 |
|---|---|
<beans> | 一般作为xml配置根标签,其他标签都是该标签的子标 |
<bean> | Bean的配置标签,上面已经详解了,此处不再阐述 |
<import> | 外部资源导入标签 |
<alias> | 指定Bean的别名标签,使用较少 |
2.9.1 beans的profile属性切换环境
<beans>标签,除了经常用的做为根标签外,还可以嵌套在根标签内,使用profile属性切换开发环境
<!--配置测试环境下,需要加载的Bean实例-->
<beans profile="test"></beans>
<!--配置开发环境下,需要加载的Bean实例-->
<beans profile="dev"></beans>
可以使用以下两种方式指定被激活的环境:
- 使用命令行动态参数,虚拟机参数位置加载
-Dspring.profiles.active=test - 使用代码的方式设置环境变量
System.setProperty("spring.profiles.active","test")
案例:
<beans profile="dev"><bean id="userService1" class="com.itheima.service.impl.UserServiceImpl"></bean></beans><beans profile="test"><bean id="userDao1" class="com.itheima.dao.impl.UserDaoImpl"></bean></beans>
@Testpublic void BeanTest4(){//指定环境System.setProperty("spring.profiles.active","test");//System.setProperty("spring.profiles.active","dev");ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
// UserService userService1 = (UserService) applicationContext.getBean("userService1");UserDao userDao1 = (UserDao) applicationContext.getBean("userDao1");
// System.out.println(userService1);System.out.println(userDao1);}

2.9.2 import标签
<import>标签,用于导入其他配置文件,项目变大后,就会导致一个配置文件内容过多,可以将一个配置文件根据业务某块进行拆分,拆分后,最终通过<import>标签导入到一个主配置文件中,项目加载主配置文件就连同<import>导入的文件一并加载了
<!--导入用户模块配置文件--><import resource="classpath:applicationContext-user.xml"></import><import resource="classpath:applicationContext-orders.xml"></import>
案例:
- user的bean
<?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="userService" class="com.itheima.service.impl.UserServiceImpl"></bean>
</beans>
- orders的bean
<?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="userDao" class="com.itheima.dao.impl.UserDaoImpl"></bean>
</beans>
- 测试
@Testpublic void BeanTest4(){ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");UserService userService = (UserService) applicationContext.getBean("userService");UserDao userDao = (UserDao) applicationContext.getBean("userDao");System.out.println(userService);System.out.println(userDao);}

2.9.3 alias标签
<alias>标签是为某个Bean添加别名,与在<bean>标签上使用name属性添加别名的方式一样,我们为UserServicelmpl指定四个别名: aaa、bbb、xxx、yyy
格式:<alias alias="自定义名字" name="bean的id">
<!--配置UserService-->
<bean id="userService" name="aaa,bbb" class="com.itheima.service.imp1.UserserviceImp1"><property name="userDao" ref="userDao"/>
</bean>
<!--指定别名-->
<alias name="userService" alias="xxx"/>
<alias name="userDao" alias="yyy"/>
测试
@Testpublic void BeanTest4(){ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");UserService userService = (UserService) applicationContext.getBean("xxx");
// UserDao userDao = (UserDao) applicationContext.getBean("userDao");System.out.println(userService);
// System.out.println(userDao);}


2.9.4 自定义命名空间的使用步骤
Spring的自定义标签需要引入外部的命名空间,并为外部的命名空间指定前缀,使用<前缀:标签>形式的标签,称之为自定义标签,自定义标签的解析流程也是Spring xml扩展点方式之一
-
第一步:先再pom文件里面引入坐标
-
第二步:去找或者抄对应的这个命名空间xmlns,例如:
xmlns:mvc="http://www.springframework.org/schema/mvc" -
第三步:去找或者抄对应的xsi这个schema的位置,例如:
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd

-
第四步:通过前缀来引入自定义标签
<!--默认标签-->
<bean id="userDao" class="com.itheima. dao .imp1.userDaoImpl" />
<!--自定义标签-->
<mvc:annotation-driven></mvc:annotation-driven>
三 小黑子Spring的get方法
3.1 spring常用的三种getBean的API
| 方法定义 | 返回值和参数 |
|---|---|
| Object getBean (String beanName) | 根据beanName从容器中获取Bean实例,要求容器中Bean唯一,返回值为Object,需要强转 |
| T getBean (Class type) | 根据Class类型从容器中获取Bean实例,要求容器中Bean类型唯一,返回值为Class类型实例,无需强转 |
| T getBean (String beanName,Class type) | 根据beanName从容器中获得Bean实例,返回值为Class类型实例,无需强转 |
//根据beanName获取容器中的Bean实例,需要手动强转
UserService userService = (UserService)applicationContext.getBean("userService");
//根据Bean类型去容器中匹配对应的Bean实例,如存在多个匹配Bean则报错
UserService userService2 = applicationContext.getBean(UserService.class);
//根据beanName获取容器中的Bean实例,指定Bean的Type类型
UserService userService3 = applicationContext.getBean("userService",UserService.class) ;
四 小黑子配置Spring非自定义的Bean
以上在xml中配置的Bean都是自己定义的,例如: UserDaolmpl,UserServicelmpl。但是,在实际开发中有些功能类并不是我们自己定义的,而是使用的第三方jar包中的,那么,这些Bean要想让Spring进行管理,也需要对其进行配置
配置非自定义的Bean需要考虑如下两个问题:
- 被配置的Bean的实例化方式是什么?无参构造、有参构造、静态工厂方式还是实例化工厂方式;
- 被配置的Bean是否需要注入必要属性
4.1 DruidDatasource
- 配置Druid数据源交由Spring管理
导入Druid坐标
<!--mysql驱动-->
<dependency><groupId>mysql</groupid><artifactId>mysql-connector-java</artifactId><version>5.1.49</version>
</dependency>
<!-- druid数据源-->
<dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.23</version>
</dependency>
- 配置数据源信息
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property><property name="url" value="jdbc:mysql://localhost:3306/mybatis"></property><property name="username" value="你的数据库名字"></property><property name="password" value="你的数据库密码"></property>
</bean>
- 测试
@Testpublic void BeanTest5(){ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");Object dataSource = applicationContext.getBean("dataSource");System.out.println(dataSource);}

4.2 Connection
Connection的产生是通过DriverManager的静态方法getConnection获取的,所以我们要用静态工厂方式配置
<bean id="clazz" class="java.lang.Class" factory-method="forName"><constructor-arg name="className" value="com.mysql.jdbc.Driver" /></bean><bean id="connection" class="java.sql.DriverManager" factory-method="getConnection" scope="prototype"><constructor-arg name="url" value="jdbc:mysql:///mybatis" /><constructor-arg name="user" value="root"/><constructor-arg name="password" value="root"/></bean>
- 测试
@Testpublic void BeanTest5(){ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");Object connection = applicationContext.getBean("connection");System.out.println(connection);}

4.3 Date
产生一个指定日期格式的对象,原始代码如下
String currentTimeStr = "2023-08-27 07:20:00" ;
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = simpleDateFormat.parse(currentTimeStr);
可以看成是实例工厂方式,使用Spring配置方式产生Date实例
<!--配置日期对象-->
<bean id="simpleDateFormat" class="java.text.SimpleDateFormat"><constructor-arg name="pattern" value="yyyy-MM-dd HH:mm:ss" />
</bean>
<bean id="date" factory-bean="simpleDateFormat" factory-method="parse"><constructor-arg name="source" value="2023-08-27 07:20:00"/>
</bean>
- 测试
@Testpublic void BeanTest5(){ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");Object date = applicationContext.getBean("date");System.out.println(date);}

4.4 SqlSessionFactory
mybatis官方文档
配置SqlSessionFactory交由Spring管理
导入Mybatis相关坐标
<dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.10</version>
</dependency>
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.30</version>
</dependency>
- 配置bean参考的原代码
//静态工厂方法方式InputStream is = Resources.getResourceAsStream("mybatis-config.xml");//无参构造实例化SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();//实例工厂方法SqlSessionFactory sqlSessionFactory = builder.build(is);
<!-- class路径是复制对象的路径 id可以是原本命名的id-->
<!-- 工厂对象--><!--静态工厂--><bean id="is" class="org.apache.ibatis.io.Resources" factory-method="getResourceAsStream"><constructor-arg name="resource" value="mybatis-config.xml">
<!-- 该name使用ctrl+点击左键查看getResourceAsStream方法的形参可知--></constructor-arg></bean>
<!-- 无参构造实例化--><bean id="builder" class="org.apache.ibatis.session.SqlSessionFactoryBuilder"></bean><!--在设置实例工厂方法--><!--实例工厂--><bean id="sqlSessionFactory" factory-bean="builder" factory-method="build"><constructor-arg name="inputStream" ref="is"></constructor-arg></bean>
- 测试
@Testpublic void BeanTest5() throws {ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");SqlSessionFactory sqlSessionFactory = (SqlSessionFactory) applicationContext.getBean("sqlSessionFactory");System.out.println(sqlSessionFactory);}

在拿到一个bean时去配置让spring去管,要分清它是属于哪种方式产生的,是属于无参构造,还是静态工厂方法,还是实例工厂方法。确定之后,再看需不需要参数
五 小黑子Bean实例化的基本流程
5.1 BeanDefinition
- Spring容器在进行初始化时,会将xml配置的
<bean>的信息封装成一个BeanDefinition对象; - 所有的
BeanDefinition存储到一个名为beanDefinitionMap的Map集合中去; - Spring框架在对该Map进行遍历,使用反射创建Bean实例对象,创建好的Bean对象存储在一个名为
singletonObjects的Map集合中; - 当调用
getBean方法时则最终从该Map集合中取出Bean实例对象返回。

DefaultListableBeanFactory对象内部维护着一个Map用于存储封装好的BeanDefinitionMap
public class DefaultListableBeanFactory extends ... implements ... {//存储<bean>标签对应的BeanDefinition对象//key:是Bean的beanName,value:是Bean定义对象BeanDefinitionprivate final Map<String,BeanDefinition> beanDefinitionMap;
}
Spring框架会取出beanDefinitionMap中的每个BeanDefinition信息,反射构造方法或调用指定的工厂方法生成Bean实例对象,所以只要将BeanDefinition注册到beanDefinitionMap这个Map中,Spring就会进行对应的Bean的实例化操作
5.2 单例池和流程总结
Bean实例及单例池singletonObjects,beanDefinitionMap中的BeanDefinition会被转化成对应的Bean实例对象,存储到单例池singletonObjects中去,在DefaultListableBeanFactory的上四级父类
DefaultSingletonBeanRegistry中,维护着singletonObjects
源码如下:
public class DefaultSingletonBeanRegistry extends ... implements ... {
//存储Bean实例的单例池
key :是Bean的beanName,value:是Bean的实例对象
private final Map<String,object> singleton0bjects = new ConcurrentHashMap(256)
基本流程:
- 加载xml配置文件,解析获取配置中的每个
<bean>的信息,封装成一个个的BeanDefinition对象;- 将
BeanDefinition存储在一个名为beanDefinitionMap的Map<String,BeanDefinition>中;- ApplicationContext底层遍历beanDefinitionMap,创建Bean实例对象;
- 创建好的Bean实例对象,被存储到一个名为
singletonObjects的Map<String,Object>中;- 当执行
applicationContext.getBean(beanName)时,从singletonObjects去匹配Bean实例返回。
六 小黑子Spring的Bean工厂后处理器
6.1 入门
Spring的后处理器是Spring对外开发的重要扩展点,允许我们介入到Bean的整个实例化流程中来,以达到动态注册BeanDefinition,动态修改BeanDefinition,以及动态修改Bean的作用。Spring主要有两种后处理器:
BeanFactoryPostProcessor: Bean工厂后处理器,在BeanDefinitionMap填充完毕,Bean实例化之前执行;BeanPostProcessor: Bean后处理器,一般在每个Bean实例化之后,填充到单例池singletonObjects之前执行。
Bean工厂后处理器——BeanFactoryPostProcessor
BeanFactoryPostProcessor是一个接口规范,实现了该接口的类只要交由Spring容器管理的话,那么Spring就会回调该接口的方法,用于对BeanDefinition注册和修改的功能。
BeanFactoryPostProcessor定义如下:
public interface BeanFactoryPostProcessor {void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory);
}
6.2 注册BeanDefinition
使用这种方法,可以不用在spring容器内再创建一个类的<bean>标签

public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory){System.out.println("beandefinitionMap填充完毕后回调该方法");//1.注册一个beandefinition 创建一个RootBeanDefinition()对象BeanDefinition BeanDefinition = new RootBeanDefinition();BeanDefinition.setBeanClassName("com.itheima.dao.impl.PersonDaoImpl");//2.将beanFactory强转成DefaultListableBeanFactory类型DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) beanFactory;defaultListableBeanFactory.registerBeanDefinition("personDao",BeanDefinition);}
}
@Testpublic void BeanTest5() throws IOException {ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");PersonDao personDao = applicationContext.getBean(PersonDao.class);System.out.println(personDao);}
<bean class="com.itheima.processor.MyBeanFactoryPostProcessor"></bean>

6.3 BeanDefinitionRegistryPostProcessor
Spring提供了一个BeanFactoryPostProcessor的子接口BeanDefinitionRegistryPostProcessor专门用于注册BeanDefinition操作
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {System.out.println("MyBeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法");//创建一个RootBeanDefinition()对象BeanDefinition beanDefinition = new RootBeanDefinition();beanDefinition.setBeanClassName("com.itheima.dao.impl.PersonDaoImpl");//不需要强转就可以创建一个BeanbeanDefinitionRegistry.registerBeanDefinition("personDao",beanDefinition);}@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {System.out.println("MyBeanDefinitionRegistryPostProcessor的postProcessBeanFactory方法");}
}
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory){System.out.println("MyBeanFactoryPostProcessor的postProcessBeanFactory方法");
// //1.注册一个beandefinition 创建一个RootBeanDefinition()对象
// BeanDefinition BeanDefinition = new RootBeanDefinition();
// BeanDefinition.setBeanClassName("com.itheima.dao.impl.PersonDaoImpl");
// //2.将beanFactory强转成DefaultListableBeanFactory类型
// DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) beanFactory;
// defaultListableBeanFactory.registerBeanDefinition("personDao",BeanDefinition);}
}
<bean class="com.itheima.processor.MyBeanFactoryPostProcessor"></bean><bean class="com.itheima.processor.MyBeanDefinitionRegistryPostProcessor"></bean>
- 测试

可见执行顺序
6.4 完善实例化流程图
BeanFactoryPostProcessor在SpringBean的实例化过程中的体现

记图非常重要
6.5 自定义@Component
要求:
- 自定义@MyComponent注解,使用在类上;
- 使用资料中提供好的包扫描器工具BaseClassScanUtils完成指定包的类扫描;
- 自定义BeanFactoryPostProcessor完成注解@MyComponent的解析,解析后最终被Spring管理。
utils包下BaseClassScanUtils
package com.itheima.utils;import com.itheima.anno.MyComponent;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.ClassUtils;import java.util.HashMap;
import java.util.Map;public class BaseClassScanUtils {//设置资源规则private static final String RESOURCE_PATTERN = "/**/*.class";public static Map<String, Class> scanMyComponentAnnotation(String basePackage) {//创建容器存储使用了指定注解的Bean字节码对象Map<String, Class> annotationClassMap = new HashMap<String, Class>();//spring工具类,可以获取指定路径下的全部类ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();try {String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +ClassUtils.convertClassNameToResourcePath(basePackage) + RESOURCE_PATTERN;Resource[] resources = resourcePatternResolver.getResources(pattern);//MetadataReader 的工厂类MetadataReaderFactory refractory = new CachingMetadataReaderFactory(resourcePatternResolver);for (Resource resource : resources) {//用于读取类信息MetadataReader reader = refractory.getMetadataReader(resource);//扫描到的classString classname = reader.getClassMetadata().getClassName();Class<?> clazz = Class.forName(classname);//判断是否属于指定的注解类型if(clazz.isAnnotationPresent(MyComponent.class)){//获得注解对象MyComponent annotation = clazz.getAnnotation(MyComponent.class);//获得属value属性值String beanName = annotation.value();//判断是否为""if(beanName!=null&&!beanName.equals("")){//存储到Map中去annotationClassMap.put(beanName,clazz);continue;}//如果没有为"",那就把当前类的类名作为beanNameannotationClassMap.put(clazz.getSimpleName(),clazz);}}} catch (Exception exception) {}return annotationClassMap;}public static void main(String[] args) {Map<String, Class> stringClassMap = scanMyComponentAnnotation("com.itheima");System.out.println(stringClassMap);}
}
anno包下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyComponent {String value();
}
beans包下
@MyComponent("otherBean")
public class OtherBean {
}
bean容器
<bean class="com.itheima.processor.MyBeanDefinitionRegistryPostProcessor"></bean>
processor包下的MyComponentBeanFactoryPostProcessor
public class MyComponentBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {//通过扫描工具去扫描指定包及其子包下的所有雷,手机使用@Mycomponent的注解的类Map<String, Class> myComponentAnnotationMap = BaseClassScanUtils.scanMyComponentAnnotation("com.itheima");//遍历Map 组装BeanDefinition进行注册myComponentAnnotationMap.forEach((beanName,clazz)->{//获得beanClassNameString beanClassName = clazz.getName();//com.itheima.beans.OtherBean//创建BeanDefinitionBeanDefinition beanDefinition = new RootBeanDefinition();beanDefinition.setBeanClassName(beanClassName);//注册beanDefinitionRegistry.registerBeanDefinition(beanClassName,beanDefinition);});}@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {}
}
- 测试
@Testpublic void BeanTest5() throws IOException {ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");OtherBean otherBean = applicationContext.getBean(OtherBean.class);System.out.println(otherBean);}

七 Spring的Bean后处理器
7.1 BeanPostProcessor
Bean被实例化后,到最终缓存到名为singletonObjects单例池之前,中间会经过Bean的初始化过程,例如:属性的填充、初始方法init的执行等,其中有一个对外进行扩展的点BeanPostProcessor,我们称为Bean后处理。跟上面的Bean工厂后处理器相似,它也是一个接口,实现了该接口并被容器管理的BeanPostProcessor,会在流程节点上被Spring自动调用。
public class MyBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println(beanName+":postProcessBeforeInitialization");return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println(beanName+":postProcessAfterInitialization");return bean;}
}
public class UserDaoImpl implements UserDao {private String username;public void setUsername(String username) {this.username = username;}public UserDaoImpl(){System.out.println("userDao实例化");}
}
<!-- <bean id="userService" class="com.itheima.service.impl.UserServiceImpl"></bean>--><bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"></bean><!-- <bean class="com.itheima.processor.MyBeanFactoryPostProcessor"></bean>-->
<!-- <bean class="com.itheima.processor.MyBeanDefinitionRegistryPostProcessor"></bean>--><!-- <bean class="com.itheima.processor.MyComponentBeanFactoryPostProcessor"></bean>--><bean class="com.itheima.processor.MyBeanPostProcessor"></bean>
- 测试
@Testpublic void BeanTest5() throws IOException {ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");}

postProcessBeforeInitialization方法和postProcessAfterInitialization方法需要进行手动创建,接口中实现的是null返回值的方法- 两者方法在bena创建之后执行
7.2 before和after方法的执行时机
- 先执行bean的构造方法
- 执行before方法
- 执行InitializingBean接口中的afterPropertiesSet()方法
- 执行在xml文件中设置的Bean的init-method方法
- 执行after方法
public class UserDaoImpl implements UserDao, InitializingBean {private String username;public void setUsername(String username) {this.username = username;}public UserDaoImpl(){System.out.println("userDao实例化");}public void init(){System.out.println("init初始化方法执行。。。");}@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("属性在设置之后执行。。。");}
}
<!-- <bean id="userService" class="com.itheima.service.impl.UserServiceImpl"></bean>--><bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl" init-method="init"></bean><!-- <bean class="com.itheima.processor.MyBeanFactoryPostProcessor"></bean>-->
<!-- <bean class="com.itheima.processor.MyBeanDefinitionRegistryPostProcessor"></bean>--><!-- <bean class="com.itheima.processor.MyComponentBeanFactoryPostProcessor"></bean>--><bean class="com.itheima.processor.MyBeanPostProcessor"></bean>
- 测试
@Testpublic void BeanTest5() throws IOException {ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");Object userDao = applicationContext.getBean("userDao");}

7.3 案例-对Bean方法执行日志功能增强

- Processor类
public class TimeLogBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {//使用动态代理对目标Bean进行增强,返回proxy对象,进而存储到singletonObjects中Object beanProxy = Proxy.newProxyInstance(bean.getClass().getClassLoader(),bean.getClass().getInterfaces(),(proxy,method,args)->{//1.输出开始时间System.out.println("方法:"+method.getName()+"-开始时间:"+new Date());//2.执行目标方法Object result = method.invoke(bean, args);//3.输出结束时间System.out.println("方法:"+method.getName()+"-结束时间:"+new Date());return result;});return beanProxy;}
}
- 接口实现类
public interface UserDao
{void show();
}
public class UserDaoImpl implements UserDao{@Overridepublic void show() {try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("show");}
}
- xml文件
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"></bean><bean class="com.itheima.processor.TimeLogBeanPostProcessor"></bean>
- 测试
@Testpublic void BeanTest5() throws IOException {ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");UserDao userDao = (UserDao) applicationContext.getBean("userDao");userDao.show();}

7.4 再次完善实例化基本流程图
BeanPostProcessor在 SpringBean的实例化过程中的体现

八 小黑子Spring Bean的生命周期
8.1 概述
Spring Bean的生命周期是从Bean 实例化之后,即通过反射创建出对象之后,导Bean成为一个完整对象,最终储存到单例池中,这个过程被称为SpringBean的生命周期。Spring Bean的生命周期大体上分为三个阶段:
- Bean的实例化阶段: Spring框架会取出
BeanDefinition的信息进行判断当前Bean的范围是否是singleton的,是否不是延迟加载的,是否不是FactoryBean等,最终将一个普通的singleton的Bean通过反射进行实例化 - Bean的初始化阶段∶Beane创建之后还仅仅是个”半成品“,还需要对Bean实例的属性进行填充、执行一些
Aware接口方法、执行BeanPostProcessor方法、执行InitializingBean接口的初始化方法、执行自定义初始化init方法等。该阶段是Spring最具技术含量和复杂度的阶段,Aop增强功能,后面要学习的Spring的注解功能等、spring高频面试题Bean的循环引用问题都是在这个阶段体现的 - Bean的完成阶段:经过初始化阶段,Bean就成为了一个完整的Spring Bean,被存储到单例池
singletonObjects中去了,即完成了Spring Bean的整个生命周期。
8.2 初始化阶段执行步骤
由于Bean的初始化阶段的步骤比较复杂,所以着重研究Bean的初始化阶段
Spring Bean的初始化过程涉及如下几个过程:
-
Bean实例的属性填充
-
Aware接口属性注入 -
BeanPostProcessor的before()方法回调 -
lnitializingBean接口的初始化方法回调 -
自定义初始化方法init回调
-
BeanPostProcessor的after()方法回调
8.3 初始化阶段注入属性信息封装
BeanDefinition 中有对当前Bean实体的注入信息通过属性propertyValues进行了存储
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl"><property name="userDao" ref="userDao"></property><property name="username" value="haohao"></property></bean><bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"></bean>
例如UserService的属性信息:
Bean实例的属性填充

8.4 属性注入的三种情况
Spring在进行属性注入时,会分为如下几种情况:
- 注入普通属性,String、int或存储基本类型的集合时,直接通过set方法的反射设置进去;
- 注入单向对象引用属性时,从容器中getBean获取后通过set方法反射设置进去,如果容器中没有,则先创建被注入对象Bean实例(完成整个生命周期)后,在进行注入操作
- 注入双向对象引用属性时,就比较复杂了,涉及了循环引用(循环依赖)问题,下面会详细阐述解决方案。
8.5 注入单向对象的代码验证
public class UserServiceImpl implements UserService {public UserServiceImpl(){System.out.println("userService创建");}private UserDao userDao;private String username;//BeanFactory去调用该反复 从容器中获的userDao设置到此处public void setUserDao(UserDao userDao){System.out.println("userService执行注入userDao的操作:setUserDao方法执行");this.userDao = userDao;}public void setUsername(String username) {this.username = username;}
}
public class UserDaoImpl implements UserDao{public UserDaoImpl(){System.out.println("userDao创建");}@Overridepublic void show() {System.out.println("show");}
}
1、user的<bean>在userService的前面
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"></bean><bean id="userService" class="com.itheima.service.impl.UserServiceImpl"><property name="userDao" ref="userDao"></property><property name="username" value="haohao"></property></bean>
- 测试
@Testpublic void BeanTest5() throws IOException {ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");UserService bean = applicationContext.getBean(UserService.class);}

2、user的<bean>在userService的后面时
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl"><property name="userDao" ref="userDao"></property><property name="username" value="haohao"></property></bean><bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"></bean>

8.6 循环依赖概念及解决方案
多个实体之间相互依赖并形成闭环的情况就叫做 “循环依赖”,也叫做 “循环引用”


8.7 三级缓存的设计原理
Spring提供了三级缓存存储完整Bean实例和半成品Bean实例,用于解决循环引用问题
在DefaultListableBeanFactory的上四级父类DefaultSingletonBeanRegistry中提供如下三个Map:
public class DefaultsingletonBeanRegistry ... {//1、最终存储单例Bean成品的容器,即实例化和初始化都完成的Bean,称之为"一级缓存"Map<String,Object> singletonObjects = new ConcurrentHashMap(256);//2、早期Bean单例池,缓存半成品对象,且当前对象已经被其他对象引用了,称之为"二级缓存"Map<String,Object> earlySingletonObjects = new ConcurrentHashMap(16);//3、单例Bean的工厂池,缓存半成品对象,对象未被引用,使用时在通过工厂创建Bean,称之为"三级缓存"Map<String,ObjectFactory<?>> singletonFactories = new HashMap(16);
}
8.8 循环依赖源码流程剖析
UserService和UserDao循环依赖的过程结合上述三级缓存描述一下
- UserService 实例化对象,但尚未初始化,将UserService存储到三级缓存;
- UserService 属性注入,需要UserDao,从缓存中获取,没有UserDao;
- UserDao 实例化对象,但尚未初始化,将UserDao存储到到三级缓存;
- UserDao 属性注入,需要UserService,从三级缓存获取UserService,UserService从三级缓存移入二级缓存;
- UserDao执行其他生命周期过程,最终成为一个完成Bean,存储到一级缓存,删除二三级缓存;
- UserService注入UserDao;
- UserService执行其他生命周期过程,最终成为一个完成Bean,存储到一级缓存,删除二三级缓存。

1.

2.

3.

8.9 Aware接口
Aware接口是一种框架辅助属性注入的一种思想,其他框架中也可以看到类似的接口。框架具备高度封装性,我们接触到的一般都是业务代码,一个底层功能API不能轻易的获取到,但是这不意味着永远用不到这些对象,如果用到了,就可以使用框架提供的类似Aware的接口,让框架给我们注入该对象。
| Aware接口 | 回调方法 | 作用 |
|---|---|---|
| ServletContextAware | setServletContext(ServletContext context) | Spring框架回调方法注入ServletContext对象,web环境下才生效 |
| BeanFactoryAware | setBeanFactory(BeanFactory factory) | Spring框架回调方法注入beanFactory对象 |
| BeanNameAware | setBeanName(String beanName) | Spring框架回调方法注入当前Bean在容器中的beanName |
| ApplicationContextAware | setApplicationContext(ApplicationContext applicationContext) | Spring框架回调方法注入applicationContext对象 |
九 小黑子对Spring IoC整体流程总结

十 小黑子进行Spring xml方式整合第三方框架
xml整合第三方框架有两种整合方案:
不需要自定义名空间,不需要使用Spring的配置文件配置第三方框架本身内容,例如: MyBatis;
需要引入第三方框架命名空间,需要使用Spring的配置文件配置第三方框架本身内容,例如:Dubbo
10.1 Mybatis整合Spring实现
Spring整合MyBatis,之前已经在Spring中简单的配置了SqlSessionFactory,但是这不是正规的整合方式,MyBatis提供了mybatis-spring.jar专门用于两大框架的整合。
Spring整合MyBatis的步骤如下:
- 导入MyBatis整合Spring的相关坐标;
<dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>3.0.1</version>
</dependency>
<dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.2.25.RELEASE</version>
</dependency>
- 编写Mapper和Mapper.xml;
- 配置
SqlSessionFactoryBean和MapperScannerConfigurer;
<!-- 配置数据源信息--><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="com.mysql.jdbc.Driver"></property><property name="url" value="jdbc:mysql://localhost:3306/mybatis"></property><property name="username" value="root"></property><property name="password" value="root"></property></bean>
<!-- 配置SqlSessionFactoryBean,作用将SqlSessionFactory储存导spring容器中--><bean class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="dataSource"></property></bean>
<!-- 作用扫描指定的包,产生Mapper对象储存导spring容器当中--><bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"><property name="basePackage" value="com.itheima.mapper"></property></bean><bean id="userService" class="com.itheima.service.impl.UserServiceImpl"><property name="userMapper" ref="userMapper"></property></bean><bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"></bean>
- 编写测试代码
public class UserServiceImpl implements UserService {//需要Mapperprivate UserMapper userMapper;public void setUserMapper(UserMapper userMapper) {this.userMapper = userMapper;}@Overridepublic void show() {List<User> all = userMapper.selectAll();for(User user : all){System.out.println(user);}}}
package com.itheima.pojo;public class User {private Long id;private String username;private String password;private String gender;private String addr;@Overridepublic String toString() {return "User{" +"id=" + id +", username='" + username + '\'' +", password='" + password + '\'' +", gender='" + gender + '\'' +", addr='" + addr + '\'' +'}';}public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public String getGender() {return gender;}public void setGender(String gender) {this.gender = gender;}public String getAddr() {return addr;}public void setAddr(String addr) {this.addr = addr;}
}
package com.itheima.mapper;import com.itheima.pojo.User;import java.util.List;public interface UserMapper {List<User> selectAll();
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.UserMapper"><select id="selectAll" resultType="com.itheima.pojo.User">select * from tb_user</select>
</mapper>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><environments default="development"><environment id="development"><transactionManager type="JDBC"/><dataSource type="POOLED"><property name="driver" value="com.mysql.jdbc.mybatis"/><property name="url" value="jdbc:mysql://localhost:3306/mybatis"/><property name="username" value="root"/><property name="password" value="root"/></dataSource></environment></environments><mappers><package name="com.itheima.mapper"/></mappers></configuration>
@Testpublic void BeanTest6(){ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");UserService userService = applicationContext.getBean(UserService.class);userService.show();}

10.2 Mybatis整合Spring源码解析
整合包里提供了一个SqlSessionFactoryBean和一个扫描Mapper的配置对象,SqlSessionFactoryBean一旦被实例化,就开始扫描Mapper并通过动态代理产生Mapper的实现类存储到Spring容器中。相关的有如下四个类:
SqlSessionFactoryBean:需要进行配置,用于提供SqlSessionFactory;MapperScannerConfigurer:"需要进行配置,用于扫描指定mapper注册BeanDefinition;MapperFactoryBean::Mapper的FactoryBean,获得指定Mapper时调用getObject方法;ClassPathMapperScanner:definition.setAutowireMode(2)修改了自动注入状态,所以MapperFactoryBean中的setSqlSessionFactory会自动注入进去。
MapperScanerConfiguer源码分析
10.3 加载外部properties文件
Spring整合其他组件时就不像MyBatis这么简单了,例如Dubbo框架在于Spring进行整合时,要使用Dubbo提供的命名空间的扩展方式,自定义了一些Dubbo的标签
<?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:dubbo="http://dubbo.apache.org/schema/dubbo"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://dubbo.apache.org/schema/dubbohttp://dubbo.apache.org/schema/dubbo/dubbo.xsd"><!-- 配置Dubbo应用信息 --><dubbo:application name="your-application-name" /><!-- 配置注册中心 --><dubbo:registry address="zookeeper://127.0.0.1:2181" /><!-- 配置服务提供者 --><dubbo:protocol name="dubbo" port="20880" /><dubbo:service interface="com.example.YourServiceInterface" ref="yourServiceBean" /><!-- 配置其他Bean --><!-- 消费者配置 --><dubbo:consumer check="false" timeout="1000" retries="0"/>
</beans>
通过配置context文件来加载外部properties文件
- jdbc.properties
jdbc.driver = com.mysql.cj.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/mybatis
jdbc.username = root
jdbc.password = root
- bean.xml
<!-- 加载properties文件--><context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<!-- 配置数据源信息--><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="${jdbc.driver}"></property><property name="url" value="${jdbc.url}"></property><property name="username" value="${jdbc.username}"></property><property name="password" value="${jdbc.password}"></property></bean>
自定义命名空间解析原理
10.4 自定义空间步骤
- 将自定义标签的约束与物理约束文件与网络约束名称的约束以键值对形式存储到一个
spring.schemas文件里,该文件存储在类加载路径的META-INF里,Spring会自动加载到; - 将自定义命名空间的名称与自定义命名空间的处理器映射关系以键值对形式存在到一个叫
spring.handlers文件里,该文件存储在类加载路径的 META-INF里,Spring会自动加载到; - 准备好
NamespaceHandler,如果命名空间只有一个标签,那么直接在parse方法中进行解析即可,一般解析结果就是注册该标签对应的BeanDefinition。如果命名空间里有多个标签,那么可以在init方法中为每个标签都注册一个BeanDefinitionParser,在执行NamespaceHandler的parse方法时在分流给不同的
BeanDefinitionParser进行解析(重写doParse方法即可)。
10.5 自定义命名空间案例

步骤分析:
- 确定命名空间名称、schema虚拟路径、标签名称;
- 编写schema约束文件haohao-annotation.xsd
- 在类加载路径下创建META目录,编写约束映射文件spring.schemas和处理器映射文件spring.handlers
- 编写命名空间处理器HaohaoNamespaceHandler,在init方法中注册HaohaoBeanDefinitionParser
- 编写标签的解析器HaohaoBeanDefinitionParser,在parse方法中注册HaohaoBeanPostProcessor
- 编写HaohaoBeanPostProcessor
============= 以上五步是框架开发者写的,以下是框架使用者写的 ====================
- 在applicationContext.xml配置文件中引入命名空间
- 在applicationContext.xml配置文件中使用自定义的标签
相关文章:
小黑子—spring:第一章
spring入门1.0 一 小黑子对spring基础进行概述1.1 spring导论1.2 传统Javaweb开发困惑及解决方法1.3 三大的思想提出1.3.1 IOC入门案例1.3.2 DI入门案例 1.4 框架概念1.5 初识spring1.5.1 Spring Framework 1.6 BeanFactory快速入门1.7 ApplicationContext快速入门1.8 BeanFact…...
【面向对象】理解面向对象编程中的封装性
封装性是面向对象编程中的重要概念之一,它允许开发者将数据和方法组合成一个独立的单元,并通过定义访问权限来控制对这个单元的访问。本文将深入探讨面向对象编程中的封装性,包括封装的概念、实现封装的方式以及封装的好处。通过详细的解释和…...
ES修改字段类型详解
一、需求概述 ES修改字段类型是指在已有的索引中,通过特定的操作方式将某个字段的类型修改为其它类型。当ES在建立索引的时候,已经确定好了每个字段的类型,而如果在建立后发现类型不符需求,就需要修改字段类型。 二、修改字段类…...
C++基础:函数模板
为了代码重用,代码必须是通用的;通用的代码就必须不受数据类型的限制。那么我们可以把数据类型改为一个设计参数,这种类型的程序设计称为参数化程序设计,软件模板有模板构造,包括函数模板和类模板。 函数模板可以用来…...
Facebook账号被封?那是因为没做对这些事
Facebook是全球最大的社交媒体平台之一,拥有数十亿的全球用户。它的主要产品包括Facebook(面向个人用户的社交媒体平台)、Instagram、WhatsApp和Messenger。同时他也是美国数字广告市场的主要参与者之一,其广告平台吸引了数百万广…...
虚拟机kafka
一、kafka安装 (1)解压 (2)修改名字为kafka212 (3)进入/opt/soft/kafka212/config目录,配置文件server.properties 21 broker.id0 36 advertised.listenersPLAINTEXT://192.168.91.11:9092 …...
软考 系统架构设计师系列知识点之设计模式(3)
接前一篇文章:软考 系统架构设计师系列知识点之设计模式(2) 所属章节: 老版(第一版)教材 第7章. 设计模式 第2节. 设计模式实例 2. 结构型模式 结构型模式控制了应用程序较大部分之间的关系。它将以不同…...
217. 存在重复元素、Leetcode的Python实现
博客主页:🏆看看是李XX还是李歘歘 🏆 🌺每天分享一些包括但不限于计算机基础、算法等相关的知识点🌺 💗点关注不迷路,总有一些📖知识点📖是你想要的💗 ⛽️今…...
49.Redis缓存设计与性能优化
缓存与数据库双写不一致小概率事件 //线程1 写数据库stock 5 ---------------》更新缓存 //线程2 写数据库stock 4 -----》更新缓存 //线程1 ------》写数据库stock 10 -----》删除缓存 //线程2 ---------------------------------------------------------------------…...
IDEA常用的一些插件
1、CodeGlance 代码迷你缩放图插件,可以快速拖动代码,和VScode一样 2、Codota 代码提示工具,扫描你的代码后,根据你的敲击完美提示。 Codota基于数百万个开源Java程序和您的上下文来完成代码行,从而帮助您以更少的…...
基于定容积法标准容器容积标定中的电动针阀自动化解决方案
摘要:在目前的六氟化硫气体精密计量中普遍采用重量法和定容法两种技术,本文分析了重量法中存在的问题以及定容法的优势,同时也指出定容法在实际应用中还存在自动化水平较低的问题。为了提高定容法精密计量过程中的自动化水平,本文…...
26 行为型模式-命令模式
1 命令模式介绍 2 命令模式原理 3 命令模式实现 模拟酒店后厨的出餐流程,来对命令模式进行一个演示,命令模式角色的角色与案例中角色的对应关系如下: 服务员: 即调用者角色,由她来发起命令. 厨师: 接收者,真正执行命令的对象. 订单: 命令中包含订单 /*** 订单类**/ public cl…...
一个Entity Framework Core的性能优化案例
概要 本文提供一个EF Core的优化案例,主要介绍一些EF Core常用的优化方法,以及在优化过程中,出现性能反复的时候的解决方法,并澄清一些对优化概念的误解,例如AsNoTracking并不包治百病。 本文使用的是Dotnet 6.0和EF…...
【Python 千题 —— 基础篇】列表排序
题目描述 题目描述 给定一个包含无序数字的列表,请将列表中的数字按从小到大的顺序排列,并输出排序后的列表。 输入描述 输入一个包含无序数字的列表。 输出描述 程序将对列表中的数字进行排序,并输出排序后的列表。 示例 示例 ① 1…...
leetcode26:删除有序数组中的重复项
leetcode26:删除有序数组中的重复项 方案一:依次遍历,如果不符合条件则冒泡交换到最后一个位置。o(n^2),结果超时 #include <algorithm> #include <iostream>using namespace std; class Solution { public:int removeDuplicat…...
[FSCTF 2023] web题解
文章目录 源码!启动!webshell是啥捏细狗2.0ez_php1Hello,youEZ_eval巴巴托斯! 源码!启动! 打开题目,发现右键被禁了 直接ctrlu查看源码得到flag webshell是啥捏 源码 <?php highlight_file(__FILE__); $😀&qu…...
linux查看内存的方式
1、显示内存状态:free -h 以合适的单位显示内存使用情况,最大为三位数,自动计算对应的单位值。单位有: B bytes K kilos M megas G gigas T teras $free -htotal used free shared buff/cache available Me…...
Python 编写 Flink 应用程序经验记录(Flink1.17.1)
目录 官方API文档 提交作业到集群运行 官方示例 环境 编写一个 Flink Python Table API 程序 执行一个 Flink Python Table API 程序 实例处理Kafka后入库到Mysql 下载依赖 flink-kafka jar 读取kafka数据 写入mysql数据 flink-mysql jar 官方API文档 https://nigh…...
如何 通过使用优先级提示,来控制所有网页资源加载顺序
当你打开浏览器的网络标签时,你会看到大量的活动。资源正在下载,信息正在提交,事件正在记录,等等。 由于有太多的活动,有效地管理这些流量的优先级变得至关重要。带宽争用是真实存在的,当所有请求同时触发时…...
10月25日,每日信息差
今天是2023年10月26日,以下是为您准备的14条信息差 第一、百世集团牵头成立全国智慧物流与供应链行业产教融合共同体在杭州正式成立,该共同体由百世集团、浙江工商大学、浙江经济职业技术学院共同牵头 第二、问界M9预定量突破15000台 第三、前三季度我…...
golang循环变量捕获问题
在 Go 语言中,当在循环中启动协程(goroutine)时,如果在协程闭包中直接引用循环变量,可能会遇到一个常见的陷阱 - 循环变量捕获问题。让我详细解释一下: 问题背景 看这个代码片段: fo…...
测试markdown--肇兴
day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...
cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...
镜像里切换为普通用户
如果你登录远程虚拟机默认就是 root 用户,但你不希望用 root 权限运行 ns-3(这是对的,ns3 工具会拒绝 root),你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案:创建非 roo…...
鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/
使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题:docker pull 失败 网络不同,需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...
2023赣州旅游投资集团
单选题 1.“不登高山,不知天之高也;不临深溪,不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
探索Selenium:自动化测试的神奇钥匙
目录 一、Selenium 是什么1.1 定义与概念1.2 发展历程1.3 功能概述 二、Selenium 工作原理剖析2.1 架构组成2.2 工作流程2.3 通信机制 三、Selenium 的优势3.1 跨浏览器与平台支持3.2 丰富的语言支持3.3 强大的社区支持 四、Selenium 的应用场景4.1 Web 应用自动化测试4.2 数据…...
文件上传漏洞防御全攻略
要全面防范文件上传漏洞,需构建多层防御体系,结合技术验证、存储隔离与权限控制: 🔒 一、基础防护层 前端校验(仅辅助) 通过JavaScript限制文件后缀名(白名单)和大小,提…...
路由基础-路由表
本篇将会向读者介绍路由的基本概念。 前言 在一个典型的数据通信网络中,往往存在多个不同的IP网段,数据在不同的IP网段之间交互是需要借助三层设备的,这些设备具备路由能力,能够实现数据的跨网段转发。 路由是数据通信网络中最基…...



