本文 首发于 🍀 永浩转载 请注明 来源

15、【对线面试官】Spring基础

要不你来讲讲Spring的IOC和AOP你是怎么理解的呗?

  1. 我个人理解下:SpringIOC解决的是对象管理和对象依赖的问题。
  2. 本来是我们自己手动new出来的对象,现在则把对象交给Spring的IOC容器管理
  3. IOC容器可以理解为一个对象工厂,我们都把该对象交给工厂,工厂管理这些对象的创建以及依赖关系
  4. 等我们需要用对象的时候,从工厂里边获取就好了

哦,你说的就是「控制反转」和「注入依赖」吧?

  1. 我认为「控制反转」指的就是:把原有自己掌控的事交给别人去处理
  2. 它更多的是一种思想或者可以理解为设计模式
  3. 比如:本来由我们自己new出来的对象,现在交由IOC容器,把对象的控制权交给它方了.
  4. 而「依赖注入」在我的理解下,它其实是「控制反转」的实现方式
  5. 对象无需自行创建或者管理它的依赖关系,依赖关系将被「自动注入」到需要它们的对象当中去

嗯,那我想问问,用SpringIOC有什么好处吗?

或者换个问法:本来我可以new出来的对象,为什么我要交由Spring IOC容器管理呢?

  1. 主要的好处在于「将对象集中统一管理」并且「降低耦合度」
  2. 如果面试官理解了「工厂模式」,那就知道为什么我们不直接new对象
  3. 要说理由的话,可以举很多例子,比如说:
  4. 我用SpringIOC可以方便单元测试、对象创建复杂、对象依赖复杂、单例等等的,什么都可以交给Spring IOC
  5. 理论上自己new出来的都可以解决上面的问题,Spring在各种场景组合下有可能不是最优解
  6. 但new出来的你要自己管理,可能你得自己写工厂,得实现一大套的东西才能满足需求
  7. 写着写着有可能还是Spring的那一套
  8. 但现在Spring现在已经帮你实现了啊!
  9. 如果项目里的对象都是就new下就完事了,没有多个实现类,那没事,不用Spring也没啥问题
  10. 并且Spring核心不仅仅IOC啊,除了把对象创建出来,还有一整套的Bean生命周期管理
  11. 比如说你要实现对象增强,AOP不就有了吗?不然你还得自己创建代理

那你继续来聊下Spring AOP呗?

  1. Spring AOP解决的是非业务代码抽取的问题
  2. AOP底层的技术是动态代理,在Spring 内实现依赖的是BeanPostProcessor
  3. 比如我们需要在方法上注入些「重复性」的非业务代码,就可以利用Spring AOP
  4. 所谓的「面向切面编程」在我理解下其实就是在方法前后增加非业务代码

那你在工作中实际用到过AOP去优化你的代码吗?

  1. ·有的。当时我用AOP来对我们公司现有的监控客户端进行封装
  2. 一个系统离不开监控,监控基本的指标有QPS、RT、ERROR等等
  3. 对外暴露的监控客户端只能在代码里写对应的上报信息(灵活,但会与业务代码掺杂在一起)
  4. 于是我利用注解+AOP的方式封装了一把,只要方法/类上带有我自定义的注解
  5. 方法被调用时,就会上报AQS、RT等信息实现了非业务代码与业务代码分离的效果

了解,你们项目一般是怎么把对象交给IOC容器管理的?

换个问法:一般是怎么定义Bean的?

  1. Spring提供了4种方式,分别是: 1):注解2):XML3):JavaConfig 4):基于Groovy 的DSL配置
  2. 一般项目我们用注解或XML比较多,少部分用JavaConfig
  3. 日常写业务代码一般用注解来定义各种对象,责任链这种一般配置在XML「注解」解决不了的就用JavaConfig
  4. 总体而言,还是得看项目的代码风格吧
  5. 反正就是定义元数据,能给到Spring解析就好了

要不来聊聊你使用Spring的感受?

  1. 当我还是初学Spring的时候,我觉得Spring很麻烦,需要有一大堆的配置信息才能跑起来
  2. 光是搭建环境就需要耗费我好长的时间
  3. 毕竟版本冲突,依赖冲突什么的就可能个下午就过去了
  4. 但毕竟一个系统环境只搭一次嘛,所以还好(后来用上了SpringBoot这又更方便了)
  5. 回来,IOC和AOP在工作用的时候还是很爽的
  6. 毕竟搞个注解什么的,配置下就可以把对象交给Spring管理了
  7. 配合Spring的生态,@Transactional注解什么的,都好用得飞起
  8. 不过,Spring给我们封装得太好了
  9. 经常就会有奇奇怪怪的"bug"出现,也踩过很多的坑了
  10. Bean经常没办法创建成功,导致项目启动失败..
  11. 对象的循环依赖问题.
  12. 同一个接口,多个实现,识别不出我要创建哪个对象.
  13. 为什么catch了异常,Spring事务为什么还会自动回滚..
  14. 等等等

循环以来

  1. 到这里,Spring整个解决循环依赖问题的实现思路已经比较清楚了。对于整体过程,读者朋友只要理解两点:

    • Spring是通过递归的方式获取目标bean及其所依赖的bean的;
    • Spring实例化一个bean的时候,是分两步进行的,首先实例化目标bean,然后为其注入属性。

    结合这两点,也就是说,Spring在实例化一个bean的时候,是首先递归的实例化其所依赖的所有bean,直到某个bean没有依赖其他bean,此时就会将该实例返回,然后反递归的将获取到的bean设置为各个上层bean的属性的。