当前位置: 首页 > news >正文

some面试题2

一.Spring 容器中 Bean 生命周期的核心流程。


✅ Spring 中 Bean 的完整生命周期(按顺序)

1. 通过 BeanDefinition 获取 Bean 的定义信息

  • Spring 启动时会加载配置文件(XML 或注解),将每个 Bean 的元数据封装成一个 BeanDefinition 对象。
  • 包括:类名、作用域(singleton / prototype)、是否懒加载、构造函数参数、属性值等。
// 示例:获取某个 bean 的定义
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
BeanDefinition bd = beanFactory.getBeanDefinition("yourBeanName");

2. 调用构造函数实例化 Bean

  • 根据 BeanDefinition 中的信息,使用默认构造方法或带参数的构造方法创建 Bean 实例。
  • 如果是原型(prototype)作用域,则每次请求都会新建一个实例;如果是单例(singleton),则只会创建一次。
YourBean yourBean = new YourBean(); // 实际由 Spring 内部反射调用

3. Bean 的依赖注入

  • 自动装配或手动配置的依赖项(如 @Autowired@Resource、setter 注入、构造器注入)在此阶段被注入。
  • 包括字段注入、setter 方法注入、构造方法注入等。
@Autowired
private SomeService someService;

4. 处理 Aware 接口

  • Spring 检查该 Bean 是否实现了某些特定的 Aware 接口,并自动回调相应的方法:
    • BeanNameAware.setBeanName():传入当前 Bean 的名称。
    • BeanFactoryAware.setBeanFactory():传入当前使用的 BeanFactory。
    • ApplicationContextAware.setApplicationContext():传入上下文对象。
public class MyBean implements BeanNameAware, ApplicationContextAware {private String beanName;private ApplicationContext context;@Overridepublic void setBeanName(String name) {this.beanName = name;}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) {this.context = applicationContext;}
}

5. BeanPostProcessor 前置处理

  • 所有注册的 BeanPostProcessor.postProcessBeforeInitialization() 方法会被依次调用。
  • 可以对 Bean 实例进行修改或包装(例如 AOP 代理生成)。
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {if ("myBean".equals(beanName)) {System.out.println("前置处理:" + beanName);}return bean; // 返回原始或包装后的 Bean}
}

6. 初始化方法

a. InitializingBean.afterPropertiesSet()
  • 如果 Bean 实现了 InitializingBean 接口,会调用其 afterPropertiesSet() 方法。
