(1)Spring 中应用了哪些设计模式?
使用了如下设计模式:
(1)工厂模式:Spring 的 IOC 容器是一个大工厂,所有 Bean 创建都在这个工厂里。
(2)代理模式:SpringAOP 的实现,就用了代理模式,实现对方法的增强。
(3)单例模式:Spring 中 Bean 对象的创建,默认是单例的。
(4)模板模式:Spring 中封装了一些框架工具,如 RedisTemplate,是模版模式的表现。
(5)观察者模式:用 Spring Context 发异步消息,内部维护了一个监听者集合,发布事件时通知所有监听者,这是观察者模式的体现。
(6)适配器模式:@RestControllerAdvice 注解,使用到了适配器模式。
(7)策略模式:Spring 中有一个 Resource 接口,它的不同实现类,会根据不同的策略去访问资源。
参看博客:如何使用Spring Context实现消息队列、统一异常处理
(2)说下什么是 IOC ?什么是 DI ?
IOC 是一种容器思想,
假如没有 IOC,我们开发中需要频繁创建 Bean 对象,而这些 Bean 对象大多是可以复用的,
手动创建效率太低,耦合高,不利于扩展和维护。
而用了 IOC 容器,可将 Bean 的创建和管理交给 IOC 容器,
我们只需要关注 Bean 的依赖关系,需要时直接注入(DI),非常方便。
(3)说下 Spring Bean 生命周期?
实例化 => 属性赋值 => 初始化 => 使用 => 销毁
(4)Spring 中的单例 Bean 会有线程安全问题吗?
不会,虽然 Spring 中默认创建的 Bean 是单例,被所有线程所共享的,
但 Spring 中 Controller、Service、DAO 等,这些 Bean 大多是无状态的,
就是说不会存储数据,也就不存在线程安全问题。
如果需要保证安全的话,
(1)可以修改 Bean 的创建方式为非单例,这样每一个线程都会创建一个新的 Bean;
(2)或者将 Bean 保存到 ThreadLocal 中;
(5)说说循环依赖是什么?
循环依赖是自己依赖自己,或者与其他的 Bean 互相依赖,你的创建依赖于我,我的创建依赖于你。
因为 Spring 有三级缓存的存在,可以解决属性注入方式的循环依赖。
(6)Spring 是怎么解决循环依赖的?
是通过三级缓存的方式解决的,如 A、B 循环依赖,
(1)实例化 A,但由于 A 依赖 B,所以 A 实例化不完整,会先放在二级缓存中,暴露出来。
(2)实例化 B,发现 B 依赖 A,依次从一级缓存到三级缓存中找 A,结果会在二级缓存中找到 A,
(3)虽然 A 不太完善,但是存在,于是 B 实例化成功,放入到一级缓存中。
(4)最后实例化 A,A 依赖于 B,逐级查找,会在一级缓存中找到实例化完成后的 B,A 实例化完成,
(5)将 A 放入到一级缓存中,删除二级缓存中的A。
(6)A、B 的实例化就都完成。
需要注意,这种情况只接受属性注入的方式,如果 AB 均采用构造器注入或者 A 采用构造器,B 采用属性注入,都无法支持。
(7)如果是被 final 修饰的类要用哪种注入方式?
构造器注入
(8)说下 Spring 三级缓存?
Singleton Object Cache(一级缓存):存储已经创建完成的 Bean 单例对象;
Early Singleton Object Cache(二级缓存):存储早期创建的 Bean 单例对象
Singleton Factories Cache(三级缓存):存储用于创建单例 Bean 的 ObjectFactory。
(9)为什么要三级缓存?二级不行吗?
不行,因为要考虑到代理对象。
如果没有代理对象,二级缓存是可以的,但因为存在代理对象,如果只有二级缓存,在 Bean 初始化过程中,
生成的代理对象会覆盖掉二级缓存中的普通对象,可能会导致取到的 Bean 对象不一致。
(10)说说什么是 AOP ?
AOP 是面向切面编程,是使用动态代理实现的可对方法进行增强的技术。
有前置、返回、异常、最终和环绕通知。
参看博客:AOP技术
(11)你平时有用到 AOP 吗?怎么用的?
有用到,
(1)接口防抖,防止短时间内频繁调用接口;
(2)记录接口的访问信息,方便以后做统计;
(3)接口鉴权;
(4)参数校验,如 Valid 框架,自定义参数校验器;
用 AOP 需要注意两点,
(1)对于 AOP 中的异常不要进行捕获,否则增强方法的事务会失效;
(2)其次,对于 AOP 中的增强方法,最终通知要先于返回通知执行,这点在设计时需要注意。
参看博客:使用AOP处理参数、使用AOP记录请求日志实现
(12)说说 JDK 动态代理和 CGLIB 代理 ?
(1)JDK 动态代理,是 Java 自带的代理机制,
通过实现 InvocationHandle 接口并使用 Proxy 类的 newProxyInstance 方法,
可在运行时动态生成代理类,要求被代理的类必须实现至少一个接口。
(2)CGLIB 代理,是基于字节码操作的代理机制,
通过继承被代理类,并覆写方法来实现代理功能。
CGLIB 动态代理可以代理没有实现接口的类,
它在运行时生成被代理类的子类,可以使用第三方库。
使用场景:
(1)CGLIB 动态代理创建的对象性能高,所花时间多,推荐用来创建单例对象,反之,使用 JDK 动态代理;
(2)JDK 动态代理要求被代理类至少实现一个接口,CGLIB 没有这个要求,所以也可以按照这个来选择。
(3)CGLIB 动态代理是通过继承来实现的,所以如果类是用 final 修饰的,那么就不能使用 CGLIB 动态代理
(13)说下 Spring 事务的传播机制?
事务的传播机制,是多个事务方法,互相调用时,事务的行为机制。
通过在 @Transational 注解中的 propagation 属性值设置,常用的值有:REQUIERD(默认)、SUPPORTS、REQUIRED_NEW。
假设目前有两个方法,方法 A 和方法 B,方法 A 中调用了方法 B。
默认情况下:如果 A 有事务,则调用的 B 加入到 A 的事务,两个方法有一个发生异常都发生回滚;
SUPPORTS:如果 A 有事务,则调用的 B 也按照事务执行,如果没有,则按非事务执行;
REQUIRED_NEW:无论 A 有没有加事务, B 都有事务,且 A 的异常不影响 B 的事务执行;
需要注意,这个事务的传播行为,是设置在被调用方的,即方法 B 上。
参看博客:SpringBoot项目的事务实现
(14)Spring 的声明式事务在哪些情况下会失效?
(1)手动捕获了异常;
(2)不在回滚异常内的异常,默认回滚异常是 Runtime Exception;
(3)非 public 修饰的方法上;
(4)事务传播没有设置好;
(5)在类里面调用本类成员方法,没有通过代理对象,事务会失效;
(6)没有交给 IOC 容器所管理的类;
(7)异步调用;
(8)数据库不支持事务;
(15)讲下 Spring MVC 的工作流程?
(1)用户发送的请求先到前置控制器(DispacherServlet);
(2)前置控制器访问处理映射器,查询对应的 handler,处理器映射器里存储了接口的请求类型,接口地址;
(3)处理映射器返回对应的处理器执行链;
(4)前置控制器请求处理适配器执行,在这里进行接口参数的封装;
(5)处理适配器,找对应的处理器(Controller) 执行;
(6)执行器执行完成后返回 ModeAndView 对象;
(7)处理器适配器将 ModeAndView 对象返回给前置控制器;
(8)前置控制器,请求视图解析器进行视图解析;
(9)视图解析器返回对应的 View 对象
(10)渲染 View 对象
(11)返回给前端
需要注意,
(1)不是所有请求都会走完整的流程,当 Controller 返回的不是 ModeAndView,而是 Json 时,前置控制器接收后,会直接返回前端。
(2)在请求处理器适配器执行前,中间会先执行拦截器,如果代码中有的话。

