前言
在工作和面试中,Spring作为一个高频出现的框架,不能只是会用,还要对他有一定深入的了解,这样出现问题或者别人跟你交流起Spring的内容的时候,不至于哑口无言,今天我们就来聊聊Spring中我们必须要知道或者掌握的内容
IOC 和AOP
可以说IOC和AOP是Spring的灵魂,我们无时无刻不在用这两个特性,下面就来讲讲IOC和AOP分别都是什么
什么是IOC
IOC(Inversion of Controll)控制反转,什么是控制反转呢?原来需要开发者手动创建对象,现在这个过程交给Spring容器,Spring预先把对象创建好,你需要的时候声明一个属性直接给注入进来,这样把原来创建对象的控制权给反转过来,就叫控制反转,那在这里我们反转的是什么内容呢?实际上这里我们反转的是获取依赖对象的过程,这样其实IOC其实还有另外一个名字,叫依赖注入,这实际上就是Spring来实现IOC的方式,通过提供一个IOC容器,在容器运行期间,利用依赖关系动态地将某种依赖关系注入到对象中。这样的好处就是最大的程度实现对象之间的解耦合。
比如我有一块业务,分别交给了三个人来实现这一块业务中的三个部分,那么只有这三个人的功能组合到一起才能实现这一块业务,那么每个人都必须要清楚自己的那部分业务什么时候要依赖对方的那一部分的业务,什么时候产生交互,这样沟通成本就很高,每个人都要做一次业务串线的操作,而不是专心于自己的业务,那这个时候,假如我们能引入第4个人,由第4个人来负责怎么样组合起这3个人的业务,什么时候交互,那么这三个人就完全不用处理和其他人之间的业务对接,完全专心于自己那一部分的业务开发了,这其实跟SpringIOC容器的思想就是类似的。
什么是AOP
1 | 面向切面的程序设计是计算机科学中的一种程序设计思想,旨在将横切关注点与业务主体进行进一步分离, |
看了维基百科上面的这么长的一段话,想必头都大了,那么到底AOP是什么呢,其实这是一种思想,一种将核心逻辑与边缘逻辑拆分的思想,我们把我们的一块业务进行拆分,把业务强相关的逻辑称为核心逻辑,将不是特别相关的业务逻辑称为边缘逻辑,那么AOP就是把核心逻辑作为关注点,将非核心逻辑也就是边缘逻辑从原有代码剥离出来,形成一个个的切面,在我们的关注点(核心逻辑)前后的连接点插入一系列的切面,将核心逻辑和边缘逻辑通过这种方式编织起来的过程就叫做AOP。这样的好处就是有一些重复的工作像事务管理、日志管理、权限控制等功能封装起来,减少重复代码,并降低模块间的耦合度,有利于系统的拓展性和可维护性。
AOP中的几个重要概念
Advice
切面的工作被称为通知,通知定义了切面是什么以及何时使用,除了描述切面的工作外,通知还解决了何时执行这个工作
JoinPoint 连接点
连接点是指应用执行过程中可以插入切面的一个点
PointCut 切点
一个切面并不需要通知应用所有的连接点,切点有助于缩小切面通知的连接点范围,通知中定义了切面的“什么”和“何时”,而切点则定义了“何处”
Aspect 切面
切面是通知和切点的集合,通知和切点定义了切面的全部内容,它们是什么,在何时何处完成其功能
Introduction 引入
Introduction允许我们向现有的类添加新方法和属性
weaving 织入
织入是将切面应用到目标对象并创建新的代理对象的过程,切面在指定的连接点被织入到目标对象中。在目标对象的生命周期中有很多个点进行织入
- 编译期 切面在目标类编译时被织入。这种方式需要特殊的编译器,AspectJ的织入编译器就是以这种方式织入切面的
- 类加载期 切面在目标类加载到JVM时候被织入,这种方式需要特殊的类加载器,它可以在目标类被引入到应用程序之前,增强该目标类的字节码。AspectJ 5就支持这种方式织入切面
- 运行期 切面在应用运行的某个时刻被织入。一般情况下,在织入切面时候AOP容器会为目标对象动态的创建一个代理对象。SpringAOP就是以这种方式织入切面的。
Spring AOP原理
Spring是在运行时通知对象,通过在代理类中包裹切面,Spring在运行期间把切面织入到Spring管理的Bean中。如下图所示,代理类封装了目标类,并拦被通知方法的调用,再把调用转发给真正的目标bean.当代理拦截到方法调用的时候,在调用bean方法之前,会执行切面逻辑。
Spring在运行时创建代理对象,代理勒种包裹了切面和目标对象,并拦截被通知方法的调用,在执行bean方法之前或之后执行切面逻辑,之后再将请求转发给目标对象
图片引用自Spring AOP 之 通知、连接点、切点、切面
直到应用需要用到被代理的bean时,spring才会创建代理对象,如果是ApplicationContext的话,在ApplicationContext从BeanFactory中加载所有bean的时候,Spring才会创建被代理的对象。因为Spring运行时才创建代理对象,所以我们不需要特殊的编译器就能织入AOP切面
Spring IOC怎么实现的?
IOC实际上是通过在Spring内部持有一个beanfactory 在Spring容器启动的时候将被加上注解的类加载到容器中,当你在使用bean的时候去管Spring的容器拿的一个过程
动态代理和静态代理
要讲动态代理和静态代理,实际上要先从代理模式说起,什么是代理模式呢?代理模式提供了对目标对象的额外访问方式,即通过代理对象访问目标对象这样可以在不修改目标对象的前提下、提供额外的功能操作,扩展目标对象的功能。说完了什么是代理模式,那么静态代理和动态代理分别是什么呢?其实静态代理还是动态代理是从代理类创建的时间节点来看的,静态代理是在程序运行期前,代理类已经存在了(通过developer由ide编写源代码后,对其编译),而动态代理是在程序运行期间才会创建相应的代理类,也就是每需要使用静态代理来对目标对象进行增强的时候,就需要创建一个目标对象的代理类,这样就可能带来很多的代理类
静态代理中的角色
- subject抽象主题类,定义了代理对象和真实对象共同的接口方法,既可以是接口又可以是抽象类
- real subject 真实主题类,也就是被委托类或被代理类,该类定义了代理对象表示的真实对象,实现了subject接口,client通过代理对象间接地调用真实主题类中的方法,由real subject来执行真正的业务逻辑
- proxy subject 代理类,该类也被称为委托类或代理类,该类中持有一个真实主题类的引用,实现了subject接口,在其实现的接口方法中调用真实主题类中对应的接口方法,起到代理的作用
- client 客户端,使用代理
spring中bean的生命周期
- 实例化bean实例及
- 按照spring上下文对实例化的bean设置对象属性(也就是进行IOC注入)
- 如果这个bean实现了BeanNameAware接口,会调用它实现的setBeanName方法,此处传递的就是spring配置文件中id的值
- BeanPostProcessor前置处理
- 是否实现InitialBean接口
- 是否配置自定义的init-method
- BeanPostProcessor后置处理
- 注册
Spring的启动流程
applicationContext继承体系
通过SpringApplication.run启动主类
1 | //启动一个计时器,记录应用启动花费的时间() |
refreshContext
1 | //设置启动时间,启动标志,移除所有类元数据缓存,准备刷新时的应用上下文 |
Spring和SpringBoot的区别
- SpringBoot内嵌了tomcat容器,不用再使用独立的web容器
- SpringBoot相对于Spring来说,使用约定由于配置的原则,不在通过以前那种大量的xml配置和引入一大堆依赖,大大简化了开发人员的工作
- 可以实现自动装配
- 通过一系列的starter,简化maven或gradle中的配置
SpringMVC工作流程
请求发送至DispatcherServlet,遍历所有的handlermapping,通过请求url找到,找到对应的HandlerExecutionChain,并看是否有前置拦截器,有的话则执行,通过handlermapping执行对应controller方法返回modelAndView给DispatcherServlet,最终返回给客户端
AbstractHandlerMethodMapping#mappingRegistry#mappingLookUp是一个hashMap,存放了url到 类全名+方法名的映射,当请求到达的时候会通过url去这个找到对应的Controller中对应的方法
Spring Cloud中用过哪些组件
注册中心 配置中心 网关
eureka、zookeeper做注册中心有什么区别
feign是如何保证负载均衡和熔断策略的
常用负载均衡策略
Round Robin轮询 将请求依次分配给后端服务,实现简单,请求均匀分配
Weight Round Robin加权轮询 一种改进的轮询算法 在服务上自行设置权值,按比例进行分配请求数的比例
Random 将请求随机分配给后端服务
Hash 根据ip、url或其他信息计算hash值或md5,然后按照服务器数量取模
Docker swarm 负载均衡是怎么处理的
docker swarm它是有路由网格 service mesh提供的一个负载均衡的功能,在docker内部是通过linux 的ipvs(内核级的4层负载均衡)来实现的
Docker swarm的服务发现
在部署一个docker服务时,会在docker内置的DNS服务器中注册服务名+VIP(虚拟IP),我们在通过服务名去请求某一个服务的时候,会去docker的DNS服务器中解析并返回目标容器的虚拟IP,然后再docker网络内部进行路由,最终请求被发送到目标服务