Spring 初级面试题

小可大魔王的个人博客 冲!~~ 本文由博客端 http://xiaokedamowang.cn 主动推送

Spring 初级面试题

一. 什么是 IOC

什么是 IOC: IOC 是一种设计思想, 对象的管理方式由程序员控制变成了由框架控制, 从创建到销毁都是框架管理

spring 是通过什么方式来实现 IOC 的: 通过 DI(依赖注入实现)

什么是 DI: DI, 依赖注入, 就是把对应的属性注入到具体的对象中

二. 什么是 AOP

AOP 是一种编程思想, 是对 OOP 面向对象编程的补充, 目前比较常见的 AOP 方式有 2 种, JDK 自带的动态代理和 CGlib, 我们需要知道一些 AOP 的术语

  • 通知(Advice): 就是我们写的增强对象的代码
  • 连接点(JoinPoint): 可以被切入的点, 如方法调用前, 方法调用后, 方法返回, 方法抛出异常等
  • 切点(PointCut): 真正被你选中的连接点, 那么多连接点, 你并不是所有的地方需要增强
  • 切面(Aspect): 切面是通知和切点的结合, 把通知应用到切点的过程叫切面

三. 什么是 BeanDefinition

我们需要 spring 帮我们管理对象, 起码让 spring 知道我们哪些对象需要被管理, 是否需要单例, 用哪个构造方法创建, 初始化方法是什么 有很多很多东西需要让 spring 知道, 这些东西就封装成 1 个对象, BeanDefinition 就是这种对象实现的接口, 定义了很多获取 Bean 信息的方法

四. BeanFactory 和 FactoryBean

  1. BeanFactory

    BeanFactory 是一个接口, 定义了一些基本的方法, springIOC 容器实现了 BeanFactory 接口, 所以可以理解为他就是 spring 容器, 常用的实现类有: ApplicationContext, DefaultListableBeanFactory, ClassPathXmlApplicationContext, AnnotationConfigApplicationContext

  2. FactoryBean

    FactoryBean 也是一个接口, 他定义了 1 个 getObject 的方法, 如果需要容器创建的类实现了这个接口, 那么容器会把调用 getObject 方法返回的对象和自己都加入容器, FactoryBean 的名称是在 getObject 名称前面加上 $, 创建比较复杂的对象时, 适合用 FactoryBean

五. 后置处理器(PostProcessor)

  1. BeanFactoryPostProcessor

    BeanFactoryPostProcessor 是用来增强容器的, 里面定义了方法 postProcessBeanFactory 方法, 参数是 ConfigurableListableBeanFactory, 这个类实现了 BeanFactory, 传进来的就是 spring 容器本身, 你可以修改里面的 BeanDefinition, 或者手动创建 1 个 BeanDefinition 塞进容器

    postProcessBeanFactory 在所有的 BeanDefinition 加载完成后调用

  2. BeanPostProcessor

    BeanPostProcessor 是用来增强或处理 Bean 的, 在初始化方法前调用 postProcessBeforeInitialization 方法, 在初始化方法后面调用 postProcessAfterInitialization

六. spring 的创建流程

从 new AnnotationConfigApplicationContext()开始

  1. 父类无参构造方法创建 DefaultListableBeanFactory
  2. 加载配置类, 创建 spring 自身启动需要的对象的 BeanDefinition, 如: 一些后置处理器, 创建需要被 spring 管理的 Bean 的 BeanDefinition
  3. refresh 方法被调用
    1. BeanFactory 的预准备工作, 如:设置类的加载器, 表达式解析器, 加载系统的环境变量等
    2. 先执行 BeanDefinitionRegistryPostProcessor, 再执行 BeanFactoryPostProcessor(根据优先级接口, 或排序接口, 顺序执行)
    3. 创建 BeanPostPreocessor
    4. 执行 BeanPostProcessor(根据优先级接口, 或排序接口, 顺序执行)
    5. 创建事件派发器, 并且注入到容器
    6. 创建所有的监听器, 并绑定到事件派发器中, 然后派发之前步骤产生的事件
    7. finishBeanFactoryInitialization 方法被调用, 初始化剩余的 Bean
      1. 获取剩下的所有的 BeanDefinition
      2. 如果不是抽象的, 并且是单例的, 并且不是懒加载的, 就创建
        1. 如果是 BeanFactory, 调用 getObject 方法
        2. 否则就调用 getBean 方法, getBean 方法会调用 doGetBean 方法 标记点: getBean
          1. 先从缓存中获取 Bean, 判断是否有
          2. 如果没有, 创建 Bean
            1. 获取当前 Bean 依赖的其他 Bean
            2. 如果有依赖的 Bean, 调用 getBean 方法先创建依赖的 Bean, 走递归, 递归点: getBean
            3. 没有依赖, 就调用 createBean
            4. 判断是否需要自定义创建
              1. 调用 resolveBeforeInstantiation 方法, 执行 InstantiationAwareBeanPostProcessor 类型的后置处理器, 返回自定义对象
            5. 不需要自定义就调用, doCreateBean
              1. 调用 createBeanInstance, 创建 Bean 实例
              2. 调用 populateBean, 给对象属性赋值**(使用三级缓存解决循环依赖)**
              3. 调用 Aware 接口的方法
              4. 调用后置处理器的 postProcessBeforeInitialization 方法
              5. 执行初始化方法
              6. 调用后置处理器的 postProcessAfterInitialization 方法**(AOP 在此处完成)**
              7. 注册 Bean 的销毁方法
            6. 把创建的对象塞进容器