(16)SpringBoot 的自动配置原理了解吗?
在依赖(Jar 包)的静态资源目录(Resource 目录)的 META-INF 文件夹下有一个 spring.factories 文件,
该文件里是 Bean 清单,都是类的全限定类名。
在项目启动时,Spring Boot 会自动有选择性的创建这些 Bean 对象,放到 IOC 容器中。
之所以说有选择性,是配合了一些选择性装配的注解,如下:
@ConditionalOnProperty:有配置才装配;
@ConditionalOnClass:有字节码文件才装配;
@ConditionalOnBean:有 Bean 才装配;
@ConditionalOnJava:符合版本的 Java 才装配;

参看博客:Spring Boot中选择性加载Bean的几种方式
(17)Spring Cloud 中有哪些常用组件?它们的作用是什么?
Gateway:所有微服务的入口;
Nacos:注册中心、配置中心;
MQ:微服务之间的异步调用;
Dubbo/Feign:微服务之间的通信;
Sentinel/hystrix:微服务保护;
(18)讲下 Spring Boot 的启动流程呢?
(1)加载配置文件
(2)创建应用程序上下文
(3)自动装配 Bean
(4)扫描组件
(5)启动应用程序
(19)Feign 和 Dubbo 有什么区别和共同点?
【相同点】:都依赖注册中心。
【不同点】:
Dubbo: 支持多传输协议,默认 Dubbo 协议,长连接,适合高并发场景;
Feign: 基于 Http 传输协议,短连接,不适合高并发的访问;
参看博客:Feign技术、Dubbo+Zookeeper使用
(20)网关和 Gateway 的区别?
网络层面上的网关是一种网络硬件设施或者软件,它的作用是连接不同类型、不同协议的网络,充当网络之间的桥梁。
常见的硬件网关有路由器,软件网关有代理服务器。
Gateway 是微服务架构的组件,是微服务请求的统一入口,可以实现请求转发、权限控制和限流。
(21)Nacos配置热更新的原理
Nacos Server 和 Nacos 客户端采用的是长轮询的方式,
服务端数据没有发生改变时,会持续连接,直到服务端的数据发生变化,或者连接超时的时候才会返回。
Nacos 配置热更新,存在一个比较服务端配置与客户端配置的过程,如果配置项过多,会导致比较的时间很长,配置同步效率变低。
对此,Nacos 采用了两点优化:
(1)分片,将配置按每 3000 个配置项分片,多次比较;
(2)分阶段比较更新,客户端把配置发给服务端,服务端将其中值有变化的配置 Key 返回给客户端,客户端拿到这些有变化的配置 Key,再循环逐个去获取服务端上新的 value,更新本地配置。也就是说配置的比较和修改后的配置值不是一次完成的。
参看博客:Nacos技术
(22)说下 Http 中的状态码呢?
http 状态码标识请求的状态,常见的状态码及含义如下:
2xx 成功:200(成功)、204(不含主体部分)、206(范围请求);
3xx 重定向:301(永久性重定向)、302(临时性重定向);
4xx 客户端错误:400(请求报文存在语法错误)、401(认证失败)、403(请求被拒绝)、404(没找到资源);
5xx 服务器错误:500(服务器错误)、503(服务不可用);
参看博客:HTTP状态码与首部字段