【美团】面试真题_2021年

1、Spring AOP底层原理

aop 底层采用动态代理机制实现:接口+实现类

  • 如果要代理的对象,实现了某个接口,那么Spring AOP会使用JDK Proxy,去创建代理对象。
  • 没有实现接口的对象,就无法使用JDK Proxy去代理了,这时候Spring AOP会使用Cglib生成一个被代理对象的子类来作为代理。

由代理对象创建出一个和impl实现类评级的一个对象,但是这个对象不是一个真正的对象,只是一个代理对象,但它可以实现和impl相同的功能,这个就是aop的横向机制原理,这样就不需要修改源代码。

2、HashMap的底层数据结构是怎样的?

JDK1.8之前

  • JDK1.8 之前HashMap 底层是数组和链表结合在一起使用也就是链表散列
  • HashMap 通过key 的hashCode 经过扰动函数处理过后得到hash 值,然后通过**(n -1) & hash** 判断当前元素存放的位置(这里的n 指的是数组的长度),如果当前位置存在元素的话,就判断该元素与要存入的元素的hash 值以及key 是否相同,如果相同的话,直接覆盖,不相同就通过拉链法解决冲突。

所谓扰动函数指的就是HashMap 的hash 方法。使用hash 方法也就是扰动函数是为了防止一些实现比较差的hashCode() 方法换句话说使用扰动函数之后可以减少碰撞。

JDK1.8之后

  • 当链表长度大于阈值(默认为8)时,会首先调用treeifyBin()方法。这个方法会根据HashMap 数组长度来决定是否转换为红黑树。只有当数组长度大于或者等于64 的情况下,才会执行转换红黑树操作,以减少搜索时间。否则,就是只是执行resize() 方法对数组扩容。

3、HashMap 的扩容机制是怎样的?

一般情况下,当元素数量超过阈值时便会触发扩容。每次扩容的容量都是之前容量的2 倍。HashMap 的容量是有上限的,必须小于1<<30,即1073741824。如果容量超出了这个数,则不再增长,且阈值会被设置为Integer.MAX_VALUE

JDK7 中的扩容机制

  • 空参数的构造函数:以默认容量、默认负载因子、默认阈值初始化数组。内部数组是空数组。有参构造函数:根据参数确定容量、负载因子、阈值等。
  • 第一次put 时会初始化数组,其容量变为不小于指定容量的2 的幂数,然后根据负载因子确定阈值。
  • 如果不是第一次扩容,则新容量=旧容量x 2 ,新阈值=新容量x 负载因子。

JDK8 的扩容机制

  • 空参数的构造函数:实例化的HashMap 默认内部数组是null,即没有实例化。第一次调用put 方法时,则会开始第一次初始化扩容,长度为16。
  • 有参构造函数:用于指定容量。会根据指定的正整数找到不小于指定容量的2 的幂数,将这个数设置赋值给阈值(threshold)。第一次调用put 方法时,会将阈值赋值给容量,然后让 阈值= 容量x 负载因子
  • 如果不是第一次扩容,则容量变为原来的2 倍,阈值也变为原来的2 倍。(容量和阈值都变为原来的2 倍时,负载因子还是不变)。

【注意】
1、首次put 时,先会触发扩容(算是初始化),然后存入数据,然后判断是否需要扩容
2、不是首次put,则不再初始化,直接存入数据,然后判断是否需要扩容

4、ConcurrentHashMap 的存储结构是怎样的?

  • Java7 中ConcurrnetHashMap 使用的分段锁,也就是每一个Segment 上同时只有一个线程可以操作,每一个Segment 都是一个类似HashMap 数组的结构,它可以扩容,它的冲突会转化为链表。但是Segment 的个数一但初始化就不能改变,默认Segment的个数是16 个。
  • Java8 中的ConcurrnetHashMap 使用的Synchronized 锁加CAS 的机制。结构也由Java7 中的Segment 数组+ HashEntry 数组+ 链表进化成了Node 数组+ 链表/ 红黑树,Node 是类似于一个HashEntry 的结构。它的冲突再达到一定大小时会转化成红黑树,在冲突小于一定数量时又退回链表

