Spring 框架[从搭建到内核,了解Spring 这篇文章就够了]

目录

Spring 是什么?

Spring 的核心思想:IOC + AOP

一、核心思想一:IOC(控制反转,Inversion of Control)

1.什么是IoC?

IoC开发(Spring 控制)

IoC 的 “反转” 本质:

2.IoC 的三大核心组件:Bean、容器、依赖注入

组件 1:Bean(被管理的对象)

什么是 Bean?

Bean 的核心属性(元数据):

组件 2:IoC 容器(Bean 的 “管家”)

Spring 容器的体系结构(核心接口):

组件 3:依赖注入(DI,Dependency Injection)

什么是 DI?

依赖注入的 3 种方式(Spring 支持,推荐前 2 种):

依赖注入的匹配规则:

3.IoC 容器的完整工作流程(从启动到销毁)

步骤 1:容器初始化(启动容器)

步骤 2:解析 Bean 定义(生成 BeanDefinition)

步骤 3:BeanFactoryPostProcessor 增强(扩展点)

步骤 4:创建 Bean 实例(实例化)

步骤 5:依赖注入(装配)

步骤 6:BeanPostProcessor 增强(扩展点)

步骤 7:Bean 初始化(初始化回调)

步骤 8:Bean 就绪(可供使用)

步骤 9:Bean 销毁(容器关闭时)

4.IoC 底层实现技术揭秘

1. 反射(Reflection)

2. 工厂模式(Factory Pattern)

3. 注册中心模式(Registry Pattern)

5.IoC 的高级特性与实际应用

1. 循环依赖的解决(Spring 的核心能力)

Spring 如何解决循环依赖?

2. 延迟加载(Lazy Initialization)

3. 条件注解(@Conditional)

二、核心思想二:AOP(面向切面编程,Aspect-Oriented Programming)

1.AOP 的核心思想是什么?

为什么需要AOP?(OOP 的痛点)

AOP 的核心解决思路:「分离关注点」

2.AOP 的核心概念(必须熟记,Spring AOP 落地的基础)

3.AOP 的底层实现:动态代理(Spring AOP 的核心技术)

1. 两种代理方式的对比(重点)

2. 手动模拟动态代理(理解 Spring AOP 底层逻辑)

步骤 1:定义目标接口和目标对象(业务逻辑)

步骤 2:定义横切逻辑(日志增强)

步骤 3:通过 JDK 动态代理生成代理对象,织入横切逻辑

步骤 4:测试代理对象(触发增强逻辑)

输出结果(横切逻辑自动植入,无侵入业务代码):

4.Spring AOP 的核心特性(与原生 AOP 框架的区别)

5.Spring AOP 实战:注解驱动实现切面(核心用法)

1. 环境准备(Maven 依赖)

6.Spring AOP 切入点表达式(重点:精准匹配目标方法)

1. 最常用:execution 表达式(匹配方法签名)

2. 次常用:@annotation 表达式(匹配注解标注的方法)

7.Spring AOP 的高级特性(实际开发高频使用)

1. 切面优先级控制(@Order 注解)

2. 引入增强(Introduction:给目标类添加新方法 / 接口)

Spring 框架基础搭建

Spring MVC 核心流程

IoC 容器实现原理

AOP 底层机制

事务管理实现

Spring Boot 自动配置

响应式编程支持

源码调试技巧


Spring 是什么?

Spring 是 Java 生态中最主流的一站式企业级开发框架,核心定位是简化 Java 开发—— 它不是单一功能组件,而是一套覆盖核心容器、AOP、数据访问、Web 开发、事务管理等全链路的技术体系,同时提供了丰富的生态扩展(如 Spring Boot、Spring Cloud),是 Java 后端开发的基石

简单说:Spring 就像一个「Java 开发工具箱 + 胶水框架」—— 它帮你解决开发中最繁琐的问题(如对象管理、依赖解耦、事务控制),同时能整合其他工具(如 MyBatis、Redis、RabbitMQ),让开发者聚焦核心业务逻辑,而非底层技术细节。

Spring 框架体系结构

Spring 的核心思想:IOC + AOP

一、核心思想一:IOC(控制反转,Inversion of Control)

        IoC 的全称为 Inversion of Control,控制反转,意为把对资源的控制权反转过来。IoC 不是一项开发技术,也不是具体功能,而是面向对象编程中的一种设计理念

1.什么是IoC?

  • 传统开发:开发者手动 new 对象(如 UserService userService = new UserService();),还要手动管理对象的依赖(如 userService.setUserDao(new UserDao());),对象的创建、组装全由开发者控制;
  • IOC 思想:将对象的创建、依赖组装的控制权,交给 Spring 容器—— 开发者只需定义对象(如用 @Component 注解),Spring 会自动创建对象、注入依赖(如用 @Autowired 注解),无需手动 new
IoC开发(Spring 控制)
// 1. 数据库连接类(交给 Spring 管理,无需手动 new)
@Component
class DBConnection {
    public Connection getConn() { ... }
}

// 2. Dao 层(依赖由 Spring 注入,无需手动 new)
@Component
class UserDao {
    @Autowired // 依赖注入:Spring 自动创建 DBConnection 并注入
    private DBConnection dbConn; 
    public void query() { ... }
}

// 3. Service 层(依赖由 Spring 注入)
@Component
class UserService {
    @Autowired
    private UserDao userDao; 
    public void getUser() { ... }
}

