1、为什么在java中对象中属性依赖不会出现循环依赖的现象而在spring就会出现循环依赖?
对于java中的对象来说,只需要一步简单的实例化,就会产生一个对象,而一旦这个对象被产生,就可以让别人使用。
而在spring中的bean对象来说,需要经过完整的生命周期之后(而实例化一个对象只是生命周期),才能将该bean对象放到单例池中,别人才能使用。
说白了,如果bean的生命周期能够像普通的java对象的生命周期来说,就不会出现循环依赖。
2、如何解决循环依赖的?
第一,先实例A对象,同时会创建ObjectFactory对象存入三级缓存singletonFactories
第二,A在初始化的时候需要B对象,这个走B的创建的逻辑
第三,B实例化完成,也会创建ObjectFactory对象存入三级缓存singletonFactories
第四,B需要注入A,通过三级缓存中获取ObjectFactory来生成一个A的对象同时存入二级缓存,这个是有两种情况,一个是可能是A的普通对象,另外一个是A的代理对象,都可以让ObjectFactory来生产对应的对象,这也是三级缓存的关键
第五,B通过从通过二级缓存earlySingletonObjects 获得到A的对象后可以正常注入,B创建成功,存入一级缓存singletonObjects
第六,回到A对象初始化,因为B对象已经创建完成,则可以直接注入B,A创建成功存入一次缓存singletonObjects
第七,二级缓存中的临时对象A清除
3、那如果只有一级缓存和三级缓存是否可行?
不行的,每次从三级缓存中拿到ObjectFactory对象,执行getObject()方法又会产生新的代理对象,因为A是单例的,所有这里我们要借助二级缓存来解决这个问题,将执行了objectFactory.getObject()产生的对象放到二级缓存中去,后面去二级缓存中拿,没必要再执行一遍objectFactory.getObject()方法再产生一个新的代理对象,保证始终只有一个代理对象。
所以如果没有AOP的话确实可以两级缓存就可以解决循环依赖的问题,如果加上AOP,两级缓存是无法解决的,不可能每次执行objectFactory.getObject()方法都给我产生一个新的代理对象,所以还要借助另外一个缓存来保存产生的代理对象。
SpringMvc
1、简单的谈一下SpringMVC的工作流程?
- 用户发送请求至前端控制器DispatcherServlet
- DispatcherServlet收到请求调用HandlerMapping处理器映射器。
- DispatcherServlet再把请求提交到对应的Controller
- Controller进行业务逻辑处理后返回一个ModelAndView
- DispatcherServlet将ModelAndView传给ViewReslover视图解析器
- ViewReslover解析后返回具体View 对象
- DispatcherServlet根据View进行渲染视图
- 响应用户
篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题
需要全套面试笔记【点击此处即可】免费获取
2、说出Spring或者SpringMVC中常用的5个注解,并解释含义
@Component 基本注解,标识一个受Spring管理的组件
@Controller 标识为一个表示层的组件
@Service 标识为一个业务层的组件
@Repository 标识为一个持久层的组件
@Autowired 自动装配
@Qualifier(“”) 具体指定要装配的组件的id值
@RequestMapping() 完成请求映射
@PathVariable 从请求路径下中获取请求参数(/user/{id}),传递给方法的形式参数
@RequestBody:注解实现接收http请求的json数据,将json转换为java对象。
@ResponseBody:注解实现将controller方法返回对象转化为json对象响应给客户端。
@RequestParam:指定请求参数的名称
3、简述SpringMVC中如何返回JSON数据
Step1:在项目中加入json转换的依赖,例如jackson,fastjson,gson等
Step2:在请求处理方法中将返回值改为具体返回的数据的类型, 例如数据的集合类List等
Step3:在请求处理方法上使用@ResponseBody注解
4、Spring MVC怎么实现统一异常处理?
开发一个全局异常处理器需要使用到两个注解:@Controlleradvice 、@ ExceptionHandler
Mybatis
1、MyBatis中 #{}和${}的区别是什么?
#{}是预编译处理,KaTeX parse error: Expected 'EOF', got '#' at position 21: …串替换; Mybatis在处理#̲{}时,会将sql中的#{}替…{}时,就是把${}替换成变量的值;
使用#{}可以有效的防止SQL注入,提高系统安全性。
${}使用场景:
1、根据参数获取表名
2、Group by 分组列
3、Order by 排序列和排列类型
防止sql注入?
1、检查变量数据类型和格式
2、过滤特殊符号
3、绑定变量,使用预编译语句
2、Mybatis 中一级缓存与二级缓存?
(1)MyBatis的缓存分为一级缓存和 二级缓存。
一级缓存是基于 PerpetualCache 的 HashMap 本地缓存,默认开启。作用域是sqlsession级别的,同一个sqlsession中执行相同的sql查询(相同的sql和参数),第一次会去查询数据库并写到缓存中,第二次从一级缓存中取。
二级缓存是基于NameSpace和Mapper级别的缓存,多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句,手动开启。针对同一个sqlSessionFactory。
多个sqlsession执行sql
(2)缓存的查找顺序:二级缓存 => 一级缓存 => 数据库
3、MyBatis如何获取自动生成的(主)键值?
在mapper的标签中使用 useGeneratedKeys 和 keyProperty 两个属性来获取自动生成的主键值。
示例:
1.<insert id=”insertname” usegeneratedkeys=”true” keyproperty=”id”>
2. insert into names (name) values (#{name})
3.</insert>
AI写代码sql
- 1
- 2
- 3
MyBatis框架提供了insert标签的属性:
useGeneratedKeys:是否使用自动增长主键
keyProperty:获取自动增长的主键值,储存在Employee对象的哪个字段中
4、简述Mybatis的动态SQL,列出常用的6个标签及作用
动态SQL是MyBatis的强大特性之一 基于功能强大的OGNL表达式。
动态SQL主要是来解决查询条件不确定的情况,在程序运行期间,根据提交的条件动态的完成查询
常用的标签:
<if> : 进行条件的判断
<where> :在<if>判断后的SQL语句前面添加WHERE关键字,并处理SQL语句开始位置的AND 或者OR的问题
<trim>:可以在SQL语句前后进行添加指定字符 或者去掉指定字符.
<set>: 主要用于修改操作时出现的逗号问题
<choose> <when> <otherwise>:类似于java中的switch语句.在所有的条件中选择其一
<foreach>:迭代操作
<sql>:sql片段,可以把条件和查询结果的字段抽取出来
AI写代码java运行
5、Mybatis 如何完成MySQL的批量操作,举例说明
MyBatis完成MySQL的批量操作主要是通过标签来拼装相应的SQL语句.
例如:
1.<insert id="insertBatch" >
2. insert into tbl_employee(last_name,email,gender,d_id) values
3. <foreach collection="emps" item="curr_emp" separator=",">
4. (#{curr_emp.lastName},#{curr_emp.email},#{curr_emp.gender},#{curr_emp.dept.id})
5. </foreach>
6.</insert>
AI写代码sql
这个在项目中也用过,为了提升性能,可以采用在映射文件中通过forEach标签遍历集合,获取每一个元素作为insert语句的参数值
6、mybatis的分页
逻辑分页:使用 MyBatis 自带的 RowBounds 进行分页,它是一次性查询很多数据,然后在数据中再进行检索。
消耗大量内存,对数据库压力较大
物理分页:自己手写 SQL 分页或使用分页插件 PageHelper,去数据库查询指定条数的分页数据的形式
7、Mybatis的工作流程
1、加载配置文件mybatis-config.xml
2、创建会话工厂。MyBatis通过读取配置文件的信息来构造出会话工厂得到SqlSessionFactory对象。
3、创建会话对象。sqlSessionFactory.openSession(),得到sqlSession对象
4、sqlsession对象.getMapper(XXX接口),得到这个接口的代理对象。
5、调用代理对象去执行代理逻辑(执行sql),返回查询结果集
8、Mybatis是否支持延迟加载?延迟加载的原理是什么?
1、mybatis 是否支持延迟加载?
延迟加载就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据
两次查询,第一次查主表,第二次查关联表,第二次查询条件是第一次查询结果的列值
是支持的,不过默认是关闭的,如果想要使用,需要添加配置开启。延迟加载可以实现先查询主表,按需实时做关联查询,返回关联表结果集,一定程度上提高了效率。
mybatis仅支持关联对象association和关联集合对象collection的延迟加载,association是一对一,collection是一对多查询,在mybatis配置文件中可以配置lazyloadingEnable=true/false。
2、延迟加载的原理是什么?
使用CGLIB为目标对象建立代理对象,当调用目标对象的方法时进入拦截器方法。
比如调用a.getB().getName(),拦截器方法invoke()发现a.getB()为null,会单独发送事先准备好的查询关联B对象的sql语句,把B查询出来然后调用a.setB(b),也是a的对象的属性b就有值了,然后调用getName(),这就是延迟加载的原理。
9、Mybatis有哪些执行器?
(1)SimpleExecutor 简单执行器
会进行两次预编译,每次都会构造一个PreparmentStatement对象------效率较低
(2)ReuseExecutor 可重用执行器
执行相同的sql语句只会进行一次预编译,效率更高
(3)BatchExecutor 批量执行器
查询时与SimpleExecutor相同会进行多次编译,更新或删除时,会批量进行,需要手动提交
原理:初始化sqlSession会读取配置文件,若配置了执行器则使用对应的执行器,未配置则使用SimpleExecutor
配置:在Mybatis配置文件中配置执行器
篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题
需要全套面试笔记【点击此处即可】免费获取
SpringBoot/SpringCloud
1、谈谈怎么理解SpringBoot框架?
Spring Boot 是 Spring 开源组织下的子项目,是 Spring 组件一站式解决方案,主要是简化了使用 Spring 的难度,简省了繁重的配置,提供了各种启动器,开发者能快速上手。
Spring Boot的优点
1、快速构建项目,可以选一些必要的组件;
2、对主流框架的无配置集成;
3、内嵌Tomcat容器,项目可独立运行;
4、删除了繁琐的xml配置文件;
5、极大地提高了开发和部署效率;
6、提供starter,简化maven配置;
7、约定大于配置
Spring Boot缺点:
1.版本迭代速度快,一些模块改动很大;
2.由于无须配置,报错时很难定位;
约定大约配置的体现:
1、starter启动器,管理jar包,简化maven配置
2、默认加载application.yml配置文件
3、通过扫描约定路径下的 spring.factories文件来识别配置类,实现 Bean 的自动装配。
2、Spring Boot 的核心注解是哪个?它主要由哪几个注解组成的?
启动类上面的注解是@SpringBootApplication,它也是 Spring Boot 的核心注解,主要组合包含了以下 3 个注解:
@SpringBootConfiguration:组合了 @Configuration 注解,实现配置文件的功能。
@EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项,
如关闭数据源自动配置功能: @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })。
@ComponentScan:Spring组件扫描。
3、Spring Boot 自动配置原理是什么?
自动配置:扫描启动类所在的包以及子包所有Bean组件并注册到IOC容器中
1、大致流程通过@SpringBootApplication进行实现,这个注解在启动类上
2、@SpringBootApplication注解由三个注解共同完成自动装配,各个注解作用如下
@SpringBootConfiguration: 标记启动类为一个spring配置类
@EnableAutoConfiguration: 实现自动装配的核心注解
@ComponentScan: spring组件扫描
3、@EnableAutoConfiguration主要是通过@Import注解导入AutoConfigurationImportSelector类自动配置选择器开启自动装配。
4、AutoConfigurationImportSelector类实现了ImportSelector 接口,重写了selectImports方法,我们就可以得到需要自动配置的类的全限定类名数组
5、通过Spring提供的SpringFactoriesLoader机制,扫描classpath下的META-INF/spring.factories文件,读取需要自动装配的配置类
6、依据@Conditional条件筛选的方式按需加载配置类,最终完成自动装配
4、SpringBoot配置文件有哪些?加载顺序?怎么实现多环境配置?
- 两种前缀开头
bootstarp>application[加载顺序]
- 3种后缀结尾
.yml>.yaml>.properties
- 组合起来其实有6种可能
5、SpringBoot和SpringCloud是什么关系
Spring Boot 是 Spring 的一套快速配置脚手架,可以基于Spring Boot 快速开发单个微服务,简化了spring开发。约定大于配置。
Spring Cloud是一个基于Spring Boot的微服务全局治理框架,提供了快速构建分布式的常用组件,如服务的配置管理、服务注册与发现,服务调用与熔断降级等。
而真正的实现目前有两套体系用的比较多
一个是 Spring Cloud Netflix,其中常用组件有
- Ribbon——负载均衡
- Hystrix——服务熔断
- Zuul——网关
- Eureka——服务注册与发现
- Feign——服务调用
一个是 Spring Cloud Alibaba
- Dubbo——消息通讯
- Nacos——注册中心/配置中心
- Seata——分布式事务
- Sentinel——熔断降级
- XXL-job:分布式任务调度
6、SpringCloud都用过哪些组件?介绍一下作用
早期我们一般认为的Spring Cloud五大组件是
- Eureka : 注册中心
- Ribbon : 负载均衡
- Feign : 远程调用
- Hystrix : 服务熔断
- Zuul/Gateway : 网关
随着SpringCloudAlibba在国内兴起 , 我们项目中使用了一些阿里巴巴的组件 - 注册中心/配置中心 Nacos
- 负载均衡 Ribbon
- 服务调用 openFeign
- 服务保护 sentinel
- 服务网关 Gateway
7、nacos、eureka的区别?
① Nacos支持服务端主动检测提供者状态:临时实例采用心跳模式,非临时实例采用主动检测模式
② 临时实例心跳不正常会被剔除,非临时实例则不会被剔除
③ Nacos支持服务列表变更的消息推送模式,服务列表更新更及时
④ Nacos集群默认采用AP方式,当集群中存在非临时实例时,采用CP模式;
- Eureka采用AP方式
- naocs默认是AP模式,可以采用CP模式
8、Nacos作用以及注册中心的原理
Nacos英文全称Dynamic Naming and Configuration Service,Na为naming/nameServer即注册中心,co为configuration即注册中心,service是指该注册/配置中心都是以服务为核心。
Nacos注册中心分为server与client,server采用Java编写,为client提供注册发现服务与配置服务。而client可以用多语言实现,client与微服务嵌套在一起,nacos提供sdk和openApi,如果没有sdk也可以根据openApi手动写服务注册与发现和配置拉取的逻辑。
服务注册原理
服务注册方法:以Java nacos client v1.0.1 为例子,服务注册的策略的是每5秒向nacos server发送一次心跳,心跳带上了服务名,服务ip,服务端口等信息。同时 nacos server也会向client 主动发起健康检查,支持tcp/http检查。如果15秒内无心跳且健康检查失败则认为实例不健康,如果30秒内健康检查失败则剔除实例。
9、服务注册和发现是什么意思?Spring Cloud 如何实现服务注册发现?
我理解的是主要三块大功能,分别是服务注册 、服务发现、服务状态监控
- 服务注册 : 服务启动的时候会将服务的信息注册到注册中心, 比如: 服务名称 , 服务的IP , 端口号等
- 服务发现 : 服务调用方调用服务的时候, 根据服务名称从注册中心拉取服务列表 , 然后根据负载均衡策略 , 选择一个服务, 获取服务的IP和端口号, 发起远程调用
- 服务状态监控 : 服务提供者会定时向注册中心发送心跳 , 注册中心也会主动向服务提供者发送心跳探测, 如果长时间没有接收到心跳, 就将服务实例从注册中心下线或者移除
使用的话, 首先需要部署注册中心服务 , 然后在我们自己的微服务中引入注册中心依赖, 然后再配置文件中配置注册中心地址 就可以了
10、Feign工作原理
Feign底层依赖于Java的动态代理机制,对原生Java Socket或者Apache HttpClient进行封装,实现了基于Http协议的远程过程调用。当然,Feign还在此基础上实现了负载均衡、熔断等机制。
主程序入口添加了@EnableFeignClients注解开启对FeignClient扫描加载处理。根据Feign Client的开发规范,定义接口并加@FeignClient注解。当程序启动时,会进行包扫描,扫描所有@FeignClient的注解的类,并且讲这些信息注入Spring IOC容器中,当定义的的Feign接口中的方法被调用时,通过JDK的代理方式,来生成具体的RequestTemplate。当生成代理时,Feign会为每个接口方法创建一个RequestTemplate对象,该对象封装HTTP请求需要的全部信息,如请求参数名,请求方法等信息都是在这个过程中确定的。然后RequestTemplate生成Request,然后把Request交给Client去处理,这里指的时Client可以是JDK原生的HTTPURLConnection,Apache的HttpClient,或OKhttp,最后Client被封装到LoadBalanceClient类,这个类结合Ribbon负载均衡发起服务之间的调用。
篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题
需要全套面试笔记【点击此处即可】免费获取
11、服务熔断和服务降级的区别
- 服务降级:指的是业务代码【下单】出现问题(异常 出错了 响应很慢),接下开通过fallback去执行兜底方法,【返回服务页面 给一个默认
查询缓存】 将核心业务降级到非核心业务 - 服务熔断:当触发了服务的熔断阈值之后,也会调用fallback去执行兜底方法。但是服务熔断来说只会去调用兜底方法,不会在去执行业务方法。【半开转态】
我们项目中涉及到服务调用得地方都会定义降级, 一般降级逻辑就是返回默认值 , 降级的实现也非常简单 , 就是创建一个类实现FallbackFactory
接口 , 然后再对应的Feign客户端接口上面 , 通过@FeignClient指定降级类
12、你们项目中微服务之间是如何通讯的?
1.同步通信:通过openFeign发送http请求调用
2.异步:消息队列,如RabbitMq、KafKa等
13、你们项目的配置文件是怎么管理的 ?
大部分的固定的配置文件都放在服务本地 , 一些根据环境不同可能会变化的部分, 放到Nacos中
Naocs中主要存放的是各个微服务共享的配置,需要随着需求动态变更的配置。
14、你们项目中有没有做过限流 ? 怎么做的 ?
常见的限流算法:漏桶算法、令牌桶算法
漏桶算法:漏桶算法其实很简单,可以粗略的认为就是注水漏水过程,往桶中以一定速率流出水,以任意速率流入水,当水超过桶流量则丢弃,因为桶容量是不变的,保证了整体的速率。
令牌桶算法:令牌桶是一个存放固定容量令牌的桶,按照固定速率r往桶里添加令牌;桶中最多存放b个令牌,当桶满时,新添加的令牌被丢弃;当一个请求达到时,会尝试从桶中获取令牌;如果有,则继续处理请求;如果没有则排队等待或者直接丢弃;可以发现,漏桶算法的流出速率恒定,而令牌桶算法的流出速率却有可能大于r;