外观
Spring 笔记
约 3051 字大约 10 分钟
2025-08-16
一、Spring 简介
1.1 什么是 Spring?
Spring 是一个开源的 Java 企业级应用开发框架,它的核心目标是简化 Java 开发,解决企业级应用开发的复杂性。Spring 通过提供一系列模块化的组件,帮助开发者构建松耦合、易于测试和维护的应用程序。
1.2 Spring 的核心特性
| 特性 | 说明 |
|---|---|
| 非侵入式 | 不强制应用继承或实现特定类/接口,保持代码纯净 |
| 控制反转 (IoC) | 将对象的创建和依赖关系交给容器管理,降低代码耦合度 |
| 依赖注入 (DI) | IoC 的具体实现方式,通过构造函数、setter 方法或字段注入依赖 |
| 面向切面编程 (AOP) | 将横切关注点(如日志、事务)与业务逻辑分离 |
| 组件化 | 提供模块化设计,可按需选择使用 |
| 一站式框架 | 提供从Web层到持久层的完整解决方案 |
1.3 Spring 模块概览
Spring 框架由多个模块组成,主要分为六大类:
- 核心容器:IoC 和 DI 的基础
- AOP 和 Instrumentation:面向切面编程支持
- 数据访问/集成:JDBC、ORM、事务管理等
- Web:Spring MVC、WebSocket 等
- 消息:消息处理支持
- 测试:单元测试和集成测试支持
二、Spring 核心容器
2.1 IoC 容器
IoC(Inversion of Control,控制反转)是 Spring 的核心思想,将对象的创建和依赖关系的管理交给 Spring 容器,而不是由应用程序自己控制。
IoC 容器的主要实现:
BeanFactory:基础容器,提供基本的 DI 功能ApplicationContext:高级容器,在BeanFactory基础上增加了企业级功能(如国际化、事件发布等)
// 创建 Spring 容器
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// 从容器中获取 Bean
UserService userService = (UserService) context.getBean("userService");2.2 Bean 的定义与配置
Bean 是 Spring 容器管理的对象实例。配置 Bean 的三种主要方式:
2.2.1 XML 配置(传统方式)
<!-- 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/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 定义 Bean -->
<bean id="userService" class="com.example.service.UserServiceImpl">
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" class="com.example.dao.UserDaoImpl"/>
</beans>2.2.2 Java 配置类(推荐)
@Configuration
@ComponentScan("com.example")
public class AppConfig {
@Bean
public UserService userService() {
return new UserServiceImpl(userDao());
}
@Bean
public UserDao userDao() {
return new UserDaoImpl();
}
}2.2.3 注解配置(最常用)
// 启用组件扫描
@Configuration
@ComponentScan("com.example")
public class AppConfig { }
// 服务层
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
// 实现方法...
}
// 数据访问层
@Repository
public class UserDaoImpl implements UserDao {
// 实现方法...
}2.3 依赖注入 (DI)
依赖注入是 IoC 的具体实现方式,主要有三种方式:
2.3.1 构造函数注入
public class UserServiceImpl implements UserService {
private final UserDao userDao;
@Autowired
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
}2.3.2 Setter 方法注入
public class UserServiceImpl implements UserService {
private UserDao userDao;
@Autowired
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}2.3.3 字段注入(简洁但不推荐用于复杂场景)
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
}2.4 Bean 的作用域
| 作用域 | 描述 |
|---|---|
| singleton | 默认作用域,整个 Spring 容器中只存在一个实例 |
| prototype | 每次请求都会创建一个新的实例 |
| request | Web 应用中,每个 HTTP 请求创建一个实例 |
| session | Web 应用中,每个 HTTP 会话创建一个实例 |
| application | Web 应用中,每个 ServletContext 创建一个实例 |
// 使用注解指定作用域
@Service
@Scope("prototype")
public class UserServiceImpl implements UserService {
// ...
}2.5 Bean 的生命周期
Bean 的生命周期包括:创建 → 依赖注入 → 初始化 → 使用 → 销毁
初始化和销毁方法:
public class UserServiceImpl implements UserService {
// 初始化方法
@PostConstruct
public void init() {
System.out.println("Bean 初始化");
}
// 销毁方法
@PreDestroy
public void destroy() {
System.out.println("Bean 销毁");
}
}XML 配置方式:
<bean id="userService" class="com.example.service.UserServiceImpl"
init-method="init" destroy-method="destroy"/>三、Spring AOP
3.1 AOP 核心概念
| 术语 | 说明 |
|---|---|
| 切面 (Aspect) | 横切关注点的模块化,如日志、事务等 |
| 连接点 (Join Point) | 程序执行过程中的一个点,如方法调用、异常抛出等 |
| 通知 (Advice) | 在特定连接点执行的动作,如"方法调用前"、"方法调用后" |
| 切入点 (Pointcut) | 匹配连接点的表达式,定义在哪些连接点应用通知 |
| 目标对象 (Target Object) | 被一个或多个切面所通知的对象 |
| 代理 (Proxy) | 由 AOP 框架创建的对象,实现切面功能 |
| 织入 (Weaving) | 将切面应用到目标对象并创建代理对象的过程 |
3.2 通知类型
| 通知类型 | 说明 | 注解 |
|---|---|---|
| 前置通知 | 在目标方法执行前执行 | @Before |
| 后置通知 | 在目标方法执行后执行(无论是否异常) | @After |
| 返回通知 | 在目标方法成功执行后执行 | @AfterReturning |
| 异常通知 | 在目标方法抛出异常后执行 | @AfterThrowing |
| 环绕通知 | 包围目标方法,可在方法执行前后自定义行为 | @Around |
3.3 切入点表达式
// 匹配指定包下所有类的所有方法
@Pointcut("execution(* com.example.service.*.*(..))")
// 匹配指定类的所有方法
@Pointcut("execution(* com.example.service.UserService.*(..))")
// 匹配所有 save 开头的方法
@Pointcut("execution(* com.example.service.*.save*(..))")
// 匹配所有 public 方法
@Pointcut("execution(public * *(..))")3.4 AOP 实现示例
@Aspect
@Component
public class LoggingAspect {
@Pointcut("execution(* com.example.service.*.*(..))")
private void serviceMethods() {}
// 前置通知
@Before("serviceMethods()")
public void beforeMethod(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
System.out.println("方法 " + methodName + " 开始执行");
}
// 返回通知
@AfterReturning(pointcut = "serviceMethods()", returning = "result")
public void afterReturning(JoinPoint joinPoint, Object result) {
String methodName = joinPoint.getSignature().getName();
System.out.println("方法 " + methodName + " 执行完成,返回结果: " + result);
}
// 异常通知
@AfterThrowing(pointcut = "serviceMethods()", throwing = "e")
public void afterThrowing(JoinPoint joinPoint, Throwable e) {
String methodName = joinPoint.getSignature().getName();
System.out.println("方法 " + methodName + " 执行异常: " + e.getMessage());
}
// 环绕通知
@Around("serviceMethods()")
public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object result = joinPoint.proceed(); // 执行目标方法
long end = System.currentTimeMillis();
System.out.println("方法 " + joinPoint.getSignature().getName() +
" 执行耗时: " + (end - start) + "ms");
return result;
}
}3.5 启用 AOP
@Configuration
@EnableAspectJAutoProxy
@ComponentScan("com.example")
public class AppConfig { }或者在 XML 中配置:
<aop:aspectj-autoproxy/>四、常用注解
4.1 核心注解
| 注解 | 说明 | 用法 |
|---|---|---|
@Component | 通用组件注解 | @Component |
@Service | 服务层组件 | @Service |
@Repository | 数据访问层组件 | @Repository |
@Controller | 控制层组件 | @Controller |
@RestController | RESTful 控制器 | @RestController |
@Configuration | 配置类 | @Configuration |
@Bean | 定义 Bean | @Bean |
@Autowired | 自动装配依赖 | @Autowired |
@Qualifier | 指定具体实现类 | @Autowired @Qualifier("beanName") |
@Value | 注入属性值 | @Value("${property.name}") |
4.2 依赖注入注解对比
| 注解 | 来源 | 匹配方式 | 名称匹配方式 |
|---|---|---|---|
@Autowired | Spring | 类型匹配 | @Qualifier |
@Resource | JSR-250 | 名称匹配 | 默认按字段名 |
@Inject | JSR-330 | 类型匹配 | @Named |
// @Autowired 示例
@Autowired
private UserService userService;
// @Resource 示例
@Resource(name = "userService")
private UserService userService;
// @Inject 示例
@Inject
private UserService userService;4.3 配置相关注解
| 注解 | 说明 |
|---|---|
@ComponentScan | 扫描组件 |
@PropertySource | 加载属性文件 |
@Profile | 指定环境配置 |
@Scope | 设置 Bean 作用域 |
@Lazy | 延迟初始化 |
@Primary | 设置首选 Bean |
// 加载配置文件
@Configuration
@PropertySource("classpath:application.properties")
public class AppConfig { }
// 按环境配置
@Profile("dev")
@Configuration
public class DevConfig { }
@Profile("prod")
@Configuration
public class ProdConfig { }五、Spring 整合 MyBatis
5.1 依赖配置
<dependencies>
<!-- Spring Core -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.18</version>
</dependency>
<!-- Spring JDBC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.18</version>
</dependency>
<!-- MyBatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.9</version>
</dependency>
<!-- MyBatis-Spring 整合 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.7</version>
</dependency>
<!-- 数据库驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
</dependencies>5.2 配置数据源
@Configuration
@PropertySource("classpath:jdbc.properties")
public class DataSourceConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
}5.3 配置 MyBatis
@Configuration
@MapperScan("com.example.mapper") // 扫描 Mapper 接口
public class MyBatisConfig {
@Autowired
private DataSource dataSource;
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource);
factoryBean.setTypeAliasesPackage("com.example.entity");
return factoryBean.getObject();
}
@Bean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
}5.4 使用 MyBatis
// 实体类
public class User {
private Long id;
private String name;
private String email;
// getter/setter
}
// Mapper 接口
public interface UserMapper {
@Select("SELECT * FROM users WHERE id = #{id}")
User findById(Long id);
@Insert("INSERT INTO users(name, email) VALUES(#{name}, #{email})")
@Options(useGeneratedKeys = true, keyProperty = "id")
int insert(User user);
}
// 服务层
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public User getUserById(Long id) {
return userMapper.findById(id);
}
public void createUser(User user) {
userMapper.insert(user);
}
}六、Spring MVC 基础
6.1 控制器示例
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
// GET /api/users - 获取所有用户
@GetMapping
public List<User> getAllUsers() {
return userService.findAll();
}
// GET /api/users/{id} - 获取指定用户
@GetMapping("/{id}")
public ResponseEntity<User> getUserById(@PathVariable Long id) {
return userService.findById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
// POST /api/users - 创建新用户
@PostMapping
public ResponseEntity<User> createUser(@Valid @RequestBody User user) {
User savedUser = userService.save(user);
URI location = ServletUriComponentsBuilder
.fromCurrentRequest()
.path("/{id}")
.buildAndExpand(savedUser.getId())
.toUri();
return ResponseEntity.created(location).body(savedUser);
}
// PUT /api/users/{id} - 更新用户
@PutMapping("/{id}")
public ResponseEntity<User> updateUser(@PathVariable Long id, @RequestBody User userDetails) {
return userService.findById(id)
.map(user -> {
user.setName(userDetails.getName());
user.setEmail(userDetails.getEmail());
User updatedUser = userService.save(user);
return ResponseEntity.ok().body(updatedUser);
})
.orElse(ResponseEntity.notFound().build());
}
// DELETE /api/users/{id} - 删除用户
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
if (!userService.existsById(id)) {
return ResponseEntity.notFound().build();
}
userService.deleteById(id);
return ResponseEntity.noContent().build();
}
}6.2 请求参数处理
@RestController
@RequestMapping("/api")
public class ParameterController {
// 路径参数
@GetMapping("/users/{id}")
public String getUserById(@PathVariable Long id) {
return "User ID: " + id;
}
// 查询参数
@GetMapping("/search")
public String searchUsers(@RequestParam String keyword,
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int size) {
return "Searching for '" + keyword + "', page " + page + ", size " + size;
}
// 请求体
@PostMapping("/login")
public String login(@RequestBody LoginRequest loginRequest) {
return "Logging in as " + loginRequest.getUsername();
}
// 请求头
@GetMapping("/headers")
public String headers(@RequestHeader("User-Agent") String userAgent) {
return "Your browser: " + userAgent;
}
// Cookie
@GetMapping("/cookies")
public String cookies(@CookieValue("JSESSIONID") String sessionId) {
return "Session ID: " + sessionId;
}
}
// 请求体对象
public class LoginRequest {
private String username;
private String password;
// getter/setter
}6.3 全局异常处理
@RestControllerAdvice
public class GlobalExceptionHandler {
// 处理资源未找到异常
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleResourceNotFound(ResourceNotFoundException ex) {
ErrorResponse error = new ErrorResponse(
HttpStatus.NOT_FOUND.value(),
"Resource Not Found",
ex.getMessage()
);
return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
}
// 处理参数验证异常
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, String>> handleValidationExceptions(
MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getFieldErrors().forEach(error ->
errors.put(error.getField(), error.getDefaultMessage()));
return ResponseEntity.badRequest().body(errors);
}
// 处理所有其他异常
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGlobalException(Exception ex) {
ErrorResponse error = new ErrorResponse(
HttpStatus.INTERNAL_SERVER_ERROR.value(),
"Internal Server Error",
ex.getMessage()
);
return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR);
}
// 错误响应类
@Data
@AllArgsConstructor
public static class ErrorResponse {
private int status;
private String title;
private String message;
}
}七、Spring Boot 快速入门
7.1 项目创建
使用 Spring Initializr 创建项目:
- 访问 https://start.spring.io
- 选择项目类型(Maven/Gradle)
- 选择 Spring Boot 版本(推荐 2.7.x 或 3.x)
- 填写 Group 和 Artifact
- 选择依赖(Web, Data JPA, MySQL 等)
- 生成并下载项目
7.2 核心配置
application.properties:
# 服务器配置
server.port=8080
# 数据库配置
spring.datasource.url=jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# JPA 配置
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect7.3 启动类
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}7.4 常用 Starter 依赖
| Starter | 说明 |
|---|---|
spring-boot-starter | 核心 Starter,包含自动配置、日志和 YAML 支持 |
spring-boot-starter-web | 构建 Web 应用,包含 Spring MVC 和 Tomcat |
spring-boot-starter-data-jpa | Spring Data JPA 与 Hibernate |
spring-boot-starter-data-redis | Redis 数据访问 |
spring-boot-starter-security | Spring Security |
spring-boot-starter-test | 测试支持 |
spring-boot-starter-thymeleaf | Thymeleaf 模板引擎 |
spring-boot-starter-validation | Bean Validation |
八、最佳实践
8.1 项目结构规范
src/main/java
└── com
└── example
├── Application.java # 主启动类
├── config # 配置类
├── controller # 控制器
├── service # 服务层
│ ├── impl # 服务实现
├── dao # 数据访问层
│ └── mapper # MyBatis Mapper
├── entity # 实体类
├── dto # 数据传输对象
├── exception # 自定义异常
└── util # 工具类8.2 配置管理
- 使用
@ConfigurationProperties统一管理配置 - 按环境分离配置文件:
application-dev.properties,application-prod.properties - 敏感信息使用环境变量或配置中心管理
@Component
@ConfigurationProperties(prefix = "app")
@Data
public class AppConfig {
private String name;
private String version;
private String description;
private Jwt jwt;
@Data
public static class Jwt {
private String secret;
private long expiration;
}
}8.3 事务管理
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private RoleRepository roleRepository;
@Transactional
public User createUser(User user, String roleName) {
User savedUser = userRepository.save(user);
Role role = roleRepository.findByName(roleName);
if (role == null) {
throw new RuntimeException("Role not found: " + roleName);
}
UserRole userRole = new UserRole();
userRole.setUser(savedUser);
userRole.setRole(role);
userRoleRepository.save(userRole);
return savedUser;
}
}事务传播行为:
REQUIRED(默认):支持当前事务,如果没有则新建REQUIRES_NEW:新建事务,如果当前存在事务则挂起SUPPORTS:支持当前事务,如果没有则以非事务方式执行NOT_SUPPORTED:以非事务方式执行,如果当前存在事务则挂起MANDATORY:支持当前事务,如果没有则抛出异常NEVER:以非事务方式执行,如果当前存在事务则抛出异常NESTED:如果当前存在事务,则在嵌套事务内执行
8.4 日志管理
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Service
public class UserService {
private static final Logger logger = LoggerFactory.getLogger(UserService.class);
public User getUserById(Long id) {
logger.info("正在查询用户,ID: {}", id);
try {
User user = userRepository.findById(id).orElse(null);
if (user == null) {
logger.warn("用户不存在,ID: {}", id);
}
return user;
} catch (Exception e) {
logger.error("查询用户时发生错误,ID: {}", id, e);
throw e;
}
}
}