// 4. 应用启动(Spring 容器统一管理)
public class App {
    public static void main(String[] args) {
        // 启动 Spring 容器:自动扫描、创建、组装所有 Bean
        ApplicationContext context = new AnnotationConfigApplicationContext("com.example");
        // 直接从容器获取对象(无需手动创建)
        UserService service = context.getBean(UserService.class);
        service.getUser();
    }
}
IoC 的 “反转” 本质:
  • 反转的 “控制”:指「对象的创建权、依赖组装权、生命周期管理权」从开发者手中,转移到了 Spring IoC 容器手中;
  • 反转的核心:开发者从 “主动创建、组装对象” 变成 “被动接收容器创建好的对象”,只需声明 “我需要什么”,容器就会自动满足依赖;
  • 通俗类比:传统开发是 “你亲自买菜、做饭、洗碗”(全程掌控);IoC 是 “你告诉餐厅(容器)想吃什么,餐厅做好后给你”(餐厅掌控食材采购、烹饪、餐具清洗)。

2.IoC 的三大核心组件:Bean、容器、依赖注入

IoC 要能工作,必须依赖三个核心组件,它们共同构成了 IoC 的基础体系:

组件 1:Bean(被管理的对象)
什么是 Bean?

        Bean 是「被 Spring IoC 容器管理的 Java 对象」—— 它不是普通的 Java 对象(普通对象是 new 出来的),而是由容器创建、组装、销毁的对象。Spring 中的所有业务对象(Service、Dao、Controller)、工具对象(Template)本质上都是 Bean。

Bean 的核心属性(元数据):

        Spring 管理 Bean 时,会通过「BeanDefinition」存储 Bean 的元数据(相当于 Bean 的 “身份证 + 说明书”),核心属性包括:

  • 全类名(className):Bean 的具体实现类(如 com.example.UserService);
  • 作用域(scope):Bean 的创建模式(单例 / 多例等);
  • 依赖关系(dependencies):Bean 依赖的其他 Bean(如 UserService 依赖 UserDao);
  • 生命周期回调(initMethod/destroyMethod):Bean 初始化 / 销毁时执行的方法;
  • 懒加载(lazyInit):是否延迟创建 Bean(默认单例 Bean 启动时创建);
  • 初始化参数(properties):Bean 的属性值(如数据库连接池的 url、用户名)。

Spring 提供 6 种 Bean 作用域(常用前 2 种),决定了 Bean 的创建时机和实例数量:

注意:单例 Bean 是 “容器级单例” ——  一个 Spring 容器中只有一个实例,多个容器可以有多个实例;多例 Bean 每次获取都会新建,容器不管理其销毁(由 GC 回收)

注解方式

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.web.context.WebApplicationContext;

@Configuration
public class BeanScopeConfig {

    // 默认是Singleton作用域
    @Bean
    public SingletonService singletonService() {
        return new SingletonService();
    }

    // 显式指定Singleton作用域
    @Bean
    @Scope("singleton")
    public SingletonService explicitSingletonService() {
        return new SingletonService();
    }

    // Prototype作用域
    @Bean
    @Scope("prototype")
    public PrototypeService prototypeService() {
        return new PrototypeService();
    }

    // Request作用域(需要Web环境)
    @Bean
    @Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
    public RequestScopedService requestScopedService() {
        return new RequestScopedService();
    }

    // Session作用域(需要Web环境)
    @Bean
    @Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
    public SessionScopedService sessionScopedService() {
        return new SessionScopedService();
    }

    // Application作用域(ServletContext级别)
    @Bean
    @Scope(value = WebApplicationContext.SCOPE_APPLICATION, proxyMode = ScopedProxyMode.TARGET_CLASS)
    public ApplicationScopedService applicationScopedService() {
        return new ApplicationScopedService();
    }
}

XML 配置方式

<bean id="myBean" class="com.example.MyBean" scope="prototype"/>
组件 2:IoC 容器(Bean 的 “管家”)

        IoC 容器是 Spring 实现 IoC 的核心载体,本质是一个「Bean 工厂 + 依赖注入引擎」,负责 Bean 的全生命周期管理:

  • 职责 1:扫描并解析 Bean 定义(从注解、XML 配置中获取 Bean 元数据);
  • 职责 2:创建 Bean 实例(根据元数据反射创建对象);
  • 职责 3:依赖注入(将 Bean 依赖的其他 Bean 自动组装);
  • 职责 4:管理 Bean 生命周期(初始化、使用、销毁);
  • 职责 5:提供 Bean 访问接口(getBean() 方法获取 Bean)。
Spring 容器的体系结构(核心接口):

Spring 容器有两个核心接口,体现了 “分层设计”:

ApplicationContext 的常见实现类:

  • AnnotationConfigApplicationContext:基于注解配置的容器(扫描 @Component@Configuration 等注解);
  • ClassPathXmlApplicationContext:基于 XML 配置的容器(加载 classpath 下的 XML 配置文件);
  • FileSystemXmlApplicationContext:基于文件系统 XML 配置的容器(加载指定路径的 XML 文件);
  • AnnotationConfigWebApplicationContext:Web 环境下基于注解的容器(Spring MVC 常用)。
组件 3:依赖注入(DI,Dependency Injection)
什么是 DI?

        DI IoC 的「具体实现方式」—— 容器在创建 Bean 时,自动将其依赖的其他 Bean 注入到当前 Bean 中,无需开发者手动 new 或 set

        注意:IoC 是「思想」(控制反转),DI 是「技术」(依赖注入);DI 是实现 IoC 的核心手段,没有 DI 就没有 IoC 的落地。

依赖注入的 3 种方式(Spring 支持,推荐前 2 种):

(1)构造器注入(推荐,Spring 4.3+ 推荐优先使用)

通过 Bean 的构造方法注入依赖,强制要求依赖必须存在(避免空指针),且注入顺序固定。

@Component
public class UserService {
    private final UserDao userDao; // 用 final 修饰,确保依赖不可变(推荐)

    // 构造器注入:Spring 自动找到 UserDao 实例注入
    @Autowired // Spring 4.3+ 单构造器时可省略 @Autowired
    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }
}

