Skip to content

Spring Framework

导航目录

1. 框架概念

1.1 生活中的框架

框架是一个集成了基本结构、规范、设计模式、编程语言和程序库等基础组件的软件系统,用于构建更高级别的应用程序。

框架思维:为解决特定问题而提供的一整套解决方案

1.2 程序中的框架

主流框架:SSM(Spring + SpringMVC + Mybatis)

  • Spring:为所有 **Bean(组件)**提供管理解决方案
  • SpringMVC:解决**表述层(控制层)**常见问题
  • Mybatis:解决**数据访问层(Dao 层)**持久化问题

框架优点

  • 提高开发效率:解耦业务逻辑与底层实现
  • 降低开发成本:标准化代码结构,降低维护难度
  • 提高应用程序稳定性:成熟的社区方案,减少人为失误
  • 提供标准化解决方案:统一团队开发规范

2. Spring Framework 简介

2.1 Spring 广义与狭义

广义 SpringSpring 技术栈(全家桶)

  • Spring Framework、Spring MVC、SpringBootSpring Cloud

狭义 SpringSpring Framework(基础框架)

2.2 Spring Framework 概述

Spring 是基于 IoC 和 AOP 的轻量级容器框架

核心概念

  • IoC 容器:负责 Bean 的实例化、配置和组装
  • IoC(控制反转):将对象的创建和依赖关系的管理权,由应用程序转移到 IoC 容器
  • DI(依赖注入):在容器内部通过构造器、Setter 或反射处理组件间的依赖关系
  • AOP(面向切面编程):在不改变源码的情况下,动态地为程序添加额外功能

主要功能模块

模块功能
Core Container核心容器,支撑 **IoC(控制反转)**和 DI(依赖注入)
AOP&Aspects面向切面编程,实现关注点分离
TX声明式事务管理,简化数据库事务控制
Testing快速整合 JUnit/TestNG 等测试环境
Data Access提供对 JDBC、ORM(MyBatis/Hibernate)的集成支持
Spring MVC经典的 Web 应用程序开发框架

2.3 Spring Framework 特点

  • 丰富的生态系统:支持全栈开发,涵盖从 Web 到微服务的各个领域
  • 模块化设计:按需引入依赖,避免臃肿
  • 非侵入式编程:业务代码无需继承框架类或实现框架接口
  • 支持响应式编程:Spring 5+ 引入了 WebFlux,支持高并发流式处理

3. Spring IoC/DI 基本实现

3.1 基于 XML 方式装配组件(了解即可)

环境准备

xml
<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>6.0.6</version>
    </dependency>
</dependencies>

实现步骤

  1. POJO 类:使用 Lombok 简化开发。
java
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
    private Integer stuId;
    private String stuName;
    private Integer stuAge;
}
  1. 配置文件(spring.xml):
xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="zs" class="com.at.bean.Student">
        <property name="stuId" value="1001"/>
        <property name="stuName" value="zs"/>
        <property name="stuAge" value="18"/>
    </bean>
</beans>
  1. 测试类
java
@Test
public void testStudent(){
    // 创建 IoC 容器
    ApplicationContext ioc = new ClassPathXmlApplicationContext("spring.xml");
    // 从容器中获取 Bean
    Student bean = ioc.getBean(Student.class);
    System.out.println("bean = " + bean);
}

3.2 基于配置类装配组件(推荐)

配置类:使用 @Configuration 标识,替代 XML。

java
@Configuration
public class SpringConfig {

    @Bean
    public Student student(){
        return new Student(1001, "zhangsan", 18);
    }
}

测试类

java
@Test
public void testSpring(){
    // 使用 AnnotationConfigApplicationContext 加载配置类
    ApplicationContext ioc = new AnnotationConfigApplicationContext(SpringConfig.class);
    Student bean = ioc.getBean(Student.class);
    System.out.println("bean = " + bean);
}

3.3 @Bean 注解详解