5、线程池大小如何设置?

  • CPU 密集型任务(N+1): 这种任务消耗的主要是CPU 资源,可以将线程数设置为N(CPU 核心数)+1,比CPU 核心数多出来的一个线程是为了防止线程偶发的缺页中断,或者其它原因导致的任务暂停而带来的影响。一旦任务暂停,CPU 就会处于空闲状态,而在这种情况下多出来的一个线程就可以充分利用CPU 的空闲时间。
  • I/O 密集型任务(2N): 这种任务应用起来,系统会用大部分的时间来处理I/O 交互,而线程在处理I/O 的时间段内不会占用CPU 来处理,这时就可以将CPU 交出给其它线程使用。因此在I/O 密集型任务的应用中,我们可以多配置一些线程,具体的计算方法是2N。

如何判断是CPU 密集任务还是IO 密集任务?
CPU 密集型简单理解就是利用CPU 计算能力的任务比如你在内存中对大量数据进行排序。单凡涉及到网络读取,文件读取这类都是IO 密集型,这类任务的特点是CPU 计算耗费时间相比于等待IO 操作完成的时间来说很少,大部分时间都花在了等待IO 操作完成上。

6、IO 密集=Ncpu*2 是怎么计算出来?

I/O 密集型任务任务应用起来,系统会用大部分的时间来处理I/O 交互,而线程在处理I/O 的时间段内不会占用CPU 来处理,这时就可以将CPU 交出给其它线程使用。因此在I/O 密集型任务的应用中,我们可以多配置一些线程。例如:数据库交互,文件上传下载,网络传输等。IO 密集型,即该任务需要大量的IO,即大量的阻塞,故需要多配置线程数。

7、G1 收集器有哪些特点?

  • G1 的全称是Garbage-First,意为垃圾优先,哪一块的垃圾最多就优先清理它。
  • G1 GC 最主要的设计目标是:将STW 停顿的时间和分布,变成可预期且可配置的。被视为JDK1.7 中HotSpot 虚拟机的一个重要进化特征。它具备一下特点:
  • 并行与并发:G1 能充分利用CPU、多核环境下的硬件优势,使用多个CPU(CPU 或者CPU 核心)来缩短Stop-The-World 停顿时间。部分其他收集器原本需要停顿Java 线程执行的GC 动作,G1 收集器仍然可以通过并发的方式让java 程序继续执行。
  • 分代收集:虽然G1 可以不需要其他收集器配合就能独立管理整个GC 堆,但是还是保留了分代的概念。
  • 空间整合:与CMS 的“标记-清理”算法不同,G1 从整体来看是基于“标记-整理”算法实现的收器;从局部上来看是基于“标记-复制”算法实现的。
  • 可预测的停顿:这是G1 相对于CMS 的另一个大优势,降低停顿时间是G1 和CMS 共同的关注点,但G1 除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M 毫秒的时间片段内。

G1 收集器在后台维护了一个优先列表,每次根据允许的收集时间,优先选择回收价值最大的
Region(这也就是它的名字Garbage-First 的由来)

8、你有哪些手段来排查OOM 的问题?

  • 增加两个参数
    -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heapdump.hprof,当OOM 发生时自动dump 堆内存信息到指定目录。
  • 同时jstat 查看监控JVM 的内存和GC 情况,先观察问题大概出在什么区域。
  • 使用MAT 工具载入到dump 文件,分析大对象的占用情况,比如HashMap 做缓存未清理,时间长了就会内存溢出,可以把改为弱引用。

9、请你谈谈MySQL 事务隔离级别,MySQL 的默认隔离级别是什么?

为了达到事务的四大特性,数据库定义了4 种不同的事务隔离级别:

  • READ-UNCOMMITTED(读取未提交):最低的隔离级别,允许脏读,也就是可能读取到其他会话中未提交事务修改的数据,可能会导致脏读、幻读或不可重复读
  • READ-COMMITTED(读取已提交): 只能读取到已经提交的数据。Oracle 等多数数据库默认都是该级别(不重复读),可以阻止脏读,但是幻读或不可重复读仍有可能发生。
  • REPEATABLE-READ(可重复读):对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生
  • SERIALIZABLE(可串行化):最高的隔离级别,完全服从ACID 的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。
  • MySQL 默认采用的REPEATABLE_READ 隔离级别。

