引言
Spring框架是Java企业级开发中最流行、最强大的开源框架之一。它提供了一个全面的编程和配置模型,为现代基于Java的企业应用程序提供了坚实的基础。无论你是刚接触Java开发的新手,还是希望提升技能的中级开发者,掌握Spring框架都是职业发展的关键一步。本指南将带你从Spring的基础概念开始,逐步深入到高级特性和实战技巧,帮助你构建健壮、可维护的企业级应用。
第一部分:Spring框架基础
1.1 Spring框架概述
Spring框架是一个分层架构,由大约20个模块组成。这些模块被组织成核心容器、数据访问/集成、Web、AOP(面向切面编程)、工具支持和测试等模块。
核心特性:
- 依赖注入(DI):通过控制反转(IoC)容器管理对象之间的依赖关系
- 面向切面编程(AOP):分离横切关注点,如日志、事务管理
- 声明式事务管理:简化事务处理
- MVC框架:提供清晰的Web层分离
- 集成支持:与各种ORM框架、消息队列等无缝集成
1.2 为什么选择Spring?
实际案例对比: 假设我们要开发一个用户管理系统,没有Spring时,我们需要手动创建和管理对象:
// 传统方式 - 手动管理依赖
public class UserService {
private UserRepository userRepository;
private EmailService emailService;
public UserService() {
this.userRepository = new UserRepositoryImpl();
this.emailService = new EmailServiceImpl();
}
// 业务方法...
}
使用Spring后:
// Spring方式 - 依赖注入
@Service
public class UserService {
private final UserRepository userRepository;
private final EmailService emailService;
@Autowired
public UserService(UserRepository userRepository, EmailService emailService) {
this.userRepository = userRepository;
this.emailService = emailService;
}
// 业务方法...
}
优势对比:
- 可测试性:Spring允许轻松注入Mock对象进行单元测试
- 可维护性:依赖关系清晰,修改实现不影响调用者
- 灵活性:通过配置可以轻松切换实现
1.3 Spring核心概念详解
1.3.1 IoC容器与Bean
Spring IoC容器负责实例化、配置和组装对象。主要实现有:
ApplicationContext(推荐)BeanFactory(较旧)
Bean定义示例:
// 使用注解定义Bean
@Component
public class MyBean {
// 类实现
}
// 使用Java配置类
@Configuration
public class AppConfig {
@Bean
public MyBean myBean() {
return new MyBean();
}
}
Bean的作用域:
- singleton(默认):每个容器一个实例
- prototype:每次请求都创建新实例
- request:每个HTTP请求一个实例
- session:每个HTTP会话一个实例
- global-session:全局HTTP会话
1.3.2 依赖注入(DI)
Spring支持三种依赖注入方式:
1. 构造器注入(推荐)
@Service
public class OrderService {
private final PaymentService paymentService;
private final NotificationService notificationService;
@Autowired
public OrderService(PaymentService paymentService,
NotificationService notificationService) {
this.paymentService = paymentService;
this.notificationService = notificationService;
}
}
2. Setter注入
@Service
public class OrderService {
private PaymentService paymentService;
@Autowired
public void setPaymentService(PaymentService paymentService) {
this.paymentService = paymentService;
}
}
3. 字段注入(不推荐用于生产代码)
@Service
public class OrderService {
@Autowired
private PaymentService paymentService;
}
第二部分:Spring Boot快速入门
2.1 Spring Boot简介
Spring Boot是Spring框架的扩展,旨在简化Spring应用的初始搭建和开发过程。它提供了:
- 自动配置
- 嵌入式服务器
- 生产就绪特性
- 简化依赖管理
2.2 创建第一个Spring Boot应用
使用Spring Initializr:
- 访问 start.spring.io
- 选择项目元数据(Group、Artifact)
- 添加依赖:Spring Web
- 生成项目并导入IDE
项目结构:
my-spring-boot-app/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/example/demo/
│ │ │ ├── DemoApplication.java
│ │ │ └── controller/
│ │ │ └── HelloController.java
│ │ └── resources/
│ │ ├── application.properties
│ │ └── static/
│ └── test/
└── pom.xml
主应用类:
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
创建第一个Controller:
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "Hello, Spring Boot!";
}
@GetMapping("/hello/{name}")
public String helloName(@PathVariable String name) {
return "Hello, " + name + "!";
}
}
2.3 Spring Boot自动配置原理
Spring Boot通过@SpringBootApplication注解启用自动配置:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
// ...
}
自动配置工作原理:
- Spring Boot启动时扫描
META-INF/spring.factories文件 - 加载
EnableAutoConfiguration键下的配置类 - 根据条件(
@Conditional注解)决定是否启用配置
自定义自动配置示例:
@Configuration
@ConditionalOnClass({DataSource.class, EmbeddedDatabaseType.class})
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public DataSource dataSource(DataSourceProperties properties) {
return properties.initializeDataSourceBuilder().build();
}
}
第三部分:Spring核心特性深入
3.1 Spring AOP(面向切面编程)
AOP允许将横切关注点(如日志、事务、安全)从业务逻辑中分离。
AOP术语:
- 切面(Aspect):横切关注点的模块化
- 连接点(Join Point):程序执行过程中的点
- 通知(Advice):在特定连接点执行的动作
- 切点(Pointcut):匹配连接点的表达式
AOP示例 - 日志记录:
@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("Executing method: {}", methodName);
}
// 后置通知
@After("serviceMethods()")
public void logAfter(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
logger.info("Finished executing method: {}", methodName);
}
// 异常通知
@AfterThrowing(pointcut = "serviceMethods()", throwing = "ex")
public void logException(JoinPoint joinPoint, Exception ex) {
String methodName = joinPoint.getSignature().getName();
logger.error("Exception in method {}: {}", methodName, ex.getMessage());
}
// 环绕通知
@Around("serviceMethods()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
try {
Object result = joinPoint.proceed();
long end = System.currentTimeMillis();
logger.info("Method {} executed in {} ms",
joinPoint.getSignature().getName(),
end - start);
return result;
} catch (Exception e) {
logger.error("Exception in method {}: {}",
joinPoint.getSignature().getName(),
e.getMessage());
throw e;
}
}
}
启用AOP:
@Configuration
@EnableAspectJAutoProxy
public class AopConfig {
// 配置类
}
3.2 Spring事务管理
Spring提供了声明式事务管理,通过@Transactional注解实现。
事务传播行为:
- REQUIRED(默认):如果存在事务则加入,否则新建
- REQUIRES_NEW:总是新建事务,挂起当前事务
- MANDATORY:必须在事务中执行
- SUPPORTS:支持事务,但不强制
- NOT_SUPPORTED:不支持事务
- NEVER:绝不能在事务中执行
事务示例:
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
@Autowired
private PaymentService paymentService;
@Transactional(propagation = Propagation.REQUIRED,
rollbackFor = Exception.class)
public Order createOrder(Order order) {
// 1. 保存订单
orderRepository.save(order);
// 2. 处理支付
paymentService.processPayment(order);
// 3. 发送通知
sendNotification(order);
return order;
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void sendNotification(Order order) {
// 独立事务的通知发送
// ...
}
}
事务配置:
@Configuration
@EnableTransactionManagement
public class TransactionConfig {
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
3.3 Spring事件机制
Spring提供了强大的事件发布/订阅机制。
自定义事件:
// 自定义事件类
public class OrderCreatedEvent extends ApplicationEvent {
private final Order order;
public OrderCreatedEvent(Object source, Order order) {
super(source);
this.order = order;
}
public Order getOrder() {
return order;
}
}
事件发布者:
@Service
public class OrderService {
@Autowired
private ApplicationEventPublisher eventPublisher;
public void createOrder(Order order) {
// 业务逻辑
orderRepository.save(order);
// 发布事件
OrderCreatedEvent event = new OrderCreatedEvent(this, order);
eventPublisher.publishEvent(event);
}
}
事件监听器:
@Component
public class OrderEventListener {
@EventListener
@Async
public void handleOrderCreated(OrderCreatedEvent event) {
// 异步处理事件
Order order = event.getOrder();
// 发送邮件、更新库存等
}
@EventListener(condition = "#event.order.amount > 1000")
public void handleLargeOrder(OrderCreatedEvent event) {
// 只处理大额订单
// 发送特殊通知
}
}
第四部分:Spring Web开发
4.1 Spring MVC架构
Spring MVC遵循Model-View-Controller模式,提供清晰的Web层分离。
核心组件:
- DispatcherServlet:前端控制器
- HandlerMapping:请求映射到处理器
- HandlerAdapter:执行处理器
- ViewResolver:解析视图
- ModelAndView:模型和视图的组合
请求处理流程:
客户端请求 → DispatcherServlet → HandlerMapping → HandlerAdapter → Controller →
ModelAndView → ViewResolver → View → 响应
4.2 RESTful API开发
Controller示例:
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
// 获取用户列表
@GetMapping
public ResponseEntity<List<User>> getUsers(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
List<User> users = userService.getUsers(page, size);
return ResponseEntity.ok(users);
}
// 获取单个用户
@GetMapping("/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
User user = userService.getUserById(id);
if (user == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(user);
}
// 创建用户
@PostMapping
public ResponseEntity<User> createUser(@Valid @RequestBody User user) {
User created = userService.createUser(user);
return ResponseEntity.status(HttpStatus.CREATED).body(created);
}
// 更新用户
@PutMapping("/{id}")
public ResponseEntity<User> updateUser(@PathVariable Long id,
@Valid @RequestBody User user) {
User updated = userService.updateUser(id, user);
if (updated == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(updated);
}
// 删除用户
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
boolean deleted = userService.deleteUser(id);
if (!deleted) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.noContent().build();
}
}
请求参数验证:
// 实体类
public class User {
@NotNull(message = "用户名不能为空")
@Size(min = 3, max = 20, message = "用户名长度必须在3-20之间")
private String username;
@Email(message = "邮箱格式不正确")
@NotNull(message = "邮箱不能为空")
private String email;
@Min(value = 18, message = "年龄必须大于18岁")
@Max(value = 100, message = "年龄不能超过100岁")
private Integer age;
// getters and setters
}
// 全局异常处理
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<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 ResponseEntity.badRequest().body(errors);
}
}
4.3 文件上传与下载
文件上传:
@RestController
@RequestMapping("/api/files")
public class FileController {
@Value("${file.upload-dir}")
private String uploadDir;
@PostMapping("/upload")
public ResponseEntity<String> uploadFile(@RequestParam("file") MultipartFile file) {
if (file.isEmpty()) {
return ResponseEntity.badRequest().body("文件不能为空");
}
try {
// 创建上传目录
Path uploadPath = Paths.get(uploadDir);
if (!Files.exists(uploadPath)) {
Files.createDirectories(uploadPath);
}
// 生成唯一文件名
String fileName = UUID.randomUUID().toString() + "_" + file.getOriginalFilename();
Path filePath = uploadPath.resolve(fileName);
// 保存文件
Files.copy(file.getInputStream(), filePath, 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 filePath = Paths.get(uploadDir).resolve(fileName);
if (!Files.exists(filePath)) {
return ResponseEntity.notFound().build();
}
Resource resource = new UrlResource(filePath.toUri());
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + fileName + "\"")
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(resource);
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(null);
}
}
}
第五部分:Spring数据访问
5.1 Spring Data JPA
Spring Data JPA简化了JPA的使用,提供了Repository接口。
实体类:
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true)
private String username;
@Column(nullable = false)
private String email;
@Column
private Integer age;
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<Order> orders = new ArrayList<>();
// 构造函数、getter/setter
}
Repository接口:
public interface UserRepository extends JpaRepository<User, Long> {
// 自定义查询方法
Optional<User> findByUsername(String username);
List<User> findByAgeGreaterThan(Integer age);
// JPQL查询
@Query("SELECT u FROM User u WHERE u.email LIKE %:email%")
List<User> findByEmailContaining(@Param("email") String email);
// 原生SQL查询
@Query(value = "SELECT * FROM users WHERE age > :age", nativeQuery = true)
List<User> findUsersByAgeGreaterThan(@Param("age") Integer age);
// 分页查询
Page<User> findByAgeGreaterThan(Integer age, Pageable pageable);
// 自定义更新
@Modifying
@Query("UPDATE User u SET u.age = :age WHERE u.id = :id")
int updateUserAge(@Param("id") Long id, @Param("age") Integer age);
}
Service层使用:
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public User createUser(User user) {
// 检查用户名是否已存在
if (userRepository.findByUsername(user.getUsername()).isPresent()) {
throw new RuntimeException("用户名已存在");
}
return userRepository.save(user);
}
public List<User> getUsersByAge(Integer age) {
return userRepository.findByAgeGreaterThan(age);
}
public Page<User> getUsersPage(int page, int size) {
Pageable pageable = PageRequest.of(page, size, Sort.by("id").descending());
return userRepository.findAll(pageable);
}
}
5.2 Spring Data MongoDB
对于NoSQL数据库,Spring Data MongoDB提供了类似的支持。
配置:
@Configuration
@EnableMongoRepositories(basePackages = "com.example.repository")
public class MongoConfig {
@Bean
public MongoTemplate mongoTemplate(MongoDatabaseFactory mongoDbFactory) {
return new MongoTemplate(mongoDbFactory);
}
}
文档实体:
@Document(collection = "products")
public class Product {
@Id
private String id;
@Field("name")
private String name;
@Field("price")
private BigDecimal price;
@Field("category")
private String category;
@Field("tags")
private List<String> tags;
// getters and setters
}
MongoDB Repository:
public interface ProductRepository extends MongoRepository<Product, String> {
List<Product> findByCategory(String category);
List<Product> findByPriceBetween(BigDecimal min, BigDecimal max);
@Query("{ 'tags': { $in: ?0 } }")
List<Product> findByTagsIn(List<String> tags);
}
第六部分:Spring Security安全框架
6.1 Spring Security基础
Spring Security提供了全面的安全解决方案,包括认证、授权、防护等。
基本配置:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf().disable() // 禁用CSRF(对于REST API)
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.httpBasic(Customizer.withDefaults()) // 基本认证
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 无状态
);
return http.build();
}
@Bean
public UserDetailsService userDetailsService() {
// 内存用户存储(生产环境应使用数据库)
UserDetails user = User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build();
UserDetails admin = User.withDefaultPasswordEncoder()
.username("admin")
.password("admin123")
.roles("ADMIN", "USER")
.build();
return new InMemoryUserDetailsManager(user, admin);
}
}
6.2 JWT认证
JWT工具类:
@Component
public class JwtTokenUtil {
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private Long expiration;
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
claims.put("roles", userDetails.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.toList()));
return Jwts.builder()
.setClaims(claims)
.setSubject(userDetails.getUsername())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + expiration * 1000))
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
public Boolean validateToken(String token, UserDetails userDetails) {
final String username = extractUsername(token);
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
}
public String extractUsername(String token) {
return extractClaim(token, Claims::getSubject);
}
public Date extractExpiration(String token) {
return extractClaim(token, Claims::getExpiration);
}
private <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
final Claims claims = extractAllClaims(token);
return claimsResolver.apply(claims);
}
private Claims extractAllClaims(String token) {
return Jwts.parserBuilder()
.setSigningKey(secret)
.build()
.parseClaimsJws(token)
.getBody();
}
private Boolean isTokenExpired(String token) {
return extractExpiration(token).before(new Date());
}
}
JWT认证过滤器:
@Component
public class JwtRequestFilter extends OncePerRequestFilter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
final String authorizationHeader = request.getHeader("Authorization");
String username = null;
String jwt = null;
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
jwt = authorizationHeader.substring(7);
username = jwtTokenUtil.extractUsername(jwt);
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
if (jwtTokenUtil.validateToken(jwt, userDetails)) {
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(
userDetails,
null,
userDetails.getAuthorities()
);
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
filterChain.doFilter(request, response);
}
}
认证控制器:
@RestController
@RequestMapping("/api/auth")
public class AuthController {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Autowired
private UserDetailsService userDetailsService;
@PostMapping("/login")
public ResponseEntity<?> createAuthenticationToken(@RequestBody AuthRequest authRequest) {
try {
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
authRequest.getUsername(),
authRequest.getPassword()
)
);
} catch (BadCredentialsException e) {
throw new RuntimeException("用户名或密码错误");
}
final UserDetails userDetails = userDetailsService
.loadUserByUsername(authRequest.getUsername());
final String jwt = jwtTokenUtil.generateToken(userDetails);
return ResponseEntity.ok(new AuthResponse(jwt));
}
}
第七部分:Spring测试
7.1 单元测试
使用JUnit 5和Mockito:
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository userRepository;
@Mock
private EmailService emailService;
@InjectMocks
private UserService userService;
@Test
void shouldCreateUserSuccessfully() {
// 准备测试数据
User user = new User();
user.setUsername("testuser");
user.setEmail("test@example.com");
// 模拟行为
when(userRepository.findByUsername("testuser")).thenReturn(Optional.empty());
when(userRepository.save(any(User.class))).thenReturn(user);
doNothing().when(emailService).sendWelcomeEmail(user.getEmail());
// 执行测试
User result = userService.createUser(user);
// 验证结果
assertNotNull(result);
assertEquals("testuser", result.getUsername());
verify(userRepository).save(user);
verify(emailService).sendWelcomeEmail(user.getEmail());
}
@Test
void shouldThrowExceptionWhenUsernameExists() {
User existingUser = new User();
existingUser.setUsername("testuser");
when(userRepository.findByUsername("testuser"))
.thenReturn(Optional.of(existingUser));
assertThrows(RuntimeException.class, () -> {
userService.createUser(new User());
});
}
}
7.2 集成测试
使用Spring Boot Test:
@SpringBootTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Transactional
class UserServiceIntegrationTest {
@Autowired
private UserService userService;
@Autowired
private UserRepository userRepository;
@Test
void shouldCreateUserInDatabase() {
User user = new User();
user.setUsername("integrationtest");
user.setEmail("integration@test.com");
User created = userService.createUser(user);
assertNotNull(created.getId());
// 验证数据库中的数据
Optional<User> found = userRepository.findById(created.getId());
assertTrue(found.isPresent());
assertEquals("integrationtest", found.get().getUsername());
}
}
7.3 Web层测试
使用MockMvc:
@WebMvcTest(UserController.class)
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Test
void shouldReturnUserWhenExists() throws Exception {
User user = new User();
user.setId(1L);
user.setUsername("testuser");
when(userService.getUserById(1L)).thenReturn(user);
mockMvc.perform(get("/api/users/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value(1))
.andExpect(jsonPath("$.username").value("testuser"));
}
@Test
void shouldReturn404WhenUserNotFound() throws Exception {
when(userService.getUserById(999L)).thenReturn(null);
mockMvc.perform(get("/api/users/999"))
.andExpect(status().isNotFound());
}
}
第八部分:Spring高级特性
8.1 Spring Cloud微服务
Spring Cloud为构建分布式系统提供了工具。
服务注册与发现(Eureka):
// Eureka Server
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
// Eureka Client
@SpringBootApplication
@EnableEurekaClient
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
}
配置中心(Config Server):
@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
API网关(Spring Cloud Gateway):
@SpringBootApplication
@EnableDiscoveryClient
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
// 路由配置
@Configuration
public class GatewayConfig {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("user-service", r -> r.path("/api/users/**")
.uri("lb://user-service"))
.route("order-service", r -> r.path("/api/orders/**")
.uri("lb://order-service"))
.build();
}
}
8.2 Spring Reactive编程
Spring WebFlux提供了响应式编程支持。
响应式Controller:
@RestController
@RequestMapping("/api/reactive")
public class ReactiveController {
@Autowired
private ReactiveUserRepository userRepository;
@GetMapping("/users")
public Flux<User> getAllUsers() {
return userRepository.findAll();
}
@GetMapping("/users/{id}")
public Mono<User> getUser(@PathVariable String id) {
return userRepository.findById(id)
.switchIfEmpty(Mono.error(new UserNotFoundException(id)));
}
@PostMapping("/users")
public Mono<User> createUser(@RequestBody User user) {
return userRepository.save(user);
}
@GetMapping("/users/stream")
public Flux<User> streamUsers() {
return userRepository.findAll()
.delayElements(Duration.ofMillis(100));
}
}
响应式Repository:
public interface ReactiveUserRepository extends ReactiveMongoRepository<User, String> {
Flux<User> findByAgeGreaterThan(Integer age);
@Query("{ 'username': ?0 }")
Mono<User> findByUsername(String username);
}
8.3 Spring批处理
Spring Batch提供了批量处理框架。
批处理配置:
@Configuration
@EnableBatchProcessing
public class BatchConfig {
@Autowired
private JobBuilderFactory jobBuilderFactory;
@Autowired
private StepBuilderFactory stepBuilderFactory;
@Bean
public Job importUserJob() {
return jobBuilderFactory.get("importUserJob")
.start(step1())
.build();
}
@Bean
public Step step1() {
return stepBuilderFactory.get("step1")
.<User, User>chunk(10)
.reader(reader())
.processor(processor())
.writer(writer())
.build();
}
@Bean
public FlatFileItemReader<User> reader() {
FlatFileItemReader<User> reader = new FlatFileItemReader<>();
reader.setResource(new ClassPathResource("users.csv"));
reader.setLineMapper(new DefaultLineMapper<User>() {{
setLineTokenizer(new DelimitedLineTokenizer() {{
setNames(new String[]{"username", "email", "age"});
}});
setFieldSetMapper(new BeanWrapperFieldSetMapper<User>() {{
setTargetType(User.class);
}});
}});
return reader;
}
@Bean
public ItemProcessor<User, User> processor() {
return user -> {
user.setEmail(user.getEmail().toLowerCase());
return user;
};
}
@Bean
public ItemWriter<User> writer() {
return users -> {
// 批量写入数据库
// ...
};
}
}
第九部分:Spring性能优化
9.1 连接池优化
HikariCP配置:
# application.properties
spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.idle-timeout=600000
spring.datasource.hikari.max-lifetime=1800000
spring.datasource.hikari.leak-detection-threshold=60000
自定义连接池:
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.hikari")
public HikariDataSource dataSource() {
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
dataSource.setUsername("root");
dataSource.setPassword("password");
dataSource.setMaximumPoolSize(20);
dataSource.setMinimumIdle(5);
dataSource.setConnectionTimeout(30000);
dataSource.setIdleTimeout(600000);
dataSource.setMaxLifetime(1800000);
dataSource.setLeakDetectionThreshold(60000);
return dataSource;
}
}
9.2 缓存优化
Spring Cache配置:
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager() {
RedisCacheManager cacheManager = RedisCacheManager.builder(redisConnectionFactory())
.cacheDefaults(RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(10))
.disableCachingNullValues())
.build();
return cacheManager;
}
@Bean
public RedisConnectionFactory redisConnectionFactory() {
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
config.setHostName("localhost");
config.setPort(6379);
return new JedisConnectionFactory(config);
}
}
缓存使用:
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Cacheable(value = "users", key = "#id")
public User getUserById(Long id) {
// 这个方法只在缓存未命中时执行
return userRepository.findById(id).orElse(null);
}
@CachePut(value = "users", key = "#user.id")
public User updateUser(User user) {
return userRepository.save(user);
}
@CacheEvict(value = "users", key = "#id")
public void deleteUser(Long id) {
userRepository.deleteById(id);
}
}
9.3 异步处理
启用异步支持:
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(25);
executor.setThreadNamePrefix("Async-");
executor.initialize();
return executor;
}
}
异步方法:
@Service
public class NotificationService {
@Async
public void sendEmail(String to, String subject, String body) {
// 模拟耗时操作
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("Email sent to: " + to);
}
@Async
@Transactional(propagation = Propagation.REQUIRES_NEW)
public CompletableFuture<Void> sendNotificationAsync(User user) {
// 异步且独立事务
return CompletableFuture.runAsync(() -> {
// 发送通知逻辑
});
}
}
第十部分:Spring项目实战
10.1 项目架构设计
分层架构:
Controller层(Web层)
↓
Service层(业务逻辑层)
↓
Repository层(数据访问层)
↓
Domain层(领域模型)
项目结构示例:
src/main/java/com/example/ecommerce/
├── config/ # 配置类
├── controller/ # 控制器
├── service/ # 业务服务
├── repository/ # 数据访问
├── entity/ # 实体类
├── dto/ # 数据传输对象
├── exception/ # 异常处理
├── security/ # 安全配置
└── util/ # 工具类
10.2 电商系统实战
用户模块:
// DTO
public class UserRegistrationDTO {
@NotBlank(message = "用户名不能为空")
@Size(min = 3, max = 20)
private String username;
@NotBlank(message = "密码不能为空")
@Size(min = 6)
private String password;
@NotBlank(message = "邮箱不能为空")
@Email
private String email;
// getters and setters
}
// Controller
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/register")
public ResponseEntity<?> register(@Valid @RequestBody UserRegistrationDTO dto) {
User user = userService.register(dto);
return ResponseEntity.status(HttpStatus.CREATED).body(user);
}
@GetMapping("/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
User user = userService.getUser(id);
return ResponseEntity.ok(user);
}
}
// Service
@Service
@Transactional
public class UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private PasswordEncoder passwordEncoder;
public User register(UserRegistrationDTO dto) {
if (userRepository.findByUsername(dto.getUsername()).isPresent()) {
throw new BusinessException("用户名已存在");
}
User user = new User();
user.setUsername(dto.getUsername());
user.setPassword(passwordEncoder.encode(dto.getPassword()));
user.setEmail(dto.getEmail());
user.setRole(Role.USER);
return userRepository.save(user);
}
public User getUser(Long id) {
return userRepository.findById(id)
.orElseThrow(() -> new UserNotFoundException(id));
}
}
商品模块:
// 实体类
@Entity
@Table(name = "products")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String name;
@Column(nullable = false)
private BigDecimal price;
@Column
private String description;
@Column
private Integer stock;
@Column
private String category;
@Column
private Boolean active = true;
// getters and setters
}
// Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
List<Product> findByCategoryAndActiveTrue(String category);
Page<Product> findByPriceBetweenAndActiveTrue(BigDecimal min, BigDecimal max, Pageable pageable);
@Query("SELECT p FROM Product p WHERE p.name LIKE %:keyword% AND p.active = true")
List<Product> searchProducts(@Param("keyword") String keyword);
}
订单模块:
@Entity
@Table(name = "orders")
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private List<OrderItem> items = new ArrayList<>();
@Column(nullable = false)
private BigDecimal totalAmount;
@Enumerated(EnumType.STRING)
private OrderStatus status;
@Column
private String shippingAddress;
@Column
private LocalDateTime orderDate;
// getters and setters
}
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
@Autowired
private ProductRepository productRepository;
@Autowired
private UserService userService;
@Transactional
public Order createOrder(Long userId, List<OrderItemDTO> items) {
User user = userService.getUser(userId);
Order order = new Order();
order.setUser(user);
order.setOrderDate(LocalDateTime.now());
order.setStatus(OrderStatus.PENDING);
BigDecimal total = BigDecimal.ZERO;
for (OrderItemDTO itemDTO : items) {
Product product = productRepository.findById(itemDTO.getProductId())
.orElseThrow(() -> new ProductNotFoundException(itemDTO.getProductId()));
if (product.getStock() < itemDTO.getQuantity()) {
throw new InsufficientStockException(product.getName());
}
OrderItem item = new OrderItem();
item.setOrder(order);
item.setProduct(product);
item.setQuantity(itemDTO.getQuantity());
item.setUnitPrice(product.getPrice());
order.getItems().add(item);
// 更新库存
product.setStock(product.getStock() - itemDTO.getQuantity());
productRepository.save(product);
total = total.add(product.getPrice().multiply(new BigDecimal(itemDTO.getQuantity())));
}
order.setTotalAmount(total);
return orderRepository.save(order);
}
}
10.3 部署与监控
Docker部署:
# Dockerfile
FROM openjdk:17-jdk-slim
WORKDIR /app
# 复制JAR文件
COPY target/ecommerce-app.jar app.jar
# 暴露端口
EXPOSE 8080
# 运行应用
ENTRYPOINT ["java", "-jar", "app.jar"]
Spring Boot Actuator监控:
# application.properties
management.endpoints.web.exposure.include=health,info,metrics
management.endpoint.health.show-details=always
management.metrics.export.prometheus.enabled=true
自定义健康检查:
@Component
public class DatabaseHealthIndicator implements HealthIndicator {
@Autowired
private DataSource dataSource;
@Override
public Health health() {
try (Connection connection = dataSource.getConnection()) {
return Health.up()
.withDetail("database", "MySQL")
.withDetail("version", connection.getMetaData().getDatabaseProductVersion())
.build();
} catch (Exception e) {
return Health.down()
.withDetail("error", e.getMessage())
.build();
}
}
}
第十一部分:最佳实践与常见问题
11.1 代码组织最佳实践
1. 遵循单一职责原则
// 不好的做法 - 一个类做太多事情
@Service
public class OrderService {
public void createOrder() { /* ... */ }
public void sendEmail() { /* ... */ }
public void updateInventory() { /* ... */ }
}
// 好的做法 - 拆分职责
@Service
public class OrderService {
@Autowired
private EmailService emailService;
@Autowired
private InventoryService inventoryService;
public void createOrder() {
// 只负责订单创建
}
}
2. 使用DTO避免暴露实体
// 实体类
@Entity
public class User {
@Id
private Long id;
private String username;
private String password; // 不应该暴露
private String email;
private String phone;
private String address;
}
// DTO类
public class UserDTO {
private Long id;
private String username;
private String email;
private String phone;
private String address;
// 不包含password字段
}
11.2 常见问题与解决方案
问题1:循环依赖
// 错误示例
@Service
public class ServiceA {
@Autowired
private ServiceB serviceB;
}
@Service
public class ServiceB {
@Autowired
private ServiceA serviceA;
}
// 解决方案1:使用Setter注入
@Service
public class ServiceA {
private ServiceB serviceB;
@Autowired
public void setServiceB(ServiceB serviceB) {
this.serviceB = serviceB;
}
}
// 解决方案2:重构设计
@Service
public class ServiceA {
@Autowired
private CommonService commonService;
}
@Service
public class ServiceB {
@Autowired
private CommonService commonService;
}
问题2:事务失效
// 错误示例 - 自调用导致事务失效
@Service
public class OrderService {
@Transactional
public void createOrder() {
// 事务开始
saveOrder();
// 事务结束
}
// 自调用,事务不生效
private void saveOrder() {
// 数据库操作
}
}
// 解决方案1:提取到另一个Bean
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
@Transactional
public void createOrder() {
orderRepository.save(new Order());
}
}
// 解决方案2:使用AopContext.currentProxy()
@Service
public class OrderService {
@Transactional
public void createOrder() {
saveOrder();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveOrder() {
// 独立事务
}
}
问题3:N+1查询问题
// 错误示例 - 导致N+1查询
@Entity
public class User {
@OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
private List<Order> orders;
}
// 查询用户时,每个用户的订单都会单独查询
List<User> users = userRepository.findAll();
for (User user : users) {
user.getOrders().size(); // 触发N+1查询
}
// 解决方案1:使用JOIN FETCH
@Query("SELECT u FROM User u JOIN FETCH u.orders WHERE u.id = :id")
User findUserWithOrders(@Param("id") Long id);
// 解决方案2:使用EntityGraph
@EntityGraph(attributePaths = {"orders"})
User findById(Long id);
// 解决方案3:批量查询
List<User> users = userRepository.findAll();
List<Long> userIds = users.stream().map(User::getId).collect(Collectors.toList());
List<Order> orders = orderRepository.findByUserIdIn(userIds);
Map<Long, List<Order>> ordersByUserId = orders.stream()
.collect(Collectors.groupingBy(Order::getUserId));
users.forEach(user -> user.setOrders(ordersByUserId.getOrDefault(user.getId(), Collections.emptyList())));
11.3 性能调优技巧
1. 数据库查询优化
// 使用索引
@Entity
@Table(indexes = {
@Index(name = "idx_username", columnList = "username"),
@Index(name = "idx_email", columnList = "email")
})
public class User {
// ...
}
// 使用分页
Page<User> users = userRepository.findAll(PageRequest.of(0, 20));
// 使用投影
public interface UserProjection {
String getUsername();
String getEmail();
}
@Query("SELECT u.username as username, u.email as email FROM User u")
List<UserProjection> findUserProjections();
2. 缓存策略
// 多级缓存
@Service
public class ProductService {
@Autowired
private ProductRepository productRepository;
@Autowired
private RedisTemplate<String, Product> redisTemplate;
@Cacheable(value = "products", key = "#id")
public Product getProduct(Long id) {
// 先查Redis缓存
String key = "product:" + id;
Product product = redisTemplate.opsForValue().get(key);
if (product != null) {
return product;
}
// 再查数据库
product = productRepository.findById(id).orElse(null);
if (product != null) {
// 写入Redis
redisTemplate.opsForValue().set(key, product, Duration.ofMinutes(10));
}
return product;
}
}
第十二部分:学习资源与进阶路线
12.1 推荐学习资源
官方文档:
- Spring Framework官方文档:https://spring.io/projects/spring-framework
- Spring Boot官方文档:https://spring.io/projects/spring-boot
- Spring Security官方文档:https://spring.io/projects/spring-security
书籍推荐:
- 《Spring实战》(Craig Walls著)
- 《Spring Boot实战》(Craig Walls著)
- 《Spring Cloud微服务实战》(翟永超著)
- 《Spring Data JPA实战》
在线课程:
- Spring官方教程
- Udemy Spring Boot课程
- Coursera Spring框架课程
12.2 进阶学习路线
阶段1:基础掌握(1-2个月)
- Spring Core(IoC、DI、AOP)
- Spring Boot基础
- Spring MVC
- Spring Data JPA
阶段2:进阶应用(2-3个月)
- Spring Security
- Spring事务管理
- Spring测试
- RESTful API设计
阶段3:高级特性(3-4个月)
- Spring Cloud微服务
- Spring WebFlux响应式编程
- Spring Batch批处理
- Spring集成
阶段4:实战项目(持续)
- 参与开源项目
- 构建个人项目
- 学习性能优化
- 掌握部署运维
12.3 社区与交流
活跃社区:
- Spring官方论坛
- Stack Overflow Spring标签
- GitHub Spring项目
- Reddit r/springframework
技术博客:
- Spring官方博客
- Baeldung Spring教程
- Spring Boot中文社区
- 各大技术平台Spring专栏
结语
Spring框架的学习是一个循序渐进的过程,从基础概念到高级特性,再到实战应用,每一步都需要扎实的理论基础和大量的实践。本指南涵盖了Spring开发的各个方面,但真正的精通来自于不断的实践和项目经验。
建议的学习路径:
- 从Spring Boot开始,快速搭建第一个应用
- 深入理解Spring Core的核心概念
- 逐步学习Spring Data、Spring Security等模块
- 通过实际项目巩固知识
- 持续关注Spring生态的发展
记住,框架只是工具,真正的价值在于你如何使用它来解决实际问题。保持好奇心,持续学习,你一定能成为Spring领域的专家!
最后,祝你学习顺利,编码愉快!