优点

  • 依赖不可变(final 修饰),线程安全;
  • 依赖强制注入(无参构造器不存在时,必须传入依赖,避免漏注入);
  • 构造器执行时依赖已注入,避免 “部分初始化” 的 Bean。

缺点

  • 依赖过多时,构造器参数会很长(可通过拆分 Bean 解决)。

(2)Setter 方法注入(适合可选依赖)

通过 Bean 的 setXxx() 方法注入依赖,支持动态修改依赖(可选)。

@Component
public class UserService {
    private UserDao userDao;

    // Setter 注入:Spring 调用 setUserDao 注入依赖
    @Autowired
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
}

优点

  • 支持可选依赖(可在 set 方法中设置默认值);
  • 支持动态修改依赖(后续可通过 set 方法替换依赖实例)。

缺点

  • 依赖可被修改(线程不安全);
  • 注入时机晚于构造器,可能出现 “Bean 已创建但依赖未注入” 的情况。

(3)字段注入(不推荐,仅适用于简单场景)

直接在字段上用 @Autowired 注入,代码最简洁,但存在隐患。

@Component
public class UserService {
    // 字段注入:Spring 通过反射直接给字段赋值(跳过 set 方法)
    @Autowired
    private UserDao userDao;
}

优点:代码简洁,无多余构造器 /set 方法。

缺点

  • 字段无法用 final 修饰(依赖可变);
  • 依赖注入通过反射实现,可读性差,调试困难;
  • 无法避免 “空指针”(若依赖未被注入,直接使用会报错);
  • 不利于单元测试(无法手动设置依赖,只能用 Spring 容器)。
依赖注入的匹配规则:

Spring 注入依赖时,按以下优先级匹配目标 Bean:

  1. 按「类型(Type)」匹配:容器中查找与依赖类型一致的 Bean;
  2. 按「名称(Name)」匹配:若存在多个同类型 Bean,按字段名 / 参数名匹配(可通过 @Qualifier("beanName") 指定名称);
  3. 按「@Primary」匹配:若多个同类型 Bean 中存在被 @Primary 标注的 Bean,优先注入该 Bean。
// 两个 UserDao 实现类
@Component("userDaoMysql") // 指定 Bean 名称为 userDaoMysql
public class UserDaoMysql implements UserDao { ... }

@Component("userDaoRedis") // 指定 Bean 名称为 userDaoRedis
@Primary // 优先注入
public class UserDaoRedis implements UserDao { ... }

// 注入时指定名称
@Component
public class UserService {
    // 按名称注入 userDaoMysql
    @Autowired
    @Qualifier("userDaoMysql")
    private UserDao userDao;
}

3.IoC 容器的完整工作流程(从启动到销毁)

Spring IoC 容器的工作过程是「从配置解析到 Bean 销毁」的全链路,每一步都有明确的职责,我们以 AnnotationConfigApplicationContext(注解驱动)为例,拆解核心步骤:

步骤 1:容器初始化(启动容器)

当执行 new AnnotationConfigApplicationContext("com.example") 时,容器开始初始化:

  • 初始化核心组件:BeanDefinitionReader(解析 Bean 定义)、BeanDefinitionScanner(扫描 Bean)、BeanFactory(Bean 工厂);
  • 扫描指定包(com.example)下的所有类,识别带有 @Component@Service@Dao@Controller 等注解的类,将其标记为 “待管理的 Bean”。
步骤 2:解析 Bean 定义(生成 BeanDefinition)

容器对扫描到的类进行解析,生成 BeanDefinition 对象(存储 Bean 元数据):

  • 解析类名、包名,确定 Bean 的全类名;
  • 解析注解中的属性(如 @Scope 指定作用域、@Autowired 指定依赖);
  • 解析类的构造方法、set 方法,确定依赖关系;
  • 将所有 BeanDefinition 存储到 BeanDefinitionRegistry(Bean 定义注册表)中,供后续创建 Bean 使用。
步骤 3:BeanFactoryPostProcessor 增强(扩展点)

容器启动时,会先执行所有 BeanFactoryPostProcessor 接口的实现类(Spring 提供的扩展点),对 BeanDefinition 进行修改:

  • 例如:PropertySourcesPlaceholderConfigurer 替换配置文件中的占位符(如 ${db.url} 替换为实际配置值);
  • 开发者可自定义 BeanFactoryPostProcessor,动态修改 Bean 的元数据(如修改 Bean 的作用域、添加依赖)。
步骤 4:创建 Bean 实例(实例化)

容器根据 BeanDefinition 创建 Bean 实例,默认通过「反射」实现:

  • 单例 Bean:容器启动时(步骤 4)创建(除非设置 lazyInit=true,使用时创建);
  • 多例 Bean:每次调用 getBean() 时创建;
  • 创建方式:
    1. 无参构造器:默认方式(需确保类有默认无参构造器);
    2. 有参构造器:构造器注入时,根据依赖找到对应的 Bean,传入构造器创建实例;
    3. 静态工厂方法:通过 @Bean 注解指定静态方法创建(如 @Bean public static UserDao userDao() { return new UserDao(); });
    4. 实例工厂方法:通过实例方法创建(如 @Bean public UserDao userDao(DBConnection dbConn) { return new UserDao(dbConn); })。
步骤 5:依赖注入(装配)

Bean 实例创建后,容器会执行依赖注入:

  • 构造器注入:创建实例时已完成(步骤 4 中传入依赖);
  • Setter 注入 / 字段注入:通过反射调用 set 方法或直接给字段赋值,将依赖的 Bean 注入到当前 Bean 中;
  • 注入时会递归创建依赖的 Bean:若 UserService 依赖 UserDaoUserDao 依赖 DBConnection,容器会先创建 DBConnection,再创建 UserDao,最后创建 UserService
