面试——框架篇(一)

目录

1、ApplicationContext refresh

1、prepareRefresh 

2、obtainFreshBeanFactory

 3、prepareBeanFactory

 4、postProcessBeanFactory

5、invokeBeanFactoryPostProcessors

6、registerBeanPostProcessors

 7、initMessageSource

 8、initApplicationContextEventMulticaster

 9、onRefresh

10、registerListeners

 11、finishBeanFactoryInitialization

 12、finishRefresh

 2、Spring Bean的生命周期

 1、处理名称,检查缓存

 2、处理父子容器

 3、处理 dependsOn

4、选择 scope 策略

5、创建 bean

1、创建 bean 实例

 2、依赖注入

 3、初始化

 4、注册可销毁 bean

 6、类型转换处理

 7、销毁 bean

3、Spring 事务失效

1、抛出检查异常导致事务不能正确回滚

2、业务方法内自己 try-catch 异常导致事务不能正确回滚

3、aop 切面顺序导致导致事务不能正确回滚

4、非 public 方法导致的事务失效

5、父子容器导致的事务失效

6、调用本类方法导致传播行为失效

7、@Transactional 没有保证原子行为

 8、@Transactional 方法导致的 synchronized 失效

 4、Spring MVC 执行流程

1、准备阶段

2、匹配阶段

3、调用阶段 


1、ApplicationContext refresh

refresh 是 AbstractApplicationContext 中的一个方法,负责初始化 ApplicationContext 容器,容器必须调用 refresh 才能正常工作。它的内部主要会调用 12 个方法,我们把它们称为 refresh 的 12 个步骤:

1、prepareRefresh 

 

  • 这一步创建和准备了 Environment 对象,它作为 ApplicationContext 的一个成员变量
  • Environment 对象的作用之一是为后续 @Value,值注入时提供键值
  • Environment 分成三个主要部分:
  1. systemProperties - 保存 java 环境键值
  2. systemEnvironment - 保存系统环境键值
  3. 自定义 PropertySource - 保存自定义键值,例如来自于 *.properties 文件的键值

2、obtainFreshBeanFactory

  • 这一步获取(或创建) BeanFactory,它也是作为 ApplicationContext 的一个成员变量
  • BeanFactory 的作用是负责 bean 的创建、依赖注入和初始化,bean 的各项特征由 BeanDefinition 定义
  1. BeanDefinition 作为 bean 的设计蓝图,规定了 bean 的特征,如单例多例、依赖关系、初始销毁方法等
  2. BeanDefinition 的来源有多种多样,可以是通过 xml 获得、配置类获得、组件扫描获得,也可以是编程添加
  • 所有的 BeanDefinition 会存入 BeanFactory 中的 beanDefinitionMap 集合

 3、prepareBeanFactory

  • 进一步完善 BeanFactory,为它的各项成员变量赋值
  • beanExpressionResolver 用来解析 SpEL,常见实现为 StandardBeanExpressionResolver
  • propertyEditorRegistrars 会注册类型转换器
  1. 它在这里使用了 ResourceEditorRegistrar 实现类
  2. 并应用 ApplicationContext 提供的 Environment 完成 ${ } 解析
  • registerResolvableDependency 来注册 beanFactory 以及 ApplicationContext,让它们也能用于依赖注入
  • beanPostProcessors 是 bean 后处理器集合,会工作在 bean 的生命周期各个阶段,此处会添加两个:
  1. ApplicationContextAwareProcessor 用来解析 Aware 接口
  2. ApplicationListenerDetector 用来识别容器中 ApplicationListener 类型的 bean 

 4、postProcessBeanFactory

  • 这一步是空实现,留给子类扩展。

        一般 Web 环境的 ApplicationContext 都要利用它注册新的 Scope,完善 Web 下的 BeanFactory

  • 这里体现的是模板方法设计模式

5、invokeBeanFactoryPostProcessors

  • 调用 beanFactory 后处理器
  • beanFactory 后处理器,充当 beanFactory 的扩展点,可以用来补充或修改 BeanDefinition
  • 常见的 beanFactory 后处理器有
  1. ConfigurationClassPostProcessor – 解析 @Configuration、@Bean、@Import、@PropertySource 等
  2. PropertySourcesPlaceHolderConfigurer – 替换 BeanDefinition 中的 ${ }
  3. MapperScannerConfigurer – 补充 Mapper 接口对应的 BeanDefinition

