Spring

注意:括号中为八股在每次面试中出现的概率

Bean 的生命周期了解么?(576/1759=32.7%)

Bean 的生命周期是 Spring 容器管理 Bean 从创建到销毁的整个过程,主要包括实例化、属性赋值、初始化和销毁这四个阶段,下面我将分阶段进行讲述。

首先,创建 Bean 实例。Spring 容器会根据配置文件或注解信息,使用 Java 反射机制实例化 Bean。

然后,属性赋值和依赖注入。Spring 会解析 Bean 的属性,例如 @Autowired、@Value 或 @Resource 等注解,并通过构造方法或 setter 方法将依赖注入到 Bean 中。

接着,初始化 Bean。在这个阶段,Spring 会进行一些额外的处理:

如果 Bean 实现了 BeanNameAware、BeanClassLoaderAware 或 BeanFactoryAware 接口,Spring 会调用相应的 set 方法,将 Bean 名称、类加载器和 BeanFactory 传递给 Bean。

如果有 BeanPostProcessor 处理器,Spring 会在初始化前调用 postProcessBeforeInitialization() 方法。

如果 Bean 实现了 InitializingBean 接口,则会执行 afterPropertiesSet() 方法。

如果在配置中指定了 init-method,Spring 会调用该方法执行自定义初始化逻辑。

初始化后,Spring 还会调用 BeanPostProcessor 的 postProcessAfterInitialization() 方法。

最后,销毁 Bean。当 Spring 容器关闭时,它会销毁 Bean:

如果 Bean 实现了 DisposableBean 接口,则会执行 destroy() 方法。

如果在配置中定义了 destroy-method,则会调用指定的销毁方法。

如果使用 @PreDestroy 注解标记了销毁前的方法,Spring 也会执行该方法释放资源。

如何记忆:

1.口诀记忆法

口诀:

创建实例先登场,

属性赋值注入忙。

初始化中三步走,

销毁资源不慌张。

解释:

“创建实例先登场” :对应 Bean 的实例化阶段。

“属性赋值注入忙” :对应属性赋值和依赖注入阶段。

“初始化中三步走” :对应初始化阶段的三个主要步骤(Aware 接口、BeanPostProcessor 前后处理、自定义初始化逻辑)。

“销毁资源不慌张” :对应销毁阶段,释放资源。

2.联想记忆法

联想:开餐馆的流程

创建实例 :开一家新餐馆,首先要租下店面(创建 Bean 实例)。

属性赋值 :装修店面并采购设备(属性赋值),比如冰箱、烤箱等(依赖注入)。

初始化

餐馆老板告诉员工餐馆的名字(Aware 接口)。

厨师检查厨房设备是否正常(BeanPostProcessor 前处理)。

正式开业前,老板安排试营业(InitializingBean 或 init-method)。

开业后,厨师调整菜单(BeanPostProcessor 后处理)。

销毁 :餐馆关门时,清理库存、关闭设备(DisposableBean 或 destroy-method)。

拓展:

1.Spring Bean 的生命周期图

2.Bean 是线程安全的吗?

Spring 框架中的 Bean 是否具备线程安全性,主要取决于它的作用域以及是否包含可变状态。

Bean 线程安全性的影响因素

Spring 默认的 Bean 作用域是 singleton,即在 IoC 容器中只会创建一个实例,并被多个线程共享。如果这个 Bean 维护了可变的成员变量,就可能在并发访问时引发数据不一致的问题,从而导致线程安全风险。

而 prototype 作用域 下,每次获取 Bean 都会创建新的实例,因此不会发生资源竞争,自然也就没有线程安全问题。

单例 Bean 是否一定不安全?

不一定!

无状态 Bean 是线程安全的:例如常见的 Service 或 Dao 层 Bean,它们通常不存储可变数据,仅执行业务逻辑,因此不会受到并发影响。

有状态 Bean 可能会引发线程安全问题:如果 Bean 存储了可变成员变量,比如用户会话信息、计数器等,可能会因多个线程同时访问导致数据不一致。

解决有状态 Bean 的线程安全问题

如果一个单例 Bean 需要维护状态,可通过以下方式确保线程安全:

设计为无状态 Bean:尽量避免定义可变成员变量,或在方法内部使用局部变量。

使用 ThreadLocal:让每个线程拥有独立的变量副本,防止数据共享导致冲突。

同步控制:在访问共享资源时,使用 synchronized 或 ReentrantLock 进行加锁,确保线程互斥访问。

3.将一个类声明为 Bean 的注解有哪些?

注解

用途

示例场景

@Component

通用注解,标记任意类为 Bean

通用组件

