Spring八股
Spring
refresh创建IOC容器
Spring 容器启动时会创建IOC容器,在创建容器时,会调用 refresh()方法,整个容器就是通过该方法,完成所有的 bean 的创建以及初始化
重要的类/对象
- ClassPathXmlApplicationContext:使用配置文件的容器
- AnnotationConfigApplicationContext:使用配置类的容器
- BeanDefinition:Bean的相关信息。如是否是单例的,Bean的类型,是否是懒加载,依赖哪些类,自动装配的模型
- beanDefinitionMap:存放bean对应的BeanDefinition
- beanDefinitionNames:存放所有bean的名字
- BeanFactoryPostProcessor:BeanFactory的后置处理器
- BeanPostProcessor:Bean的后置处理器,通过它可以实现AOP、Bean的创建
重要步骤

invokeBeanFactoryPostProcessors()会执行所有BeanFactoryPostProcessor
- Spring内置的BeanFactory后处理器:
ConfigurationClassPostProcessor,会解析出所有交由Spring管理的Bean并解析成BeanDefinition - 自定义的BeanFactory后处理器:实现了
BeanFactoryPostProcessor接口或BeanDefinitionRegistryPostProcessor接口。会先执行后者的实现类

registerBeanPostProcessors()会找到并实例化所有的BeanPostProcessor,并放到beanPostProcessors集合中
finishBeanFactoryInitialization()完成了所有非懒加载的单例Bean的实例化和初始化,属性的填充以及解决了循环依赖等问题
12个步骤
概览
- 1:准备工作
- 2~6:创建准备BeanFactory的各项功能
- 7~12:准备ApplicationContext
1.prepareRefresh()-开启容器,加载键值
创建和准备了Environment对象
Environment对象的作用:加载了一些键值信息:
- jvm提供的键值
- 操作系统系统里的键值
- 配置文件里提供的键值信息
Environment的作用之一是为后续@Value注入值的时候提供信息

2.obtainFreshBeanFactory()-获取或创建Bean工厂
创建了BeanFactory对象
BeanDefinitionMap为BeanFactory的成员对象,存储了BeanDefinition
- BeanDefinition作为bean的设计蓝图,规定了bean的特征,如单例多例、依赖关系、初始化销毁方法等
- BeanDefinition的来源可以通过xml获取、配置类获得、组件扫描或编程添加等
BeanFactory的作用是负责bean的创建、依赖注入和初始化

3.prepareBeanFactory()-完善Bean工厂
创建用来解析SpEL的beanExpressionResolver
创建执行类型转换的propertyEditorRegistrars
创建特殊bean注入的resolvableDependencies
扩展bean的创建功能的beanPostProcessors

StandardBeanExpressionResolver来解析SpEL
ResourceEditorRegister会注释类型转换器,并应用ApplicationContext提供的Environment来完成${}解析
特殊bean指beanFactory以及ApplicationContext,通过registerResolvableDependency来注册它们
4.postProcessBeanFactory()-子类扩展Bean工厂
- 后处理可以理解为添加功能。
- 默认为空实现,留个子类扩展
- 一般Web环境的ApplicationContext都要利用它注册新的Scope,完善Web下的BeanFactory
5.invokeBeanFactoryPostProcessors()-后处理器扩展Bean工厂
- 预先定义好的后处理,通过处理器的方式添加BeanFactory的功能
- 后置处理器可以扩展beanFactory的功能,用来补充和修改BeanDefinition
- ConfigurationClassPostProcessor-解析@Configuration、@Bean、@Import等
6.registerBeanPostProcessors()-注册Bean后处理器
- bean后后处理器充当bean的扩展点,可以工作在bean的实例化、依赖注入、初始化阶段
- 如有解析@Autowired、@Value、@Resource等注解的后处理器
7.initMessageSource()-国际化
初始化messageSource,用来做国际化功能

8.initApplicationEventMulticaster()-初始化事件多播器
- 这个类会持有所有的事件监听器(
ApplicationListener),当有ApplicationEvent事件发布时,该事件监听器能根据事件类型,检索到对该事件感兴趣的ApplicationListener

9.onRefresh()-初始化子容器中的bean
- 执行其他的初始化操作,例如和
SpringMVC整合时,需要初始化一些其他的bean,但是对于纯Spring工程来说,onRefresh()方法是一个空方法。
10.registerListeners()-注册监听器
- 接收事件
11.finishBeanFactoryInitialization()- 初始化所有剩下的单例 bean
- conversionService也是一套转换机制,作为对PropertyEditor的补充
- 内嵌值解析器用来解析@Value中的${},借用的是Environment的功能
- 单例池用来缓存所有单例对象
- 对象创建分为三个阶段:创建、依赖注入、初始化,每个阶段都有不同的bean处理器参与扩展

