图灵300题-41~60-笔记003
图灵300题
图灵面试题视频:https://www.bilibili.com/video/BV17z421B7rB?spm_id_from=333.788.videopod.episodes&vd_source=be7914db0accdc2315623a7ad0709b85&p=20。
本文是学习笔记,如果需要面试没有时间阅读原博文,可以快速浏览笔记。
推荐深度阅读对应书籍或者知识点原文,避免碎片化学习。
41. 线程池的底层工作原理
线程池内部是通过队列+线程实现的。当利用线程池执行任务时:
- 如果此时线程池中的线程数量小于
corePoolSize
,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。 - 如果此时线程池中的线程数量等于
corePoolSize
,但是缓冲队列workQueue
未满,那么任务被放入缓冲队列。 - 如果此时线程池中的线程数量大于等于
corePoolSize
,缓冲队列workQueue
满,并且线程池中的数量小于maximumPoolSize
,建新的线程来处理被添加的任务。 - 如果此时线程池中的线程数量大于
corePoolSize
,缓冲队列workQueue
满,并且线程池中的数量等于maximumPoolSize
,那么通过handler
所指定的策略来处理此任务。 - 当线程池中的线程数量大于
corePoolSize
时,如果某线程空闲时间超过keepAliveTime
,线程将被终止。这样,线程池可以动态地调整池中的线程数。
42. 线程池中阻塞队列的作用及相关问题
- 一般的队列只能保证作为一个有限长度的缓冲区,如果超出了缓冲长度,就无法保留当前的任务了,阻塞队列通过阻塞可以保留住当前想要继续入队的任务。
阻塞队列可以保证任务队列中没有任务时阻塞获取任务的线程,使得线程进入wait
状态,释放cpu
资源。
阻塞队列自带阻塞和唤醒的功能,不需要额外处理,无任务执行时,线程池利用阻塞队列的take
方法挂起,从而维持核心线程的存活、不至于一直占用cpu
资源。 - 在创建新线程的时候,是要获取全局锁的,这个时候其它的就得阻塞,影响了整体效率。
就好比一个企业里面有10个(core
)正式工的名额,最多招10个正式工,要是任务超过正式工人数(task>core
)的情况下,工厂领导(线程池)不是首先扩招工人,还是这10人,但是任务可以稍微积压一下,即先放到队列去(代价低)。10个正式工慢慢干,迟早会干完的,要是任务还在继续增加,超过正式工的加班忍耐极限了(队列满了),就得招外包帮忙了(注意是临时工)。要是正式工加上外包还是不能完成任务,那新来的任务就会被领导拒绝了(线程池的拒绝策略)。
43. 线程池中线程复用原理
线程池将线程和任务进行解耦,线程是线程,任务是任务,摆脱了之前通过Thread
创建线程时的一个线程必须对应一个任务的限制。
在线程池中,同一个线程可以从阻塞队列中不断获取新任务来执行,其核心原理在于线程池对Thread
进行了封装,并不是每次执行任务都会调用Thread.start()
来创建新线程,而是让每个线程去执行一个“循环任务”,在这个“循环任务”中不停检查是否有任务需要被执行,如果有则直接执行,也就是调用任务中的run
方法,将run
方法当成一个普通的方法执行,通过这种方式只使用固定的线程就将所有任务的run
方法串联起来。
44. ReentrantLock
中的公平锁和非公平锁的底层实现
首先不管是公平锁和非公平锁,它们的底层实现都会使用AQS
来进行排队,它们的区别在于:线程在使用lock()
方法加锁时,如果是公平锁,会先检查AQS
队列中是否存在线程在排队,如果有线程在排队,则当前线程也进行排队;如果是非公平锁,则不会去检查是否有线程在排队,而是直接竞争锁。
不管是公平锁还是非公平锁,一旦没竞争到锁,都会进行排队,当锁释放时,都是唤醒排在最前面的线程,所以非公平锁只是体现在了线程加锁阶段,而没有体现在线程被唤醒阶段。
另外,ReentrantLock
是可重入锁,不管是公平锁还是非公平锁都是可重入的。
45. ReentrantLock
中tryLock()
和lock()
方法的区别
tryLock()
表示尝试加锁,可能加到,也可能加不到,该方法不会阻塞线程,如果加到锁则返回true
,没有加到则返回false
。lock()
表示阻塞加锁,线程会阻塞直到加到锁,方法也没有返回值。
46. CountDownLatch
和Semaphore
的区别和底层原理
CountDownLatch
表示计数器,可以给CountDownLatch
设置一个数字,一个线程调用CountDownLatch
的await()
将会阻塞,其他线程可以调用CountDownLatch
的countDown()
方法来对CountDownLatch
中的数字减一,当数字被减成0后,所有await
的线程都将被唤醒。
对应的底层原理就是,调用await()
方法的线程会利用AQS
排队,一旦数字被减为0,则会将AQS
中排队的线程依次唤醒。
Semaphore
表示信号量,可以设置许可的个数,表示同时允许最多多少个线程使用该信号量,通过acquire()
来获取许可,如果没有许可可用则线程阻塞,并通过AQS
来排队,可以通过release()
方法来释放许可,当某个线程释放了某个许可后,会从AQS
中正在排队的第一个线程开始依次唤醒,直到没有空闲许可。
47. Synchronized
的偏向锁、轻量级锁、重量级锁
- 偏向锁:在锁对象的对象头中记录一下当前获取到该锁的线程ID,该线程下次如果又来获取该锁就可以直接获取到了。
- 轻量级锁:由偏向锁升级而来,当一个线程获取到锁后,此时这把锁是偏向锁,此时如果有第二个线程来竞争锁,偏向锁就会升级为轻量级锁,之所以叫轻量级锁,是为了和重量级锁区分开来,轻量级锁底层是通过自旋来实现的,并不会阻塞线程。
- 如果自旋次数过多仍然没有获取到锁,则会升级为重量级锁,重量级锁会导致线程阻塞。
- 自旋锁:自旋锁就是线程在获取锁的过程中,不会去阻塞线程,也就无所谓唤醒线程,阻塞和唤醒这两个步骤都是需要操作系统去进行的,比较消耗时间,自旋锁是线程通过
CAS
获取预期的一个标记,如果没有获取到,则继续循环获取,如果获取到了则表示获取到了锁,这个过程线程一直在运行中,相对而言没有使用太多的操作系统资源,比较轻量。
48. ReentrantLock
比synchronized
- 关键字与类的区别
synchronized
是一个关键字,而ReentrantLock
是一个类。关键字是Java语言内置的语法元素,而类则是Java中面向对象编程的一种结构。 - 加锁与释放锁方式的区别
synchronized
会自动加锁与释放锁,当线程进入同步代码块时自动获取锁,执行完毕或抛出异常时自动释放锁。而ReentrantLock
需要程序员手动加锁与释放锁,通常通过调用lock()
方法加锁,unlock()
方法释放锁,并且unlock()
一般要放在finally
块中,以确保在任何情况下锁都能被正确释放,避免死锁。 - 实现层面的区别
synchronized
的底层是JVM层面的锁,它的实现依赖于JVM的内部机制。而ReentrantLock
是API层面的锁,它是基于Java的类库实现的,开发者可以通过调用相关方法来使用它。 - 公平性的区别
synchronized
是非公平锁,即线程获取锁的顺序不一定是按照请求锁的先后顺序。而ReentrantLock
可以选择是公平锁还是非公平锁,通过在创建ReentrantLock
对象时传入true
参数可以创建公平锁,使得线程按照请求锁的顺序获取锁。 - 锁状态标识的区别
synchronized
锁的状态,锁信息保存在对象头中。而ReentrantLock
通过代码中int
类型的state
来标识锁的状态,例如,state
为0表示锁未被占用,大于0表示锁被占用的次数(可重入次数) 。
49. 谈谈对AOP的理解
系统是由许多不同的组件所组成的,每一个组件各负责一块特定功能。除了实现自身核心功能之外,这些组件还经常承担着额外的职责。例如日志、事务管理和安全这样的核心服务经常融入到自身具有核心业务逻辑的组件中去。
这些系统服务经常被称为横切关注点,因为它们会跨越系统的多个组件。
当我们需要为分散的对象引入公共行为的时候,OOP
则显得无能为力。也就是说,OOP
允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。
日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。
在OOP
设计中,它导致了大量代码的重复,而不利于各个模块的重用。
AOP
:将程序中的交叉业务逻辑(比如安全,日志,事务等),封装成一个切面,然后注入到目标对象(具体业务逻辑)中去。AOP
可以对某个对象或某些对象的功能进行增强,比如对象中的方法进行增强,可以在执行某个方法之前额外地做一些事情,在某个方法执行之后额外地做一些事情。
50. 谈谈对IOC的理解
实际上就是个map(key, value)
,里面存的是各种对象(在xml
里配置的bean
节点、@repository
、@service
、@controller
、@component
),在项目启动的时候会读取配置文件里面的bean
节点,根据全限定类名使用反射创建对象放到map
里;扫描到打上上述注解的类还是通过反射创建对象放到map
里。
这个时候map
里就有各种对象了,接下来在代码里需要用到里面的对象时,再通过DI
注入(autowired
、resource
等注解,xml
里bean
节点内的ref
属性,项目启动的时候会读取xml
节点ref
属性根据id
注入,也会扫描这些注解,根据类型或id
注入;id
就是对象名)。
没有引入IOC
容器之前,对象A依赖于对象B,那么对象A在初始化或者运行到某一点的时候,自己必须主动去创建对象B或者使用已经创建的对象B。无论是创建还是使用对象B,控制权都在自己手上。
引入IOC
容器之后,对象A与对象B之间失去了直接联系,当对象A运行到需要对象B的时候,IOC
容器会主动创建一个对象B注入到对象A需要的地方。
通过前后的对比,不难看出来:对象A获得依赖对象B的过程,由主动行为变为了被动行为,控制权颠倒过来了,这就是“控制反转”这个名称的由来。
全部对象的控制权全部上缴给“第三方”IOC
容器,所以,IOC
容器成了整个系统的关键核心,它起到了一种类似“粘合剂”的作用,把系统中的所有对象粘合在一起发挥作用,如果没有这个“粘合剂”,对象与对象之间会彼此失去联系,这就是有人把IOC
容器比喻成“粘合剂”的由来。
51. 解释下Spring支持的几种bean的作用域
singleton
:默认,每个容器中只有一个bean
的实例,单例的模式由BeanFactory
自身来维护。该对象的生命周期是与Spring IOC
容器一致的(但在第一次被注入时才会创建)。prototype
:为每一个bean
请求提供一个实例。在每次注入时都会创建一个新的对象。request
:bean
被定义为在每个HTTP
请求中创建一个单例对象,也就是说在单个请求中都会复用这一个单例对象。session
:与request
范围类似,确保每个session
中有一个bean
的实例,在session
过期后,bean
会随之失效。application
:bean
被定义为在ServletContext
的生命周期中复用一个单例对象。websocket
:bean
被定义为在websocket
的生命周期中复用一个单例对象。global-session
:全局作用域,global-session
和Portlet
应用相关。当你的应用部署在Portlet
容器中工作时,它包含很多portlet
。如果你想要声明让所有的portlet
共用全局的存储变量的话,那么这全局变量需要存储在global-session
中。全局作用域与Servlet
中的session
作用域效果相同。
52. spring事务传播机制
多个事务方法相互调用时,事务如何在这些方法间传播。
方法A是一个事务的方法,方法A执行过程中调用了方法B,那么方法B有无事务以及方法B对事务的要求不同都会对方法A的事务具体执行造成影响,同时方法A的事务对方法B的事务执行也有影响,这种影响具体是什么就由两个方法所定义的事务传播类型所决定。
REQUIRED
(Spring
默认的事务传播类型):如果当前没有事务,则自己新建一个事务,如果当前存在事务,则加入这个事务。SUPPORTS
:当前存在事务,则加入当前事务,如果当前没有事务,就以非事务方法执行。MANDATORY
:当前存在事务,则加入当前事务,如果当前事务不存在,则抛出异常。REQUIRES_NEW
:创建一个新事务,如果存在当前事务,则挂起该事务。NOT_SUPPORTED
:以非事务方式执行,如果当前存在事务,则挂起当前事务。NEVER
:不使用事务,如果当前事务存在,则抛出异常。NESTED
:如果当前事务存在,则在嵌套事务中执行,否则REQUIRED
的操作一样(开启一个事务)。
NESTED
和REQUIRES_NEW
的区别
REQUIRES_NEW
是新建一个事务并且新开启的这个事务与原有事务无关,而NESTED
则是当前存在事务时(把当前事务称之为父事务)会开启一个嵌套事务(称之为一个子事务)。在NESTED
情况下父事务回滚时,子事务也会回滚,而在REQUIRES_NEW
情况下,原有事务回滚,不会影响新开启的事务。
NESTED
和REQUIRED
的区别
REQUIRED
情况下,调用方存在事务时,则被调用方和调用方使用同一事务,那么被调用方出现异常时,由于共用一个事务,所以无论调用方是否catch
其异常,事务都会回滚。而在NESTED
情况下,被调用方发生异常时,调用方可以catch
其异常,父事务不受影响,这样只有子事务回滚。
53. Spring中的Bean创建的生命周期有哪些步骤
Spring中一个Bean
的创建大概分为以下几个步骤:
- 推断构造方法。
- 实例化。
- 填充属性,也就是依赖注入。
- 处理
Aware
回调。 - 初始化前,处理
@PostConstruct
注解。 - 初始化,处理
InitializingBean
接口。 - 初始化后,进行
AOP
。
54.Spring事务失效
Spring事务的原理是基于AOP(面向切面编程)进行切面增强。当事务失效时,根本原因往往是AOP不起作用了。以下是几种常见的事务失效情况:
-
发生自调用
当类里面使用this
调用本类的方法时(这里的this
通常指普通的类实例 ),此时这个this
对象不是代理类,而是UserService
对象本身。这会导致事务失效,因为AOP的增强无法作用于这种自调用。
解决方案:让this
变成UserService
的代理类即可。可以通过将当前类注入到自身(借助Spring容器),然后使用注入的代理对象调用方法。 -
方法非public
@Transactional
只能用于public
的方法上,否则事务不会生效。如果要在非public
方法上使用事务,可以开启AspectJ代理模式。在AspectJ代理模式下,Spring会通过字节码生成技术,直接在类的字节码中织入事务相关的逻辑,从而绕过访问修饰符的限制。 -
数据库不支持事务
如果所使用的数据库本身不支持事务操作,那么Spring事务自然无法生效。例如,MySQL的MyISAM存储引擎默认不支持事务,只有InnoDB存储引擎支持事务。在这种情况下,需要将数据库表的存储引擎更改为支持事务的类型。 -
未被Spring管理
如果一个对象没有被Spring容器管理,那么它也就无法享受Spring事务的功能。只有被Spring容器管理的Bean,Spring才能在其方法调用前后织入事务相关的逻辑。确保相关的类被正确地配置为Spring Bean,例如通过@Component
、@Service
等注解进行标注。 -
异常被吃掉
当事务中的异常被捕获并且没有重新抛出,或者抛出的异常没有被定义为需要回滚的异常(Spring默认只有RuntimeException
及其子类会触发事务回滚 ),事务将不会回滚。为了保证事务回滚,需要在捕获异常时根据业务需求,决定是否重新抛出异常,或者在@Transactional
注解中显式指定需要回滚的异常类型。
55. spring中的Bean是线程安全的吗
在Spring
框架中,Bean
的线程安全性并不是由Spring
本身直接保证的,而是取决于Bean
的作用域(Scope
)以及Bean
内部的状态管理。
- Bean的作用域
Spring
提供了几种Bean
的作用域,这些作用域决定了Bean
的生命周期和可见性:
Singleton
(单例):这是Spring
的默认作用域。在单例作用域中,Spring
容器在整个应用中只维护Bean
的一个实例。这意味着所有对这个Bean
的引用都指向同一个对象。因此,如果Bean
是有状态的(即它的实例变量在多次方法调用之间保持状态),并且这些状态被多个线程共享,那么这个Bean
就不是线程安全的。Prototype
(原型):每次请求都会创建一个新的Bean
实例。因此,原型作用域的Bean
是线程安全的,因为每个线程都会获得自己的Bean
实例,不会共享状态。Request
(请求):每次HTTP
请求都会创建一个新的Bean
实例,并且该实例仅在当前HTTP
请求内有效。这通常用于Web
应用程序中,并且由于每个请求都有自己的实例,因此也是线程安全的。Session
(会话):每个HTTP
会话都会创建一个新的Bean
实例,并且该实例仅在当前HTTP
会话内有效。这同样适用于Web
应用程序,并且由于每个会话都有自己的实例,因此也是线程安全的。Application/Global Session
(全局会话):这通常用于Portlet
应用程序中,类似于Session
作用域,但它在Portlet
的上下文中具有更广泛的范围。
- 线程安全性的实现
- 无状态
Bean
:如果Bean
是无状态的(即它不包含任何实例变量来存储状态),那么它自然是线程安全的,因为所有线程都共享相同的实例,但没有任何状态可以修改。 - 同步方法:如果
Bean
是有状态的,并且需要被多个线程访问,那么可以通过在访问共享资源的方法上添加同步(synchronized
)关键字来确保线程安全。然而,这可能会降低性能,因为一次只有一个线程可以执行同步方法。 - 使用并发集合:如果
Bean
包含集合或其他共享资源,并且这些资源需要被多个线程访问,那么应该使用Java
并发包(java.util.concurrent
)中提供的线程安全集合。
56. BeanFactory
和ApplicationContext
- 功能与特性
BeanFactory
:- 是Spring框架中最底层的接口,是IoC(控制反转)容器的核心。
- 主要负责Bean的定义、加载、实例化、依赖注入和生命周期管理。
- 提供了IoC容器最基本的功能,但功能相对基础,主要关注于Bean的实例化、配置和生命周期管理。
- 不支持国际化、资源文件访问等高级功能。
ApplicationContext
:- 是
BeanFactory
的子接口,扩展了BeanFactory
的功能,并提供了更全面的容器特性。 - 是Spring应用中的核心容器,用于管理和配置应用中的对象(称为beans)。
- 支持国际化、资源访问、事件传播等高级功能。
- 提供了更丰富的Bean管理功能,如自动装配、生命周期管理等。
- 是
- 加载方式
BeanFactory
:- 采用延迟加载(Lazy Loading)方式,即在容器启动时不会立即创建所有Bean,而是在Bean被请求时(通过
getBean()
方法)才会创建和加载。 - 这种加载方式使得
BeanFactory
在资源受限的环境或轻量级的应用程序中具有优势,因为它具有较小的内存占用和较快的启动速度。
- 采用延迟加载(Lazy Loading)方式,即在容器启动时不会立即创建所有Bean,而是在Bean被请求时(通过
ApplicationContext
:- 在容器启动时,会一次性加载并实例化所有Bean。
- 这种加载方式使得
ApplicationContext
在运行时的速度相对BeanFactory
更快,因为它避免了在请求Bean时的加载和实例化开销。 - 同时,由于一次性加载所有Bean,
ApplicationContext
能够在启动时发现配置错误,如依赖注入问题。
- 配置方式
BeanFactory
:- 通常以编程的方式创建和配置。
- 配置相对较为繁琐,需要手动编写代码来定义和注册Bean。
ApplicationContext
:- 支持以声明的方式(如XML配置文件、注解等)进行配置。
- 配置更加灵活和方便,开发者可以通过XML文件或注解来定义和注册Bean,减少了代码量并提高了开发效率。
- 使用场景
BeanFactory
:- 主要面向Spring框架本身,为框架内部的Bean管理提供基础设施。
- 适用于资源受限的环境或轻量级的应用程序。
ApplicationContext
:- 主要面向使用Spring框架的开发者,提供了更全面的容器服务。
- 适用于大多数企业级应用,特别是那些需要全面容器服务的场景。
- 提供了更丰富的功能和更好的开发体验。
57. Spring事务传播机制
-
基于事务管理器和AOP机制
Spring中的事务实现是基于事务管理器和AOP(面向切面编程)的。 -
@Transactional
注解判断
首先,对于使用了@Transactional
注解的方法,Spring会创建一个代理对象作为Bean。当调用该代理对象的方法时,会先判断该方法上是否加了@Transactional
注解。 -
事务连接创建
如果加了,那么利用事务管理器创建一个数据库连接。 -
自动提交设置
并且将该数据库连接的autocommit
属性设为false
,禁止此连接的自动提交,这是实现Spring事务非常重要的一步。 -
方法执行与事务提交
然后执行当前方法,方法中会执行SQL。 -
异常处理与事务提交策略
执行完当前方法后,如果没有出现异常就直接提交事务,否则仍然回滚事务。如果出现了异常,并且这个异常是需要回滚的就会回滚事务。事务的隔离级别对应的就是数据库的隔离级别。 -
事务传播机制
Spring事务的传播机制是Spring事务自己实现的,也是事务中最复杂的。Spring事务的传播机制基于数据库连接来做的,一个数据库连接一个事务,如果传播机制配置为需要新开一个事务,那么实际上就是先建立一个新的数据库连接,然后在这个新连接上进行事务操作。
58. Spring中什么时候@Transactional会失效
在Spring框架中,@Transactional
注解用于对方法或类进行标注,以启用声明式事务管理。然而,在某些情况下,@Transactional
注解可能不会按预期工作,导致事务管理失效。以下是一些可能导致@Transactional
失效的情况:
- 私有方法:如果
@Transactional
注解应用在一个私有方法上,由于Spring AOP通常使用CGLIB来代理类,而CGLIB无法覆盖私有方法,因此事务管理不会生效。 - 同一类中的方法调用:当一个带有
@Transactional
注解的方法被同一类中的其他方法调用时,这种调用实际上是在调用类自身而不是通过代理对象,因此事务管理也不会生效。 - 异常被捕获且未抛出:如果在方法内捕获了所有异常并且没有重新抛出,即使发生了异常,事务也不会回滚。默认情况下,只有未检查异常(即
RuntimeException
及其子类)或者Error
才会触发事务回滚。 - 非公共方法:
@Transactional
注解通常只能应用于公共方法上。如果方法不是公共的,事务管理可能不会生效。 - 错误的事务传播行为:如果方法内的事务传播行为(如
PROPAGATION_REQUIRED
等)与实际的事务需求不符,也可能导致事务管理失效。 - 数据库不支持事务:例如,MySQL的MyISAM存储引擎不支持事务,因此即使正确配置了事务注解,如果底层数据库不支持,事务也无法生效。
- 事务隔离级别和锁定问题:如果事务隔离级别设置不当,或者存在死锁等问题,虽然事务本身可能仍然生效,但是可能会导致一些不可预料的行为。
- 配置问题:如果Spring的事务管理配置不正确,比如没有启用事务管理器或事务切面,那么
@Transactional
注解也将无效。
要解决这些问题,可以确保@Transactional
注解正确地应用在公共方法上,并确保任何必要的异常都被适当地处理以触发回滚。另外,确认应用程序的事务配置以及使用的数据库存储引擎支持事务处理。如果在同一类中调用事务性方法,可以通过代理对象显式调用该方法,或者重构代码以避免这种情况。
59. Spring容器启动流程是怎样的
- 在创建Spring容器,也就是启动Spring时:
- 首先会进行扫描,扫描得到所有的
BeanDefinition
对象,并存在一个Map
中。 - 然后筛选出非懒加载的单例
BeanDefinition
进行创建Bean,对于多例Bean不需要在启动过程中去进行创建,对于多例Bean会在每次获取Bean时利用BeanDefinition
去创建。 - 利用
BeanDefinition
创建Bean就是Bean的创建生命周期,这期间包括了合并BeanDefinition
、推断构造方法、实例化、属性填充、初始化前、初始化、初始化后等步骤,其中AOP就是发生在初始化后这一步骤中。 - 单例Bean创建完了之后,Spring会发布一个容器启动事件。
- Spring启动结束。
- 在源码中会更复杂,比如源码中会提供一些模板方法,让子类来实现,比如源码中还涉及到一些
BeanFactoryPostProcessor
和BeanPostProcessor
的注册,Spring的扫描就是通过BeanFactoryPostProcessor
来实现的,依赖注入就是通过BeanPostProcessor
来实现的。 - 在Spring启动过程中还会去处理
@Import
等注解。
60. BeanFactory中的设计模式
- 工厂模式:
BeanFactory
体现了工厂模式,负责创建和管理Bean对象。 - FactoryBean:一种特殊的Bean,用于创建复杂对象或具有特殊创建逻辑的对象,也是工厂模式的一种应用。
- 适配器模式:
AdvisorAdapter
接口对Advisor
进行适配,体现了适配器模式,使不同类型的Advisor
能在Spring的环境中统一使用。 - 访问者模式:
PropertyAccessor
接口作为属性访问器,用于访问和设置某个对象的属性,在一些复杂的属性操作场景下涉及访问者模式的思想。 - 装饰器模式:
BeanWrapper
对Bean进行包装,增强其功能,符合装饰器模式的特点。 - 代理模式:AOP在Spring中的实现大量使用了代理模式,为Bean创建代理对象来实现切面功能。
- 观察者模式:Spring的事件监听机制是观察者模式的典型应用,当特定事件发生时通知相关的监听器。
- 策略模式:
InstantiationStrategy
根据不同的情况进行实例化,体现了策略模式,提供多种实例化策略供选择。 - 模板模式:
JdbcTemplate
是模板模式的应用,定义了操作数据库的模板方法,子类可以根据具体需求进行定制。 - 委派模式:
BeanDefinitionParserDelegate
在解析BeanDefinition
过程中使用了委派模式,将复杂的解析工作委派给不同的组件处理。 - 责任链模式:
BeanPostProcessor
接口形成了责任链模式,多个BeanPostProcessor
可以依次对Bean进行处理,增强Bean的功能。