public class MyBean implements InitializingBean {@Overridepublic void afterPropertiesSet() {// 初始化逻辑}
}
b. init-method
  • 在 XML 配置中指定的 init-method,或使用 @Bean(initMethod = "xxx") 注解的方法也会在此时被调用。
@Bean(initMethod = "customInit")
public MyBean myBean() {return new MyBean();
}public class MyBean {public void customInit() {// 自定义初始化方法}
}

7. BeanPostProcessor 后置处理

  • 所有 BeanPostProcessor.postProcessAfterInitialization() 方法被调用。
  • 这是 AOP 最常见的介入点,比如为 Bean 创建动态代理。
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {if ("myBean".equals(beanName)) {System.out.println("后置处理:" + beanName);}return bean;
}

8. 使用 Bean

  • 此时 Bean 已完全初始化完成,可以正常使用。
  • 如果是单例 Bean,它将一直存在于 Spring 容器中直到容器关闭。

9. 销毁 Bean

  • 当容器关闭时,如果 Bean 是单例的,Spring 会执行销毁逻辑。
a. DisposableBean.destroy()
  • 如果 Bean 实现了 DisposableBean 接口,会调用 destroy() 方法。
public class MyBean implements DisposableBean {@Overridepublic void destroy() {// 销毁资源}
}
b. destroy-method
  • XML 配置或 @Bean(destroyMethod = "xxx") 指定的方法也会被执行。
@Bean(destroyMethod = "customDestroy")
public MyBean myBean() {return new MyBean();
}public class MyBean {public void customDestroy() {// 自定义销毁方法}
}

📌 总结:完整的 Spring Bean 生命周期流程图

[BeanDefinition加载]↓[实例化]↓[依赖注入]↓[Aware接口处理]↓
[BeanPostProcessor 前置]↓[InitializingBean/ init-method]↓
[BeanPostProcessor 后置]↓[使用中...]↓[DisposableBean/ destroy-method]↓[销毁完成]

💡 小贴士

  • 单例 vs 原型作用域

    • 单例 Bean 只创建一次,且在容器关闭时才会销毁。
    • 原型 Bean 每次请求都创建新实例,Spring 不负责管理其销毁。
  • AOP 是在哪个阶段生效?

    • 通常是在 BeanPostProcessor 的后置处理阶段完成的,例如 AbstractAutoProxyCreator
  • 自定义扩展点

    • 可以实现 BeanFactoryPostProcessor 修改 BeanDefinition;
    • 使用 ApplicationListener 监听容器事件;
    • 使用 SmartLifecycle 控制启动/停止逻辑。

二.类装载的执行过程


✅ Java 类加载机制的完整生命周期(7个阶段)

1. 加载(Loading)

  • 作用:查找并加载 .class 文件(可以来自本地磁盘、网络、加密文件等)。
  • 触发方式
    • 显式调用 Class.forName()
    • 隐式加载(如 new 一个对象时自动加载类);
    • 反射、序列化、反序列化等操作也会触发类加载。
  • 结果
    • 将类的二进制字节流转换为方法区的数据结构;
    • 在堆中生成一个 java.lang.Class 对象作为访问入口。
Class<?> clazz = Class.forName("com.example.MyClass"); // 触发类加载

2. 验证(Verification)

  • 作用:确保被加载的类的字节码是合法的、安全的,不会危害 JVM。
  • 验证内容
    • 文件格式验证(是否符合 .class 文件规范);
    • 元数据验证(是否有不正确的继承关系、错误的方法签名);
    • 字节码验证(保证方法体中的指令不会做出危害行为);
    • 符号引用验证(确保解析时能正确找到对应的类/方法/字段)。

⚠️ 如果验证失败,JVM会抛出 VerifyError 错误。


3. 准备(Preparation)

  • 作用:为类变量(static 修饰的变量)分配内存,并设置默认初始值(不是代码中赋的值)。
  • 注意
    • 不包括实例变量;
    • 初始值通常是零值(如 int=0, boolean=false, Object=null);
    • 常量(final static)在该阶段就会被显式初始化。
public static int value = 123; // 准备阶段会被设为 0,初始化阶段才会赋值为 123
public static final String NAME = "Tom"; // 常量会在准备阶段直接赋值

4. 解析(Resolution)

  • 作用:将常量池中的符号引用替换为直接引用(即实际内存地址)。
  • 符号引用:用一组符号来描述目标,比如类名、方法名、字段名;
  • 直接引用:指向目标的指针、偏移量或句柄。

例如:

SomeClass obj = new SomeClass(); // 调用 new 指令时,SomeClass 是符号引用

解析可以在初始化之前完成,也可以在初始化之后按需进行(称为 延迟解析)。


5. 初始化(Initialization)

  • 作用:真正执行类中定义的 Java 程序代码(静态变量赋值、静态代码块)。
  • 执行顺序
    • 父类先于子类;
    • 静态变量赋值与静态代码块按照代码顺序执行;
  • 触发时机
    • 主动使用类时才触发初始化(如创建实例、访问静态变量、调用静态方法等)。
static {System.out.println("静态代码块执行");
}

6. 使用(Using)

  • 作用:JVM 开始从 main 方法开始执行用户程序代码。
  • 此阶段属于程序运行期,不再属于类加载过程本身。
public static void main(String[] args) {MyClass obj = new MyClass(); // 使用类
}

7. 卸载(Unloading)

  • 作用:当类不再被使用且满足一定条件时,由 JVM 的垃圾回收器回收其占用的内存。
  • 卸载条件
    • 该类的所有实例都被回收;
    • 加载该类的 ClassLoader 被回收;
    • 该类对应的 java.lang.Class 对象没有被引用;
  • 通常发生在自定义类加载器加载的类中,Bootstrap 类加载器加载的类一般不会被卸载。

📌 总结图示

[加载] → [验证] → [准备] → [解析] → [初始化] → [使用] → [卸载]
阶段是否由JVM主导是否可扩展是否必须
加载✅(可通过自定义ClassLoader)
验证
准备
解析
初始化
使用
卸载❌(可选)

💡 示例代码演示类加载顺序

class Parent {static {System.out.println("父类静态代码块");}
}class Child extends Parent {static {System.out.println("子类静态代码块");}
}public class TestClassLoad {public static void main(String[] args) {Child child = new Child();}
}

输出结果:

父类静态代码块
子类静态代码块

三.自动装配(Autowiring)和 自动配置(Auto-Configuration)


✅ 一、Spring 的自动装配(Autowiring)

📌 定义:

自动装配 是 Spring IoC 容器的一种依赖注入方式,它可以根据类型(或名称)自动将 Bean 注入到其他 Bean 中,而无需手动通过 XML 配置或 Java Config 显式指定依赖关系。

🧩 常见的实现方式:

  1. @Autowired
    • 根据类型自动装配。
    • 可用于字段、构造方法、setter 方法等。
@Service
public class OrderService {@Autowiredprivate OrderRepository orderRepository;
}
  1. @Resource
    • 默认按名称自动装配(来自 J2EE 标准)。
    • 可以通过 name 属性指定 bean 名称。
@Resource(name = "userRepository")
private UserRepository userRepo;
  1. 构造函数注入 / setter 注入
    • 自动装配也可以通过构造器或 setter 实现。
@Autowired
public OrderService(OrderRepository repo) {this.repo = repo;
}

✅ 优点:

  • 减少冗余代码;
  • 提高可维护性和解耦性;
  • 更加符合面向接口编程的思想。

✅ 二、Spring Boot 的自动配置(Auto-Configuration)

📌 定义:

自动配置 是 Spring Boot 提供的一项功能,它基于类路径中的 jar 包和项目配置,自动创建并注册一些常用的 Bean 到 Spring 容器中,从而简化开发流程。

🔍 工作原理:

Spring Boot 使用了以下机制来实现自动配置:

  • @Conditional 注解家族:根据某些条件决定是否加载某个 Bean。
  • spring.factories 文件:位于 META-INF/spring.factories,列出所有自动配置类。
  • @EnableAutoConfiguration 注解:启用自动配置功能(通常由 @SpringBootApplication 间接引入)。

🧩 示例说明:

当你添加了如下依赖:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

Spring Boot 会自动为你配置:

  • 数据源(DataSource)
  • Hibernate/JPA 相关 Bean
  • 事务管理器(TransactionManager)
  • 如果你配置了数据库连接信息,还会自动创建 EntityManagerFactory

✅ 优点:

  • 极大简化了 Spring 应用的初始搭建;
  • 减少了大量的样板配置代码;
  • 支持开箱即用的默认行为;
  • 同时支持自定义覆盖默认配置。

🔄 对比总结:自动装配 vs 自动配置

特性自动装配(Autowiring)自动配置(Auto-Configuration)
所属框架Spring FrameworkSpring Boot
作用将已有的 Bean 自动注入到其他 Bean 中根据依赖和环境自动创建 Bean 并注册进容器
关键注解@Autowired, @Resource@EnableAutoConfiguration, @ConditionalOn...
是否需要先有 Bean否(自动帮你创建)
主要用途依赖注入简化配置,减少手动编写配置类
示例注入 Service 到 Controller自动配置数据源、WebMvc、Security 等

🧪 举个例子对比理解:

场景:我们要使用一个 EmailService

✅ 自动装配(前提:已经有 EmailService Bean 存在)
@Component
public class EmailService {public void sendEmail() { ... }
}@RestController
public class UserController {@Autowiredprivate EmailService emailService; // 自动注入已存在的 Bean
}
✅ 自动配置(前提:添加了相关 starter,如 spring-boot-starter-mail)

Spring Boot 会自动配置好 JavaMailSender 这个 Bean,你只需直接注入使用即可:

@RestController
public class UserController {@Autowiredprivate JavaMailSender mailSender; // 自动配置的 Bean
}

✅ 总结一句话:

自动装配(Autowiring) 是把已有的 Bean 自动注入到其他组件中;
自动配置(Auto-Configuration) 是根据依赖自动帮你创建并注册 Bean。

两者结合使用,是 Spring Boot 快速开发的核心思想之一。

如果你还想了解如何自定义自动配置类(比如写自己的 Starter),我也可以继续讲解 😄

相关文章:

  • 树莓派超全系列教程文档--(49)远程访问树莓派
  • 2.2.1 05年T3
  • 网络常识:网线和光纤的区别
  • 微信小程序的软件测试用例编写指南及示例
  • Java 继承(上)
  • JavaScript性能优化全景指南
  • STM32的DMA入门指南:让单片机学会“自动搬运“数据
  • 自动驾驶决策规划框架详解:从理论到实践
  • Asp.Net Core 通过JWT版本号实现JWT无法提前撤回的问题
  • GitHub push失败解决办法-fatal: unable to access ‘https://github.com/xxx
  • 封装文档核心知识点总结(通俗版)
  • 局域协作中的前端调试:WebDebugX 在本地多端调试中的实践
  • 自动驾驶中的博弈式交互规划:从理论到实践
  • @vue/composition-api
  • LINUX安装运行jeelowcode后端项目(idea启动)
  • kerberos在无痕浏览器 获取用户信息失败 如何判断是否无痕浏览器
  • 设计模式-开放封闭原则
  • AI智能体策略FunctionCalling和ReAct有什么区别?
  • Qtc++开发遇到的问题-按钮点击不管用?
  • 大模型三大缺陷与RAG破解之道
  • 吉林沈阳网站建设/黑科技引流推广神器
  • 一家专做中式设计的网站/chatgpt入口
  • wordpress 搜索增强/seo的中文意思是什么
  • 投资网站建设/竞价排名是按照什么来计费的
  • 学习html的网站/微信指数查询
  • 网站收录怎么做/寻找客户的渠道和方法