@Service

标记服务层组件

业务逻辑层

@Repository

标记数据访问层组件

数据库操作

@Controller

标记表现层组件

Web 控制器

@Configuration

配置类,配合 @Bean 显式声明 Bean

自定义 Bean 配置

@Conditional

根据条件动态注册 Bean

环境或逻辑条件

@Profile

根据激活的环境注册 Bean

不同环境下的配置

@Scope

定义 Bean 的作用域

单例、原型等

@RestController

RESTful Web 服务控制器

API 接口开发

@EnableScheduling

配合 @Scheduled 声明定时任务 Bean

定时任务

谈谈自己对于 Spring IoC 的了解(664/1759=37.7%)

Spring IoC(Inversion of Control,控制反转)是 Spring 框架的核心机制之一,负责管理对象的创建、依赖关系和生命周期,从而实现组件解耦,提升代码的可维护性和扩展性。接下来我会详细讲述 Spring IoC 的核心概念、实现方式、工作流程以及它解决的问题。

首先,IoC 的核心思想 是将对象的管理权从应用程序代码中转移到 Spring 容器。传统方式下,类 A 依赖于类 B,A 需要自己创建 B 的实例,而在 IoC 模式下,Spring 负责实例化和注入 B,A 只需要声明依赖即可。

其次,Spring IoC 主要通过依赖注入(DI)来实现。Spring 通过 XML 配置、Java 注解(@Autowired、@Resource)或 Java 代码(@Bean)定义 Bean 及其依赖关系,容器会在运行时自动解析并注入相应的对象。

接着,Spring IoC 的工作流程 可以分为三个阶段:

第一个阶段是IOC 容器初始化,

Spring 解析 XML 配置或注解,获取所有 Bean 的定义信息,生成 BeanDefinition。

BeanDefinition 存储了 Bean 的基本信息(类名、作用域、依赖等),并注册到 IOC 容器的 BeanDefinitionMap 中。

这个阶段完成了 IoC 容器的初始化,但还未实例化 Bean。

第二个阶段是Bean 实例化及依赖注入

Spring 通过反射实例化那些 未设置 lazy-init 且是单例模式 的 Bean。

依赖注入(DI)发生在这个阶段,Spring 根据 BeanDefinition 解析 Bean 之间的依赖关系,并通过构造方法、setter 方法或字段注入(@Autowired)完成对象的注入。

第三个阶段是Bean 的使用

业务代码可以通过 @Autowired 或 BeanFactory.getBean() 获取 Bean。

对于 设置了 lazy-init 的 Bean 或非单例 Bean,它们的实例化不会在 IoC 容器初始化时完成,而是在 第一次调用 getBean() 时 进行创建和初始化,且 Spring 不会长期管理它们。

最后,Spring IoC 主要解决三个问题

第一个是降低耦合,组件之间通过接口和依赖注入解耦,增强了代码的灵活性。

第二个是简化对象管理,开发者无需手动创建对象,Spring 统一管理 Bean 生命周期。

第三个是提升维护性,当需要修改依赖关系时,只需调整配置,而无需修改业务代码。

如何记忆:

1.口诀记忆法

口诀:

控制反转交容器,

依赖注入解耦合。

初始化时解析好,

实例注入用得妙。

使用阶段灵活调,

降低耦合效率高。

解释:

“控制反转交容器” :IoC 核心思想是将对象管理权交给 Spring 容器。

“依赖注入解耦合” :通过依赖注入(DI)实现组件之间的解耦。

“初始化时解析好” :IoC 容器初始化阶段解析 BeanDefinition。

“实例注入用得妙” :Bean 实例化和依赖注入阶段完成对象创建与注入。

“使用阶段灵活调” :业务代码可以通过多种方式获取 Bean。

“降低耦合效率高” :Spring IoC 解决了耦合问题,提升了代码维护性。

2.谐音记忆法

谐音:爱豆三步曲

“爱” :Inversion of Control(控制反转)。

“豆” :Dependency Injection(依赖注入)。

“三步曲” :Spring IoC 的三个主要阶段(初始化、实例化、使用)。

具体谐音:

“爱” :控制反转,爱上了 Spring 容器。

“豆” :依赖注入,像一颗颗小豆子被种下。

“三步曲” :第一步:解析清单(初始化);第二步:种下豆子(实例化);第三步:收获果实(使用)。

示例:

想象一个农夫种豆子的过程:

农夫(开发者)把种子(对象)交给土地(Spring 容器);

土地自动浇水施肥(依赖注入);

最后收获成熟的豆子(使用阶段)。

拓展:

1.传统应用程序图

