15、【对线面试官】Spring基础
要不你来讲讲Spring的IOC和AOP你是怎么理解的呗?
- 我个人理解下:SpringIOC解决的是对象管理和对象依赖的问题。
- 本来是我们自己手动new出来的对象,现在则把对象交给Spring的IOC容器管理
- IOC容器可以理解为一个对象工厂,我们都把该对象交给工厂,工厂管理这些对象的创建以及依赖关系
- 等我们需要用对象的时候,从工厂里边获取就好了
哦,你说的就是「控制反转」和「注入依赖」吧?
- 我认为「控制反转」指的就是:把原有自己掌控的事交给别人去处理
- 它更多的是一种思想或者可以理解为设计模式
- 比如:本来由我们自己new出来的对象,现在交由IOC容器,把对象的控制权交给它方了.
- 而「依赖注入」在我的理解下,它其实是「控制反转」的实现方式
- 对象无需自行创建或者管理它的依赖关系,依赖关系将被「自动注入」到需要它们的对象当中去
嗯,那我想问问,用SpringIOC有什么好处吗?
或者换个问法:本来我可以new出来的对象,为什么我要交由Spring IOC容器管理呢?
- 主要的好处在于「将对象集中统一管理」并且「降低耦合度」
- 如果面试官理解了「工厂模式」,那就知道为什么我们不直接new对象
- 要说理由的话,可以举很多例子,比如说:
- 我用SpringIOC可以方便单元测试、对象创建复杂、对象依赖复杂、单例等等的,什么都可以交给Spring IOC
- 理论上自己new出来的都可以解决上面的问题,Spring在各种场景组合下有可能不是最优解
- 但new出来的你要自己管理,可能你得自己写工厂,得实现一大套的东西才能满足需求
- 写着写着有可能还是Spring的那一套
- 但现在Spring现在已经帮你实现了啊!
- 如果项目里的对象都是就new下就完事了,没有多个实现类,那没事,不用Spring也没啥问题
- 并且Spring核心不仅仅IOC啊,除了把对象创建出来,还有一整套的Bean生命周期管理
- 比如说你要实现对象增强,AOP不就有了吗?不然你还得自己创建代理
那你继续来聊下Spring AOP呗?
- Spring AOP解决的是非业务代码抽取的问题
- AOP底层的技术是动态代理,在Spring 内实现依赖的是BeanPostProcessor
- 比如我们需要在方法上注入些「重复性」的非业务代码,就可以利用Spring AOP
- 所谓的「面向切面编程」在我理解下其实就是在方法前后增加非业务代码
那你在工作中实际用到过AOP去优化你的代码吗?
- ·有的。当时我用AOP来对我们公司现有的监控客户端进行封装
- 一个系统离不开监控,监控基本的指标有QPS、RT、ERROR等等
- 对外暴露的监控客户端只能在代码里写对应的上报信息(灵活,但会与业务代码掺杂在一起)
- 于是我利用注解+AOP的方式封装了一把,只要方法/类上带有我自定义的注解
- 方法被调用时,就会上报AQS、RT等信息实现了非业务代码与业务代码分离的效果
了解,你们项目一般是怎么把对象交给IOC容器管理的?
换个问法:一般是怎么定义Bean的?
- Spring提供了4种方式,分别是: 1):注解2):XML3):JavaConfig 4):基于Groovy 的DSL配置
- 一般项目我们用注解或XML比较多,少部分用JavaConfig
- 日常写业务代码一般用注解来定义各种对象,责任链这种一般配置在XML「注解」解决不了的就用JavaConfig
- 总体而言,还是得看项目的代码风格吧
- 反正就是定义元数据,能给到Spring解析就好了
要不来聊聊你使用Spring的感受?
- 当我还是初学Spring的时候,我觉得Spring很麻烦,需要有一大堆的配置信息才能跑起来
- 光是搭建环境就需要耗费我好长的时间
- 毕竟版本冲突,依赖冲突什么的就可能个下午就过去了
- 但毕竟一个系统环境只搭一次嘛,所以还好(后来用上了SpringBoot这又更方便了)
- 回来,IOC和AOP在工作用的时候还是很爽的
- 毕竟搞个注解什么的,配置下就可以把对象交给Spring管理了
- 配合Spring的生态,@Transactional注解什么的,都好用得飞起
- 不过,Spring给我们封装得太好了
- 经常就会有奇奇怪怪的"bug"出现,也踩过很多的坑了
- Bean经常没办法创建成功,导致项目启动失败..
- 对象的循环依赖问题.
- 同一个接口,多个实现,识别不出我要创建哪个对象.
- 为什么catch了异常,Spring事务为什么还会自动回滚..
- 等等等
循环以来
到这里,Spring整个解决循环依赖问题的实现思路已经比较清楚了。对于整体过程,读者朋友只要理解两点:
- Spring是通过递归的方式获取目标bean及其所依赖的bean的;
- Spring实例化一个bean的时候,是分两步进行的,首先实例化目标bean,然后为其注入属性。
结合这两点,也就是说,Spring在实例化一个bean的时候,是首先递归的实例化其所依赖的所有bean,直到某个bean没有依赖其他bean,此时就会将该实例返回,然后反递归的将获取到的bean设置为各个上层bean的属性的。