步骤 6:BeanPostProcessor 增强(扩展点)

依赖注入完成后,容器会执行所有 BeanPostProcessor 接口的实现类,对 Bean 进行增强:

  • 前置处理(postProcessBeforeInitialization):在 Bean 初始化前执行(如给 Bean 动态添加属性、AOP 代理生成);
  • 后置处理(postProcessAfterInitialization):在 Bean 初始化后执行(如 Spring AOP 就是通过该方法生成代理对象);
  • 示例:AutowiredAnnotationBeanPostProcessor 就是 Spring 内置的 BeanPostProcessor,负责解析 @Autowired 注解,完成依赖注入。
步骤 7:Bean 初始化(初始化回调)

Bean 增强后,容器会执行 Bean 的初始化方法(按以下顺序):

  1. 执行 @PostConstruct 注解标注的方法(JSR-250 标准注解);
  2. 执行 Bean 定义中指定的 initMethod(如 XML 中的 init-method 或 @Bean(initMethod="init"));
  3. 执行 InitializingBean 接口的 afterPropertiesSet() 方法(Spring 内置接口)。

初始化方法用于 Bean 的初始化逻辑(如初始化连接池、加载配置文件)。

步骤 8:Bean 就绪(可供使用)

初始化完成后,Bean 被存储到容器的缓存中(单例 Bean 存储在 singletonObjects 缓存),此时可以通过 context.getBean() 获取 Bean 并使用。

步骤 9:Bean 销毁(容器关闭时)

当容器调用 close() 方法(或应用停止)时,容器会销毁 Bean(仅单例 Bean,多例 Bean 由 GC 回收):

  1. 执行 @PreDestroy 注解标注的方法(JSR-250 标准注解);
  2. 执行 Bean 定义中指定的 destroyMethod(如 XML 中的 destroy-method 或 @Bean(destroyMethod="destroy"));
  3. 执行 DisposableBean 接口的 destroy() 方法(Spring 内置接口)。

销毁方法用于释放资源(如关闭连接池、释放文件句柄)。

总结流程:容器启动 → 扫描 Bean → 解析 Bean 定义 → 增强 Bean 定义 → 创建 Bean 实例 → 依赖注入 → 增强 Bean → 初始化 → 就绪 → 容器关闭 → 销毁。

4.IoC 底层实现技术揭秘

Spring IoC 容器的底层依赖 3 个核心技术,正是这些技术让 “控制反转” 成为可能:

1. 反射(Reflection)

反射是 Java 提供的动态访问类成员(构造器、方法、字段)的能力,Spring 通过反射实现:

  • Bean 实例化:通过 Class.newInstance() 或 Constructor.newInstance() 创建 Bean 实例;
  • 依赖注入:通过 Field.set() 给字段赋值,或 Method.invoke() 调用 set 方法;
  • 生命周期回调:通过 Method.invoke() 执行 @PostConstructinitMethod 等方法。

示例(反射创建 Bean 并注入依赖):

// 模拟 Spring 反射创建 Bean
public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        // 1. 获取 UserService 类对象
        Class<UserService> userServiceClass = UserService.class;
        // 2. 获取 UserDao 类对象
        Class<UserDao> userDaoClass = UserDao.class;

        // 3. 反射创建 UserDao 实例
        UserDao userDao = userDaoClass.getConstructor().newInstance();

        // 4. 反射创建 UserService 实例(通过构造器注入 UserDao)
        Constructor<UserService> constructor = userServiceClass.getConstructor(UserDao.class);
        UserService userService = constructor.newInstance(userDao);

        // 5. 调用方法
        userService.getUser();
    }
}
2. 工厂模式(Factory Pattern)

Spring IoC 容器本质是一个「超级工厂」,通过 BeanFactory 接口封装了 Bean 的创建逻辑:

  • 工厂接口:BeanFactory 定义了 Bean 创建的标准(getBean() 方法);
  • 工厂实现:DefaultListableBeanFactory 是核心实现类,负责 Bean 定义的注册、实例化、依赖注入;
  • 工厂模式的优势:将 Bean 的创建逻辑与使用逻辑分离,开发者无需关心 Bean 如何创建,只需通过工厂获取。
3. 注册中心模式(Registry Pattern)

Spring 用 BeanDefinitionRegistry 存储所有 Bean 的元数据(BeanDefinition),相当于一个 “Bean 注册中心”:

  • 注册:容器扫描 Bean 时,将解析后的 BeanDefinition 注册到 BeanDefinitionRegistry
  • 查找:创建 Bean 或注入依赖时,从注册中心查找对应的 BeanDefinition
  • 优势:集中管理 Bean 元数据,支持动态添加、修改、删除 Bean 定义,扩展性强。

5.IoC 的高级特性与实际应用

1. 循环依赖的解决(Spring 的核心能力)

什么是循环依赖?

两个或多个 Bean 互相依赖,形成闭环(如 A→B→A 或 A→B→C→A)。

示例(循环依赖):

@Component
public class A {
    @Autowired
    private B b; // A 依赖 B
}

@Component
public class B {
    @Autowired
    private A a; // B 依赖 A
}

传统开发的问题:

new A() 时需要 Bnew B() 时需要 A,导致无限循环,无法创建实例。

Spring 如何解决循环依赖?

Spring 仅支持「单例 Bean + 非构造器注入」的循环依赖,核心是「三级缓存」机制:

  • 一级缓存(singletonObjects):存储完全初始化完成的单例 Bean(可供使用);
  • 二级缓存(earlySingletonObjects):存储提前暴露的单例 Bean 实例(已实例化,但未完成依赖注入和初始化);
  • 三级缓存(singletonFactories):存储 Bean 的工厂对象(用于生成提前暴露的实例)。