@Bean 核心属性

  • name/value:指定 Bean 名称,默认为方法名
  • initMethod:指定 初始化回调 方法
  • destroyMethod:指定 销毁回调 方法
  • autowireCandidate:是否作为自动装配候选者

第三方组件注入(以 Druid 为例)

java
@Configuration
public class DruidConfig {

    @Value("${jdbc.driverClassName}")
    private String driverClassName;

    @Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driverClassName);
        ds.setUrl(url);
        ds.setUsername(username);
        ds.setPassword(password);
        return ds;
    }
}

3.4 Bean 生命周期

Bean 的完整生命周期(核心 6 阶段)

  1. 加载 Bean 定义:读取 XML 或配置类,生成 BeanDefinition
  2. 实例化 Bean:通过反射调用构造器创建对象
  3. 设置属性:执行依赖注入(DI)
  4. 初始化方法:执行自定义的 init-methodPostConstruct
  5. 正常使用:Bean 已准备就绪,存入 Singleton Objects 缓存中
  6. 销毁方法:容器关闭时执行 destroy-methodPreDestroy

3.5 Bean 作用域

作用域含义创建时机默认值
singleton单例,全局唯一容器启动时立即创建
prototype多例,每次获取创建新对象调用 getBean() 时创建

4. Spring IoC/DI 深入学习

4.1 开启组件扫描

通过 @ComponentScan 自动探测并注册标记了 @Component 等注解的类。

java
@Configuration
@ComponentScan(basePackages = "com.at")
public class SpringConfig {}

4.2 分层管理组件注解

为了提高代码可读性,Spring 提供了 @Component 的衍生注解:

注解说明
@Component通用组件,当不确定属于哪一层时使用
@Repository数据访问层(Dao 层),通常配合异常转换使用
@Service业务逻辑层(Service 层)
@Controller控制层(Web 层/MVC)

4.3 属性自动注入(重点)

@Value:注入字面量或读取配置文件(${...}

@Autowired核心注入注解,默认按类型匹配。若有多个同类型 Bean,则按 ID 匹配。

@Qualifier:配合 @Autowired 显式指定 Bean ID

@Primary:当有多个同类型 Bean 时,优先使用标记了该注解的 Bean。

java
@Component
@Primary
public class PrimaryUserDao implements UserDao {}

@Component
public class CommonUserDao implements UserDao {}

@Resource:Java EE 规范注解,默认按名称匹配。

最佳实践

  • 优先使用构造器注入:保证依赖不为空且对象不可变。
  • 避免循环依赖:尽量通过设计规避,Spring 默认支持 Setter 循环依赖,但不推荐依赖它。

4.4 循环依赖处理机制

循环依赖是指 A 依赖 B,B 又依赖 A。Spring 通过 三级缓存 机制解决 Setter 注入的循环依赖(不支持构造器循环依赖)。

  1. singletonObjects(一级缓存):存放完全初始化好的 Bean。
  2. earlySingletonObjects(二级缓存):存放原始的 Bean(尚未填充属性,用于解决 AOP 代理问题)。
  3. singletonFactories(三级缓存):存放 Bean 工厂对象,用于生成原始 Bean 或代理对象。

4.5 @Import 注解

用于快速导入其他配置类或普通类到容器中。

4.6 @Conditional 注解

条件装配:根据特定条件决定是否加载某个 Bean。

4.7 FactoryBean 与 BeanFactory 区别

  • BeanFactorySpring 容器的基础设施,负责管理所有 Bean 的生命周期。
  • FactoryBean一种特殊的 Bean,用于创建复杂的 Bean 实例(如集成 MyBatis 的 SqlSessionFactoryBean)。获取时需加 & 前缀才能拿到 FactoryBean 本身,否则拿到的是它创建的对象。

5. Spring 资源管理与验证

5.1 Resource 抽象

Spring 提供了 Resource 接口,用于统一访问各种底层的资源(文件、类路径资源、URL 等)。

  • UrlResource:访问网络资源(http:)或本地文件(file:)。
  • ClassPathResource:访问类路径下的资源。
  • FileSystemResource:访问文件系统中的资源。

代码示例

java
Resource resource = new ClassPathResource("config.properties");
InputStream is = resource.getInputStream(); // 统一流获取方式

5.2 数据验证(Validation)

Spring 提供了对 JSR-303/JSR-380 Bean Validation 的全面支持。

常用验证注解

注解说明
@NotNull任何类型不能为 null
@NotEmpty集合、字符串、Map 不能为空(size > 0)
@NotBlank字符串不能为 null 且去除首尾空格后长度 > 0
@Min(value)数字的最小值
@Max(value)数字的最大值
@Size(min, max)字符串、集合、Map 的大小必须在指定范围内
@Email必须是合法的 Email 格式
@Pattern(regexp)必须匹配指定的正则表达式

代码示例

java
@Data
public class UserForm {
    @NotBlank(message = "用户名不能为空")
    private String username;

    @Size(min = 6, max = 20, message = "密码长度必须在6-20位之间")
    private String password;

    @Email(message = "邮箱格式不正确")
    private String email;
}

// 在 Controller 中使用 @Valid 触发验证
// 当配置了全局异常处理后,此处的 BindingResult 参数可以移除
@PostMapping("/register")
public String register(@Valid UserForm form) {
    // 校验逻辑已移至 GlobalExceptionHandler
    return "success";
}

5.3 统一异常处理

为了避免在每个 Controller 方法中都编写 BindingResult 的判断逻辑,我们可以使用 @RestControllerAdvice 创建一个全局异常处理器,专门拦截验证失败时抛出的 MethodArgumentNotValidException

代码示例

java
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST) // 返回 400 状态码
    public Map<String, Object> handleValidationExceptions(MethodArgumentNotValidException ex) {
        Map<String, String> errors = new HashMap<>();
        ex.getBindingResult().getAllErrors().forEach((error) -> {
            String fieldName = ((FieldError) error).getField();
            String errorMessage = error.getDefaultMessage();
            errors.put(fieldName, errorMessage);
        });

        Map<String, Object> response = new HashMap<>();
        response.put("code", 400);
        response.put("message", "请求参数校验失败");
        response.put("errors", errors);
        return response;
    }
}