10、可重复读解决了哪些问题?

  • 可重复读的核心就是一致性读(consistent read);保证多次读取同一个数据时,其值都和事务开始时候的内容是一致,禁止读取到别的事务未提交的数据,会造成幻读。
  • 而事务更新数据的时候,只能用当前读。如果当前的记录的行锁被其他事务占用的话,就需要进入锁等待。
  • 查询只承认在事务启动前就已经提交完成的数据。
  • 可重复读解决的是重复读的问题,可重复读在快照读的情况下是不会有幻读,但当前读的时候会有幻读。

11、对SQL 慢查询会考虑哪些优化?

  • 分析语句,是否加载了不必要的字段/数据。
  • 分析SQL 执行计划(explain extended),思考可能的优化点,是否命中索引等。
  • 查看SQL 涉及的表结构和索引信息。如果SQL 很复杂,优化SQL 结构。
  • 按照可能的优化点执行表结构变更、增加索引、SQL 改写等操作。
  • 查看优化后的执行时间和执行计划。
  • 如果表数据量太大,考虑分表。
  • 利用缓存,减少查询次数。

12、谈一谈缓存穿透、缓存击穿和缓存雪崩,以及解决办法?

缓存穿透

  • 问题:大量并发查询不存在的KEY,在缓存和数据库中都不存在,同时给缓存和数据库带来压力。
  • 原因:一般而言,缓存穿透有2 种可能性:业务数据被误删,导致缓存和数据库中都没有数据。恶意进行ddos 攻击。
  • 分析:为什么会多次透传呢?不存在一直为空,需要注意让缓存能够区分KEY 不存在和查询到一个空值。
  • 解决办法:缓存空值的KEY,这样第一次不存在也会被加载会记录,下次拿到有这个KEY。Bloom 过滤或RoaingBitmap 判断KEY 是否存在,如果布隆过滤器中没有查到这个数据,就不去数据库中查。在处理请求前增加恶意请求检查,如果检测到是恶意攻击,则拒绝进行服务。完全以缓存为准,使用延迟异步加载的策略(异步线程负责维护缓存的数据,定期或根据条件触发更新),这样就不会触发更新。

缓存击穿

  • 问题:某个KEY 失效的时候,正好有大量并发请求访问这个KEY。
  • 分析:跟穿透其实很像,属于比较偶然的。
  • 解决办法:KEY 的更新操作添加全局互斥锁。完全以缓存为准,使用延迟异步加载的策略(异步线程负责维护缓存的数据,定期或根据条件触发更新),这样就不会触发更新。

缓存雪崩

  • 问题:当某一时刻发生大规模的缓存失效的情况,导致大量的请求无法获取数据,从而将流量压力传导到数据库上,导致数据库压力过大甚至宕机。
  • 原因:一般而言,缓存雪崩有2 种可能性:大量的数据同一个时间失效:比如业务关系强相关的数据要求同时失效Redis 宕机
  • 分析:一般来说,由于更新策略、或者数据热点、缓存服务宕机等原因,可能会导致缓存数据同一个时间点大规模不可用,或者都更新。所以,需要我们的更新策略要在时间上合适,数据要均匀分享,缓存服务器要多台高可用。
  • 解决办法:更新策略在时间上做到比较平均。如果数据需要同一时间失效,可以给这批数据加上一些随机值,使得这批数据不要在同一个时间过期,降低数据库的压力。使用的热数据尽量分散到不同的机器上。多台机器做主从复制或者多副本,实现高可用。做好主从的部署,当主节点挂掉后,能快速的使用从结点顶上。实现熔断限流机制,对系统进行负载能力控制。对于非核心功能的业务,拒绝其请求,只允许核心功能业务访问数据库获取数据。服务降价:提供默认返回值,或简单的提示信息。

13、LRU 是什么?如何实现?

最近最少使用策略LRU(Least Recently Used)是一种缓存淘汰算法,是一种缓存淘汰机制。

  • 使用双向链表实现的队列,队列的最大容量为缓存的大小。在使用过程中,把最近使用的页面移动到队列头,最近没有使用的页面将被放在队列尾的位置
  • 使用一个哈希表,把页号作为键,把缓存在队列中的节点的地址作为值,只需要把这个页对应的节点移动到队列的前面,如果需要的页面在内存中,此时需要把这个页面加载到内存中,简单的说,就是将一个新节点添加到队列前面,并在哈希表中跟新相应的节点地址,如果队列是满的,那么就从队尾移除一个节点,并将新节点添加到队列的前面。
