目录
5、invokeBeanFactoryPostProcessors
8、initApplicationContextEventMulticaster
11、finishBeanFactoryInitialization
2、业务方法内自己 try-catch 异常导致事务不能正确回滚
8、@Transactional 方法导致的 synchronized 失效
1、ApplicationContext refresh
refresh 是 AbstractApplicationContext 中的一个方法,负责初始化 ApplicationContext 容器,容器必须调用 refresh 才能正常工作。它的内部主要会调用 12 个方法,我们把它们称为 refresh 的 12 个步骤:
1、prepareRefresh
- 这一步创建和准备了 Environment 对象,它作为 ApplicationContext 的一个成员变量
- Environment 对象的作用之一是为后续 @Value,值注入时提供键值
- Environment 分成三个主要部分:
- systemProperties - 保存 java 环境键值
- systemEnvironment - 保存系统环境键值
- 自定义 PropertySource - 保存自定义键值,例如来自于 *.properties 文件的键值
2、obtainFreshBeanFactory
- 这一步获取(或创建) BeanFactory,它也是作为 ApplicationContext 的一个成员变量
- BeanFactory 的作用是负责 bean 的创建、依赖注入和初始化,bean 的各项特征由 BeanDefinition 定义
- BeanDefinition 作为 bean 的设计蓝图,规定了 bean 的特征,如单例多例、依赖关系、初始销毁方法等
- BeanDefinition 的来源有多种多样,可以是通过 xml 获得、配置类获得、组件扫描获得,也可以是编程添加
- 所有的 BeanDefinition 会存入 BeanFactory 中的 beanDefinitionMap 集合
3、prepareBeanFactory
- 进一步完善 BeanFactory,为它的各项成员变量赋值
- beanExpressionResolver 用来解析 SpEL,常见实现为 StandardBeanExpressionResolver
- propertyEditorRegistrars 会注册类型转换器
- 它在这里使用了 ResourceEditorRegistrar 实现类
- 并应用 ApplicationContext 提供的 Environment 完成 ${ } 解析
- registerResolvableDependency 来注册 beanFactory 以及 ApplicationContext,让它们也能用于依赖注入
- beanPostProcessors 是 bean 后处理器集合,会工作在 bean 的生命周期各个阶段,此处会添加两个:
- ApplicationContextAwareProcessor 用来解析 Aware 接口
- ApplicationListenerDetector 用来识别容器中 ApplicationListener 类型的 bean
4、postProcessBeanFactory
- 这一步是空实现,留给子类扩展。
一般 Web 环境的 ApplicationContext 都要利用它注册新的 Scope,完善 Web 下的 BeanFactory
- 这里体现的是模板方法设计模式
5、invokeBeanFactoryPostProcessors
- 调用 beanFactory 后处理器
- beanFactory 后处理器,充当 beanFactory 的扩展点,可以用来补充或修改 BeanDefinition
- 常见的 beanFactory 后处理器有
- ConfigurationClassPostProcessor – 解析 @Configuration、@Bean、@Import、@PropertySource 等
- PropertySourcesPlaceHolderConfigurer – 替换 BeanDefinition 中的 ${ }
- MapperScannerConfigurer – 补充 Mapper 接口对应的 BeanDefinition
6、registerBeanPostProcessors
- 继续从 beanFactory 中找出 bean 后处理器,添加至 beanPostProcessors 集合中
- bean 后处理器,充当 bean 的扩展点,可以工作在 bean 的实例化、依赖注入、初始化阶段,常见的有:
- AutowiredAnnotationBeanPostProcessor 功能有:解析 @Autowired,@Value 注解
- CommonAnnotationBeanPostProcessor 功能有:解析 @Resource,@PostConstruct,@PreDestroy
- AnnotationAwareAspectJAutoProxyCreator 功能有:为符合切点的目标 bean 自动创建代理
7、initMessageSource
- 为 ApplicationContext 添加 messageSource 成员,实现国际化功能
- 去 beanFactory 内找名为 messageSource 的 bean,如果没有,则提供空的 MessageSource 实现
8、initApplicationContextEventMulticaster
- 这一步为 ApplicationContext 添加事件广播器成员,即 applicationContextEventMulticaster
- 它的作用是发布事件给监听器
- 去 beanFactory 找名为 applicationEventMulticaster 的 bean 作为事件广播器,若没有,会创建默认的事件广播器
- 之后就可以调用 ApplicationContext.publishEvent(事件对象) 来发布事件
9、onRefresh
- 这一步是空实现,留给子类扩展
SpringBoot 中的子类在这里准备了 WebServer,即内嵌 web 容器
- 体现的是模板方法设计模式
10、registerListeners
- 从多种途径找到事件监听器,并添加至 applicationEventMulticaster
- 事件监听器顾名思义,用来接收事件广播器发布的事件,有如下来源
- 事先编程添加的
- 来自容器中的 bean
- 来自于 @EventListener 的解析
- 要实现事件监听器,只需要实现 ApplicationListener 接口,重写其中 onApplicationEvent(E e) 方法即可
11、finishBeanFactoryInitialization
- 将 beanFactory 的成员补充完毕,并初始化所有非延迟单例 bean
- conversionService 也是一套转换机制,作为对 PropertyEditor 的补充
- embeddedValueResolvers 即内嵌值解析器,用来解析 @Value 中的 ${ },借用的是 Environment 的功能
- singletonObjects 即单例池,缓存所有单例对象
对象的创建都分三个阶段,每一阶段都有不同的 bean 后处理器参与进来,扩展功能
12、finishRefresh
- 这一步会为 ApplicationContext 添加 lifecycleProcessor 成员,用来控制容器内需要生命周期管理的 bean
- 如果容器中有名称为 lifecycleProcessor 的 bean 就用它,否则创建默认的生命周期管理器
- 准备好生命周期管理器,就可以实现
- 调用 context 的 start,即可触发所有实现 LifeCycle 接口 bean 的 start
- 调用 context 的 stop,即可触发所有实现 LifeCycle 接口 bean 的 stop
- 发布 ContextRefreshed 事件,整个 refresh 执行完成
2、Spring Bean的生命周期
bean 的生命周期从调用 beanFactory 的 getBean 开始,到这个 bean 被销毁,可以总结为以下七个阶段:
1、处理名称,检查缓存
- 处理别名,将别名解析为实际名称
- 对 FactoryBean 也会特殊处理,如果以 & 开头表示要获取 FactoryBean 本身,否则表示要获取其产品
- 这里针对单例对象会检查一级、二级、三级缓存
singletonFactories 三级缓存,存放单例工厂对象
earlySingletonObjects 二级缓存,存放单例工厂的产品对象
如果发生循环依赖,产品是代理;无循环依赖,产品是原始对象
singletonObjects 一级缓存,存放单例成品对象
2、处理父子容器
- 如果当前容器根据名字找不到这个 bean,此时若父容器存在,则执行父容器的 getBean 流程
- 父子容器的 bean 名称可以重复
3、处理 dependsOn
- 如果当前 bean 有通过 dependsOn 指定了非显式依赖的 bean,这一步会提前创建这些 dependsOn 的 bean
- 所谓非显式依赖,就是指两个 bean 之间不存在直接依赖关系,但需要控制它们的创建先后顺序
4、选择 scope 策略
- 对于 singleton scope,首先到单例池去获取 bean,如果有则直接返回,没有再进入创建流程
- 对于 prototype scope,每次都会进入创建流程
- 对于自定义 scope,例如 request,首先到 request 域获取 bean,如果有则直接返回,没有再进入创建流程
5、创建 bean
1、创建 bean 实例
2、依赖注入
3、初始化
4、注册可销毁 bean
判断并登记可销毁 bean
- 判断依据
- 如果实现了 DisposableBean 或 AutoCloseable 接口,则为可销毁 bean
- 如果自定义了 destroyMethod,则为可销毁 bean
- 如果采用 @Bean 没有指定 destroyMethod,则采用自动推断方式获取销毁方法名(close,shutdown)
- 如果有 @PreDestroy 标注的方法
- 存储位置
- singleton scope 的可销毁 bean 会存储于 beanFactory 的成员当中
- 自定义 scope 的可销毁 bean 会存储于对应的域对象当中
- prototype scope 不会存储,需要自己找到此对象销毁
- 存储时都会封装为 DisposableBeanAdapter 类型对销毁方法的调用进行适配
6、类型转换处理
如果 getBean 的 requiredType 参数与实际得到的对象类型不同,会尝试进行类型转换
7、销毁 bean
- 销毁时机
- singleton bean 的销毁在 ApplicationContext.close 时,此时会找到所有 DisposableBean 的名字,逐一销毁
- 自定义 scope bean 的销毁在作用域对象生命周期结束时
- prototype bean 的销毁可以通过自己手动调用 AutowireCapableBeanFactory.destroyBean 方法执行销毁
- 同一 bean 中不同形式销毁方法的调用次序
- 优先后处理器销毁,即 @PreDestroy
- 其次 DisposableBean 接口销毁
- 最后 destroyMethod 销毁(包括自定义名称,推断名称,AutoCloseable 接口 多选一)
3、Spring 事务失效
1、抛出检查异常导致事务不能正确回滚
检查异常:抛出或try。。。catch。。。的异常
原因:Spring 默认只会回滚非检查异常
解法:配置 rollbackFor 属性
@Transactional(rollbackFor = Exception.class)
2、业务方法内自己 try-catch 异常导致事务不能正确回滚
原因:事务通知只有捉到了目标抛出的异常,才能进行后续的回滚处理,如果目标自己处理掉异常,事务通知无法知悉
- 解法1:异常原样抛出
在 catch 块添加 `throw new RuntimeException(e);
- 解法2:手动设置 TransactionStatus.setRollbackOnly()
在 catch 块添加 `TransactionInterceptor.currentTransactionStatus().setRollbackOnly();`
3、aop 切面顺序导致导致事务不能正确回滚
原因:事务切面优先级最低,但如果自定义的切面优先级和他一样,则还是自定义切面在内层,这时若自定义切面没有正确抛出异常…
- 解法1、2:同情况2 中的解法:1、2
- 解法3:调整切面顺序,在 MyAspect 上添加 `@Order(Ordered.LOWEST_PRECEDENCE - 1)` (不推荐)
4、非 public 方法导致的事务失效
原因:Spring 为方法创建代理、添加事务通知、前提条件都是该方法是 public 的
- 解法1:改为 public 方法
- 解法2:添加 bean 配置如下(不推荐)
@Bean
public TransactionAttributeSource transactionAttributeSource() {
return new AnnotationTransactionAttributeSource(false);
}
5、父子容器导致的事务失效
现在配置了父子容器,WebConfig 对应子容器,AppConfig 对应父容器,发现事务依然失效
原因:子容器扫描范围过大,把未加事务配置的 service 扫描进来
- 解法1:各扫描各的,不要图简便
- 解法2:不要用父子容器,所有 bean 放在同一容器
6、调用本类方法导致传播行为失效
原因:本类方法调用不经过代理,因此无法增强
- 解法1:依赖注入自己(代理)来调用
- 解法2:通过 AopContext 拿到代理对象,来调用
- 解法3:通过 CTW,LTW 实现功能增强
方法一:
@Service
public class Service6 {
@Autowired
private Service6 proxy; // 本质上是一种循环依赖
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void foo() throws FileNotFoundException {
LoggerUtils.get().debug("foo");
System.out.println(proxy.getClass());
proxy.bar();
}
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void bar() throws FileNotFoundException {
LoggerUtils.get().debug("bar");
}
}
方法二:还需要在 AppConfig 上添加 `@EnableAspectJAutoProxy(exposeProxy = true)`
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void foo() throws FileNotFoundException {
LoggerUtils.get().debug("foo");
((Service6) AopContext.currentProxy()).bar();
}
7、@Transactional 没有保证原子行为
上面的代码实际上是有 bug 的,假设 from 余额为 1000,两个线程都来转账 1000,可能会出现扣减为负数的情况
- 原因:事务的原子性仅涵盖 insert、update、delete、select … for update 语句,select 方法并不阻塞
如上图所示,红色线程和蓝色线程的查询都发生在扣减之前,都以为自己有足够的余额做扣减
8、@Transactional 方法导致的 synchronized 失效
针对上面的问题,能否在方法上加 synchronized 锁来解决呢?
答案是不行,原因如下:
- synchronized 保证的仅是目标方法的原子性,环绕目标方法的还有 commit 等操作,它们并未处于 sync 块内
- 可以参考下图发现,蓝色线程的查询只要在红色线程提交之前执行,那么依然会查询到有 1000 足够余额来转账
- 解法1:synchronized 范围应扩大至代理方法调用
- 解法2:使用 select … for update 替换 select
4、Spring MVC 执行流程
1、准备阶段
- 在 Web 容器第一次用到 DispatcherServlet 的时候,会创建其对象并执行 init 方法
- init 方法内会创建 Spring Web 容器,并调用容器 refresh 方法
- refresh 过程中会创建并初始化 SpringMVC 中的重要组件, 例如 MultipartResolver,HandlerMapping,HandlerAdapter,HandlerExceptionResolverViewResolver 等
- 容器初始化后,会将上一步初始化好的重要组件,赋值给 DispatcherServlet 的成员变量,留待后用
2、匹配阶段
- 用户发送的请求统一到达前端控制器 DispatcherServlet
- DispatcherServlet 遍历所有 HandlerMapping ,找到与路径匹配的处理器
① HandlerMapping 有多个,每个 HandlerMapping 会返回不同的处理器对象,谁先匹配,返回谁的处理器。其中能识别 @RequestMapping 的优先级最高
② 对应 @RequestMapping 的处理器是 HandlerMethod,它包含了控制器对象和控制器方法信息
③ 其中路径与处理器的映射关系在 HandlerMapping 初始化时就会建立好
3. 将 HandlerMethod 连同匹配到的拦截器,生成调用链对象 HandlerExecutionChain 返回
4. 遍历HandlerAdapter 处理器适配器,找到能处理 HandlerMethod 的适配器对象,开始调用
3、调用阶段
1. 执行拦截器 preHandle
2. 由 HandlerAdapter 调用 HandlerMethod
① 调用前处理不同类型的参数
② 调用后处理不同类型的返回值
3. 第 2 步没有异常
① 返回 ModelAndView
② 执行拦截器 postHandle 方法
③ 解析视图,得到 View 对象,进行视图渲染
4. 第 2 步有异常,进入 HandlerExceptionResolver 异常处理流程
5. 最后都会执行拦截器的 afterCompletion 方法
6. 如果控制器方法标注了 @ResponseBody 注解,则在第 2 步,就会生成 json 结果,并标记 ModelAndView 已处理,这样就不会执行第 3 步的视图渲染