通过这种方式,当 @Valid 校验失败时,前端会收到一个结构化的 JSON 错误响应。这使得 Controller 层的代码更整洁,只关注核心业务逻辑。

6. Spring 核心扩展点

Spring 提供了丰富的扩展接口,允许开发者在 Bean 生命周期的不同阶段插入自定义逻辑。

5.1 BeanPostProcessor(Bean 后置处理器)

Bean 初始化前后执行自定义逻辑(如 AOP 代理、数据校验)。

java
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        System.out.println("==> Bean 初始化前: " + beanName);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        System.out.println("==> Bean 初始化后: " + beanName);
        return bean;
    }
}

5.2 BeanFactoryPostProcessor(容器后置处理器)

BeanFactory 加载完 Bean 定义之后,Bean 实例化之前执行。常用于修改 BeanDefinition

java
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        // 可以动态修改 Bean 的属性定义
        BeanDefinition bd = beanFactory.getBeanDefinition("student");
        bd.getPropertyValues().add("stuName", "ModifiedName");
    }
}

5.3 Aware 接口系列

允许 Bean 获取容器内部的基础资源(如 ApplicationContextBeanName)。

java
@Component
public class MyAwareBean implements ApplicationContextAware {
    private ApplicationContext context;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.context = applicationContext; // 获取容器引用
    }
}

7. Spring AOP 详解

6.1 AOP 核心思想

AOP(面向切面编程) 是一种通过预编译方式运行期动态代理实现程序功能的统一维护的一种技术。其核心是将核心业务逻辑横切关注点(如日志、事务、安全检查)解耦。

6.2 代理模式

静态代理:硬编码实现代理类,一个代理类只能服务于一种接口,扩展性差。

动态代理(核心实现方式)

  • JDK 动态代理基于接口实现。利用 Proxy.newProxyInstance() 在运行时创建接口的实现类。
  • CGLIB 动态代理基于继承实现。通过继承目标类并重写其方法来实现代理(Spring Boot 2.x+ 默认使用)。

