引言
Spring框架是Java企业级开发的事实标准,它通过依赖注入(DI)和面向切面编程(AOP)等核心思想,极大地简化了企业应用的开发。本指南将从基础概念出发,逐步深入到高级特性,并结合实战案例,帮助你全面掌握Spring框架的核心原理与应用技巧。
第一部分:Spring基础入门
1.1 Spring框架概述
Spring是一个轻量级的开源框架,最初由Rod Johnson在2003年发布。它的核心特性包括:
- 依赖注入(DI):通过控制反转(IoC)容器管理对象之间的依赖关系
- 面向切面编程(AOP):将横切关注点(如日志、事务)从业务逻辑中分离
- 声明式事务管理:通过注解或配置实现事务管理
- 集成支持:与各种持久层框架(如MyBatis、Hibernate)无缝集成
1.2 环境搭建
1.2.1 开发工具准备
- JDK 8或更高版本
- Maven或Gradle构建工具
- IDE(推荐IntelliJ IDEA或Eclipse)
1.2.2 创建第一个Spring项目
使用Maven创建一个简单的Spring项目:
<!-- pom.xml -->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>spring-demo</artifactId>
<version>1.0.0</version>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<spring.version>5.3.20</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
</project>
1.2.3 创建简单的Bean和配置类
// 1. 创建一个简单的Bean
public class HelloWorldService {
private String message;
public HelloWorldService(String message) {
this.message = message;
}
public void sayHello() {
System.out.println("Hello, " + message);
}
public void setMessage(String message) {
this.message = message;
}
}
// 2. 创建配置类
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean
public HelloWorldService helloWorldService() {
return new HelloWorldService("Spring Framework");
}
}
// 3. 主程序
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MainApp {
public static void main(String[] args) {
// 创建Spring容器
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// 获取Bean
HelloWorldService service = context.getBean(HelloWorldService.class);
// 使用Bean
service.sayHello();
}
}
1.3 依赖注入详解
1.3.1 构造器注入
public class OrderService {
private final UserService userService;
private final PaymentService paymentService;
// 构造器注入
public OrderService(UserService userService, PaymentService paymentService) {
this.userService = userService;
this.paymentService = paymentService;
}
public void createOrder() {
// 业务逻辑
}
}
// 配置类
@Configuration
public class ServiceConfig {
@Bean
public UserService userService() {
return new UserService();
}
@Bean
public PaymentService paymentService() {
return new PaymentService();
}
@Bean
public OrderService orderService(UserService userService, PaymentService paymentService) {
return new OrderService(userService, paymentService);
}
}
1.3.2 Setter注入
public class OrderService {
private UserService userService;
private PaymentService paymentService;
// Setter方法注入
public void setUserService(UserService userService) {
this.userService = userService;
}
public void setPaymentService(PaymentService paymentService) {
this.paymentService = paymentService;
}
}
1.3.3 字段注入(不推荐在生产环境使用)
public class OrderService {
@Autowired
private UserService userService;
@Autowired
private PaymentService paymentService;
}
1.4 Bean的作用域
Spring支持以下Bean作用域:
- singleton(默认):每个Spring容器中一个Bean实例
- prototype:每次请求都创建新实例
- request:每个HTTP请求一个实例(Web环境)
- session:每个HTTP会话一个实例(Web环境)
- global-session:全局HTTP会话(Portlet环境)
@Configuration
public class ScopeConfig {
@Bean
@Scope("singleton") // 默认作用域
public SingletonBean singletonBean() {
return new SingletonBean();
}
@Bean
@Scope("prototype")
public PrototypeBean prototypeBean() {
return new PrototypeBean();
}
}
第二部分:Spring核心原理深入
2.1 IoC容器工作原理
2.1.1 Bean的生命周期
Spring Bean的完整生命周期包括以下阶段:
- 实例化:通过构造器或工厂方法创建Bean实例
- 属性赋值:通过setter或构造器注入依赖
- 初始化前:调用
BeanPostProcessor.postProcessBeforeInitialization() - 初始化:调用
InitializingBean.afterPropertiesSet()或@PostConstruct方法 - 初始化后:调用
BeanPostProcessor.postProcessAfterInitialization() - 使用:Bean被应用程序使用
- 销毁:调用
DisposableBean.destroy()或@PreDestroy方法
@Component
public class LifecycleBean implements InitializingBean, DisposableBean {
public LifecycleBean() {
System.out.println("1. 构造器调用");
}
@PostConstruct
public void postConstruct() {
System.out.println("3. @PostConstruct方法调用");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("4. InitializingBean.afterPropertiesSet()调用");
}
@PreDestroy
public void preDestroy() {
System.out.println("6. @PreDestroy方法调用");
}
@Override
public void destroy() throws Exception {
System.out.println("7. DisposableBean.destroy()调用");
}
}
2.1.2 BeanPostProcessor详解
BeanPostProcessor是Spring提供的扩展点,允许在Bean初始化前后进行自定义处理。
@Component
public class CustomBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof UserService) {
System.out.println("在UserService初始化前进行处理");
// 可以在这里修改Bean的属性或行为
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof UserService) {
System.out.println("在UserService初始化后进行处理");
// 可以在这里创建代理对象
}
return bean;
}
}
2.2 AOP原理与应用
2.2.1 AOP核心概念
- 切面(Aspect):横切关注点的模块化
- 连接点(Join Point):程序执行过程中的某个位置
- 切点(Pointcut):匹配连接点的表达式
- 通知(Advice):在切点执行的具体操作
- 目标对象(Target):被代理的对象
- 代理(Proxy):Spring创建的代理对象
2.2.2 基于注解的AOP实现
// 1. 定义切面
@Aspect
@Component
public class LoggingAspect {
// 定义切点:所有public方法
@Pointcut("execution(public * com.example.service.*.*(..))")
public void serviceMethods() {}
// 前置通知
@Before("serviceMethods()")
public void beforeMethod(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
System.out.println("开始执行方法: " + methodName);
}
// 后置通知
@After("serviceMethods()")
public void afterMethod(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
System.out.println("方法执行完成: " + methodName);
}
// 返回通知
@AfterReturning(pointcut = "serviceMethods()", returning = "result")
public void afterReturning(JoinPoint joinPoint, Object result) {
System.out.println("方法返回值: " + result);
}
// 异常通知
@AfterThrowing(pointcut = "serviceMethods()", throwing = "ex")
public void afterThrowing(JoinPoint joinPoint, Exception ex) {
System.out.println("方法执行异常: " + ex.getMessage());
}
// 环绕通知
@Around("serviceMethods()")
public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
System.out.println("环绕通知 - 方法开始");
try {
Object result = joinPoint.proceed(); // 执行目标方法
long end = System.currentTimeMillis();
System.out.println("环绕通知 - 方法执行完成,耗时: " + (end - start) + "ms");
return result;
} catch (Exception e) {
System.out.println("环绕通知 - 方法执行异常: " + e.getMessage());
throw e;
}
}
}
// 2. 启用AOP
@Configuration
@EnableAspectJAutoProxy
public class AopConfig {
// 配置类
}
// 3. 目标类
@Service
public class UserService {
public String getUserById(Long id) {
System.out.println("执行getUserById方法");
return "User-" + id;
}
public void deleteUser(Long id) {
System.out.println("执行deleteUser方法");
if (id == null) {
throw new IllegalArgumentException("ID不能为空");
}
}
}
2.2.3 AOP实现原理
Spring AOP默认使用JDK动态代理(针对接口)或CGLIB代理(针对类)。代理对象的创建过程:
- 代理创建时机:当Bean被注入到其他Bean时,Spring容器会检查是否需要创建代理
- 代理创建过程:
- 获取Bean的Class对象
- 检查是否需要代理(通过
@Aspect注解或XML配置) - 创建代理对象(JDK或CGLIB)
- 将代理对象放入容器
// 深入理解代理创建
public class ProxyCreator {
// JDK动态代理示例
public static Object createJdkProxy(Object target, Class<?>[] interfaces) {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
interfaces,
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("JDK代理 - 方法调用前");
Object result = method.invoke(target, args);
System.out.println("JDK代理 - 方法调用后");
return result;
}
}
);
}
// CGLIB代理示例(需要添加cglib依赖)
public static Object createCglibProxy(Object target) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("CGLIB代理 - 方法调用前");
Object result = proxy.invokeSuper(obj, args);
System.out.println("CGLIB代理 - 方法调用后");
return result;
}
});
return enhancer.create();
}
}
2.3 事务管理
2.3.1 事务传播行为
Spring定义了7种事务传播行为:
- REQUIRED(默认):如果当前存在事务,则加入;否则新建事务
- REQUIRES_NEW:总是新建事务,如果当前存在事务,则挂起当前事务
- SUPPORTS:如果当前存在事务,则加入;否则以非事务方式执行
- MANDATORY:如果当前存在事务,则加入;否则抛出异常
- NOT_SUPPORTED:以非事务方式执行,如果当前存在事务,则挂起
- NEVER:以非事务方式执行,如果当前存在事务,则抛出异常
- NESTED:如果当前存在事务,则在嵌套事务中执行;否则新建事务
@Service
public class OrderService {
@Autowired
private UserService userService;
@Autowired
private PaymentService paymentService;
// 默认传播行为:REQUIRED
@Transactional
public void createOrder(Order order) {
// 1. 创建订单
orderRepository.save(order);
// 2. 扣减库存
inventoryService.deductStock(order.getProductId(), order.getQuantity());
// 3. 支付处理
paymentService.processPayment(order);
// 4. 发送通知
notificationService.sendOrderNotification(order);
}
// REQUIRES_NEW传播行为
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void logOrderCreation(Order order) {
// 独立事务,即使外层事务回滚,这个日志也会保存
auditLogRepository.save(new AuditLog("ORDER_CREATED", order.getId()));
}
// NESTED传播行为
@Transactional(propagation = Propagation.NESTED)
public void processSubOrder(Order order) {
// 嵌套事务,如果外层事务回滚,嵌套事务也会回滚
// 但嵌套事务可以独立提交
subOrderRepository.save(order.getSubOrder());
}
}
2.3.2 事务隔离级别
Spring支持以下事务隔离级别:
- DEFAULT:使用数据库默认隔离级别
- READ_UNCOMMITTED:读未提交
- READ_COMMITTED:读已提交(Oracle默认)
- REPEATABLE_READ:可重复读(MySQL默认)
- SERIALIZABLE:串行化
@Service
public class AccountService {
// 使用READ_COMMITTED隔离级别
@Transactional(isolation = Isolation.READ_COMMITTED)
public void transferMoney(Long fromAccountId, Long toAccountId, BigDecimal amount) {
Account fromAccount = accountRepository.findById(fromAccountId);
Account toAccount = accountRepository.findById(toAccountId);
// 检查余额
if (fromAccount.getBalance().compareTo(amount) < 0) {
throw new InsufficientBalanceException("余额不足");
}
// 扣款
fromAccount.setBalance(fromAccount.getBalance().subtract(amount));
accountRepository.save(fromAccount);
// 存款
toAccount.setBalance(toAccount.getBalance().add(amount));
accountRepository.save(toAccount);
}
}
第三部分:Spring Boot实战
3.1 Spring Boot快速入门
3.1.1 创建Spring Boot项目
使用Spring Initializr创建项目:
<!-- pom.xml -->
<project>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
3.1.2 创建RESTful API
// 1. 实体类
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String username;
@Column(nullable = false)
private String email;
// 构造器、getter、setter省略
}
// 2. Repository接口
public interface UserRepository extends JpaRepository<User, Long> {
User findByUsername(String username);
List<User> findByEmailContaining(String email);
}
// 3. Service层
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public User createUser(User user) {
// 业务逻辑验证
if (userRepository.findByUsername(user.getUsername()) != null) {
throw new RuntimeException("用户名已存在");
}
return userRepository.save(user);
}
public List<User> getAllUsers() {
return userRepository.findAll();
}
public User getUserById(Long id) {
return userRepository.findById(id)
.orElseThrow(() -> new RuntimeException("用户不存在"));
}
}
// 4. Controller层
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) {
User createdUser = userService.createUser(user);
return ResponseEntity.status(HttpStatus.CREATED).body(createdUser);
}
@GetMapping
public ResponseEntity<List<User>> getAllUsers() {
return ResponseEntity.ok(userService.getAllUsers());
}
@GetMapping("/{id}")
public ResponseEntity<User> getUserById(@PathVariable Long id) {
return ResponseEntity.ok(userService.getUserById(id));
}
@PutMapping("/{id}")
public ResponseEntity<User> updateUser(@PathVariable Long id, @RequestBody User user) {
User existingUser = userService.getUserById(id);
existingUser.setUsername(user.getUsername());
existingUser.setEmail(user.getEmail());
User updatedUser = userService.createUser(existingUser);
return ResponseEntity.ok(updatedUser);
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
userService.deleteUser(id);
return ResponseEntity.noContent().build();
}
}
// 5. 主应用类
@SpringBootApplication
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}
}
3.2 Spring Boot配置管理
3.2.1 配置文件
Spring Boot支持多种配置文件格式:
- application.properties:键值对格式
- application.yml:YAML格式(推荐)
# application.yml
server:
port: 8080
servlet:
context-path: /api
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: update
show-sql: true
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL8Dialect
profiles:
active: dev
logging:
level:
com.example: DEBUG
org.springframework.web: INFO
3.2.2 配置类
@Configuration
@ConfigurationProperties(prefix = "app")
public class AppConfig {
private String name;
private String version;
private Database database = new Database();
// 构造器、getter、setter省略
@Data
public static class Database {
private String url;
private String username;
private String password;
}
}
// 使用配置
@Service
public class ConfigService {
@Autowired
private AppConfig appConfig;
public void printConfig() {
System.out.println("应用名称: " + appConfig.getName());
System.out.println("数据库URL: " + appConfig.getDatabase().getUrl());
}
}
3.3 Spring Boot高级特性
3.3.1 自动配置原理
Spring Boot自动配置的核心是@EnableAutoConfiguration注解,它通过spring.factories文件加载自动配置类。
// 自定义自动配置
@Configuration
@ConditionalOnClass({DataSource.class, EmbeddedDatabaseType.class})
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "hikari", matchIfMissing = true)
@EnableConfigurationProperties(DataSourceProperties.class)
public class HikariDataSourceAutoConfiguration {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.hikari")
public HikariDataSource dataSource(DataSourceProperties properties) {
HikariDataSource dataSource = properties.initializeDataSourceBuilder()
.type(HikariDataSource.class)
.build();
dataSource.setPoolName("HikariCP-Pool");
return dataSource;
}
}
3.3.2 多环境配置
# application-dev.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb_dev
username: dev_user
password: dev_password
# application-prod.yml
spring:
datasource:
url: jdbc:mysql://prod-server:3306/mydb_prod
username: prod_user
password: prod_password
hikari:
maximum-pool-size: 20
minimum-idle: 5
// 环境特定配置
@Configuration
@Profile("dev")
public class DevConfig {
@Bean
public DataSource devDataSource() {
// 开发环境数据源配置
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript("classpath:schema-dev.sql")
.build();
}
}
@Configuration
@Profile("prod")
public class ProdConfig {
@Bean
public DataSource prodDataSource() {
// 生产环境数据源配置
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl("jdbc:mysql://prod-server:3306/mydb_prod");
dataSource.setUsername("prod_user");
dataSource.setPassword("prod_password");
dataSource.setMaximumPoolSize(20);
return dataSource;
}
}
第四部分:Spring高级实战
4.1 Spring Security安全框架
4.1.1 基础配置
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable() // 禁用CSRF(REST API通常禁用)
.authorizeRequests()
.antMatchers("/api/public/**").permitAll() // 公开接口
.antMatchers("/api/admin/**").hasRole("ADMIN") // 管理员接口
.anyRequest().authenticated() // 其他接口需要认证
.and()
.httpBasic() // 使用HTTP Basic认证
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); // 无状态会话
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
// 用户详情服务
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("用户不存在");
}
return org.springframework.security.core.userdetails.User
.withUsername(user.getUsername())
.password(user.getPassword())
.roles(user.getRoles().toArray(new String[0]))
.build();
}
}
4.1.2 JWT认证
// JWT工具类
@Component
public class JwtUtil {
private static final String SECRET_KEY = "your-secret-key";
private static final long EXPIRATION_TIME = 86400000; // 24小时
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
claims.put("roles", userDetails.getAuthorities());
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 String extractUsername(String token) {
return Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody()
.getSubject();
}
public boolean validateToken(String token, UserDetails userDetails) {
final String username = extractUsername(token);
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
}
private boolean isTokenExpired(String token) {
return extractExpiration(token).before(new Date());
}
private Date extractExpiration(String token) {
return Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody()
.getExpiration();
}
}
// JWT认证过滤器
@Component
public class JwtRequestFilter extends OncePerRequestFilter {
@Autowired
private JwtUtil jwtUtil;
@Autowired
private UserDetailsService userDetailsService;
@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 = jwtUtil.extractUsername(jwt);
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if (jwtUtil.validateToken(jwt, userDetails)) {
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(
userDetails,
null,
userDetails.getAuthorities()
);
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
}
filterChain.doFilter(request, response);
}
}
4.2 Spring Data JPA高级用法
4.2.1 自定义查询方法
public interface UserRepository extends JpaRepository<User, Long> {
// 方法名约定查询
List<User> findByUsername(String username);
List<User> findByEmailContaining(String email);
List<User> findByAgeGreaterThanAndStatus(Integer age, String status);
// @Query注解
@Query("SELECT u FROM User u WHERE u.username = :username")
User findByUsernameWithQuery(@Param("username") String username);
// 原生SQL查询
@Query(value = "SELECT * FROM users WHERE age > :age", nativeQuery = true)
List<User> findUsersByAgeGreaterThan(@Param("age") Integer age);
// 分页查询
@Query("SELECT u FROM User u WHERE u.status = :status")
Page<User> findByStatus(@Param("status") String status, Pageable pageable);
// 动态查询
@Query("SELECT u FROM User u WHERE " +
"(:username IS NULL OR u.username = :username) AND " +
"(:email IS NULL OR u.email = :email)")
List<User> findUsersByDynamicCriteria(@Param("username") String username,
@Param("email") String email);
// 自定义更新
@Modifying
@Query("UPDATE User u SET u.status = :status WHERE u.id = :id")
int updateUserStatus(@Param("id") Long id, @Param("status") String status);
// 自定义删除
@Modifying
@Query("DELETE FROM User u WHERE u.status = :status")
int deleteUsersByStatus(@Param("status") String status);
}
4.2.2 复杂查询与Specification
// 使用Specification进行动态查询
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public List<User> searchUsers(String username, String email, Integer minAge, Integer maxAge) {
Specification<User> spec = Specification.where(null);
if (username != null && !username.isEmpty()) {
spec = spec.and((root, query, cb) -> cb.equal(root.get("username"), username));
}
if (email != null && !email.isEmpty()) {
spec = spec.and((root, query, cb) -> cb.like(root.get("email"), "%" + email + "%"));
}
if (minAge != null) {
spec = spec.and((root, query, cb) -> cb.greaterThanOrEqualTo(root.get("age"), minAge));
}
if (maxAge != null) {
spec = spec.and((root, query, cb) -> cb.lessThanOrEqualTo(root.get("age"), maxAge));
}
return userRepository.findAll(spec);
}
// 分页查询
public Page<User> searchUsersWithPagination(String username, String email,
Integer minAge, Integer maxAge,
int page, int size) {
Specification<User> spec = Specification.where(null);
if (username != null && !username.isEmpty()) {
spec = spec.and((root, query, cb) -> cb.equal(root.get("username"), username));
}
if (email != null && !email.isEmpty()) {
spec = spec.and((root, query, cb) -> cb.like(root.get("email"), "%" + email + "%"));
}
if (minAge != null) {
spec = spec.and((root, query, cb) -> cb.greaterThanOrEqualTo(root.get("age"), minAge));
}
if (maxAge != null) {
spec = spec.and((root, query, cb) -> cb.lessThanOrEqualTo(root.get("age"), maxAge));
}
Pageable pageable = PageRequest.of(page, size, Sort.by("id").descending());
return userRepository.findAll(spec, pageable);
}
}
4.3 Spring Cloud微服务架构
4.3.1 服务注册与发现(Eureka)
# Eureka Server配置
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 Server启动类
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
// Eureka Client配置
server:
port: 8081
spring:
application:
name: user-service
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true # 使用IP地址注册
// Eureka Client启动类
@SpringBootApplication
@EnableDiscoveryClient
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
}
// 服务调用
@Service
public class OrderService {
@Autowired
private RestTemplate restTemplate;
@LoadBalanced
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
public User getUserById(Long userId) {
// 通过服务名调用,Eureka会自动负载均衡
return restTemplate.getForObject(
"http://user-service/api/users/" + userId,
User.class
);
}
}
4.3.2 配置中心(Config Server)
# Config Server配置
server:
port: 8888
spring:
application:
name: config-server
cloud:
config:
server:
git:
uri: https://github.com/your-repo/config-repo
username: your-username
password: your-password
search-paths: config
default-label: main
// Config Server启动类
@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
// Config Client配置
spring:
application:
name: user-service
cloud:
config:
uri: http://localhost:8888
profile: dev
label: main
4.4 Spring性能优化
4.4.1 连接池优化
# HikariCP配置
spring:
datasource:
hikari:
# 连接池大小
maximum-pool-size: 20
minimum-idle: 5
# 连接超时
connection-timeout: 30000
# 空闲连接超时
idle-timeout: 600000
# 连接最大生命周期
max-lifetime: 1800000
# 连接测试查询
connection-test-query: SELECT 1
# 连接预热
initialization-fail-timeout: 1
# 连接泄漏检测
leak-detection-threshold: 60000
4.4.2 缓存优化
// 启用缓存
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager() {
// 使用Caffeine缓存
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCaffeine(Caffeine.newBuilder()
.expireAfterWrite(10, TimeUnit.MINUTES)
.maximumSize(1000)
.recordStats());
return cacheManager;
}
}
// 使用缓存
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Cacheable(value = "users", key = "#id")
public User getUserById(Long id) {
System.out.println("从数据库查询用户: " + id);
return userRepository.findById(id).orElse(null);
}
@CacheEvict(value = "users", key = "#id")
public void deleteUser(Long id) {
userRepository.deleteById(id);
}
@CachePut(value = "users", key = "#user.id")
public User updateUser(User user) {
return userRepository.save(user);
}
@Cacheable(value = "users", key = "'all'")
public List<User> getAllUsers() {
System.out.println("从数据库查询所有用户");
return userRepository.findAll();
}
}
4.4.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.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}
// 异步服务
@Service
public class NotificationService {
@Async
public void sendEmailNotification(String email, String message) {
System.out.println("开始发送邮件到: " + email);
// 模拟耗时操作
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("邮件发送完成: " + email);
}
@Async
public CompletableFuture<String> processOrderAsync(Long orderId) {
System.out.println("开始异步处理订单: " + orderId);
// 模拟业务处理
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return CompletableFuture.completedFuture("订单处理完成: " + orderId);
}
}
第五部分:Spring测试与调试
5.1 单元测试
5.1.1 JUnit 5 + Mockito
// 被测试的Service
@Service
public class OrderService {
@Autowired
private PaymentService paymentService;
@Autowired
private InventoryService inventoryService;
@Transactional
public Order createOrder(Order order) {
// 验证库存
if (!inventoryService.checkStock(order.getProductId(), order.getQuantity())) {
throw new InsufficientStockException("库存不足");
}
// 扣减库存
inventoryService.deductStock(order.getProductId(), order.getQuantity());
// 支付处理
PaymentResult paymentResult = paymentService.processPayment(order);
if (!paymentResult.isSuccess()) {
throw new PaymentException("支付失败");
}
// 保存订单
return orderRepository.save(order);
}
}
// 单元测试
@ExtendWith(MockitoExtension.class)
class OrderServiceTest {
@Mock
private PaymentService paymentService;
@Mock
private InventoryService inventoryService;
@Mock
private OrderRepository orderRepository;
@InjectMocks
private OrderService orderService;
@Test
void shouldCreateOrderSuccessfully() {
// 准备测试数据
Order order = new Order();
order.setProductId(1L);
order.setQuantity(2);
order.setAmount(new BigDecimal("99.99"));
// Mock行为
when(inventoryService.checkStock(1L, 2)).thenReturn(true);
when(inventoryService.deductStock(1L, 2)).thenReturn(true);
PaymentResult paymentResult = new PaymentResult();
paymentResult.setSuccess(true);
paymentResult.setTransactionId("TXN-12345");
when(paymentService.processPayment(order)).thenReturn(paymentResult);
when(orderRepository.save(any(Order.class))).thenReturn(order);
// 执行测试
Order result = orderService.createOrder(order);
// 验证结果
assertNotNull(result);
assertEquals(order.getProductId(), result.getProductId());
// 验证方法调用
verify(inventoryService).checkStock(1L, 2);
verify(inventoryService).deductStock(1L, 2);
verify(paymentService).processPayment(order);
verify(orderRepository).save(order);
}
@Test
void shouldThrowExceptionWhenInsufficientStock() {
// 准备测试数据
Order order = new Order();
order.setProductId(1L);
order.setQuantity(100);
// Mock行为
when(inventoryService.checkStock(1L, 100)).thenReturn(false);
// 执行测试并验证异常
assertThrows(InsufficientStockException.class, () -> {
orderService.createOrder(order);
});
// 验证方法调用
verify(inventoryService).checkStock(1L, 100);
verifyNoInteractions(paymentService);
verifyNoInteractions(orderRepository);
}
}
5.1.2 Spring Boot Test
// 集成测试
@SpringBootTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Rollback
class OrderServiceIntegrationTest {
@Autowired
private OrderService orderService;
@Autowired
private OrderRepository orderRepository;
@Autowired
private TestEntityManager entityManager;
@Test
@Transactional
void shouldCreateOrderInDatabase() {
// 准备测试数据
Product product = new Product();
product.setName("Test Product");
product.setStock(10);
entityManager.persist(product);
Order order = new Order();
order.setProductId(product.getId());
order.setQuantity(2);
order.setAmount(new BigDecimal("99.99"));
// 执行测试
Order savedOrder = orderService.createOrder(order);
// 验证结果
assertNotNull(savedOrder.getId());
// 验证数据库状态
Order foundOrder = orderRepository.findById(savedOrder.getId()).orElse(null);
assertNotNull(foundOrder);
assertEquals(2, foundOrder.getQuantity());
// 验证库存扣减
Product updatedProduct = entityManager.find(Product.class, product.getId());
assertEquals(8, updatedProduct.getStock());
}
}
5.2 测试配置
# application-test.yml
spring:
datasource:
url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
driver-class-name: org.h2.Driver
username: sa
password:
jpa:
hibernate:
ddl-auto: create-drop
show-sql: true
properties:
hibernate:
format_sql: true
flyway:
enabled: false
// 测试配置类
@TestConfiguration
public class TestConfig {
@Bean
public DataSource testDataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript("classpath:test-schema.sql")
.addScript("classpath:test-data.sql")
.build();
}
@Bean
public CacheManager testCacheManager() {
return new ConcurrentMapCacheManager();
}
}
第六部分:Spring最佳实践
6.1 项目结构
src/main/java/com/example/
├── config/ # 配置类
├── controller/ # 控制器层
├── service/ # 服务层
├── repository/ # 数据访问层
├── entity/ # 实体类
├── dto/ # 数据传输对象
├── exception/ # 自定义异常
├── util/ # 工具类
└── aspect/ # 切面类
6.2 命名规范
- 包名:小写,使用点分隔,如
com.example.project.module - 类名:大驼峰,如
UserService,OrderController - 方法名:小驼峰,如
getUserById,createOrder - 常量:全大写,下划线分隔,如
MAX_RETRY_COUNT - 变量名:小驼峰,如
userName,orderList
6.3 异常处理
// 全局异常处理器
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public ErrorResponse handleResourceNotFoundException(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 handleException(Exception ex) {
// 记录日志
log.error("系统异常", ex);
return new ErrorResponse("INTERNAL_ERROR", "系统内部错误");
}
}
// 自定义异常
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException(String message) {
super(message);
}
}
// 错误响应DTO
@Data
public class ErrorResponse {
private String code;
private String message;
private long timestamp;
public ErrorResponse(String code, String message) {
this.code = code;
this.message = message;
this.timestamp = System.currentTimeMillis();
}
}
6.4 日志配置
# application.yml
logging:
level:
root: INFO
com.example: DEBUG
org.springframework.web: INFO
org.hibernate.SQL: DEBUG
org.hibernate.type.descriptor.sql.BasicBinder: TRACE
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
file: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
file:
name: logs/application.log
max-size: 10MB
max-history: 30
// 日志使用示例
@Service
public class OrderService {
private static final Logger logger = LoggerFactory.getLogger(OrderService.class);
public Order createOrder(Order order) {
logger.debug("开始创建订单: {}", order);
try {
// 业务逻辑
Order savedOrder = orderRepository.save(order);
logger.info("订单创建成功,订单ID: {}", savedOrder.getId());
return savedOrder;
} catch (Exception e) {
logger.error("创建订单失败,订单信息: {}", order, e);
throw new OrderCreationException("创建订单失败", e);
}
}
}
第七部分:Spring高级主题
7.1 Spring响应式编程(Reactive)
// 响应式Web应用
@SpringBootApplication
public class ReactiveApplication {
public static void main(String[] args) {
SpringApplication.run(ReactiveApplication.class, args);
}
}
// 响应式Controller
@RestController
@RequestMapping("/api/reactive")
public class ReactiveController {
@Autowired
private ReactiveUserService reactiveUserService;
@GetMapping("/users/{id}")
public Mono<User> getUserById(@PathVariable Long id) {
return reactiveUserService.getUserById(id);
}
@GetMapping("/users")
public Flux<User> getAllUsers() {
return reactiveUserService.getAllUsers();
}
@PostMapping("/users")
public Mono<User> createUser(@RequestBody User user) {
return reactiveUserService.createUser(user);
}
}
// 响应式Service
@Service
public class ReactiveUserService {
@Autowired
private ReactiveUserRepository reactiveUserRepository;
public Mono<User> getUserById(Long id) {
return reactiveUserRepository.findById(id)
.switchIfEmpty(Mono.error(new ResourceNotFoundException("用户不存在")));
}
public Flux<User> getAllUsers() {
return reactiveUserRepository.findAll();
}
public Mono<User> createUser(User user) {
return reactiveUserRepository.save(user);
}
}
// 响应式Repository
public interface ReactiveUserRepository extends ReactiveMongoRepository<User, Long> {
Flux<User> findByUsername(String username);
Mono<User> findByEmail(String email);
}
7.2 Spring WebSocket
// WebSocket配置
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new ChatWebSocketHandler(), "/ws/chat")
.setAllowedOrigins("*");
}
}
// WebSocket处理器
@Component
public class ChatWebSocketHandler extends TextWebSocketHandler {
private static final List<WebSocketSession> sessions = new CopyOnWriteArrayList<>();
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
sessions.add(session);
session.sendMessage(new TextMessage("欢迎加入聊天室"));
}
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
String payload = message.getPayload();
System.out.println("收到消息: " + payload);
// 广播消息给所有客户端
for (WebSocketSession s : sessions) {
if (s.isOpen()) {
s.sendMessage(new TextMessage("用户" + session.getId() + ": " + payload));
}
}
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
sessions.remove(session);
}
}
7.3 Spring批处理
// 批处理配置
@Configuration
@EnableBatchProcessing
public class BatchConfig {
@Autowired
private JobBuilderFactory jobBuilderFactory;
@Autowired
private StepBuilderFactory stepBuilderFactory;
@Bean
public Job importUserJob() {
return jobBuilderFactory.get("importUserJob")
.start(importUserStep())
.next(verifyUserStep())
.build();
}
@Bean
public Step importUserStep() {
return stepBuilderFactory.get("importUserStep")
.<User, User>chunk(10)
.reader(userItemReader())
.processor(userItemProcessor())
.writer(userItemWriter())
.build();
}
@Bean
public Step verifyUserStep() {
return stepBuilderFactory.get("verifyUserStep")
.tasklet((contribution, chunkContext) -> {
// 验证逻辑
System.out.println("验证用户数据完成");
return RepeatStatus.FINISHED;
})
.build();
}
@Bean
public ItemReader<User> userItemReader() {
// 从文件读取用户数据
FlatFileItemReader<User> reader = new FlatFileItemReader<>();
reader.setResource(new ClassPathResource("users.csv"));
reader.setLineMapper(new DefaultLineMapper<User>() {{
setLineTokenizer(new DelimitedLineTokenizer() {{
setNames("id", "username", "email", "age");
}});
setFieldSetMapper(new BeanWrapperFieldSetMapper<User>() {{
setTargetType(User.class);
}});
}});
return reader;
}
@Bean
public ItemProcessor<User, User> userItemProcessor() {
return user -> {
// 数据处理逻辑
user.setUsername(user.getUsername().toUpperCase());
return user;
};
}
@Bean
public ItemWriter<User> userItemWriter() {
return users -> {
// 批量写入数据库
System.out.println("写入 " + users.size() + " 条用户记录");
};
}
}
第八部分:Spring监控与运维
8.1 Spring Boot Actuator
# application.yml
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus,loggers
base-path: /actuator
endpoint:
health:
show-details: always
metrics:
export:
prometheus:
enabled: true
metrics:
export:
prometheus:
enabled: true
distribution:
percentiles-histogram:
http.server.requests: true
// 自定义健康检查
@Component
public class CustomHealthIndicator implements HealthIndicator {
@Autowired
private DataSource dataSource;
@Override
public Health health() {
try (Connection connection = dataSource.getConnection()) {
return Health.up()
.withDetail("database", "connected")
.withDetail("version", connection.getMetaData().getDatabaseProductVersion())
.build();
} catch (Exception e) {
return Health.down()
.withDetail("error", e.getMessage())
.build();
}
}
}
8.2 分布式追踪
// 集成Sleuth + Zipkin
@Configuration
public class TracingConfig {
@Bean
public Sampler defaultSampler() {
return Sampler.ALWAYS_SAMPLE; // 采样率100%(生产环境应调整)
}
}
// 使用追踪
@Service
public class OrderService {
@Autowired
private RestTemplate restTemplate;
public Order createOrder(Order order) {
// Sleuth会自动添加追踪信息
User user = restTemplate.getForObject(
"http://user-service/api/users/" + order.getUserId(),
User.class
);
// 业务逻辑
return orderRepository.save(order);
}
}
总结
通过本指南的学习,你应该已经掌握了Spring框架从基础到高级的完整知识体系。Spring框架的核心在于其灵活的架构设计和强大的扩展能力。在实际开发中,建议:
- 理解原理:深入理解IoC、AOP、事务等核心原理
- 实践驱动:通过实际项目巩固知识
- 持续学习:关注Spring生态的最新发展
- 最佳实践:遵循社区公认的最佳实践
Spring框架的学习是一个持续的过程,建议在实际项目中不断探索和应用,逐步提升自己的技术水平。