2.IoC控制反转图

3.IoC 和 DI 的区别?

IoC(Inversion of Control,控制反转)和 DI(Dependency Injection,依赖注入)是 Spring 框架中非常重要的两个概念。虽然它们密切相关,但它们的含义和作用有所不同。以下是它们的区别及联系:

(1)定义与核心思想

IoC(控制反转)

定义:控制反转是一种设计原则,指的是将对象的创建、依赖管理和生命周期的控制权从应用程序代码转移到框架或容器中。

核心思想:传统开发中,对象需要自己负责创建依赖的对象(即“正向控制”)。而在 IoC 中,对象不再负责创建依赖,而是由容器来管理这些依赖关系。

DI(依赖注入)

定义:依赖注入是 IoC 的一种实现方式,指的是容器通过构造方法、setter 方法或字段注入的方式,将对象的依赖自动传递给它。

核心思想:对象只需要声明它需要的依赖,而不需要关心如何获取这些依赖。容器会负责将依赖注入到对象中。

(2)区别对比

维度

IoC(控制反转)

DI(依赖注入)

定义

一种设计原则,强调控制权的转移

一种具体实现方式,用于实现 IoC

关注点

对象的创建、依赖管理和生命周期的控制权

如何将依赖传递给对象

实现方式

通过容器(如 Spring 容器)管理对象

通过构造方法、setter 方法或字段注入依赖

范围

更广泛,包含 DI 和其他实现方式

是 IoC 的一个子集

示例

Spring 容器接管了对象的创建和管理

Spring 容器通过 @Autowired 注入依赖

(3)联系

DI 是 IoC 的实现方式:依赖注入是控制反转的一种具体实现形式。IoC 是一种更广泛的设计原则,而 DI 是 IoC 的一种技术手段。

共同目标:两者都旨在降低代码的耦合性,提升代码的可维护性和扩展性。

(4)示例说明

传统方式(无 IoC 和 DI)

public class UserService {
    private UserRepository userRepository;

    public UserService() {
        this.userRepository = new UserRepository(); // 自己创建依赖
    }

    public void addUser(String name) {
        userRepository.save(name);
    }
}

问题:UserService 直接依赖 UserRepository,耦合度高。如果需要更换 UserRepository 的实现,必须修改 UserService 的代码。

使用 IoC 和 DI

@Component
public class UserService {
    @Autowired
    private UserRepository userRepository; // 声明依赖

    public void addUser(String name) {
        userRepository.save(name);
    }
}

改进:UserService 不再负责创建 UserRepository,而是通过 Spring 容器注入依赖。如果需要更换 UserRepository 的实现,只需在配置中调整,无需修改 UserService 的代码。

什么是动态代理?(453/1759=25.8%)

动态代理是一种在运行时动态生成代理对象,并在代理对象中增强目标对象方法的技术。它被广泛用于 AOP(面向切面编程)、权限控制、日志记录等场景,使得程序更加灵活、可维护。动态代理可以通过 JDK 原生的 Proxy 机制或 CGLIB 方式实现。接下来我会讲述动态代理的实现方式和执行流程。

首先,JDK 动态代理基于接口,适用于代理实现了接口的对象,当使用 JDK 动态代理时,主要分为四步,

第一步是定义接口,由于动态代理是基于接口进行代理的,因此目标对象必须实现接口。

第二步是创建并实现 InvocationHandler 接口,并在 invoke 方法中定义增强逻辑。

第三步是生成代理对象,使用 Proxy.newProxyInstance 创建代理对象,代理对象内部会调用 invoke 方法。

第四步是调用代理方法,当调用代理对象的方法时,invoke 方法会被触发,执行增强逻辑,并最终调用目标方法。

其次,

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

【神品八股】1759篇面经精华 文章被收录于专栏

神哥引路,稳稳起步!!早鸟特惠,仅剩177名额!晚了就涨到29.9了! 核心亮点: 1.数据驱动,精准高频:基于1759篇面经、24139道八股题,精准提炼真实高频八股。 2.科学记忆,高效掌握:融合科学记忆法和面试表达技巧,记得住,说得出。 3.提升思维,掌握财商:不仅可学习八股,更可教你变现,3个月赚不回购买价,全额退。 适宜人群: 在校生、社招求职者及自学者。

全部评论
先收藏,然后吃灰
1 回复 分享
发布于 昨天 08:33 江苏
1 回复 分享
发布于 昨天 09:00 广东
开背!
1 回复 分享
发布于 昨天 11:07 四川

相关推荐

点赞 评论 收藏
分享
评论
3
7
分享

创作者周榜

更多
牛客网
牛客企业服务