6.3 AOP 相关术语(必须掌握)

  • Aspect(切面):横切关注点的模块化,通常是一个类。
  • Advice(通知):切面在特定连接点执行的操作(如前置、后置)。
  • JoinPoint(连接点):程序执行过程中的某个点,如方法执行前、后。
  • Pointcut(切入点):匹配连接点的表达式,决定通知在哪些方法上执行。
  • Target(目标对象):被一个或多个切面所通知的对象。
  • Proxy(代理对象):由 AOP 框架创建的对象,用于实现切面逻辑。

7.4 切入点表达式(Pointcut)

切入点表达式用于精确匹配需要被拦截的方法。

语法结构execution([权限修饰符] [返回值类型] [类的全限定名].[方法名]([参数列表]) [异常类型])

常用通配符

  • *:匹配任意数量的字符(如任意返回值、任意方法名)。
  • ..:匹配任意数量的参数,或在包路径中表示当前包及其子包。

经典示例

表达式含义
execution(public * *(..))匹配所有 public 方法
execution(* set*(..))匹配所有以 set 开头的方法
execution(* com.at.service.*.*(..))匹配 com.at.service 包下所有类的所有方法
execution(* com.at.service..*.*(..))匹配 com.at.service及其子包下所有类的所有方法

逻辑运算符: 支持使用 &&(与)、||(或)、!(非)组合表达式。

java
@Pointcut("execution(* com.at.service.*.*(..)) && !execution(* com.at.service.UserService.*(..))")
public void myPointcut(){}

7.5 基于注解实现 AOP

环境准备:引入 spring-boot-starter-aop

切面类示例

java
@Component
@Aspect // 标识为切面类
public class LoggingAspect {

    // 重用切入点表达式
    @Pointcut("execution(* com.at.aop.CalcImpl.*(..))")
    public void calcPointcut(){}

    @Before("calcPointcut()")
    public void logBefore(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println("==> 前置通知:" + methodName + " 执行,参数:" + Arrays.toString(args));
    }
}

7.6 AOP 五大通知类型