public class LruDemo {
    class LRUCache<K, V> extends LinkedHashMap {
        private final int CACHE_SIZE;

        /**
         * 传递进来最多能缓存多少数据
         *
         * @param cacheSize
         */
        public LRUCache(int cacheSize) {
            // true 表示让 LinkedHashMap 按照访问顺序来进行排序,最近访问的放在头部,最老访问的放在尾部。
            super((int) Math.ceil(cacheSize / 0.75) + 1, 0.75f, true);
            CACHE_SIZE = cacheSize;
        }

        @Override
        protected boolean removeEldestEntry(Map.Entry eldest) {
            // 当 map 中的数据量大于指定的缓存个数的时候,就自动删除最老的数据。
            return size() > CACHE_SIZE;
        }
    }
}

14、什么是堆内存?参数如何设置?

堆内存是指由程序代码自由分配的内存,与栈内存作区分。
在Java 中,堆内存主要用于分配对象的存储空间,只要拿到对象引用,所有线程都可以访问堆内存。

  • -Xmx, 指定最大堆内存。如-Xmx4g. 这只是限制了Heap 部分的最大值为4g。这个内存不包括栈内存,也不包括堆外使用的内存。
  • -Xms, 指定堆内存空间的初始大小。如-Xms4g。而且指定的内存大小,并不是操作系统实际分配的初始值,而是GC 先规划好,用到才分配。专用服务器上需要保持–Xms和–Xmx 一致,否则应用刚启动可能就有好几个FullGC。当两者配置不一致时,堆内存扩容可能会导致性能抖动。
  • -Xmn, 等价于-XX:NewSize,使用G1 垃圾收集器不应该设置该选项,在其他的某些业务场景下可以设置。官方建议设置为-Xmx 的1/2 ~ 1/4.
  • -XX:MaxPermSize=size, 这是JDK1.7 之前使用的。Java8 默认允许的Meta 空间无限大,此参数无效。
  • -XX:MaxMetaspaceSize=size, Java8 默认不限制Meta 空间, 一般不允许设置该选项。
  • -XX:MaxDirectMemorySize=size,系统可以使用的最大堆外内存,这个参数跟-Dsun.nio.MaxDirectMemorySize 效果相同。
  • -Xss, 设置每个线程栈的字节数。例如-Xss1m 指定线程栈为1MB,与-XX:ThreadStackSize=1m 等价

15、栈和队列,举个使用场景例子?

  • 栈(后进先出)可以用于字符匹配,数据反转等场景
  • 队列(先进先出)可以用于任务队列,共享打印机等场景

16、MySQL 为什么InnoDB 是默认引擎?

聚集索引是指数据库表行中数据的物理顺序与键值的逻辑(索引)顺序相同。一个表只能有一个聚簇索引,因为一个表的物理顺序只有一种情况,所以,对应的聚簇索引只能有一个。聚簇索引的叶子节点就是数据节点,既存储索引值,又在叶子节点存储行数据。

Innodb 创建表后生成的文件有:
frm:创建表的语句
idb:表里面的数据+索引文件

17、MySQL 索引底层结构为什么使用B+树?

  • 哈希虽然能够提供O(1) 的单数据行操作性能,但是对于范围查询和排序却无法很好地支持,最终导致全表扫描;B 树能够在非叶节子点中存储数据,但是这也导致在查询连续数据时可能会带来更多的随机I/O,而B+树的所有叶节点可以通过指针相互连接,能够减少顺序遍历时产生的额外随机I/O;
  • 第一,B 树一个节点里存的是数据,而B+树存储的是索引(地址),所以B 树里一个节点存不了很多个数据,但是B+树一个节点能存很多索引,B+树叶子节点存所有的数据。
  • 第二,B+树的叶子节点是数据阶段用了一个链表串联起来,便于范围查找。

18、B+ 树的叶子节点链表是单向还是双向?

双向链表

19、MVCC 是什么?它的底层原理是什么?

MVCC,多版本并发控制,它是通过读取历史版本的数据,来降低并发事务冲突,从而提高并
发性能的一种机制。

  • 事务版本号
  • 表的隐藏列
  • undo log
  • read view