解决流程(以 A→B→A 为例):

  1. 容器创建 A:实例化 A(未注入依赖),将 A 的工厂对象存入三级缓存;
  2. A 需要注入 B:容器开始创建 B;
  3. 容器创建 B:实例化 B(未注入依赖),将 B 的工厂对象存入三级缓存;
  4. B 需要注入 A:容器从三级缓存中获取 A 的工厂对象,生成 A 的提前暴露实例(未初始化),存入二级缓存,删除三级缓存中的 A;
  5. B 注入 A 后,完成依赖注入和初始化,存入一级缓存(B 就绪);
  6. A 注入 B 后,完成依赖注入和初始化,存入一级缓存(A 就绪)。

关键:提前暴露未完全初始化的 Bean 实例,打破循环依赖的死锁。注意:构造器注入的循环依赖无法解决(会抛出 BeanCurrentlyInCreationException),因为构造器执行时 Bean 还未实例化,无法提前暴露。

2. 延迟加载(Lazy Initialization)

默认情况下,单例 Bean 会在容器启动时创建(饿汉式),延迟加载可让 Bean 在「第一次使用时」创建(懒汉式)。

开启方式:

  • 单个 Bean 延迟加载:在 Bean 类上添加 @Lazy 注解;
  • 全局延迟加载:在配置类上添加 @Lazy 注解(所有单例 Bean 都延迟加载)。
@Component
@Lazy // 延迟加载:容器启动时不创建,第一次 getBean() 时创建
public class UserService { ... }

适用场景:

  • Bean 初始化成本高(如连接远程服务);
  • Bean 不常用(如后台定时任务);
  • 减少容器启动时间(避免启动时创建大量 Bean)。
3. 条件注解(@Conditional

根据特定条件决定是否创建 Bean,实现 “按需创建”。

示例:根据系统环境创建不同的 DBConnection

// 自定义条件:Windows 系统
public class WindowsCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return context.getEnvironment().getProperty("os.name").contains("Windows");
    }
}

// 自定义条件:Linux 系统
public class LinuxCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return context.getEnvironment().getProperty("os.name").contains("Linux");
    }
}

// 按条件创建 Bean
@Component
@Conditional(WindowsCondition.class) // Windows 系统时创建
public class WindowsDBConnection implements DBConnection { ... }

@Component
@Conditional(LinuxCondition.class) // Linux 系统时创建
public class LinuxDBConnection implements DBConnection { ... }

常用内置条件注解:

  • @ConditionalOnClass:类路径下存在指定类时创建;
  • @ConditionalOnMissingClass:类路径下不存在指定类时创建;
  • @ConditionalOnBean:容器中存在指定 Bean 时创建;
  • @ConditionalOnMissingBean:容器中不存在指定 Bean 时创建;
  • @ConditionalOnProperty:配置文件中存在指定属性时创建。

二、核心思想二:AOP(面向切面编程,Aspect-Oriented Programming)

1.AOP 的核心思想是什么?

AOP 的本质是「在不修改原有业务代码的前提下,对方法进行统一增强」—— 把日志记录、事务控制、权限校验等「通用功能」(横切逻辑)抽离成独立的「切面」,动态植入到目标方法的执行流程中(如方法执行前、执行后、异常时)。

为什么需要AOP?(OOP 的痛点)

OOP(面向对象编程)是「垂直分层」思想(如 Service、Dao、Controller 层),但面对「横切逻辑」(横跨多个层的通用功能)时会陷入困境:

  • 代码冗余:日志、事务等代码需要在每个业务方法中重复编写(如每个 Service 方法都要写 try-catch 控制事务);
  • 耦合严重:通用功能与业务逻辑紧耦合(如想关闭日志,需要修改所有业务方法);
  • 维护成本高:修改通用功能时(如日志格式变更),需要修改所有关联的业务代码。
AOP 的核心解决思路:「分离关注点」

AOP 将软件系统的功能分为两类,实现分离管理:

  • 核心关注点:业务逻辑(如下单、查询用户、支付),是开发者聚焦的核心;
  • 横切关注点:通用功能(如日志、事务、权限、异常处理),横跨多个核心关注点。

AOP 的目标是:让核心关注点和横切关注点完全分离,横切逻辑统一维护在切面中,无需侵入业务代码

2.AOP 的核心概念(必须熟记,Spring AOP 落地的基础)

AOP 的所有操作都围绕以下 6 个核心概念展开,结合示例理解更清晰:

假设场景:给 OrderService 的 placeOrder()(下单)和 cancelOrder()(取消订单)方法添加「日志记录」和「事务控制」功能。

关键关联:

  • 连接点是「候选者」(所有可能植入的时机),切入点是「筛选后的目标」(实际植入的时机);
  • 切面是「规则 + 逻辑」的组合,织入是「执行缝合」的过程,代理对象是「缝合后的产物」;
  • 开发者只需定义「切面」(切入点 + 通知),Spring 自动完成「织入」并生成「代理对象」。

3.AOP 的底层实现:动态代理(Spring AOP 的核心技术)

AOP 的织入过程依赖「动态代理」技术 —— 在程序运行时动态生成代理对象,代理对象包装目标对象,并重写目标方法,将横切逻辑植入其中。

Spring AOP 支持两种动态代理方式,自动选择:

  • JDK 动态代理:基于 Java 原生 java.lang.reflect.Proxy 类,要求目标对象必须实现接口;
  • CGLIB 动态代理:基于第三方库 CGLIB(Code Generation Library),通过生成目标对象的子类实现代理,无需目标对象实现接口。
1. 两种代理方式的对比(重点)