通知类型注解执行时机
前置通知@Before目标方法执行之前
返回通知@AfterReturning目标方法正常返回之后(可以获取返回值)
异常通知@AfterThrowing目标方法抛出异常之后
后置通知@After目标方法执行之后(无论是否异常,类似于 finally
环绕通知@Around最强大的通知,可以手动控制目标方法的执行(pjp.proceed()

7.7 切面优先级

使用 @Order(value) 指定优先级,数值越小,优先级越高(外层切面先执行)。

8. Spring 事务管理进阶

8.1 事务核心属性(ACID)

  • 原子性(Atomicity):事务是最小执行单位,不可分割。
  • 一致性(Consistency):执行前后数据库状态保持一致。
  • 隔离性(Isolation):并发访问时,事务之间相互隔离。
  • 持久性(Durability):事务提交后,对数据的改变是永久的。

8.2 Spring 声明式事务(推荐)

通过 @Transactional 注解实现,底层基于 AOP 拦截。

核心配置

  • 在配置类上添加 @EnableTransactionManagement
  • 注册 PlatformTransactionManager(如 DataSourceTransactionManager)。

8.3 @Transactional 核心属性

  • propagation(传播行为)
    • REQUIRED(默认):如果当前有事务则加入,没有则新建。
    • REQUIRES_NEW:无论当前是否有事务,都挂起当前事务并新建一个独立事务。
  • isolation(隔离级别)
    • READ_COMMITTED:读已提交(解决脏读)。
    • REPEATABLE_READ:可重复读(解决不可重复读,MySQL 默认)。
  • readOnly:只读事务,优化查询性能。
  • timeout:超时时间,强制回滚。
  • rollbackFor:指定哪些异常触发回滚(建议指定为 Exception.class,Spring 默认只回滚运行时异常)。

8.4 事务管理器底层原理(重点)

Spring 事务通过 TransactionManagerTransactionDefinitionTransactionStatus 三个核心接口协作完成。

  • TransactionManager:具体执行事务的提交、回滚。
  • TransactionDefinition:定义事务隔离、传播、超时等属性。
  • TransactionStatus:代表当前事务的状态。

8.5 事务失效场景(面试高频)

  1. 非 public 方法@Transactional 只能用于 public 方法。
  2. 内部方法调用:同一个类中方法自调用,不会经过代理对象,导致事务失效。
  3. 未被 Spring 管理:类没有标记 @Service 等注解。
  4. 异常被 catch 掉:代码中手动 try-catch 了异常且未重新抛出。
  5. 数据库引擎不支持:如 MySQL 使用了 MyISAM 而非 InnoDB。

9. Spring 事件机制与响应式编程

9.1 Spring 事件(Event)

用于组件之间的解耦,基于观察者模式。

  1. 定义事件:继承 ApplicationEvent
  2. 发布事件:使用 ApplicationEventPublisher
  3. 监听事件:使用 @EventListener
java
// 1. 定义事件
public class MyOrderEvent extends ApplicationEvent {
    public MyOrderEvent(Object source) { super(source); }
}

// 2. 监听事件
@Component
public class MyOrderListener {
    @EventListener
    public void onOrderEvent(MyOrderEvent event) {
        System.out.println("收到订单事件: " + event.getSource());
    }
}

9.2 环境与 Profile(环境隔离)

通过 @Profile 实现不同环境(dev/test/prod)的配置隔离。

9.3 Spring WebFlux 响应式编程(Spring 5+)

WebFlux 是 Spring 推出的非阻塞、响应式 Web 框架,基于 Reactor 库。

核心组件

  • Mono:表示 0 或 1 个元素的异步序列。
  • Flux:表示 0 到 N 个元素的异步序列。

代码示例

java
@RestController
public class HelloController {
    @GetMapping("/hello")
    public Mono<String> hello() {
        return Mono.just("Hello WebFlux");
    }
}

10. Spring 容器启动全流程

Spring 容器启动的精髓在于 AbstractApplicationContext.refresh() 方法,主要包含以下 12 个核心步骤:

  1. prepareRefresh:准备上下文环境(设置启动时间、活动状态等)。
  2. obtainFreshBeanFactory:初始化 BeanFactory,加载 BeanDefinition
  3. prepareBeanFactory:对 BeanFactory 进行属性填充。
  4. postProcessBeanFactory:子类自定义扩展点。
  5. invokeBeanFactoryPostProcessors:执行 BeanFactoryPostProcessor
  6. registerBeanPostProcessors:注册 BeanPostProcessor
  7. initMessageSource:初始化国际化资源。
  8. initApplicationEventMulticaster:初始化事件广播器。
  9. onRefresh:特定子类的刷新逻辑(如 Web 容器初始化)。
  10. registerListeners:注册事件监听器。
  11. finishBeanFactoryInitialization实例化并初始化所有非懒加载的单例 Bean(最核心一步)。
  12. finishRefresh:广播启动完成事件。

11. Spring 开发最佳实践

  1. 优先使用 Java 配置类:避免臃肿的 XML,利用类型安全和 IDE 提示。
  2. 强制使用构造器注入:解决 @Autowired 字段注入无法标记 final 且难以进行单元测试的问题。
  3. 接口导向编程:利用 JDK 动态代理的特性,增强系统的解耦和扩展性。
  4. 合理规划切面:不要滥用 AOP,避免切面逻辑过于分散导致系统难以调试。
  5. 明确事务回滚范围:始终使用 @Transactional(rollbackFor = Exception.class),确保受检异常(Checked Exception)也能触发回滚。
  6. 关注循环依赖:尽量通过重构代码解决循环依赖,而非依赖 Spring 的三级缓存自动处理。
  7. 使用 @EventListener 代替硬编码调用:在复杂业务逻辑中利用事件驱动机制解耦核心流程与非核心流程。