如果你刚开始接触Java企业级开发,Spring框架绝对是绕不开的大山。它就像Java世界的“操作系统”,虽然你看不见它,但几乎所有现代Java应用都运行在它之上。别担心,我们不讲枯燥的理论堆砌,而是把Spring拆解成你可以亲手摸到的东西,让你真正理解它为什么存在、怎么工作,以及如何在实际项目中让它为你所用。
Spring到底解决了什么痛点?
在Spring出现之前,Java开发者的日子并不好过。想象一下,你要写一个用户管理系统,需要连接数据库、处理业务逻辑、调用外部服务、管理事务……每个模块之间都紧紧耦合在一起。修改一个数据库连接方式,可能整个项目都要重构。更糟糕的是,测试这些组件几乎不可能,因为你很难在不启动整个应用的情况下单独测试某个Service。
Spring的核心贡献在于引入了控制反转(IoC)和面向切面编程(AOP)这两个概念。简单来说,Spring帮你管理对象的生命周期和依赖关系,让你专注于业务逻辑而不是基础设施代码。你不再需要手动new对象、手动关闭连接、手动管理事务——Spring替你完成这些琐事。
理解IoC容器:Spring的心脏
IoC(Inversion of Control,控制反转)是Spring最核心的概念。传统编程中,对象自己负责创建和管理依赖;而在Spring中,这个责任被反转给了Spring容器。
让我用一个具体的例子来说明。假设你有一个 UserService,它依赖 UserRepository 来操作数据库:
// 传统的写法:对象自己管理依赖
public class UserService {
private UserRepository userRepository = new UserRepository();
public void createUser(String name) {
User user = new User(name);
userRepository.save(user);
}
}
这种写法的麻烦在于:如果有一天你想把 UserRepository 换成 Mock 对象进行测试,或者想换成另一个实现类(比如基于缓存的 Repository),你就必须修改 UserService 的代码。这违反了开闭原则。
使用Spring的IoC容器后,代码变成了这样:
// Spring风格:依赖由容器注入
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public void createUser(String name) {
User user = new User(name);
userRepository.save(user);
}
}
// Repository接口
public interface UserRepository {
void save(User user);
}
// 具体实现
@Repository
public class JdbcUserRepository implements UserRepository {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public void save(User user) {
jdbcTemplate.update("INSERT INTO users (name) VALUES (?)", user.getName());
}
}
// 配置类:告诉Spring如何组装这些Bean
@Configuration
@ComponentScan(basePackages = "com.example")
public class AppConfig {
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
@Bean
public DataSource dataSource() {
// 实际项目中会使用HikariCP等连接池
return new DriverManagerDataSource("jdbc:h2:mem:testdb");
}
}
// 启动Spring容器
public class Main {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = context.getBean(UserService.class);
userService.createUser("张三");
}
}
在这个例子中,Spring容器负责创建 UserService、UserRepository 和 JdbcTemplate 对象,并将它们正确地组装在一起。你只需要关注业务逻辑,不需要关心对象如何创建、如何依赖。
Bean的生命周期与作用域
理解Spring Bean的生命周期对于调试问题和优化性能至关重要。当一个Bean被请求时,Spring容器会经历以下步骤:
- 实例化:容器通过反射创建Bean的实例
- 属性填充:注入依赖的其他Bean
- 初始化:执行自定义初始化逻辑(如 @PostConstruct 标注的方法)
- 使用:Bean在应用中提供服务
- 销毁:容器关闭时清理资源
@Component
public class DataSourceBean {
@PostConstruct
public void init() {
System.out.println("DataSource初始化完成");
}
@PreDestroy
public void cleanup() {
System.out.println("DataSource清理完成");
}
}
Spring提供了多种Bean作用域,最常用的有:
- singleton(默认):整个应用只有一个实例
- prototype:每次请求都创建新实例
- request:每个HTTP请求创建一个实例(Web应用)
- session:每个HTTP会话创建一个实例(Web应用)
@Component
@Scope("prototype")
public class PrototypeBean {
private int id = (int) (Math.random() * 1000);
public int getId() {
return id;
}
}
// 测试prototype作用域
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
PrototypeBean bean1 = context.getBean(PrototypeBean.class);
PrototypeBean bean2 = context.getBean(PrototypeBean.class);
System.out.println(bean1.getId() == bean2.getId()); // false,每次都是新实例
Spring MVC:构建Web应用的优雅方式
Spring MVC是Spring框架的Web模块,它基于请求-响应模型,帮助你构建RESTful API或传统MVC应用。
一个简单的REST Controller
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
User user = userService.findById(id);
if (user == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(user);
}
@PostMapping
public ResponseEntity<User> createUser(@RequestBody CreateUserRequest request) {
User user = userService.createUser(request.getName());
return ResponseEntity.status(HttpStatus.CREATED).body(user);
}
@PutMapping("/{id}")
public ResponseEntity<User> updateUser(@PathVariable Long id,
@RequestBody UpdateUserRequest request) {
User user = userService.updateUser(id, request.getName());
return ResponseEntity.ok(user);
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
userService.deleteUser(id);
return ResponseEntity.noContent().build();
}
}
参数校验与异常处理
public class CreateUserRequest {
@NotBlank(message = "用户名不能为空")
@Size(min = 2, max = 50, message = "用户名长度必须在2-50之间")
private String name;
@Email(message = "邮箱格式不正确")
private String email;
// getters and setters
}
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, String>> handleValidationExceptions(
MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getFieldErrors().forEach(error ->
errors.put(error.getField(), error.getDefaultMessage()));
return ResponseEntity.badRequest().body(errors);
}
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<Map<String, String>> handleResourceNotFound(
ResourceNotFoundException ex) {
Map<String, String> error = new HashMap<>();
error.put("error", ex.getMessage());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
}
}
Spring Data JPA:让数据访问变得简单
Spring Data JPA是Spring框架的数据访问抽象层,它让你只需定义接口就能获得完整的CRUD操作能力,无需编写任何实现代码。
定义实体
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, length = 50)
private String name;
@Column(unique = true)
private String email;
@CreationTimestamp
private LocalDateTime createdAt;
@UpdateTimestamp
private LocalDateTime updatedAt;
// getters and setters
}
定义Repository接口
public interface UserRepository extends JpaRepository<User, Long> {
// 自动实现:SELECT * FROM users WHERE email = ?
Optional<User> findByEmail(String email);
// 自动实现:SELECT * FROM users WHERE name LIKE %?%
List<User> findByNameContaining(String name);
// 自定义查询:使用JPQL
@Query("SELECT u FROM User u WHERE u.createdAt > :since")
List<User> findRecentUsers(@Param("since") LocalDateTime since);
// 原生SQL查询
@Query(value = "SELECT COUNT(*) FROM users WHERE active = true",
nativeQuery = true)
long countActiveUsers();
}
使用Repository
@Service
@Transactional
public class UserService {
@Autowired
private UserRepository userRepository;
public User createUser(String name, String email) {
// 检查邮箱是否已存在
userRepository.findByEmail(email).ifPresent(u -> {
throw new IllegalArgumentException("邮箱已存在: " + email);
});
User user = new User();
user.setName(name);
user.setEmail(email);
return userRepository.save(user);
}
public User findById(Long id) {
return userRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("用户不存在: " + id));
}
public List<User> searchUsers(String keyword) {
return userRepository.findByNameContaining(keyword);
}
public void deleteUser(Long id) {
User user = findById(id);
userRepository.delete(user);
}
}
Spring Security:安全认证与授权
在现代应用中,安全是重中之重。Spring Security提供了完整的认证和授权解决方案。
基础配置
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Autowired
private UserService userService;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.httpBasic(Customizer.withDefaults())
.sessionManagement(session ->
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
);
return http.build();
}
@Bean
public AuthenticationManager authenticationManager(
AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager();
}
}
JWT Token认证(现代应用常用)
@Component
public class JwtTokenProvider {
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private long expiration;
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
return Jwts.builder()
.setClaims(claims)
.setSubject(userDetails.getUsername())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + expiration))
.signWith(SignatureAlgorithm.HS256, secret)
.compact();
}
public String getUsernameFromToken(String token) {
return Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody()
.getSubject();
}
public boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey(secret).parseClaimsJws(token);
return true;
} catch (JwtException | IllegalArgumentException e) {
return false;
}
}
}
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private JwtTokenProvider tokenProvider;
@Autowired
private CustomUserDetailsService userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain)
throws ServletException, IOException {
String token = getJwtFromRequest(request);
if (token != null && tokenProvider.validateToken(token)) {
String username = tokenProvider.getUsernameFromToken(token);
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
chain.doFilter(request, response);
}
private String getJwtFromRequest(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7);
}
return null;
}
}
AOP:面向切面编程的实际应用
AOP让你能够在不修改业务代码的情况下,横切关注点(如日志、事务、安全)被封装到切面中,然后在需要的地方自动应用。
自定义日志切面
@Aspect
@Component
public class LoggingAspect {
private static final Logger log = LoggerFactory.getLogger(LoggingAspect.class);
// 匹配所有Service层的公共方法
@Around("execution(* com.example.service.*.*(..))")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
Object proceed = joinPoint.proceed();
stopWatch.stop();
log.info("{} 执行完成,耗时: {} ms",
joinPoint.getSignature().toShortString(),
stopWatch.getTotalTimeMillis());
return proceed;
}
// 记录异常日志
@AfterThrowing(pointcut = "execution(* com.example.service.*.*(..))",
throwing = "exception")
public void logException(JoinPoint joinPoint, Exception exception) {
log.error("方法 {} 发生异常: {}",
joinPoint.getSignature().getName(),
exception.getMessage(),
exception);
}
}
事务管理切面
Spring已经内置了事务管理,你只需要添加注解即可:
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
@Autowired
private InventoryService inventoryService;
// 整个方法在一个事务中执行
@Transactional(rollbackFor = Exception.class)
public Order createOrder(Long userId, List<Long> productIds) {
// 1. 计算总价
BigDecimal totalAmount = productIds.stream()
.map(productId -> inventoryService.getProductPrice(productId))
.reduce(BigDecimal.ZERO, BigDecimal::add);
// 2. 创建订单
Order order = new Order();
order.setUserId(userId);
order.setTotalAmount(totalAmount);
order.setStatus(OrderStatus.PENDING);
orderRepository.save(order);
// 3. 扣减库存(如果失败,整个事务回滚)
for (Long productId : productIds) {
inventoryService.deductStock(productId, 1);
}
return order;
}
// 只读查询,优化性能
@Transactional(readOnly = true)
public Order getOrder(Long orderId) {
return orderRepository.findById(orderId)
.orElseThrow(() -> new ResourceNotFoundException("订单不存在"));
}
}
实际项目架构:从0到1搭建Spring Boot应用
Spring Boot是Spring框架的入门友好版本,它通过约定优于配置的理念,让你快速启动项目。
项目结构
src/main/java/com/example/order/
├── OrderApplication.java # 启动类
├── config/ # 配置类
│ ├── SecurityConfig.java
│ ├── SwaggerConfig.java
│ └── WebMvcConfig.java
├── controller/ # 控制器层
│ ├── OrderController.java
│ └── ProductController.java
├── service/ # 服务层
│ ├── OrderService.java
│ ├── ProductService.java
│ └── impl/
│ ├── OrderServiceImpl.java
│ └── ProductServiceImpl.java
├── repository/ # 数据访问层
│ ├── OrderRepository.java
│ └── ProductRepository.java
├── entity/ # 实体类
│ ├── Order.java
│ ├── OrderItem.java
│ └── Product.java
├── dto/ # 数据传输对象
│ ├── CreateOrderRequest.java
│ ├── OrderResponse.java
│ └── ProductResponse.java
├── exception/ # 异常处理
│ ├── GlobalExceptionHandler.java
│ ├── ResourceNotFoundException.java
│ └── BusinessException.java
└── aspect/ # 切面
└── LoggingAspect.java
启动类
@SpringBootApplication
@ComponentScan(basePackages = "com.example.order")
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
application.yml 配置
”`yaml server: port: 8080
spring: datasource:
url: jdbc:h2:mem:orderdb
driver-class-name: org.h2.Driver
username: sa
password:
jpa:
database-platform: org.hibernate.dialect.H2Dialect
hibernate:
ddl-auto: update
show-sql: true
h2:
console:
enabled: true
path: /h2-console
jwt: secret: mySecretKey123456789 expiration: 86400000