2. 手动模拟动态代理(理解 Spring AOP 底层逻辑)

通过手动实现 JDK 动态代理,感受 AOP 的核心原理:

步骤 1:定义目标接口和目标对象(业务逻辑)
// 目标接口
public interface OrderService {
    void placeOrder(String orderId); // 下单方法(核心业务)
}

// 目标对象(实现接口)
public class OrderServiceImpl implements OrderService {
    @Override
    public void placeOrder(String orderId) {
        System.out.println("核心业务:执行下单逻辑,订单ID=" + orderId);
    }
}
步骤 2:定义横切逻辑(日志增强)
// 横切逻辑:日志记录
public class LogAdvice {
    // 方法执行前增强
    public void before(String orderId) {
        System.out.println("日志增强:下单方法开始执行,订单ID=" + orderId);
    }

    // 方法执行后增强
    public void after(String orderId) {
        System.out.println("日志增强:下单方法执行结束,订单ID=" + orderId);
    }
}
步骤 3:通过 JDK 动态代理生成代理对象,织入横切逻辑
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 代理处理器:负责织入横切逻辑
public class OrderServiceProxyHandler implements InvocationHandler {
    private final Object target; // 目标对象(OrderServiceImpl)
    private final LogAdvice logAdvice; // 横切逻辑(日志)

    // 构造器注入目标对象和横切逻辑
    public OrderServiceProxyHandler(Object target, LogAdvice logAdvice) {
        this.target = target;
        this.logAdvice = logAdvice;
    }

    // 核心方法:代理对象的所有方法调用都会触发此方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 1. 执行前置增强(日志记录)
        logAdvice.before((String) args[0]);

        // 2. 执行目标对象的核心业务方法
        Object result = method.invoke(target, args);

        // 3. 执行后置增强(日志记录)
        logAdvice.after((String) args[0]);

        return result;
    }

    // 生成代理对象的工厂方法
    public static OrderService createProxy(OrderService target, LogAdvice logAdvice) {
        return (OrderService) Proxy.newProxyInstance(
            target.getClass().getClassLoader(), // 类加载器
            target.getClass().getInterfaces(),  // 目标对象实现的接口
            new OrderServiceProxyHandler(target, logAdvice) // 代理处理器
        );
    }
}
步骤 4:测试代理对象(触发增强逻辑)
public class ProxyTest {
    public static void main(String[] args) {
        // 1. 创建目标对象和横切逻辑对象
        OrderService target = new OrderServiceImpl();
        LogAdvice logAdvice = new LogAdvice();

        // 2. 生成代理对象
        OrderService proxy = OrderServiceProxyHandler.createProxy(target, logAdvice);

        // 3. 调用代理对象的方法(触发增强)
        proxy.placeOrder("ORDER_123456");
    }
}
输出结果(横切逻辑自动植入,无侵入业务代码):
日志增强:下单方法开始执行,订单ID=ORDER_123456
核心业务:执行下单逻辑,订单ID=ORDER_123456
日志增强:下单方法执行结束,订单ID=ORDER_123456

4.Spring AOP 的核心特性(与原生 AOP 框架的区别)

Spring AOP 是「基于动态代理的 AOP 框架」,并非完整的 AOP 实现(完整 AOP 如 AspectJ),但贴合 Spring 生态,足够满足绝大多数业务场景。其核心特性:

  1. 基于代理:仅支持「方法级别的增强」(连接点是方法执行前后、异常时),不支持字段、构造器级别的增强(如需更细粒度增强,需结合 AspectJ);
  2. 动态织入:织入过程在程序运行时完成(通过动态代理生成代理对象),无需编译期或类加载期修改字节码;
  3. 与 IoC 深度集成:切面、目标对象都是 Spring 管理的 Bean,依赖注入自动完成(如切面中可通过 @Autowired 注入其他 Bean);
  4. 注解驱动:通过 @Aspect@Pointcut@Before 等注解快速定义切面,无需复杂 XML 配置。

5.Spring AOP 实战:注解驱动实现切面(核心用法)

Spring AOP 最常用的方式是「注解驱动」,步骤简单:定义切面 → 配置切入点 → 编写通知 → 启动容器。

1. 环境准备(Maven 依赖)

Spring AOP 依赖 spring-context 和 spring-aspects(AOP 注解支持):

<dependencies>
    <!-- Spring 核心容器 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.20</version>
    </dependency>
    <!-- Spring AOP 注解支持 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>5.3.20</version>
    </dependency>
</dependencies>

步骤 1:定义目标对象(业务 Bean)

import org.springframework.stereotype.Service;

// 目标对象:订单服务(核心业务)
@Service // 交给 Spring 管理为 Bean
public class OrderService {
    // 核心业务方法1:下单
    public void placeOrder(String orderId) {
        System.out.println("核心业务:下单成功,订单ID=" + orderId);
        // 模拟异常(测试异常通知)
        // if (orderId.length() < 10) throw new RuntimeException("订单ID格式错误");
    }

    // 核心业务方法2:取消订单
    public String cancelOrder(String orderId) {
        System.out.println("核心业务:取消订单,订单ID=" + orderId);
        return "取消成功";
    }
}

步骤 2:定义切面(@Aspect + 通知注解)

Spring 提供 5 种通知类型(覆盖方法执行的全流程),结合切入点表达式使用:

5 种通知类型详解:

切面类完整实现(包含 5 种通知):

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

// 切面类 = @Aspect(标识切面) + @Component(交给 Spring 管理)
@Aspect
@Component
public class OrderAspect {
    // ################### 步骤1:定义切入点(Pointcut) ###################
    // @Pointcut:定义切入点规则,value 是切入点表达式
    // 规则:匹配 com.example.service 包下所有类的所有方法
    @Pointcut("execution(* com.example.service.*.*(..))")
    public void orderPointcut() {} // 切入点签名(无实际逻辑,仅作为规则引用)