七. Bean 的生命周期

  1. 实例化 Bean
  2. 设置对象属性
  3. 调用 Aware 接口方法
  4. BeanPostProcessor 前置处理方法
  5. Bean 的初始化方法
  6. BeanPostProcessor 后置处理方法
  7. 使用中...
  8. 销毁

八. 循环依赖

spring 使用三级缓存解决循环依赖

// 一级缓存
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 三级缓存 三级缓存value spring扔进去的是一个lambda表达式 
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
// 二级缓存
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
// 扔进三级缓存的lambda表达式
if (earlySingletonExposure) {
   if (logger.isTraceEnabled()) {
       logger.trace("Eagerly caching bean '" + beanName +
                    "' to allow for resolving potential circular references");
   }
   // 这里就是扔进去的lambda表达式
   addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

使用三级缓存是为了解决 AOP 的循环依赖

如果没有 AOP, 二级缓存就可以解决

下图是大概的过程: 并不完整, 为了方便理解, 少了正常 AOP 的过程

image-20210817044637223

九. spring 事务原理

spring 通过 AOP 实现动态代理, 通过 ThreadLocal 存取数据库连接, 实现事务

十. 事务传播

常量名称 常量解释
PROPAGATION_REQUIRED(默认) 如果没有, 就新建事务, 如果有就加入
PROPAGATION_REQUIRES_NEW 不管外面有没有, 都创建新的事务 (多数据源的时候使用, 下面其他的我都没用过)
PROPAGATION_SUPPORTS 如果有事务, 就加入, 如果没有, 不创建
PROPAGATION_MANDATORY 必须运行在事务中, 如果没有事务, 就报错
PROPAGATION_NOT_SUPPORTED 表示该方法不应该运行在事务中, 如果外面有事务, 挂起该事务
PROPAGATION_NEVER 表示该方法不应该运行在事务中, 如果外面有事务, 直接报错
PROPAGATION_NESTED 如果没有事务, 开启新事物, 如果有事务, 开启子事务, 事务嵌套, 必须等外面的事务完成才能提交 (具体用途我不清楚)

十一. 事务失效

  1. 方法不是 public

  2. 抛出异常类型错误, 或者被吃掉了

  3. 没有开启事务的 A 方法调用了开启事务的 B 方法, 事务会失效

    代理对象内部有 1 个属性是原始对象, 代理对象的 A 方法没有事务,B 方法有事务, 调用过程如下

    调用代理对象的 A 方法(没有事务), 代理对象的 A 方法调用原始对象的 A 方法, 原始方法的 A 方法内部调用了 B 方法(此时的 B 方法是原始对象的), 所有事务失效

十二. 哪些设计模式

十三. spring 整合 mybatis 原理

  1. 扫描包, 获取全部的接口
  2. 注入 1 个 ImportBeanDefinitionRegistrar, 通过 registerBeanDefinitions 方法, 注册 BeanFactory 类型的 BeanDefinition
  3. 通过 BeanFactory, 然后在 getObject 调用 sqlSession.getMapper(Class clazz)生成代理对象

springmvc 顺便就和 spring 放在一起了

十四. springmvc 执行流程

  1. 请求先进入 DispachServlet
  2. 通过 URL 去 HandlerMappings 查找对应的 HandlerExecutionChain
  3. 基于 HandlerExecutionChain 查找适配器 HandlerAdapter
  4. 先调用拦截器 pre 方法
  5. 然后再执行 HandlerAdapter(这里执行之后就会调用我们写的 Controller 里的方法)返回 View 对象
  6. 再调用拦截器 post 方法
  7. 视图解析器 ViewResolver 找到对应的视图 View
  8. 渲染视图 View, 填充数据
  9. 最后调用拦截器 after 方法
  10. 返回 View
  • 面试

    面试造航母,上班拧螺丝。多面试,少加班。

    317 引用 • 1390 回帖 • 9 关注
  • Spring

    Spring 是一个开源框架,是于 2003 年兴起的一个轻量级的 Java 开发框架,由 Rod Johnson 在其著作《Expert One-On-One J2EE Development and Design》中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 JavaEE 应用程序开发提供集成的框架。

    905 引用 • 1446 回帖 • 539 关注
1 操作
xiaokedamowang 在 2021-08-18 00:38:31 更新了该帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...