6、registerBeanPostProcessors

  • 继续从 beanFactory 中找出 bean 后处理器,添加至 beanPostProcessors 集合中
  • bean 后处理器,充当 bean 的扩展点,可以工作在 bean 的实例化、依赖注入、初始化阶段,常见的有:
  1. AutowiredAnnotationBeanPostProcessor 功能有:解析 @Autowired,@Value 注解
  2. CommonAnnotationBeanPostProcessor 功能有:解析 @Resource,@PostConstruct,@PreDestroy
  3. 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
  • 事件监听器顾名思义,用来接收事件广播器发布的事件,有如下来源
  1. 事先编程添加的
  2. 来自容器中的 bean
  3. 来自于 @EventListener 的解析
  • 要实现事件监听器,只需要实现 ApplicationListener 接口,重写其中 onApplicationEvent(E e) 方法即可 

 11、finishBeanFactoryInitialization

  • 将 beanFactory 的成员补充完毕,并初始化所有非延迟单例 bean
  • conversionService 也是一套转换机制,作为对 PropertyEditor 的补充
  • embeddedValueResolvers 即内嵌值解析器,用来解析 @Value 中的 ${ },借用的是 Environment 的功能
  • singletonObjects 即单例池,缓存所有单例对象

        对象的创建都分三个阶段,每一阶段都有不同的 bean 后处理器参与进来,扩展功能 

 12、finishRefresh

  • 这一步会为 ApplicationContext 添加 lifecycleProcessor 成员,用来控制容器内需要生命周期管理的 bean
  • 如果容器中有名称为 lifecycleProcessor 的 bean 就用它,否则创建默认的生命周期管理器
  • 准备好生命周期管理器,就可以实现
  1. 调用 context 的 start,即可触发所有实现 LifeCycle 接口 bean 的 start
  2. 调用 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

  • 判断依据
  1. 如果实现了 DisposableBean 或 AutoCloseable 接口,则为可销毁 bean
  2. 如果自定义了 destroyMethod,则为可销毁 bean
  3. 如果采用 @Bean 没有指定 destroyMethod,则采用自动推断方式获取销毁方法名(close,shutdown)
  4. 如果有 @PreDestroy 标注的方法
  • 存储位置
  1. singleton scope 的可销毁 bean 会存储于 beanFactory 的成员当中
  2. 自定义 scope 的可销毁 bean 会存储于对应的域对象当中
  3. prototype scope 不会存储,需要自己找到此对象销毁
  • 存储时都会封装为 DisposableBeanAdapter 类型对销毁方法的调用进行适配

 6、类型转换处理

 如果 getBean 的 requiredType 参数与实际得到的对象类型不同,会尝试进行类型转换

 7、销毁 bean

  • 销毁时机
  1. singleton bean 的销毁在 ApplicationContext.close 时,此时会找到所有 DisposableBean 的名字,逐一销毁
  2. 自定义 scope bean 的销毁在作用域对象生命周期结束时
  3. prototype bean 的销毁可以通过自己手动调用 AutowireCapableBeanFactory.destroyBean 方法执行销毁
  • 同一 bean 中不同形式销毁方法的调用次序
  1. 优先后处理器销毁,即 @PreDestroy
  2. 其次 DisposableBean 接口销毁
  3. 最后 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、准备阶段

  1. 在 Web 容器第一次用到 DispatcherServlet 的时候,会创建其对象并执行 init 方法
  2. init 方法内会创建 Spring Web 容器,并调用容器 refresh 方法
  3. refresh 过程中会创建并初始化 SpringMVC 中的重要组件, 例如 MultipartResolver,HandlerMapping,HandlerAdapter,HandlerExceptionResolverViewResolver 等
  4. 容器初始化后,会将上一步初始化好的重要组件,赋值给 DispatcherServlet 的成员变量,留待后用

2、匹配阶段

  1. 用户发送的请求统一到达前端控制器 DispatcherServlet
  2. 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 步的视图渲染

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值