Java - Spring 篇

3.1 单例 Bean 线程安全

Spring 的 Bean 默认是单例的。是否线程安全取决于 Bean 是否有状态(成员变量)。无状态的 Bean 是线程安全的。


3.2 AOP

是什么

AOP(面向切面编程):将散落在各个方法中的重复代码(日志、权限、事务等)抽取出来统一管理,在运行时自动”织入”到目标方法中,实现业务逻辑与横切逻辑的解耦。

核心术语

术语含义
切面(Aspect)封装横切逻辑的类
连接点(Join Point)所有可被拦截的方法
切入点(Pointcut)实际被拦截的方法(表达式匹配)
通知(Advice)切面中要执行的具体逻辑
织入(Weaving)将切面代码插入目标方法的过程

五种通知类型

通知执行时机特点
@Before方法执行前不能阻止方法执行
@AfterReturning方法正常返回后可获取返回值
@AfterThrowing方法抛异常后可获取异常对象
@After方法执行完毕后(无论成功失败)类似 finally
@Around环绕整个方法最强大,可控制是否执行、修改返回值

底层原理

Spring AOP 基于动态代理:

  • JDK 动态代理:目标类实现了接口 → 基于接口生成代理
  • CGLIB 动态代理:目标类没实现接口 → 通过继承生成子类代理

一句话记忆:AOP = 把重复的”外围代码”抽出来,Spring 自动帮你”贴”到目标方法上,业务代码只关心核心逻辑。


3.3 Spring 事务失效场景

情景一:异常被捕获处理

原因:事务通知只有捕捉到异常才会回滚,如果目标自己 catch 处理掉了异常,事务无法感知。

解决:在 catch 块中抛出异常。

情景二:抛出检查异常

Spring 默认只回滚非检查异常(RuntimeException 及其子类)。

解决:配置 rollbackFor 属性。

情景三:非 public 方法

Spring AOP 基于动态代理,非 public 方法无法被代理,事务失效。


3.4 Bean 生命周期与循环依赖

Bean 生命周期

核心流程:

构造函数 → 依赖注入 → Aware 接口 → BeanPostProcessor#before 
→ InitializingBean → 自定义 init → BeanPostProcessor#after(AOP 发生地)

代码示例:

@Service
public class UserService implements BeanNameAware, InitializingBean {

    private String beanName;

    // 【第一步】构造函数
    public UserService() {
        System.out.println("1. [构造函数] 实例化 Bean");
    }

    // 【第三步】Aware 接口回调
    @Override
    public void setBeanName(String name) {
        this.beanName = name;
        System.out.println("2. [Aware接口] 获知自己的名字: " + name);
    }

    // 【第五步】初始化方法
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("4. [InitializingBean] 属性设置完毕");
    }

    // 【第六步】自定义 init 方法
    public void myInit() {
        System.out.println("5. [自定义Init] 执行自定义初始化逻辑");
    }
}

循环依赖

Spring 的死循环:A 依赖 B,B 依赖 A → 实例化嵌套,无法完成。

三级缓存解决:

缓存名称存放内容
一级缓存singletonObjects完全初始化好的 Bean
二级缓存earlySingletonObjects早期的半成品 Bean(已实例化但未注入)
三级缓存singletonFactoriesBean 工厂(可生成代理对象)

构造方法导致的循环依赖:三级缓存也解决不了!因为构造方法是在实例化阶段执行的,连半成品都拿不到。


3.5 Spring MVC 执行流程

Spring MVC = 基于 Servlet 技术构建的、以 DispatcherServlet 为总指挥的、帮你自动化处理 HTTP 请求全流程的 Web 框架。


3.6 Spring Boot 自动配置

核心流程:

  1. 引导类上的 @SpringBootApplication 封装了三个注解:

    • @SpringBootConfiguration
    • @EnableAutoConfiguration(核心)
    • @ComponentScan
  2. @EnableAutoConfiguration 通过 @Import 导入配置选择器,读取 classpath 下 META-INF/spring.factories 中的配置类全类名

  3. 条件注解(如 @ConditionalOnClass)判断是否有对应的 class 文件,有则加载该配置类的所有 Bean 到容器


3.7 MyBatis

执行流程

  1. 映射文件加载 — 加载相关配置
  2. 构建会话工厂 — 全局唯一,生成 SqlSession
  3. 创建会话 — 项目与数据库的会话,包含执行 SQL 的所有方法
  4. 调用 Executor 执行器 — 真正执行数据库操作,也负责查询缓存的维护
  5. MappedStatement 对象 — 将数据参数转变为数据库框架可理解的形式

一级缓存 vs 二级缓存

缓存作用域默认状态
一级缓存SqlSession 级别默认开启,Session flush/close 后清空
二级缓存全局(Mapper 级别)需手动开启

SqlSession = 完成一次数据库操作的临时通道。用完即开,用完即关,专人专用。