20、undo log 具体怎么回滚事务?

  • 对于insert 类型的sql,会在undo log 中记录下方才你insert 进来的数据的ID,当你想roll back 时,根据ID 完成精准的删除。
  • 对于delete 类型的sql,会在undo log 中记录方才你删除的数据,当你回滚时会将删除前的数据insert 进去。
  • 对于update 类型的sql,会在undo log 中记录下修改前的数据,回滚时只需要反向update 即可。
  • 对于select 类型的sql,别费心了,select 不需要回滚。

21、如何查询慢SQL 产生的原因

  • 分析SQL 执行计划(explain extended),思考可能的优化点,是否命中索引等。
  • 没有索引或者没有用到索引(这是查询慢最常见的问题,是程序设计的缺陷)。
  • 内存不足。
  • 网络速度慢。
  • 是否查询出的数据量过大(可以采用多次查询,其他的方法降低数据量)。
  • 是否返回了不必要的行和列。
  • 锁或者死锁。
  • I/O 吞吐量小,形成了瓶颈效应。
  • sp_lock,sp_who,活动的用户查看,原因是读写竞争资源。

22、索引失效的情况有哪些?

  • like 以%开头索引无效,当like 以&结尾,索引有效。
  • or 语句前后没有同事使用索引,当且仅当or 语句查询条件的前后列均为索引时,索引生效。
  • 组合索引,使用的不是第一列索引时候,索引失效,即最左匹配规则。
  • 数据类型出现隐式转换,如varchar 不加单引号的时候可能会自动转换为int 类型,这个时候索引失效。
  • 在索引列上使用IS NULL 或者IS NOT NULL 时候,索引失效,因为索引是不索引空值得。
  • 在索引字段上使用,NOT、<>、!= 、时候是不会使用索引的,对于这样的处理只会进行全表扫描。
  • 对索引字段进行计算操作,函数操作时不会使用索引。
  • 当全表扫描速度比索引速度快的时候不会使用索引。

23、一个Redis 实例最多能存放多少的keys?List、Set、Sorted Set 他们最多能存放多少元素?

理论上Redis 可以处理多达232 的keys,并且在实际中进行了测试,每个实例至少存放了2 亿5 千万的keys。我们正在测试一些较大的值。任何list、set、和sorted set 都可以放232 个元素。换句话说,Redis 的存储极限是系统中的可用内存值。

24、Redis 数据结构压缩列表和跳跃表的区别

  • 压缩列表(ziplist)本质上就是一个字节数组,是Redis 为了节约内存而设计的一种线性数据结构,可以包含多个元素,每个元素可以是一个字节数组或一个整数。
  • 跳跃表(skiplist)是一种有序数据结构,它通过在每个节点中维持多个指向其他节点的指
    针,从而达到快速访问节点的目的。跳跃表支持平均O(logN)、最坏O(N)复杂度
    的节点查找,还可以通过顺序性操作来批量处理节点。

25、为什么数据量小的时候用压缩列表?

为了省内存。

26、Redis 主从同步是怎么实现的?

全量同步
master 服务器会开启一个后台进程用于将redis 中的数据生成一个rdb 文件,与此同时,服务器会缓存所有接收到的来自客户端的写命令(包含增、删、改),当后台保存进程处理完毕后,会将该rdb 文件传递给slave 服务器,而slave 服务器会将rdb 文件保存在磁盘并通过读取该文件将数据加载到内存,在此之后master 服务器会将在此期间缓存的命令通过redis 传输协议发送给slave 服务器,然后slave 服务器将这些命令依次作用于自己本地的数据集上最终达到数据的一致性。

增量同步
从redis 2.8 版本以前,并不支持部分同步,当主从服务器之间的连接断掉之后,master 服务器和slave 服务器之间都是进行全量数据同步。从redis 2.8 开始,即使主从连接中途断掉,也不需要进行全量同步,因为从这个版本开始融入了部分同步的概念。部分同步的实现依赖于在master 服务器内存中给每个slave 服务器维护了一份同步日志和同步标识,每个slave 服务器在跟master 服务器进行同步时都会携带自己的同步标识和上次同步的最后位置。当主从连接断掉之后,slave 服务器隔断时间
(默认1s)主动尝试和master 服务器进行连接,如果从服务器携带的偏移量标识还在master 服务器上的同步备份日志中,那么就从slave 发送的偏移量开始继续上次的同步操作,如果slave 发送的偏移量已经不再master 的同步备份日志中(可能由于主从之间断掉的时间比较长或者在断掉的短暂时间内master 服务器接收到大量的写操作),则必须进行一次全量更新。在部分同步过程中,master 会将本地记录的同步备份日志中记录的指令依次发送给slave 服务器从而达到数据一致。

