引言
Spring框架是Java企业级应用开发的事实标准,它通过依赖注入(DI)和面向切面编程(AOP)等核心机制,极大地简化了企业级应用的开发。本指南将从基础概念讲起,逐步深入到高级特性,并结合实战技巧,帮助你全面掌握Spring框架。
1. Spring基础概念
1.1 什么是Spring框架?
Spring是一个开源的Java应用框架,最初由Rod Johnson在2003年发布。它的核心特性包括:
- 控制反转(IoC):将对象的创建和依赖关系的管理交给Spring容器
- 依赖注入(DI):通过构造器、setter方法或字段注入依赖
- 面向切面编程(AOP):分离横切关注点,如日志、事务管理
- 模块化设计:可以按需使用Spring的不同模块
1.2 Spring模块概览
Spring框架包含多个模块,主要分为:
- Spring Core:提供IoC容器和DI支持
- Spring AOP:提供面向切面编程支持
- Spring MVC:Web应用框架
- Spring Data:简化数据访问
- Spring Security:安全框架
- Spring Boot:快速构建Spring应用的工具
2. Spring核心原理详解
2.1 IoC容器与Bean生命周期
Spring IoC容器负责管理Bean的创建、配置和生命周期。主要实现类有:
- BeanFactory:基础容器,延迟加载Bean
- ApplicationContext:扩展BeanFactory,提供企业级功能
Bean生命周期示例
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class MyBean implements InitializingBean, DisposableBean,
BeanNameAware, ApplicationContextAware {
private String beanName;
private ApplicationContext applicationContext;
// BeanNameAware回调
@Override
public void setBeanName(String name) {
this.beanName = name;
System.out.println("Bean名称: " + name);
}
// ApplicationContextAware回调
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
System.out.println("ApplicationContext已注入");
}
// InitializingBean回调
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("属性设置完成,执行初始化逻辑");
}
// 自定义初始化方法(通过@PostConstruct注解)
@PostConstruct
public void customInit() {
System.out.println("自定义初始化方法执行");
}
// DisposableBean回调
@Override
public void destroy() throws Exception {
System.out.println("Bean销毁,执行清理逻辑");
}
// 自定义销毁方法(通过@PreDestroy注解)
@PreDestroy
public void customDestroy() {
System.out.println("自定义销毁方法执行");
}
}
2.2 依赖注入(DI)详解
Spring支持多种依赖注入方式:
构造器注入(推荐)
@Service
public class OrderService {
private final UserService userService;
private final PaymentService paymentService;
// 构造器注入
@Autowired
public OrderService(UserService userService, PaymentService paymentService) {
this.userService = userService;
this.paymentService = paymentService;
}
public void processOrder(Order order) {
// 业务逻辑
}
}
Setter注入
@Service
public class OrderService {
private UserService userService;
private PaymentService paymentService;
// Setter注入
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
@Autowired
public void setPaymentService(PaymentService paymentService) {
this.paymentService = paymentService;
}
}
字段注入(不推荐)
@Service
public class OrderService {
@Autowired
private UserService userService;
@Autowired
private PaymentService paymentService;
}
2.3 Bean的作用域
Spring Bean支持以下作用域:
- singleton:默认,每个容器中一个Bean实例
- prototype:每次请求都创建新实例
- request:每个HTTP请求一个实例(Web环境)
- session:每个HTTP会话一个实例(Web环境)
- global-session:全局HTTP会话(Portlet环境)
@Component
@Scope("prototype") // 每次获取都创建新实例
public class PrototypeBean {
private final String id = UUID.randomUUID().toString();
public String getId() {
return id;
}
}
3. Spring AOP深入解析
3.1 AOP核心概念
- 切面(Aspect):横切关注点的模块化
- 连接点(Join Point):程序执行过程中的点(如方法执行)
- 切点(Pointcut):匹配连接点的表达式
- 通知(Advice):在切点执行的具体逻辑
- 目标对象(Target):被代理的对象
- 代理(Proxy):Spring创建的代理对象
3.2 AOP实现示例
自定义切面
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Aspect
@Component
public class LoggingAspect {
private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);
// 切点:所有Service层的方法
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceMethods() {}
// 前置通知
@Before("serviceMethods()")
public void logBefore(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
logger.info("执行方法: {},参数: {}", methodName, Arrays.toString(joinPoint.getArgs()));
}
// 后置通知
@After("serviceMethods()")
public void logAfter(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
logger.info("方法执行完成: {}", methodName);
}
// 返回通知
@AfterReturning(pointcut = "serviceMethods()", returning = "result")
public void logAfterReturning(JoinPoint joinPoint, Object result) {
String methodName = joinPoint.getSignature().getName();
logger.info("方法返回结果: {} -> {}", methodName, result);
}
// 异常通知
@AfterThrowing(pointcut = "serviceMethods()", throwing = "ex")
public void logAfterThrowing(JoinPoint joinPoint, Throwable ex) {
String methodName = joinPoint.getSignature().getName();
logger.error("方法执行异常: {},异常信息: {}", methodName, ex.getMessage());
}
// 环绕通知(最强大)
@Around("serviceMethods()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
String methodName = joinPoint.getSignature().getName();
logger.info("开始执行方法: {}", methodName);
try {
// 执行目标方法
Object result = joinPoint.proceed();
long endTime = System.currentTimeMillis();
logger.info("方法执行成功: {},耗时: {}ms", methodName, endTime - startTime);
return result;
} catch (Throwable ex) {
long endTime = System.currentTimeMillis();
logger.error("方法执行失败: {},耗时: {}ms,异常: {}",
methodName, endTime - startTime, ex.getMessage());
throw ex;
}
}
}
使用注解驱动AOP
import org.springframework.stereotype.Component;
import org.springframework.core.annotation.Order;
@Aspect
@Component
@Order(1) // 定义多个切面时的执行顺序
public class TransactionAspect {
@Pointcut("@annotation(org.springframework.transaction.annotation.Transactional)")
public void transactionalMethods() {}
@Around("transactionalMethods()")
public Object manageTransaction(ProceedingJoinPoint joinPoint) throws Throwable {
// 开启事务
TransactionStatus status = beginTransaction();
try {
Object result = joinPoint.proceed();
// 提交事务
commitTransaction(status);
return result;
} catch (Exception e) {
// 回滚事务
rollbackTransaction(status);
throw e;
}
}
private TransactionStatus beginTransaction() {
// 事务开始逻辑
return null;
}
private void commitTransaction(TransactionStatus status) {
// 事务提交逻辑
}
private void rollbackTransaction(TransactionStatus status) {
// 事务回滚逻辑
}
}
4. Spring MVC实战
4.1 Spring MVC核心组件
- DispatcherServlet:前端控制器
- HandlerMapping:请求映射处理器
- HandlerAdapter:处理器适配器
- ViewResolver:视图解析器
- ModelAndView:模型和视图容器
4.2 RESTful API开发示例
import org.springframework.web.bind.annotation.*;
import org.springframework.http.ResponseEntity;
import org.springframework.http.HttpStatus;
import javax.validation.Valid;
@RestController
@RequestMapping("/api/v1/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
// GET /api/v1/users/{id}
@GetMapping("/{id}")
public ResponseEntity<User> getUserById(@PathVariable Long id) {
User user = userService.findById(id);
if (user == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(user);
}
// GET /api/v1/users?name=John
@GetMapping
public ResponseEntity<List<User>> getUsersByName(@RequestParam(required = false) String name) {
List<User> users = name != null ?
userService.findByName(name) : userService.findAll();
return ResponseEntity.ok(users);
}
// POST /api/v1/users
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public User createUser(@Valid @RequestBody User user) {
return userService.save(user);
}
// PUT /api/v1/users/{id}
@PutMapping("/{id}")
public ResponseEntity<User> updateUser(@PathVariable Long id,
@Valid @RequestBody User user) {
if (!userService.existsById(id)) {
return ResponseEntity.notFound().build();
}
user.setId(id);
return ResponseEntity.ok(userService.save(user));
}
// DELETE /api/v1/users/{id}
@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deleteUser(@PathVariable Long id) {
userService.deleteById(id);
}
// 处理异常
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Map<String, String> 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);
});
return errors;
}
}
4.3 文件上传与下载
import org.springframework.web.multipart.MultipartFile;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
@RestController
@RequestMapping("/api/files")
public class FileController {
@PostMapping("/upload")
public ResponseEntity<String> uploadFile(@RequestParam("file") MultipartFile file) {
if (file.isEmpty()) {
return ResponseEntity.badRequest().body("文件不能为空");
}
try {
// 保存文件到指定目录
String fileName = file.getOriginalFilename();
Path path = Paths.get("uploads/" + fileName);
Files.copy(file.getInputStream(), path, StandardCopyOption.REPLACE_EXISTING);
return ResponseEntity.ok("文件上传成功: " + fileName);
} catch (IOException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("文件上传失败: " + e.getMessage());
}
}
@GetMapping("/download/{fileName}")
public ResponseEntity<Resource> downloadFile(@PathVariable String fileName) {
try {
Path path = Paths.get("uploads/" + fileName);
Resource resource = new UrlResource(path.toUri());
if (!resource.exists() || !resource.isReadable()) {
return ResponseEntity.notFound().build();
}
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setContentDispositionFormData("attachment", fileName);
return ResponseEntity.ok()
.headers(headers)
.body(resource);
} catch (MalformedURLException e) {
return ResponseEntity.badRequest().build();
}
}
}
5. Spring Data JPA实战
5.1 JPA基础配置
# application.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/spring_demo
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: update
show-sql: true
properties:
hibernate:
format_sql: true
dialect: org.hibernate.dialect.MySQL8Dialect
5.2 实体类定义
import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true, length = 50)
private String username;
@Column(nullable = false, length = 100)
private String email;
@Column(name = "created_at")
private LocalDateTime createdAt;
@Column(name = "updated_at")
private LocalDateTime updatedAt;
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<Order> orders = new ArrayList<>();
// 构造方法
public User() {}
public User(String username, String email) {
this.username = username;
this.email = email;
}
// 预持久化回调
@PrePersist
protected void onCreate() {
this.createdAt = LocalDateTime.now();
this.updatedAt = LocalDateTime.now();
}
// 预更新回调
@PreUpdate
protected void onUpdate() {
this.updatedAt = LocalDateTime.now();
}
// Getter和Setter方法
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public LocalDateTime getCreatedAt() { return createdAt; }
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
public LocalDateTime getUpdatedAt() { return updatedAt; }
public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; }
public List<Order> getOrders() { return orders; }
public void setOrders(List<Order> orders) { this.orders = orders; }
}
5.3 Repository接口
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
// 方法名查询
Optional<User> findByUsername(String username);
List<User> findByEmailContaining(String email);
// 使用JPQL查询
@Query("SELECT u FROM User u WHERE u.username = :username AND u.email = :email")
Optional<User> findByUsernameAndEmail(@Param("username") String username,
@Param("email") String email);
// 原生SQL查询
@Query(value = "SELECT * FROM users WHERE created_at > :date", nativeQuery = true)
List<User> findUsersCreatedAfter(@Param("date") LocalDateTime date);
// 分页查询
@Query("SELECT u FROM User u WHERE u.username LIKE %:keyword%")
Page<User> findByUsernameContaining(@Param("keyword") String keyword, Pageable pageable);
// 自定义更新操作
@Modifying
@Query("UPDATE User u SET u.email = :email WHERE u.id = :id")
int updateUserEmail(@Param("id") Long id, @Param("email") String email);
// 复杂查询
@Query("SELECT u FROM User u JOIN u.orders o WHERE o.status = 'PENDING' GROUP BY u.id")
List<User> findUsersWithPendingOrders();
}
5.4 Service层实现
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
// 创建用户
public User createUser(User user) {
// 业务验证
if (userRepository.findByUsername(user.getUsername()).isPresent()) {
throw new RuntimeException("用户名已存在");
}
// 密码加密(示例)
// user.setPassword(passwordEncoder.encode(user.getPassword()));
return userRepository.save(user);
}
// 获取用户(带分页)
public Page<User> getUsers(int page, int size, String sortBy) {
Pageable pageable = PageRequest.of(page, size, Sort.by(sortBy));
return userRepository.findAll(pageable);
}
// 根据用户名查询
public Optional<User> findByUsername(String username) {
return userRepository.findByUsername(username);
}
// 更新用户信息
public User updateUser(Long id, User userUpdates) {
User user = userRepository.findById(id)
.orElseThrow(() -> new RuntimeException("用户不存在"));
// 只更新允许的字段
if (userUpdates.getEmail() != null) {
user.setEmail(userUpdates.getEmail());
}
return userRepository.save(user);
}
// 删除用户
@Transactional
public void deleteUser(Long id) {
if (!userRepository.existsById(id)) {
throw new RuntimeException("用户不存在");
}
userRepository.deleteById(id);
}
// 复杂业务逻辑:获取用户订单统计
public Map<String, Object> getUserOrderStats(Long userId) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new RuntimeException("用户不存在"));
Map<String, Object> stats = new HashMap<>();
stats.put("totalOrders", user.getOrders().size());
stats.put("pendingOrders",
user.getOrders().stream()
.filter(order -> "PENDING".equals(order.getStatus()))
.count());
stats.put("totalSpent",
user.getOrders().stream()
.mapToDouble(Order::getAmount)
.sum());
return stats;
}
}
6. Spring Boot进阶特性
6.1 自动配置原理
Spring Boot通过@SpringBootApplication注解实现自动配置:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@SpringBootApplication
@EnableConfigurationProperties(AppProperties.class)
@EnableJpaRepositories(basePackages = "com.example.repository")
@EntityScan(basePackages = "com.example.entity")
@ComponentScan(basePackages = "com.example")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
6.2 自定义Starter开发
创建自定义Starter的步骤:
- 创建Starter模块:
my-spring-boot-starter - 定义自动配置类:
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
@AutoConfiguration
@ConditionalOnClass(MyService.class)
@EnableConfigurationProperties(MyProperties.class)
public class MyServiceAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public MyService myService(MyProperties properties) {
return new MyService(properties);
}
}
- 配置自动配置文件:在
META-INF/spring.factories中添加:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.MyServiceAutoConfiguration
6.3 配置文件管理
# application.yml
spring:
profiles:
active: dev
config:
import: optional:configserver:http://localhost:8888
---
# 开发环境配置
spring:
config:
activate:
on-profile: dev
datasource:
url: jdbc:h2:mem:testdb
username: sa
password:
jpa:
hibernate:
ddl-auto: create-drop
show-sql: true
---
# 生产环境配置
spring:
config:
activate:
on-profile: prod
datasource:
url: jdbc:mysql://prod-db:3306/app
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
jpa:
hibernate:
ddl-auto: validate
show-sql: false
7. Spring Security实战
7.1 基础安全配置
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
@Override
public UserDetailsService userDetailsService() {
UserDetails user = User.withUsername("user")
.password(passwordEncoder().encode("password"))
.roles("USER")
.build();
UserDetails admin = User.withUsername("admin")
.password(passwordEncoder().encode("admin"))
.roles("ADMIN", "USER")
.build();
return new InMemoryUserDetailsManager(user, admin);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api/public/**").permitAll()
.antMatchers("/api/admin/**").hasRole("ADMIN")
.antMatchers("/api/user/**").hasAnyRole("USER", "ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/home")
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login?logout")
.permitAll()
.and()
.csrf().disable(); // 对于REST API,通常需要禁用CSRF
}
}
7.2 JWT认证实现
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Component;
import java.util.*;
import java.util.stream.Collectors;
@Component
public class JwtTokenProvider {
private static final String SECRET_KEY = "your-secret-key-change-in-production";
private static final long EXPIRATION_TIME = 86400000; // 24小时
public String generateToken(Authentication authentication) {
UserDetailsImpl userPrincipal = (UserDetailsImpl) authentication.getPrincipal();
Date now = new Date();
Date expiryDate = new Date(now.getTime() + EXPIRATION_TIME);
return Jwts.builder()
.setSubject(userPrincipal.getUsername())
.setIssuedAt(now)
.setExpiration(expiryDate)
.claim("roles", userPrincipal.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.toList()))
.signWith(SignatureAlgorithm.HS512, SECRET_KEY)
.compact();
}
public Authentication getAuthentication(String token) {
Claims claims = Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody();
String username = claims.getSubject();
List<String> roles = claims.get("roles", List.class);
Collection<GrantedAuthority> authorities = roles.stream()
.map(role -> new SimpleGrantedAuthority(role))
.collect(Collectors.toList());
return new UsernamePasswordAuthenticationToken(username, null, authorities);
}
public boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token);
return true;
} catch (Exception e) {
return false;
}
}
}
8. Spring性能优化技巧
8.1 连接池优化
# application.yml
spring:
datasource:
hikari:
connection-timeout: 30000
maximum-pool-size: 20
minimum-idle: 5
idle-timeout: 600000
max-lifetime: 1800000
leak-detection-threshold: 60000
pool-name: MyHikariCP
8.2 缓存配置
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
@Configuration
@EnableCaching
public class CacheConfig {
// 使用Redis作为缓存
@Bean
public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.serializeValuesWith(RedisSerializationContext.SerializationPair
.fromSerializer(new GenericJackson2JsonRedisSerializer()))
.entryTtl(Duration.ofMinutes(10));
return RedisCacheManager.builder(connectionFactory)
.cacheDefaults(config)
.build();
}
// 使用ConcurrentMap作为缓存(开发环境)
@Bean
public CacheManager concurrentMapCacheManager() {
return new ConcurrentMapCacheManager("users", "products", "orders");
}
}
8.3 异步处理
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Service;
@Service
@EnableAsync
public class AsyncService {
@Async("taskExecutor")
public CompletableFuture<String> processAsync(String data) {
// 模拟耗时操作
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return CompletableFuture.completedFuture("Processed: " + data);
}
@Async
public void sendEmail(String to, String subject, String body) {
// 发送邮件的耗时操作
System.out.println("Sending email to " + to);
}
}
// 配置线程池
@Configuration
public class AsyncConfig {
@Bean
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("Async-");
executor.initialize();
return executor;
}
}
9. 测试策略
9.1 单元测试
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository userRepository;
@InjectMocks
private UserService userService;
@Test
void shouldCreateUserSuccessfully() {
// 准备测试数据
User user = new User("testuser", "test@example.com");
when(userRepository.findByUsername("testuser")).thenReturn(Optional.empty());
when(userRepository.save(any(User.class))).thenReturn(user);
// 执行测试
User result = userService.createUser(user);
// 验证结果
assertNotNull(result);
assertEquals("testuser", result.getUsername());
verify(userRepository, times(1)).save(user);
}
@Test
void shouldThrowExceptionWhenUsernameExists() {
User existingUser = new User("existing", "existing@example.com");
when(userRepository.findByUsername("existing"))
.thenReturn(Optional.of(existingUser));
assertThrows(RuntimeException.class, () -> {
userService.createUser(new User("existing", "new@example.com"));
});
}
}
9.2 集成测试
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.ActiveProfiles;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("test")
class UserControllerIntegrationTest {
@Autowired
private TestRestTemplate restTemplate;
@Test
void shouldCreateUserAndReturn201() {
User user = new User("integration", "integration@example.com");
ResponseEntity<User> response = restTemplate.postForEntity(
"/api/v1/users", user, User.class);
assertEquals(HttpStatus.CREATED, response.getStatusCode());
assertNotNull(response.getBody());
assertEquals("integration", response.getBody().getUsername());
}
@Test
void shouldReturn404ForNonExistentUser() {
ResponseEntity<User> response = restTemplate.getForEntity(
"/api/v1/users/99999", User.class);
assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
}
}
10. 实战项目示例:电商系统
10.1 项目架构设计
src/main/java/com/example/ecommerce/
├── config/ # 配置类
├── controller/ # 控制器层
├── service/ # 服务层
├── repository/ # 数据访问层
├── entity/ # 实体类
├── dto/ # 数据传输对象
├── exception/ # 异常处理
└── security/ # 安全配置
10.2 核心实体关系
// 用户实体
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true)
private String username;
private String password;
@Enumerated(EnumType.STRING)
private UserRole role;
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<Order> orders;
}
// 商品实体
@Entity
@Table(name = "products")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String description;
private BigDecimal price;
private Integer stock;
@OneToMany(mappedBy = "product", cascade = CascadeType.ALL)
private List<OrderItem> orderItems;
}
// 订单实体
@Entity
@Table(name = "orders")
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "user_id")
private User user;
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private List<OrderItem> items;
private BigDecimal totalAmount;
@Enumerated(EnumType.STRING)
private OrderStatus status;
private LocalDateTime orderDate;
}
// 订单项实体
@Entity
@Table(name = "order_items")
public class OrderItem {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "order_id")
private Order order;
@ManyToOne
@JoinColumn(name = "product_id")
private Product product;
private Integer quantity;
private BigDecimal price;
}
10.3 服务层实现
@Service
@Transactional
public class OrderService {
private final OrderRepository orderRepository;
private final ProductRepository productRepository;
private final UserRepository userRepository;
private final PaymentService paymentService;
public OrderService(OrderRepository orderRepository,
ProductRepository productRepository,
UserRepository userRepository,
PaymentService paymentService) {
this.orderRepository = orderRepository;
this.productRepository = productRepository;
this.userRepository = userRepository;
this.paymentService = paymentService;
}
public Order createOrder(Long userId, List<OrderItemDTO> items) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new RuntimeException("用户不存在"));
Order order = new Order();
order.setUser(user);
order.setOrderDate(LocalDateTime.now());
order.setStatus(OrderStatus.PENDING);
BigDecimal totalAmount = BigDecimal.ZERO;
List<OrderItem> orderItems = new ArrayList<>();
for (OrderItemDTO itemDTO : items) {
Product product = productRepository.findById(itemDTO.getProductId())
.orElseThrow(() -> new RuntimeException("商品不存在"));
// 检查库存
if (product.getStock() < itemDTO.getQuantity()) {
throw new RuntimeException("库存不足");
}
// 扣减库存
product.setStock(product.getStock() - itemDTO.getQuantity());
productRepository.save(product);
OrderItem orderItem = new OrderItem();
orderItem.setOrder(order);
orderItem.setProduct(product);
orderItem.setQuantity(itemDTO.getQuantity());
orderItem.setPrice(product.getPrice());
orderItems.add(orderItem);
totalAmount = totalAmount.add(
product.getPrice().multiply(BigDecimal.valueOf(itemDTO.getQuantity())));
}
order.setItems(orderItems);
order.setTotalAmount(totalAmount);
// 处理支付
paymentService.processPayment(order);
return orderRepository.save(order);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void cancelOrder(Long orderId) {
Order order = orderRepository.findById(orderId)
.orElseThrow(() -> new RuntimeException("订单不存在"));
if (order.getStatus() != OrderStatus.PENDING) {
throw new RuntimeException("只能取消待处理订单");
}
// 恢复库存
for (OrderItem item : order.getItems()) {
Product product = item.getProduct();
product.setStock(product.getStock() + item.getQuantity());
productRepository.save(product);
}
order.setStatus(OrderStatus.CANCELLED);
orderRepository.save(order);
}
}
11. 常见问题与解决方案
11.1 循环依赖问题
问题:A依赖B,B依赖A,导致Bean创建失败。
解决方案:
- 使用构造器注入(Spring会尝试解决循环依赖)
- 重构代码,引入第三方组件
- 使用
@Lazy延迟初始化
@Service
public class ServiceA {
private final ServiceB serviceB;
@Autowired
public ServiceA(@Lazy ServiceB serviceB) {
this.serviceB = serviceB;
}
}
11.2 事务失效场景
常见原因:
- 方法不是public
- 自调用问题(this.method())
- 异常类型不匹配
- 数据库不支持事务
解决方案:
@Service
public class MyService {
// 正确:通过代理调用
@Transactional
public void method1() {
// 业务逻辑
}
// 错误:自调用,事务失效
public void method2() {
method1(); // 这里不会触发事务
}
// 正确:通过注入自身调用
@Autowired
private MyService self;
public void method3() {
self.method1(); // 这里会触发事务
}
}
11.3 N+1查询问题
问题:查询主实体时,关联实体被延迟加载,导致多次查询。
解决方案:
- 使用
@EntityGraph注解 - 使用JOIN FETCH
- 使用DTO投影
// 使用@EntityGraph
@EntityGraph(attributePaths = {"orders"})
@Query("SELECT u FROM User u WHERE u.id = :id")
Optional<User> findByIdWithOrders(@Param("id") Long id);
// 使用JOIN FETCH
@Query("SELECT u FROM User u JOIN FETCH u.orders WHERE u.id = :id")
Optional<User> findByIdWithOrders(@Param("id") Long id);
// 使用DTO投影
public interface UserOrderSummary {
Long getId();
String getUsername();
List<OrderSummary> getOrders();
interface OrderSummary {
Long getId();
BigDecimal getTotalAmount();
}
}
@Query("SELECT u.id as id, u.username as username, o as orders " +
"FROM User u JOIN u.orders o WHERE u.id = :id")
UserOrderSummary findUserOrderSummary(@Param("id") Long id);
12. 学习路径建议
12.1 初级阶段(1-2个月)
- 掌握Spring Core基础(IoC、DI、Bean生命周期)
- 学习Spring MVC基础
- 理解AOP概念和基本使用
- 完成简单CRUD项目
12.2 中级阶段(2-3个月)
- 深入学习Spring Data JPA
- 掌握Spring Security基础
- 学习Spring Boot自动配置原理
- 完成带用户认证的Web应用
12.3 高级阶段(3-6个月)
- 深入理解Spring源码
- 掌握微服务架构(Spring Cloud)
- 学习性能优化和调优
- 完成分布式系统项目
12.4 持续学习
- 关注Spring官方博客和文档
- 参与开源项目贡献
- 学习相关技术栈(Docker、Kubernetes、消息队列等)
- 参加技术社区和会议
13. 推荐资源
13.1 官方文档
- Spring Framework官方文档:https://spring.io/projects/spring-framework
- Spring Boot官方文档:https://spring.io/projects/spring-boot
- Spring Data JPA文档:https://spring.io/projects/spring-data-jpa
13.2 书籍推荐
- 《Spring实战》(Craig Walls著)
- 《Spring Boot编程思想》(小马哥著)
- 《Spring源码深度解析》(郝佳著)
13.3 在线课程
- Spring官方培训课程
- Udemy上的Spring Boot课程
- 极客时间Spring专栏
13.4 开源项目参考
- Spring PetClinic示例项目
- Spring Boot Admin
- Spring Cloud微服务示例
结语
Spring框架是一个功能强大且不断发展的生态系统。通过本指南的学习,你应该已经掌握了从基础到进阶的核心知识。记住,理论学习必须结合实践,建议你从一个简单的项目开始,逐步增加复杂度,不断挑战自己。
Spring的学习是一个持续的过程,随着版本的更新和新技术的出现,你需要保持学习的热情和好奇心。祝你在Spring开发的道路上取得成功!
