最近面试遇到的问题,都记录下来了。
1 索引基本原理
索引的原理:就是把无序的数据变成有序的查询
(1) 把创建了索引的列的内容进行排序
(2)对排序结果生成倒排表
(3)在倒排表内容上拼上数据地址链
(4)在查询的时候,先拿到倒排表内容,再取出数据地址链,从而拿到具体数据
2 mysql聚簇和非聚簇索引的区别
都是B+树
聚集索引:表数据按照索引的顺序来存储的,也就是说索引项的顺序与表中记录的物理顺序一致。对于聚集索引,叶子结点即存储了真实的数据行,不再有另外单独的数据页。 在一张表上最多只能创建一个聚集索引,因为真实数据的物理顺序只能有一种。
非聚集索引:表数据存储顺序与索引顺序无关。对于非聚集索引,叶结点包含索引字段值及指向数据页数据行的逻辑指针,其行数量与数据表行数据量一致。
3 MySQL最左匹配原则
在MySQL建立联合索引时会遵守最左前缀匹配原则,即最左优先,在检索数据时从联合索引的最左边开始匹配。
要想理解联合索引的最左匹配原则,先来理解下索引的底层原理。索引的底层是一颗B+树,那么联合索引的底层也就是一颗B+树,只不过联合索引的B+树节点中存储的是键值。由于构建一棵B+树只能根据一个值来确定索引关系,所以数据库依赖联合索引最左的字段来构建。
原文连接:https://www.cnblogs.com/ljl150/p/12934071.html
4 事务的基本特性和隔离级别
1.事务的四大特性(ACID):指数据库事务正确执行的四个基本要素的缩写。包含:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。一个支持事务(Transaction)的数据库,必需要具有这四种特性,否则在事务过程(Transaction processing)当中无法保证数据的正确性,交易过程极可能达不到交易方的要求
1 read uncommitted : 读取尚未提交的数据 :哪个问题都不能解决
2 read committed:读取已经提交的数据 :可以解决脏读 ---- oracle、sql server、postgresql 默认的
3 repeatable read:重读读取:可以解决脏读 和 不可重复读 ---mysql默认的
4 serializable:串行化:可以解决 脏读 不可重复读 和 虚读---相当于锁表
5 RDB 和 AOF 机制
RDB的实现是先fork出一个子进程,在指定的时间间隔内将内存中的数据集快照写入临时文件中,写入成功后,覆盖最终的RDB文件,用二进制压缩存储。RDB也是Redis默认的持久化机制。
AOF持久化的实现,是将所有的写命令都记录下来,以aof文件的形式进行记录(在aof文件中,我们可以看到具体的命令),在redis启动之后,会读取该文件,重新构建出内存中的数据,默认情况下AOF机制是关闭的,需要使用的时候需要进行配置
优缺点:
RDB优点:
RDB在恢复大数据集时,相比于AOF恢复速度快,(恢复时会加载rdb文件,存储在默认目录下,以下命令来查看)
configgetdir
2.RDB文件是一个非常紧凑的文件,它保存了 Redis 在某个时间点上的数据集, 这种文件非常适合用于进行备份
缺点
若在save的过程中服务器宕机,那么数据会丢失
Redis需要fork出一个子进程,在数据量很大情况下,会很耗时
AOF优缺点:
优点
AOF 的默认策略为每秒钟fsync 一次,在这种配置下,Redis 仍然可以保持良好的性能,并且就算发生故障停机,也最多只会丢失一秒钟的数据( fsync 会在后台线程执行,所以主线程可以继续努力地处理命令请求)Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写: 重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合
缺点
在大数据量的时候,数据恢复比较慢对于相同的数据集,AOF文件大小大于RDB文件根据所使用的 fsync策略,AOF的速度可能会慢于RDB 。 在一般情况下,每秒fsync的性能依然非常高, 而关闭 fsync 可以让 AOF 的速度和 RDB 一样快, 即使在高负荷之下也是如此。
6 Redis的过期键的删除策略
Redis是key-value数据库,我们可以设置Redis中缓存的key的过期时间。Redis的过期策略就是指当Redis中缓存的key过期了,Redis如何处理。
惰性过期:只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源,却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。
定期过期:每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。该策略是一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。
(expires字典会保存所有设置了过期时间的key的过期时间数据,其中,key是指向键空间中的某个键的 指针,value是该键的毫秒精度的UNIX时间戳表示的过期时间。键空间是指该Redis集群中保存的所有键。)
Redis中同时使用了惰性过期和定期过期两种过期策略。
7 缓存雪崩、缓存穿透、缓存击穿
缓存穿透
缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。在流量大时,可能DB就挂掉了,要是有人利用不存在的key频繁攻击我们的应用,这就是漏洞。
解决方案
有很多种方法可以有效地解决缓存穿透问题,最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被 这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。另外也有一个更为简单粗暴的方法(我们采用的就是这种),如果一个查询返回的数据为空(不管是数 据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。
缓存雪崩
缓存雪崩是指在我们设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到DB,DB瞬时压力过重雪崩。
解决方案
缓存失效时的雪崩效应对底层系统的冲击非常可怕。大多数系统设计者考虑用加锁或者队列的方式保证缓存的单线 程(进程)写,从而避免失效时大量的并发请求落到底层存储系统上。这里分享一个简单方案就时讲缓存失效时间分散开,比如我们可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件
缓存击穿
对于一些设置了过期时间的key,如果这些key可能会在某些时间点被超高并发地访问,是一种非常“热点”的数据。这个时候,需要考虑一个问题:缓存被“击穿”的问题,这个和缓存雪崩的区别在于这里针对某一key缓存,前者则是很多key。
缓存在某个时间点过期的时候,恰好在这个时间点对这个Key有大量的并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。
解决方案
1.使用互斥锁(mutex key)业界比较常用的做法,是使用mutex。简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db,而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一个mutex key,当操作返回成功时,再进行load db的操作并回设缓存;否则,就重试整个get缓存的方法。SETNX,是「SET if Not eXists」的缩写,也就是只有不存在的时候才设置,可以利用它来实现锁的效果。在redis2.6.1之前版本未实现setnx的过期时间,所以这里给出两种版本代码参考:
publicStringget(key) {
Stringvalue=redis.get(key);
if(value==null) {//代表缓存值过期
//设置3min的超时,防止del操作失败的时候,下次缓存过期一直不能load db
if(redis.setnx(key_mutex,1,3*60)==1) {//代表设置成功
value=db.get(key);
redis.set(key,value,expire_secs);
redis.del(key_mutex);
}else{//这个时候代表同时候的其他线程已经load db并回设到缓存了,这时候重试获取缓存值即可
sleep(50);
get(key);//重试
}
}else{
returnvalue;
}
}
永不过期
(1) 从redis上看,确实没有设置过期时间,这就保证了,不会出现热点key过期问题,也就是“物理”不过期。
(2) 从功能上看,如果不过期,那不就成静态的了吗?所以我们把过期时间存在key对应的value里,如果发现要过期了,通过一个后台的异步线程进行缓存的构建,也就是“逻辑”过期
8 CAP理论
CAP原则又称CAP定理,指的是在一个分布式系统中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可得兼。
CAP原则是NOSQL数据库的基石。
分布式系统的CAP理论:理论首先把分布式系统中的三个特性进行了如下归纳:
一致性(C):在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本)
可用性(A):在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。(对数据更新具备高可用性)
分区容忍性(P):以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在C和A之间做出选择。
9 分布式架构下,Session 共享有什么方案
不要有session:大家可能觉得我说了句废话,但是确实在某些场景下,是可以没有session的,其实在很多接口类系统当中,都提倡【API无状态服务】;也就是每一次的接口访问,都不依赖于session、不依赖于前一次的接口访问;
存入cookie中:将session存储到cookie中,但是缺点也很明显,例如每次请求都得带着session,数据存储在客户端本地,是有风险的;
session同步:对个服务器之间同步session,这样可以保证每个服务器上都有全部的session信息,不过当服务器数量比较多的时候,同步是会有延迟甚至同步失败;
使用Nginx(或其他复杂均衡软硬件)中的ip绑定策略,同一个ip只能在指定的同一个机器访问,但是这样做风险也比较大,而且也是去了负载均衡的意义;
我们现在的系统会把session放到Redis中存储,虽然架构上变得复杂,并且需要多访问一次Redis,但是这种方案带来的好处也是很大的:实现session共享,可以水平扩展(增加Redis服务器),服务器重启session不丢失(不过也要注意session在Redis中的刷新/失效机制),不仅可以跨服务器session共享,甚至可以跨平台(例如网页端和APP端)。
原文地址:https://www.cnblogs.com/shoshana-kong/p/10939772.html
10 简述你对RPC、RMI的理解
一:RPC 远程过程调用
RPC(Remote Procedure Call Protocol)远程过程调用协议,通过网络从远程计算机上请求调用某种服务。
一次RPC调用的过程大概有10步:
执行客户端调用语句,传送参数
调用本地系统发送网络消息
消息传送到远程主机
服务器得到消息并取得参数
根据调用请求以及参数执行远程过程(服务)
执行过程完毕,将结果返回服务器句柄
服务器句柄返回结果,调用远程主机的系统网络服务发送结果
消息传回本地主机
客户端句柄由本地主机的网络服务接收消息
客户端接收到调用语句返回的结果数据
RMI 远程方法调用
RMI:远程方法调用(Remote Method Invocation)。能够让在客户端Java虚拟机上的对象像调用本地对象一样调用服务端Java虚拟机中的对象上的方法。
RMI远程调用步骤:
客户调用客户端辅助对象stub上的方法
客户端辅助对象stub打包调用信息(变量,方法名),通过网络发送给服务端辅助对象skeleton
服务端辅助对象skeleton将客户端辅助对象发送来的信息解包,找出真正被调用的方法以及该方法所在对象
调用真正服务对象上的真正方法,并将结果返回给服务端辅助对象skeleton
服务端辅助对象将结果打包,发送给客户端辅助对象stub
客户端辅助对象将返回值解包,返回给调用者
客户获得返回值
11 简述kafka架构设计
Consumer Group:消费者组,消费者组内每个消费者负责消费不同分区的数据,提高消费能力。逻 辑上的一个订阅者。
Topic:可以理解为一个队列,Topic 将消息分类,生产者和消费者面向的是同一个 Topic。
Partition:为了实现扩展性,提高并发能力,一个Topic 以多个Partition的方式分布到多个 Broker上,每个 Partition 是一个 有序的队列。一个 Topic 的每个Partition都有若干个副本(Replica),一个Leader 和若干个 Follower。生产者发送数据的对象,以及消费者消费数据的对象,都是 Leader。Follower负责实时从 Leader 中同步数据,保持和 Leader 数据的同步。Leader 发生故障时,某个Follower 还会成为新的 Leader。
Offset:消费者消费的位置信息,监控数据消费到什么位置,当消费者挂掉再重新恢复的时候,可以从 消费位置继续消费。
Zookeeper:Kafka 集群能够正常工作,需要依赖于 Zookeeper,Zookeeper 帮助 Kafka 存储和管理 集群信息。
12 什么是Hystrix?简述实现机制
Hystrix的工作原理是:
防止任何单个依赖项用尽所有容器(例如Tomcat)用户线程。脱落负载并快速失败而不是排队。在可行的情况下提供回退以保护用户免于失败。使用隔离技术(例如隔板,泳道和断路器模式)来限制任何一个依赖项的影响。通过近实时指标,监控和警报优化发现时间通过Hystrix的大多数方面的配置更改的低延迟传播和对动态属性更改的支持来优化恢复时间,这允许您使用低延迟反馈循环进行实时操作修改。防止整个依赖关系客户端执行中的故障,而不仅仅是网络流量。实现方式:
将对外部系统(或“依赖项”)的所有调用包含在通常在单独线程中执行的对象HystrixCommand或HystrixObservableCommand对象中(这是命令模式的示例)。定时调用的时间超过您定义的阈值。有一个默认的,而是由“属性”的方式对大多数依赖你自定义设置这些超时,使它们成功率笔99.5略高。为每个依赖服务维护一个小的线程池(或信号量); 如果它变满,将立即拒绝发往该依赖项的请求而不是排队。衡量成功,失败(客户端引发的异常),超时和线程拒绝。如果服务的错误百分比超过阈值,则手动或自动地使断路器跳闸以停止对特定服务的所有请求一段时间。当请求失败时执行callback逻辑,被拒绝,超时或短路。近乎实时地监控指标和配置更改。注意:Netflix 已经官方宣布不再维护 Hystrix,所以在生产环境,技术选型的时候需要综合考虑。
13 分布式事务解决方案
答案参考:https://www.cnblogs.com/bluemiaomiao/p/11216380.html
14 如何实现接口的幂等性
什么是幂等性
接口幂等性就是用户对于同一操作发起的一次请求或者多次请求的结果是一致性的,不会因为
多次点击而产生了副作用,比如支付场景,用户购买了商品支付扣款成功,但是返回结果的时候
网络异常,此时钱已经扣了,用户再次点击按钮,此时会进行第二次扣款,返回结果成功,用户
查询余额发现多扣钱了,流水记录也变成了两条,这就没有保证接口的幂等性。
幂等解决方案
一、token机制
1、服务端提供了发送token的接口。我们在分析业务的时候,那些业务存在幂等性问题的,就必须在执行业务前,
先去获取token,服务器会把token保存到redis中。
2、然后调用业务接口请求时,把token携带过去,一般放在请求头部。
3、服务器判断token是否与redis中的token匹配,匹配成功,然后删除token继续执行业务。
4、如果判断token不存在redis中,就表示重复操作,直接返回重复标记给client,用这样的方式来保证业务代码
的幂等性。
注意事项:
先删除token还是先处理业务?必须先删除令牌,避免并发情况下收到多请求执行通过。
获取token,校验token,删除token三个动作必须保证原子性,使用redis的Lua脚本完成这个操作。
(如果不知道Lua脚本为什么能保证原子性,推荐去了解redis的线程模型)
二、各种锁机制
1、数据库悲观锁
select * from t_user whereuser_id=1forupdate;
悲观锁使用时一般伴随事务一起使用,数据锁定时间可能会很长,需要根据实际情况选用。
另外需要注意的是,Id字段是主键或者唯一索引,不然可能造成锁表的结果,处理起来会非常麻烦。
2、数据库乐观锁
这种方法适合在更新场景中,update t_usersetage=age+1,version=version+1whereuser_id=1andversion=1;
根据version版本,也就是在操作库存前先获取当前商品的version版本号,然后操作的时候带上此version号。
我们第一次操作库存时,得到version为1,调用库存服务version变为2;但返回给订单服务出现了问题,订单又一次发起
调用库存服务,当订单服务传入version还是1,再执行上面的SQL语句时,因为version已经变成2了,where条件不成立。
这样就保证不了不管调用几次,只会真正的处理一次。
而且,乐观锁主要使用与处理读多写少的场景。
3、业务层分布式锁
如果多个机器可能在同一时间同时处理相同的数据,比如多台机器定时任务都拿到了相同数据,我们就可以加分布式锁,
锁定此数据,处理完成后释放锁。获取到锁的必须先判断这个数据是否被处理过。
三、各种唯一约束
1、数据库唯一约束
插入数据,应该按照唯一索引进行插入,比如订单号,相同的订单号就不可能有两条记录插入、
我们在数据库层面防止重复。
这个机制就是利用了数据的主键唯一约束的特性,解决了在insert场景时幂等问题。但主键的
要求不是自增的主键,这样就需要业务生成全局唯一的主键。
如果是在分库分表的场景下,路由规则要保证相同请求下,落地在同一个数据库中和同一表中,
要不然数据库主键约束就不起作用了,因为是不同的数据库和表主键不相关联。
2、redis 防重
很多数据需要处理,只能被处理一次,比如我们可以计算数据的MD5将其放入redis的缓存中,
每次处理数据,先判断这个MD5是否已经存在,存在就不处理。
3、防重表
使用订单号orderNo作为去重表的唯一索引,把唯一索引插入去重表,在进行业务操作,且他们在
同一个事务当中。这样来保证重复请求时,因为防重表有唯一约束,导致请求失败,避免了幂等问题。
这里要注意的是,去重表应该在同一库中,这样就保证了在同一个事务,即使业务操作失败了,它也
会把去重表的数据回滚。这个很好的保证了数据一致性。
4、全局请求唯一ID
调用接口时,生成一个唯一id,redis将数据保存到集合中(去重),存在即处理过,可以使用Nginx
设置每个请求的唯一id; proxy_set_header X-Request-Id$request_id;
缺点:nginx层面生成的id不区分数据并不能保证幂等,需从页面调用时由后台生成唯一id。
14 Spring Boot 自动配置原理
Spring Boot启动的时候会通过@EnableAutoConfiguration注解找到META-INF/spring.factories配置文件中的所有自动配置类,并对其进行加载,这些自动配置类都是以AutoConfiguration结尾来命名的,它实际上就是一个JavaConfig形式的Spring容器配置类,通过@Bean导入到Spring容器中,以Properties结尾命名的类是和配置文件进行绑定的。它能通过这些以Properties结尾命名的类中取得在全局配置文件中配置的属性,我们可以通过修改配置文件对应的属性来修改自动配置的默认值,来完成自定义配置
15 SpringMVC 工作流程
(1)客户端通过url发送请求
(2-3)核心控制器Dispatcher Servlet接收到请求,通过系统或自定义的映射器配置找到对应的handler,并将url映射的控制器controller返回给核心控制器。
(4)通过核心控制器找到系统或默认的适配器
(5-7)由找到的适配器,调用实现对应接口的处理器,并将结果返回给适配器,结果中包含数据模型和视图对象,再由适配器返回给核心控制器
(8-9)核心控制器将获取的数据和视图结合的对象传递给视图解析器,获取解析得到的结果,并由视图解析器响应给核心控制器
(10)核心控制器将结果返回给客户端
16 描述一下Spring Bean的生命周期
实例化 Instantiation
属性赋值 Populate
初始化 Initialization
销毁 Destruction
实例化 -> 属性赋值 -> 初始化 -> 销毁
原文地址:https://www.jianshu.com/p/1dec08d290c1
16 谈谈你对AOP的理解
将程序中的交叉业务逻辑(比如安全,日志,事务等),封装成一个切面,然后注入到目标对象(具体业务逻辑)中去。AOP可以对某个对象或某些对象的功能进行增强,比如对象中的方法进行增强,可以在执行某个方法之前额外的做一些事情,在某个方法执行之后额外的做一些事情
17 线程的生命周期?线程有几种状态
线程的生命周期包含5个阶段,包括:新建、就绪、运行、阻塞、销毁
出生状态:用户在创建线程时所处的状态,在用户使用该线程实例调用 start() 方法之前,线程都处于出生状态。
就绪状态:也称可执行状态,当用户调用 start() 方法之后,线程处于就绪状态。
运行状态:当线程得到系统资源后进入运行状态。
等待状态:当处于运行状态下的线程调用 Thread 类的 wait() 方法时,该线程就会进入等待状态。进入等待状态的线程必须调用 Thread 类的 notify() 方法才能被唤醒。notifyAll() 方法是将所有处于等待状态下的线程唤醒。
休眠状态:当线程调用 Thread 类中的 sleep() 方法时,则会进入休眠状态。
阻塞状态:如果一个线程在运行状态下发出输入/输出请求,该线程将进入阻塞状态,在其等待输入/输出结束时,线程进入就绪状态。对阻塞的线程来说,即使系统资源关闭,线程依然不能回到运行状态。
死亡状态:当线程的 run() 方法执行完毕,线程进入死亡状态。
18 sleep()、wait()、join()、yield()的区别
sleep() sleep()方法需要指定等待的时间,它可以让当前正在执行的线程在指定的时间内暂停执行,进入阻塞状态,该方法既可以让其他同优先级或者高优先级的线程得到执行的机会,也可以让低优先级的线程得到执行机会。但是sleep()方法不会释放“锁标志”,也就是说如果有synchronized同步块,其他线程仍然不能访问共享数据。 wait() wait()方法需要和notify()及notifyAll()两个方法一起介绍,这三个方法用于协调多个线程对共享数据的存取,所以必须在synchronized语句块内使用,也就是说,调用wait(),notify()和notifyAll()的任务在调用这些方法前必须拥有对象的锁。注意,它们都是Object类的方法,而不是Thread类的方法。 wait()方法与sleep()方法的不同之处在于,wait()方法会释放对象的“锁标志”。当调用某一对象的wait()方法后,会使当前线程暂停执行,并将当前线程放入对象等待池中,直到调用了notify()方法后,将从对象等待池中移出任意一个线程并放入锁标志等待池中,只有锁标志等待池中的线程可以获取锁标志,它们随时准备争夺锁的拥有权。当调用了某个对象的notifyAll()方法,会将对象等待池中的所有线程都移动到该对象的锁标志等待池。 除了使用notify()和notifyAll()方法,还可以使用带毫秒参数的wait(long timeout)方法,效果是在延迟timeout毫秒后,被暂停的线程将被恢复到锁标志等待池。 此外,wait(),notify()及notifyAll()只能在synchronized语句中使用,但是如果使用的是ReenTrantLock实现同步,该如何达到这三个方法的效果呢?解决方法是使用ReenTrantLock.newCondition()获取一个Condition类对象,然后Condition的await(),signal()以及signalAll()分别对应上面的三个方法。
yield() yield()方法和sleep()方法类似,也不会释放“锁标志”,区别在于,它没有参数,即yield()方法只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行,另外yield()方法只能使同优先级或者高优先级的线程得到执行机会,这也和sleep()方法不同。
join()
join()方法会使当前线程等待调用join()方法的线程结束后才能继续执行
18 简述线程池处理流程
提交任务后,线程池先判断线程数是否达到了核心线程数(corePoolSize)。如果未达到线程数,则创建核心线程处理任务;否则,就执行下一步;
接着线程池判断任务队列是否满了。如果没满,则将任务添加到任务队列中;否则,执行下一步;
接着因为任务队列满了,线程池就判断线程数是否达到了最大线程数。如果未达到,则创建非核心线程处理任务;否则,就执行饱和策略,默认会抛出RejectedExecutionException异常。
饱和策略:RejectedExecutionHandler
当任务队列和线程池都满了时所采取的应对策略,默认是AbordPolicy,表示无法处理新任务,并抛出RejectedExecutionException异常。
19 什么是bean的自动装配,有哪些方式
byType(根据类型自动装配):若IOC容器中有多个与目标Bean类型一致的Bean。在这种情况下,Spring将无法判断哪个Bean最合适该属性,所以不能执行自动配置。
byName(根据名称自动装配):必须将目标Bean的名称和属性名设置的完全相同。
constructor(通过构造器自动装配):当Bean中存在多个构造器时,此种自动装配方式会很复杂,不推荐使用
20 Spring框架中的单例Bean是线程安全的么
Spring容器中的Bean是否线程安全,容器本身并没有提供Bean的线程安全策略,因此可以说Spring容器中的Bean本身不具备线程安全的特性,但是具体还是要结合具体scope的Bean去研究。
Spring 的 bean 作用域(scope)类型 1、singleton:单例,默认作用域。
2、prototype:原型,每次创建一个新对象。
3、request:请求,每次Http请求创建一个新对象,适用于WebApplicationContext环境下。
4、session:会话,同一个会话共享一个实例,不同会话使用不用的实例。
5、global-session:全局会话,所有会话共享一个实例。
线程安全这个问题,要从单例与原型Bean分别进行说明。
原型Bean 对于原型Bean,每次创建一个新对象,也就是线程之间并不存在Bean共享,自然是不会有线程安全的问题。
单例Bean 对于单例Bean,所有线程都共享一个单例实例Bean,因此是存在资源的竞争。
如果单例Bean,是一个无状态Bean,也就是线程中的操作不会对Bean的成员执行查询以外的操作,那么这个单例Bean是线程安全的。比如Spring mvc 的 Controller、Service、Dao等,这些Bean大多是无状态的,只关注于方法本身