    // ################### 步骤2:编写通知(Advice) ###################
    // 1. 前置通知:目标方法执行前执行
    @Before("orderPointcut()")
    public void beforeAdvice(JoinPoint joinPoint) {
        // JoinPoint:连接点对象,可获取目标方法信息(方法名、参数)
        String methodName = joinPoint.getSignature().getName(); // 获取方法名
        Object[] args = joinPoint.getArgs(); // 获取方法参数
        System.out.println("[前置通知] 方法" + methodName + "开始执行,参数:" + args[0]);
    }

    // 2. 后置返回通知:目标方法正常执行完成后执行(可获取返回值)
    @AfterReturning(value = "orderPointcut()", returning = "result")
    public void afterReturningAdvice(JoinPoint joinPoint, Object result) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("[后置返回通知] 方法" + methodName + "正常执行,返回值:" + result);
    }

    // 3. 后置异常通知:目标方法抛出异常后执行(可获取异常)
    @AfterThrowing(value = "orderPointcut()", throwing = "ex")
    public void afterThrowingAdvice(JoinPoint joinPoint, Exception ex) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("[后置异常通知] 方法" + methodName + "执行异常,异常信息:" + ex.getMessage());
    }

    // 4. 最终通知:目标方法执行结束后执行(无论正常/异常)
    @After("orderPointcut()")
    public void afterAdvice(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("[最终通知] 方法" + methodName + "执行结束,释放资源");
    }

    // 5. 环绕通知:环绕目标方法执行(功能最强,需手动调用 proceed() 执行目标方法)
    @Around("orderPointcut()")
    public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        String methodName = proceedingJoinPoint.getSignature().getName();
        Object[] args = proceedingJoinPoint.getArgs();

        // ① 前置增强:目标方法执行前
        System.out.println("[环绕通知-前] 方法" + methodName + "准备执行,参数:" + args[0]);

        Object result = null;
        try {
            // ② 执行目标方法(必须调用 proceed(),否则目标方法不会执行)
            result = proceedingJoinPoint.proceed(); // 执行目标方法,获取返回值
            // 可修改返回值(如对返回值进行包装)
            if (result != null) {
                result = "【环绕通知包装】" + result;
            }
        } catch (Exception e) {
            // ③ 异常增强:目标方法抛出异常时
            System.out.println("[环绕通知-异常] 方法" + methodName + "执行异常:" + e.getMessage());
            throw e; // 可选择抛出异常或处理异常
        }

        // ④ 后置增强:目标方法执行后
        System.out.println("[环绕通知-后] 方法" + methodName + "执行完成,包装后返回值:" + result);

        return result; // 返回修改后的结果
    }
}

步骤 3:启动 Spring 容器,测试增强效果

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

// 配置类:开启组件扫描和 AOP 自动代理
@Configuration
@ComponentScan("com.example") // 扫描 com.example 包下的 Bean
@EnableAspectJAutoProxy // 开启 Spring AOP 自动代理(必须添加,否则 AOP 不生效)
public class AppConfig {
    public static void main(String[] args) {
        // 启动 Spring 容器
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

        // 获取目标对象的代理对象(Spring 自动生成代理,注入的是代理对象)
        OrderService orderService = context.getBean(OrderService.class);

        // 调用代理对象的方法(触发 AOP 增强)
        System.out.println("=== 测试下单方法 ===");
        orderService.placeOrder("ORDER_1234567890");

        System.out.println("\n=== 测试取消订单方法 ===");
        String cancelResult = orderService.cancelOrder("ORDER_9876543210");
        System.out.println("最终返回结果:" + cancelResult);

        // 关闭容器
        context.close();
    }
}

6.Spring AOP 切入点表达式(重点:精准匹配目标方法)

切入点表达式是 @Pointcut 的核心,用于精准匹配需要增强的目标方法。Spring AOP 支持多种切入点表达式,最常用的是 execution 和 @annotation

1. 最常用:execution 表达式(匹配方法签名)

语法格式(6 部分组成):

execution( [访问修饰符] 返回值类型 包名.类名.方法名(参数类型) [throws 异常类型] )

  • 可省略部分:访问修饰符(如 public)、throws 异常类型;
  • 通配符:
    • *:匹配任意字符(如 * 表示任意返回值类型、任意类、任意方法);
    • ..:匹配任意层级的包或任意个数的参数(如 com.example..* 表示 com.example 下所有子包的类)。

常用 execution 表达式示例:

2. 次常用:@annotation 表达式(匹配注解标注的方法)

于匹配被指定注解标注的方法,适合「按注解增强」场景(如事务注解 @Transactional)。