12.finishRefresh()-准备声明周期管理器,发布ContextRefresh事件
- lifecycleProcessor用来控制容器内需要生命周期管理的bean

Bean的作用域
- Singleton:默认,在IOC容器中只有一个实例
- Prototype:可以有多个实例
- Request:每次http请求都会创建一个Bean
- Session:一个Session中对应一个Bean对应一个实例
- Global-session:全局Session中,一个Bean对应一个实例
Bean的生命周期

7个阶段
Bean为懒惰初始化,生命周期从doGetBean()方法开始
概览
- 阶段1:处理名称,检查缓存
- 阶段2:检查父工厂
- 阶段3:检查DependsOn
- 阶段4:按Scope创建bean
- 阶段5:创建bean
- 阶段6:类型转换
- 阶段7:销毁bean
阶段1
- 先把别名解析为实际名称,再进行后续处理
- 若要FactoryBean本身,需要使用&名称获取
- 检查缓存里是否有:
- 一级缓存是singletonObjects,放单例成品对象
- 二级缓存是earlySingletonObjects,放单例工厂的产品,可称为提前单例对象
- 三级缓存是singletonFactories,放单例工厂
阶段2
- 缓存中没有bean回去父容器中找
- 父容器中的bean和子容器中的bean名称可以重复
阶段3
- 如果A显式依赖于B,则B会被先创建
- DependsOn用在非显式依赖的bean的创建顺序控制
阶段4
- 常见scope:
- singleton scope表示从单例池范围内获取bean
- 容器创建时创建,容器销毁时销毁
- prototype scope表示从不缓存bean,每次创建新的
- 用时创建,不用时手动销毁
- request scope表示从request对象范围内获取bean
- 在request域中创建,request销毁时销毁
- singleton scope表示从单例池范围内获取bean
阶段5
- 创建阶段:
- AutowiredAnnotationBeanPostProcessor选择构造:优先使用带@Autowired的构造方法来创建bean,如果只有一个有参构造,则用这个有参构造创建bean
- 默认构造:保底使用无参构造来创建
- 依赖注入阶段:
- 后处理器扩展功能:按注解匹配要注入的bean
- 根据名字、类型去容器中找bean
- xml精确指定bean
- 初始化阶段:
- 处理Aware接口,如BeanNameAware、BeanFactoryAware,BeanFactory会把相应的资源传递进来
- 调用初始化方法,初始化方法定义有@PostConstruct、实现InitalizingBean接口等
- 创建aop代理
- 注册可销毁bean
- 将有销毁方法的bean保存起来,销毁时调用方法

阶段6
- 如果getBean的requiredType参数与实际得到的对象类型不同,会尝试类型转换
阶段7
- 根据scpoe来销毁bean
事务的传播行为
| 级别 | 描述 |
|---|---|
| Required(默认) | 支持当前事务,没有事务会创建一个 |
| Supports | 支持当前事务,没有事务则以非事务的方式执行 |
| Mandatory | 支持当前事务,没有事务会抛出异常 |
| Requires_new | 创建新事务并挂起当前事务 |
| Not_supported | 以非事务方式执行,会挂起当前事务 |
| Never | 以非事务方式执行,有事务会抛出异常 |
| Nested | 嵌套事务 |
事务失效场景
场景1:检查异常
需要
try..catch或throw的异常都是检查异常运行时异常是非检查异常
抛出检查异常导致事务不能正常回滚
原因:spring默认只会回滚非检查异常
解决方式:在注解上加上@Transactional(rollbackFor=Exception.class)设置需要回滚的异常
场景2:错误try catch
原因:事务是通过代理实现的,如果在被代理方法内catch了异常,外层调用者是不知道发生了异常的,从而事务失效
解决方式1:把异常抛出去
解决方式2:手动设置事务状态TransactionStatus.setRollbackOnly()
场景3:aop切面顺序不对
原因:在自定义切面里catch了异常,导致更上层的事务切面不知道发生了异常
解决方式1:同场景2
解决方式2:使自定义切面优先级比事务的高@Order(Ordered.LOWEST.PRECEDENCE - 1)
场景4:加在非公共方法上
原因:Spring为方法创建代理、添加事务通知、前提条件都是方法是public的
场景5:父子容器导致的事务失效
原因:子容器扫描范围过大,把未加事务配置的service扫描进来
解决方式1:容器各自扫各自的包
解决方式2:不用父子容器
场景6:调用本类方法导致传播行为失效
原因:本类方法调用没有经过代理增强
解决方式:依赖注入自身
场景7:锁失效
原因:@Transactional没有保证原子行为,在方法上加锁只能保证方法内的原子性,提交事务时锁失效
解决方式1:synchronized范围扩大至代理方法调用
解决方式2:sql语句select ... for update替换select
SpringMVC执行流程
初始化阶段
- 第一次用到DispatcherServlet时,会创建其对象并执行init方法
- 容器初始化后,会将初始化好的重要组件赋值给DispatcherServlet的成员变量