Redis 主从同步策略
主从刚刚连接的时候,进行全量同步;全同步结束后,进行增量同步。当然,如果有需要,
slave 在任何时候都可以发起全量同步。redis 策略是,无论如何,首先会尝试进行增量同步,
如不成功,要求从机进行全量同步。

27、Redis 持久化RDB 和AOF 优缺点

RDB
RDB 持久化方式,是将Redis 某一时刻的数据持久化到磁盘中,是一种快照式的持久化方法。
RDB 优点

  • RDB 是一个非常紧凑(有压缩)的文件,它保存了某个时间点的数据,非常适用于数据的备份。
  • RDB 作为一个非常紧凑(有压缩)的文件,可以很方便传送到另一个远端数据中心,非常适用于灾难恢复。
  • RDB 在保存RDB 文件时父进程唯一需要做的就是fork 出一个子进程,接下来的工作全部由子进程来做,父进程不需要再做其他IO 操作,所以RDB 持久化方式可以最大化redis 的性能。
  • 与AOF 相比,在恢复大的数据集的时候,RDB 方式会更快一些。

RDB 缺点

  • Redis 意外宕机时,会丢失部分数据。
  • 当Redis 数据量比较大时,fork 的过程是非常耗时的,fork 子进程时是会阻塞的,在这期间Redis 是不能响应客户端的请求的。

AOF
AOF 方式是将执行过的写指令记录下来,在数据恢复时按照从前到后的顺序再将指令都执行一遍。
AOF 优点

  • 使用AOF 会让你的Redis 更加持久化。
  • AOF 文件是一个只进行追加的日志文件,不需要在写入时读取文件。
  • Redis 可以在AOF 文件体积变得过大时,自动地在后台对AOF 进行重写。
  • AOF 文件可读性高,分析容易。
    AOF 缺点
  • 对于相同的数据来说,AOF 文件大小通常要大于RDB 文件。
  • 根据所使用的fsync 策略,AOF 的速度可能会慢于RDB

28、谈谈自己对于Spring AOP 的了解?

AOP(Aspect-Oriented Programming:面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。
在这里插入图片描述

29、Spring Bean 容器的生命周期是什么样的?

  • Bean 容器找到配置文件中Spring Bean 的定义。
  • Bean 容器利用Java Reflection API 创建一个Bean 的实例。
  • 如果涉及到一些属性值利用set()方法设置一些属性值。
  • 如果Bean 实现了BeanNameAware 接口,调用setBeanName()方法,传入Bean 的名字。
  • 如果Bean 实现了BeanClassLoaderAware 接口,调用setBeanClassLoader()方法,传入ClassLoader 对象的实例。
  • 如果Bean 实现了BeanFactoryAware 接口,调用setBeanFactory()方法,传入BeanFactory 对象的实例。
  • 与上面的类似,如果实现了其他*.Aware 接口,就调用相应的方法。
  • 如果有和加载这个Bean 的Spring 容器相关的BeanPostProcessor 对象,执行
    postProcessBeforeInitialization() 方法
  • 如果Bean 实现了InitializingBean 接口,执行afterPropertiesSet()方法。
  • 如果Bean 在配置文件中的定义包含init-method 属性,执行指定的方法。
  • 如果有和加载这个Bean 的Spring 容器相关的BeanPostProcessor 对象,执行
    postProcessAfterInitialization() 方法
  • 当要销毁Bean 的时候,如果Bean 实现了DisposableBean 接口,执行destroy() 方
    法。
  • 当要销毁Bean 的时候,如果Bean 在配置文件中的定义包含destroy-method 属性,
    执行指定的方法。

更加详细Spring Bean生命周期理解点击此处

30、RabbitMQ 如何保证消息不丢失?

生产者:
方案1:开启RabbitMQ 事务(同步,性能差)
方案2:开启confirm 模式(异步,性能较好)

MQ:
(1)exchange 持久化
(2)queue 持久化
(3)消息持久化

消费者:关闭自动ACK

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值