示例:

  1. 自定义注解 @Log(标记需要添加日志的方法
    import java.lang.annotation.*;
    
    @Target(ElementType.METHOD) // 仅作用于方法
    @Retention(RetentionPolicy.RUNTIME) // 运行时生效
    public @interface Log {
        String value() default ""; // 注解属性(可传入日志描述)
    }
  2. 切入点表达式匹配 @Log 注解:
    @Aspect
    @Component
    public class LogAspect {
        // 切入点:匹配所有被 @Log 注解标注的方法
        @Pointcut("@annotation(com.example.annotation.Log)")
        public void logPointcut() {}
    
        // 前置通知:获取注解属性
        @Before("logPointcut() && @annotation(log)") // 通过 @annotation(log) 注入注解实例
        public void beforeLog(JoinPoint joinPoint, Log log) {
            String methodName = joinPoint.getSignature().getName();
            String logDesc = log.value(); // 获取注解的属性值
            System.out.println("[日志通知] 方法" + methodName + ",描述:" + logDesc);
        }
    }
  3. 目标方法添加 @Log 注解(触发增强):
    @Service
    public class OrderService {
        @Log("用户下单操作") // 添加注解,触发日志增强
        public void placeOrder(String orderId) {
            System.out.println("下单成功,订单ID=" + orderId);
        }
    }

7.Spring AOP 的高级特性(实际开发高频使用)

1. 切面优先级控制(@Order 注解)

当多个切面增强同一个目标方法时,需要控制切面的执行顺序:

  • @Order(value = 数字):数字越小,切面优先级越高(先执行);
  • 默认优先级:数字越大,优先级越低(未标注 @Order 的切面优先级最低)。
// 日志切面(优先级 1,先执行)
@Aspect
@Component
@Order(1)
public class LogAspect {
    @Before("execution(* com.example.service.*.*(..))")
    public void logBefore() {
        System.out.println("日志切面:前置增强");
    }
}

// 事务切面(优先级 2,后执行)
@Aspect
@Component
@Order(2)
public class TransactionAspect {
    @Before("execution(* com.example.service.*.*(..))")
    public void transactionBefore() {
        System.out.println("事务切面:前置增强");
    }
}
2. 引入增强(Introduction:给目标类添加新方法 / 接口)

引入增强是 Spring AOP 的高级功能,允许给目标类「动态添加新的接口和方法实现」,无需修改目标类代码。

示例:给 OrderService 添加 Loggable 接口的实现(动态添加日志方法):

  1. 定义新接口 Loggable
    public interface Loggable {
        void log(String message); // 新方法:记录日志
    }
  2. 切面中通过 @DeclareParents 引入接口实现:
    @Aspect
    @Component
    public class IntroductionAspect {
        // @DeclareParents:给目标类添加新接口
        // value:目标类(OrderService),defaultImpl:接口的默认实现类
        @DeclareParents(value = "com.example.service.OrderService", defaultImpl = LoggableImpl.class)
        public static Loggable loggable; // 要添加的接口
    }
    
    // 接口实现类
    public class LoggableImpl implements Loggable {
        @Override
        public void log(String message) {
            System.out.println("引入增强:" + message);
        }
    }

Spring 框架基础搭建

环境准备

  • JDK 8+(推荐JDK 11或17)
  • Maven 3.6+或Gradle 7.x
  • IDE(IntelliJ IDEA/Eclipse)

创建项目 通过Spring Initializr生成项目模板:

curl https://start.spring.io/starter.zip -d dependencies=web \
-d type=maven-project -d javaVersion=17 -o demo.zip

或使用IDE内置的Spring Initializr向导选择Web、JDBC等模块。

核心配置文件

  • application.propertiesapplication.yml
server.port=8080
spring.datasource.url=jdbc:h2:mem:testdb
spring.h2.console.enabled=true

Spring MVC 核心流程

请求处理链路

  1. DispatcherServlet接收HTTP请求
  2. HandlerMapping解析请求URL到Controller方法
  3. 参数解析器处理@RequestParam/@RequestBody等注解
  4. 方法执行后通过ViewResolver或HttpMessageConverter返回响应

示例控制器

@RestController
@RequestMapping("/api")
public class DemoController {
    @GetMapping("/hello")
    public String hello(@RequestParam String name) {
        return "Hello " + name;
    }
}

IoC 容器实现原理

Bean生命周期

  • 实例化 → 属性填充 → 初始化前(@PostConstruct) → 初始化(InitializingBean) → 初始化后(AOP代理) → 销毁

配置方式对比

// 注解配置
@Component
public class ServiceA {
    @Autowired
    private ServiceB serviceB;
}

// Java显式配置
@Configuration
public class AppConfig {
    @Bean
    public ServiceB serviceB() {
        return new ServiceB();
    }
}

AOP 底层机制

代理模式选择

  • JDK动态代理:基于接口(默认)
  • CGLIB:代理类(需添加依赖)

切面定义示例

@Aspect
@Component
public class LogAspect {
    @Around("execution(* com.example..*(..))")
    public Object logExecutionTime(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = pjp.proceed();
        System.out.println("Method executed in " + (System.currentTimeMillis()-start) + "ms");
        return result;
    }
}

事务管理实现

传播行为配置

@Transactional(propagation = Propagation.REQUIRED)
public void transferMoney(Account from, Account to, BigDecimal amount) {
    // 业务逻辑
}

底层实现

  • PlatformTransactionManager抽象
  • 事务同步管理器(TransactionSynchronizationManager)
  • 基于AOP的拦截器(TransactionInterceptor)

Spring Boot 自动配置

条件化Bean加载

@Configuration
@ConditionalOnClass(DataSource.class)
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceAutoConfiguration {
    @Bean
    @ConditionalOnMissingBean
    public DataSource dataSource() {
        // 自动配置逻辑
    }
}

自定义Starter步骤

  1. 创建autoconfigure模块
  2. 编写META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
  3. 定义配置属性类(@ConfigurationProperties)

响应式编程支持

WebFlux核心组件

  • RouterFunction方式定义路由:
@Bean
public RouterFunction<ServerResponse> routes() {
    return route(GET("/hello"), 
        req -> ok().bodyValue("Hello WebFlux"));
}

Reactive仓库示例

public interface UserRepository extends ReactiveCrudRepository<User, Long> {
    Flux<User> findByAgeGreaterThan(int age);
}

源码调试技巧

关键断点位置

  • AbstractApplicationContext.refresh()
  • DefaultListableBeanFactory.preInstantiateSingletons()
  • AbstractAutowireCapableBeanFactory.createBean()
  • ProxyFactory.getProxy()

日志配置

logging.level.org.springframework=DEBUG
logging.level.org.hibernate.SQL=TRACE

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值