匹配阶段
- 用户请求到达DispatcherServlet,由其遍历所有的HandlerMapping,找到与路径匹配的处理器
- 将HandlerMethod连同匹配到的拦截器,生成调用链对象返回
- 遍历HandlerAdapter,找到能处理HandlerMethod的适配器,开始调用

执行阶段
- 执行拦截器的preHandle
- 调用HandlerMethod,返回ModelAndView
- 执行拦截器的postHandle
- 解析ModelAndView
- 执行拦截器的afterCompletion

注解
Spring常见注解

SpringMVC常见注解

SpringBoot常见注解

SpringBoot自动配置
@SpringBootApplication主要由三个注解构成
- @SpringBootConfiguration
- @Configuration可以多个,@SpringBootConfiguration只能一个,用于表示当前类为SpringBoot配置类
- @EnableAutoConfiguration
- @AutoConfigurationPackage:记录标注了这个注解的包名
- @Import(AutoConfigurationImportSelector.class):导入自动配置相关的bean,加载META-INF/spring.factories中的配置类
- @ComponentScan
- 属性excludeFilters用于排除一些类不注册为bean
循环依赖

切面
通过代理对象来关联ProxyFactory实现切面,SpringBoot通过自动代理后处理器来识别相关注解
术语
Advisor:最基础的切面,把一个Advice和一个Pointcut组合起来
Advice:增强
Pointcut:切点
Aspect:切面
最基本的切面是Advisor,一个Aspect切面对应一到多个Advisor
最基本的Advice是MethodInterceptor,其他Advice最终都会适配为MethodInterceptor
创建代理的方式有:实现了用户自定义接口,采用jdk动态代理;没有实现用户自定义接口,采用cglib代理;设置了setProxyTargetClass(True),统一采用cglib代理
切面、切点、通知等都不会被代理
自动代理后处理器调用时机:创建阶段、依赖注入阶段、初始化阶段
注解
- @Aspect:表示为切面类
- @Around、@Before、@After等:通知方法,可以加切点表达式
三级缓存

作用
- 一级缓存作用:限制bean在beanFactory中只存一份
只有一级缓存不能解决循环依赖问题

二级缓存作用(Spring中的三级缓存):存还未装载成员变量的空对象,解决循环依赖,在初始化完后把二级缓存中的空对象清除掉

二级缓存不能解决循环依赖中有代理的情况

三级缓存作用(Spring中的二级缓存):解决循环依赖中代理创建过晚的问题,通过工厂判断是否需要代理,需要则提前创建代理对象,否则返回本身空对象

三级缓存不能解决构造函数中有循环依赖:构造不出来,无法放到缓存中

解决方式1:加@Lazy注解,会先创建代理对象注入,待用到时再注入

解决方式2:通过
ObjectFactory<B> b来先注入工厂,使用时在创建bean
单例Bean是线程安全的吗
不是线程安全的。@Scope注解默认是sington单例的。如果spring的bean中注入的都是无状态的对象,是没有线程安全问题的,如果在bean中定义了可以修改的成员变量,是要考虑线程安全问题的,可以使用多例或加锁来解决问题
AOP
面向切面编程,用于将那些与业务无关,但对多个对象产生影响的公共行为或逻辑,抽出并封装成一个可重用的模块
常见AOP使用场景
记录操作日志
缓存处理
事务
编程式事务:使用TransactionTemplate,对代码有侵入性
声明式事务:使用@Transactional注解,通过AOP功能,对方法进行拦截
自动装配
@SpringBootApplication中包含的@EnableAutoConfiguration中用@Import导入了配置选择器
内部读取了该项目和引用的Jar包的classpath路径下META-INF/spring.factories文件中的所配置的类的全类名,根据这些配置类中定义的Bean所指定的条件来决定是否注入到Spring容器中