Spring框架重点概述
在当今的软件开发领域,企业级应用系统往往需要面对复杂的业务逻辑、高并发的访问量以及灵活多变的业务需求。如何构建一个高内聚、低耦合、易维护、可扩展的应用框架,成为众多开发者关注的核心问题。
Spring 框架自 2002 年由 Rod Johnson 提出以来,凭借其轻量级、非侵入性、松耦合的设计理念,迅速成为 Java 开发领域的事实标准。它不仅提供了 IoC(控制反转) 与 AOP(面向切面编程) 这样的核心功能,还涵盖了 数据访问、事务管理、Web 框架(Spring MVC)、测试支持、与第三方框架的整合 等多个方面。
本文将围绕 Spring 框架的重点内容进行系统性阐述,涵盖 核心容器、Bean 管理、IoC、AOP、数据访问与事务管理、Web 框架、测试支持 等内容,旨在帮助读者全面理解 Spring 的设计思想和应用价值,从而在实际开发中更好地运用这一框架。
Spring 的设计哲学可以概括为以下几点:
-
解耦合:通过 IoC 容器降低对象之间的依赖。
-
关注点分离:通过 AOP 将横切逻辑(如日志、安全、事务)与业务逻辑解耦。
-
一致性:提供统一的编程模型,屏蔽不同技术栈之间的差异。
-
模块化:整个框架由多个模块组成,可以按需引入,不必全部使用。
官方文档:Spring Framework 文档 :: Spring Framework - Spring 框架
一、核心容器
1.1 核心容器简介
Spring 核心容器(Core Container) 是整个 Spring 框架的基石,负责 管理对象(Bean)的生命周期,并为上层模块(如 AOP、事务、Spring MVC)提供基础支持。
核心容器由以下模块组成:
-
spring-core:提供基本工具类(如资源加载、类型转换等)。
-
spring-beans:定义 Bean 的配置、创建和依赖注入。
-
spring-context:在 core 和 beans 基础上,提供类似 JNDI 注册、国际化、事件机制等功能。
-
spring-expression(SpEL):Spring 表达式语言,支持在运行时动态获取对象属性、方法调用等。
其核心接口是 ApplicationContext
,它是 Spring IoC 容器的实现类。
1.2 核心容器的作用
-
实例化和管理 Bean
-
容器负责根据配置文件(XML)、注解或 Java 配置类来创建对象(Bean)。
-
统一管理 Bean 的生命周期(创建、初始化、销毁)。
-
-
依赖注入(DI)
-
容器根据配置为 Bean 注入依赖对象,而不是由代码手动创建。
-
-
配置与解耦
-
将对象的依赖关系配置在 XML、注解或 Java 类中,避免硬编码。
-
-
扩展支持
-
提供国际化、事件发布/监听、资源加载等企业级功能。
-
1.3 代码示例
使用 XML 配置 Bean
(1)applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!-- 定义 UserRepository --><bean id="userRepository" class="com.example.repository.UserRepository"/><!-- 定义 UserService,并注入 UserRepository --><bean id="userService" class="com.example.service.UserService"><property name="userRepository" ref="userRepository"/></bean></beans>
(2)Java 代码
// Repository 层
public class UserRepository {public void save(String name) {System.out.println("保存用户:" + name);}
}// Service 层
public class UserService {private UserRepository userRepository;// Setter 注入public void setUserRepository(UserRepository userRepository) {this.userRepository = userRepository;}public void registerUser(String name) {System.out.println("业务逻辑:注册用户");userRepository.save(name);}
}// 测试
public class App {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");UserService userService = context.getBean("userService", UserService.class);userService.registerUser("Tom");}
}
二、Bean
2.1 什么是 Bean?
在 Spring 中,Bean 指的是由 IoC 容器 管理的对象。
它本质上就是一个 Java 对象,只不过由 Spring 负责:
-
创建(new 出来)
-
初始化(赋值、依赖注入)
-
生命周期管理(初始化回调、销毁回调)
换句话说,任何被 Spring 容器实例化、装配和管理的对象,都是 Spring Bean。
2.2 Bean 的定义方式
1. XML 配置方式(传统)
<!-- applicationContext.xml -->
<bean id="userService" class="com.example.service.UserService"><property name="userRepository" ref="userRepository"/>
</bean><bean id="userRepository" class="com.example.repository.UserRepository"/>
对应 Java 代码:
public class UserService {private UserRepository userRepository;public void setUserRepository(UserRepository userRepository) {this.userRepository = userRepository;}
}
2. 注解方式(现代推荐)
@Service
public class UserService {@Autowiredprivate UserRepository userRepository;
}@Repository
public class UserRepository {}
并在配置类中启用扫描:
@Configuration
@ComponentScan("com.example")
public class AppConfig {}
2.3 Bean 的作用域(Scope)
Spring Bean 默认是 单例模式(singleton),也支持其他作用域:
作用域 | 说明 |
---|---|
singleton | 默认,每个 Spring 容器只创建一个 Bean 实例 |
prototype | 每次请求都会创建一个新的 Bean 实例 |
request | 每个 HTTP 请求创建一个 Bean(仅 Web 应用有效) |
session | 每个 HTTP 会话创建一个 Bean |
application | 在 ServletContext 级别共享一个 Bean |
2.4 Bean 的生命周期
Spring 对 Bean 的管理包括 创建 → 初始化 → 使用 → 销毁。
-
实例化:Spring 容器通过反射创建对象。
-
依赖注入:将依赖对象注入到 Bean 中。
-
初始化:如果实现了
InitializingBean
或定义了@PostConstruct
,会在这里调用。 -
使用:Bean 被应用程序调用。
-
销毁:如果实现了
DisposableBean
或定义了@PreDestroy
,会在容器关闭时执行。
示例:
@Component
public class MyBean implements InitializingBean, DisposableBean {@Overridepublic void afterPropertiesSet() {System.out.println("Bean 初始化完成");}@Overridepublic void destroy() {System.out.println("Bean 被销毁");}
}
也可以用注解:
@Component
public class MyBean {@PostConstructpublic void init() {System.out.println("初始化");}@PreDestroypublic void cleanup() {System.out.println("销毁");}
}
2.5 依赖注入方式
1. 构造器注入
@Component
public class OrderService {private final PaymentService paymentService;@Autowiredpublic OrderService(PaymentService paymentService) {this.paymentService = paymentService;}
}
2. Setter 注入
@Component
public class OrderService {private PaymentService paymentService;@Autowiredpublic void setPaymentService(PaymentService paymentService) {this.paymentService = paymentService;}
}
3. 字段注入
@Component
public class OrderService {@Autowiredprivate PaymentService paymentService;
}
官方建议优先使用 构造器注入,因为它更符合 依赖不可变性 的原则。但企业实际上使用更多的是字段注入,更加方便。
2.6 Bean 的高级特性
1. Bean 的别名
<bean id="userService" class="com.example.UserService" name="us,serviceUser"/>
这样可以用 "userService"
或 "us"
或 "serviceUser"
获取。
2. 延迟加载(Lazy Initialization)
默认情况下,Spring 会在容器启动时创建所有 singleton
Bean。
可以指定 @Lazy
,让 Bean 在第一次使用时才创建。
@Component
@Lazy
public class HeavyBean {}
3. Bean 的条件加载
通过 @Conditional
可以控制 Bean 是否注册。
@Configuration
public class AppConfig {@Bean@ConditionalOnProperty(name = "feature.enabled", havingValue = "true")public FeatureService featureService() {return new FeatureService();}
}
4. Bean 的优先级
当有多个候选 Bean 时,可以用 @Primary
指定优先注入,或用 @Qualifier
精确指定。
@Service
@Primary
public class MySQLRepository implements UserRepository {}@Service
public class OracleRepository implements UserRepository {}@Autowired
@Qualifier("oracleRepository")
private UserRepository repository;
三、IOC
3.1 IOC 的定义与原理
IOC(Inversion of Control),即控制反转,是一种设计模式,它的核心思想是将控制权交给外部容器来管理,而不是由应用程序自己控制。简单来说,IOC 就是通过某种方式将对象的创建、依赖关系的注入和生命周期管理交给外部容器来做,常见的容器就是 Spring 的 IoC 容器。
在传统的编程方式中,我们通常直接在类中创建依赖对象,通过硬编码方式来控制对象的实例化和管理。而在 Spring 中,应用的对象(Bean)以及对象之间的依赖关系都由容器负责。容器通过配置(XML 或注解)来描述 Bean 之间的关系,并负责在运行时注入依赖。
IOC 的原理就是反转了应用程序的控制方式,从而使得对象的创建、依赖关系和生命周期管理不再依赖于应用代码本身。这样不仅减少了代码之间的耦合度,也提高了系统的灵活性和可测试性。
3.2 IOC 的实现方式
Spring 提供了多种实现 IOC 的方式,主要通过XML 配置和注解配置两种方式。
3.2.1 XML 配置方式
XML 配置是 Spring 最早的配置方式,它的核心是通过配置文件来定义 Spring 容器中的 Bean 以及它们之间的关系。XML 配置文件通常以 beans.xml 命名,并使用 <bean> 标签来声明 Bean。
在 XML 配置方式下,我们需要在 XML 文件中手动定义 Bean 的信息,Spring 容器根据这些配置来创建和管理 Bean 实例。
XML 配置示例:
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!-- 定义一个 Car Bean --><bean id="car" class="com.example.Car"><property name="make" value="Toyota"/><property name="model" value="Corolla"/></bean><!-- 定义一个 Engine Bean --><bean id="engine" class="com.example.Engine"><property name="type" value="V6"/></bean>
</beans>
在上述 XML 配置中,我们通过 <bean> 标签定义了 Car 和 Engine 两个 Bean,并通过 <property> 标签设置它们的属性。这些配置最终会交给 Spring 容器处理,从而实例化并注入依赖。
3.2.2 注解配置方式
随着 Spring 的发展,注解配置方式成为了更为简洁和便捷的选择。Spring 提供了一些注解,如 @Component、@Autowired、@Configuration 等,可以让我们用更少的代码来进行 Bean 的声明和依赖注入。
使用注解配置方式时,Spring 会通过扫描类路径中的 Bean 注解(如 @Component),自动将其注册到 Spring 容器中。而通过 @Autowired 注解,Spring 会自动注入 Bean 的依赖。
注解配置示例:
import org.springframework.stereotype.Component;@Component
public class Car {private String make;private String model;public String getMake() {return make;}public void setMake(String make) {this.make = make;}public String getModel() {return model;}public void setModel(String model) {this.model = model;}
}@Configuration
@ComponentScan(basePackages = "com.example")
public class AppConfig {
}
在上面的代码中,@Component
注解将 Car
类标记为一个 Bean,而 @Configuration
和 @ComponentScan
注解指定了包扫描的路径。Spring 容器会扫描指定的包,自动注册所有符合条件的 Bean。
四、AOP
4.1 AOP 的定义与原理
AOP(Aspect-Oriented Programming),即面向切面编程,是一种编程思想,旨在通过分离横切关注点来提高代码的可维护性和可复用性。横切关注点是指那些涉及到多个模块的功能,例如日志、事务、安全等。
AOP 通过定义“切面”(Aspect)来集中处理横切关注点,从而避免在每个模块中都重复编写相同的代码。AOP 的目标是将一些通用的功能模块化,并将其应用到多个不同的模块中。
AOP 的核心概念包括:
- 切面类(Aspect):该类通过@Aspect注解标记为一个切面类(一个公共模块)。在类中,定义了切入点和通过,比如@Before注解表示该通知再目标方法执行前调用。切面定义了何时、如何以及在哪里应用通知。
- 连接点(Joinpoint):连接点是程序执行过程中可以插入AOP逻辑的点,通常指方法的执行
- 通知(Advice):定义了切面的具体操作,表示”做某些事“,包括前置通知(Before)、后置通知(After)、环绕通知(Around)、异常通知(AfterThrowing)、返回通知(AfterReturning)
- 切入点(Pointcut):@Pointcunt注解标记在一个方法上,定义了在哪执行通知。
- 织入(Weaving):将切面应用到目标对象的过程。
- 目标对象:目标对象是指被AOP代理的对象。
4.2 AOP 的实现方式
Spring AOP 提供了基于代理的实现方式。Spring 的 AOP 可以通过两种方式来实现:
- JDK 动态代理:适用于目标类实现了接口的情况,Spring 会通过 JDK 的反射机制创建目标类的代理对象。
- CGLIB 代理:当目标类没有实现接口时,Spring 会使用 CGLIB 库通过继承的方式创建目标类的子类。
4.2.1 AOP 的应用
Spring AOP 的常见应用场景包括:
- 事务管理:AOP 可以在方法执行前后自动管理事务的开启、提交、回滚等操作。
- 日志记录:AOP 可以用来在方法执行前后记录日志。
- 性能监控:AOP 可以用来监控方法执行的性能,记录执行时间。
4.3 AOP 的代码示例
通过注解来实现 AOP 非常简便,以下是一个日志记录的 AOP 示例。
@Aspect
@Component
public class LoggingAspect {@Before("execution(* com.example.service.*.*(..))")public void logBefore(JoinPoint joinPoint) {System.out.println("Method " + joinPoint.getSignature().getName() + " is about to be executed");}@After("execution(* com.example.service.*.*(..))")public void logAfter(JoinPoint joinPoint) {System.out.println("Method " + joinPoint.getSignature().getName() + " has been executed");}
}
在这个例子中,@Aspect
注解定义了一个切面,@Before
和 @After
注解则分别在方法执行前后插入日志记录。
五、数据访问与事务管理
在企业级应用开发中,数据访问 和 事务管理 是最核心的部分。传统 JDBC 操作存在以下问题:
-
代码冗余(需要频繁编写连接获取、关闭、异常处理)。
-
异常处理复杂(需要捕获并转换 SQLException)。
-
事务控制困难(需要显式调用
commit
和rollback
)。
Spring 框架通过 统一的 DAO 抽象层、JdbcTemplate 工具类 和 声明式事务管理,极大简化了开发者的负担。
5.1 数据访问支持
Spring 提供多种数据访问方式,支持 JDBC、JPA、Hibernate、MyBatis 等主流框架。其核心是提供一致的异常层次结构(DataAccessException
),屏蔽底层差异。
1. JdbcTemplate
JdbcTemplate 是 Spring 对 JDBC 的封装,大幅简化了 SQL 操作。
示例代码(查询操作):
@Repository
public class UserRepository {@Autowiredprivate JdbcTemplate jdbcTemplate;// 插入用户public void saveUser(String name, int age) {String sql = "INSERT INTO users (name, age) VALUES (?, ?)";jdbcTemplate.update(sql, name, age);}// 查询用户public User findUserById(int id) {String sql = "SELECT * FROM users WHERE id = ?";return jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(User.class), id);}// 查询所有用户public List<User> findAllUsers() {String sql = "SELECT * FROM users";return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(User.class));}
}
可以看到,JdbcTemplate 自动管理 资源释放(连接、语句、结果集),开发者只需关注 SQL 与业务逻辑。
2. ORM 框架集成(JPA / Hibernate)
Spring 也支持 ORM 框架,例如 JPA。通过 Spring Data JPA,我们可以使用接口操作数据库。
示例代码:
@Entity
public class User {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Integer id;private String name;private int age;// getter、setter 省略
}public interface UserRepository extends JpaRepository<User, Integer> {User findByName(String name);
}
调用时无需编写 SQL,Spring Data JPA 会根据方法名自动生成查询。
@Service
public class UserService {@Autowiredprivate UserRepository userRepository;public void addUser(User user) {userRepository.save(user);}public User getUser(String name) {return userRepository.findByName(name);}
}
5.2 事务管理
在企业应用中,事务管理是保证 数据一致性 的关键。
Spring 提供两种事务管理方式:
-
编程式事务管理(手动控制)。
-
声明式事务管理(推荐,基于 AOP)。
1. 编程式事务管理
通过 PlatformTransactionManager
手动管理事务,常见于底层灵活场景。
示例代码:
@Service
public class AccountService {@Autowiredprivate JdbcTemplate jdbcTemplate;@Autowiredprivate PlatformTransactionManager transactionManager;public void transfer(int fromId, int toId, double amount) {TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());try {jdbcTemplate.update("UPDATE account SET balance = balance - ? WHERE id = ?", amount, fromId);jdbcTemplate.update("UPDATE account SET balance = balance + ? WHERE id = ?", amount, toId);transactionManager.commit(status); // 提交事务} catch (Exception e) {transactionManager.rollback(status); // 回滚事务throw e;}}
}
虽然灵活,但这种写法显得冗长,不符合“解耦”的理念。
2. 声明式事务管理(推荐)
Spring 通过 AOP 拦截器 自动管理事务,只需使用 @Transactional
注解即可。
示例代码:
@Service
public class AccountService {@Autowiredprivate JdbcTemplate jdbcTemplate;@Transactionalpublic void transfer(int fromId, int toId, double amount) {jdbcTemplate.update("UPDATE account SET balance = balance - ? WHERE id = ?", amount, fromId);// 模拟异常if (true) throw new RuntimeException("转账失败!");jdbcTemplate.update("UPDATE account SET balance = balance + ? WHERE id = ?", amount, toId);}
}
在该例中,如果抛出异常,Spring 会自动回滚事务;如果执行成功,则自动提交。
事务注解常用属性
-
propagation
:事务传播行为(如REQUIRED
、REQUIRES_NEW
)。 -
isolation
:事务隔离级别(如READ_COMMITTED
、SERIALIZABLE
)。 -
timeout
:事务超时时间。 -
readOnly
:只读事务优化。 -
rollbackFor
:指定哪些异常触发回滚。
3. @Transactional注解的实现逻辑
(1)启动时扫描注解
当Spring容器启动时,TransactionAnnotationParser会扫描@Transactional注解,并将其解析为一个TransactionAttribute对象
(2)创建代理对象
Spring使用AOP为带有@Transactional的类生成代理对象,核心是TransactionInterceptor(事务拦截器)
(3)方法调用拦截
当外部调用代理对象的方法时,事务拦截器会拦截方法调用,拦截器通过TransactionManager来管理事务:判断当前是否存在事务,如果需要新建事务就调用begin( )方法,执行目标方法,方法执行成功则提交事务,异常时根据回滚规则决定是否回滚。
TransactionInterceptor
继承了 MethodInterceptor
,核心代码:
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {// 获取事务属性TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);// 获取事务管理器PlatformTransactionManager tm = determineTransactionManager(txAttr);// 开启事务TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);Object retVal;try {// 执行目标方法retVal = invocation.proceed();}catch (Throwable ex) {// 出现异常 -> 回滚completeTransactionAfterThrowing(txInfo, ex);throw ex;}finally {cleanupTransactionInfo(txInfo);}// 提交事务commitTransactionAfterReturning(txInfo);return retVal;
}
六、Web框架(Spring MVC)
6.1 概述
Spring MVC(Model-View-Controller)是 Spring 框架的核心 Web 组件,基于 Servlet API 实现,采用 前端控制器(DispatcherServlet)模式。
它的主要目标是:
-
将请求的处理和视图渲染进行解耦。
-
提供清晰的分层:控制层(Controller)、服务层(Service)、数据层(DAO)。
-
提供灵活的视图解析,支持 JSP、Thymeleaf、FreeMarker 等。
Spring MVC 的核心就是 DispatcherServlet,它作为前端控制器,负责接收所有请求,并协调各个组件(HandlerMapping、HandlerAdapter、ViewResolver 等)完成处理。
6.2 工作流程
Spring MVC 的请求处理流程如下:
-
用户发起请求,例如访问
http://localhost:8080/hello
。 -
DispatcherServlet 接收请求(拦截所有以
/
或.do
结尾的请求)。 -
HandlerMapping 定位处理器,找到与请求匹配的
Controller
方法。 -
HandlerAdapter 调用 Controller,执行对应的业务逻辑。
-
Controller 返回 ModelAndView,包含数据模型和视图名称。
-
ViewResolver 解析视图,将视图名称解析为实际的页面(如
hello.jsp
或hello.html
)。 -
DispatcherServlet 渲染视图并返回响应。
👉 总结:请求进 DispatcherServlet,经过 HandlerMapping 找到 Controller,返回数据给 DispatcherServlet,再由 ViewResolver 渲染视图,最后返回给浏览器。
6.3 核心组件
Spring MVC 主要包含以下组件:
-
DispatcherServlet:前端控制器,统一入口。
-
HandlerMapping:根据请求找到对应的处理器(Controller 方法)。
-
Controller:业务处理核心。
-
HandlerAdapter:适配器,调用具体的处理器方法。
-
ModelAndView:包含模型数据和视图信息。
-
ViewResolver:将逻辑视图名解析为具体的视图实现。
-
View:最终渲染页面。
七、测试支持
7.1 概述
在企业开发中,测试是保证代码质量的核心环节。Spring 框架自带了一套强大的测试支持,帮助开发者在 单元测试 和 集成测试 中更高效地完成验证工作。
Spring 测试支持主要解决以下几个问题:
-
容器管理:测试时可以直接启动 Spring IoC 容器,避免手动实例化 Bean。
-
依赖注入:测试类中也可以使用
@Autowired
,直接注入 Bean。 -
事务管理:测试方法默认事务回滚,避免污染数据库。
-
Mock 支持:可模拟 Web 请求和响应,进行 Spring MVC 的测试。
-
与主流测试框架集成:支持 JUnit 4、JUnit 5、TestNG。
7.2 Spring 测试框架基础
Spring 提供了 spring-test
模块,该模块集成了 JUnit、TestNG,并扩展了对 Spring IoC 容器和事务的支持。
核心注解包括:
-
@RunWith(SpringJUnit4ClassRunner.class):在 JUnit4 中启动 Spring 容器。
-
@ExtendWith(SpringExtension.class):JUnit5 对应的扩展。
-
@ContextConfiguration:指定配置文件或配置类。
-
@SpringBootTest:Spring Boot 项目中常用,启动完整应用上下文。
-
@Transactional:使测试方法运行在事务中,默认回滚。
7.3 测试类型实例
7.3.1 基础示例(JUnit4)
配置类
@Configuration
@ComponentScan("com.example")
public class AppConfig {
}
Service 类
@Service
public class UserService {private final List<String> users = new ArrayList<>();public void addUser(String name) {users.add(name);}public boolean hasUser(String name) {return users.contains(name);}
}
测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AppConfig.class)
public class UserServiceTest {@Autowiredprivate UserService userService;@Testpublic void testAddUser() {userService.addUser("Tom");assertTrue(userService.hasUser("Tom"));}
}
说明:
-
@RunWith(SpringJUnit4ClassRunner.class)
让 JUnit 知道需要用 SpringRunner 来执行测试。 -
@ContextConfiguration
告诉测试类如何加载 Spring 配置。 -
@Autowired
可以像在业务代码里一样,直接注入 Bean。
7.3.2 事务回滚测试
Spring 提供了对事务测试的支持。默认情况下,使用 @Transactional
注解的测试方法在执行后会自动回滚,保证数据库不会被污染。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AppConfig.class)
@Transactional
public class AccountServiceTest {@Autowiredprivate JdbcTemplate jdbcTemplate;@Testpublic void testTransfer() {jdbcTemplate.update("INSERT INTO account (id, balance) VALUES (1, 100)");jdbcTemplate.update("INSERT INTO account (id, balance) VALUES (2, 50)");jdbcTemplate.update("UPDATE account SET balance = balance - 30 WHERE id = ?", 1);jdbcTemplate.update("UPDATE account SET balance = balance + 30 WHERE id = ?", 2);int balance1 = jdbcTemplate.queryForObject("SELECT balance FROM account WHERE id = 1", Integer.class);int balance2 = jdbcTemplate.queryForObject("SELECT balance FROM account WHERE id = 2", Integer.class);assertEquals(70, balance1);assertEquals(80, balance2);}
}
执行完成后,数据库中的插入数据会自动回滚,测试环境干净、可重复。
7.3.3 Spring MVC 测试支持
Spring 提供了 MockMvc 工具,用来模拟 HTTP 请求,不需要启动真实的 Web 服务器即可测试 Controller。
示例代码:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AppConfig.class)
@WebAppConfiguration
public class HelloControllerTest {@Autowiredprivate WebApplicationContext wac;private MockMvc mockMvc;@Beforepublic void setup() {mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();}@Testpublic void testHello() throws Exception {mockMvc.perform(get("/hello")).andExpect(status().isOk()).andExpect(model().attribute("msg", "Hello Spring MVC")).andExpect(view().name("hello"));}
}
说明:
-
@WebAppConfiguration
表示测试需要 Web 环境。 -
MockMvc
通过perform(get("/url"))
发送请求,并验证响应状态、模型数据和视图名称。
7.3.4 Spring Boot 测试支持
在 Spring Boot 中,测试更简洁:
示例代码:
@SpringBootTest
@RunWith(SpringRunner.class)
public class UserServiceBootTest {@Autowiredprivate UserService userService;@Testpublic void testUser() {userService.addUser("Alice");assertTrue(userService.hasUser("Alice"));}
}
说明:
-
@SpringBootTest
会加载完整的 Spring Boot 应用上下文,相当于“全栈启动”,适合集成测试。 -
Spring Boot 还内置了
@DataJpaTest
、@WebMvcTest
等细化注解,帮助只加载部分上下文,提升测试速度。
总的来说,Spring 框架的成功在于它既有理论上的先进性,又有实践中的易用性。它不仅为开发者提供了构建企业级应用的完整解决方案,还推动了整个 Java 生态的进化。正因如此,Spring 成为现代 Java 开发中不可或缺的重要基石。