引言:为什么选择Spring框架?
Spring框架是Java生态系统中最流行的企业级应用开发框架,自2003年诞生以来,已经成为Java开发者的必备技能。Spring的核心优势在于其”轻量级”和”非侵入式”的设计理念,通过依赖注入(DI)和面向切面编程(AOP)解决了企业级开发中的复杂性问题。
在实际开发中,Spring不仅仅是一个框架,更是一个完整的生态系统。从基础的IoC容器到Spring Boot的自动化配置,再到Spring Cloud的微服务解决方案,Spring为开发者提供了一站式的开发体验。根据2023年的调查,超过85%的Java企业应用都在使用Spring框架,这充分证明了其在行业中的地位。
第一部分:Spring核心概念详解
1.1 控制反转(IoC)与依赖注入(DI)
控制反转是Spring框架的基石。传统的编程方式中,对象主动创建和管理其依赖的对象;而在IoC模式下,这个过程被反转了,由容器来负责对象的创建和管理。
生活中的类比:想象你去餐厅吃饭。传统方式是你自己买菜、洗菜、做饭(主动创建依赖);而IoC方式是你去餐厅,告诉服务员你想吃什么,厨房(容器)会为你准备好一切(被动接收依赖)。
代码示例:手动创建对象 vs Spring IoC
// 传统方式:手动创建依赖
public class UserService {
private UserRepository userRepository;
public UserService() {
// 紧耦合,难以测试和维护
this.userRepository = new UserRepositoryImpl();
}
}
// Spring方式:依赖注入
public class UserService {
private final UserRepository userRepository;
// 构造器注入,松耦合
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
配置示例:XML配置方式
<!-- beans.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="userRepository" class="com.example UserRepositoryImpl"/>
<!-- 注入依赖 -->
<bean id="userService" class="com.example UserService">
<property name="userRepository" ref="userRepository"/>
</bean>
</beans>
配置示例:注解配置方式(推荐)
// 定义Bean
@Repository
public class UserRepositoryImpl implements UserRepository {
// 实现细节
}
// 注入依赖
@Service
public class UserService {
private final UserRepository userRepository;
@Autowired
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
1.2 面向切面编程(AOP)
AOP允许开发者在不修改业务代码的情况下,为程序添加横切关注点(如日志、事务、安全等)。这解决了OOP中代码重复的问题。
实际应用场景:在所有Service方法执行前后记录日志,或者为所有写操作添加事务管理。
代码示例:自定义日志切面
@Aspect
@Component
public class LoggingAspect {
// 定义切点:所有Service层的方法
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceMethods() {}
// 前置通知
@Before("serviceMethods()")
public void logBefore(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
System.out.println("执行方法: " + methodName + ",参数: " + Arrays.toString(joinPoint.getArgs()));
}
// 后置通知
@After("serviceMethods()")
public void logAfter(JoinPoint joinPoint) {
System.out.println("方法执行完成: " + joinPoint.getSignature().getName());
}
// 环绕通知(最强大)
@Around("serviceMethods()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object result = joinPoint.proceed(); // 执行原方法
long duration = System.currentTimeMillis() - start;
System.out.println("方法执行耗时: " + duration + "ms");
return result;
}
}
启用AOP的配置
@Configuration
@EnableAspectJAutoProxy
@ComponentScan("com.example")
public class AppConfig {
// 配置类
}
1.3 Bean的生命周期管理
Spring容器管理Bean的完整生命周期,包括实例化、初始化、使用和销毁四个阶段。
代码示例:自定义生命周期管理
@Component
public class CustomBean implements InitializingBean, DisposableBean {
public CustomBean() {
System.out.println("1. 构造器被调用");
}
@PostConstruct
public void postConstruct() {
System.out.println("2. @PostConstruct被调用");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("3. afterPropertiesSet被调用");
}
@PreDestroy
public void preDestroy() {
System.out.println("4. @PreDestroy被调用");
}
@Override
public void destroy() throws Exception {
System.out.println("5. destroy被调用");
}
}
第二部分:Spring Boot快速入门
2.1 Spring Boot的核心优势
Spring Boot通过”约定优于配置”的理念,极大地简化了Spring应用的开发过程。它内置了Tomcat、Jetty等Web服务器,提供了自动配置功能,让开发者可以专注于业务逻辑。
创建第一个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 // 控制器
│ │ │ └── service/
│ │ └── resources/
│ │ ├── application.properties // 配置文件
│ │ └── static/ // 静态资源
│ └── test/
└── pom.xml // Maven配置
启动类代码
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
// 这一行代码启动了整个Spring Boot应用
// 包括:初始化Spring容器、启动内嵌Web服务器、加载自动配置
SpringApplication.run(DemoApplication.class, args);
}
}
2.2 RESTful API开发实战
创建一个完整的用户管理API
// 实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Long id;
private String name;
private String email;
}
// Controller层
@RestController
@RequestMapping("/api/users")
public class UserController {
private final Map<Long, User> userStore = new ConcurrentHashMap<>();
private AtomicLong idGenerator = new AtomicLong(1);
// 创建用户
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) {
user.setId(idGenerator.getAndIncrement());
userStore.put(user.getId(), user);
return ResponseEntity.status(HttpStatus.CREATED).body(user);
}
// 获取用户
@GetMapping("/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
User user = userStore.get(id);
if (user == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(user);
}
// 更新用户
@PutMapping("/{id}")
public ResponseEntity<User> updateUser(@PathVariable Long id, @RequestBody User user) {
if (!userStore.containsKey(id)) {
return ResponseEntity.notFound().build();
}
user.setId(id);
userStore.put(id, user);
return ResponseEntity.ok(user);
}
// 删除用户
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
userStore.remove(id);
return ResponseEntity.noContent().build();
}
// 获取所有用户
@GetMapping
public ResponseEntity<List<User>> getAllUsers() {
return ResponseEntity.ok(new ArrayList<>(userStore.values()));
}
}
测试API的HTTP请求示例
# 创建用户
curl -X POST http://localhost:8080/api/users \
-H "Content-Type: application/json" \
-d '{"name":"张三","email":"zhangsan@example.com"}'
# 获取用户
curl http://localhost:8080/api/users/1
# 获取所有用户
curl http://localhost:8080/api/users
2.3 配置管理详解
application.properties vs application.yml
properties格式:
# application.properties
server.port=8080
spring.application.name=my-app
spring.datasource.url=jdbc:mysql://localhost:3306/mydb
spring.datasource.username=root
spring.datasource.password=secret
logging.level.com.example=DEBUG
yml格式(推荐):
# application.yml
server:
port: 8080
spring:
application:
name: my-app
datasource:
url: jdbc:mysql://localhost:3306/mydb
username: root
password: secret
logging:
level:
com.example: DEBUG
多环境配置
创建多个配置文件:
application-dev.yml- 开发环境application-test.yml- 测试环境application-prod.yml- 生产环境
在主配置文件中激活环境:
# application.yml
spring:
profiles:
active: dev # 激活开发环境
自定义配置与@Value注解
# application.yml
app:
name: My Application
version: 1.0.0
features:
- logging
- monitoring
- caching
@Component
public class AppConfig {
@Value("${app.name}")
private String appName;
@Value("${app.version}")
private String appVersion;
@Value("${app.features[0]}")
private String firstFeature;
// 使用配置类(更推荐)
@ConfigurationProperties(prefix = "app")
@Component
@Data
public static class AppProperties {
private String name;
private String version;
private List<String> features;
}
}
第三部分:Spring Data JPA与数据库操作
3.1 JPA基础概念
JPA(Java Persistence API)是Java持久化规范,Spring Data JPA在其基础上提供了更便捷的Repository抽象。
实体类定义
@Entity
@Table(name = "users")
@Data
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, length = 50)
private String name;
@Column(unique = true, nullable = false)
private String email;
@Column(name = "created_at")
private LocalDateTime createdAt;
@PrePersist
public void prePersist() {
this.createdAt = LocalDateTime.now();
}
}
3.2 Repository接口
Spring Data JPA通过Repository接口提供了CRUD操作的自动实现。
基础Repository接口
// 继承JpaRepository获得完整CRUD功能
public interface UserRepository extends JpaRepository<User, Long> {
// Spring Data JPA会根据方法名自动生成查询
List<User> findByName(String name);
// 支持And、Or等关键词
List<User> findByNameAndEmail(String name, String email);
// 支持Like查询
List<User> findByNameContaining(String keyword);
// 支持排序
List<User> findByOrderByNameAsc();
// 分页查询
Page<User> findByEmailContaining(String email, Pageable pageable);
// 自定义JPQL查询
@Query("SELECT u FROM User u WHERE u.email LIKE %:keyword%")
List<User> searchByEmail(@Param("keyword") String keyword);
// 原生SQL查询
@Query(value = "SELECT * FROM users WHERE name = :name", nativeQuery = true)
List<User> findByNameNative(@Param("name") String name);
// 自定义更新操作
@Modifying
@Query("UPDATE User u SET u.email = :email WHERE u.id = :id")
int updateEmail(@Param("id") Long id, @Param("email") String email);
}
3.3 Service层实现
@Service
@Transactional
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
// 创建用户
public User createUser(String name, String email) {
// 业务验证
if (userRepository.findByEmail(email).isPresent()) {
throw new RuntimeException("邮箱已存在");
}
User user = new User();
user.setName(name);
user.setEmail(email);
return userRepository.save(user);
}
// 复杂查询示例
public List<User> searchUsers(String keyword, int page, int size) {
Pageable pageable = PageRequest.of(page, size, Sort.by("name").ascending());
return userRepository.findByEmailContaining(keyword, pageable).getContent();
}
// 批量操作
@Transactional
public void batchCreateUsers(List<User> users) {
// 事务确保要么全部成功,要么全部失败
userRepository.saveAll(users);
}
}
第四部分:Spring事务管理
4.1 事务基础概念
事务是数据库操作的原子性单位,确保一组操作要么全部成功,要么全部失败。
代码示例:事务传播行为
@Service
public class OrderService {
private final OrderRepository orderRepository;
private final InventoryService inventoryService;
@Transactional(propagation = Propagation.REQUIRED)
public void createOrder(Order order) {
// 1. 创建订单
orderRepository.save(order);
// 2. 扣减库存(如果失败,整个事务回滚)
inventoryService.decreaseStock(order.getProductId(), order.getQuantity());
}
}
@Service
public class InventoryService {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void decreaseStock(Long productId, int quantity) {
// 独立事务,即使外层事务回滚,这个操作也会提交
// 实现细节...
}
}
4.2 事务传播行为详解
| 传播行为 | 说明 | 使用场景 |
|---|---|---|
| REQUIRED | 如果存在事务则加入,否则新建 | 默认行为,大多数情况 |
| REQUIRES_NEW | 总是新建事务,挂起当前事务 | 需要独立提交的日志记录 |
| NESTED | 嵌套事务(如果支持) | 部分回滚场景 |
| MANDATORY | 必须存在事务,否则抛异常 | 强制要求事务环境 |
4.3 事务失效的常见场景
场景1:方法不是public
// ❌ 事务失效
@Transactional
private void privateMethod() {
// ...
}
// ✅ 正确
@Transactional
public void publicMethod() {
// ...
}
场景2:同类内部调用
@Service
public class UserService {
public void methodA() {
methodB(); // 这里不会触发事务,因为是内部调用
}
@Transactional
public void methodB() {
// ...
}
}
解决方案:
@Service
public class UserService {
@Autowired
private UserService self; // 注入自己
public void methodA() {
self.methodB(); // 通过代理对象调用
}
@Transactional
public void methodB() {
// ...
}
}
场景3:异常类型不匹配
// ❌ 事务失效(默认只回滚RuntimeException)
@Transactional
public void method() {
try {
// 业务逻辑
throw new Exception("检查异常"); // 不会回滚
} catch (Exception e) {
// 捕获后未抛出
}
}
// ✅ 正确配置
@Transactional(rollbackFor = Exception.class)
public void method() {
throw new Exception("检查异常"); // 会回滚
}
第五部分:Spring Security安全框架
5.1 认证与授权基础
Spring Security提供了强大的认证和授权功能。
基础配置示例
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// 启用CSRF保护
.csrf().disable()
// 授权配置
.authorizeHttpRequests(authz -> authz
// 允许匿名访问的路径
.requestMatchers("/api/public/**", "/login").permitAll()
// 需要ADMIN角色的路径
.requestMatchers("/api/admin/**").hasRole("ADMIN")
// 需要认证的路径
.anyRequest().authenticated()
)
// 表单登录配置
.formLogin(form -> form
.loginPage("/login")
.defaultSuccessUrl("/home")
.permitAll()
)
// 登出配置
.logout(logout -> logout
.logoutUrl("/logout")
.logoutSuccessUrl("/login?logout")
);
return http.build();
}
// 用户详情服务
@Bean
public UserDetailsService userDetailsService() {
// 内存用户存储(生产环境应使用数据库)
UserDetails user = User.builder()
.username("user")
.password("{noop}password") // {noop}表示明文密码
.roles("USER")
.build();
UserDetails admin = User.builder()
.username("admin")
.password("{noop}admin123")
.roles("ADMIN", "USER")
.build();
return new InMemoryUserDetailsManager(user, admin);
}
// 密码编码器
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
5.2 JWT认证实现
JWT工具类
@Component
public class JwtTokenUtil {
private static final String SECRET_KEY = "your-secret-key-min-256-bits";
private static final long EXPIRATION_TIME = 86400000; // 24小时
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_TIME))
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.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 getClaimsFromToken(token).getSubject();
}
private Claims getClaimsFromToken(String token) {
return Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody();
}
private Boolean isTokenExpired(String token) {
return getClaimsFromToken(token).getExpiration().before(new Date());
}
}
JWT认证过滤器
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
final String authHeader = request.getHeader("Authorization");
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
filterChain.doFilter(request, response);
return;
}
final String jwt = authHeader.substring(7);
final String username = jwtTokenUtil.extractUsername(jwt);
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = 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);
}
}
第六部分:Spring Boot高级特性
6.1 自动配置原理
Spring Boot的自动配置基于”条件化配置”理念,通过@Conditional注解实现。
自定义Starter开发
// 1. 自定义配置属性
@ConfigurationProperties(prefix = "mystarter")
public class MyStarterProperties {
private boolean enabled = true;
private String message = "Hello";
// getters and setters
}
// 2. 自动配置类
@Configuration
@ConditionalOnClass(MyService.class)
@ConditionalOnProperty(prefix = "mystarter", name = "enabled", havingValue = "true", matchIfMissing = true)
@EnableConfigurationProperties(MyStarterProperties.class)
public class MyStarterAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public MyService myService(MyStarterProperties properties) {
return new MyService(properties.getMessage());
}
}
// 3. 在META-INF/spring.factories中注册
# spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.mystarter.MyStarterAutoConfiguration
6.2 Actuator监控
配置Actuator端点
# application.yml
management:
endpoints:
web:
exposure:
include: health,info,metrics,env
endpoint:
health:
show-details: always
info:
env:
enabled: true
自定义健康检查
@Component
public class DatabaseHealthIndicator implements HealthIndicator {
@Autowired
private DataSource dataSource;
@Override
public Health health() {
try (Connection connection = dataSource.getConnection()) {
// 执行简单查询验证连接
connection.createStatement().execute("SELECT 1");
return Health.up()
.withDetail("database", "MySQL")
.withDetail("status", "connected")
.build();
} catch (Exception e) {
return Health.down()
.withDetail("error", e.getMessage())
.build();
}
}
}
6.3 异步处理与事件驱动
异步方法配置
@Configuration
@EnableAsync
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;
}
}
@Service
public class EmailService {
@Async
public void sendEmail(String to, String subject, String content) {
// 模拟耗时操作
try {
Thread.sleep(5000);
System.out.println("邮件已发送到: " + to);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
事件监听机制
// 自定义事件
public class UserRegisteredEvent extends ApplicationEvent {
private final User user;
public UserRegisteredEvent(Object source, User user) {
super(source);
this.user = user;
}
public User getUser() { return user; }
}
// 事件发布者
@Service
public class RegistrationService {
@Autowired
private ApplicationEventPublisher eventPublisher;
public void registerUser(User user) {
// 保存用户
// ...
// 发布事件
eventPublisher.publishEvent(new UserRegisteredEvent(this, user));
}
}
// 事件监听者
@Component
public class UserRegistrationListener {
@EventListener
public void handleUserRegistration(UserRegisteredEvent event) {
System.out.println("新用户注册: " + event.getUser().getName());
// 发送欢迎邮件、记录日志等...
}
@EventListener
@Async
public void asyncHandleRegistration(UserRegisteredEvent event) {
// 异步处理
System.out.println("异步处理用户注册事件");
}
}
第七部分:Spring Cloud微服务架构
7.1 服务注册与发现(Eureka)
Eureka Server配置
// 启动类
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
// application.yml
server:
port: 8761
eureka:
client:
register-with-eureka: false # 不注册自己
fetch-registry: false # 不获取注册表
service-url:
defaultZone: http://localhost:8761/eureka/
server:
enable-self-preservation: false # 关闭自我保护模式(开发环境)
Eureka Client配置
@SpringBootApplication
@EnableEurekaClient
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
}
# application.yml
server:
port: 8081
spring:
application:
name: user-service
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true # 使用IP注册
7.2 服务调用(OpenFeign)
Feign客户端定义
// 声明式HTTP客户端
@FeignClient(name = "user-service", fallback = UserServiceFallback.class)
public interface UserServiceClient {
@GetMapping("/api/users/{id}")
User getUser(@PathVariable("id") Long id);
@PostMapping("/api/users")
User createUser(@RequestBody User user);
@GetMapping("/api/users/search")
List<User> searchUsers(@RequestParam("keyword") String keyword);
}
// 降级处理
@Component
public class UserServiceFallback implements UserServiceClient {
@Override
public User getUser(Long id) {
// 返回默认值或缓存值
return new User(id, "默认用户", "default@example.com");
}
@Override
public User createUser(User user) {
throw new RuntimeException("服务暂时不可用");
}
@Override
public List<User> searchUsers(String keyword) {
return Collections.emptyList();
}
}
// 使用Feign客户端
@Service
public class OrderService {
@Autowired
private UserServiceClient userServiceClient;
public Order createOrder(Long userId, Order order) {
// 远程调用用户服务
User user = userServiceClient.getUser(userId);
if (user == null) {
throw new RuntimeException("用户不存在");
}
// 创建订单逻辑
// ...
return order;
}
}
7.3 配置中心(Config Server)
Config Server配置
@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
# application.yml
server:
port: 8888
spring:
cloud:
config:
server:
git:
uri: https://github.com/your-org/config-repo
search-paths: '{application}'
username: your-username
password: your-password
default-label: main
Config Client配置
# bootstrap.yml (优先于application.yml加载)
spring:
application:
name: user-service
cloud:
config:
uri: http://localhost:8888
profile: dev
label: main
第八部分:常见问题与解决方案
8.1 循环依赖问题
问题描述
两个或多个Bean相互依赖,形成闭环。
// ❌ 循环依赖示例
@Service
public class ServiceA {
@Autowired
private ServiceB serviceB;
}
@Service
public class ServiceB {
@Autowired
private ServiceA serviceA;
}
解决方案
方案1:使用@Lazy延迟注入
@Service
public class ServiceA {
@Autowired
@Lazy
private ServiceB serviceB;
}
方案2:使用Setter注入
@Service
public class ServiceA {
private ServiceB serviceB;
@Autowired
public void setServiceB(ServiceB serviceB) {
this.serviceB = serviceB;
}
}
方案3:重构设计(推荐)
// 提取公共逻辑到第三个Service
@Service
public class CommonService {
// 公共方法
}
@Service
public class ServiceA {
@Autowired
private CommonService commonService;
}
@Service
public class ServiceB {
@Autowired
private CommonService commonService;
}
8.2 数据库连接池问题
常见配置与优化
# HikariCP配置(Spring Boot默认)
spring:
datasource:
hikari:
# 连接池名称
pool-name: MyHikariCP
# 最小空闲连接
minimum-idle: 5
# 最大连接数
maximum-pool-size: 20
# 连接超时时间(毫秒)
connection-timeout: 30000
# 连接最大生命周期(毫秒)
max-lifetime: 1800000
# 空闲连接存活时间(毫秒)
idle-timeout: 600000
# 连接测试查询
connection-test-query: SELECT 1
连接泄漏检测
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties("spring.datasource.hikari")
public DataSource dataSource() {
HikariDataSource dataSource = new HikariDataSource();
// 启用泄漏检测
dataSource.setLeakDetectionThreshold(60000); // 60秒
return dataSource;
}
}
8.3 性能调优技巧
1. N+1查询问题
问题代码:
// ❌ N+1问题:先查询所有用户,然后为每个用户查询订单
List<User> users = userRepository.findAll();
for (User user : users) {
// 每次循环都会产生一次SQL查询
user.getOrders().size(); // 触发懒加载
}
解决方案:
// ✅ 使用JOIN FETCH一次性加载
@Query("SELECT DISTINCT u FROM User u LEFT JOIN FETCH u.orders")
List<User> findAllWithOrders();
// 或者使用EntityGraph
@EntityGraph(attributePaths = {"orders"})
List<User> findAll();
2. 批量操作优化
// ❌ 逐条插入,性能差
for (User user : users) {
userRepository.save(user);
}
// ✅ 批量插入
userRepository.saveAll(users);
// ✅ JPA批量更新
@Modifying
@Query("UPDATE User u SET u.status = :status WHERE u.id IN :ids")
void batchUpdateStatus(@Param("ids") List<Long> ids, @Param("status") String status);
// ✅ 使用原生JDBC批量操作
@Autowired
private JdbcTemplate jdbcTemplate;
public void batchInsert(List<User> users) {
String sql = "INSERT INTO users (name, email) VALUES (?, ?)";
jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
User user = users.get(i);
ps.setString(1, user.getName());
ps.setString(2, user.getEmail());
}
@Override
public int getBatchSize() {
return users.size();
}
});
}
3. 缓存优化
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager() {
// 使用Caffeine缓存
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCaffeine(Caffeine.newBuilder()
.initialCapacity(100)
.maximumSize(1000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.recordStats()); // 启用统计
return cacheManager;
}
}
@Service
public class UserService {
@Cacheable(value = "users", key = "#id")
public User getUserById(Long id) {
// 只有第一次会执行这里,后续从缓存获取
return userRepository.findById(id).orElse(null);
}
@CacheEvict(value = "users", key = "#id")
public void updateUser(Long id, User user) {
// 更新后清除缓存
userRepository.save(user);
}
@CachePut(value = "users", key = "#user.id")
public User saveUser(User user) {
// 更新缓存
return userRepository.save(user);
}
}
8.4 异常处理最佳实践
全局异常处理器
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public ErrorResponse handleResourceNotFound(ResourceNotFoundException ex) {
return new ErrorResponse("RESOURCE_NOT_FOUND", ex.getMessage());
}
@ExceptionHandler(ValidationException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ErrorResponse handleValidationException(ValidationException ex) {
return new ErrorResponse("VALIDATION_ERROR", ex.getMessage());
}
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ErrorResponse handleGenericException(Exception ex) {
// 记录日志
log.error("Unexpected error", ex);
return new ErrorResponse("INTERNAL_ERROR", "系统内部错误");
}
}
// 自定义异常
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException(String message) {
super(message);
}
}
// 错误响应格式
@Data
@AllArgsConstructor
public class ErrorResponse {
private String code;
private String message;
}
8.5 测试策略
单元测试示例
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository userRepository;
@InjectMocks
private UserService userService;
@Test
void shouldCreateUserSuccessfully() {
// Given
User user = new User(null, "张三", "zhangsan@example.com");
when(userRepository.save(any(User.class))).thenReturn(user);
// When
User result = userService.createUser("张三", "zhangsan@example.com");
// Then
assertNotNull(result.getId());
assertEquals("张三", result.getName());
verify(userRepository, times(1)).save(any(User.class));
}
@Test
void shouldThrowExceptionWhenEmailExists() {
// Given
when(userRepository.findByEmail("zhangsan@example.com"))
.thenReturn(Optional.of(new User()));
// When & Then
assertThrows(RuntimeException.class, () -> {
userService.createUser("张三", "zhangsan@example.com");
});
}
}
集成测试示例
@SpringBootTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Transactional
class UserServiceIntegrationTest {
@Autowired
private UserService userService;
@Autowired
private UserRepository userRepository;
@Test
void shouldCreateUserInDatabase() {
// When
User user = userService.createUser("李四", "lisi@example.com");
// Then
User savedUser = userRepository.findById(user.getId()).orElse(null);
assertNotNull(savedUser);
assertEquals("李四", savedUser.getName());
}
}
第九部分:Spring学习路线图
9.1 初级阶段(1-2个月)
核心概念掌握
- IoC容器和Bean生命周期
- 依赖注入的三种方式
- AOP原理与应用
- Spring Boot基础
Web开发
- RESTful API设计
- 请求参数校验
- 全局异常处理
- 文件上传下载
数据持久化
- JPA基础
- Repository使用
- 事务管理
- 连接池配置
9.2 中级阶段(3-6个月)
高级特性
- 自定义Starter开发
- 自动配置原理
- Actuator监控
- 异步处理与事件
安全框架
- Spring Security基础
- JWT认证
- OAuth2.0
- 方法级安全
测试
- 单元测试(JUnit + Mockito)
- 集成测试
- 测试覆盖率
- Testcontainers
9.3 高级阶段(6个月以上)
微服务架构
- Spring Cloud全家桶
- 服务注册与发现
- 配置中心
- 熔断降级(Hystrix/Resilience4j)
性能优化
- JVM调优
- 数据库优化
- 缓存策略
- 并发编程
DevOps
- Docker容器化
- Kubernetes部署
- CI/CD流水线
- 监控与日志(ELK)
第十部分:总结与建议
学习建议
理论结合实践:不要只看文档,一定要动手写代码。建议从一个完整的项目开始,比如博客系统、电商后台等。
阅读源码:Spring的源码设计非常精妙,阅读源码能让你深入理解框架原理。可以从
ApplicationContext的实现开始。关注社区:Spring生态更新很快,关注官方博客、GitHub仓库和Stack Overflow上的问题。
构建知识体系:将零散的知识点串联起来,形成完整的知识体系。比如理解IoC如何支撑AOP,AOP如何实现事务管理。
重视测试:良好的测试习惯能让你在重构和升级时更有信心。
常用资源推荐
- 官方文档:spring.io/projects/spring-framework
- Spring Boot文档:spring.io/projects/spring-boot
- Spring Cloud文档:spring.io/projects/spring-cloud
- Stack Overflow:Spring相关问题的宝库
- GitHub:关注spring-projects组织
最后的忠告
Spring框架虽然强大,但不要过度使用。记住”简单就是美”的原则,只有在真正需要时才引入新的复杂性。一个优秀的Spring开发者不仅要会用框架,更要理解其背后的设计思想,这样才能在技术选型和架构设计中做出正确的决策。
本文档涵盖了Spring框架的核心知识点和实战技巧,希望能为你的Spring学习之旅提供有价值的指导。学习是一个持续的过程,保持好奇心和实践精神,你一定能成为Spring专家!# Java开发框架Spring学习指南:从入门到精通的实战技巧与常见问题解析
引言:为什么选择Spring框架?
Spring框架是Java生态系统中最流行的企业级应用开发框架,自2003年诞生以来,已经成为Java开发者的必备技能。Spring的核心优势在于其”轻量级”和”非侵入式”的设计理念,通过依赖注入(DI)和面向切面编程(AOP)解决了企业级开发中的复杂性问题。
在实际开发中,Spring不仅仅是一个框架,更是一个完整的生态系统。从基础的IoC容器到Spring Boot的自动化配置,再到Spring Cloud的微服务解决方案,Spring为开发者提供了一站式的开发体验。根据2023年的调查,超过85%的Java企业应用都在使用Spring框架,这充分证明了其在行业中的地位。
第一部分:Spring核心概念详解
1.1 控制反转(IoC)与依赖注入(DI)
控制反转是Spring框架的基石。传统的编程方式中,对象主动创建和管理其依赖的对象;而在IoC模式下,这个过程被反转了,由容器来负责对象的创建和管理。
生活中的类比:想象你去餐厅吃饭。传统方式是你自己买菜、洗菜、做饭(主动创建依赖);而IoC方式是你去餐厅,告诉服务员你想吃什么,厨房(容器)会为你准备好一切(被动接收依赖)。
代码示例:手动创建对象 vs Spring IoC
// 传统方式:手动创建依赖
public class UserService {
private UserRepository userRepository;
public UserService() {
// 紧耦合,难以测试和维护
this.userRepository = new UserRepositoryImpl();
}
}
// Spring方式:依赖注入
public class UserService {
private final UserRepository userRepository;
// 构造器注入,松耦合
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
配置示例:XML配置方式
<!-- beans.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="userRepository" class="com.example UserRepositoryImpl"/>
<!-- 注入依赖 -->
<bean id="userService" class="com.example UserService">
<property name="userRepository" ref="userRepository"/>
</bean>
</beans>
配置示例:注解配置方式(推荐)
// 定义Bean
@Repository
public class UserRepositoryImpl implements UserRepository {
// 实现细节
}
// 注入依赖
@Service
public class UserService {
private final UserRepository userRepository;
@Autowired
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
1.2 面向切面编程(AOP)
AOP允许开发者在不修改业务代码的情况下,为程序添加横切关注点(如日志、事务、安全等)。这解决了OOP中代码重复的问题。
实际应用场景:在所有Service方法执行前后记录日志,或者为所有写操作添加事务管理。
代码示例:自定义日志切面
@Aspect
@Component
public class LoggingAspect {
// 定义切点:所有Service层的方法
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceMethods() {}
// 前置通知
@Before("serviceMethods()")
public void logBefore(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
System.out.println("执行方法: " + methodName + ",参数: " + Arrays.toString(joinPoint.getArgs()));
}
// 后置通知
@After("serviceMethods()")
public void logAfter(JoinPoint joinPoint) {
System.out.println("方法执行完成: " + joinPoint.getSignature().getName());
}
// 环绕通知(最强大)
@Around("serviceMethods()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object result = joinPoint.proceed(); // 执行原方法
long duration = System.currentTimeMillis() - start;
System.out.println("方法执行耗时: " + duration + "ms");
return result;
}
}
启用AOP的配置
@Configuration
@EnableAspectJAutoProxy
@ComponentScan("com.example")
public class AppConfig {
// 配置类
}
1.3 Bean的生命周期管理
Spring容器管理Bean的完整生命周期,包括实例化、初始化、使用和销毁四个阶段。
代码示例:自定义生命周期管理
@Component
public class CustomBean implements InitializingBean, DisposableBean {
public CustomBean() {
System.out.println("1. 构造器被调用");
}
@PostConstruct
public void postConstruct() {
System.out.println("2. @PostConstruct被调用");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("3. afterPropertiesSet被调用");
}
@PreDestroy
public void preDestroy() {
System.out.println("4. @PreDestroy被调用");
}
@Override
public void destroy() throws Exception {
System.out.println("5. destroy被调用");
}
}
第二部分:Spring Boot快速入门
2.1 Spring Boot的核心优势
Spring Boot通过”约定优于配置”的理念,极大地简化了Spring应用的开发过程。它内置了Tomcat、Jetty等Web服务器,提供了自动配置功能,让开发者可以专注于业务逻辑。
创建第一个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 // 控制器
│ │ │ └── service/
│ │ └── resources/
│ │ ├── application.properties // 配置文件
│ │ └── static/ // 静态资源
│ └── test/
└── pom.xml // Maven配置
启动类代码
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
// 这一行代码启动了整个Spring Boot应用
// 包括:初始化Spring容器、启动内嵌Web服务器、加载自动配置
SpringApplication.run(DemoApplication.class, args);
}
}
2.2 RESTful API开发实战
创建一个完整的用户管理API
// 实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Long id;
private String name;
private String email;
}
// Controller层
@RestController
@RequestMapping("/api/users")
public class UserController {
private final Map<Long, User> userStore = new ConcurrentHashMap<>();
private AtomicLong idGenerator = new AtomicLong(1);
// 创建用户
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) {
user.setId(idGenerator.getAndIncrement());
userStore.put(user.getId(), user);
return ResponseEntity.status(HttpStatus.CREATED).body(user);
}
// 获取用户
@GetMapping("/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
User user = userStore.get(id);
if (user == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(user);
}
// 更新用户
@PutMapping("/{id}")
public ResponseEntity<User> updateUser(@PathVariable Long id, @RequestBody User user) {
if (!userStore.containsKey(id)) {
return ResponseEntity.notFound().build();
}
user.setId(id);
userStore.put(id, user);
return ResponseEntity.ok(user);
}
// 删除用户
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
userStore.remove(id);
return ResponseEntity.noContent().build();
}
// 获取所有用户
@GetMapping
public ResponseEntity<List<User>> getAllUsers() {
return ResponseEntity.ok(new ArrayList<>(userStore.values()));
}
}
测试API的HTTP请求示例
# 创建用户
curl -X POST http://localhost:8080/api/users \
-H "Content-Type: application/json" \
-d '{"name":"张三","email":"zhangsan@example.com"}'
# 获取用户
curl http://localhost:8080/api/users/1
# 获取所有用户
curl http://localhost:8080/api/users
2.3 配置管理详解
application.properties vs application.yml
properties格式:
# application.properties
server.port=8080
spring.application.name=my-app
spring.datasource.url=jdbc:mysql://localhost:3306/mydb
spring.datasource.username=root
spring.datasource.password=secret
logging.level.com.example=DEBUG
yml格式(推荐):
# application.yml
server:
port: 8080
spring:
application:
name: my-app
datasource:
url: jdbc:mysql://localhost:3306/mydb
username: root
password: secret
logging:
level:
com.example: DEBUG
多环境配置
创建多个配置文件:
application-dev.yml- 开发环境application-test.yml- 测试环境application-prod.yml- 生产环境
在主配置文件中激活环境:
# application.yml
spring:
profiles:
active: dev # 激活开发环境
自定义配置与@Value注解
# application.yml
app:
name: My Application
version: 1.0.0
features:
- logging
- monitoring
- caching
@Component
public class AppConfig {
@Value("${app.name}")
private String appName;
@Value("${app.version}")
private String appVersion;
@Value("${app.features[0]}")
private String firstFeature;
// 使用配置类(更推荐)
@ConfigurationProperties(prefix = "app")
@Component
@Data
public static class AppProperties {
private String name;
private String version;
private List<String> features;
}
}
第三部分:Spring Data JPA与数据库操作
3.1 JPA基础概念
JPA(Java Persistence API)是Java持久化规范,Spring Data JPA在其基础上提供了更便捷的Repository抽象。
实体类定义
@Entity
@Table(name = "users")
@Data
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, length = 50)
private String name;
@Column(unique = true, nullable = false)
private String email;
@Column(name = "created_at")
private LocalDateTime createdAt;
@PrePersist
public void prePersist() {
this.createdAt = LocalDateTime.now();
}
}
3.2 Repository接口
Spring Data JPA通过Repository接口提供了CRUD操作的自动实现。
基础Repository接口
// 继承JpaRepository获得完整CRUD功能
public interface UserRepository extends JpaRepository<User, Long> {
// Spring Data JPA会根据方法名自动生成查询
List<User> findByName(String name);
// 支持And、Or等关键词
List<User> findByNameAndEmail(String name, String email);
// 支持Like查询
List<User> findByNameContaining(String keyword);
// 支持排序
List<User> findByOrderByNameAsc();
// 分页查询
Page<User> findByEmailContaining(String email, Pageable pageable);
// 自定义JPQL查询
@Query("SELECT u FROM User u WHERE u.email LIKE %:keyword%")
List<User> searchByEmail(@Param("keyword") String keyword);
// 原生SQL查询
@Query(value = "SELECT * FROM users WHERE name = :name", nativeQuery = true)
List<User> findByNameNative(@Param("name") String name);
// 自定义更新操作
@Modifying
@Query("UPDATE User u SET u.email = :email WHERE u.id = :id")
int updateEmail(@Param("id") Long id, @Param("email") String email);
}
3.3 Service层实现
@Service
@Transactional
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
// 创建用户
public User createUser(String name, String email) {
// 业务验证
if (userRepository.findByEmail(email).isPresent()) {
throw new RuntimeException("邮箱已存在");
}
User user = new User();
user.setName(name);
user.setEmail(email);
return userRepository.save(user);
}
// 复杂查询示例
public List<User> searchUsers(String keyword, int page, int size) {
Pageable pageable = PageRequest.of(page, size, Sort.by("name").ascending());
return userRepository.findByEmailContaining(keyword, pageable).getContent();
}
// 批量操作
@Transactional
public void batchCreateUsers(List<User> users) {
// 事务确保要么全部成功,要么全部失败
userRepository.saveAll(users);
}
}
第四部分:Spring事务管理
4.1 事务基础概念
事务是数据库操作的原子性单位,确保一组操作要么全部成功,要么全部失败。
代码示例:事务传播行为
@Service
public class OrderService {
private final OrderRepository orderRepository;
private final InventoryService inventoryService;
@Transactional(propagation = Propagation.REQUIRED)
public void createOrder(Order order) {
// 1. 创建订单
orderRepository.save(order);
// 2. 扣减库存(如果失败,整个事务回滚)
inventoryService.decreaseStock(order.getProductId(), order.getQuantity());
}
}
@Service
public class InventoryService {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void decreaseStock(Long productId, int quantity) {
// 独立事务,即使外层事务回滚,这个操作也会提交
// 实现细节...
}
}
4.2 事务传播行为详解
| 传播行为 | 说明 | 使用场景 |
|---|---|---|
| REQUIRED | 如果存在事务则加入,否则新建 | 默认行为,大多数情况 |
| REQUIRES_NEW | 总是新建事务,挂起当前事务 | 需要独立提交的日志记录 |
| NESTED | 嵌套事务(如果支持) | 部分回滚场景 |
| MANDATORY | 必须存在事务,否则抛异常 | 强制要求事务环境 |
4.3 事务失效的常见场景
场景1:方法不是public
// ❌ 事务失效
@Transactional
private void privateMethod() {
// ...
}
// ✅ 正确
@Transactional
public void publicMethod() {
// ...
}
场景2:同类内部调用
@Service
public class UserService {
public void methodA() {
methodB(); // 这里不会触发事务,因为是内部调用
}
@Transactional
public void methodB() {
// ...
}
}
解决方案:
@Service
public class UserService {
@Autowired
private UserService self; // 注入自己
public void methodA() {
self.methodB(); // 通过代理对象调用
}
@Transactional
public void methodB() {
// ...
}
}
场景3:异常类型不匹配
// ❌ 事务失效(默认只回滚RuntimeException)
@Transactional
public void method() {
try {
// 业务逻辑
throw new Exception("检查异常"); // 不会回滚
} catch (Exception e) {
// 捕获后未抛出
}
}
// ✅ 正确配置
@Transactional(rollbackFor = Exception.class)
public void method() {
throw new Exception("检查异常"); // 会回滚
}
第五部分:Spring Security安全框架
5.1 认证与授权基础
Spring Security提供了强大的认证和授权功能。
基础配置示例
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// 启用CSRF保护
.csrf().disable()
// 授权配置
.authorizeHttpRequests(authz -> authz
// 允许匿名访问的路径
.requestMatchers("/api/public/**", "/login").permitAll()
// 需要ADMIN角色的路径
.requestMatchers("/api/admin/**").hasRole("ADMIN")
// 需要认证的路径
.anyRequest().authenticated()
)
// 表单登录配置
.formLogin(form -> form
.loginPage("/login")
.defaultSuccessUrl("/home")
.permitAll()
)
// 登出配置
.logout(logout -> logout
.logoutUrl("/logout")
.logoutSuccessUrl("/login?logout")
);
return http.build();
}
// 用户详情服务
@Bean
public UserDetailsService userDetailsService() {
// 内存用户存储(生产环境应使用数据库)
UserDetails user = User.builder()
.username("user")
.password("{noop}password") // {noop}表示明文密码
.roles("USER")
.build();
UserDetails admin = User.builder()
.username("admin")
.password("{noop}admin123")
.roles("ADMIN", "USER")
.build();
return new InMemoryUserDetailsManager(user, admin);
}
// 密码编码器
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
5.2 JWT认证实现
JWT工具类
@Component
public class JwtTokenUtil {
private static final String SECRET_KEY = "your-secret-key-min-256-bits";
private static final long EXPIRATION_TIME = 86400000; // 24小时
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_TIME))
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.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 getClaimsFromToken(token).getSubject();
}
private Claims getClaimsFromToken(String token) {
return Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody();
}
private Boolean isTokenExpired(String token) {
return getClaimsFromToken(token).getExpiration().before(new Date());
}
}
JWT认证过滤器
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
final String authHeader = request.getHeader("Authorization");
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
filterChain.doFilter(request, response);
return;
}
final String jwt = authHeader.substring(7);
final String username = jwtTokenUtil.extractUsername(jwt);
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = 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);
}
}
第六部分:Spring Boot高级特性
6.1 自动配置原理
Spring Boot的自动配置基于”条件化配置”理念,通过@Conditional注解实现。
自定义Starter开发
// 1. 自定义配置属性
@ConfigurationProperties(prefix = "mystarter")
public class MyStarterProperties {
private boolean enabled = true;
private String message = "Hello";
// getters and setters
}
// 2. 自动配置类
@Configuration
@ConditionalOnClass(MyService.class)
@ConditionalOnProperty(prefix = "mystarter", name = "enabled", havingValue = "true", matchIfMissing = true)
@EnableConfigurationProperties(MyStarterProperties.class)
public class MyStarterAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public MyService myService(MyStarterProperties properties) {
return new MyService(properties.getMessage());
}
}
// 3. 在META-INF/spring.factories中注册
# spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.mystarter.MyStarterAutoConfiguration
6.2 Actuator监控
配置Actuator端点
# application.yml
management:
endpoints:
web:
exposure:
include: health,info,metrics,env
endpoint:
health:
show-details: always
info:
env:
enabled: true
自定义健康检查
@Component
public class DatabaseHealthIndicator implements HealthIndicator {
@Autowired
private DataSource dataSource;
@Override
public Health health() {
try (Connection connection = dataSource.getConnection()) {
// 执行简单查询验证连接
connection.createStatement().execute("SELECT 1");
return Health.up()
.withDetail("database", "MySQL")
.withDetail("status", "connected")
.build();
} catch (Exception e) {
return Health.down()
.withDetail("error", e.getMessage())
.build();
}
}
}
6.3 异步处理与事件驱动
异步方法配置
@Configuration
@EnableAsync
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;
}
}
@Service
public class EmailService {
@Async
public void sendEmail(String to, String subject, String content) {
// 模拟耗时操作
try {
Thread.sleep(5000);
System.out.println("邮件已发送到: " + to);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
事件监听机制
// 自定义事件
public class UserRegisteredEvent extends ApplicationEvent {
private final User user;
public UserRegisteredEvent(Object source, User user) {
super(source);
this.user = user;
}
public User getUser() { return user; }
}
// 事件发布者
@Service
public class RegistrationService {
@Autowired
private ApplicationEventPublisher eventPublisher;
public void registerUser(User user) {
// 保存用户
// ...
// 发布事件
eventPublisher.publishEvent(new UserRegisteredEvent(this, user));
}
}
// 事件监听者
@Component
public class UserRegistrationListener {
@EventListener
public void handleUserRegistration(UserRegisteredEvent event) {
System.out.println("新用户注册: " + event.getUser().getName());
// 发送欢迎邮件、记录日志等...
}
@EventListener
@Async
public void asyncHandleRegistration(UserRegisteredEvent event) {
// 异步处理
System.out.println("异步处理用户注册事件");
}
}
第七部分:Spring Cloud微服务架构
7.1 服务注册与发现(Eureka)
Eureka Server配置
// 启动类
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
// application.yml
server:
port: 8761
eureka:
client:
register-with-eureka: false # 不注册自己
fetch-registry: false # 不获取注册表
service-url:
defaultZone: http://localhost:8761/eureka/
server:
enable-self-preservation: false # 关闭自我保护模式(开发环境)
Eureka Client配置
@SpringBootApplication
@EnableEurekaClient
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
}
# application.yml
server:
port: 8081
spring:
application:
name: user-service
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true # 使用IP注册
7.2 服务调用(OpenFeign)
Feign客户端定义
// 声明式HTTP客户端
@FeignClient(name = "user-service", fallback = UserServiceFallback.class)
public interface UserServiceClient {
@GetMapping("/api/users/{id}")
User getUser(@PathVariable("id") Long id);
@PostMapping("/api/users")
User createUser(@RequestBody User user);
@GetMapping("/api/users/search")
List<User> searchUsers(@RequestParam("keyword") String keyword);
}
// 降级处理
@Component
public class UserServiceFallback implements UserServiceClient {
@Override
public User getUser(Long id) {
// 返回默认值或缓存值
return new User(id, "默认用户", "default@example.com");
}
@Override
public User createUser(User user) {
throw new RuntimeException("服务暂时不可用");
}
@Override
public List<User> searchUsers(String keyword) {
return Collections.emptyList();
}
}
// 使用Feign客户端
@Service
public class OrderService {
@Autowired
private UserServiceClient userServiceClient;
public Order createOrder(Long userId, Order order) {
// 远程调用用户服务
User user = userServiceClient.getUser(userId);
if (user == null) {
throw new RuntimeException("用户不存在");
}
// 创建订单逻辑
// ...
return order;
}
}
7.3 配置中心(Config Server)
Config Server配置
@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
# application.yml
server:
port: 8888
spring:
cloud:
config:
server:
git:
uri: https://github.com/your-org/config-repo
search-paths: '{application}'
username: your-username
password: your-password
default-label: main
Config Client配置
# bootstrap.yml (优先于application.yml加载)
spring:
application:
name: user-service
cloud:
config:
uri: http://localhost:8888
profile: dev
label: main
第八部分:常见问题与解决方案
8.1 循环依赖问题
问题描述
两个或多个Bean相互依赖,形成闭环。
// ❌ 循环依赖示例
@Service
public class ServiceA {
@Autowired
private ServiceB serviceB;
}
@Service
public class ServiceB {
@Autowired
private ServiceA serviceA;
}
解决方案
方案1:使用@Lazy延迟注入
@Service
public class ServiceA {
@Autowired
@Lazy
private ServiceB serviceB;
}
方案2:使用Setter注入
@Service
public class ServiceA {
private ServiceB serviceB;
@Autowired
public void setServiceB(ServiceB serviceB) {
this.serviceB = serviceB;
}
}
方案3:重构设计(推荐)
// 提取公共逻辑到第三个Service
@Service
public class CommonService {
// 公共方法
}
@Service
public class ServiceA {
@Autowired
private CommonService commonService;
}
@Service
public class ServiceB {
@Autowired
private CommonService commonService;
}
8.2 数据库连接池问题
常见配置与优化
# HikariCP配置(Spring Boot默认)
spring:
datasource:
hikari:
# 连接池名称
pool-name: MyHikariCP
# 最小空闲连接
minimum-idle: 5
# 最大连接数
maximum-pool-size: 20
# 连接超时时间(毫秒)
connection-timeout: 30000
# 连接最大生命周期(毫秒)
max-lifetime: 1800000
# 空闲连接存活时间(毫秒)
idle-timeout: 600000
# 连接测试查询
connection-test-query: SELECT 1
连接泄漏检测
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties("spring.datasource.hikari")
public DataSource dataSource() {
HikariDataSource dataSource = new HikariDataSource();
// 启用泄漏检测
dataSource.setLeakDetectionThreshold(60000); // 60秒
return dataSource;
}
}
8.3 性能调优技巧
1. N+1查询问题
问题代码:
// ❌ N+1问题:先查询所有用户,然后为每个用户查询订单
List<User> users = userRepository.findAll();
for (User user : users) {
// 每次循环都会产生一次SQL查询
user.getOrders().size(); // 触发懒加载
}
解决方案:
// ✅ 使用JOIN FETCH一次性加载
@Query("SELECT DISTINCT u FROM User u LEFT JOIN FETCH u.orders")
List<User> findAllWithOrders();
// 或者使用EntityGraph
@EntityGraph(attributePaths = {"orders"})
List<User> findAll();
2. 批量操作优化
// ❌ 逐条插入,性能差
for (User user : users) {
userRepository.save(user);
}
// ✅ 批量插入
userRepository.saveAll(users);
// ✅ JPA批量更新
@Modifying
@Query("UPDATE User u SET u.status = :status WHERE u.id IN :ids")
void batchUpdateStatus(@Param("ids") List<Long> ids, @Param("status") String status);
// ✅ 使用原生JDBC批量操作
@Autowired
private JdbcTemplate jdbcTemplate;
public void batchInsert(List<User> users) {
String sql = "INSERT INTO users (name, email) VALUES (?, ?)";
jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
User user = users.get(i);
ps.setString(1, user.getName());
ps.setString(2, user.getEmail());
}
@Override
public int getBatchSize() {
return users.size();
}
});
}
3. 缓存优化
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager() {
// 使用Caffeine缓存
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCaffeine(Caffeine.newBuilder()
.initialCapacity(100)
.maximumSize(1000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.recordStats()); // 启用统计
return cacheManager;
}
}
@Service
public class UserService {
@Cacheable(value = "users", key = "#id")
public User getUserById(Long id) {
// 只有第一次会执行这里,后续从缓存获取
return userRepository.findById(id).orElse(null);
}
@CacheEvict(value = "users", key = "#id")
public void updateUser(Long id, User user) {
// 更新后清除缓存
userRepository.save(user);
}
@CachePut(value = "users", key = "#user.id")
public User saveUser(User user) {
// 更新缓存
return userRepository.save(user);
}
}
8.4 异常处理最佳实践
全局异常处理器
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public ErrorResponse handleResourceNotFound(ResourceNotFoundException ex) {
return new ErrorResponse("RESOURCE_NOT_FOUND", ex.getMessage());
}
@ExceptionHandler(ValidationException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ErrorResponse handleValidationException(ValidationException ex) {
return new ErrorResponse("VALIDATION_ERROR", ex.getMessage());
}
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ErrorResponse handleGenericException(Exception ex) {
// 记录日志
log.error("Unexpected error", ex);
return new ErrorResponse("INTERNAL_ERROR", "系统内部错误");
}
}
// 自定义异常
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException(String message) {
super(message);
}
}
// 错误响应格式
@Data
@AllArgsConstructor
public class ErrorResponse {
private String code;
private String message;
}
8.5 测试策略
单元测试示例
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository userRepository;
@InjectMocks
private UserService userService;
@Test
void shouldCreateUserSuccessfully() {
// Given
User user = new User(null, "张三", "zhangsan@example.com");
when(userRepository.save(any(User.class))).thenReturn(user);
// When
User result = userService.createUser("张三", "zhangsan@example.com");
// Then
assertNotNull(result.getId());
assertEquals("张三", result.getName());
verify(userRepository, times(1)).save(any(User.class));
}
@Test
void shouldThrowExceptionWhenEmailExists() {
// Given
when(userRepository.findByEmail("zhangsan@example.com"))
.thenReturn(Optional.of(new User()));
// When & Then
assertThrows(RuntimeException.class, () -> {
userService.createUser("张三", "zhangsan@example.com");
});
}
}
集成测试示例
@SpringBootTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Transactional
class UserServiceIntegrationTest {
@Autowired
private UserService userService;
@Autowired
private UserRepository userRepository;
@Test
void shouldCreateUserInDatabase() {
// When
User user = userService.createUser("李四", "lisi@example.com");
// Then
User savedUser = userRepository.findById(user.getId()).orElse(null);
assertNotNull(savedUser);
assertEquals("李四", savedUser.getName());
}
}
第九部分:Spring学习路线图
9.1 初级阶段(1-2个月)
核心概念掌握
- IoC容器和Bean生命周期
- 依赖注入的三种方式
- AOP原理与应用
- Spring Boot基础
Web开发
- RESTful API设计
- 请求参数校验
- 全局异常处理
- 文件上传下载
数据持久化
- JPA基础
- Repository使用
- 事务管理
- 连接池配置
9.2 中级阶段(3-6个月)
高级特性
- 自定义Starter开发
- 自动配置原理
- Actuator监控
- 异步处理与事件
安全框架
- Spring Security基础
- JWT认证
- OAuth2.0
- 方法级安全
测试
- 单元测试(JUnit + Mockito)
- 集成测试
- 测试覆盖率
- Testcontainers
9.3 高级阶段(6个月以上)
微服务架构
- Spring Cloud全家桶
- 服务注册与发现
- 配置中心
- 熔断降级(Hystrix/Resilience4j)
性能优化
- JVM调优
- 数据库优化
- 缓存策略
- 并发编程
DevOps
- Docker容器化
- Kubernetes部署
- CI/CD流水线
- 监控与日志(ELK)
第十部分:总结与建议
学习建议
理论结合实践:不要只看文档,一定要动手写代码。建议从一个完整的项目开始,比如博客系统、电商后台等。
阅读源码:Spring的源码设计非常精妙,阅读源码能让你深入理解框架原理。可以从
ApplicationContext的实现开始。关注社区:Spring生态更新很快,关注官方博客、GitHub仓库和Stack Overflow上的问题。
构建知识体系:将零散的知识点串联起来,形成完整的知识体系。比如理解IoC如何支撑AOP,AOP如何实现事务管理。
重视测试:良好的测试习惯能让你在重构和升级时更有信心。
常用资源推荐
- 官方文档:spring.io/projects/spring-framework
- Spring Boot文档:spring.io/projects/spring-boot
- Spring Cloud文档:spring.io/projects/spring-cloud
- Stack Overflow:Spring相关问题的宝库
- GitHub:关注spring-projects组织
最后的忠告
Spring框架虽然强大,但不要过度使用。记住”简单就是美”的原则,只有在真正需要时才引入新的复杂性。一个优秀的Spring开发者不仅要会用框架,更要理解其背后的设计思想,这样才能在技术选型和架构设计中做出正确的决策。
本文档涵盖了Spring框架的核心知识点和实战技巧,希望能为你的Spring学习之旅提供有价值的指导。学习是一个持续的过程,保持好奇心和实践精神,你一定能成为Spring专家!
